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