Search
lxdream.org :: lxdream/src/dreamcast.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/dreamcast.c
changeset 180:e6dcf9b65658
prev167:71c0cc416a64
next265:5daf59b7f31b
author nkeynes
date Sat Aug 05 00:18:21 2006 +0000 (17 years ago)
permissions -rw-r--r--
last change Add error lines to tests with incomplete polys
Split clip tests to separate data file
Add tests for cmd bit 23 ("use list size field")
view annotate diff log raw
     1 /**
     2  * $Id: dreamcast.c,v 1.18 2006-07-02 04:59:00 nkeynes Exp $
     3  * Central switchboard for the system. This pulls all the individual modules
     4  * together into some kind of coherent structure. This is also where you'd
     5  * add Naomi support, if I ever get a board to play with...
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    20 #include <errno.h>
    21 #include <glib/gstrfuncs.h>
    22 #include "dream.h"
    23 #include "mem.h"
    24 #include "aica/aica.h"
    25 #include "asic.h"
    26 #include "dreamcast.h"
    27 #include "gdrom/ide.h"
    28 #include "maple/maple.h"
    30 /**
    31  * Current state of the DC virtual machine
    32  */
    33 #define STATE_UNINIT 0
    34 #define STATE_RUNNING 1
    35 #define STATE_STOPPING 2
    36 #define STATE_STOPPED 3 
    37 static volatile int dreamcast_state = STATE_UNINIT;
    38 static uint32_t timeslice_length = DEFAULT_TIMESLICE_LENGTH;
    39 static char *dreamcast_config = "DEFAULT";
    41 #define MAX_MODULES 32
    42 static int num_modules = 0;
    43 dreamcast_module_t modules[MAX_MODULES];
    45 /**
    46  * The unknown module is used for logging files without an actual module
    47  * declaration
    48  */
    49 struct dreamcast_module unknown_module = { "****", NULL, NULL, NULL, NULL, 
    50 					   NULL, NULL, NULL };
    52 /**
    53  * This function is responsible for defining how all the pieces of the
    54  * dreamcast actually fit together. 
    55  *
    56  * Note currently the locations of the various MMIO pages are hard coded in
    57  * the MMIO definitions - they should probably be moved here.
    58  */
    59 void dreamcast_configure( )
    60 {
    61     /* Register the memory framework */
    62     dreamcast_register_module( &mem_module );
    64     /* Setup standard memory map */
    65     mem_create_repeating_ram_region( 0x0C000000, 16 MB, MEM_REGION_MAIN, 0x01000000, 0x0F000000 );
    66     mem_create_ram_region( 0x00800000, 2 MB, MEM_REGION_AUDIO );
    67     mem_create_ram_region( 0x00703000, 8 KB, MEM_REGION_AUDIO_SCRATCH );
    68     mem_create_ram_region( 0x05000000, 8 MB, MEM_REGION_VIDEO );
    69     if( mem_load_rom( dreamcast_get_config_value(CONFIG_BIOS_PATH),
    70 		      0x00000000, 0x00200000, 0x89f2b1a1 ) == NULL ) {
    71 	/* Bios wasn't found. Dump an empty ram region in there for something to do */
    72 	mem_create_ram_region( 0x00000000, 0x00200000, MEM_REGION_BIOS );
    73     }
    74     mem_create_ram_region( 0x00200000, 0x00020000, MEM_REGION_FLASH );
    75     mem_load_block( dreamcast_get_config_value(CONFIG_FLASH_PATH),
    76 		    0x00200000, 0x00020000 );
    78     /* Load in the rest of the core modules */
    79     dreamcast_register_module( &sh4_module );
    80     dreamcast_register_module( &asic_module );
    81     dreamcast_register_module( &pvr2_module );
    82     dreamcast_register_module( &aica_module );
    83     dreamcast_register_module( &maple_module );
    84     dreamcast_register_module( &ide_module );
    85 }
    87 /**
    88  * Constructs a system configuration for the AICA in standalone mode,
    89  * ie sound chip only.
    90  */
    91 void dreamcast_configure_aica_only( )
    92 {
    93     dreamcast_register_module( &mem_module );
    94     mem_create_ram_region( 0x00800000, 2 MB, MEM_REGION_AUDIO );
    95     mem_create_ram_region( 0x00703000, 8 KB, MEM_REGION_AUDIO_SCRATCH );
    96     dreamcast_register_module( &aica_module );
    97     aica_enable();
    98     dreamcast_state = STATE_STOPPED;
    99 }
   101 void dreamcast_register_module( dreamcast_module_t module ) 
   102 {
   103     modules[num_modules++] = module;
   104     if( module->init != NULL )
   105 	module->init();
   106 }
   109 void dreamcast_init( void )
   110 {
   111     dreamcast_configure();
   112     dreamcast_state = STATE_STOPPED;
   113 }
   115 void dreamcast_reset( void )
   116 {
   117     int i;
   118     for( i=0; i<num_modules; i++ ) {
   119 	if( modules[i]->reset != NULL )
   120 	    modules[i]->reset();
   121     }
   122 }
   124 void dreamcast_run( void )
   125 {
   126     int i;
   127     if( dreamcast_state != STATE_RUNNING ) {
   128 	for( i=0; i<num_modules; i++ ) {
   129 	    if( modules[i]->start != NULL )
   130 		modules[i]->start();
   131 	}
   132     }
   133     dreamcast_state = STATE_RUNNING;
   134     while( dreamcast_state == STATE_RUNNING ) {
   135 	int time_to_run = timeslice_length;
   136 	for( i=0; i<num_modules; i++ ) {
   137 	    if( modules[i]->run_time_slice != NULL )
   138 		time_to_run = modules[i]->run_time_slice( time_to_run );
   139 	}
   141     }
   143     for( i=0; i<num_modules; i++ ) {
   144 	if( modules[i]->stop != NULL )
   145 	    modules[i]->stop();
   146     }
   147     dreamcast_state = STATE_STOPPED;
   148 }
   150 void dreamcast_stop( void )
   151 {
   152     if( dreamcast_state == STATE_RUNNING )
   153 	dreamcast_state = STATE_STOPPING;
   154 }
   156 gboolean dreamcast_is_running( void )
   157 {
   158     return dreamcast_state == STATE_RUNNING;
   159 }
   161 /***************************** User Configuration **************************/
   164 static struct dreamcast_config_entry global_config[] =
   165     {{ "bios", CONFIG_TYPE_FILE, "dcboot.rom" },
   166      { "flash", CONFIG_TYPE_FILE, "dcflash.rom" },
   167      { "default path", CONFIG_TYPE_PATH, "." },
   168      { "save path", CONFIG_TYPE_PATH, "save" },
   169      { "bootstrap", CONFIG_TYPE_FILE, "IP.BIN" },
   170      { NULL, CONFIG_TYPE_NONE }};
   172 static struct dreamcast_config_entry serial_config[] =
   173     {{ "device", CONFIG_TYPE_FILE, "/dev/ttyS1" },
   174      { NULL, CONFIG_TYPE_NONE }};
   176 struct dreamcast_config_group dreamcast_config_root[] = 
   177     {{ "global", global_config },
   178      { "controllers", NULL },
   179      { "serial", serial_config },
   180      { NULL, CONFIG_TYPE_NONE }};
   182 void dreamcast_set_default_config( )
   183 {
   184     struct dreamcast_config_group *group = dreamcast_config_root;
   185     while( group->key != NULL ) {
   186 	struct dreamcast_config_entry *param = group->params;
   187 	if( param != NULL ) {
   188 	    while( param->key != NULL ) {
   189 		if( param->value != param->default_value ) {
   190 		    if( param->value != NULL )
   191 			free( param->value );
   192 		    param->value = (gchar *)param->default_value;
   193 		}
   194 		param++;
   195 	    }
   196 	}
   197 	group++;
   198     }
   199     maple_detach_all();
   200 }
   202 const gchar *dreamcast_get_config_value( int key )
   203 {
   204     return global_config[key].value;
   205 }
   207 gboolean dreamcast_load_config( const gchar *filename )
   208 {
   209     FILE *f = fopen(filename, "ro");
   210     gboolean result;
   212     if( f == NULL ) {
   213 	ERROR( "Unable to open '%s': %s", filename, strerror(errno) );
   214 	return FALSE;
   215     }
   217     result = dreamcast_load_config_stream( f );
   218     fclose(f);
   219     return result;
   220 }
   222 gboolean dreamcast_load_config_stream( FILE *f )
   223 {
   225     char buf[512], *p;
   226     int maple_device = -1, maple_subdevice = -1;
   227     struct dreamcast_config_group devgroup;
   228     struct dreamcast_config_group *group = NULL;
   229     maple_device_t device = NULL;
   230     dreamcast_set_default_config();
   232     while( fgets( buf, sizeof(buf), f ) != NULL ) {
   233 	g_strstrip(buf);
   234 	if( buf[0] == '#' )
   235 	    continue;
   236 	if( *buf == '[' ) {
   237 	    char *p = strchr(buf, ']');
   238 	    if( p != NULL ) {
   239 		struct dreamcast_config_group *tmp_group;
   240 		maple_device = maple_subdevice = -1;
   241 		*p = '\0';
   242 		g_strstrip(buf+1);
   243 		tmp_group = &dreamcast_config_root[0];
   244 		while( tmp_group->key != NULL ) {
   245 		    if( strcasecmp(tmp_group->key, buf+1) == 0 ) {
   246 			group = tmp_group;
   247 			break;
   248 		    }
   249 		    tmp_group++;
   250 		}
   251 	    }
   252 	} else if( group != NULL ) {
   253 	    char *value = strchr( buf, '=' );
   254 	    if( value != NULL ) {
   255 		struct dreamcast_config_entry *param = group->params;
   256 		*value = '\0';
   257 		value++;
   258 		g_strstrip(buf);
   259 		g_strstrip(value);
   260 		if( strcmp(group->key,"controllers") == 0  ) {
   261 		    if( g_strncasecmp( buf, "device ", 7 ) == 0 ) {
   262 			maple_device = strtoul( buf+7, NULL, 0 );
   263 			if( maple_device < 0 || maple_device > 3 ) {
   264 			    ERROR( "Device number must be between 0..3 (not '%s')", buf+7);
   265 			    continue;
   266 			}
   267 			maple_subdevice = 0;
   268 			device = maple_new_device( value );
   269 			if( device == NULL ) {
   270 			    ERROR( "Unrecognized device '%s'", value );
   271 			} else {
   272 			    devgroup.key = "controllers";
   273 			    devgroup.params = maple_get_device_config(device);
   274 			    maple_attach_device( device, maple_device, maple_subdevice );
   275 			    group = &devgroup;
   276 			}
   277 			continue;
   278 		    } else if( g_strncasecmp( buf, "subdevice ", 10 ) == 0 ) {
   279 			maple_subdevice = strtoul( buf+10, NULL, 0 );
   280 			if( maple_device == -1 ) {
   281 			    ERROR( "Subdevice not allowed without primary device" );
   282 			} else if( maple_subdevice < 1 || maple_subdevice > 5 ) {
   283 			    ERROR( "Subdevice must be between 1..5 (not '%s')", buf+10 );
   284 			} else if( (device = maple_new_device(value)) == NULL ) {
   285 			    ERROR( "Unrecognized subdevice '%s'", value );
   286 			} else {
   287 			    devgroup.key = "controllers";
   288 			    devgroup.params = maple_get_device_config(device);
   289 			    maple_attach_device( device, maple_device, maple_subdevice );
   290 			    group = &devgroup;
   291 			}
   292 			continue;
   293 		    }
   294 		}
   295 		while( param->key != NULL ) {
   296 		    if( strcasecmp( param->key, buf ) == 0 ) {
   297 			param->value = g_strdup(value);
   298 			break;
   299 		    }
   300 		    param++;
   301 		}
   302 	    }
   303 	}
   304     }
   305     return TRUE;
   306 }
   308 gboolean dreamcast_save_config( const gchar *filename )
   309 {
   310     FILE *f = fopen(filename, "wo");
   311     gboolean result;
   312     if( f == NULL ) {
   313 	ERROR( "Unable to open '%s': %s", filename, strerror(errno) );
   314 	return FALSE;
   315     }
   316     result = dreamcast_save_config_stream(f);
   317     fclose(f);
   318 }    
   320 gboolean dreamcast_save_config_stream( FILE *f )
   321 {
   322     struct dreamcast_config_group *group = &dreamcast_config_root[0];
   324     while( group->key != NULL ) {
   325 	struct dreamcast_config_entry *entry = group->params;
   326 	fprintf( f, "[%s]\n", group->key );
   328 	if( entry != NULL ) {
   329 	    while( entry->key != NULL ) {
   330 		fprintf( f, "%s = %s\n", entry->key, entry->value );
   331 		entry++;
   332 	    }
   333 	} else if( strcmp(group->key, "controllers") == 0 ) {
   334 	    int i,j;
   335 	    for( i=0; i<4; i++ ) {
   336 		for( j=0; j<6; j++ ) {
   337 		    maple_device_t dev = maple_get_device( i, j );
   338 		    if( dev != NULL ) {
   339 			if( j == 0 )
   340 			    fprintf( f, "Device %d = %s\n", i, dev->device_class->name );
   341 			else 
   342 			    fprintf( f, "Subdevice %d = %s\n", j, dev->device_class->name );
   343 			entry = dev->get_config(dev);
   344 			while( entry->key != NULL ) {
   345 			    fprintf( f, "%*c%s = %s\n", j==0?4:8, ' ',entry->key, entry->value );
   346 			    entry++;
   347 			}
   348 		    }
   349 		}
   350 	    }
   351 	}
   352 	fprintf( f, "\n" );
   353 	group++;
   354     }
   355     return TRUE;
   356 }
   358 /********************************* Save States *****************************/
   360 #define DREAMCAST_SAVE_MAGIC "%!-lxDream!Save\0"
   361 #define DREAMCAST_SAVE_VERSION 0x00010000
   363 struct save_state_header {
   364     char magic[16];
   365     uint32_t version;
   366     uint32_t module_count;
   367 };
   369 int dreamcast_load_state( const gchar *filename )
   370 {
   371     int i,j;
   372     uint32_t count, len;
   373     int have_read[MAX_MODULES];
   374     char tmp[64];
   375     struct save_state_header header;
   376     FILE *f;
   378     f = fopen( filename, "r" );
   379     if( f == NULL ) return errno;
   381     fread( &header, sizeof(header), 1, f );
   382     if( strncmp( header.magic, DREAMCAST_SAVE_MAGIC, 16 ) != 0 ) {
   383 	ERROR( "Not a %s save state file", APP_NAME );
   384 	return 1;
   385     }
   386     if( header.version != DREAMCAST_SAVE_VERSION ) {
   387 	ERROR( "%s save state version not supported", APP_NAME );
   388 	return 1;
   389     }
   390     if( header.module_count > MAX_MODULES ) {
   391 	ERROR( "%s save state is corrupted (bad module count)", APP_NAME );
   392 	return 1;
   393     }
   394     for( i=0; i<MAX_MODULES; i++ ) {
   395 	have_read[i] = 0;
   396     }
   398     for( i=0; i<header.module_count; i++ ) {
   399 	fread(tmp, 4, 1, f );
   400 	if( strncmp(tmp, "BLCK", 4) != 0 ) {
   401 	    ERROR( "%s save state is corrupted (missing block header %d)", APP_NAME, i );
   402 	    return 2;
   403 	}
   404 	len = fread_string(tmp, sizeof(tmp), f );
   405 	if( len > 64 || len < 1 ) {
   406 	    ERROR( "%s save state is corrupted (bad string)", APP_NAME );
   407 	    return 2;
   408 	}
   410 	/* Find the matching module by name */
   411 	for( j=0; j<num_modules; j++ ) {
   412 	    if( strcmp(modules[j]->name,tmp) == 0 ) {
   413 		have_read[j] = 1;
   414 		if( modules[j]->load == NULL ) {
   415 		    ERROR( "%s save state is corrupted (no loader for %s)", APP_NAME, modules[j]->name );
   416 		    return 2;
   417 		} else if( modules[j]->load(f) != 0 ) {
   418 		    ERROR( "%s save state is corrupted (%s failed)", APP_NAME, modules[j]->name );
   419 		    return 2;
   420 		}
   421 		break;
   422 	    }
   423 	}
   424 	if( j == num_modules ) {
   425 	    ERROR( "%s save state contains unrecognized section", APP_NAME );
   426 	    return 2;
   427 	}
   428     }
   430     /* Any modules that we didn't load - reset to the default state.
   431      * (ie it's not an error to skip a module if you don't actually
   432      * care about its state).
   433      */
   434     for( j=0; j<num_modules; j++ ) {
   435 	if( have_read[j] == 0 && modules[j]->reset != NULL ) {
   436 	    modules[j]->reset();
   437 	}
   438     }
   439     fclose(f);
   440     INFO( "Save state read from %s", filename );
   441 }
   443 int dreamcast_save_state( const gchar *filename )
   444 {
   445     int i;
   446     FILE *f;
   447     struct save_state_header header;
   449     f = fopen( filename, "w" );
   450     if( f == NULL )
   451 	return errno;
   452     strcpy( header.magic, DREAMCAST_SAVE_MAGIC );
   453     header.version = DREAMCAST_SAVE_VERSION;
   454     header.module_count = 0;
   456     for( i=0; i<num_modules; i++ ) {
   457 	if( modules[i]->save != NULL )
   458 	    header.module_count++;
   459     }
   460     fwrite( &header, sizeof(header), 1, f );
   461     for( i=0; i<num_modules; i++ ) {
   462 	if( modules[i]->save != NULL ) {
   463 	    fwrite( "BLCK", 4, 1, f );
   464 	    fwrite_string( modules[i]->name, f );
   465 	    modules[i]->save(f);
   466 	}
   467     }
   468     fclose( f );
   469     INFO( "Save state written to %s", filename );
   470 }
.