summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--blastem.c115
-rw-r--r--blastem.h3
-rw-r--r--debug.c22
-rw-r--r--gdb_remote.c14
-rw-r--r--genesis.c206
-rw-r--r--genesis.h19
-rw-r--r--gst.c4
-rw-r--r--io.c71
-rw-r--r--io.h10
-rw-r--r--m68k_core.h3
-rw-r--r--m68k_core_x86.c4
-rw-r--r--menu.c21
-rw-r--r--system.c33
-rw-r--r--system.h49
15 files changed, 367 insertions, 209 deletions
diff --git a/Makefile b/Makefile
index 03bf09a..3b2c9ed 100644
--- a/Makefile
+++ b/Makefile
@@ -125,7 +125,7 @@ Z80OBJS=z80inst.o z80_to_x86.o
AUDIOOBJS=ym2612.o psg.o wave.o
CONFIGOBJS=config.o tern.o util.o
-MAINOBJS=blastem.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o menu.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o menu.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
ifeq ($(CPU),x86_64)
CFLAGS+=-DX86_64 -m64
diff --git a/blastem.c b/blastem.c
index 5e3ac59..565db9e 100644
--- a/blastem.c
+++ b/blastem.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <ctype.h>
+#include "system.h"
#include "68kinst.h"
#include "m68k_core.h"
#include "z80_to_x86.h"
@@ -66,7 +67,7 @@ int load_smd_rom(long filesize, FILE * f, uint16_t **buffer)
return rom_size;
}
-int load_rom(char * filename, uint16_t **dst)
+int load_rom(char * filename, uint16_t **dst, system_type *stype)
{
uint8_t header[10];
FILE * f = fopen(filename, "rb");
@@ -90,6 +91,9 @@ int load_rom(char * filename, uint16_t **dst)
if (header[2]) {
fatal_error("%s is a split SMD ROM which is not currently supported", filename);
}
+ if (stype) {
+ *stype = SYSTEM_GENESIS;
+ }
return load_smd_rom(filesize, f, dst);
}
}
@@ -98,6 +102,9 @@ int load_rom(char * filename, uint16_t **dst)
fatal_error("Error reading from %s\n", filename);
}
fclose(f);
+ if (stype) {
+ *stype = detect_system_type((uint8_t *)*dst, filesize);
+ }
return filesize;
}
@@ -111,26 +118,17 @@ char *save_state_path;
char * save_filename;
-genesis_context *genesis;
-genesis_context *menu_context;
-genesis_context *game_context;
+system_header *current_system;
+system_header *menu_context;
+system_header *game_context;
void persist_save()
{
if (!game_context) {
return;
}
- FILE * f = fopen(save_filename, "wb");
- if (!f) {
- fprintf(stderr, "Failed to open %s file %s for writing\n", game_context->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
- return;
- }
- fwrite(game_context->save_storage, 1, game_context->save_size, f);
- fclose(f);
- printf("Saved %s to %s\n", game_context->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+ game_context->persist_save(game_context);
}
-
-
char *title;
void update_title(char *rom_name)
{
@@ -142,8 +140,9 @@ void update_title(char *rom_name)
render_update_caption(title);
}
-void setup_saves(char *fname, rom_info *info, genesis_context *context)
+void setup_saves(char *fname, rom_info *info, system_header *context)
{
+ static uint8_t persist_save_registered;
char * barename = basename_no_extension(fname);
char const * parts[3] = {get_save_dir(), PATH_SEP, barename};
char *save_dir = alloc_concat_m(3, parts);
@@ -154,21 +153,18 @@ void setup_saves(char *fname, rom_info *info, genesis_context *context)
parts[2] = info->save_type == SAVE_I2C ? "save.eeprom" : "save.sram";
free(save_filename);
save_filename = alloc_concat_m(3, parts);
+ //TODO: make quick save filename dependent on system type
parts[2] = "quicksave.gst";
free(save_state_path);
save_state_path = alloc_concat_m(3, parts);
context->save_dir = save_dir;
free(barename);
if (info->save_type != SAVE_NONE) {
- FILE * f = fopen(save_filename, "rb");
- if (f) {
- uint32_t read = fread(context->save_storage, 1, info->save_size, f);
- fclose(f);
- if (read > 0) {
- printf("Loaded %s from %s\n", info->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
- }
+ context->load_save(context);
+ if (!persist_save_registered) {
+ atexit(persist_save);
+ persist_save_registered = 1;
}
- atexit(persist_save);
}
}
@@ -181,13 +177,15 @@ int main(int argc, char ** argv)
int debug = 0;
int ym_log = 0;
int loaded = 0;
+ system_type stype;
uint8_t force_region = 0;
char * romfname = NULL;
FILE *address_log = NULL;
char * statefile = NULL;
int rom_size, lock_on_size;
uint16_t *cart = NULL, *lock_on = NULL;
- uint8_t * debuggerfun = NULL;
+ debugger_type dtype = DEBUGGER_NATIVE;
+ uint8_t start_in_debugger = 0;
uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1;
uint8_t debug_target = 0;
for (int i = 1; i < argc; i++) {
@@ -202,7 +200,7 @@ int main(int argc, char ** argv)
exit_after = atoi(argv[i]);
break;
case 'd':
- debuggerfun = (uint8_t *)debugger;
+ start_in_debugger = 1;
//allow debugging the menu
if (argv[i][2] == 'm') {
debug_target = 1;
@@ -210,7 +208,7 @@ int main(int argc, char ** argv)
break;
case 'D':
gdb_remote_init();
- debuggerfun = (uint8_t *)gdb_debug_enter;
+ dtype = DEBUGGER_GDB;
break;
case 'f':
fullscreen = !fullscreen;
@@ -256,7 +254,7 @@ int main(int argc, char ** argv)
if (i >= argc) {
fatal_error("-o must be followed by a lock on cartridge filename\n");
}
- lock_on_size = load_rom(argv[i], &lock_on);
+ lock_on_size = load_rom(argv[i], &lock_on, NULL);
if (!lock_on_size) {
fatal_error("Failed to load lock on cartridge %s\n", argv[i]);
}
@@ -283,7 +281,7 @@ int main(int argc, char ** argv)
fatal_error("Unrecognized switch %s\n", argv[i]);
}
} else if (!loaded) {
- if (!(rom_size = load_rom(argv[i], &cart))) {
+ if (!(rom_size = load_rom(argv[i], &cart, &stype))) {
fatal_error("Failed to open %s for reading\n", argv[i]);
}
romfname = argv[i];
@@ -302,7 +300,7 @@ int main(int argc, char ** argv)
romfname = "menu.bin";
}
if (is_absolute_path(romfname)) {
- if (!(rom_size = load_rom(romfname, &cart))) {
+ if (!(rom_size = load_rom(romfname, &cart, &stype))) {
fatal_error("Failed to open UI ROM %s for reading", romfname);
}
} else {
@@ -311,6 +309,7 @@ int main(int argc, char ** argv)
if (!cart) {
fatal_error("Failed to open UI ROM %s for reading", romfname);
}
+ stype = detect_system_type((uint8_t *)cart, fsize);
rom_size = nearest_pow2(fsize);
if (rom_size > fsize) {
cart = realloc(cart, rom_size);
@@ -341,43 +340,41 @@ int main(int argc, char ** argv)
rom_info info;
uint32_t ym_opts = (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0;
- genesis = alloc_config_genesis(cart, rom_size, lock_on, lock_on_size, ym_opts, force_region, &info);
- setup_saves(romfname, &info, genesis);
+ current_system = alloc_config_system(stype, cart, rom_size, lock_on, lock_on_size, ym_opts, force_region, &info);
+ setup_saves(romfname, &info, current_system);
update_title(info.name);
if (menu) {
- menu_context = genesis;
+ menu_context = current_system;
} else {
- genesis->m68k->options->address_log = address_log;
- game_context = genesis;
+ //TODO: make this an option flag
+ //genesis->m68k->options->address_log = address_log;
+ game_context = current_system;
}
- set_keybindings(genesis->ports);
- start_genesis(genesis, menu ? NULL : statefile, menu == debug_target ? debuggerfun : NULL);
+ current_system->debugger_type = dtype;
+ current_system->enter_debugger = start_in_debugger && menu == debug_target;
+ current_system->start_context(current_system, menu ? NULL : statefile);
for(;;)
{
- if (genesis->should_exit) {
+ if (current_system->should_exit) {
break;
}
if (menu && menu_context->next_rom) {
if (game_context) {
- if (game_context->save_type != SAVE_NONE) {
- genesis = game_context;
- persist_save();
- genesis = menu_context;
- }
+ game_context->persist_save(game_context);
//swap to game context arena and mark all allocated pages in it free
- genesis->arena = set_current_arena(game_context->arena);
+ current_system->arena = set_current_arena(game_context->arena);
mark_all_free();
- free_genesis(game_context);
+ game_context->free_context(game_context);
} else {
//start a new arena and save old one in suspended genesis context
- genesis->arena = start_new_arena();
+ current_system->arena = start_new_arena();
}
- if (!(rom_size = load_rom(menu_context->next_rom, &cart))) {
+ if (!(rom_size = load_rom(menu_context->next_rom, &cart, &stype))) {
fatal_error("Failed to open %s for reading\n", menu_context->next_rom);
}
//allocate new genesis context
- game_context = alloc_config_genesis(cart, rom_size, lock_on, lock_on_size, ym_opts,force_region, &info);
+ game_context = alloc_config_system(stype, cart, rom_size, lock_on, lock_on_size, ym_opts,force_region, &info);
menu_context->next_context = game_context;
game_context->next_context = menu_context;
setup_saves(menu_context->next_rom, &info, game_context);
@@ -385,22 +382,22 @@ int main(int argc, char ** argv)
free(menu_context->next_rom);
menu_context->next_rom = NULL;
menu = 0;
- genesis = game_context;
- genesis->m68k->options->address_log = address_log;
- map_all_bindings(genesis->ports);
- start_genesis(genesis, statefile, menu == debug_target ? debuggerfun : NULL);
+ current_system = game_context;
+ //TODO: make this an option flag
+ //genesis->m68k->options->address_log = address_log;
+ current_system->debugger_type = dtype;
+ current_system->enter_debugger = start_in_debugger && menu == debug_target;
+ current_system->start_context(current_system, statefile);
} else if (menu && game_context) {
- genesis->arena = set_current_arena(game_context->arena);
- genesis = game_context;
+ current_system->arena = set_current_arena(game_context->arena);
+ current_system = game_context;
menu = 0;
- map_all_bindings(genesis->ports);
- resume_68k(genesis->m68k);
+ current_system->resume_context(current_system);
} else if (!menu && menu_context) {
- genesis->arena = set_current_arena(menu_context->arena);
- genesis = menu_context;
+ current_system->arena = set_current_arena(menu_context->arena);
+ current_system = menu_context;
menu = 1;
- map_all_bindings(genesis->ports);
- resume_68k(genesis->m68k);
+ current_system->resume_context(current_system);
} else {
break;
}
diff --git a/blastem.h b/blastem.h
index 940cca6..790b10e 100644
--- a/blastem.h
+++ b/blastem.h
@@ -2,6 +2,7 @@
#define BLASTEM_H_
#include "tern.h"
+#include "system.h"
extern int headless;
extern int exit_after;
@@ -9,8 +10,10 @@ extern int z80_enabled;
extern int frame_limit;
extern tern_node * config;
+extern system_header *current_system;
extern char *save_state_path;
+extern char *save_filename;
#define QUICK_SAVE_SLOT 10
#endif //BLASTEM_H_
diff --git a/debug.c b/debug.c
index fa5181e..4e7b504 100644
--- a/debug.c
+++ b/debug.c
@@ -603,7 +603,7 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
break;
}
value = strtol(param, NULL, 16);
- insert_breakpoint(context, value, (uint8_t *)debugger);
+ insert_breakpoint(context, value, debugger);
new_bp = malloc(sizeof(bp_def));
new_bp->next = breakpoints;
new_bp->address = value;
@@ -620,7 +620,7 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
break;
}
value = strtol(param, NULL, 16);
- insert_breakpoint(context, value, (uint8_t *)debugger);
+ insert_breakpoint(context, value, debugger);
return 0;
case 'd':
if (input_buf[1] == 'i') {
@@ -682,7 +682,7 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
branch_f = after;
branch_t = m68k_branch_target(&inst, context->dregs, context->aregs);
- insert_breakpoint(context, branch_t, (uint8_t *)debugger);
+ insert_breakpoint(context, branch_t, debugger);
} else if(inst.op == M68K_DBCC) {
if ( inst.extra.cond == COND_FALSE) {
if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) {
@@ -691,13 +691,13 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
} else {
branch_t = after;
branch_f = m68k_branch_target(&inst, context->dregs, context->aregs);
- insert_breakpoint(context, branch_f, (uint8_t *)debugger);
+ insert_breakpoint(context, branch_f, debugger);
}
} else {
after = m68k_branch_target(&inst, context->dregs, context->aregs);
}
}
- insert_breakpoint(context, after, (uint8_t *)debugger);
+ insert_breakpoint(context, after, debugger);
return 0;
case 'o':
if (inst.op == M68K_RTS) {
@@ -711,7 +711,7 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
branch_t = 0;
} else {
branch_f = after;
- insert_breakpoint(context, branch_t, (uint8_t *)debugger);
+ insert_breakpoint(context, branch_t, debugger);
}
} else if(inst.op == M68K_DBCC) {
uint32_t target = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
@@ -721,14 +721,14 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
} else {
branch_f = target;
branch_t = after;
- insert_breakpoint(context, branch_f, (uint8_t *)debugger);
+ insert_breakpoint(context, branch_f, debugger);
}
}
} else {
after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
}
}
- insert_breakpoint(context, after, (uint8_t *)debugger);
+ insert_breakpoint(context, after, debugger);
return 0;
case 's':
if (inst.op == M68K_RTS) {
@@ -739,16 +739,16 @@ int run_debugger_command(m68k_context *context, char *input_buf, m68kinst inst,
if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
branch_f = after;
branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
- insert_breakpoint(context, branch_t, (uint8_t *)debugger);
+ insert_breakpoint(context, branch_t, debugger);
} else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) {
branch_t = after;
branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
- insert_breakpoint(context, branch_f, (uint8_t *)debugger);
+ insert_breakpoint(context, branch_f, debugger);
} else {
after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
}
}
- insert_breakpoint(context, after, (uint8_t *)debugger);
+ insert_breakpoint(context, after, debugger);
return 0;
case 'v': {
genesis_context * gen = context->system;
diff --git a/gdb_remote.c b/gdb_remote.c
index e2b47f0..b2de7db 100644
--- a/gdb_remote.c
+++ b/gdb_remote.c
@@ -216,16 +216,16 @@ void gdb_run_command(m68k_context * context, uint32_t pc, char * command)
if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
branch_f = after;
branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
- insert_breakpoint(context, branch_t, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, branch_t, gdb_debug_enter);
} else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) {
branch_t = after;
branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
- insert_breakpoint(context, branch_f, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, branch_f, gdb_debug_enter);
} else {
after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
}
}
- insert_breakpoint(context, after, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, after, gdb_debug_enter);
cont = 1;
expect_break_response = 1;
@@ -243,7 +243,7 @@ void gdb_run_command(m68k_context * context, uint32_t pc, char * command)
uint8_t type = command[1];
if (type < '2') {
uint32_t address = strtoul(command+3, NULL, 16);
- insert_breakpoint(context, address, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, address, gdb_debug_enter);
bp_def *new_bp = malloc(sizeof(bp_def));
new_bp->next = breakpoints;
new_bp->address = address;
@@ -433,16 +433,16 @@ void gdb_run_command(m68k_context * context, uint32_t pc, char * command)
if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
branch_f = after;
branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
- insert_breakpoint(context, branch_t, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, branch_t, gdb_debug_enter);
} else if(inst.op == M68K_DBCC && inst.extra.cond != COND_FALSE) {
branch_t = after;
branch_f = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
- insert_breakpoint(context, branch_f, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, branch_f, gdb_debug_enter);
} else {
after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
}
}
- insert_breakpoint(context, after, (uint8_t *)gdb_debug_enter);
+ insert_breakpoint(context, after, gdb_debug_enter);
cont = 1;
expect_break_response = 1;
diff --git a/genesis.c b/genesis.c
index b386421..293cb5c 100644
--- a/genesis.c
+++ b/genesis.c
@@ -9,6 +9,8 @@
#include "render.h"
#include "gst.h"
#include "util.h"
+#include "debug.h"
+#include "gdb_remote.h"
#define MCLKS_NTSC 53693175
#define MCLKS_PAL 53203395
@@ -27,6 +29,7 @@ uint32_t MCLKS_PER_68K;
uint16_t read_dma_value(uint32_t address)
{
+ genesis_context *genesis = (genesis_context *)current_system;
//addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do multiply by 2
uint16_t *ptr = get_native_pointer(address*2, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen);
if (ptr) {
@@ -38,6 +41,7 @@ uint16_t read_dma_value(uint32_t address)
uint16_t get_open_bus_value()
{
+ genesis_context *genesis = (genesis_context *)current_system;
return read_dma_value(genesis->m68k->last_prefetch_address/2);
}
@@ -173,9 +177,9 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
}
vdp_adjust_cycles(v_context, mclks);
- io_adjust_cycles(gen->ports, context->current_cycle, mclks);
- io_adjust_cycles(gen->ports+1, context->current_cycle, mclks);
- io_adjust_cycles(gen->ports+2, context->current_cycle, mclks);
+ io_adjust_cycles(gen->io.ports, context->current_cycle, mclks);
+ io_adjust_cycles(gen->io.ports+1, context->current_cycle, mclks);
+ io_adjust_cycles(gen->io.ports+2, context->current_cycle, mclks);
context->current_cycle -= mclks;
z80_adjust_cycles(z_context, mclks);
gen->ym->current_cycle -= mclks;
@@ -192,18 +196,18 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
vdp_int_ack(v_context);
context->int_ack = 0;
}
- if (!address && (break_on_sync || gen->save_state)) {
+ if (!address && (gen->header.enter_debugger || gen->header.save_state)) {
context->sync_cycle = context->current_cycle + 1;
}
adjust_int_cycle(context, v_context);
if (address) {
- if (break_on_sync) {
- break_on_sync = 0;
+ if (gen->header.enter_debugger) {
+ gen->header.enter_debugger = 0;
debugger(context, address);
}
- if (gen->save_state && (z_context->pc || (!z_context->reset && !z_context->busreq))) {
- uint8_t slot = gen->save_state - 1;
- gen->save_state = 0;
+ if (gen->header.save_state && (z_context->pc || (!z_context->reset && !z_context->busreq))) {
+ uint8_t slot = gen->header.save_state - 1;
+ gen->header.save_state = 0;
//advance Z80 core to the start of an instruction
while (!z_context->pc)
{
@@ -215,7 +219,7 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
} else {
char slotname[] = "slot_0.gst";
slotname[5] = '0' + slot;
- char const *parts[] = {gen->save_dir, PATH_SEP, slotname};
+ char const *parts[] = {gen->header.save_dir, PATH_SEP, slotname};
save_path = alloc_concat_m(3, parts);
}
save_gst(gen, save_path, address);
@@ -223,7 +227,7 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
if (slot != QUICK_SAVE_SLOT) {
free(save_path);
}
- } else if(gen->save_state) {
+ } else if(gen->header.save_state) {
context->sync_cycle = context->current_cycle + 1;
}
}
@@ -486,22 +490,22 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
switch(location/2)
{
case 0x1:
- io_data_write(gen->ports, value, context->current_cycle);
+ io_data_write(gen->io.ports, value, context->current_cycle);
break;
case 0x2:
- io_data_write(gen->ports+1, value, context->current_cycle);
+ io_data_write(gen->io.ports+1, value, context->current_cycle);
break;
case 0x3:
- io_data_write(gen->ports+2, value, context->current_cycle);
+ io_data_write(gen->io.ports+2, value, context->current_cycle);
break;
case 0x4:
- gen->ports[0].control = value;
+ gen->io.ports[0].control = value;
break;
case 0x5:
- gen->ports[1].control = value;
+ gen->io.ports[1].control = value;
break;
case 0x6:
- gen->ports[2].control = value;
+ gen->io.ports[2].control = value;
break;
}
} else {
@@ -597,22 +601,22 @@ uint8_t io_read(uint32_t location, m68k_context * context)
value = gen->version_reg;
break;
case 0x1:
- value = io_data_read(gen->ports, context->current_cycle);
+ value = io_data_read(gen->io.ports, context->current_cycle);
break;
case 0x2:
- value = io_data_read(gen->ports+1, context->current_cycle);
+ value = io_data_read(gen->io.ports+1, context->current_cycle);
break;
case 0x3:
- value = io_data_read(gen->ports+2, context->current_cycle);
+ value = io_data_read(gen->io.ports+2, context->current_cycle);
break;
case 0x4:
- value = gen->ports[0].control;
+ value = gen->io.ports[0].control;
break;
case 0x5:
- value = gen->ports[1].control;
+ value = gen->io.ports[1].control;
break;
case 0x6:
- value = gen->ports[2].control;
+ value = gen->io.ports[2].control;
break;
default:
value = 0xFF;
@@ -737,8 +741,9 @@ void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value)
return context;
}
-void set_speed_percent(genesis_context * context, uint32_t percent)
+static void set_speed_percent(system_header * system, uint32_t percent)
{
+ genesis_context *context = (genesis_context *)system;
uint32_t old_clock = context->master_clock;
context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100;
while (context->ym->current_cycle != context->psg->cycles) {
@@ -774,6 +779,109 @@ void set_region(genesis_context *gen, rom_info *info, uint8_t region)
gen->master_clock = gen->normal_clock;
}
+static void start_genesis(system_header *system, char *statefile)
+{
+ genesis_context *gen = (genesis_context *)system;
+ set_keybindings(&gen->io);
+ if (statefile) {
+ uint32_t pc = load_gst(gen, statefile);
+ if (!pc) {
+ fatal_error("Failed to load save state %s\n", statefile);
+ }
+ printf("Loaded %s\n", statefile);
+ if (gen->header.enter_debugger) {
+ gen->header.enter_debugger = 0;
+ insert_breakpoint(gen->m68k, pc, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter);
+ }
+ adjust_int_cycle(gen->m68k, gen->vdp);
+ start_68k_context(gen->m68k, pc);
+ } else {
+ if (gen->header.enter_debugger) {
+ gen->header.enter_debugger = 0;
+ uint32_t address = gen->cart[2] << 16 | gen->cart[3];
+ insert_breakpoint(gen->m68k, address, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter);
+ }
+ m68k_reset(gen->m68k);
+ }
+}
+
+static void resume_genesis(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ map_all_bindings(&gen->io);
+ resume_68k(gen->m68k);
+}
+
+static void inc_debug_mode(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ gen->vdp->debug++;
+ if (gen->vdp->debug == 7) {
+ gen->vdp->debug = 0;
+ }
+}
+
+static void inc_debug_pal(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ gen->vdp->debug_pal++;
+ if (gen->vdp->debug_pal == 4) {
+ gen->vdp->debug_pal = 0;
+ }
+}
+
+static void request_exit(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ gen->m68k->should_return = 1;
+}
+
+static void persist_save(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ if (gen->save_type == SAVE_NONE) {
+ return;
+ }
+ FILE * f = fopen(save_filename, "wb");
+ if (!f) {
+ fprintf(stderr, "Failed to open %s file %s for writing\n", gen->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+ return;
+ }
+ fwrite(gen->save_storage, 1, gen->save_size, f);
+ fclose(f);
+ printf("Saved %s to %s\n", gen->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+}
+
+static void load_save(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ FILE * f = fopen(save_filename, "rb");
+ if (f) {
+ uint32_t read = fread(gen->save_storage, 1, gen->save_size, f);
+ fclose(f);
+ if (read > 0) {
+ printf("Loaded %s from %s\n", gen->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+ }
+ }
+}
+
+static void free_genesis(system_header *system)
+{
+ genesis_context *gen = (genesis_context *)system;
+ vdp_free(gen->vdp);
+ m68k_options_free(gen->m68k->options);
+ free(gen->m68k);
+ free(gen->work_ram);
+ z80_options_free(gen->z80->options);
+ free(gen->z80);
+ free(gen->zram);
+ ym_free(gen->ym);
+ psg_free(gen->psg);
+ free(gen->save_storage);
+ free(gen->header.save_dir);
+ free(gen->lock_on);
+}
+
genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t ym_opts, uint8_t force_region)
{
static memmap_chunk z80_map[] = {
@@ -784,6 +892,15 @@ genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on
{ 0x7F00, 0x8000, 0x00FF, 0, 0, 0, NULL, NULL, NULL, z80_vdp_port_read, z80_vdp_port_write}
};
genesis_context *gen = calloc(1, sizeof(genesis_context));
+ gen->header.set_speed_percent = set_speed_percent;
+ gen->header.start_context = start_genesis;
+ gen->header.resume_context = resume_genesis;
+ gen->header.load_save = load_save;
+ gen->header.persist_save = persist_save;
+ gen->header.free_context = free_genesis;
+ gen->header.request_exit = request_exit;
+ gen->header.inc_debug_mode = inc_debug_mode;
+ gen->header.inc_debug_pal = inc_debug_pal;
set_region(gen, rom, force_region);
gen->vdp = malloc(sizeof(vdp_context));
@@ -855,47 +972,6 @@ genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on
return gen;
}
-
-
-void free_genesis(genesis_context *gen)
-{
- vdp_free(gen->vdp);
- m68k_options_free(gen->m68k->options);
- free(gen->m68k);
- free(gen->work_ram);
- z80_options_free(gen->z80->options);
- free(gen->z80);
- free(gen->zram);
- ym_free(gen->ym);
- psg_free(gen->psg);
- free(gen->save_storage);
- free(gen->save_dir);
- free(gen->lock_on);
-}
-
-void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger)
-{
-
- if (statefile) {
- uint32_t pc = load_gst(gen, statefile);
- if (!pc) {
- fatal_error("Failed to load save state %s\n", statefile);
- }
- printf("Loaded %s\n", statefile);
- if (debugger) {
- insert_breakpoint(gen->m68k, pc, debugger);
- }
- adjust_int_cycle(gen->m68k, gen->vdp);
- start_68k_context(gen->m68k, pc);
- } else {
- if (debugger) {
- uint32_t address = gen->cart[2] << 16 | gen->cart[3];
- insert_breakpoint(gen->m68k, address, debugger);
- }
- m68k_reset(gen->m68k);
- }
-}
-
genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, rom_info *info_out)
{
static memmap_chunk base_map[] = {
diff --git a/genesis.h b/genesis.h
index 11f132e..6a2ab6d 100644
--- a/genesis.h
+++ b/genesis.h
@@ -7,6 +7,7 @@
#define GENESIS_H_
#include <stdint.h>
+#include "system.h"
#include "m68k_core.h"
#include "z80_to_x86.h"
#include "ym2612.h"
@@ -19,20 +20,17 @@
typedef struct genesis_context genesis_context;
struct genesis_context {
+ system_header header;
m68k_context *m68k;
z80_context *z80;
vdp_context *vdp;
ym2612_context *ym;
psg_context *psg;
- genesis_context *next_context;
uint16_t *cart;
uint16_t *lock_on;
uint16_t *work_ram;
uint8_t *zram;
void *extra;
- arena *arena;
- char *next_rom;
- char *save_dir;
uint8_t *save_storage;
eeprom_map *eeprom_map;
uint32_t num_eeprom;
@@ -45,30 +43,19 @@ struct genesis_context {
uint8_t bank_regs[8];
uint16_t mapper_start_index;
uint8_t save_type;
- io_port ports[3];
+ sega_io io;
uint8_t version_reg;
uint8_t bus_busy;
- uint8_t should_exit;
- uint8_t save_state;
- uint8_t mouse_mode;
- uint8_t mouse_captured;
eeprom_state eeprom;
};
-extern genesis_context * genesis;
-extern int break_on_sync;
-
#define RAM_WORDS 32 * 1024
#define Z80_RAM_BYTES 8 * 1024
uint16_t read_dma_value(uint32_t address);
uint16_t get_open_bus_value();
m68k_context * sync_components(m68k_context *context, uint32_t address);
-m68k_context * debugger(m68k_context * context, uint32_t address);
-void set_speed_percent(genesis_context * context, uint32_t percent);
genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, rom_info *info_out);
-void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger);
-void free_genesis(genesis_context *gen);
#endif //GENESIS_H_
diff --git a/gst.c b/gst.c
index 6a27387..4a16b0b 100644
--- a/gst.c
+++ b/gst.c
@@ -434,8 +434,8 @@ uint32_t load_gst(genesis_context * gen, char * fname)
if (!z80_load_gst(gen->z80, gstfile)) {
goto error_close;
}
- gen->ports[0].control = 0x40;
- gen->ports[1].control = 0x40;
+ gen->io.ports[0].control = 0x40;
+ gen->io.ports[1].control = 0x40;
fseek(gstfile, GST_68K_RAM, SEEK_SET);
for (int i = 0; i < (32*1024);) {
diff --git a/io.c b/io.c
index 3a6db6c..c3808df 100644
--- a/io.c
+++ b/io.c
@@ -107,7 +107,8 @@ typedef struct {
#define DEFAULT_JOYBUTTON_ALLOC 12
-static keybinding * bindings[0x10000];
+static sega_io *current_io;
+static keybinding *bindings[0x10000];
static joystick joysticks[MAX_JOYSTICKS];
static mousebinding mice[MAX_MICE];
static io_port *keyboard_port;
@@ -294,8 +295,8 @@ void handle_joydown(int joystick, int button)
void handle_mousedown(int mouse, int button)
{
- if (genesis->mouse_mode == MOUSE_CAPTURE && !genesis->mouse_captured) {
- genesis->mouse_captured = 1;
+ if (current_io->mouse_mode == MOUSE_CAPTURE && !current_io->mouse_captured) {
+ current_io->mouse_captured = 1;
render_relative_mouse(1);
return;
}
@@ -348,24 +349,16 @@ void handle_binding_up(keybinding * binding)
switch (binding->subtype_a)
{
case UI_DEBUG_MODE_INC:
- ui_debug_mode++;
- if (ui_debug_mode == 7) {
- ui_debug_mode = 0;
- }
- genesis->vdp->debug = ui_debug_mode;
+ current_system->inc_debug_mode(current_system);
break;
case UI_DEBUG_PAL_INC:
- ui_debug_pal++;
- if (ui_debug_pal == 4) {
- ui_debug_pal = 0;
- }
- genesis->vdp->debug_pal = ui_debug_pal;
+ current_system->inc_debug_pal(current_system);
break;
case UI_ENTER_DEBUGGER:
- break_on_sync = 1;
+ current_system->enter_debugger = 1;
break;
case UI_SAVE_STATE:
- genesis->save_state = QUICK_SAVE_SLOT+1;
+ current_system->save_state = QUICK_SAVE_SLOT+1;
break;
case UI_NEXT_SPEED:
current_speed++;
@@ -373,7 +366,7 @@ void handle_binding_up(keybinding * binding)
current_speed = 0;
}
printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
- set_speed_percent(genesis, speeds[current_speed]);
+ current_system->set_speed_percent(current_system, speeds[current_speed]);
break;
case UI_PREV_SPEED:
current_speed--;
@@ -381,26 +374,27 @@ void handle_binding_up(keybinding * binding)
current_speed = num_speeds - 1;
}
printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
- set_speed_percent(genesis, speeds[current_speed]);
+ current_system->set_speed_percent(current_system, speeds[current_speed]);
break;
case UI_SET_SPEED:
if (binding->value < num_speeds) {
current_speed = binding->value;
printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
- set_speed_percent(genesis, speeds[current_speed]);
+ current_system->set_speed_percent(current_system, speeds[current_speed]);
} else {
printf("Setting speed to %d\n", speeds[current_speed]);
- set_speed_percent(genesis, binding->value);
+ current_system->set_speed_percent(current_system, speeds[current_speed]);
}
break;
case UI_RELEASE_MOUSE:
- if (genesis->mouse_captured) {
- genesis->mouse_captured = 0;
+ if (current_io->mouse_captured) {
+ current_io->mouse_captured = 0;
render_relative_mouse(0);
}
break;
case UI_EXIT:
- genesis->m68k->should_return = 1;
+ current_system->request_exit(current_system);
+ break;
}
break;
}
@@ -460,7 +454,7 @@ void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16
return;
}
//TODO: relative mode
- switch(genesis->mouse_mode)
+ switch(current_io->mouse_mode)
{
case MOUSE_ABSOLUTE: {
float scale_x = 640.0 / ((float)render_width());
@@ -476,7 +470,7 @@ void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16
break;
}
case MOUSE_CAPTURE: {
- if (genesis->mouse_captured) {
+ if (current_io->mouse_captured) {
mice[mouse].motion_port->device.mouse.cur_x += deltax;
mice[mouse].motion_port->device.mouse.cur_y += deltay;
}
@@ -721,7 +715,8 @@ static void cleanup_sockfile()
void setup_io_devices(tern_node * config, rom_info *rom, genesis_context *gen)
{
- io_port * ports = gen->ports;
+ current_io = &gen->io;
+ io_port * ports = current_io->ports;
tern_node *io_nodes = tern_get_node(tern_find_path(config, "io\0devices\0"));
char * io_1 = rom->port1_override ? rom->port1_override : tern_find_ptr(io_nodes, "1");
char * io_2 = rom->port2_override ? rom->port2_override : tern_find_ptr(io_nodes, "2");
@@ -732,13 +727,13 @@ void setup_io_devices(tern_node * config, rom_info *rom, genesis_context *gen)
process_device(io_ext, ports+2);
if (render_fullscreen()) {
- gen->mouse_mode = MOUSE_RELATIVE;
+ current_io->mouse_mode = MOUSE_RELATIVE;
render_relative_mouse(1);
} else {
if (rom->mouse_mode && !strcmp(rom->mouse_mode, "absolute")) {
- gen->mouse_mode = MOUSE_ABSOLUTE;
+ current_io->mouse_mode = MOUSE_ABSOLUTE;
} else {
- gen->mouse_mode = MOUSE_CAPTURE;
+ current_io->mouse_mode = MOUSE_CAPTURE;
}
}
@@ -926,8 +921,15 @@ void process_mouse(char *mousenum, tern_val value, void *data)
}
}
-void set_keybindings(io_port *ports)
+void set_keybindings(sega_io *io)
{
+ static uint8_t already_done;
+ if (already_done) {
+ map_all_bindings(io);
+ return;
+ }
+ already_done = 1;
+ io_port *ports = io->ports;
tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP);
special = tern_insert_int(special, "down", RENDERKEY_DOWN);
special = tern_insert_int(special, "left", RENDERKEY_LEFT);
@@ -1067,11 +1069,14 @@ void set_keybindings(io_port *ports)
speeds[i] = 100;
}
}
- map_all_bindings(ports);
+ map_all_bindings(io);
}
-void map_all_bindings(io_port *ports)
+void map_all_bindings(sega_io *io)
{
+ current_io = io;
+ io_port *ports = io->ports;
+
for (int bucket = 0; bucket < 0x10000; bucket++)
{
if (bindings[bucket])
@@ -1117,7 +1122,7 @@ void map_all_bindings(io_port *ports)
}
//not really related to the intention of this function, but the best place to do this currently
if (speeds[0] != 100) {
- set_speed_percent(genesis, speeds[0]);
+ current_system->set_speed_percent(current_system, speeds[0]);
}
}
@@ -1133,7 +1138,7 @@ void mouse_check_ready(io_port *port, uint32_t current_cycle)
if (port->device.mouse.tr_counter == 3) {
port->device.mouse.latched_x = port->device.mouse.cur_x;
port->device.mouse.latched_y = port->device.mouse.cur_y;
- if (genesis->mouse_mode == MOUSE_ABSOLUTE) {
+ if (current_io->mouse_mode == MOUSE_ABSOLUTE) {
//avoid overflow in absolute mode
int deltax = port->device.mouse.latched_x - port->device.mouse.last_read_x;
if (abs(deltax) > 255) {
diff --git a/io.h b/io.h
index ead06ba..b0de148 100644
--- a/io.h
+++ b/io.h
@@ -59,6 +59,12 @@ typedef struct {
uint8_t device_type;
} io_port;
+typedef struct {
+ io_port ports[3];
+ uint8_t mouse_mode;
+ uint8_t mouse_captured;
+} sega_io;
+
#define GAMEPAD_TH0 0
#define GAMEPAD_TH1 1
#define GAMEPAD_EXTRA 2
@@ -77,8 +83,8 @@ enum {
typedef struct genesis_context genesis_context;
-void set_keybindings(io_port *ports);
-void map_all_bindings(io_port *ports);
+void set_keybindings(sega_io *io);
+void map_all_bindings(sega_io *io);
void setup_io_devices(tern_node * config, rom_info *rom, genesis_context * gen);
void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction);
void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle);
diff --git a/m68k_core.h b/m68k_core.h
index 277a2e5..4d3634e 100644
--- a/m68k_core.h
+++ b/m68k_core.h
@@ -72,6 +72,7 @@ typedef struct m68k_context {
} m68k_context;
typedef m68k_context *(*m68k_reset_handler)(m68k_context *context);
+typedef m68k_context *(*m68k_debug_handler)(m68k_context *context, uint32_t pc);
void translate_m68k_stream(uint32_t address, m68k_context * context);
void start_68k_context(m68k_context * context, uint32_t address);
@@ -80,7 +81,7 @@ void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chu
m68k_context * init_68k_context(m68k_options * opts, m68k_reset_handler reset_handler);
void m68k_reset(m68k_context * context);
void m68k_options_free(m68k_options *opts);
-void insert_breakpoint(m68k_context * context, uint32_t address, uint8_t * bp_handler);
+void insert_breakpoint(m68k_context * context, uint32_t address, m68k_debug_handler bp_handler);
void remove_breakpoint(m68k_context * context, uint32_t address);
m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context);
uint32_t get_instruction_start(m68k_options *opts, native_map_slot * native_code_map, uint32_t address);
diff --git a/m68k_core_x86.c b/m68k_core_x86.c
index 1546585..c1ba19d 100644
--- a/m68k_core_x86.c
+++ b/m68k_core_x86.c
@@ -2289,7 +2289,7 @@ m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context)
return context;
}
-void insert_breakpoint(m68k_context * context, uint32_t address, code_ptr bp_handler)
+void insert_breakpoint(m68k_context * context, uint32_t address, m68k_debug_handler bp_handler)
{
static code_ptr bp_stub = NULL;
m68k_options * opts = context->options;
@@ -2315,7 +2315,7 @@ void insert_breakpoint(m68k_context * context, uint32_t address, code_ptr bp_han
//Save context and call breakpoint handler
call(code, opts->gen.save_context);
push_r(code, opts->gen.scratch1);
- call_args_abi(code, bp_handler, 2, opts->gen.context_reg, opts->gen.scratch1);
+ call_args_abi(code, (code_ptr)bp_handler, 2, opts->gen.context_reg, opts->gen.scratch1);
mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR);
//Restore context
call(code, opts->gen.load_context);
diff --git a/menu.c b/menu.c
index cd770df..eff0a8b 100644
--- a/menu.c
+++ b/menu.c
@@ -269,7 +269,7 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
char buf[4096];
copy_string_from_guest(m68k, dst, buf, sizeof(buf));
char const *pieces[] = {menu->curpath, PATH_SEP, buf};
- gen->next_rom = alloc_concat_m(3, pieces);
+ gen->header.next_rom = alloc_concat_m(3, pieces);
m68k->should_return = 1;
break;
}
@@ -278,7 +278,7 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
{
case 1:
m68k->should_return = 1;
- gen->should_exit = 1;
+ gen->header.should_exit = 1;
break;
case 2:
m68k->should_return = 1;
@@ -290,10 +290,10 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
case 4: {
char *buffer = malloc(SAVE_INFO_BUFFER_SIZE);
char *cur = buffer;
- if (gen->next_context && gen->next_context->save_dir) {
+ if (gen->header.next_context && gen->header.next_context->save_dir) {
char *end = buffer + SAVE_INFO_BUFFER_SIZE;
char slotfile[] = "slot_0.gst";
- char const * parts[3] = {gen->next_context->save_dir, PATH_SEP, slotfile};
+ char const * parts[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfile};
struct tm ltime;
char *fname;
time_t modtime;
@@ -338,14 +338,14 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
break;
case 5:
//save state
- if (gen->next_context) {
- gen->next_context->save_state = dst + 1;
+ if (gen->header.next_context) {
+ gen->header.next_context->save_state = dst + 1;
}
m68k->should_return = 1;
break;
case 6:
//load state
- if (gen->next_context && gen->next_context->save_dir) {
+ if (gen->header.next_context && gen->header.next_context->save_dir) {
char numslotname[] = "slot_0.gst";
char *slotname;
if (dst == QUICK_SAVE_SLOT) {
@@ -354,14 +354,15 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
numslotname[5] = '0' + dst;
slotname = numslotname;
}
- char const *parts[] = {gen->next_context->save_dir, PATH_SEP, slotname};
+ char const *parts[] = {gen->header.next_context->save_dir, PATH_SEP, slotname};
char *gstpath = alloc_concat_m(3, parts);
- uint32_t pc = load_gst(gen->next_context, gstpath);
+ genesis_context *next = (genesis_context *)gen->header.next_context;
+ uint32_t pc = load_gst(next, gstpath);
free(gstpath);
if (!pc) {
break;
}
- gen->next_context->m68k->resume_pc = get_native_address_trans(gen->next_context->m68k, pc);
+ next->m68k->resume_pc = get_native_address_trans(next->m68k, pc);
}
m68k->should_return = 1;
break;
diff --git a/system.c b/system.c
new file mode 100644
index 0000000..5d5e9f8
--- /dev/null
+++ b/system.c
@@ -0,0 +1,33 @@
+#include <string.h>
+#include "system.h"
+#include "genesis.h"
+
+system_type detect_system_type(uint8_t *rom, long filesize)
+{
+ if (filesize >= 0x104 && !memcmp("SEGA", rom + 0x100, 4)) {
+ //TODO: Differentiate between vanilla Genesis and Sega CD/32X games
+ return SYSTEM_GENESIS;
+ }
+ //TODO: Detect SMS and Jaguar ROMs here
+
+ //More certain checks failed, look for a valid 68K reset vector
+ if (filesize >= 8) {
+ uint32_t reset = rom[4] << 24 | rom[5] << 16 | rom[6] << 8 | rom[7];
+ if (!(reset & 1) && reset < filesize) {
+ //we have a valid looking reset vector, assume it's a Genesis ROM
+ return SYSTEM_GENESIS;
+ }
+ }
+ return SYSTEM_UNKNOWN;
+}
+
+system_header *alloc_config_system(system_type stype, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t opts, uint8_t force_region, rom_info *info_out)
+{
+ switch (stype)
+ {
+ case SYSTEM_GENESIS:
+ return &(alloc_config_genesis(rom, rom_size, lock_on, lock_on_size, opts, force_region, info_out))->header;
+ default:
+ return NULL;
+ }
+}
diff --git a/system.h b/system.h
new file mode 100644
index 0000000..97dde44
--- /dev/null
+++ b/system.h
@@ -0,0 +1,49 @@
+#ifndef SYSTEM_H_
+#define SYSTEM_H_
+#include <stdint.h>
+#include "arena.h"
+#include "romdb.h"
+
+typedef struct system_header system_header;
+
+typedef enum {
+ SYSTEM_UNKNOWN,
+ SYSTEM_GENESIS,
+ SYSTEM_SMS,
+ SYSTEM_JAGUAR
+} system_type;
+
+typedef enum {
+ DEBUGGER_NATIVE,
+ DEBUGGER_GDB
+} debugger_type;
+
+typedef void (*system_fun)(system_header *);
+typedef void (*start_system_fun)(system_header *, char *);
+typedef void (*speed_system_fun)(system_header *, uint32_t);
+
+struct system_header {
+ system_header *next_context;
+ start_system_fun start_context;
+ system_fun resume_context;
+ system_fun load_save;
+ system_fun persist_save;
+ system_fun request_exit;
+ system_fun free_context;
+ speed_system_fun set_speed_percent;
+ system_fun inc_debug_mode;
+ system_fun inc_debug_pal;
+ arena *arena;
+ char *next_rom;
+ char *save_dir;
+ uint8_t enter_debugger;
+ uint8_t should_exit;
+ uint8_t save_state;
+ debugger_type debugger_type;
+ system_type type;
+};
+
+system_type detect_system_type(uint8_t *rom, long filesize);
+system_header *alloc_config_system(system_type stype, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t opts, uint8_t force_region, rom_info *info_out);
+
+#endif //SYSTEM_H_