filename | src/bios.c |
changeset | 1107:7b279d10f46f |
prev | 1103:de9ad2c0cf56 |
next | 1298:d0eb2307b847 |
author | nkeynes |
date | Tue Feb 28 17:25:26 2012 +1000 (11 years ago) |
permissions | -rw-r--r-- |
last change | Implement display output for the GLES2 case (no fixed function rendering) |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * "Fake" BIOS functions, for operation without the actual BIOS.
5 *
6 * Copyright (c) 2005-2010 Nathan Keynes.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
19 #include "dream.h"
20 #include "mem.h"
21 #include "syscall.h"
22 #include "asic.h"
23 #include "dreamcast.h"
24 #include "bootstrap.h"
25 #include "sh4/sh4.h"
26 #include "drivers/cdrom/cdrom.h"
27 #include "drivers/cdrom/isofs.h"
28 #include "gdrom/gdrom.h"
30 gboolean bios_boot_gdrom_disc( void );
32 /* Definitions from KOS */
33 #define COMMAND_QUEUE_LENGTH 16
35 #define GD_CMD_PIOREAD 16 /* readcd */
36 #define GD_CMD_DMAREAD 17 /* readcd */
37 #define GD_CMD_GETTOC 18
38 #define GD_CMD_GETTOC2 19 /* toc2 */
39 #define GD_CMD_PLAY 20 /* playcd */
40 #define GD_CMD_PLAY2 21 /* playcd */
41 #define GD_CMD_PAUSE 22 /* No params */
42 #define GD_CMD_RELEASE 23 /* No params */
43 #define GD_CMD_INIT 24 /* No params */
44 #define GD_CMD_SEEK 27
45 #define GD_CMD_READ 28
46 #define GD_CMD_STOP 33 /* No params */
47 #define GD_CMD_GETSCD 34
48 #define GD_CMD_GETSES 35
50 #define GD_CMD_STATUS_NONE 0
51 #define GD_CMD_STATUS_ACTIVE 1
52 #define GD_CMD_STATUS_DONE 2
53 #define GD_CMD_STATUS_ABORT 3
54 #define GD_CMD_STATUS_ERROR 4
56 #define GD_ERROR_OK 0
57 #define GD_ERROR_NO_DISC 2
58 #define GD_ERROR_DISC_CHANGE 6
59 #define GD_ERROR_SYSTEM 1
61 struct gdrom_toc2_params {
62 uint32_t session;
63 sh4addr_t buffer;
64 };
66 struct gdrom_readcd_params {
67 cdrom_lba_t lba;
68 cdrom_count_t count;
69 sh4addr_t buffer;
70 uint32_t unknown;
71 };
73 struct gdrom_playcd_params {
74 cdrom_lba_t start;
75 cdrom_lba_t end;
76 uint32_t repeat;
77 };
79 typedef union gdrom_cmd_params {
80 struct gdrom_toc2_params toc2;
81 struct gdrom_readcd_params readcd;
82 struct gdrom_playcd_params playcd;
83 } *gdrom_cmd_params_t;
88 typedef struct gdrom_queue_entry {
89 int status;
90 uint32_t cmd_code;
91 union gdrom_cmd_params params;
92 uint32_t result[4];
93 } *gdrom_queue_entry_t;
95 static struct gdrom_queue_entry gdrom_cmd_queue[COMMAND_QUEUE_LENGTH];
97 static struct bios_gdrom_status {
98 uint32_t status;
99 uint32_t disk_type;
100 } bios_gdrom_status;
102 void bios_gdrom_init( void )
103 {
104 memset( &gdrom_cmd_queue, 0, sizeof(gdrom_cmd_queue) );
105 }
107 void bios_gdrom_run_command( gdrom_queue_entry_t cmd )
108 {
109 DEBUG( "BIOS GD command %d", cmd->cmd_code );
110 cdrom_error_t status = CDROM_ERROR_OK;
111 sh4ptr_t ptr;
112 switch( cmd->cmd_code ) {
113 case GD_CMD_INIT:
114 /* *shrug* */
115 cmd->status = GD_CMD_STATUS_DONE;
116 break;
117 case GD_CMD_GETTOC2:
118 ptr = mem_get_region( cmd->params.toc2.buffer );
119 status = gdrom_read_toc( ptr );
120 if( status == CDROM_ERROR_OK ) {
121 /* Convert data to little-endian */
122 struct gdrom_toc *toc = (struct gdrom_toc *)ptr;
123 for( unsigned i=0; i<99; i++ ) {
124 toc->track[i] = ntohl(toc->track[i]);
125 }
126 toc->first = ntohl(toc->first);
127 toc->last = ntohl(toc->last);
128 toc->leadout = ntohl(toc->leadout);
129 }
130 break;
131 case GD_CMD_PIOREAD:
132 case GD_CMD_DMAREAD:
133 ptr = mem_get_region( cmd->params.readcd.buffer );
134 status = gdrom_read_cd( cmd->params.readcd.lba,
135 cmd->params.readcd.count, 0x28, ptr, NULL );
136 break;
137 default:
138 WARN( "Unknown BIOS GD command %d\n", cmd->cmd_code );
139 cmd->status = GD_CMD_STATUS_ERROR;
140 cmd->result[0] = GD_ERROR_SYSTEM;
141 return;
142 }
144 switch( status ) {
145 case CDROM_ERROR_OK:
146 cmd->status = GD_CMD_STATUS_DONE;
147 cmd->result[0] = GD_ERROR_OK;
148 break;
149 case CDROM_ERROR_NODISC:
150 cmd->status = GD_CMD_STATUS_ERROR;
151 cmd->result[0] = GD_ERROR_NO_DISC;
152 break;
153 default:
154 cmd->status = GD_CMD_STATUS_ERROR;
155 cmd->result[0] = GD_ERROR_SYSTEM;
156 }
157 }
159 uint32_t bios_gdrom_enqueue( uint32_t cmd, sh4addr_t data )
160 {
161 int i;
162 for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
163 if( gdrom_cmd_queue[i].status != GD_CMD_STATUS_ACTIVE ) {
164 gdrom_cmd_queue[i].status = GD_CMD_STATUS_ACTIVE;
165 gdrom_cmd_queue[i].cmd_code = cmd;
166 switch( cmd ) {
167 case GD_CMD_PIOREAD:
168 case GD_CMD_DMAREAD:
169 mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.readcd, data, sizeof(struct gdrom_readcd_params) );
170 break;
171 case GD_CMD_GETTOC2:
172 mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.toc2, data, sizeof(struct gdrom_toc2_params) );
173 break;
174 case GD_CMD_PLAY:
175 case GD_CMD_PLAY2:
176 mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.playcd, data, sizeof(struct gdrom_playcd_params) );
177 break;
178 }
179 return i;
180 }
181 }
182 return -1;
183 }
185 void bios_gdrom_run_queue( void )
186 {
187 int i;
188 for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
189 if( gdrom_cmd_queue[i].status == GD_CMD_STATUS_ACTIVE ) {
190 bios_gdrom_run_command( &gdrom_cmd_queue[i] );
191 }
192 }
193 }
195 gdrom_queue_entry_t bios_gdrom_get_command( uint32_t id )
196 {
197 if( id >= COMMAND_QUEUE_LENGTH ||
198 gdrom_cmd_queue[id].status == GD_CMD_STATUS_NONE )
199 return NULL;
200 return &gdrom_cmd_queue[id];
201 }
203 /**
204 * Address of the system information block (in the flash rom). Also repeats
205 * at FLASH_SYSINFO_SEGMENT+0xA0
206 */
207 #define FLASH_SYSINFO_SEGMENT 0x0021a000
208 #define FLASH_CONFIG_SEGMENT 0x0021c000
209 #define FLASH_CONFIG_LENGTH 0x00004000
210 #define FLASH_PARTITION_MAGIC "KATANA_FLASH____"
212 /**
213 * Locate the active config block. FIXME: This isn't completely correct, but it works
214 * under at least some circumstances.
215 */
216 static char *bios_find_flash_config( sh4addr_t segment, uint32_t length )
217 {
218 char *start = mem_get_region(segment);
219 char *p = start + 0x80;
220 char *end = p + length;
221 char *result = NULL;
223 if( memcmp( start, FLASH_PARTITION_MAGIC, 16 ) != 0 )
224 return NULL; /* Missing magic */
225 while( p < end ) {
226 if( p[0] == 0x05 && p[1] == 0 ) {
227 result = p;
228 }
229 p += 0x40;
230 }
231 return result;
232 }
234 /**
235 * Syscall information courtesy of Marcus Comstedt
236 */
237 static void bios_sysinfo_vector( uint32_t syscallid )
238 {
239 char *flash_segment, *flash_config;
240 char *dest;
241 DEBUG( "BIOS SYSINFO: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
243 switch( sh4r.r[7] ) {
244 case 0: /* SYSINFO_INIT */
245 /* Initialize the region 8c000068 .. 8c00007f from the flash rom
246 * uint64_t system_id;
247 * char [5] system_props;
248 * char [3] zero_pad (?)
249 * char [8] settings;
250 **/
251 flash_segment = mem_get_region(FLASH_SYSINFO_SEGMENT);
252 flash_config = bios_find_flash_config(FLASH_CONFIG_SEGMENT,FLASH_CONFIG_LENGTH);
253 dest = mem_get_region( 0x8c000068 );
254 memset( dest, 0, 24 );
255 memcpy( dest, flash_segment + 0x56, 8 );
256 memcpy( dest + 8, flash_segment, 5 );
257 if( flash_config != NULL ) {
258 memcpy( dest+16, flash_config+2, 8 );
259 }
260 break;
261 case 2: /* SYSINFO_ICON */
262 /* Not supported yet */
263 break;
264 case 3: /* SYSINFO_ID */
265 sh4r.r[0] = 0x8c000068;
266 break;
267 }
268 }
270 static void bios_flashrom_vector( uint32_t syscallid )
271 {
272 char *dest;
273 DEBUG( "BIOS FLASHROM: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
275 switch( sh4r.r[7] ) {
276 case 0: /* FLASHROM_INFO */
277 break;
278 case 1: /* FLASHROM_READ */
280 break;
281 case 2: /* FLASHROM_WRITE */
282 break;
283 case 3: /* FLASHROM_DELETE */
284 break;
285 }
286 }
288 static void bios_romfont_vector( uint32_t syscallid )
289 {
290 DEBUG( "BIOS ROMFONT: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
291 /* Not implemented */
292 }
294 static void bios_gdrom_vector( uint32_t syscallid )
295 {
296 gdrom_queue_entry_t cmd;
298 DEBUG( "BIOS GDROM: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
300 switch( sh4r.r[6] ) {
301 case 0: /* GD-Rom */
302 switch( sh4r.r[7] ) {
303 case 0: /* Send command */
304 sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], sh4r.r[5] );
305 break;
306 case 1: /* Check command */
307 cmd = bios_gdrom_get_command( sh4r.r[4] );
308 if( cmd == NULL ) {
309 sh4r.r[0] = GD_CMD_STATUS_NONE;
310 } else {
311 sh4r.r[0] = cmd->status;
312 if( cmd->status == GD_CMD_STATUS_ERROR &&
313 sh4r.r[5] != 0 ) {
314 mem_copy_to_sh4( sh4r.r[5], (sh4ptr_t)&cmd->result, sizeof(cmd->result) );
315 }
316 }
317 break;
318 case 2: /* Mainloop */
319 bios_gdrom_run_queue();
320 break;
321 case 3: /* Init */
322 bios_gdrom_init();
323 break;
324 case 4: /* Drive status */
325 if( sh4r.r[4] != 0 ) {
326 mem_copy_to_sh4( sh4r.r[4], (sh4ptr_t)&bios_gdrom_status,
327 sizeof(bios_gdrom_status) );
328 }
329 sh4r.r[0] = 0;
330 break;
331 case 8: /* Abort command */
332 cmd = bios_gdrom_get_command( sh4r.r[4] );
333 if( cmd == NULL || cmd->status != GD_CMD_STATUS_ACTIVE ) {
334 sh4r.r[0] = -1;
335 } else {
336 cmd->status = GD_CMD_STATUS_ABORT;
337 sh4r.r[0] = 0;
338 }
339 break;
340 case 9: /* Reset */
341 break;
342 case 10: /* Set mode */
343 sh4r.r[0] = 0;
344 break;
345 }
346 break;
347 case -1: /* Misc */
348 break;
349 default: /* ??? */
350 break;
351 }
352 }
354 static void bios_menu_vector( uint32_t syscallid )
355 {
356 DEBUG( "BIOS MENU: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
358 switch( sh4r.r[4] ) {
359 case 0:
360 WARN( "Entering main program" );
361 break;
362 case 1:
363 WARN( "Program aborted to DC menu");
364 dreamcast_stop();
365 break;
366 }
367 }
369 void bios_boot( uint32_t syscallid )
370 {
371 /* Initialize hardware */
372 /* Boot disc if present */
373 if( !bios_boot_gdrom_disc() ) {
374 dreamcast_stop();
375 }
376 }
378 void bios_install( void )
379 {
380 bios_gdrom_init();
381 syscall_add_hook_vector( 0xB0, 0x8C0000B0, bios_sysinfo_vector );
382 syscall_add_hook_vector( 0xB4, 0x8C0000B4, bios_romfont_vector );
383 syscall_add_hook_vector( 0xB8, 0x8C0000B8, bios_flashrom_vector );
384 syscall_add_hook_vector( 0xBC, 0x8C0000BC, bios_gdrom_vector );
385 syscall_add_hook_vector( 0xE0, 0x8C0000E0, bios_menu_vector );
386 }
388 #define MIN_ISO_SECTORS 32
390 static gboolean bios_load_ipl( cdrom_disc_t disc, cdrom_track_t track, const char *program_name,
391 unsigned char *buffer, gboolean unscramble )
392 {
393 gboolean rv = TRUE;
395 IsoImageFilesystem *iso = iso_filesystem_new_from_track( disc, track, NULL );
396 if( iso == NULL ) {
397 ERROR( "Disc is not bootable (invalid ISO9660 filesystem)" );
398 return FALSE;
399 }
400 IsoFileSource *file = NULL;
401 int status = iso->get_by_path(iso, program_name, &file );
402 if( status != 1 ) {
403 ERROR( "Disc is not bootable (initial program '%s' not found)", program_name );
404 iso_filesystem_unref(iso);
405 return FALSE;
406 }
408 struct stat st;
409 if( iso_file_source_stat(file, &st) == 1 ) {
410 if( st.st_size > (0x8D000000 - BINARY_LOAD_ADDR) ) {
411 ERROR( "Disc is not bootable (Initial program is too large to fit into memory)" );
412 rv = FALSE;
413 } else if( iso_file_source_open(file) == 1 ) {
414 size_t len;
415 if( unscramble ) {
416 char *tmp = g_malloc(st.st_size);
417 len = iso_file_source_read(file, tmp, st.st_size);
418 bootprogram_unscramble(buffer, tmp, st.st_size);
419 g_free(tmp);
420 } else {
421 len = iso_file_source_read(file, buffer, st.st_size);
422 }
424 if( len != st.st_size ) {
425 ERROR( "Disc is not bootable (Unable to read initial program '%s')", program_name );
426 rv = FALSE;
427 }
428 iso_file_source_close(file);
429 }
430 } else {
431 ERROR( "Disc is not bootable (Unable to get size of initial program '%s')", program_name );
432 rv = FALSE;
433 }
435 iso_file_source_unref(file);
436 iso_filesystem_unref(iso);
437 return rv;
438 }
440 gboolean bios_boot_gdrom_disc( void )
441 {
442 cdrom_disc_t disc = gdrom_get_current_disc();
444 int status = gdrom_get_drive_status();
445 if( status == CDROM_DISC_NONE ) {
446 ERROR( "No disc in drive" );
447 return FALSE;
448 }
450 /* Find the bootable data track (if present) */
451 cdrom_track_t track = gdrom_disc_get_boot_track(disc);
452 if( track == NULL ) {
453 ERROR( "Disc is not bootable" );
454 return FALSE;
455 }
456 uint32_t lba = track->lba;
457 uint32_t sectors = cdrom_disc_get_track_size(disc,track);
458 if( sectors < MIN_ISO_SECTORS ) {
459 ERROR( "Disc is not bootable" );
460 return FALSE;
461 }
462 /* Load the initial bootstrap into DC ram at 8c008000 */
463 size_t length = BOOTSTRAP_SIZE;
464 unsigned char *bootstrap = mem_get_region(BOOTSTRAP_LOAD_ADDR);
465 if( cdrom_disc_read_sectors( disc, track->lba, BOOTSTRAP_SIZE/2048,
466 CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, bootstrap, &length ) !=
467 CDROM_ERROR_OK ) {
468 ERROR( "Disc is not bootable" );
469 return FALSE;
470 }
472 /* Check the magic just to be sure */
473 dc_bootstrap_head_t metadata = (dc_bootstrap_head_t)bootstrap;
474 if( memcmp( metadata->magic, BOOTSTRAP_MAGIC, BOOTSTRAP_MAGIC_SIZE ) != 0 ) {
475 ERROR( "Disc is not bootable (missing dreamcast bootstrap)" );
476 return FALSE;
477 }
479 /* Get the initial program from the bootstrap (usually 1ST_READ.BIN) */
480 char program_name[18] = "/";
481 memcpy(program_name+1, metadata->boot_file, 16);
482 program_name[17] = '\0';
483 for( int i=16; i >= 0 && program_name[i] == ' '; i-- ) {
484 program_name[i] = '\0';
485 }
487 /* Bootstrap is good. Now find the program in the actual filesystem... */
488 unsigned char *program = mem_get_region(BINARY_LOAD_ADDR);
489 gboolean isGDROM = (disc->disc_type == CDROM_DISC_GDROM );
490 if( !bios_load_ipl( disc, track, program_name, program, !isGDROM ) )
491 return FALSE;
492 asic_enable_ide_interface(isGDROM);
493 dreamcast_program_loaded( "", BOOTSTRAP_ENTRY_ADDR );
494 return TRUE;
495 }
.