Search
lxdream.org :: lxdream/src/bios.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/bios.c
changeset 1298:d0eb2307b847
prev1107:7b279d10f46f
author nkeynes
date Wed Feb 04 08:38:23 2015 +1000 (5 years ago)
permissions -rw-r--r--
last change Fix assorted compile warnings reported by Clang
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 sh4ptr_t bios_find_flash_config( sh4addr_t segment, uint32_t length )
   217 {
   218     sh4ptr_t start = mem_get_region(segment);
   219     sh4ptr_t p = start + 0x80;
   220     sh4ptr_t end = p + length;
   221     sh4ptr_t 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     sh4ptr_t flash_segment, flash_config;
   240     sh4ptr_t 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     DEBUG( "BIOS FLASHROM: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
   274     switch( sh4r.r[7] ) {
   275     case 0: /* FLASHROM_INFO */
   276         break;
   277     case 1: /* FLASHROM_READ */
   279         break;
   280     case 2: /* FLASHROM_WRITE */
   281         break;
   282     case 3: /* FLASHROM_DELETE */
   283         break;
   284     }
   285 }
   287 static void bios_romfont_vector( uint32_t syscallid )
   288 {
   289     DEBUG( "BIOS ROMFONT: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
   290     /* Not implemented */
   291 }
   293 static void bios_gdrom_vector( uint32_t syscallid )
   294 {
   295     gdrom_queue_entry_t cmd;
   297     DEBUG( "BIOS GDROM: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
   299     switch( sh4r.r[6] ) {
   300     case 0: /* GD-Rom */
   301         switch( sh4r.r[7] ) {
   302         case 0: /* Send command */
   303             sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], sh4r.r[5] );
   304             break;
   305         case 1:  /* Check command */
   306             cmd = bios_gdrom_get_command( sh4r.r[4] );
   307             if( cmd == NULL ) {
   308                 sh4r.r[0] = GD_CMD_STATUS_NONE;
   309             } else {
   310                 sh4r.r[0] = cmd->status;
   311                 if( cmd->status == GD_CMD_STATUS_ERROR &&
   312                         sh4r.r[5] != 0 ) {
   313                     mem_copy_to_sh4( sh4r.r[5], (sh4ptr_t)&cmd->result, sizeof(cmd->result) );
   314                 }
   315             }
   316             break;
   317         case 2: /* Mainloop */
   318             bios_gdrom_run_queue();
   319             break;
   320         case 3: /* Init */
   321             bios_gdrom_init();
   322             break;
   323         case 4: /* Drive status */
   324             if( sh4r.r[4] != 0 ) {
   325                 mem_copy_to_sh4( sh4r.r[4], (sh4ptr_t)&bios_gdrom_status,
   326                         sizeof(bios_gdrom_status) );
   327             }
   328             sh4r.r[0] = 0;
   329             break;
   330         case 8: /* Abort command */
   331             cmd = bios_gdrom_get_command( sh4r.r[4] );
   332             if( cmd == NULL || cmd->status != GD_CMD_STATUS_ACTIVE ) {
   333                 sh4r.r[0] = -1;
   334             } else {
   335                 cmd->status = GD_CMD_STATUS_ABORT;
   336                 sh4r.r[0] = 0;
   337             }
   338             break;
   339         case 9: /* Reset */
   340             break;
   341         case 10: /* Set mode */
   342             sh4r.r[0] = 0;
   343             break;
   344         }
   345         break;
   346         case -1: /* Misc */
   347         break;
   348         default: /* ??? */
   349             break;
   350     }
   351 }
   353 static void bios_menu_vector( uint32_t syscallid )
   354 {
   355     DEBUG( "BIOS MENU: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
   357     switch( sh4r.r[4] ) {
   358     case 0:
   359         WARN( "Entering main program" );
   360         break;
   361     case 1:
   362         WARN( "Program aborted to DC menu");
   363         dreamcast_stop();
   364         break;
   365     }
   366 }
   368 void bios_boot( uint32_t syscallid )
   369 {
   370     /* Initialize hardware */
   371     /* Boot disc if present */
   372     if( !bios_boot_gdrom_disc() ) {
   373         dreamcast_stop();
   374     }
   375 }
   377 void bios_install( void ) 
   378 {
   379     bios_gdrom_init();
   380     syscall_add_hook_vector( 0xB0, 0x8C0000B0, bios_sysinfo_vector );
   381     syscall_add_hook_vector( 0xB4, 0x8C0000B4, bios_romfont_vector );
   382     syscall_add_hook_vector( 0xB8, 0x8C0000B8, bios_flashrom_vector );
   383     syscall_add_hook_vector( 0xBC, 0x8C0000BC, bios_gdrom_vector );
   384     syscall_add_hook_vector( 0xE0, 0x8C0000E0, bios_menu_vector );
   385 }
   387 #define MIN_ISO_SECTORS 32
   389 static gboolean bios_load_ipl( cdrom_disc_t disc, cdrom_track_t track, const char *program_name,
   390                                unsigned char *buffer, gboolean unscramble )
   391 {
   392     gboolean rv = TRUE;
   394     IsoImageFilesystem *iso = iso_filesystem_new_from_track( disc, track, NULL );
   395     if( iso == NULL ) {
   396         ERROR( "Disc is not bootable (invalid ISO9660 filesystem)" );
   397         return FALSE;
   398     }
   399     IsoFileSource *file = NULL;
   400     int status = iso->get_by_path(iso, program_name, &file );
   401     if( status != 1 ) {
   402         ERROR( "Disc is not bootable (initial program '%s' not found)", program_name );
   403         iso_filesystem_unref(iso);
   404         return FALSE;
   405     }
   407     struct stat st;
   408     if( iso_file_source_stat(file, &st) == 1 ) {
   409         if( st.st_size > (0x8D000000 - BINARY_LOAD_ADDR) ) {
   410             ERROR( "Disc is not bootable (Initial program is too large to fit into memory)" );
   411             rv = FALSE;
   412         } else if( iso_file_source_open(file) == 1 ) {
   413             size_t len;
   414             if( unscramble ) {
   415                 unsigned char *tmp = g_malloc(st.st_size);
   416                 len = iso_file_source_read(file, tmp, st.st_size);
   417                 bootprogram_unscramble(buffer, tmp, st.st_size);
   418                 g_free(tmp);
   419             } else {
   420                 len = iso_file_source_read(file, buffer, st.st_size);
   421             }
   423             if( len != st.st_size ) {
   424                 ERROR( "Disc is not bootable (Unable to read initial program '%s')", program_name );
   425                 rv = FALSE;
   426             }
   427             iso_file_source_close(file);
   428         }
   429     } else {
   430         ERROR( "Disc is not bootable (Unable to get size of initial program '%s')", program_name );
   431         rv = FALSE;
   432     }
   434     iso_file_source_unref(file);
   435     iso_filesystem_unref(iso);
   436     return rv;
   437 }
   439 gboolean bios_boot_gdrom_disc( void )
   440 {
   441     cdrom_disc_t disc = gdrom_get_current_disc();
   443     int status = gdrom_get_drive_status();
   444     if( status == CDROM_DISC_NONE ) {
   445         ERROR( "No disc in drive" );
   446         return FALSE;
   447     }
   449     /* Find the bootable data track (if present) */
   450     cdrom_track_t track = gdrom_disc_get_boot_track(disc);
   451     if( track == NULL ) {
   452         ERROR( "Disc is not bootable" );
   453         return FALSE;
   454     }
   455     uint32_t sectors = cdrom_disc_get_track_size(disc,track);
   456     if( sectors < MIN_ISO_SECTORS ) {
   457         ERROR( "Disc is not bootable" );
   458         return FALSE;
   459     }
   460     /* Load the initial bootstrap into DC ram at 8c008000 */
   461     size_t length = BOOTSTRAP_SIZE;
   462     unsigned char *bootstrap = mem_get_region(BOOTSTRAP_LOAD_ADDR);
   463     if( cdrom_disc_read_sectors( disc, track->lba, BOOTSTRAP_SIZE/2048,
   464             CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, bootstrap, &length ) !=
   465             CDROM_ERROR_OK ) {
   466         ERROR( "Disc is not bootable" );
   467         return FALSE;
   468     }
   470     /* Check the magic just to be sure */
   471     dc_bootstrap_head_t metadata = (dc_bootstrap_head_t)bootstrap;
   472     if( memcmp( metadata->magic, BOOTSTRAP_MAGIC, BOOTSTRAP_MAGIC_SIZE ) != 0 ) {
   473         ERROR( "Disc is not bootable (missing dreamcast bootstrap)" );
   474         return FALSE;
   475     }
   477     /* Get the initial program from the bootstrap (usually 1ST_READ.BIN) */
   478     char program_name[18] = "/";
   479     memcpy(program_name+1, metadata->boot_file, 16);
   480     program_name[17] = '\0';
   481     for( int i=16; i >= 0 && program_name[i] == ' '; i-- ) {
   482         program_name[i] = '\0';
   483     }
   485     /* Bootstrap is good. Now find the program in the actual filesystem... */
   486     unsigned char *program = mem_get_region(BINARY_LOAD_ADDR);
   487     gboolean isGDROM = (disc->disc_type == CDROM_DISC_GDROM );
   488     if( !bios_load_ipl( disc, track, program_name, program, !isGDROM ) )
   489         return FALSE;
   490     asic_enable_ide_interface(isGDROM);
   491     dreamcast_program_loaded( "", BOOTSTRAP_ENTRY_ADDR );
   492     return TRUE;
   493 }
.