Search
lxdream.org :: lxdream/src/bios.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/bios.c
changeset 1099:566cdeb157ec
prev1065:bc1cc0c54917
next1100:50e702af9373
author nkeynes
date Wed Feb 10 18:16:19 2010 +1000 (10 years ago)
permissions -rw-r--r--
last change First draft of basic ISO9660 filesystem reader
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     4  * "Fake" BIOS functions, for operation without the actual BIOS.
     5  *
     6  * Copyright (c) 2005 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 "dreamcast.h"
    23 #include "bootstrap.h"
    24 #include "sh4/sh4.h"
    25 #include "drivers/cdrom/cdrom.h"
    26 #include "drivers/cdrom/isoread.h"
    27 #include "gdrom/gdrom.h"
    29 #define COMMAND_QUEUE_LENGTH 16
    31 /* TODO: Check if these are the real ATAPI command codes or not */
    32 #define GD_CMD_PIOREAD     16
    33 #define GD_CMD_DMAREAD     17
    34 #define GD_CMD_GETTOC      18
    35 #define GD_CMD_GETTOC2     19
    36 #define GD_CMD_PLAY        20
    37 #define GD_CMD_PLAY2       21
    38 #define GD_CMD_PAUSE       22
    39 #define GD_CMD_RELEASE     23
    40 #define GD_CMD_INIT        24
    41 #define GD_CMD_SEEK        27
    42 #define GD_CMD_READ        28
    43 #define GD_CMD_STOP        33
    44 #define GD_CMD_GETSCD      34
    45 #define GD_CMD_GETSES      35
    47 #define GD_CMD_STATUS_NONE 0
    48 #define GD_CMD_STATUS_ACTIVE 1
    49 #define GD_CMD_STATUS_DONE 2
    50 #define GD_CMD_STATUS_ABORT 3
    51 #define GD_CMD_STATUS_ERROR 4
    53 #define GD_ERROR_OK          0
    54 #define GD_ERROR_NO_DISC     2
    55 #define GD_ERROR_DISC_CHANGE 6
    56 #define GD_ERROR_SYSTEM      1
    59 typedef struct gdrom_command {
    60     int status;
    61     uint32_t cmd_code;
    62     sh4ptr_t data;
    63     uint32_t result[4];
    64 } *gdrom_command_t;
    66 static struct gdrom_command gdrom_cmd_queue[COMMAND_QUEUE_LENGTH];
    68 static struct bios_gdrom_status {
    69     uint32_t status;
    70     uint32_t disk_type;
    71 } bios_gdrom_status;
    73 void bios_gdrom_run_command( gdrom_command_t cmd )
    74 {
    75     DEBUG( "BIOS GD command %d", cmd->cmd_code );
    76     switch( cmd->cmd_code ) {
    77     case GD_CMD_INIT:
    78         /* *shrug* */
    79         cmd->status = GD_CMD_STATUS_DONE;
    80         break;
    81     default:
    82         cmd->status = GD_CMD_STATUS_ERROR;
    83         cmd->result[0] = GD_ERROR_SYSTEM;
    84         break;
    85     }
    86 }
    88 void bios_gdrom_init( void )
    89 {
    90     memset( &gdrom_cmd_queue, 0, sizeof(gdrom_cmd_queue) );
    91 }
    93 uint32_t bios_gdrom_enqueue( uint32_t cmd, sh4ptr_t ptr )
    94 {
    95     int i;
    96     for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
    97         if( gdrom_cmd_queue[i].status != GD_CMD_STATUS_ACTIVE ) {
    98             gdrom_cmd_queue[i].status = GD_CMD_STATUS_ACTIVE;
    99             gdrom_cmd_queue[i].cmd_code = cmd;
   100             gdrom_cmd_queue[i].data = ptr;
   101             return i;
   102         }
   103     }
   104     return -1;
   105 }
   107 void bios_gdrom_run_queue( void ) 
   108 {
   109     int i;
   110     for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
   111         if( gdrom_cmd_queue[i].status == GD_CMD_STATUS_ACTIVE ) {
   112             bios_gdrom_run_command( &gdrom_cmd_queue[i] );
   113         }
   114     }
   115 }
   117 gdrom_command_t bios_gdrom_get_command( uint32_t id )
   118 {
   119     if( id >= COMMAND_QUEUE_LENGTH ||
   120             gdrom_cmd_queue[id].status == GD_CMD_STATUS_NONE )
   121         return NULL;
   122     return &gdrom_cmd_queue[id];
   123 }
   125 /**
   126  * Syscall list courtesy of Marcus Comstedt
   127  */
   129 void bios_syscall( uint32_t syscallid )
   130 {
   131     gdrom_command_t cmd;
   133     switch( syscallid ) {
   134     case 0xB0: /* sysinfo */
   135         break;
   136     case 0xB4: /* Font */
   137         break;
   138     case 0xB8: /* Flash */
   139         break;
   140     case 0xBC: /* Misc/GD-Rom */
   141         switch( sh4r.r[6] ) {
   142         case 0: /* GD-Rom */
   143             switch( sh4r.r[7] ) {
   144             case 0: /* Send command */
   145                 if( sh4r.r[5] == 0 )
   146                     sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], NULL );
   147                 else
   148                     sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], mem_get_region(sh4r.r[5]) );
   149                 break;
   150             case 1:  /* Check command */
   151                 cmd = bios_gdrom_get_command( sh4r.r[4] );
   152                 if( cmd == NULL ) {
   153                     sh4r.r[0] = GD_CMD_STATUS_NONE;
   154                 } else {
   155                     sh4r.r[0] = cmd->status;
   156                     if( cmd->status == GD_CMD_STATUS_ERROR &&
   157                             sh4r.r[5] != 0 ) {
   158                         mem_copy_to_sh4( sh4r.r[5], (sh4ptr_t)&cmd->result, sizeof(cmd->result) );
   159                     }
   160                 }
   161                 break;
   162             case 2: /* Mainloop */
   163                 bios_gdrom_run_queue();
   164                 break;
   165             case 3: /* Init */
   166                 bios_gdrom_init();
   167                 break;
   168             case 4: /* Drive status */
   169                 if( sh4r.r[4] != 0 ) {
   170                     mem_copy_to_sh4( sh4r.r[4], (sh4ptr_t)&bios_gdrom_status, 
   171                             sizeof(bios_gdrom_status) );
   172                 }
   173                 sh4r.r[0] = 0;
   174                 break;
   175             case 8: /* Abort command */
   176                 cmd = bios_gdrom_get_command( sh4r.r[4] );
   177                 if( cmd == NULL || cmd->status != GD_CMD_STATUS_ACTIVE ) {
   178                     sh4r.r[0] = -1;
   179                 } else {
   180                     cmd->status = GD_CMD_STATUS_ABORT;
   181                     sh4r.r[0] = 0;
   182                 }
   183                 break;
   184             case 9: /* Reset */
   185                 break;
   186             case 10: /* Set mode */
   187                 sh4r.r[0] = 0;
   188                 break;
   189             }
   190             break;
   191             case -1: /* Misc */
   192             break;
   193             default: /* ??? */
   194                 break;
   195         }
   196         break;
   197         case 0xE0: /* Menu */
   198             switch( sh4r.r[7] ) {
   199             case 0:
   200                 WARN( "Entering main program" );
   201                 break;
   202             case 1:
   203                 WARN( "Program aborted to DC menu");
   204                 dreamcast_stop();
   205                 break;
   206             }
   207     }
   208 }
   210 void bios_boot( uint32_t syscallid )
   211 {
   212     /* Initialize hardware */
   213     /* Boot disc if present */
   214     bios_boot_gdrom_disc();
   215 }
   217 void bios_install( void ) 
   218 {
   219     bios_gdrom_init();
   220     syscall_add_hook_vector( 0xB0, 0x8C0000B0, bios_syscall );
   221     syscall_add_hook_vector( 0xB4, 0x8C0000B4, bios_syscall );
   222     syscall_add_hook_vector( 0xB8, 0x8C0000B8, bios_syscall );
   223     syscall_add_hook_vector( 0xBC, 0x8C0000BC, bios_syscall );
   224     syscall_add_hook_vector( 0xE0, 0x8C0000E0, bios_syscall );
   225 }
   227 #define MIN_ISO_SECTORS 32
   229 gboolean bios_boot_gdrom_disc( void )
   230 {
   231     cdrom_disc_t disc = gdrom_get_current_disc();
   233     int status = gdrom_get_drive_status();
   234     if( status == CDROM_DISC_NONE ) {
   235         ERROR( "No disc in drive" );
   236         return FALSE;
   237     }
   239     /* Find the bootable data track (if present) */
   240     cdrom_track_t track = gdrom_disc_get_boot_track(disc);
   241     if( track == NULL ) {
   242         ERROR( "Disc is not bootable" );
   243         return FALSE;
   244     }
   245     uint32_t lba = track->lba;
   246     uint32_t sectors = cdrom_disc_get_track_size(disc,track);
   247     if( sectors < MIN_ISO_SECTORS ) {
   248         ERROR( "Disc is not bootable" );
   249         return FALSE;
   250     }
   251     /* Load the initial bootstrap into DC ram at 8c008000 */
   252     size_t length = BOOTSTRAP_SIZE;
   253     unsigned char *bootstrap = mem_get_region(BOOTSTRAP_LOAD_ADDR);
   254     if( cdrom_disc_read_sectors( disc, track->lba, BOOTSTRAP_SIZE/2048,
   255             CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, bootstrap, &length ) !=
   256             CDROM_ERROR_OK ) {
   257         ERROR( "Disc is not bootable" );
   258         return FALSE;
   259     }
   261     /* Check the magic just to be sure */
   262     dc_bootstrap_head_t metadata = (dc_bootstrap_head_t)bootstrap;
   263     if( memcmp( metadata->magic, BOOTSTRAP_MAGIC, BOOTSTRAP_MAGIC_SIZE ) != 0 ) {
   264         ERROR( "Disc is not bootable (missing dreamcast bootstrap)" );
   265         return FALSE;
   266     }
   268     /* Get the initial program from the bootstrap (usually 1ST_READ.BIN) */
   269     char program_name[17];
   270     memcpy(program_name, metadata->boot_file, 16);
   271     program_name[16] = '\0';
   272     for( int i=15; i >= 0 && program_name[i] == ' '; i-- ) {
   273         program_name[i] = '\0';
   274     }
   276     /* Bootstrap is good. Now find the program in the actual filesystem... */
   277     isofs_reader_t iso = isofs_reader_new_from_track( disc, track, NULL );
   278     if( iso == NULL ) {
   279         ERROR( "Disc is not bootable" );
   280         return FALSE;
   281     }
   282     isofs_reader_dirent_t ent = isofs_reader_get_file( iso, program_name );
   283     if( ent == NULL ) {
   284         ERROR( "Disc is not bootable (initial program '%s' not found)", program_name );
   285         isofs_reader_destroy(iso);
   286         return FALSE;
   287     }
   289     if( ent->size > (0x8D000000 - BINARY_LOAD_ADDR) ) {
   290         /* Bootstrap isn't going to fit in memory. Complain and abort */
   291         ERROR( "Disc is not bootable (initial program too large)" );
   292         isofs_reader_destroy(iso);
   293         return FALSE;
   294     }
   295     unsigned char *program = mem_get_region(BINARY_LOAD_ADDR);
   296     int program_sectors = (ent->size+2047)/2048;
   297     if( disc->disc_type == CDROM_DISC_GDROM ) {
   298         /* Load the binary directly into RAM */
   299         if( isofs_reader_read_file( iso, ent, 0, ent->size, program ) !=
   300                 CDROM_ERROR_OK ) {
   301             ERROR( "Disc is not bootable (failed to read initial program)\n" );
   302             isofs_reader_destroy(iso);
   303             return FALSE;
   304         }
   305     } else {
   306         /* Load the binary into a temp buffer */
   307         unsigned char tmp[program_sectors*2048];
   308         if( isofs_reader_read_file( iso, ent, 0, ent->size, tmp ) !=
   309                 CDROM_ERROR_OK ) {
   310             ERROR( "Disc is not bootable (failed to read initial program)\n" );
   311             isofs_reader_destroy(iso);
   312             return FALSE;
   313         }
   314         bootprogram_unscramble(program, tmp, ent->size);
   315     }
   316     isofs_reader_destroy(iso);
   317     dreamcast_program_loaded( "", BOOTSTRAP_LOAD_ADDR );
   318 }
.