nkeynes@87 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
nkeynes@87 | 3 | *
|
nkeynes@87 | 4 | * "Fake" BIOS functions, for operation without the actual BIOS.
|
nkeynes@87 | 5 | *
|
nkeynes@1100 | 6 | * Copyright (c) 2005-2010 Nathan Keynes.
|
nkeynes@87 | 7 | *
|
nkeynes@87 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@87 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@87 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@87 | 11 | * (at your option) any later version.
|
nkeynes@87 | 12 | *
|
nkeynes@87 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@87 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@87 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@87 | 16 | * GNU General Public License for more details.
|
nkeynes@87 | 17 | */
|
nkeynes@87 | 18 |
|
nkeynes@87 | 19 | #include "dream.h"
|
nkeynes@87 | 20 | #include "mem.h"
|
nkeynes@102 | 21 | #include "syscall.h"
|
nkeynes@1100 | 22 | #include "asic.h"
|
nkeynes@422 | 23 | #include "dreamcast.h"
|
nkeynes@1099 | 24 | #include "bootstrap.h"
|
nkeynes@564 | 25 | #include "sh4/sh4.h"
|
nkeynes@1099 | 26 | #include "drivers/cdrom/cdrom.h"
|
nkeynes@1107 | 27 | #include "drivers/cdrom/isofs.h"
|
nkeynes@1099 | 28 | #include "gdrom/gdrom.h"
|
nkeynes@87 | 29 |
|
nkeynes@1103 | 30 | gboolean bios_boot_gdrom_disc( void );
|
nkeynes@1103 | 31 |
|
nkeynes@1100 | 32 | /* Definitions from KOS */
|
nkeynes@87 | 33 | #define COMMAND_QUEUE_LENGTH 16
|
nkeynes@87 | 34 |
|
nkeynes@1100 | 35 | #define GD_CMD_PIOREAD 16 /* readcd */
|
nkeynes@1100 | 36 | #define GD_CMD_DMAREAD 17 /* readcd */
|
nkeynes@87 | 37 | #define GD_CMD_GETTOC 18
|
nkeynes@1100 | 38 | #define GD_CMD_GETTOC2 19 /* toc2 */
|
nkeynes@1100 | 39 | #define GD_CMD_PLAY 20 /* playcd */
|
nkeynes@1100 | 40 | #define GD_CMD_PLAY2 21 /* playcd */
|
nkeynes@1100 | 41 | #define GD_CMD_PAUSE 22 /* No params */
|
nkeynes@1100 | 42 | #define GD_CMD_RELEASE 23 /* No params */
|
nkeynes@1100 | 43 | #define GD_CMD_INIT 24 /* No params */
|
nkeynes@87 | 44 | #define GD_CMD_SEEK 27
|
nkeynes@87 | 45 | #define GD_CMD_READ 28
|
nkeynes@1100 | 46 | #define GD_CMD_STOP 33 /* No params */
|
nkeynes@87 | 47 | #define GD_CMD_GETSCD 34
|
nkeynes@87 | 48 | #define GD_CMD_GETSES 35
|
nkeynes@87 | 49 |
|
nkeynes@1100 | 50 | #define GD_CMD_STATUS_NONE 0
|
nkeynes@87 | 51 | #define GD_CMD_STATUS_ACTIVE 1
|
nkeynes@1100 | 52 | #define GD_CMD_STATUS_DONE 2
|
nkeynes@1100 | 53 | #define GD_CMD_STATUS_ABORT 3
|
nkeynes@1100 | 54 | #define GD_CMD_STATUS_ERROR 4
|
nkeynes@87 | 55 |
|
nkeynes@87 | 56 | #define GD_ERROR_OK 0
|
nkeynes@87 | 57 | #define GD_ERROR_NO_DISC 2
|
nkeynes@87 | 58 | #define GD_ERROR_DISC_CHANGE 6
|
nkeynes@87 | 59 | #define GD_ERROR_SYSTEM 1
|
nkeynes@87 | 60 |
|
nkeynes@1101 | 61 | struct gdrom_toc2_params {
|
nkeynes@1101 | 62 | uint32_t session;
|
nkeynes@1101 | 63 | sh4addr_t buffer;
|
nkeynes@1101 | 64 | };
|
nkeynes@1101 | 65 |
|
nkeynes@1101 | 66 | struct gdrom_readcd_params {
|
nkeynes@1101 | 67 | cdrom_lba_t lba;
|
nkeynes@1101 | 68 | cdrom_count_t count;
|
nkeynes@1101 | 69 | sh4addr_t buffer;
|
nkeynes@1101 | 70 | uint32_t unknown;
|
nkeynes@1101 | 71 | };
|
nkeynes@1101 | 72 |
|
nkeynes@1101 | 73 | struct gdrom_playcd_params {
|
nkeynes@1101 | 74 | cdrom_lba_t start;
|
nkeynes@1101 | 75 | cdrom_lba_t end;
|
nkeynes@1101 | 76 | uint32_t repeat;
|
nkeynes@1101 | 77 | };
|
nkeynes@1101 | 78 |
|
nkeynes@1100 | 79 | typedef union gdrom_cmd_params {
|
nkeynes@1101 | 80 | struct gdrom_toc2_params toc2;
|
nkeynes@1101 | 81 | struct gdrom_readcd_params readcd;
|
nkeynes@1101 | 82 | struct gdrom_playcd_params playcd;
|
nkeynes@1100 | 83 | } *gdrom_cmd_params_t;
|
nkeynes@1100 | 84 |
|
nkeynes@1100 | 85 |
|
nkeynes@1100 | 86 |
|
nkeynes@1100 | 87 |
|
nkeynes@1100 | 88 | typedef struct gdrom_queue_entry {
|
nkeynes@87 | 89 | int status;
|
nkeynes@87 | 90 | uint32_t cmd_code;
|
nkeynes@1101 | 91 | union gdrom_cmd_params params;
|
nkeynes@87 | 92 | uint32_t result[4];
|
nkeynes@1100 | 93 | } *gdrom_queue_entry_t;
|
nkeynes@87 | 94 |
|
nkeynes@1100 | 95 | static struct gdrom_queue_entry gdrom_cmd_queue[COMMAND_QUEUE_LENGTH];
|
nkeynes@87 | 96 |
|
nkeynes@87 | 97 | static struct bios_gdrom_status {
|
nkeynes@87 | 98 | uint32_t status;
|
nkeynes@87 | 99 | uint32_t disk_type;
|
nkeynes@87 | 100 | } bios_gdrom_status;
|
nkeynes@87 | 101 |
|
nkeynes@1101 | 102 | void bios_gdrom_init( void )
|
nkeynes@1101 | 103 | {
|
nkeynes@1101 | 104 | memset( &gdrom_cmd_queue, 0, sizeof(gdrom_cmd_queue) );
|
nkeynes@1101 | 105 | }
|
nkeynes@1101 | 106 |
|
nkeynes@1100 | 107 | void bios_gdrom_run_command( gdrom_queue_entry_t cmd )
|
nkeynes@87 | 108 | {
|
nkeynes@87 | 109 | DEBUG( "BIOS GD command %d", cmd->cmd_code );
|
nkeynes@1101 | 110 | cdrom_error_t status = CDROM_ERROR_OK;
|
nkeynes@1101 | 111 | sh4ptr_t ptr;
|
nkeynes@87 | 112 | switch( cmd->cmd_code ) {
|
nkeynes@87 | 113 | case GD_CMD_INIT:
|
nkeynes@736 | 114 | /* *shrug* */
|
nkeynes@736 | 115 | cmd->status = GD_CMD_STATUS_DONE;
|
nkeynes@736 | 116 | break;
|
nkeynes@1101 | 117 | case GD_CMD_GETTOC2:
|
nkeynes@1101 | 118 | ptr = mem_get_region( cmd->params.toc2.buffer );
|
nkeynes@1101 | 119 | status = gdrom_read_toc( ptr );
|
nkeynes@1101 | 120 | if( status == CDROM_ERROR_OK ) {
|
nkeynes@1101 | 121 | /* Convert data to little-endian */
|
nkeynes@1101 | 122 | struct gdrom_toc *toc = (struct gdrom_toc *)ptr;
|
nkeynes@1101 | 123 | for( unsigned i=0; i<99; i++ ) {
|
nkeynes@1101 | 124 | toc->track[i] = ntohl(toc->track[i]);
|
nkeynes@1101 | 125 | }
|
nkeynes@1101 | 126 | toc->first = ntohl(toc->first);
|
nkeynes@1101 | 127 | toc->last = ntohl(toc->last);
|
nkeynes@1101 | 128 | toc->leadout = ntohl(toc->leadout);
|
nkeynes@1101 | 129 | }
|
nkeynes@1101 | 130 | break;
|
nkeynes@1101 | 131 | case GD_CMD_PIOREAD:
|
nkeynes@1101 | 132 | case GD_CMD_DMAREAD:
|
nkeynes@1101 | 133 | ptr = mem_get_region( cmd->params.readcd.buffer );
|
nkeynes@1101 | 134 | status = gdrom_read_cd( cmd->params.readcd.lba,
|
nkeynes@1101 | 135 | cmd->params.readcd.count, 0x28, ptr, NULL );
|
nkeynes@1101 | 136 | break;
|
nkeynes@1101 | 137 | default:
|
nkeynes@1101 | 138 | WARN( "Unknown BIOS GD command %d\n", cmd->cmd_code );
|
nkeynes@1101 | 139 | cmd->status = GD_CMD_STATUS_ERROR;
|
nkeynes@1101 | 140 | cmd->result[0] = GD_ERROR_SYSTEM;
|
nkeynes@1101 | 141 | return;
|
nkeynes@1101 | 142 | }
|
nkeynes@1101 | 143 |
|
nkeynes@1101 | 144 | switch( status ) {
|
nkeynes@1101 | 145 | case CDROM_ERROR_OK:
|
nkeynes@1101 | 146 | cmd->status = GD_CMD_STATUS_DONE;
|
nkeynes@1101 | 147 | cmd->result[0] = GD_ERROR_OK;
|
nkeynes@1101 | 148 | break;
|
nkeynes@1101 | 149 | case CDROM_ERROR_NODISC:
|
nkeynes@1101 | 150 | cmd->status = GD_CMD_STATUS_ERROR;
|
nkeynes@1101 | 151 | cmd->result[0] = GD_ERROR_NO_DISC;
|
nkeynes@1101 | 152 | break;
|
nkeynes@87 | 153 | default:
|
nkeynes@736 | 154 | cmd->status = GD_CMD_STATUS_ERROR;
|
nkeynes@736 | 155 | cmd->result[0] = GD_ERROR_SYSTEM;
|
nkeynes@87 | 156 | }
|
nkeynes@87 | 157 | }
|
nkeynes@87 | 158 |
|
nkeynes@1101 | 159 | uint32_t bios_gdrom_enqueue( uint32_t cmd, sh4addr_t data )
|
nkeynes@87 | 160 | {
|
nkeynes@87 | 161 | int i;
|
nkeynes@87 | 162 | for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
|
nkeynes@736 | 163 | if( gdrom_cmd_queue[i].status != GD_CMD_STATUS_ACTIVE ) {
|
nkeynes@736 | 164 | gdrom_cmd_queue[i].status = GD_CMD_STATUS_ACTIVE;
|
nkeynes@736 | 165 | gdrom_cmd_queue[i].cmd_code = cmd;
|
nkeynes@1101 | 166 | switch( cmd ) {
|
nkeynes@1101 | 167 | case GD_CMD_PIOREAD:
|
nkeynes@1101 | 168 | case GD_CMD_DMAREAD:
|
nkeynes@1101 | 169 | mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.readcd, data, sizeof(struct gdrom_readcd_params) );
|
nkeynes@1101 | 170 | break;
|
nkeynes@1101 | 171 | case GD_CMD_GETTOC2:
|
nkeynes@1101 | 172 | mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.toc2, data, sizeof(struct gdrom_toc2_params) );
|
nkeynes@1101 | 173 | break;
|
nkeynes@1101 | 174 | case GD_CMD_PLAY:
|
nkeynes@1101 | 175 | case GD_CMD_PLAY2:
|
nkeynes@1101 | 176 | mem_copy_from_sh4( (unsigned char *)&gdrom_cmd_queue[i].params.playcd, data, sizeof(struct gdrom_playcd_params) );
|
nkeynes@1101 | 177 | break;
|
nkeynes@1101 | 178 | }
|
nkeynes@736 | 179 | return i;
|
nkeynes@736 | 180 | }
|
nkeynes@87 | 181 | }
|
nkeynes@87 | 182 | return -1;
|
nkeynes@87 | 183 | }
|
nkeynes@87 | 184 |
|
nkeynes@87 | 185 | void bios_gdrom_run_queue( void )
|
nkeynes@87 | 186 | {
|
nkeynes@87 | 187 | int i;
|
nkeynes@87 | 188 | for( i=0; i<COMMAND_QUEUE_LENGTH; i++ ) {
|
nkeynes@736 | 189 | if( gdrom_cmd_queue[i].status == GD_CMD_STATUS_ACTIVE ) {
|
nkeynes@736 | 190 | bios_gdrom_run_command( &gdrom_cmd_queue[i] );
|
nkeynes@736 | 191 | }
|
nkeynes@87 | 192 | }
|
nkeynes@87 | 193 | }
|
nkeynes@87 | 194 |
|
nkeynes@1100 | 195 | gdrom_queue_entry_t bios_gdrom_get_command( uint32_t id )
|
nkeynes@87 | 196 | {
|
nkeynes@87 | 197 | if( id >= COMMAND_QUEUE_LENGTH ||
|
nkeynes@736 | 198 | gdrom_cmd_queue[id].status == GD_CMD_STATUS_NONE )
|
nkeynes@736 | 199 | return NULL;
|
nkeynes@87 | 200 | return &gdrom_cmd_queue[id];
|
nkeynes@87 | 201 | }
|
nkeynes@87 | 202 |
|
nkeynes@87 | 203 | /**
|
nkeynes@1102 | 204 | * Address of the system information block (in the flash rom). Also repeats
|
nkeynes@1102 | 205 | * at FLASH_SYSINFO_SEGMENT+0xA0
|
nkeynes@87 | 206 | */
|
nkeynes@1102 | 207 | #define FLASH_SYSINFO_SEGMENT 0x0021a000
|
nkeynes@1102 | 208 | #define FLASH_CONFIG_SEGMENT 0x0021c000
|
nkeynes@1102 | 209 | #define FLASH_CONFIG_LENGTH 0x00004000
|
nkeynes@1102 | 210 | #define FLASH_PARTITION_MAGIC "KATANA_FLASH____"
|
nkeynes@87 | 211 |
|
nkeynes@1102 | 212 | /**
|
nkeynes@1102 | 213 | * Locate the active config block. FIXME: This isn't completely correct, but it works
|
nkeynes@1102 | 214 | * under at least some circumstances.
|
nkeynes@1102 | 215 | */
|
nkeynes@1102 | 216 | static char *bios_find_flash_config( sh4addr_t segment, uint32_t length )
|
nkeynes@1102 | 217 | {
|
nkeynes@1102 | 218 | char *start = mem_get_region(segment);
|
nkeynes@1102 | 219 | char *p = start + 0x80;
|
nkeynes@1102 | 220 | char *end = p + length;
|
nkeynes@1102 | 221 | char *result = NULL;
|
nkeynes@1102 | 222 |
|
nkeynes@1102 | 223 | if( memcmp( start, FLASH_PARTITION_MAGIC, 16 ) != 0 )
|
nkeynes@1102 | 224 | return NULL; /* Missing magic */
|
nkeynes@1102 | 225 | while( p < end ) {
|
nkeynes@1102 | 226 | if( p[0] == 0x05 && p[1] == 0 ) {
|
nkeynes@1102 | 227 | result = p;
|
nkeynes@1102 | 228 | }
|
nkeynes@1102 | 229 | p += 0x40;
|
nkeynes@1102 | 230 | }
|
nkeynes@1102 | 231 | return result;
|
nkeynes@1102 | 232 | }
|
nkeynes@1102 | 233 |
|
nkeynes@1102 | 234 | /**
|
nkeynes@1102 | 235 | * Syscall information courtesy of Marcus Comstedt
|
nkeynes@1102 | 236 | */
|
nkeynes@1102 | 237 | static void bios_sysinfo_vector( uint32_t syscallid )
|
nkeynes@1102 | 238 | {
|
nkeynes@1102 | 239 | char *flash_segment, *flash_config;
|
nkeynes@1102 | 240 | char *dest;
|
nkeynes@1102 | 241 | DEBUG( "BIOS SYSINFO: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
|
nkeynes@1102 | 242 |
|
nkeynes@1102 | 243 | switch( sh4r.r[7] ) {
|
nkeynes@1102 | 244 | case 0: /* SYSINFO_INIT */
|
nkeynes@1102 | 245 | /* Initialize the region 8c000068 .. 8c00007f from the flash rom
|
nkeynes@1102 | 246 | * uint64_t system_id;
|
nkeynes@1102 | 247 | * char [5] system_props;
|
nkeynes@1102 | 248 | * char [3] zero_pad (?)
|
nkeynes@1102 | 249 | * char [8] settings;
|
nkeynes@1102 | 250 | **/
|
nkeynes@1102 | 251 | flash_segment = mem_get_region(FLASH_SYSINFO_SEGMENT);
|
nkeynes@1102 | 252 | flash_config = bios_find_flash_config(FLASH_CONFIG_SEGMENT,FLASH_CONFIG_LENGTH);
|
nkeynes@1102 | 253 | dest = mem_get_region( 0x8c000068 );
|
nkeynes@1102 | 254 | memset( dest, 0, 24 );
|
nkeynes@1102 | 255 | memcpy( dest, flash_segment + 0x56, 8 );
|
nkeynes@1102 | 256 | memcpy( dest + 8, flash_segment, 5 );
|
nkeynes@1102 | 257 | if( flash_config != NULL ) {
|
nkeynes@1102 | 258 | memcpy( dest+16, flash_config+2, 8 );
|
nkeynes@1102 | 259 | }
|
nkeynes@1102 | 260 | break;
|
nkeynes@1102 | 261 | case 2: /* SYSINFO_ICON */
|
nkeynes@1102 | 262 | /* Not supported yet */
|
nkeynes@1102 | 263 | break;
|
nkeynes@1102 | 264 | case 3: /* SYSINFO_ID */
|
nkeynes@1102 | 265 | sh4r.r[0] = 0x8c000068;
|
nkeynes@1102 | 266 | break;
|
nkeynes@1102 | 267 | }
|
nkeynes@1102 | 268 | }
|
nkeynes@1102 | 269 |
|
nkeynes@1102 | 270 | static void bios_flashrom_vector( uint32_t syscallid )
|
nkeynes@1102 | 271 | {
|
nkeynes@1102 | 272 | char *dest;
|
nkeynes@1102 | 273 | DEBUG( "BIOS FLASHROM: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
|
nkeynes@1102 | 274 |
|
nkeynes@1102 | 275 | switch( sh4r.r[7] ) {
|
nkeynes@1102 | 276 | case 0: /* FLASHROM_INFO */
|
nkeynes@1102 | 277 | break;
|
nkeynes@1102 | 278 | case 1: /* FLASHROM_READ */
|
nkeynes@1102 | 279 |
|
nkeynes@1102 | 280 | break;
|
nkeynes@1102 | 281 | case 2: /* FLASHROM_WRITE */
|
nkeynes@1102 | 282 | break;
|
nkeynes@1102 | 283 | case 3: /* FLASHROM_DELETE */
|
nkeynes@1102 | 284 | break;
|
nkeynes@1102 | 285 | }
|
nkeynes@1102 | 286 | }
|
nkeynes@1102 | 287 |
|
nkeynes@1102 | 288 | static void bios_romfont_vector( uint32_t syscallid )
|
nkeynes@1102 | 289 | {
|
nkeynes@1102 | 290 | DEBUG( "BIOS ROMFONT: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
|
nkeynes@1102 | 291 | /* Not implemented */
|
nkeynes@1102 | 292 | }
|
nkeynes@1102 | 293 |
|
nkeynes@1102 | 294 | static void bios_gdrom_vector( uint32_t syscallid )
|
nkeynes@87 | 295 | {
|
nkeynes@1100 | 296 | gdrom_queue_entry_t cmd;
|
nkeynes@87 | 297 |
|
nkeynes@1102 | 298 | DEBUG( "BIOS GDROM: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
|
nkeynes@1102 | 299 |
|
nkeynes@1102 | 300 | switch( sh4r.r[6] ) {
|
nkeynes@1102 | 301 | case 0: /* GD-Rom */
|
nkeynes@1102 | 302 | switch( sh4r.r[7] ) {
|
nkeynes@1102 | 303 | case 0: /* Send command */
|
nkeynes@1102 | 304 | sh4r.r[0] = bios_gdrom_enqueue( sh4r.r[4], sh4r.r[5] );
|
nkeynes@1102 | 305 | break;
|
nkeynes@1102 | 306 | case 1: /* Check command */
|
nkeynes@1102 | 307 | cmd = bios_gdrom_get_command( sh4r.r[4] );
|
nkeynes@1102 | 308 | if( cmd == NULL ) {
|
nkeynes@1102 | 309 | sh4r.r[0] = GD_CMD_STATUS_NONE;
|
nkeynes@1102 | 310 | } else {
|
nkeynes@1102 | 311 | sh4r.r[0] = cmd->status;
|
nkeynes@1102 | 312 | if( cmd->status == GD_CMD_STATUS_ERROR &&
|
nkeynes@1102 | 313 | sh4r.r[5] != 0 ) {
|
nkeynes@1102 | 314 | mem_copy_to_sh4( sh4r.r[5], (sh4ptr_t)&cmd->result, sizeof(cmd->result) );
|
nkeynes@736 | 315 | }
|
nkeynes@736 | 316 | }
|
nkeynes@736 | 317 | break;
|
nkeynes@1102 | 318 | case 2: /* Mainloop */
|
nkeynes@1102 | 319 | bios_gdrom_run_queue();
|
nkeynes@736 | 320 | break;
|
nkeynes@1102 | 321 | case 3: /* Init */
|
nkeynes@1102 | 322 | bios_gdrom_init();
|
nkeynes@1102 | 323 | break;
|
nkeynes@1102 | 324 | case 4: /* Drive status */
|
nkeynes@1102 | 325 | if( sh4r.r[4] != 0 ) {
|
nkeynes@1102 | 326 | mem_copy_to_sh4( sh4r.r[4], (sh4ptr_t)&bios_gdrom_status,
|
nkeynes@1102 | 327 | sizeof(bios_gdrom_status) );
|
nkeynes@1102 | 328 | }
|
nkeynes@1102 | 329 | sh4r.r[0] = 0;
|
nkeynes@1102 | 330 | break;
|
nkeynes@1102 | 331 | case 8: /* Abort command */
|
nkeynes@1102 | 332 | cmd = bios_gdrom_get_command( sh4r.r[4] );
|
nkeynes@1102 | 333 | if( cmd == NULL || cmd->status != GD_CMD_STATUS_ACTIVE ) {
|
nkeynes@1102 | 334 | sh4r.r[0] = -1;
|
nkeynes@1102 | 335 | } else {
|
nkeynes@1102 | 336 | cmd->status = GD_CMD_STATUS_ABORT;
|
nkeynes@1102 | 337 | sh4r.r[0] = 0;
|
nkeynes@1102 | 338 | }
|
nkeynes@1102 | 339 | break;
|
nkeynes@1102 | 340 | case 9: /* Reset */
|
nkeynes@1102 | 341 | break;
|
nkeynes@1102 | 342 | case 10: /* Set mode */
|
nkeynes@1102 | 343 | sh4r.r[0] = 0;
|
nkeynes@1102 | 344 | break;
|
nkeynes@736 | 345 | }
|
nkeynes@736 | 346 | break;
|
nkeynes@1102 | 347 | case -1: /* Misc */
|
nkeynes@1102 | 348 | break;
|
nkeynes@1102 | 349 | default: /* ??? */
|
nkeynes@1102 | 350 | break;
|
nkeynes@1102 | 351 | }
|
nkeynes@1102 | 352 | }
|
nkeynes@1102 | 353 |
|
nkeynes@1102 | 354 | static void bios_menu_vector( uint32_t syscallid )
|
nkeynes@1102 | 355 | {
|
nkeynes@1102 | 356 | DEBUG( "BIOS MENU: r4 = %08X, r5 = %08X, r6 = %08x, r7= %08X", sh4r.r[4], sh4r.r[5], sh4r.r[6], sh4r.r[7] );
|
nkeynes@1102 | 357 |
|
nkeynes@1102 | 358 | switch( sh4r.r[4] ) {
|
nkeynes@1102 | 359 | case 0:
|
nkeynes@1102 | 360 | WARN( "Entering main program" );
|
nkeynes@1102 | 361 | break;
|
nkeynes@1102 | 362 | case 1:
|
nkeynes@1102 | 363 | WARN( "Program aborted to DC menu");
|
nkeynes@1102 | 364 | dreamcast_stop();
|
nkeynes@1102 | 365 | break;
|
nkeynes@87 | 366 | }
|
nkeynes@87 | 367 | }
|
nkeynes@102 | 368 |
|
nkeynes@1099 | 369 | void bios_boot( uint32_t syscallid )
|
nkeynes@1099 | 370 | {
|
nkeynes@1099 | 371 | /* Initialize hardware */
|
nkeynes@1099 | 372 | /* Boot disc if present */
|
nkeynes@1103 | 373 | if( !bios_boot_gdrom_disc() ) {
|
nkeynes@1100 | 374 | dreamcast_stop();
|
nkeynes@1100 | 375 | }
|
nkeynes@1099 | 376 | }
|
nkeynes@1099 | 377 |
|
nkeynes@102 | 378 | void bios_install( void )
|
nkeynes@102 | 379 | {
|
nkeynes@102 | 380 | bios_gdrom_init();
|
nkeynes@1102 | 381 | syscall_add_hook_vector( 0xB0, 0x8C0000B0, bios_sysinfo_vector );
|
nkeynes@1102 | 382 | syscall_add_hook_vector( 0xB4, 0x8C0000B4, bios_romfont_vector );
|
nkeynes@1102 | 383 | syscall_add_hook_vector( 0xB8, 0x8C0000B8, bios_flashrom_vector );
|
nkeynes@1102 | 384 | syscall_add_hook_vector( 0xBC, 0x8C0000BC, bios_gdrom_vector );
|
nkeynes@1102 | 385 | syscall_add_hook_vector( 0xE0, 0x8C0000E0, bios_menu_vector );
|
nkeynes@102 | 386 | }
|
nkeynes@1099 | 387 |
|
nkeynes@1099 | 388 | #define MIN_ISO_SECTORS 32
|
nkeynes@1099 | 389 |
|
nkeynes@1107 | 390 | static gboolean bios_load_ipl( cdrom_disc_t disc, cdrom_track_t track, const char *program_name,
|
nkeynes@1107 | 391 | unsigned char *buffer, gboolean unscramble )
|
nkeynes@1107 | 392 | {
|
nkeynes@1107 | 393 | gboolean rv = TRUE;
|
nkeynes@1107 | 394 |
|
nkeynes@1107 | 395 | IsoImageFilesystem *iso = iso_filesystem_new_from_track( disc, track, NULL );
|
nkeynes@1107 | 396 | if( iso == NULL ) {
|
nkeynes@1107 | 397 | ERROR( "Disc is not bootable (invalid ISO9660 filesystem)" );
|
nkeynes@1107 | 398 | return FALSE;
|
nkeynes@1107 | 399 | }
|
nkeynes@1107 | 400 | IsoFileSource *file = NULL;
|
nkeynes@1107 | 401 | int status = iso->get_by_path(iso, program_name, &file );
|
nkeynes@1107 | 402 | if( status != 1 ) {
|
nkeynes@1107 | 403 | ERROR( "Disc is not bootable (initial program '%s' not found)", program_name );
|
nkeynes@1107 | 404 | iso_filesystem_unref(iso);
|
nkeynes@1107 | 405 | return FALSE;
|
nkeynes@1107 | 406 | }
|
nkeynes@1107 | 407 |
|
nkeynes@1107 | 408 | struct stat st;
|
nkeynes@1107 | 409 | if( iso_file_source_stat(file, &st) == 1 ) {
|
nkeynes@1107 | 410 | if( st.st_size > (0x8D000000 - BINARY_LOAD_ADDR) ) {
|
nkeynes@1107 | 411 | ERROR( "Disc is not bootable (Initial program is too large to fit into memory)" );
|
nkeynes@1107 | 412 | rv = FALSE;
|
nkeynes@1107 | 413 | } else if( iso_file_source_open(file) == 1 ) {
|
nkeynes@1107 | 414 | size_t len;
|
nkeynes@1107 | 415 | if( unscramble ) {
|
nkeynes@1107 | 416 | char *tmp = g_malloc(st.st_size);
|
nkeynes@1107 | 417 | len = iso_file_source_read(file, tmp, st.st_size);
|
nkeynes@1107 | 418 | bootprogram_unscramble(buffer, tmp, st.st_size);
|
nkeynes@1107 | 419 | g_free(tmp);
|
nkeynes@1107 | 420 | } else {
|
nkeynes@1107 | 421 | len = iso_file_source_read(file, buffer, st.st_size);
|
nkeynes@1107 | 422 | }
|
nkeynes@1107 | 423 |
|
nkeynes@1107 | 424 | if( len != st.st_size ) {
|
nkeynes@1107 | 425 | ERROR( "Disc is not bootable (Unable to read initial program '%s')", program_name );
|
nkeynes@1107 | 426 | rv = FALSE;
|
nkeynes@1107 | 427 | }
|
nkeynes@1107 | 428 | iso_file_source_close(file);
|
nkeynes@1107 | 429 | }
|
nkeynes@1107 | 430 | } else {
|
nkeynes@1107 | 431 | ERROR( "Disc is not bootable (Unable to get size of initial program '%s')", program_name );
|
nkeynes@1107 | 432 | rv = FALSE;
|
nkeynes@1107 | 433 | }
|
nkeynes@1107 | 434 |
|
nkeynes@1107 | 435 | iso_file_source_unref(file);
|
nkeynes@1107 | 436 | iso_filesystem_unref(iso);
|
nkeynes@1107 | 437 | return rv;
|
nkeynes@1107 | 438 | }
|
nkeynes@1107 | 439 |
|
nkeynes@1099 | 440 | gboolean bios_boot_gdrom_disc( void )
|
nkeynes@1099 | 441 | {
|
nkeynes@1099 | 442 | cdrom_disc_t disc = gdrom_get_current_disc();
|
nkeynes@1099 | 443 |
|
nkeynes@1099 | 444 | int status = gdrom_get_drive_status();
|
nkeynes@1099 | 445 | if( status == CDROM_DISC_NONE ) {
|
nkeynes@1099 | 446 | ERROR( "No disc in drive" );
|
nkeynes@1099 | 447 | return FALSE;
|
nkeynes@1099 | 448 | }
|
nkeynes@1099 | 449 |
|
nkeynes@1099 | 450 | /* Find the bootable data track (if present) */
|
nkeynes@1099 | 451 | cdrom_track_t track = gdrom_disc_get_boot_track(disc);
|
nkeynes@1099 | 452 | if( track == NULL ) {
|
nkeynes@1099 | 453 | ERROR( "Disc is not bootable" );
|
nkeynes@1099 | 454 | return FALSE;
|
nkeynes@1099 | 455 | }
|
nkeynes@1099 | 456 | uint32_t lba = track->lba;
|
nkeynes@1099 | 457 | uint32_t sectors = cdrom_disc_get_track_size(disc,track);
|
nkeynes@1099 | 458 | if( sectors < MIN_ISO_SECTORS ) {
|
nkeynes@1099 | 459 | ERROR( "Disc is not bootable" );
|
nkeynes@1099 | 460 | return FALSE;
|
nkeynes@1099 | 461 | }
|
nkeynes@1099 | 462 | /* Load the initial bootstrap into DC ram at 8c008000 */
|
nkeynes@1099 | 463 | size_t length = BOOTSTRAP_SIZE;
|
nkeynes@1099 | 464 | unsigned char *bootstrap = mem_get_region(BOOTSTRAP_LOAD_ADDR);
|
nkeynes@1099 | 465 | if( cdrom_disc_read_sectors( disc, track->lba, BOOTSTRAP_SIZE/2048,
|
nkeynes@1099 | 466 | CDROM_READ_DATA|CDROM_READ_MODE2_FORM1, bootstrap, &length ) !=
|
nkeynes@1099 | 467 | CDROM_ERROR_OK ) {
|
nkeynes@1099 | 468 | ERROR( "Disc is not bootable" );
|
nkeynes@1099 | 469 | return FALSE;
|
nkeynes@1099 | 470 | }
|
nkeynes@1099 | 471 |
|
nkeynes@1099 | 472 | /* Check the magic just to be sure */
|
nkeynes@1099 | 473 | dc_bootstrap_head_t metadata = (dc_bootstrap_head_t)bootstrap;
|
nkeynes@1099 | 474 | if( memcmp( metadata->magic, BOOTSTRAP_MAGIC, BOOTSTRAP_MAGIC_SIZE ) != 0 ) {
|
nkeynes@1099 | 475 | ERROR( "Disc is not bootable (missing dreamcast bootstrap)" );
|
nkeynes@1099 | 476 | return FALSE;
|
nkeynes@1099 | 477 | }
|
nkeynes@1099 | 478 |
|
nkeynes@1099 | 479 | /* Get the initial program from the bootstrap (usually 1ST_READ.BIN) */
|
nkeynes@1107 | 480 | char program_name[18] = "/";
|
nkeynes@1107 | 481 | memcpy(program_name+1, metadata->boot_file, 16);
|
nkeynes@1107 | 482 | program_name[17] = '\0';
|
nkeynes@1107 | 483 | for( int i=16; i >= 0 && program_name[i] == ' '; i-- ) {
|
nkeynes@1099 | 484 | program_name[i] = '\0';
|
nkeynes@1099 | 485 | }
|
nkeynes@1099 | 486 |
|
nkeynes@1099 | 487 | /* Bootstrap is good. Now find the program in the actual filesystem... */
|
nkeynes@1107 | 488 | unsigned char *program = mem_get_region(BINARY_LOAD_ADDR);
|
nkeynes@1107 | 489 | gboolean isGDROM = (disc->disc_type == CDROM_DISC_GDROM );
|
nkeynes@1107 | 490 | if( !bios_load_ipl( disc, track, program_name, program, !isGDROM ) )
|
nkeynes@1099 | 491 | return FALSE;
|
nkeynes@1107 | 492 | asic_enable_ide_interface(isGDROM);
|
nkeynes@1100 | 493 | dreamcast_program_loaded( "", BOOTSTRAP_ENTRY_ADDR );
|
nkeynes@1100 | 494 | return TRUE;
|
nkeynes@1099 | 495 | }
|