Search
lxdream.org :: lxdream/src/bios.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/bios.c
changeset 1107:7b279d10f46f
prev1103:de9ad2c0cf56
next1298:d0eb2307b847
author nkeynes
date Thu Feb 23 08:41:07 2012 +1000 (12 years ago)
permissions -rw-r--r--
last change rename BUILD_SHARED conditional flag to BUILD_PLUGINS, for clarity
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 }
.