changeset 1111:2eb54e24914e

Mostly working changes to allow support for multiple emulated system types in main blastem program
author Michael Pavone <pavone@retrodev.com>
date Mon, 19 Dec 2016 13:28:18 -0800
parents d1eed3b1121c
children 7ab7c8fb34ba
files Makefile blastem.c blastem.h debug.c gdb_remote.c genesis.c genesis.h gst.c io.c io.h m68k_core.h m68k_core_x86.c menu.c system.c system.h
diffstat 15 files changed, 367 insertions(+), 209 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Dec 14 23:27:42 2016 -0800
+++ b/Makefile	Mon Dec 19 13:28:18 2016 -0800
@@ -125,7 +125,7 @@
 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
--- a/blastem.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/blastem.c	Mon Dec 19 13:28:18 2016 -0800
@@ -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 @@
 	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 @@
 			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 @@
 		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_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 @@
 	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 @@
 	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 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 @@
 				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 @@
 				break;
 			case 'D':
 				gdb_remote_init();
-				debuggerfun = (uint8_t *)gdb_debug_enter;
+				dtype = DEBUGGER_GDB;
 				break;
 			case 'f':
 				fullscreen = !fullscreen;
@@ -256,7 +254,7 @@
 				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 @@
 				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 @@
 			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 @@
 			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 @@
 
 	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 @@
 			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;
 		}
--- a/blastem.h	Wed Dec 14 23:27:42 2016 -0800
+++ b/blastem.h	Mon Dec 19 13:28:18 2016 -0800
@@ -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 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_
--- a/debug.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/debug.c	Mon Dec 19 13:28:18 2016 -0800
@@ -603,7 +603,7 @@
 					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 @@
 				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 @@
 				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 @@
 					} 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 @@
 							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 @@
 						} 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 @@
 				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;
--- a/gdb_remote.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/gdb_remote.c	Mon Dec 19 13:28:18 2016 -0800
@@ -216,16 +216,16 @@
 			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 @@
 		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 @@
 					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;
--- a/genesis.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/genesis.c	Mon Dec 19 13:28:18 2016 -0800
@@ -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 @@
 
 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 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 @@
 		}
 
 		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 @@
 		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 @@
 			} 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 @@
 			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 @@
 			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 @@
 				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 @@
 	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 @@
 	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 @@
 		{ 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 @@
 	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[] = {
--- a/genesis.h	Wed Dec 14 23:27:42 2016 -0800
+++ b/genesis.h	Mon Dec 19 13:28:18 2016 -0800
@@ -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 @@
 	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_
 
--- a/gst.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/gst.c	Mon Dec 19 13:28:18 2016 -0800
@@ -434,8 +434,8 @@
 	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);) {
--- a/io.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/io.c	Mon Dec 19 13:28:18 2016 -0800
@@ -107,7 +107,8 @@
 
 #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_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 @@
 		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 @@
 				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 @@
 				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 @@
 		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 @@
 		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 @@
 
 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 @@
 	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 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 @@
 			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 @@
 	}
 	//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 @@
 		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) {
--- a/io.h	Wed Dec 14 23:27:42 2016 -0800
+++ b/io.h	Mon Dec 19 13:28:18 2016 -0800
@@ -59,6 +59,12 @@
 	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 @@
 
 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);
--- a/m68k_core.h	Wed Dec 14 23:27:42 2016 -0800
+++ b/m68k_core.h	Mon Dec 19 13:28:18 2016 -0800
@@ -72,6 +72,7 @@
 } 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 @@
 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);
--- a/m68k_core_x86.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/m68k_core_x86.c	Mon Dec 19 13:28:18 2016 -0800
@@ -2289,7 +2289,7 @@
 	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 @@
 		//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);
--- a/menu.c	Wed Dec 14 23:27:42 2016 -0800
+++ b/menu.c	Mon Dec 19 13:28:18 2016 -0800
@@ -269,7 +269,7 @@
 			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 @@
 			{
 			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 @@
 		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 @@
 			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 @@
 					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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system.c	Mon Dec 19 13:28:18 2016 -0800
@@ -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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system.h	Mon Dec 19 13:28:18 2016 -0800
@@ -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_