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