changeset 1103:22e87b739ad6

WIP split of ROM loading/argument parsing from Genesis emulation code. Compiles and doesn't crash, but nothing works. Still a few too many globals as well.
author Michael Pavone <pavone@retrodev.com>
date Fri, 09 Dec 2016 09:48:48 -0800
parents c15896605bf2
children 4224980a5f84
files blastem.c blastem.h debug.c gdb_remote.c gdb_remote.h genesis.c genesis.h gst.c io.c menu.c render.h render_sdl.c romdb.c stateview.c util.c util.h vdp.c vgmplay.c
diffstat 18 files changed, 1093 insertions(+), 1068 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/blastem.c	Fri Dec 09 09:48:48 2016 -0800
@@ -14,42 +14,23 @@
 #include "mem.h"
 #include "vdp.h"
 #include "render.h"
-#include "blastem.h"
+#include "genesis.h"
 #include "gdb_remote.h"
 #include "gst.h"
 #include "util.h"
 #include "romdb.h"
 #include "terminal.h"
 #include "arena.h"
+#include "config.h"
 
 #define BLASTEM_VERSION "0.4.1"
 
-#define MCLKS_NTSC 53693175
-#define MCLKS_PAL  53203395
-
-uint32_t MCLKS_PER_68K;
-#define MCLKS_PER_YM  7
-#define MCLKS_PER_Z80 15
-#define MCLKS_PER_PSG (MCLKS_PER_Z80*16)
-#define DEFAULT_SYNC_INTERVAL MCLKS_LINE
-#define DEFAULT_LOWPASS_CUTOFF 3390
-
-//TODO: Figure out the exact value for this
-#define LINES_NTSC 262
-#define LINES_PAL 312
-
-#define MAX_SOUND_CYCLES 100000
-
 #ifdef __ANDROID__
 #define FULLSCREEN_DEFAULT 1
 #else
 #define FULLSCREEN_DEFAULT 0
 #endif
 
-uint16_t *cart;
-uint16_t *ram;
-uint8_t z80_ram[Z80_RAM_BYTES];
-
 int headless = 0;
 int exit_after = 0;
 int z80_enabled = 1;
@@ -67,13 +48,13 @@
 #define SMD_MAGIC3 0xBB
 #define SMD_BLOCK_SIZE 0x4000
 
-int load_smd_rom(long filesize, FILE * f)
+int load_smd_rom(long filesize, FILE * f, uint16_t **buffer)
 {
 	uint8_t block[SMD_BLOCK_SIZE];
 	filesize -= SMD_HEADER_SIZE;
 	fseek(f, SMD_HEADER_SIZE, SEEK_SET);
 
-	uint16_t * dst = cart = malloc(nearest_pow2(filesize));
+	uint16_t *dst = *buffer = malloc(nearest_pow2(filesize));
 	int rom_size = filesize;
 	while (filesize > 0) {
 		fread(block, 1, SMD_BLOCK_SIZE, f);
@@ -85,15 +66,7 @@
 	return rom_size;
 }
 
-void byteswap_rom(int filesize)
-{
-	for(unsigned short * cur = cart; cur - cart < filesize/2; ++cur)
-	{
-		*cur = (*cur >> 8) | (*cur << 8);
-	}
-}
-
-int load_rom(char * filename)
+int load_rom(char * filename, uint16_t **dst)
 {
 	uint8_t header[10];
 	FILE * f = fopen(filename, "rb");
@@ -117,742 +90,25 @@
 			if (header[2]) {
 				fatal_error("%s is a split SMD ROM which is not currently supported", filename);
 			}
-			return load_smd_rom(filesize, f);
+			return load_smd_rom(filesize, f, dst);
 		}
 	}
-	cart = malloc(nearest_pow2(filesize));
-	if (filesize != fread(cart, 1, filesize, f)) {
+	*dst = malloc(nearest_pow2(filesize));
+	if (filesize != fread(*dst, 1, filesize, f)) {
 		fatal_error("Error reading from %s\n", filename);
 	}
 	fclose(f);
 	return filesize;
 }
 
-uint16_t read_dma_value(uint32_t address)
-{
-	//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) {
-		return *ptr;
-	}
-	//TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area
-	return 0;
-}
 
-uint16_t get_open_bus_value()
-{
-	return read_dma_value(genesis->m68k->last_prefetch_address/2);
-}
-
-void adjust_int_cycle(m68k_context * context, vdp_context * v_context)
-{
-	//static int old_int_cycle = CYCLE_NEVER;
-	genesis_context *gen = context->system;
-	if (context->sync_cycle - context->current_cycle > gen->max_cycles) {
-		context->sync_cycle = context->current_cycle + gen->max_cycles;
-	}
-	context->int_cycle = CYCLE_NEVER;
-	if ((context->status & 0x7) < 6) {
-		uint32_t next_vint = vdp_next_vint(v_context);
-		if (next_vint != CYCLE_NEVER) {
-			context->int_cycle = next_vint;
-			context->int_num = 6;
-		}
-		if ((context->status & 0x7) < 4) {
-			uint32_t next_hint = vdp_next_hint(v_context);
-			if (next_hint != CYCLE_NEVER) {
-				next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint;
-				if (next_hint < context->int_cycle) {
-					context->int_cycle = next_hint;
-					context->int_num = 4;
-
-				}
-			}
-		}
-	}
-	if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) {
-		context->int_pending = INT_PENDING_NONE;
-	}
-	/*if (context->int_cycle != old_int_cycle) {
-		printf("int cycle changed to: %d, level: %d @ %d(%d), frame: %d, vcounter: %d, hslot: %d, mask: %d, hint_counter: %d\n", context->int_cycle, context->int_num, v_context->cycles, context->current_cycle, v_context->frame, v_context->vcounter, v_context->hslot, context->status & 0x7, v_context->hint_counter);
-		old_int_cycle = context->int_cycle;
-	}*/
-
-	context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle;
-	if (context->should_return) {
-		context->target_cycle = context->current_cycle;
-	} else if (context->target_cycle < context->current_cycle) {
-		//Changes to SR can result in an interrupt cycle that's in the past
-		//This can cause issues with the implementation of STOP though
-		context->target_cycle = context->current_cycle;
-	}
-	/*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n",
-		context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7),
-		v_context->regs[REG_MODE_2] & 0x20, v_context->regs[REG_MODE_1] & 0x10, v_context->hint_counter, v_context->regs[REG_HINT], v_context->cycles / MCLKS_LINE);*/
-}
 
 int break_on_sync = 0;
 char *save_state_path;
 
-//#define DO_DEBUG_PRINT
-#ifdef DO_DEBUG_PRINT
-#define dprintf printf
-#define dputs puts
-#else
-#define dprintf
-#define dputs
-#endif
-
-#define Z80_VINT_DURATION 128
-
-void z80_next_int_pulse(z80_context * z_context)
-{
-		genesis_context * gen = z_context->system;
-	z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp);
-	z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION * MCLKS_PER_Z80;
-			}
-
-void sync_z80(z80_context * z_context, uint32_t mclks)
-{
-#ifndef NO_Z80
-	if (z80_enabled) {
-		z80_run(z_context, mclks);
-	} else
-#endif
-	{
-		z_context->current_cycle = mclks;
-	}
-}
-
-void sync_sound(genesis_context * gen, uint32_t target)
-{
-	//printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2);
-	while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) {
-		uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES;
-		//printf("Running PSG to cycle %d\n", cur_target);
-		psg_run(gen->psg, cur_target);
-		//printf("Running YM-2612 to cycle %d\n", cur_target);
-		ym_run(gen->ym, cur_target);
-	}
-	psg_run(gen->psg, target);
-	ym_run(gen->ym, target);
-
-	//printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2);
-}
-
-uint32_t last_frame_num;
-
-//My refresh emulation isn't currently good enough and causes more problems than it solves
-#ifdef REFRESH_EMULATION
-#define REFRESH_INTERVAL 128
-#define REFRESH_DELAY 2
-uint32_t last_sync_cycle;
-uint32_t refresh_counter;
-#endif
-
-m68k_context * sync_components(m68k_context * context, uint32_t address)
-{
-	genesis_context * gen = context->system;
-	vdp_context * v_context = gen->vdp;
-	z80_context * z_context = gen->z80;
-#ifdef REFRESH_EMULATION
-	//lame estimation of refresh cycle delay
-	if (!gen->bus_busy) {
-		refresh_counter += context->current_cycle - last_sync_cycle;
-		context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL));
-		refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL);
-	}
-#endif
-
-	uint32_t mclks = context->current_cycle;
-	sync_z80(z_context, mclks);
-	sync_sound(gen, mclks);
-	vdp_run_context(v_context, mclks);
-	if (v_context->frame != last_frame_num) {
-		//printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", last_frame_num, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot);
-		last_frame_num = v_context->frame;
-
-		if(exit_after){
-			--exit_after;
-			if (!exit_after) {
-				exit(0);
-			}
-		}
-
-		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);
-		context->current_cycle -= mclks;
-		z80_adjust_cycles(z_context, mclks);
-		gen->ym->current_cycle -= mclks;
-		gen->psg->cycles -= mclks;
-		if (gen->ym->write_cycle != CYCLE_NEVER) {
-			gen->ym->write_cycle = gen->ym->write_cycle >= mclks ? gen->ym->write_cycle - mclks : 0;
-		}
-	}
-	gen->frame_end = vdp_cycles_to_frame_end(v_context);
-	context->sync_cycle = gen->frame_end;
-	//printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot);
-	if (context->int_ack) {
-		//printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot);
-		vdp_int_ack(v_context);
-		context->int_ack = 0;
-	}
-	if (!address && (break_on_sync || gen->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;
-			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;
-			//advance Z80 core to the start of an instruction
-			while (!z_context->pc)
-			{
-				sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80);
-			}
-			char *save_path;
-			if (slot == QUICK_SAVE_SLOT) {
-				save_path = save_state_path;
-			} else {
-				char slotname[] = "slot_0.gst";
-				slotname[5] = '0' + slot;
-				char const *parts[] = {gen->save_dir, PATH_SEP, slotname};
-				save_path = alloc_concat_m(3, parts);
-			}
-			save_gst(gen, save_path, address);
-			printf("Saved state to %s\n", save_path);
-			if (slot != QUICK_SAVE_SLOT) {
-				free(save_path);
-			}
-		} else if(gen->save_state) {
-			context->sync_cycle = context->current_cycle + 1;
-		}
-	}
-#ifdef REFRESH_EMULATION
-	last_sync_cycle = context->current_cycle;
-#endif
-	return context;
-}
-
-m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value)
-{
-	if (vdp_port & 0x2700E0) {
-		fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port);
-	}
-	vdp_port &= 0x1F;
-	//printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle);
-	sync_components(context, 0);
-	genesis_context * gen = context->system;
-	vdp_context *v_context = gen->vdp;
-	if (vdp_port < 0x10) {
-		int blocked;
-		uint32_t before_cycle = v_context->cycles;
-		if (vdp_port < 4) {
-
-			while (vdp_data_port_write(v_context, value) < 0) {
-				while(v_context->flags & FLAG_DMA_RUN) {
-					vdp_run_dma_done(v_context, gen->frame_end);
-					if (v_context->cycles >= gen->frame_end) {
-						uint32_t cycle_diff = v_context->cycles - context->current_cycle;
-						uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K;
-						if (m68k_cycle_diff < cycle_diff) {
-							m68k_cycle_diff += MCLKS_PER_68K;
-						}
-						context->current_cycle += m68k_cycle_diff;
-						gen->bus_busy = 1;
-						sync_components(context, 0);
-						gen->bus_busy = 0;
-					}
-				}
-				//context->current_cycle = v_context->cycles;
-			}
-		} else if(vdp_port < 8) {
-			blocked = vdp_control_port_write(v_context, value);
-			if (blocked) {
-				while (blocked) {
-					while(v_context->flags & FLAG_DMA_RUN) {
-						vdp_run_dma_done(v_context, gen->frame_end);
-						if (v_context->cycles >= gen->frame_end) {
-							uint32_t cycle_diff = v_context->cycles - context->current_cycle;
-							uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K;
-							if (m68k_cycle_diff < cycle_diff) {
-								m68k_cycle_diff += MCLKS_PER_68K;
-							}
-							context->current_cycle += m68k_cycle_diff;
-							gen->bus_busy = 1;
-							sync_components(context, 0);
-							gen->bus_busy = 0;
-						}
-						if (!(v_context->flags & FLAG_DMA_RUN)) {
-							//two more slots of delay are needed to kill sufficient sprite capacity in Overdrive
-							//TODO: Measure exact value with logic analyzer
-							vdp_run_context(v_context, v_context->cycles + 1);
-							vdp_run_context(v_context, v_context->cycles + 1);
-						}
-					}
-					
-					if (blocked < 0) {
-						blocked = vdp_control_port_write(v_context, value);
-					} else {
-						blocked = 0;
-					}
-				}
-			} else {
-				context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context);
-				//printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot);
-				adjust_int_cycle(context, v_context);
-			}
-		} else {
-			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
-		}
-		if (v_context->cycles != before_cycle) {
-			//printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
-			uint32_t cycle_diff = v_context->cycles - context->current_cycle;
-			uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K;
-			if (m68k_cycle_diff < cycle_diff) {
-				m68k_cycle_diff += MCLKS_PER_68K;
-			}
-			context->current_cycle += m68k_cycle_diff;
-#ifdef REFRESH_EMULATION
-			last_sync_cycle = context->current_cycle;
-#endif
-			//Lock the Z80 out of the bus until the VDP access is complete
-			gen->bus_busy = 1;
-			sync_z80(gen->z80, v_context->cycles);
-			gen->bus_busy = 0;
-		}
-	} else if (vdp_port < 0x18) {
-		psg_write(gen->psg, value);
-	} else {
-		//TODO: Implement undocumented test register(s)
-	}
-	return context;
-}
-
-m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value)
-{
-	return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : ((vdp_port & 1) ? value : 0));
-}
-
-void * z80_vdp_port_write(uint32_t vdp_port, void * vcontext, uint8_t value)
-{
-	z80_context * context = vcontext;
-	genesis_context * gen = context->system;
-	vdp_port &= 0xFF;
-	if (vdp_port & 0xE0) {
-		fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port);
-	}
-	if (vdp_port < 0x10) {
-		//These probably won't currently interact well with the 68K accessing the VDP
-		vdp_run_context(gen->vdp, context->current_cycle);
-		if (vdp_port < 4) {
-			vdp_data_port_write(gen->vdp, value << 8 | value);
-		} else if (vdp_port < 8) {
-			vdp_control_port_write(gen->vdp, value << 8 | value);
-		} else {
-			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
-		}
-	} else if (vdp_port < 0x18) {
-		sync_sound(gen, context->current_cycle);
-		psg_write(gen->psg, value);
-	} else {
-		vdp_test_port_write(gen->vdp, value);
-	}
-	return context;
-}
-
-uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context)
-{
-	if (vdp_port & 0x2700E0) {
-		fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port);
-	}
-	vdp_port &= 0x1F;
-	uint16_t value;
-	sync_components(context, 0);
-	genesis_context *gen = context->system;
-	vdp_context * v_context = gen->vdp;
-	uint32_t before_cycle = v_context->cycles;
-	if (vdp_port < 0x10) {
-		if (vdp_port < 4) {
-			value = vdp_data_port_read(v_context);
-		} else if(vdp_port < 8) {
-			value = vdp_control_port_read(v_context);
-		} else {
-			value = vdp_hv_counter_read(v_context);
-			//printf("HV Counter: %X at cycle %d\n", value, v_context->cycles);
-		}
-	} else if (vdp_port < 0x18){
-		fatal_error("Illegal read from PSG  port %X\n", vdp_port);
-	} else {
-		value = vdp_test_port_read(v_context);
-	}
-	if (v_context->cycles != before_cycle) {
-		//printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
-		context->current_cycle = v_context->cycles;
-#ifdef REFRES_EMULATION
-		last_sync_cycle = context->current_cycle;
-#endif
-		//Lock the Z80 out of the bus until the VDP access is complete
-		genesis_context *gen = context->system;
-		gen->bus_busy = 1;
-		sync_z80(gen->z80, v_context->cycles);
-		gen->bus_busy = 0;
-	}
-	return value;
-}
-
-uint8_t vdp_port_read_b(uint32_t vdp_port, m68k_context * context)
-{
-	uint16_t value = vdp_port_read(vdp_port, context);
-	if (vdp_port & 1) {
-		return value;
-	} else {
-		return value >> 8;
-	}
-}
-
-uint8_t z80_vdp_port_read(uint32_t vdp_port, void * vcontext)
-{
-	z80_context * context = vcontext;
-	if (vdp_port & 0xE0) {
-		fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port);
-	}
-	genesis_context * gen = context->system;
-	//VDP access goes over the 68K bus like a bank area access
-	//typical delay from bus arbitration
-	context->current_cycle += 3 * MCLKS_PER_Z80;
-	//TODO: add cycle for an access right after a previous one
-	//TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high
-	//      Needs a new logic analyzer capture to get the actual delay on the 68K side
-	gen->m68k->current_cycle += 8 * MCLKS_PER_68K;
 
 
-	vdp_port &= 0x1F;
-	uint16_t ret;
-	if (vdp_port < 0x10) {
-		//These probably won't currently interact well with the 68K accessing the VDP
-		vdp_run_context(gen->vdp, context->current_cycle);
-		if (vdp_port < 4) {
-			ret = vdp_data_port_read(gen->vdp);
-		} else if (vdp_port < 8) {
-			ret = vdp_control_port_read(gen->vdp);
-		} else {
-			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
-		}
-	} else {
-		//TODO: Figure out the correct value today
-		ret = 0xFFFF;
-	}
-	return vdp_port & 1 ? ret : ret >> 8;
-}
 
-uint32_t zram_counter = 0;
-
-m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value)
-{
-	genesis_context * gen = context->system;
-	if (location < 0x10000) {
-		//Access to Z80 memory incurs a one 68K cycle wait state
-		context->current_cycle += MCLKS_PER_68K;
-		if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) {
-			location &= 0x7FFF;
-			if (location < 0x4000) {
-				z80_ram[location & 0x1FFF] = value;
-#ifndef NO_Z80
-				z80_handle_code_write(location & 0x1FFF, gen->z80);
-#endif
-			} else if (location < 0x6000) {
-				sync_sound(gen, context->current_cycle);
-				if (location & 1) {
-					ym_data_write(gen->ym, value);
-				} else if(location & 2) {
-					ym_address_write_part2(gen->ym, value);
-				} else {
-					ym_address_write_part1(gen->ym, value);
-				}
-			} else if (location == 0x6000) {
-				gen->z80->bank_reg = (gen->z80->bank_reg >> 1 | value << 8) & 0x1FF;
-				if (gen->z80->bank_reg < 0x80) {
-					gen->z80->mem_pointers[1] = (gen->z80->bank_reg << 15) + ((char *)gen->z80->mem_pointers[2]);
-				} else {
-					gen->z80->mem_pointers[1] = NULL;
-				}
-			} else {
-				fatal_error("68K write to unhandled Z80 address %X\n", location);
-			}
-		}
-	} else {
-		location &= 0x1FFF;
-		if (location < 0x100) {
-			switch(location/2)
-			{
-			case 0x1:
-				io_data_write(gen->ports, value, context->current_cycle);
-				break;
-			case 0x2:
-				io_data_write(gen->ports+1, value, context->current_cycle);
-				break;
-			case 0x3:
-				io_data_write(gen->ports+2, value, context->current_cycle);
-				break;
-			case 0x4:
-				gen->ports[0].control = value;
-				break;
-			case 0x5:
-				gen->ports[1].control = value;
-				break;
-			case 0x6:
-				gen->ports[2].control = value;
-				break;
-			}
-		} else {
-			if (location == 0x1100) {
-				if (value & 1) {
-					dputs("bus requesting Z80");
-					if (z80_enabled) {
-						z80_assert_busreq(gen->z80, context->current_cycle);
-					} else {
-						gen->z80->busack = 1;
-					}
-				} else {
-					if (gen->z80->busreq) {
-						dputs("releasing z80 bus");
-						#ifdef DO_DEBUG_PRINT
-						char fname[20];
-						sprintf(fname, "zram-%d", zram_counter++);
-						FILE * f = fopen(fname, "wb");
-						fwrite(z80_ram, 1, sizeof(z80_ram), f);
-						fclose(f);
-						#endif
-					}
-					if (z80_enabled) {
-						z80_clear_busreq(gen->z80, context->current_cycle);
-					} else {
-						gen->z80->busack = 0;
-					}
-				}
-			} else if (location == 0x1200) {
-				sync_z80(gen->z80, context->current_cycle);
-				if (value & 1) {
-					if (z80_enabled) {
-						z80_clear_reset(gen->z80, context->current_cycle);
-					} else {
-						gen->z80->reset = 0;
-					}
-				} else {
-					if (z80_enabled) {
-						z80_assert_reset(gen->z80, context->current_cycle);
-					} else {
-						gen->z80->reset = 1;
-					}
-				}
-			}
-		}
-	}
-	return context;
-}
-
-m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value)
-{
-	if (location < 0x10000 || (location & 0x1FFF) >= 0x100) {
-		return io_write(location, context, value >> 8);
-	} else {
-		return io_write(location, context, value);
-	}
-}
-
-#define USA 0x80
-#define JAP 0x00
-#define EUR 0xC0
-#define NO_DISK 0x20
-uint8_t version_reg = NO_DISK | USA;
-
-uint8_t io_read(uint32_t location, m68k_context * context)
-{
-	uint8_t value;
-	genesis_context *gen = context->system;
-	if (location < 0x10000) {
-		//Access to Z80 memory incurs a one 68K cycle wait state
-		context->current_cycle += MCLKS_PER_68K;
-		if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) {
-			location &= 0x7FFF;
-			if (location < 0x4000) {
-				value = z80_ram[location & 0x1FFF];
-			} else if (location < 0x6000) {
-				sync_sound(gen, context->current_cycle);
-				value = ym_read_status(gen->ym);
-			} else {
-				value = 0xFF;
-			}
-		} else {
-			value = 0xFF;
-		}
-	} else {
-		location &= 0x1FFF;
-		if (location < 0x100) {
-			switch(location/2)
-			{
-			case 0x0:
-				//version bits should be 0 for now since we're not emulating TMSS
-				value = version_reg;
-				break;
-			case 0x1:
-				value = io_data_read(gen->ports, context->current_cycle);
-				break;
-			case 0x2:
-				value = io_data_read(gen->ports+1, context->current_cycle);
-				break;
-			case 0x3:
-				value = io_data_read(gen->ports+2, context->current_cycle);
-				break;
-			case 0x4:
-				value = gen->ports[0].control;
-				break;
-			case 0x5:
-				value = gen->ports[1].control;
-				break;
-			case 0x6:
-				value = gen->ports[2].control;
-				break;
-			default:
-				value = 0xFF;
-			}
-		} else {
-			if (location == 0x1100) {
-				value = z80_enabled ? !z80_get_busack(gen->z80, context->current_cycle) : !gen->z80->busack;
-				value |= (get_open_bus_value() >> 8) & 0xFE;
-				dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d)\n", value, context->current_cycle, gen->z80->reset);
-			} else if (location == 0x1200) {
-				value = !gen->z80->reset;
-			} else {
-				value = 0xFF;
-				printf("Byte read of unknown IO location: %X\n", location);
-			}
-		}
-	}
-	return value;
-}
-
-uint16_t io_read_w(uint32_t location, m68k_context * context)
-{
-	uint16_t value = io_read(location, context);
-	if (location < 0x10000 || (location & 0x1FFF) < 0x100) {
-		value = value | (value << 8);
-	} else {
-		value <<= 8;
-		value |= get_open_bus_value() & 0xFF;
-	}
-	return value;
-}
-
-void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value)
-{
-	z80_context * context = vcontext;
-	genesis_context * gen = context->system;
-	sync_sound(gen, context->current_cycle);
-	if (location & 1) {
-		ym_data_write(gen->ym, value);
-	} else if (location & 2) {
-		ym_address_write_part2(gen->ym, value);
-	} else {
-		ym_address_write_part1(gen->ym, value);
-	}
-	return context;
-}
-
-uint8_t z80_read_ym(uint32_t location, void * vcontext)
-{
-	z80_context * context = vcontext;
-	genesis_context * gen = context->system;
-	sync_sound(gen, context->current_cycle);
-	return ym_read_status(gen->ym);
-}
-
-uint8_t z80_read_bank(uint32_t location, void * vcontext)
-{
-	z80_context * context = vcontext;
-	genesis_context *gen = context->system;
-	if (gen->bus_busy) {
-		context->current_cycle = context->sync_cycle;
-	}
-	//typical delay from bus arbitration
-	context->current_cycle += 3 * MCLKS_PER_Z80;
-	//TODO: add cycle for an access right after a previous one
-	//TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high
-	//      Needs a new logic analyzer capture to get the actual delay on the 68K side
-	gen->m68k->current_cycle += 8 * MCLKS_PER_68K;
-
-	location &= 0x7FFF;
-	if (context->mem_pointers[1]) {
-		return context->mem_pointers[1][location ^ 1];
-	}
-	uint32_t address = context->bank_reg << 15 | location;
-	if (address >= 0xC00000 && address < 0xE00000) {
-		return z80_vdp_port_read(location & 0xFF, context);
-	} else {
-		fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, context->bank_reg << 15);
-	}
-	return 0;
-}
-
-void *z80_write_bank(uint32_t location, void * vcontext, uint8_t value)
-{
-	z80_context * context = vcontext;
-	genesis_context *gen = context->system;
-	if (gen->bus_busy) {
-		context->current_cycle = context->sync_cycle;
-	}
-	//typical delay from bus arbitration
-	context->current_cycle += 3 * MCLKS_PER_Z80;
-	//TODO: add cycle for an access right after a previous one
-	//TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high
-	//      Needs a new logic analyzer capture to get the actual delay on the 68K side
-	gen->m68k->current_cycle += 8 * MCLKS_PER_68K;
-
-	location &= 0x7FFF;
-	uint32_t address = context->bank_reg << 15 | location;
-	if (address >= 0xE00000) {
-		address &= 0xFFFF;
-		((uint8_t *)ram)[address ^ 1] = value;
-	} else if (address >= 0xC00000) {
-		z80_vdp_port_write(location & 0xFF, context, value);
-	} else {
-		fprintf(stderr, "Unhandled write by Z80 to address %X through banked memory area\n", address);
-	}
-	return context;
-}
-
-void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value)
-{
-	z80_context * context = vcontext;
-
-	context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF;
-	if (context->bank_reg < 0x100) {
-		genesis_context *gen = context->system;
-		context->mem_pointers[1] = get_native_pointer(context->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen);
-	} else {
-		context->mem_pointers[1] = NULL;
-	}
-
-	return context;
-}
-
-void set_speed_percent(genesis_context * context, uint32_t percent)
-{
-	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) {
-		sync_sound(context, context->psg->cycles + MCLKS_PER_PSG);
-}
-	ym_adjust_master_clock(context->ym, context->master_clock);
-	psg_adjust_master_clock(context->psg, context->master_clock);
-}
 
 char * save_filename;
 genesis_context *genesis;
@@ -873,124 +129,9 @@
 	printf("Saved %s to %s\n", game_context->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
 }
 
-#ifndef NO_Z80
-const memmap_chunk z80_map[] = {
-	{ 0x0000, 0x4000,  0x1FFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, z80_ram, NULL, NULL, NULL,              NULL },
-	{ 0x8000, 0x10000, 0x7FFF, 0, 0, 0,                                  NULL,    NULL, NULL, z80_read_bank,     z80_write_bank},
-	{ 0x4000, 0x6000,  0x0003, 0, 0, 0,                                  NULL,    NULL, NULL, z80_read_ym,       z80_write_ym},
-	{ 0x6000, 0x6100,  0xFFFF, 0, 0, 0,                                  NULL,    NULL, NULL, NULL,              z80_write_bank_reg},
-	{ 0x7F00, 0x8000,  0x00FF, 0, 0, 0,                                  NULL,    NULL, NULL, z80_vdp_port_read, z80_vdp_port_write}
-};
-#endif
 
-genesis_context *alloc_init_genesis(rom_info *rom, int fps, uint32_t ym_opts)
-{
-	genesis_context *gen = calloc(1, sizeof(genesis_context));
-	gen->master_clock = gen->normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL;
-
-	gen->vdp = malloc(sizeof(vdp_context));
-	init_vdp_context(gen->vdp, version_reg & 0x40);
-	gen->frame_end = vdp_cycles_to_frame_end(gen->vdp);
-	char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval;
-	gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL;
-
-	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval;
-	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
-	
-	gen->ym = malloc(sizeof(ym2612_context));
-	ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_opts, lowpass_cutoff);
-
-	gen->psg = malloc(sizeof(psg_context));
-	psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff);
-
-	gen->z80 = calloc(1, sizeof(z80_context));
-#ifndef NO_Z80
-	z80_options *z_opts = malloc(sizeof(z80_options));
-	init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80);
-	init_z80_context(gen->z80, z_opts);
-	z80_assert_reset(gen->z80, 0);
-#endif
-
-	gen->z80->system = gen;
-	gen->z80->mem_pointers[0] = z80_ram;
-	gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart;
-
-	gen->cart = cart;
-	gen->work_ram = ram;
-	gen->zram = z80_ram;
-	setup_io_devices(config, rom, gen);
-
-	gen->save_type = rom->save_type;
-	gen->save_type = rom->save_type;
-	if (gen->save_type != SAVE_NONE) {
-		gen->save_ram_mask = rom->save_mask;
-		gen->save_size = rom->save_size;
-		gen->save_storage = rom->save_buffer;
-		gen->eeprom_map = rom->eeprom_map;
-		gen->num_eeprom = rom->num_eeprom;
-		if (gen->save_type == SAVE_I2C) {
-			eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size);
-		}
-	} else {
-		gen->save_storage = NULL;
-	}
-
-	m68k_options *opts = malloc(sizeof(m68k_options));
-	init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K);
-	//TODO: make this configurable
-	opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY;
-	gen->m68k = init_68k_context(opts, NULL);
-	gen->m68k->system = gen;
-
-	for (int i = 0; i < rom->map_chunks; i++)
-	{
-		if (rom->map[i].flags & MMAP_PTR_IDX) {
-			gen->m68k->mem_pointers[rom->map[i].ptr_index] = rom->map[i].buffer;
-		}
-	}
-
-	return gen;
-}
-
-void free_genesis(genesis_context *gen)
-{
-	vdp_free(gen->vdp);
-	m68k_options_free(gen->m68k->options);
-	free(gen->m68k);
-	z80_options_free(gen->z80->options);
-	free(gen->z80);
-	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 = cart[2] << 16 | cart[3];
-			insert_breakpoint(gen->m68k, address, debugger);
-		}
-		m68k_reset(gen->m68k);
-	}
-}
 
 char *title;
-
 void update_title(char *rom_name)
 {
 	if (title) {
@@ -1001,25 +142,6 @@
 	render_update_caption(title);
 }
 
-void set_region(rom_info *info, uint8_t region)
-{
-	if (!region) {
-		char * def_region = tern_find_ptr(config, "default_region");
-		if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) {
-			region = translate_region_char(toupper(*def_region));
-		} else {
-			region = info->regions;
-		}
-	}
-	if (region & REGION_E) {
-		version_reg = NO_DISK | EUR;
-	} else if (region & REGION_J) {
-		version_reg = NO_DISK | JAP;
-	} else {
-		version_reg = NO_DISK | USA;
-	}
-}
-
 void setup_saves(char *fname, rom_info *info, genesis_context *context)
 {
 	char * barename = basename_no_extension(fname);
@@ -1059,12 +181,12 @@
 	int debug = 0;
 	int ym_log = 0;
 	int loaded = 0;
-	uint8_t force_version = 0;
+	uint8_t force_region = 0;
 	char * romfname = NULL;
 	FILE *address_log = NULL;
 	char * statefile = NULL;
 	int rom_size, lock_on_size;
-	uint16_t *lock_on = NULL;
+	uint16_t *cart = NULL, *lock_on = NULL;
 	uint8_t * debuggerfun = NULL;
 	uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1;
 	uint8_t debug_target = 0;
@@ -1111,8 +233,8 @@
 				if (i >= argc) {
 					fatal_error("-r must be followed by region (J, U or E)\n");
 				}
-				force_version = translate_region_char(toupper(argv[i][0]));
-				if (!force_version) {
+				force_region = translate_region_char(toupper(argv[i][0]));
+				if (!force_region) {
 					fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]);
 				}
 				break;
@@ -1134,15 +256,10 @@
 				if (i >= argc) {
 					fatal_error("-o must be followed by a lock on cartridge filename\n");
 				}
-				uint16_t *tmp = cart;
-				lock_on_size = load_rom(argv[i]);
-				if (lock_on_size) {
-					byteswap_rom(lock_on_size);
-					lock_on = cart;
-				} else {
+				lock_on_size = load_rom(argv[i], &lock_on);
+				if (!lock_on_size) {
 					fatal_error("Failed to load lock on cartridge %s\n", argv[i]);
 				}
-				cart = tmp;
 				break;
 			}
 			case 'h':
@@ -1166,7 +283,7 @@
 				fatal_error("Unrecognized switch %s\n", argv[i]);
 			}
 		} else if (!loaded) {
-			if (!(rom_size = load_rom(argv[i]))) {
+			if (!(rom_size = load_rom(argv[i], &cart))) {
 				fatal_error("Failed to open %s for reading\n", argv[i]);
 			}
 			romfname = argv[i];
@@ -1185,7 +302,7 @@
 			romfname = "menu.bin";
 		}
 		if (is_absolute_path(romfname)) {
-			if (!(rom_size = load_rom(romfname))) {
+			if (!(rom_size = load_rom(romfname, &cart))) {
 				fatal_error("Failed to open UI ROM %s for reading", romfname);
 			}
 		} else {
@@ -1199,34 +316,10 @@
 				cart = realloc(cart, rom_size);
 			}
 		}
-		//TODO: load relative to executable or from assets depending on platform
 
 		loaded = 1;
 	}
-	char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval;
-	if (!m68k_divider) {
-		m68k_divider = "7";
-	}
-	MCLKS_PER_68K = atoi(m68k_divider);
-	if (!MCLKS_PER_68K) {
-		MCLKS_PER_68K = 7;
-	}
-	ram = malloc(RAM_WORDS * sizeof(uint16_t));
-	memmap_chunk base_map[] = {
-		{0xE00000, 0x1000000, 0xFFFF,   0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram,
-		           NULL,          NULL,         NULL,            NULL},
-		{0xC00000, 0xE00000,  0x1FFFFF, 0, 0, 0,                                  NULL,
-		           (read_16_fun)vdp_port_read,  (write_16_fun)vdp_port_write,
-		           (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b},
-		{0xA00000, 0xA12000,  0x1FFFF,  0, 0, 0,                                  NULL,
-		           (read_16_fun)io_read_w,      (write_16_fun)io_write_w,
-		           (read_8_fun)io_read,         (write_8_fun)io_write}
-	};
-	tern_node *rom_db = load_rom_db();
-	rom_info info = configure_rom(rom_db, cart, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0]));
-	byteswap_rom(rom_size);
-	set_region(&info, force_version);
-	update_title(info.name);
+	
 	int def_width = 0;
 	char *config_width = tern_find_path(config, "video\0width\0").ptrval;
 	if (config_width) {
@@ -1237,21 +330,20 @@
 	}
 	width = width < 320 ? def_width : width;
 	height = height < 240 ? (width/320) * 240 : height;
-	uint32_t fps = 60;
-	if (version_reg & 0x40) {
-		fps = 50;
-	}
+
 	char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0").ptrval;
 	if (config_fullscreen && !strcmp("on", config_fullscreen)) {
 		fullscreen = !fullscreen;
 	}
 	if (!headless) {
-		render_init(width, height, title, fps, fullscreen);
+		render_init(width, height, "BlastEm", fullscreen);
 	}
 
-	genesis = alloc_init_genesis(&info, fps, (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0);
-	genesis->lock_on = lock_on;
+	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);
+	update_title(info.name);
 	if (menu) {
 		menu_context = genesis;
 	} else {
@@ -1273,32 +365,23 @@
 					persist_save();
 					genesis = menu_context;
 				}
-				free(game_context->cart);
-				base_map[0].buffer = ram = game_context->work_ram;
-			} else {
-				base_map[0].buffer = ram = malloc(RAM_WORDS * sizeof(uint16_t));
-			}
-			memset(ram, 0, RAM_WORDS * sizeof(uint16_t));
-			if (!(rom_size = load_rom(menu_context->next_rom))) {
-				fatal_error("Failed to open %s for reading\n", menu_context->next_rom);
-			}
-			info = configure_rom(rom_db, cart, rom_size, NULL, 0, base_map, sizeof(base_map)/sizeof(base_map[0]));
-			byteswap_rom(rom_size);
-			set_region(&info, force_version);
-			update_title(info.name);
-			if (!game_context) {
-				//start a new arena and save old one in suspended genesis context
-				genesis->arena = start_new_arena();
-			} else {
+				//swap to game context arena and mark all allocated pages in it free
 				genesis->arena = set_current_arena(game_context->arena);
 				mark_all_free();
 				free_genesis(game_context);
+			} else {
+				//start a new arena and save old one in suspended genesis context
+				genesis->arena = start_new_arena();
+			}
+			if (!(rom_size = load_rom(menu_context->next_rom, &cart))) {
+				fatal_error("Failed to open %s for reading\n", menu_context->next_rom);
 			}
 			//allocate new genesis context
-			game_context = alloc_init_genesis(&info, fps, ym_log ? YM_OPT_WAVE_LOG : 0);
+			game_context = alloc_config_genesis(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);
+			update_title(info.name);
 			free(menu_context->next_rom);
 			menu_context->next_rom = NULL;
 			menu = 0;
@@ -1309,16 +392,12 @@
 		} else if (menu && game_context) {
 			genesis->arena = set_current_arena(game_context->arena);
 			genesis = game_context;
-			cart = genesis->cart;
-			ram = genesis->work_ram;
 			menu = 0;
 			map_all_bindings(genesis->ports);
 			resume_68k(genesis->m68k);
 		} else if (!menu && menu_context) {
 			genesis->arena = set_current_arena(menu_context->arena);
 			genesis = menu_context;
-			cart = genesis->cart;
-			ram = genesis->work_ram;
 			menu = 1;
 			map_all_bindings(genesis->ports);
 			resume_68k(genesis->m68k);
--- a/blastem.h	Mon Nov 28 22:45:46 2016 -0800
+++ b/blastem.h	Fri Dec 09 09:48:48 2016 -0800
@@ -1,78 +1,16 @@
-/*
- Copyright 2013 Michael Pavone
- This file is part of BlastEm.
- BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
-*/
 #ifndef BLASTEM_H_
 #define BLASTEM_H_
 
-#include <stdint.h>
-#include "m68k_core.h"
-#include "z80_to_x86.h"
-#include "ym2612.h"
-#include "vdp.h"
-#include "psg.h"
-#include "io.h"
-#include "config.h"
-#include "romdb.h"
-#include "arena.h"
-
-typedef struct genesis_context genesis_context;
+#include "tern.h"
 
-struct genesis_context {
-	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;
-	uint32_t        save_size;
-	uint32_t        save_ram_mask;
-	uint32_t        master_clock; //Current master clock value
-	uint32_t        normal_clock; //Normal master clock (used to restore master clock after turbo mode)
-	uint32_t        frame_end;
-	uint32_t        max_cycles;
-	uint8_t         bank_regs[8];
-	uint16_t        mapper_start_index;
-	uint8_t         save_type;
-	io_port         ports[3];
-	uint8_t         bus_busy;
-	uint8_t         should_exit;
-	uint8_t         save_state;
-	uint8_t         mouse_mode;
-	uint8_t         mouse_captured;
-	eeprom_state    eeprom;
-};
+extern int headless;
+extern int exit_after;
+extern int z80_enabled;
+extern int frame_limit;
 
-extern genesis_context * genesis;
-extern int headless;
-extern int break_on_sync;
 extern tern_node * config;
 
-#define RAM_WORDS 32 * 1024
-#define Z80_RAM_BYTES 8 * 1024
+extern char *save_state_path;
 #define QUICK_SAVE_SLOT 10
 
-extern uint16_t *cart;
-extern uint16_t *ram;
-extern uint8_t z80_ram[Z80_RAM_BYTES];
-
-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);
-
 #endif //BLASTEM_H_
-
--- a/debug.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/debug.c	Fri Dec 09 09:48:48 2016 -0800
@@ -1,5 +1,5 @@
 #include "debug.h"
-#include "blastem.h"
+#include "genesis.h"
 #include "68kinst.h"
 #include <stdlib.h>
 #include <string.h>
@@ -140,6 +140,7 @@
 	uint32_t value;
 	char format[8];
 	strcpy(format, "%s: %d\n");
+	genesis_context *system = context->system;
 	switch (format_char)
 	{
 	case 'x':
@@ -305,14 +306,14 @@
 		if (param[1] == 'x') {
 			uint16_t p_addr = strtol(param+2, NULL, 16);
 			if (p_addr < 0x4000) {
-				value = z80_ram[p_addr & 0x1FFF];
+				value = system->zram[p_addr & 0x1FFF];
 			} else if(p_addr >= 0x8000) {
 				uint32_t v_addr = context->bank_reg << 15;
 				v_addr += p_addr & 0x7FFF;
 				if (v_addr < 0x400000) {
-					value = cart[v_addr/2];
+					value = system->cart[v_addr/2];
 				} else if(v_addr > 0xE00000) {
-					value = ram[(v_addr & 0xFFFF)/2];
+					value = system->work_ram[(v_addr & 0xFFFF)/2];
 				}
 				if (v_addr & 1) {
 					value &= 0xFF;
@@ -333,6 +334,7 @@
 	static uint16_t branch_t;
 	static uint16_t branch_f;
 	z80inst inst;
+	genesis_context *system = context->system;
 	init_terminal();
 	//Check if this is a user set breakpoint, or just a temporary one
 	bp_def ** this_bp = find_breakpoint(&zbreakpoints, address);
@@ -343,7 +345,7 @@
 	}
 	uint8_t * pc;
 	if (address < 0x4000) {
-		pc = z80_ram + (address & 0x1FFF);
+		pc = system->zram + (address & 0x1FFF);
 	} else if (address >= 0x8000) {
 		if (context->bank_reg < (0x400000 >> 15)) {
 			fatal_error("Entered Z80 debugger in banked memory address %X, which is not yet supported\n", address);
@@ -469,7 +471,7 @@
 					after += inst.immed;
 				} else if(inst.op == Z80_RET) {
 					if (context->sp < 0x4000) {
-						after = z80_ram[context->sp & 0x1FFF] | z80_ram[(context->sp+1) & 0x1FFF] << 8;
+						after = system->zram[context->sp & 0x1FFF] | system->zram[(context->sp+1) & 0x1FFF] << 8;
 					}
 				}
 				zinsert_breakpoint(context, after, (uint8_t *)zdebugger);
@@ -495,7 +497,7 @@
 				}
 				FILE * f = fopen(param, "wb");
 				if (f) {
-					if(fwrite(z80_ram, 1, sizeof(z80_ram), f) != sizeof(z80_ram)) {
+					if(fwrite(system->zram, 1, Z80_RAM_BYTES, f) != Z80_RAM_BYTES) {
 						fputs("Error writing file\n", stderr);
 					}
 					fclose(f);
@@ -521,6 +523,7 @@
 {
 	char * param;
 	char format_char;
+	genesis_context *system = context->system;
 	uint32_t value;
 	bp_def *new_bp, **this_bp;
 	switch(input_buf[0])
@@ -571,16 +574,16 @@
 					stack &= 0xFFFF;
 					uint8_t non_adr_count = 0;
 					do {
-						uint32_t bt_address = ram[stack/2] << 16 | ram[stack/2+1];
+						uint32_t bt_address = system->work_ram[stack/2] << 16 | system->work_ram[stack/2+1];
 						bt_address = get_instruction_start(context->options, context->native_code_map, bt_address - 2);
 						if (bt_address) {
 							stack += 4;
 							non_adr_count = 0;
 							uint16_t *bt_pc = NULL;
 							if (bt_address < 0x400000) {
-								bt_pc = cart + bt_address/2;
+								bt_pc = system->cart + bt_address/2;
 							} else if(bt_address > 0xE00000) {
-								bt_pc = ram + (bt_address & 0xFFFF)/2;
+								bt_pc = system->work_ram + (bt_address & 0xFFFF)/2;
 							}
 							m68k_decode(bt_pc, &inst, bt_address);
 							m68k_disasm(&inst, input_buf);
@@ -851,12 +854,8 @@
 		branch_t = branch_f = 0;
 	}
 
-	uint16_t * pc;
-	if (address < 0x400000) {
-		pc = cart + address/2;
-	} else if(address > 0xE00000) {
-		pc = ram + (address & 0xFFFF)/2;
-	} else {
+	uint16_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->options->gen);
+	if (!pc) {
 		fatal_error("Entered 68K debugger at address %X\n", address);
 	}
 	uint16_t * after_pc = m68k_decode(pc, &inst, address);
--- a/gdb_remote.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/gdb_remote.c	Fri Dec 09 09:48:48 2016 -0800
@@ -168,7 +168,7 @@
 		return;
 	}
 	if (address >= 0xA00000 && address < 0xA04000) {
-		z80_ram[address & 0x1FFF] = value;
+		gen->zram[address & 0x1FFF] = value;
 		genesis_context * gen = context->system;
 #ifndef NO_Z80
 		z80_handle_code_write(address & 0x1FFF, gen->z80);
--- a/gdb_remote.h	Mon Nov 28 22:45:46 2016 -0800
+++ b/gdb_remote.h	Fri Dec 09 09:48:48 2016 -0800
@@ -1,6 +1,6 @@
 #ifndef GDB_REMOTE_H_
 #define GDB_REMOTE_H_
-#include "blastem.h"
+#include "genesis.h"
 
 void gdb_remote_init(void);
 m68k_context * gdb_debug_enter(m68k_context * context, uint32_t pc);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/genesis.c	Fri Dec 09 09:48:48 2016 -0800
@@ -0,0 +1,936 @@
+/*
+ Copyright 2013-2016 Michael Pavone
+ This file is part of BlastEm.
+ BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
+*/
+#include "genesis.h"
+#include "blastem.h"
+#include <stdlib.h>
+#include "render.h"
+#include "gst.h"
+#include "util.h"
+#define MCLKS_NTSC 53693175
+#define MCLKS_PAL  53203395
+
+uint32_t MCLKS_PER_68K;
+#define MCLKS_PER_YM  7
+#define MCLKS_PER_Z80 15
+#define MCLKS_PER_PSG (MCLKS_PER_Z80*16)
+#define DEFAULT_SYNC_INTERVAL MCLKS_LINE
+#define DEFAULT_LOWPASS_CUTOFF 3390
+
+//TODO: Figure out the exact value for this
+#define LINES_NTSC 262
+#define LINES_PAL 312
+
+#define MAX_SOUND_CYCLES 100000
+
+uint16_t *cart;
+uint16_t *ram;
+uint8_t z80_ram[Z80_RAM_BYTES];
+
+uint16_t read_dma_value(uint32_t address)
+{
+	//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) {
+		return *ptr;
+	}
+	//TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area
+	return 0;
+}
+
+uint16_t get_open_bus_value()
+{
+	return read_dma_value(genesis->m68k->last_prefetch_address/2);
+}
+
+void adjust_int_cycle(m68k_context * context, vdp_context * v_context)
+{
+	//static int old_int_cycle = CYCLE_NEVER;
+	genesis_context *gen = context->system;
+	if (context->sync_cycle - context->current_cycle > gen->max_cycles) {
+		context->sync_cycle = context->current_cycle + gen->max_cycles;
+	}
+	context->int_cycle = CYCLE_NEVER;
+	if ((context->status & 0x7) < 6) {
+		uint32_t next_vint = vdp_next_vint(v_context);
+		if (next_vint != CYCLE_NEVER) {
+			context->int_cycle = next_vint;
+			context->int_num = 6;
+		}
+		if ((context->status & 0x7) < 4) {
+			uint32_t next_hint = vdp_next_hint(v_context);
+			if (next_hint != CYCLE_NEVER) {
+				next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint;
+				if (next_hint < context->int_cycle) {
+					context->int_cycle = next_hint;
+					context->int_num = 4;
+
+				}
+			}
+		}
+	}
+	if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) {
+		context->int_pending = INT_PENDING_NONE;
+	}
+	/*if (context->int_cycle != old_int_cycle) {
+		printf("int cycle changed to: %d, level: %d @ %d(%d), frame: %d, vcounter: %d, hslot: %d, mask: %d, hint_counter: %d\n", context->int_cycle, context->int_num, v_context->cycles, context->current_cycle, v_context->frame, v_context->vcounter, v_context->hslot, context->status & 0x7, v_context->hint_counter);
+		old_int_cycle = context->int_cycle;
+	}*/
+
+	context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle;
+	if (context->should_return) {
+		context->target_cycle = context->current_cycle;
+	} else if (context->target_cycle < context->current_cycle) {
+		//Changes to SR can result in an interrupt cycle that's in the past
+		//This can cause issues with the implementation of STOP though
+		context->target_cycle = context->current_cycle;
+	}
+	/*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n",
+		context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7),
+		v_context->regs[REG_MODE_2] & 0x20, v_context->regs[REG_MODE_1] & 0x10, v_context->hint_counter, v_context->regs[REG_HINT], v_context->cycles / MCLKS_LINE);*/
+}
+
+//#define DO_DEBUG_PRINT
+#ifdef DO_DEBUG_PRINT
+#define dprintf printf
+#define dputs puts
+#else
+#define dprintf
+#define dputs
+#endif
+
+#define Z80_VINT_DURATION 128
+
+void z80_next_int_pulse(z80_context * z_context)
+{
+		genesis_context * gen = z_context->system;
+	z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp);
+	z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION * MCLKS_PER_Z80;
+			}
+
+void sync_z80(z80_context * z_context, uint32_t mclks)
+{
+#ifndef NO_Z80
+	if (z80_enabled) {
+		z80_run(z_context, mclks);
+	} else
+#endif
+	{
+		z_context->current_cycle = mclks;
+	}
+}
+
+void sync_sound(genesis_context * gen, uint32_t target)
+{
+	//printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2);
+	while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) {
+		uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES;
+		//printf("Running PSG to cycle %d\n", cur_target);
+		psg_run(gen->psg, cur_target);
+		//printf("Running YM-2612 to cycle %d\n", cur_target);
+		ym_run(gen->ym, cur_target);
+	}
+	psg_run(gen->psg, target);
+	ym_run(gen->ym, target);
+
+	//printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2);
+}
+
+uint32_t last_frame_num;
+
+//My refresh emulation isn't currently good enough and causes more problems than it solves
+#ifdef REFRESH_EMULATION
+#define REFRESH_INTERVAL 128
+#define REFRESH_DELAY 2
+uint32_t last_sync_cycle;
+uint32_t refresh_counter;
+#endif
+
+m68k_context * sync_components(m68k_context * context, uint32_t address)
+{
+	genesis_context * gen = context->system;
+	vdp_context * v_context = gen->vdp;
+	z80_context * z_context = gen->z80;
+#ifdef REFRESH_EMULATION
+	//lame estimation of refresh cycle delay
+	if (!gen->bus_busy) {
+		refresh_counter += context->current_cycle - last_sync_cycle;
+		context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL));
+		refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL);
+	}
+#endif
+
+	uint32_t mclks = context->current_cycle;
+	sync_z80(z_context, mclks);
+	sync_sound(gen, mclks);
+	vdp_run_context(v_context, mclks);
+	if (v_context->frame != last_frame_num) {
+		//printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", last_frame_num, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot);
+		last_frame_num = v_context->frame;
+
+		if(exit_after){
+			--exit_after;
+			if (!exit_after) {
+				exit(0);
+			}
+		}
+
+		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);
+		context->current_cycle -= mclks;
+		z80_adjust_cycles(z_context, mclks);
+		gen->ym->current_cycle -= mclks;
+		gen->psg->cycles -= mclks;
+		if (gen->ym->write_cycle != CYCLE_NEVER) {
+			gen->ym->write_cycle = gen->ym->write_cycle >= mclks ? gen->ym->write_cycle - mclks : 0;
+		}
+	}
+	gen->frame_end = vdp_cycles_to_frame_end(v_context);
+	context->sync_cycle = gen->frame_end;
+	//printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot);
+	if (context->int_ack) {
+		//printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot);
+		vdp_int_ack(v_context);
+		context->int_ack = 0;
+	}
+	if (!address && (break_on_sync || gen->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;
+			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;
+			//advance Z80 core to the start of an instruction
+			while (!z_context->pc)
+			{
+				sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80);
+			}
+			char *save_path;
+			if (slot == QUICK_SAVE_SLOT) {
+				save_path = save_state_path;
+			} else {
+				char slotname[] = "slot_0.gst";
+				slotname[5] = '0' + slot;
+				char const *parts[] = {gen->save_dir, PATH_SEP, slotname};
+				save_path = alloc_concat_m(3, parts);
+			}
+			save_gst(gen, save_path, address);
+			printf("Saved state to %s\n", save_path);
+			if (slot != QUICK_SAVE_SLOT) {
+				free(save_path);
+			}
+		} else if(gen->save_state) {
+			context->sync_cycle = context->current_cycle + 1;
+		}
+	}
+#ifdef REFRESH_EMULATION
+	last_sync_cycle = context->current_cycle;
+#endif
+	return context;
+}
+
+m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value)
+{
+	if (vdp_port & 0x2700E0) {
+		fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port);
+	}
+	vdp_port &= 0x1F;
+	//printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle);
+	sync_components(context, 0);
+	genesis_context * gen = context->system;
+	vdp_context *v_context = gen->vdp;
+	if (vdp_port < 0x10) {
+		int blocked;
+		uint32_t before_cycle = v_context->cycles;
+		if (vdp_port < 4) {
+
+			while (vdp_data_port_write(v_context, value) < 0) {
+				while(v_context->flags & FLAG_DMA_RUN) {
+					vdp_run_dma_done(v_context, gen->frame_end);
+					if (v_context->cycles >= gen->frame_end) {
+						uint32_t cycle_diff = v_context->cycles - context->current_cycle;
+						uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K;
+						if (m68k_cycle_diff < cycle_diff) {
+							m68k_cycle_diff += MCLKS_PER_68K;
+						}
+						context->current_cycle += m68k_cycle_diff;
+						gen->bus_busy = 1;
+						sync_components(context, 0);
+						gen->bus_busy = 0;
+					}
+				}
+				//context->current_cycle = v_context->cycles;
+			}
+		} else if(vdp_port < 8) {
+			blocked = vdp_control_port_write(v_context, value);
+			if (blocked) {
+				while (blocked) {
+					while(v_context->flags & FLAG_DMA_RUN) {
+						vdp_run_dma_done(v_context, gen->frame_end);
+						if (v_context->cycles >= gen->frame_end) {
+							uint32_t cycle_diff = v_context->cycles - context->current_cycle;
+							uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K;
+							if (m68k_cycle_diff < cycle_diff) {
+								m68k_cycle_diff += MCLKS_PER_68K;
+							}
+							context->current_cycle += m68k_cycle_diff;
+							gen->bus_busy = 1;
+							sync_components(context, 0);
+							gen->bus_busy = 0;
+						}
+						if (!(v_context->flags & FLAG_DMA_RUN)) {
+							//two more slots of delay are needed to kill sufficient sprite capacity in Overdrive
+							//TODO: Measure exact value with logic analyzer
+							vdp_run_context(v_context, v_context->cycles + 1);
+							vdp_run_context(v_context, v_context->cycles + 1);
+						}
+					}
+					
+					if (blocked < 0) {
+						blocked = vdp_control_port_write(v_context, value);
+					} else {
+						blocked = 0;
+					}
+				}
+			} else {
+				context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context);
+				//printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot);
+				adjust_int_cycle(context, v_context);
+			}
+		} else {
+			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
+		}
+		if (v_context->cycles != before_cycle) {
+			//printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
+			uint32_t cycle_diff = v_context->cycles - context->current_cycle;
+			uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K;
+			if (m68k_cycle_diff < cycle_diff) {
+				m68k_cycle_diff += MCLKS_PER_68K;
+			}
+			context->current_cycle += m68k_cycle_diff;
+#ifdef REFRESH_EMULATION
+			last_sync_cycle = context->current_cycle;
+#endif
+			//Lock the Z80 out of the bus until the VDP access is complete
+			gen->bus_busy = 1;
+			sync_z80(gen->z80, v_context->cycles);
+			gen->bus_busy = 0;
+		}
+	} else if (vdp_port < 0x18) {
+		psg_write(gen->psg, value);
+	} else {
+		//TODO: Implement undocumented test register(s)
+	}
+	return context;
+}
+
+m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value)
+{
+	return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : ((vdp_port & 1) ? value : 0));
+}
+
+void * z80_vdp_port_write(uint32_t vdp_port, void * vcontext, uint8_t value)
+{
+	z80_context * context = vcontext;
+	genesis_context * gen = context->system;
+	vdp_port &= 0xFF;
+	if (vdp_port & 0xE0) {
+		fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port);
+	}
+	if (vdp_port < 0x10) {
+		//These probably won't currently interact well with the 68K accessing the VDP
+		vdp_run_context(gen->vdp, context->current_cycle);
+		if (vdp_port < 4) {
+			vdp_data_port_write(gen->vdp, value << 8 | value);
+		} else if (vdp_port < 8) {
+			vdp_control_port_write(gen->vdp, value << 8 | value);
+		} else {
+			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
+		}
+	} else if (vdp_port < 0x18) {
+		sync_sound(gen, context->current_cycle);
+		psg_write(gen->psg, value);
+	} else {
+		vdp_test_port_write(gen->vdp, value);
+	}
+	return context;
+}
+
+uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context)
+{
+	if (vdp_port & 0x2700E0) {
+		fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port);
+	}
+	vdp_port &= 0x1F;
+	uint16_t value;
+	sync_components(context, 0);
+	genesis_context *gen = context->system;
+	vdp_context * v_context = gen->vdp;
+	uint32_t before_cycle = v_context->cycles;
+	if (vdp_port < 0x10) {
+		if (vdp_port < 4) {
+			value = vdp_data_port_read(v_context);
+		} else if(vdp_port < 8) {
+			value = vdp_control_port_read(v_context);
+		} else {
+			value = vdp_hv_counter_read(v_context);
+			//printf("HV Counter: %X at cycle %d\n", value, v_context->cycles);
+		}
+	} else if (vdp_port < 0x18){
+		fatal_error("Illegal read from PSG  port %X\n", vdp_port);
+	} else {
+		value = vdp_test_port_read(v_context);
+	}
+	if (v_context->cycles != before_cycle) {
+		//printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
+		context->current_cycle = v_context->cycles;
+#ifdef REFRES_EMULATION
+		last_sync_cycle = context->current_cycle;
+#endif
+		//Lock the Z80 out of the bus until the VDP access is complete
+		genesis_context *gen = context->system;
+		gen->bus_busy = 1;
+		sync_z80(gen->z80, v_context->cycles);
+		gen->bus_busy = 0;
+	}
+	return value;
+}
+
+uint8_t vdp_port_read_b(uint32_t vdp_port, m68k_context * context)
+{
+	uint16_t value = vdp_port_read(vdp_port, context);
+	if (vdp_port & 1) {
+		return value;
+	} else {
+		return value >> 8;
+	}
+}
+
+uint8_t z80_vdp_port_read(uint32_t vdp_port, void * vcontext)
+{
+	z80_context * context = vcontext;
+	if (vdp_port & 0xE0) {
+		fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port);
+	}
+	genesis_context * gen = context->system;
+	//VDP access goes over the 68K bus like a bank area access
+	//typical delay from bus arbitration
+	context->current_cycle += 3 * MCLKS_PER_Z80;
+	//TODO: add cycle for an access right after a previous one
+	//TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high
+	//      Needs a new logic analyzer capture to get the actual delay on the 68K side
+	gen->m68k->current_cycle += 8 * MCLKS_PER_68K;
+
+
+	vdp_port &= 0x1F;
+	uint16_t ret;
+	if (vdp_port < 0x10) {
+		//These probably won't currently interact well with the 68K accessing the VDP
+		vdp_run_context(gen->vdp, context->current_cycle);
+		if (vdp_port < 4) {
+			ret = vdp_data_port_read(gen->vdp);
+		} else if (vdp_port < 8) {
+			ret = vdp_control_port_read(gen->vdp);
+		} else {
+			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
+		}
+	} else {
+		//TODO: Figure out the correct value today
+		ret = 0xFFFF;
+	}
+	return vdp_port & 1 ? ret : ret >> 8;
+}
+
+uint32_t zram_counter = 0;
+
+m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value)
+{
+	genesis_context * gen = context->system;
+	if (location < 0x10000) {
+		//Access to Z80 memory incurs a one 68K cycle wait state
+		context->current_cycle += MCLKS_PER_68K;
+		if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) {
+			location &= 0x7FFF;
+			if (location < 0x4000) {
+				z80_ram[location & 0x1FFF] = value;
+#ifndef NO_Z80
+				z80_handle_code_write(location & 0x1FFF, gen->z80);
+#endif
+			} else if (location < 0x6000) {
+				sync_sound(gen, context->current_cycle);
+				if (location & 1) {
+					ym_data_write(gen->ym, value);
+				} else if(location & 2) {
+					ym_address_write_part2(gen->ym, value);
+				} else {
+					ym_address_write_part1(gen->ym, value);
+				}
+			} else if (location == 0x6000) {
+				gen->z80->bank_reg = (gen->z80->bank_reg >> 1 | value << 8) & 0x1FF;
+				if (gen->z80->bank_reg < 0x80) {
+					gen->z80->mem_pointers[1] = (gen->z80->bank_reg << 15) + ((char *)gen->z80->mem_pointers[2]);
+				} else {
+					gen->z80->mem_pointers[1] = NULL;
+				}
+			} else {
+				fatal_error("68K write to unhandled Z80 address %X\n", location);
+			}
+		}
+	} else {
+		location &= 0x1FFF;
+		if (location < 0x100) {
+			switch(location/2)
+			{
+			case 0x1:
+				io_data_write(gen->ports, value, context->current_cycle);
+				break;
+			case 0x2:
+				io_data_write(gen->ports+1, value, context->current_cycle);
+				break;
+			case 0x3:
+				io_data_write(gen->ports+2, value, context->current_cycle);
+				break;
+			case 0x4:
+				gen->ports[0].control = value;
+				break;
+			case 0x5:
+				gen->ports[1].control = value;
+				break;
+			case 0x6:
+				gen->ports[2].control = value;
+				break;
+			}
+		} else {
+			if (location == 0x1100) {
+				if (value & 1) {
+					dputs("bus requesting Z80");
+					if (z80_enabled) {
+						z80_assert_busreq(gen->z80, context->current_cycle);
+					} else {
+						gen->z80->busack = 1;
+					}
+				} else {
+					if (gen->z80->busreq) {
+						dputs("releasing z80 bus");
+						#ifdef DO_DEBUG_PRINT
+						char fname[20];
+						sprintf(fname, "zram-%d", zram_counter++);
+						FILE * f = fopen(fname, "wb");
+						fwrite(z80_ram, 1, sizeof(z80_ram), f);
+						fclose(f);
+						#endif
+					}
+					if (z80_enabled) {
+						z80_clear_busreq(gen->z80, context->current_cycle);
+					} else {
+						gen->z80->busack = 0;
+					}
+				}
+			} else if (location == 0x1200) {
+				sync_z80(gen->z80, context->current_cycle);
+				if (value & 1) {
+					if (z80_enabled) {
+						z80_clear_reset(gen->z80, context->current_cycle);
+					} else {
+						gen->z80->reset = 0;
+					}
+				} else {
+					if (z80_enabled) {
+						z80_assert_reset(gen->z80, context->current_cycle);
+					} else {
+						gen->z80->reset = 1;
+					}
+				}
+			}
+		}
+	}
+	return context;
+}
+
+m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value)
+{
+	if (location < 0x10000 || (location & 0x1FFF) >= 0x100) {
+		return io_write(location, context, value >> 8);
+	} else {
+		return io_write(location, context, value);
+	}
+}
+
+#define FOREIGN 0x80
+#define HZ50 0x40
+#define USA FOREIGN
+#define JAP 0x00
+#define EUR (HZ50|FOREIGN)
+#define NO_DISK 0x20
+
+uint8_t io_read(uint32_t location, m68k_context * context)
+{
+	uint8_t value;
+	genesis_context *gen = context->system;
+	if (location < 0x10000) {
+		//Access to Z80 memory incurs a one 68K cycle wait state
+		context->current_cycle += MCLKS_PER_68K;
+		if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) {
+			location &= 0x7FFF;
+			if (location < 0x4000) {
+				value = z80_ram[location & 0x1FFF];
+			} else if (location < 0x6000) {
+				sync_sound(gen, context->current_cycle);
+				value = ym_read_status(gen->ym);
+			} else {
+				value = 0xFF;
+			}
+		} else {
+			value = 0xFF;
+		}
+	} else {
+		location &= 0x1FFF;
+		if (location < 0x100) {
+			switch(location/2)
+			{
+			case 0x0:
+				//version bits should be 0 for now since we're not emulating TMSS
+				value = gen->version_reg;
+				break;
+			case 0x1:
+				value = io_data_read(gen->ports, context->current_cycle);
+				break;
+			case 0x2:
+				value = io_data_read(gen->ports+1, context->current_cycle);
+				break;
+			case 0x3:
+				value = io_data_read(gen->ports+2, context->current_cycle);
+				break;
+			case 0x4:
+				value = gen->ports[0].control;
+				break;
+			case 0x5:
+				value = gen->ports[1].control;
+				break;
+			case 0x6:
+				value = gen->ports[2].control;
+				break;
+			default:
+				value = 0xFF;
+			}
+		} else {
+			if (location == 0x1100) {
+				value = z80_enabled ? !z80_get_busack(gen->z80, context->current_cycle) : !gen->z80->busack;
+				value |= (get_open_bus_value() >> 8) & 0xFE;
+				dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d)\n", value, context->current_cycle, gen->z80->reset);
+			} else if (location == 0x1200) {
+				value = !gen->z80->reset;
+			} else {
+				value = 0xFF;
+				printf("Byte read of unknown IO location: %X\n", location);
+			}
+		}
+	}
+	return value;
+}
+
+uint16_t io_read_w(uint32_t location, m68k_context * context)
+{
+	uint16_t value = io_read(location, context);
+	if (location < 0x10000 || (location & 0x1FFF) < 0x100) {
+		value = value | (value << 8);
+	} else {
+		value <<= 8;
+		value |= get_open_bus_value() & 0xFF;
+	}
+	return value;
+}
+
+void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value)
+{
+	z80_context * context = vcontext;
+	genesis_context * gen = context->system;
+	sync_sound(gen, context->current_cycle);
+	if (location & 1) {
+		ym_data_write(gen->ym, value);
+	} else if (location & 2) {
+		ym_address_write_part2(gen->ym, value);
+	} else {
+		ym_address_write_part1(gen->ym, value);
+	}
+	return context;
+}
+
+uint8_t z80_read_ym(uint32_t location, void * vcontext)
+{
+	z80_context * context = vcontext;
+	genesis_context * gen = context->system;
+	sync_sound(gen, context->current_cycle);
+	return ym_read_status(gen->ym);
+}
+
+uint8_t z80_read_bank(uint32_t location, void * vcontext)
+{
+	z80_context * context = vcontext;
+	genesis_context *gen = context->system;
+	if (gen->bus_busy) {
+		context->current_cycle = context->sync_cycle;
+	}
+	//typical delay from bus arbitration
+	context->current_cycle += 3 * MCLKS_PER_Z80;
+	//TODO: add cycle for an access right after a previous one
+	//TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high
+	//      Needs a new logic analyzer capture to get the actual delay on the 68K side
+	gen->m68k->current_cycle += 8 * MCLKS_PER_68K;
+
+	location &= 0x7FFF;
+	if (context->mem_pointers[1]) {
+		return context->mem_pointers[1][location ^ 1];
+	}
+	uint32_t address = context->bank_reg << 15 | location;
+	if (address >= 0xC00000 && address < 0xE00000) {
+		return z80_vdp_port_read(location & 0xFF, context);
+	} else {
+		fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, context->bank_reg << 15);
+	}
+	return 0;
+}
+
+void *z80_write_bank(uint32_t location, void * vcontext, uint8_t value)
+{
+	z80_context * context = vcontext;
+	genesis_context *gen = context->system;
+	if (gen->bus_busy) {
+		context->current_cycle = context->sync_cycle;
+	}
+	//typical delay from bus arbitration
+	context->current_cycle += 3 * MCLKS_PER_Z80;
+	//TODO: add cycle for an access right after a previous one
+	//TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high
+	//      Needs a new logic analyzer capture to get the actual delay on the 68K side
+	gen->m68k->current_cycle += 8 * MCLKS_PER_68K;
+
+	location &= 0x7FFF;
+	uint32_t address = context->bank_reg << 15 | location;
+	if (address >= 0xE00000) {
+		address &= 0xFFFF;
+		((uint8_t *)ram)[address ^ 1] = value;
+	} else if (address >= 0xC00000) {
+		z80_vdp_port_write(location & 0xFF, context, value);
+	} else {
+		fprintf(stderr, "Unhandled write by Z80 to address %X through banked memory area\n", address);
+	}
+	return context;
+}
+
+void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value)
+{
+	z80_context * context = vcontext;
+
+	context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF;
+	if (context->bank_reg < 0x100) {
+		genesis_context *gen = context->system;
+		context->mem_pointers[1] = get_native_pointer(context->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen);
+	} else {
+		context->mem_pointers[1] = NULL;
+	}
+
+	return context;
+}
+
+void set_speed_percent(genesis_context * context, uint32_t percent)
+{
+	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) {
+		sync_sound(context, context->psg->cycles + MCLKS_PER_PSG);
+}
+	ym_adjust_master_clock(context->ym, context->master_clock);
+	psg_adjust_master_clock(context->psg, context->master_clock);
+}
+
+void set_region(genesis_context *gen, rom_info *info, uint8_t region)
+{
+	if (!region) {
+		char * def_region = tern_find_ptr(config, "default_region");
+		if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) {
+			region = translate_region_char(toupper(*def_region));
+		} else {
+			region = info->regions;
+		}
+	}
+	if (region & REGION_E) {
+		gen->version_reg = NO_DISK | EUR;
+	} else if (region & REGION_J) {
+		gen->version_reg = NO_DISK | JAP;
+	} else {
+		gen->version_reg = NO_DISK | USA;
+	}
+	
+	if (region & HZ50) {
+		gen->normal_clock = MCLKS_PAL;
+	} else {
+		gen->normal_clock = MCLKS_NTSC;
+	}
+	gen->master_clock = gen->normal_clock;
+}
+
+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[] = {
+		{ 0x0000, 0x4000,  0x1FFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL, NULL, NULL, NULL,              NULL },
+		{ 0x8000, 0x10000, 0x7FFF, 0, 0, 0,                                  NULL, NULL, NULL, z80_read_bank,     z80_write_bank},
+		{ 0x4000, 0x6000,  0x0003, 0, 0, 0,                                  NULL, NULL, NULL, z80_read_ym,       z80_write_ym},
+		{ 0x6000, 0x6100,  0xFFFF, 0, 0, 0,                                  NULL, NULL, NULL, NULL,              z80_write_bank_reg},
+		{ 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));
+	set_region(gen, rom, force_region);
+
+	gen->vdp = malloc(sizeof(vdp_context));
+	init_vdp_context(gen->vdp, gen->version_reg & 0x40);
+	gen->frame_end = vdp_cycles_to_frame_end(gen->vdp);
+	char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval;
+	gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL;
+
+	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval;
+	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
+	
+	gen->ym = malloc(sizeof(ym2612_context));
+	ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_opts, lowpass_cutoff);
+
+	gen->psg = malloc(sizeof(psg_context));
+	psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff);
+
+	gen->z80 = calloc(1, sizeof(z80_context));
+	z80_map[0].buffer = gen->zram = calloc(1, Z80_RAM_BYTES);
+#ifndef NO_Z80
+	z80_options *z_opts = malloc(sizeof(z80_options));
+	init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80);
+	init_z80_context(gen->z80, z_opts);
+	z80_assert_reset(gen->z80, 0);
+#endif
+
+	gen->z80->system = gen;
+	gen->z80->mem_pointers[0] = gen->zram;
+	gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart;
+
+	gen->cart = main_rom;
+	gen->lock_on = lock_on;
+	gen->work_ram = calloc(2, RAM_WORDS);
+	gen->zram = z80_ram;
+	setup_io_devices(config, rom, gen);
+
+	gen->save_type = rom->save_type;
+	gen->save_type = rom->save_type;
+	if (gen->save_type != SAVE_NONE) {
+		gen->save_ram_mask = rom->save_mask;
+		gen->save_size = rom->save_size;
+		gen->save_storage = rom->save_buffer;
+		gen->eeprom_map = rom->eeprom_map;
+		gen->num_eeprom = rom->num_eeprom;
+		if (gen->save_type == SAVE_I2C) {
+			eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size);
+		}
+	} else {
+		gen->save_storage = NULL;
+	}
+	
+	for (int i = 0; i < rom->map_chunks; i++)
+	{
+		if (rom->map[i].flags & MMAP_PTR_IDX) {
+			gen->m68k->mem_pointers[rom->map[i].ptr_index] = rom->map[i].buffer;
+		}
+		if (rom->map[i].start == 0xE00000) {
+			rom->map[i].buffer = gen->work_ram;
+		}
+	}
+
+	m68k_options *opts = malloc(sizeof(m68k_options));
+	init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K);
+	//TODO: make this configurable
+	opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY;
+	gen->m68k = init_68k_context(opts, NULL);
+	gen->m68k->system = gen;
+
+	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 = cart[2] << 16 | 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[] = {
+		{0xE00000, 0x1000000, 0xFFFF,   0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL,
+		           NULL,          NULL,         NULL,            NULL},
+		{0xC00000, 0xE00000,  0x1FFFFF, 0, 0, 0,                                  NULL,
+		           (read_16_fun)vdp_port_read,  (write_16_fun)vdp_port_write,
+		           (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b},
+		{0xA00000, 0xA12000,  0x1FFFF,  0, 0, 0,                                  NULL,
+		           (read_16_fun)io_read_w,      (write_16_fun)io_write_w,
+		           (read_8_fun)io_read,         (write_8_fun)io_write}
+	};
+	static tern_node *rom_db;
+	if (!rom_db) {
+		rom_db = load_rom_db();
+	}
+	*info_out = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0]));
+#ifndef BIG_ENDIAN
+	byteswap_rom(rom, rom_size);
+	if (lock_on) {
+		byteswap_rom(lock_on, lock_on_size);
+	}
+#endif
+	char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval;
+	if (!m68k_divider) {
+		m68k_divider = "7";
+	}
+	MCLKS_PER_68K = atoi(m68k_divider);
+	if (!MCLKS_PER_68K) {
+		MCLKS_PER_68K = 7;
+	}
+	return alloc_init_genesis(info_out, rom, lock_on, ym_opts, force_region);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/genesis.h	Fri Dec 09 09:48:48 2016 -0800
@@ -0,0 +1,74 @@
+/*
+ Copyright 2013 Michael Pavone
+ This file is part of BlastEm.
+ BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
+*/
+#ifndef GENESIS_H_
+#define GENESIS_H_
+
+#include <stdint.h>
+#include "m68k_core.h"
+#include "z80_to_x86.h"
+#include "ym2612.h"
+#include "vdp.h"
+#include "psg.h"
+#include "io.h"
+#include "romdb.h"
+#include "arena.h"
+
+typedef struct genesis_context genesis_context;
+
+struct genesis_context {
+	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;
+	uint32_t        save_size;
+	uint32_t        save_ram_mask;
+	uint32_t        master_clock; //Current master clock value
+	uint32_t        normal_clock; //Normal master clock (used to restore master clock after turbo mode)
+	uint32_t        frame_end;
+	uint32_t        max_cycles;
+	uint8_t         bank_regs[8];
+	uint16_t        mapper_start_index;
+	uint8_t         save_type;
+	io_port         ports[3];
+	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	Mon Nov 28 22:45:46 2016 -0800
+++ b/gst.c	Fri Dec 09 09:48:48 2016 -0800
@@ -3,8 +3,10 @@
  This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
+#include "genesis.h"
 #include "gst.h"
 #include <string.h>
+#include <stdio.h>
 
 #define GST_68K_REGS 0x80
 #define GST_68K_REG_SIZE (0xDA-GST_68K_REGS)
--- a/io.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/io.c	Fri Dec 09 09:48:48 2016 -0800
@@ -17,6 +17,7 @@
 
 #include "io.h"
 #include "blastem.h"
+#include "genesis.h"
 #include "render.h"
 #include "util.h"
 
--- a/menu.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/menu.c	Fri Dec 09 09:48:48 2016 -0800
@@ -3,7 +3,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
-#include "blastem.h"
+#include "genesis.h"
 #include "menu.h"
 #include "backend.h"
 #include "util.h"
--- a/render.h	Mon Nov 28 22:45:46 2016 -0800
+++ b/render.h	Fri Dec 09 09:48:48 2016 -0800
@@ -60,7 +60,7 @@
 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch);
 void render_framebuffer_updated(uint8_t which, int width);
-void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen);
+void render_init(int width, int height, char * title, uint8_t fullscreen);
 void render_update_caption(char *title);
 void render_wait_quit(vdp_context * context);
 void render_wait_psg(psg_context * context);
--- a/render_sdl.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/render_sdl.c	Fri Dec 09 09:48:48 2016 -0800
@@ -9,6 +9,7 @@
 #include <math.h>
 #include "render.h"
 #include "blastem.h"
+#include "genesis.h"
 #include "io.h"
 #include "util.h"
 
@@ -32,9 +33,6 @@
 
 static uint32_t last_frame = 0;
 
-static uint32_t min_delay;
-static uint32_t frame_delay = 1000/60;
-
 static int16_t * current_psg = NULL;
 static int16_t * current_ym = NULL;
 
@@ -245,7 +243,7 @@
 	}
 }
 
-void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen)
+void render_init(int width, int height, char * title, uint8_t fullscreen)
 {
 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
 		fatal_error("Unable to init SDL: %s\n", SDL_GetError());
@@ -362,21 +360,6 @@
 	scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on");
 
 	caption = title;
-	min_delay = 0;
-	for (int i = 0; i < 100; i++) {
-		uint32_t start = SDL_GetTicks();
-		SDL_Delay(1);
-		uint32_t delay = SDL_GetTicks()-start;
-		if (delay > min_delay) {
-			min_delay = delay;
-		}
-	}
-	if (!min_delay) {
-		min_delay = 1;
-	}
-	printf("minimum delay: %d\n", min_delay);
-
-	frame_delay = 1000/fps;
 
 	audio_mutex = SDL_CreateMutex();
 	psg_cond = SDL_CreateCond();
--- a/romdb.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/romdb.c	Fri Dec 09 09:48:48 2016 -0800
@@ -3,7 +3,7 @@
 #include "config.h"
 #include "romdb.h"
 #include "util.h"
-#include "blastem.h"
+#include "genesis.h"
 #include "menu.h"
 
 #define DOM_TITLE_START 0x120
@@ -287,15 +287,15 @@
 			}
 		} else {
 			//Used for games that only use the mapper for SRAM
-			context->mem_pointers[gen->mapper_start_index] = cart + 0x200000/2;
+			context->mem_pointers[gen->mapper_start_index] = gen->cart + 0x200000/2;
 			//For games that need more than 4MB
 			for (int i = 1; i < 8; i++)
 			{
-				context->mem_pointers[gen->mapper_start_index + i] = cart + 0x40000*gen->bank_regs[i];
+				context->mem_pointers[gen->mapper_start_index + i] = gen->cart + 0x40000*gen->bank_regs[i];
 			}
 		}
 	} else {
-		context->mem_pointers[gen->mapper_start_index + address] = cart + 0x40000*value;
+		context->mem_pointers[gen->mapper_start_index + address] = gen->cart + 0x40000*value;
 	}
 	return context;
 }
@@ -543,7 +543,7 @@
 			info->map[1].read_8 = (read_8_fun)read_sram_b;
 			info->map[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area
 			info->map[1].write_8 = (write_8_fun)write_sram_area_b;
-			info->map[1].buffer = cart + 0x200000 / sizeof(uint16_t);
+			info->map[1].buffer = rom + 0x200000 / sizeof(uint16_t);
 
 			memmap_chunk *last = info->map + info->map_chunks - 1;
 			memset(last, 0, sizeof(memmap_chunk));
--- a/stateview.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/stateview.c	Fri Dec 09 09:48:48 2016 -0800
@@ -8,7 +8,8 @@
 #include "vdp.h"
 #include "render.h"
 #include "util.h"
-#include "blastem.h"
+#include "genesis.h"
+#include "config.h"
 
 
 uint16_t read_dma_value(uint32_t address)
@@ -109,7 +110,7 @@
 	height = height < 240 ? (width/320) * 240 : height;
 
 	vdp_context context;
-	render_init(width, height, "GST State Viewer", 60, 0);
+	render_init(width, height, "GST State Viewer", 0);
 	init_vdp_context(&context, 0);
 	vdp_load_gst(&context, state_file);
 	vdp_run_to_vblank(&context);
--- a/util.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/util.c	Fri Dec 09 09:48:48 2016 -0800
@@ -57,6 +57,15 @@
 	return ret;
 }
 
+void byteswap_rom(int filesize, uint16_t *cart)
+{
+	for(uint16_t *cur = cart; cur - cart < filesize/2; ++cur)
+	{
+		*cur = (*cur >> 8) | (*cur << 8);
+	}
+}
+
+
 long file_size(FILE * f)
 {
 	fseek(f, 0, SEEK_END);
--- a/util.h	Mon Nov 28 22:45:46 2016 -0800
+++ b/util.h	Fri Dec 09 09:48:48 2016 -0800
@@ -21,6 +21,8 @@
 char * alloc_concat(char const * first, char const * second);
 //Allocates a new string containing the concatenation of the strings pointed to by parts
 char * alloc_concat_m(int num_parts, char const ** parts);
+//Byteswaps a ROM image in memory
+void byteswap_rom(int filesize, uint16_t *cart);
 //Returns the size of a file using fseek and ftell
 long file_size(FILE * f);
 //Strips whitespace and non-printable characters from the beginning and end of a string
--- a/vdp.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/vdp.c	Fri Dec 09 09:48:48 2016 -0800
@@ -5,6 +5,7 @@
 */
 #include "vdp.h"
 #include "blastem.h"
+#include "genesis.h"
 #include <stdlib.h>
 #include <string.h>
 #include "render.h"
--- a/vgmplay.c	Mon Nov 28 22:45:46 2016 -0800
+++ b/vgmplay.c	Fri Dec 09 09:48:48 2016 -0800
@@ -89,7 +89,7 @@
 
 	uint32_t fps = 60;
 	config = load_config(argv[0]);
-	render_init(320, 240, "vgm play", 60, 0);
+	render_init(320, 240, "vgm play", 0);
 
 	uint32_t opts = 0;
 	if (argc >= 3 && !strcmp(argv[2], "-y")) {