4 * "Fake" BIOS functions, for operation without the actual BIOS.
6 * Copyright (c) 2005-2010 Nathan Keynes.
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.
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.
23 #include "dreamcast.h"
24 #include "bootstrap.h"
26 #include "drivers/cdrom/cdrom.h"
27 #include "drivers/cdrom/isoread.h"
28 #include "gdrom/gdrom.h"
30 /* Definitions from KOS */
31 #define COMMAND_QUEUE_LENGTH 16
33 #define GD_CMD_PIOREAD 16 /* readcd */
34 #define GD_CMD_DMAREAD 17 /* readcd */
35 #define GD_CMD_GETTOC 18
36 #define GD_CMD_GETTOC2 19 /* toc2 */
37 #define GD_CMD_PLAY 20 /* playcd */
38 #define GD_CMD_PLAY2 21 /* playcd */
39 #define GD_CMD_PAUSE 22 /* No params */
40 #define GD_CMD_RELEASE 23 /* No params */
41 #define GD_CMD_INIT 24 /* No params */
42 #define GD_CMD_SEEK 27
43 #define GD_CMD_READ 28
44 #define GD_CMD_STOP 33 /* No params */
45 #define GD_CMD_GETSCD 34
46 #define GD_CMD_GETSES 35
48 #define GD_CMD_STATUS_NONE 0
49 #define GD_CMD_STATUS_ACTIVE 1
50 #define GD_CMD_STATUS_DONE 2
51 #define GD_CMD_STATUS_ABORT 3
52 #define GD_CMD_STATUS_ERROR 4
55 #define GD_ERROR_NO_DISC 2
56 #define GD_ERROR_DISC_CHANGE 6
57 #define GD_ERROR_SYSTEM 1
59 struct gdrom_toc2_params {
64 struct gdrom_readcd_params {
71 struct gdrom_playcd_params {
77 typedef union gdrom_cmd_params {
78 struct gdrom_toc2_params toc2;
79 struct gdrom_readcd_params readcd;
80 struct gdrom_playcd_params playcd;
81 } *gdrom_cmd_params_t;
86 typedef struct gdrom_queue_entry {
89 union gdrom_cmd_params params;
91 } *gdrom_queue_entry_t;
93 static struct gdrom_queue_entry gdrom_cmd_queue[COMMAND_QUEUE_LENGTH];
95 static struct bios_gdrom_status {
100 void bios_gdrom_init( void )
102 memset( &gdrom_cmd_queue, 0, sizeof(gdrom_cmd_queue) );
105 void bios_gdrom_run_command( gdrom_queue_entry_t cmd )
107 DEBUG( "BIOS GD command %d", cmd->cmd_code );
108 cdrom_error_t status = CDROM_ERROR_OK;
110 switch( cmd->cmd_code ) {
113 cmd->status = GD_CMD_STATUS_DONE;
116 ptr = mem_get_region( cmd->params.toc2.buffer );
117 status = gdrom_read_toc( ptr );
118 if( status == CDROM_ERROR_OK ) {
119 /* Convert data to little-endian */
120 struct gdrom_toc *toc = (struct gdrom_toc *)ptr;
121 for( unsigned i=0; i<99; i++ ) {
122 toc->track[i] = ntohl(toc->track[i]);
124 toc->first = ntohl(toc->first);
125 toc->last = ntohl(toc->last);
126 toc->leadout = ntohl(toc->leadout);
131 ptr = mem_get_region( cmd->params.readcd.buffer );
132 status = gdrom_read_cd( cmd->params.readcd.lba,
133 cmd->params.readcd.count, 0x28, ptr, NULL );
136 WARN( "Unknown BIOS GD command %d\n", cmd->cmd_code );
137 cmd->status = GD_CMD_STATUS_ERROR;
138 cmd->result[0] = GD_ERROR_SYSTEM;
144 cmd->status = GD_CMD_STATUS_DONE;
145 cmd->result[0] = GD_ERROR_OK;
147 case CDROM_ERROR_NODISC:
148 cmd->status = GD_CMD_STATUS_ERROR;
149 cmd->result[0] = GD_ERROR_NO_DISC;
152 cmd->status = GD_CMD_STATUS_ERROR;
153 cmd->result[0] = GD_ERROR_SYSTEM;
157 uint32_t bios_gdrom_enqueue( uint32_t cmd, sh4addr_t data )
160 for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
161 if( gdrom_cmd_queue[i].status != GD_CMD_STATUS_ACTIVE ) {
162 gdrom_cmd_queue[i].status = GD_CMD_STATUS_ACTIVE;
163 gdrom_cmd_queue[i].cmd_code = cmd;
167 mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.readcd, data, sizeof(struct gdrom_readcd_params) );
170 mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.toc2, data, sizeof(struct gdrom_toc2_params) );
174 mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.playcd, data, sizeof(struct gdrom_playcd_params) );
183 void bios_gdrom_run_queue( void )
186 for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
187 if( gdrom_cmd_queue[i].status == GD_CMD_STATUS_ACTIVE ) {
188 bios_gdrom_run_command( &gdrom_cmd_queue[i] );
193 gdrom_queue_entry_t bios_gdrom_get_command( uint32_t id )
195 if( id >= COMMAND_QUEUE_LENGTH ||
196 gdrom_cmd_queue[id].status == GD_CMD_STATUS_NONE )
198 return &gdrom_cmd_queue[id];
202 * Syscall list courtesy of Marcus Comstedt
205 void bios_syscall( uint32_t syscallid )
207 gdrom_queue_entry_t cmd;
209 switch( syscallid ) {
210 case 0xB0: /* sysinfo */
212 case 0xB4: /* Font */
214 case 0xB8: /* Flash */
216 case 0xBC: /* Misc/GD-Rom */
217 switch( sh4r.r[6] ) {
219 switch( sh4r.r[7] ) {
220 case 0: /* Send command */
221 sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], sh4r.r[5] );
223 case 1: /* Check command */
224 cmd = bios_gdrom_get_command( sh4r.r[4] );
226 sh4r.r[0] = GD_CMD_STATUS_NONE;
228 sh4r.r[0] = cmd->status;
229 if( cmd->status == GD_CMD_STATUS_ERROR &&
231 mem_copy_to_sh4( sh4r.r[5], (sh4ptr_t)&cmd->result, sizeof(cmd->result) );
235 case 2: /* Mainloop */
236 bios_gdrom_run_queue();
241 case 4: /* Drive status */
242 if( sh4r.r[4] != 0 ) {
243 mem_copy_to_sh4( sh4r.r[4], (sh4ptr_t)&bios_gdrom_status,
244 sizeof(bios_gdrom_status) );
248 case 8: /* Abort command */
249 cmd = bios_gdrom_get_command( sh4r.r[4] );
250 if( cmd == NULL || cmd->status != GD_CMD_STATUS_ACTIVE ) {
253 cmd->status = GD_CMD_STATUS_ABORT;
259 case 10: /* Set mode */
270 case 0xE0: /* Menu */
271 switch( sh4r.r[7] ) {
273 WARN( "Entering main program" );
276 WARN( "Program aborted to DC menu");
283 void bios_boot( uint32_t syscallid )
285 /* Initialize hardware */
286 /* Boot disc if present */
287 if( bios_boot_gdrom_disc() ) {
288 sh4r.pr = sh4r.pc; /* Set the syscall return address to the bootstrap entry */
294 void bios_install( void )
297 syscall_add_hook_vector( 0xB0, 0x8C0000B0, bios_syscall );
298 syscall_add_hook_vector( 0xB4, 0x8C0000B4, bios_syscall );
299 syscall_add_hook_vector( 0xB8, 0x8C0000B8, bios_syscall );
300 syscall_add_hook_vector( 0xBC, 0x8C0000BC, bios_syscall );
301 syscall_add_hook_vector( 0xE0, 0x8C0000E0, bios_syscall );
304 #define MIN_ISO_SECTORS 32
306 gboolean bios_boot_gdrom_disc( void )
308 cdrom_disc_t disc = gdrom_get_current_disc();
310 int status = gdrom_get_drive_status();
311 if( status == CDROM_DISC_NONE ) {
312 ERROR( "No disc in drive" );
316 /* Find the bootable data track (if present) */
317 cdrom_track_t track = gdrom_disc_get_boot_track(disc);
318 if( track == NULL ) {
319 ERROR( "Disc is not bootable" );
322 uint32_t lba = track->lba;
323 uint32_t sectors = cdrom_disc_get_track_size(disc,track);
324 if( sectors < MIN_ISO_SECTORS ) {
325 ERROR( "Disc is not bootable" );
328 /* Load the initial bootstrap into DC ram at 8c008000 */
329 size_t length = BOOTSTRAP_SIZE;
330 unsigned char *bootstrap = mem_get_region(BOOTSTRAP_LOAD_ADDR);
331 if( cdrom_disc_read_sectors( disc, track->lba, BOOTSTRAP_SIZE/2048,
332 CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, bootstrap, &length ) !=
334 ERROR( "Disc is not bootable" );
338 /* Check the magic just to be sure */
339 dc_bootstrap_head_t metadata = (dc_bootstrap_head_t)bootstrap;
340 if( memcmp( metadata->magic, BOOTSTRAP_MAGIC, BOOTSTRAP_MAGIC_SIZE ) != 0 ) {
341 ERROR( "Disc is not bootable (missing dreamcast bootstrap)" );
345 /* Get the initial program from the bootstrap (usually 1ST_READ.BIN) */
346 char program_name[17];
347 memcpy(program_name, metadata->boot_file, 16);
348 program_name[16] = '\0';
349 for( int i=15; i >= 0 && program_name[i] == ' '; i-- ) {
350 program_name[i] = '\0';
353 /* Bootstrap is good. Now find the program in the actual filesystem... */
354 isofs_reader_t iso = isofs_reader_new_from_track( disc, track, NULL );
356 ERROR( "Disc is not bootable" );
359 isofs_reader_dirent_t ent = isofs_reader_get_file( iso, program_name );
361 ERROR( "Disc is not bootable (initial program '%s' not found)", program_name );
362 isofs_reader_destroy(iso);
366 if( ent->size > (0x8D000000 - BINARY_LOAD_ADDR) ) {
367 /* Bootstrap isn't going to fit in memory. Complain and abort */
368 ERROR( "Disc is not bootable (initial program too large)" );
369 isofs_reader_destroy(iso);
372 unsigned char *program = mem_get_region(BINARY_LOAD_ADDR);
373 int program_sectors = (ent->size+2047)/2048;
374 if( disc->disc_type == CDROM_DISC_GDROM ) {
375 /* Load the binary directly into RAM */
376 if( isofs_reader_read_file( iso, ent, 0, ent->size, program ) !=
378 ERROR( "Disc is not bootable (failed to read initial program)\n" );
379 isofs_reader_destroy(iso);
382 asic_enable_ide_interface(TRUE);
384 /* Load the binary into a temp buffer */
385 unsigned char tmp[program_sectors*2048];
386 if( isofs_reader_read_file( iso, ent, 0, ent->size, tmp ) !=
388 ERROR( "Disc is not bootable (failed to read initial program)\n" );
389 isofs_reader_destroy(iso);
392 bootprogram_unscramble(program, tmp, ent->size);
393 asic_enable_ide_interface(FALSE);
395 isofs_reader_destroy(iso);
396 dreamcast_program_loaded( "", BOOTSTRAP_ENTRY_ADDR );
.