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