Search
lxdream.org :: lxdream/src/bios.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/bios.c
changeset 1100:50e702af9373
prev1099:566cdeb157ec
next1101:78e762cec843
author nkeynes
date Mon Feb 15 17:27:14 2010 +1000 (10 years ago)
permissions -rw-r--r--
last change Hook up the fake bios boot
Use fakebios if invoked with -b, or if there's no boot rom loaded
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/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
    54 #define GD_ERROR_OK          0
    55 #define GD_ERROR_NO_DISC     2
    56 #define GD_ERROR_DISC_CHANGE 6
    57 #define GD_ERROR_SYSTEM      1
    59 typedef union gdrom_cmd_params {
    60     struct gdrom_toc2_params {
    61         uint32_t session;
    62         sh4addr_t buffer;
    63     } toc2;
    65     struct gdrom_readcd_params {
    66         cdrom_lba_t sector;
    67         cdrom_count_t count;
    68         sh4addr_t buffer;
    69         uint32_t unknown;
    70     } readcd;
    72     struct gdrom_playcd_params {
    73         cdrom_lba_t start;
    74         cdrom_lba_t end;
    75         uint32_t repeat;
    76     } playcd;
    77 } *gdrom_cmd_params_t;
    82 typedef struct gdrom_queue_entry {
    83     int status;
    84     uint32_t cmd_code;
    85     sh4ptr_t data;
    86     uint32_t result[4];
    87 } *gdrom_queue_entry_t;
    89 static struct gdrom_queue_entry gdrom_cmd_queue[COMMAND_QUEUE_LENGTH];
    91 static struct bios_gdrom_status {
    92     uint32_t status;
    93     uint32_t disk_type;
    94 } bios_gdrom_status;
    96 void bios_gdrom_run_command( gdrom_queue_entry_t cmd )
    97 {
    98     DEBUG( "BIOS GD command %d", cmd->cmd_code );
    99     switch( cmd->cmd_code ) {
   100     case GD_CMD_INIT:
   101         /* *shrug* */
   102         cmd->status = GD_CMD_STATUS_DONE;
   103         break;
   104     default:
   105         cmd->status = GD_CMD_STATUS_ERROR;
   106         cmd->result[0] = GD_ERROR_SYSTEM;
   107         break;
   108     }
   109 }
   111 void bios_gdrom_init( void )
   112 {
   113     memset( &gdrom_cmd_queue, 0, sizeof(gdrom_cmd_queue) );
   114 }
   116 uint32_t bios_gdrom_enqueue( uint32_t cmd, sh4ptr_t ptr )
   117 {
   118     int i;
   119     for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
   120         if( gdrom_cmd_queue[i].status != GD_CMD_STATUS_ACTIVE ) {
   121             gdrom_cmd_queue[i].status = GD_CMD_STATUS_ACTIVE;
   122             gdrom_cmd_queue[i].cmd_code = cmd;
   123             gdrom_cmd_queue[i].data = ptr;
   124             return i;
   125         }
   126     }
   127     return -1;
   128 }
   130 void bios_gdrom_run_queue( void ) 
   131 {
   132     int i;
   133     for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
   134         if( gdrom_cmd_queue[i].status == GD_CMD_STATUS_ACTIVE ) {
   135             bios_gdrom_run_command( &gdrom_cmd_queue[i] );
   136         }
   137     }
   138 }
   140 gdrom_queue_entry_t bios_gdrom_get_command( uint32_t id )
   141 {
   142     if( id >= COMMAND_QUEUE_LENGTH ||
   143             gdrom_cmd_queue[id].status == GD_CMD_STATUS_NONE )
   144         return NULL;
   145     return &gdrom_cmd_queue[id];
   146 }
   148 /**
   149  * Syscall list courtesy of Marcus Comstedt
   150  */
   152 void bios_syscall( uint32_t syscallid )
   153 {
   154     gdrom_queue_entry_t cmd;
   156     switch( syscallid ) {
   157     case 0xB0: /* sysinfo */
   158         break;
   159     case 0xB4: /* Font */
   160         break;
   161     case 0xB8: /* Flash */
   162         break;
   163     case 0xBC: /* Misc/GD-Rom */
   164         switch( sh4r.r[6] ) {
   165         case 0: /* GD-Rom */
   166             switch( sh4r.r[7] ) {
   167             case 0: /* Send command */
   168                 if( sh4r.r[5] == 0 )
   169                     sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], NULL );
   170                 else
   171                     sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], mem_get_region(sh4r.r[5]) );
   172                 break;
   173             case 1:  /* Check command */
   174                 cmd = bios_gdrom_get_command( sh4r.r[4] );
   175                 if( cmd == NULL ) {
   176                     sh4r.r[0] = GD_CMD_STATUS_NONE;
   177                 } else {
   178                     sh4r.r[0] = cmd->status;
   179                     if( cmd->status == GD_CMD_STATUS_ERROR &&
   180                             sh4r.r[5] != 0 ) {
   181                         mem_copy_to_sh4( sh4r.r[5], (sh4ptr_t)&cmd->result, sizeof(cmd->result) );
   182                     }
   183                 }
   184                 break;
   185             case 2: /* Mainloop */
   186                 bios_gdrom_run_queue();
   187                 break;
   188             case 3: /* Init */
   189                 bios_gdrom_init();
   190                 break;
   191             case 4: /* Drive status */
   192                 if( sh4r.r[4] != 0 ) {
   193                     mem_copy_to_sh4( sh4r.r[4], (sh4ptr_t)&bios_gdrom_status, 
   194                             sizeof(bios_gdrom_status) );
   195                 }
   196                 sh4r.r[0] = 0;
   197                 break;
   198             case 8: /* Abort command */
   199                 cmd = bios_gdrom_get_command( sh4r.r[4] );
   200                 if( cmd == NULL || cmd->status != GD_CMD_STATUS_ACTIVE ) {
   201                     sh4r.r[0] = -1;
   202                 } else {
   203                     cmd->status = GD_CMD_STATUS_ABORT;
   204                     sh4r.r[0] = 0;
   205                 }
   206                 break;
   207             case 9: /* Reset */
   208                 break;
   209             case 10: /* Set mode */
   210                 sh4r.r[0] = 0;
   211                 break;
   212             }
   213             break;
   214             case -1: /* Misc */
   215             break;
   216             default: /* ??? */
   217                 break;
   218         }
   219         break;
   220         case 0xE0: /* Menu */
   221             switch( sh4r.r[7] ) {
   222             case 0:
   223                 WARN( "Entering main program" );
   224                 break;
   225             case 1:
   226                 WARN( "Program aborted to DC menu");
   227                 dreamcast_stop();
   228                 break;
   229             }
   230     }
   231 }
   233 void bios_boot( uint32_t syscallid )
   234 {
   235     /* Initialize hardware */
   236     /* Boot disc if present */
   237     if( bios_boot_gdrom_disc() ) {
   238         sh4r.pr = sh4r.pc; /* Set the syscall return address to the bootstrap entry */
   239     } else {
   240         dreamcast_stop();
   241     }
   242 }
   244 void bios_install( void ) 
   245 {
   246     bios_gdrom_init();
   247     syscall_add_hook_vector( 0xB0, 0x8C0000B0, bios_syscall );
   248     syscall_add_hook_vector( 0xB4, 0x8C0000B4, bios_syscall );
   249     syscall_add_hook_vector( 0xB8, 0x8C0000B8, bios_syscall );
   250     syscall_add_hook_vector( 0xBC, 0x8C0000BC, bios_syscall );
   251     syscall_add_hook_vector( 0xE0, 0x8C0000E0, bios_syscall );
   252 }
   254 #define MIN_ISO_SECTORS 32
   256 gboolean bios_boot_gdrom_disc( void )
   257 {
   258     cdrom_disc_t disc = gdrom_get_current_disc();
   260     int status = gdrom_get_drive_status();
   261     if( status == CDROM_DISC_NONE ) {
   262         ERROR( "No disc in drive" );
   263         return FALSE;
   264     }
   266     /* Find the bootable data track (if present) */
   267     cdrom_track_t track = gdrom_disc_get_boot_track(disc);
   268     if( track == NULL ) {
   269         ERROR( "Disc is not bootable" );
   270         return FALSE;
   271     }
   272     uint32_t lba = track->lba;
   273     uint32_t sectors = cdrom_disc_get_track_size(disc,track);
   274     if( sectors < MIN_ISO_SECTORS ) {
   275         ERROR( "Disc is not bootable" );
   276         return FALSE;
   277     }
   278     /* Load the initial bootstrap into DC ram at 8c008000 */
   279     size_t length = BOOTSTRAP_SIZE;
   280     unsigned char *bootstrap = mem_get_region(BOOTSTRAP_LOAD_ADDR);
   281     if( cdrom_disc_read_sectors( disc, track->lba, BOOTSTRAP_SIZE/2048,
   282             CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, bootstrap, &length ) !=
   283             CDROM_ERROR_OK ) {
   284         ERROR( "Disc is not bootable" );
   285         return FALSE;
   286     }
   288     /* Check the magic just to be sure */
   289     dc_bootstrap_head_t metadata = (dc_bootstrap_head_t)bootstrap;
   290     if( memcmp( metadata->magic, BOOTSTRAP_MAGIC, BOOTSTRAP_MAGIC_SIZE ) != 0 ) {
   291         ERROR( "Disc is not bootable (missing dreamcast bootstrap)" );
   292         return FALSE;
   293     }
   295     /* Get the initial program from the bootstrap (usually 1ST_READ.BIN) */
   296     char program_name[17];
   297     memcpy(program_name, metadata->boot_file, 16);
   298     program_name[16] = '\0';
   299     for( int i=15; i >= 0 && program_name[i] == ' '; i-- ) {
   300         program_name[i] = '\0';
   301     }
   303     /* Bootstrap is good. Now find the program in the actual filesystem... */
   304     isofs_reader_t iso = isofs_reader_new_from_track( disc, track, NULL );
   305     if( iso == NULL ) {
   306         ERROR( "Disc is not bootable" );
   307         return FALSE;
   308     }
   309     isofs_reader_dirent_t ent = isofs_reader_get_file( iso, program_name );
   310     if( ent == NULL ) {
   311         ERROR( "Disc is not bootable (initial program '%s' not found)", program_name );
   312         isofs_reader_destroy(iso);
   313         return FALSE;
   314     }
   316     if( ent->size > (0x8D000000 - BINARY_LOAD_ADDR) ) {
   317         /* Bootstrap isn't going to fit in memory. Complain and abort */
   318         ERROR( "Disc is not bootable (initial program too large)" );
   319         isofs_reader_destroy(iso);
   320         return FALSE;
   321     }
   322     unsigned char *program = mem_get_region(BINARY_LOAD_ADDR);
   323     int program_sectors = (ent->size+2047)/2048;
   324     if( disc->disc_type == CDROM_DISC_GDROM ) {
   325         /* Load the binary directly into RAM */
   326         if( isofs_reader_read_file( iso, ent, 0, ent->size, program ) !=
   327                 CDROM_ERROR_OK ) {
   328             ERROR( "Disc is not bootable (failed to read initial program)\n" );
   329             isofs_reader_destroy(iso);
   330             return FALSE;
   331         }
   332         asic_enable_ide_interface(TRUE);
   333     } else {
   334         /* Load the binary into a temp buffer */
   335         unsigned char tmp[program_sectors*2048];
   336         if( isofs_reader_read_file( iso, ent, 0, ent->size, tmp ) !=
   337                 CDROM_ERROR_OK ) {
   338             ERROR( "Disc is not bootable (failed to read initial program)\n" );
   339             isofs_reader_destroy(iso);
   340             return FALSE;
   341         }
   342         bootprogram_unscramble(program, tmp, ent->size);
   343         asic_enable_ide_interface(FALSE);
   344     }
   345     isofs_reader_destroy(iso);
   346     dreamcast_program_loaded( "", BOOTSTRAP_ENTRY_ADDR );
   347     return TRUE;
   348 }
.