changeset 451:b7c3b2d22858

Added support for saving savestates. Added gst savestate format test harness
author Mike Pavone <pavone@retrodev.com>
date Fri, 26 Jul 2013 19:55:04 -0700
parents e85a107e6ec0
children 608815ab4ff2
files Makefile blastem.c blastem.h default.cfg gst.c gst.h io.c testgst.c vdp.c vdp.h ym2612.c ym2612.h z80_to_x86.h zruntime.S
diffstat 14 files changed, 656 insertions(+), 303 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Jul 20 23:49:31 2013 -0700
+++ b/Makefile	Fri Jul 26 19:55:04 2013 -0700
@@ -18,18 +18,18 @@
 
 all : dis trans stateview blastem
 
-blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS)
-	$(CC) -ggdb -o blastem  blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS)
+blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS)
+	$(CC) -ggdb -o blastem  blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS)
 
 dis : dis.o 68kinst.o
 	$(CC) -o dis dis.o 68kinst.o
 
 zdis : zdis.o z80inst.o
 	$(CC) -o zdis zdis.o z80inst.o
-	
+
 libemu68k.a : $(M68KOBJS) $(TRANSOBJS)
 	ar rcs libemu68k.a $(M68KOBJS) $(TRANSOBJS)
-	
+
 trans : trans.o $(M68KOBJS) $(TRANSOBJS)
 	$(CC) -o trans trans.o $(M68KOBJS) $(TRANSOBJS)
 
@@ -48,6 +48,9 @@
 vgmplay : vgmplay.o render_sdl.o $(AUDIOOBJS)
 	$(CC) -o vgmplay vgmplay.o render_sdl.o $(AUDIOOBJS) `pkg-config --libs $(LIBS)`
 
+testgst : testgst.o gst.o
+	$(CC) -o testgst testgst.o gst.o
+
 test_x86 : test_x86.o gen_x86.o
 	$(CC) -o test_x86 test_x86.o gen_x86.o
 
@@ -56,7 +59,7 @@
 
 offsets : offsets.c z80_to_x86.h m68k_to_x86.h
 	$(CC) -o offsets offsets.c
-	
+
 %.o : %.S
 	$(CC) -c -o $@ $<
 
--- a/blastem.c	Sat Jul 20 23:49:31 2013 -0700
+++ b/blastem.c	Fri Jul 26 19:55:04 2013 -0700
@@ -5,6 +5,7 @@
 #include "vdp.h"
 #include "render.h"
 #include "blastem.h"
+#include "gst.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -152,6 +153,7 @@
 }
 
 int break_on_sync = 0;
+int save_state = 0;
 
 uint8_t reset = 1;
 uint8_t need_reset = 0;
@@ -258,9 +260,19 @@
 		context->int_ack = 0;
 	}
 	adjust_int_cycle(context, v_context);
-	if (break_on_sync && address) {
-		break_on_sync = 0;
-		debugger(context, address);
+	if (address) {
+		if (break_on_sync) {
+			break_on_sync = 0;
+			debugger(context, address);
+		}
+		if (save_state) {
+			save_state = 0;
+			while (!z_context->pc)
+			{
+				sync_z80(z_context, z_context->current_cycle * MCLKS_PER_Z80 + MCLKS_PER_Z80);
+			}
+			save_gst(gen, "savestate.gst", address);
+		}
 	}
 	return context;
 }
@@ -1465,190 +1477,6 @@
 	return context;
 }
 
-#define GST_68K_REGS 0x80
-#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS)
-#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS)
-#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS)
-#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS)
-#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS)
-#define GST_68K_RAM  0x2478
-#define GST_Z80_REGS 0x404
-#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS)
-#define GST_Z80_RAM 0x474
-
-uint32_t read_le_32(uint8_t * data)
-{
-	return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
-}
-
-uint16_t read_le_16(uint8_t * data)
-{
-	return data[1] << 8 | data[0];
-}
-
-uint16_t read_be_16(uint8_t * data)
-{
-	return data[0] << 8 | data[1];
-}
-
-uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile)
-{
-	uint8_t buffer[4096];
-	fseek(gstfile, GST_68K_REGS, SEEK_SET);
-	if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) {
-		fputs("Failed to read 68K registers from savestate\n", stderr);
-		return 0;
-	}
-	uint8_t * curpos = buffer;
-	for (int i = 0; i < 8; i++) {
-		context->dregs[i] = read_le_32(curpos);
-		curpos += sizeof(uint32_t);
-	}
-	for (int i = 0; i < 8; i++) {
-		context->aregs[i] = read_le_32(curpos);
-		curpos += sizeof(uint32_t);
-	}
-	uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET);
-	uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET);
-	context->status = sr >> 8;
-	for (int flag = 4; flag >= 0; flag--) {
-		context->flags[flag] = sr & 1;
-		sr >>= 1;
-	}
-	if (context->status & (1 << 5)) {
-		context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET);
-	} else {
-		context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET);
-	}
-	fseek(gstfile, GST_68K_RAM, SEEK_SET);
-	for (int i = 0; i < (32*1024);) {
-		if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) {
-			fputs("Failed to read 68K RAM from savestate\n", stderr);
-			return 0;
-		}
-		for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) {
-			context->mem_pointers[1][i++] = read_be_16(curpos);
-		}
-	}
-	return pc;
-}
-
-uint8_t z80_load_gst(z80_context * context, FILE * gstfile)
-{
-	uint8_t regdata[GST_Z80_REG_SIZE];
-	fseek(gstfile, GST_Z80_REGS, SEEK_SET);
-	if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
-		fputs("Failed to read Z80 registers from savestate\n", stderr);
-		return 0;
-	}
-	uint8_t * curpos = regdata;
-	uint8_t f = *(curpos++);
-	context->flags[ZF_C] = f & 1;
-	f >>= 1;
-	context->flags[ZF_N] = f & 1;
-	f >>= 1;
-	context->flags[ZF_PV] = f & 1;
-	f >>= 2;
-	context->flags[ZF_H] = f & 1;
-	f >>= 2;
-	context->flags[ZF_Z] = f & 1;
-	f >>= 1;
-	context->flags[ZF_S] = f;
-
-	context->regs[Z80_A] = *curpos;
-	curpos += 3;
-	for (int reg = Z80_C; reg <= Z80_IYH; reg++) {
-		context->regs[reg++] = *(curpos++);
-		context->regs[reg] = *curpos;
-		curpos += 3;
-	}
-	uint16_t pc = read_le_16(curpos);
-	curpos += 4;
-	context->sp = read_le_16(curpos);
-	curpos += 4;
-	f = *(curpos++);
-	context->alt_flags[ZF_C] = f & 1;
-	f >>= 1;
-	context->alt_flags[ZF_N] = f & 1;
-	f >>= 1;
-	context->alt_flags[ZF_PV] = f & 1;
-	f >>= 2;
-	context->alt_flags[ZF_H] = f & 1;
-	f >>= 2;
-	context->alt_flags[ZF_Z] = f & 1;
-	f >>= 1;
-	context->alt_flags[ZF_S] = f;
-	context->alt_regs[Z80_A] = *curpos;
-	curpos += 3;
-	for (int reg = Z80_C; reg <= Z80_H; reg++) {
-		context->alt_regs[reg++] = *(curpos++);
-		context->alt_regs[reg] = *curpos;
-		curpos += 3;
-	}
-	context->regs[Z80_I] = *curpos;
-	curpos += 2;
-	context->iff1 = context->iff2 = *curpos;
-	curpos += 2;
-	reset = !*(curpos++);
-	busreq = *curpos;
-	curpos += 3;
-	uint32_t bank = read_le_32(curpos);
-	if (bank < 0x400000) {
-		context->mem_pointers[1] = context->mem_pointers[2] + bank;
-	} else {
-		context->mem_pointers[1] = NULL;
-	}
-	context->bank_reg = bank >> 15;
-	fseek(gstfile, GST_Z80_RAM, SEEK_SET);
-	if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) {
-		fputs("Failed to read Z80 RAM from savestate\n", stderr);
-		return 0;
-	}
-	context->native_pc =  z80_get_native_address_trans(context, pc);
-	return 1;
-}
-
-uint32_t load_gst(genesis_context * gen, char * fname)
-{
-	FILE * gstfile = fopen(fname, "rb");
-	if (!gstfile) {
-		fprintf(stderr, "Could not open file %s for reading\n", fname);
-		goto error;
-	}
-	char ident[5];
-	if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) {
-		fprintf(stderr, "Could not read ident code from %s\n", fname);
-		goto error_close;
-	}
-	if (memcmp(ident, "GST\xE0\x40", 3) != 0) {
-		fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\xE0\\x40.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]);
-		goto error_close;
-	}
-	uint32_t pc = m68k_load_gst(gen->m68k, gstfile);
-	if (!pc) {
-		goto error_close;
-	}
-	if (!vdp_load_gst(gen->vdp, gstfile)) {
-		goto error_close;
-	}
-	if (!ym_load_gst(gen->ym, gstfile)) {
-		goto error_close;
-	}
-	if (!z80_load_gst(gen->z80, gstfile)) {
-		goto error_close;
-	}
-	gen->ports[0].control = 0x40;
-	gen->ports[1].control = 0x40;
-	adjust_int_cycle(gen->m68k, gen->vdp);
-	fclose(gstfile);
-	return pc;
-
-error_close:
-	fclose(gstfile);
-error:
-	return 0;
-}
-
 #define ROM_END   0x1A4
 #define RAM_ID    0x1B0
 #define RAM_FLAGS 0x1B2
@@ -1805,11 +1633,15 @@
 	if (statefile) {
 		uint32_t pc = load_gst(gen, statefile);
 		if (!pc) {
+			fprintf(stderr, "Failed to load save state %s\n", statefile);
 			exit(1);
 		}
+		printf("Loaded %s\n", statefile);
 		if (debug) {
 			insert_breakpoint(&context, pc, (uint8_t *)debugger);
 		}
+		adjust_int_cycle(gen->m68k, gen->vdp);
+		gen->z80->native_pc =  z80_get_native_address_trans(gen->z80, gen->z80->pc);
 		start_68k_context(&context, pc);
 	} else {
 		if (debug) {
--- a/blastem.h	Sat Jul 20 23:49:31 2013 -0700
+++ b/blastem.h	Fri Jul 26 19:55:04 2013 -0700
@@ -31,7 +31,10 @@
 
 extern genesis_context * genesis;
 extern int break_on_sync;
+extern int save_state;
 extern tern_node * config;
+extern uint8_t busreq;
+extern uint8_t reset;
 
 uint16_t read_dma_value(uint32_t address);
 m68k_context * debugger(m68k_context * context, uint32_t address);
--- a/default.cfg	Sat Jul 20 23:49:31 2013 -0700
+++ b/default.cfg	Fri Jul 26 19:55:04 2013 -0700
@@ -13,11 +13,12 @@
 		e gamepads.1.z
 		f gamepads.1.mode
 		enter gamepads.1.start
-		
+
 		[ ui.vdp_debug_mode
 		] ui.vdp_debug_pal
 		u ui.enter_debugger
 		esc ui.exit
+    ` ui.save_state
 	}
 	pads {
 		0 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gst.c	Fri Jul 26 19:55:04 2013 -0700
@@ -0,0 +1,478 @@
+#include "gst.h"
+#include <string.h>
+
+#define GST_68K_REGS 0x80
+#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS)
+#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS)
+#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS)
+#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS)
+#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS)
+#define GST_68K_RAM  0x2478
+#define GST_Z80_REGS 0x404
+#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS)
+#define GST_Z80_RAM 0x474
+#define GST_VDP_REGS 0xFA
+#define GST_VDP_MEM 0x12478
+#define GST_YM_OFFSET 0x1E4
+#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET)
+
+uint32_t read_le_32(uint8_t * data)
+{
+	return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
+}
+
+uint16_t read_le_16(uint8_t * data)
+{
+	return data[1] << 8 | data[0];
+}
+
+uint16_t read_be_16(uint8_t * data)
+{
+	return data[0] << 8 | data[1];
+}
+
+void write_le_32(uint8_t * dst, uint32_t val)
+{
+	dst[0] = val;
+	dst[1] = val >> 8;
+	dst[2] = val >> 16;
+	dst[3] = val >> 24;
+}
+
+void write_le_16(uint8_t * dst, uint16_t val)
+{
+	dst[0] = val;
+	dst[1] = val >> 8;
+}
+
+void write_be_32(uint8_t * dst, uint32_t val)
+{
+	dst[0] = val >> 24;
+	dst[1] = val >> 16;
+	dst[2] = val >> 8;
+	dst[3] = val;
+}
+
+void write_be_16(uint8_t * dst, uint16_t val)
+{
+	dst[0] = val >> 8;
+	dst[1] = val;
+}
+
+uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile)
+{
+	uint8_t buffer[4096];
+	fseek(gstfile, GST_68K_REGS, SEEK_SET);
+	if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) {
+		fputs("Failed to read 68K registers from savestate\n", stderr);
+		return 0;
+	}
+	uint8_t * curpos = buffer;
+	for (int i = 0; i < 8; i++) {
+		context->dregs[i] = read_le_32(curpos);
+		curpos += sizeof(uint32_t);
+	}
+	for (int i = 0; i < 8; i++) {
+		context->aregs[i] = read_le_32(curpos);
+		curpos += sizeof(uint32_t);
+	}
+	uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET);
+	uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET);
+	context->status = sr >> 8;
+	for (int flag = 4; flag >= 0; flag--) {
+		context->flags[flag] = sr & 1;
+		sr >>= 1;
+	}
+	if (context->status & (1 << 5)) {
+		context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET);
+	} else {
+		context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET);
+	}
+	fseek(gstfile, GST_68K_RAM, SEEK_SET);
+	for (int i = 0; i < (32*1024);) {
+		if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) {
+			fputs("Failed to read 68K RAM from savestate\n", stderr);
+			return 0;
+		}
+		for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) {
+			context->mem_pointers[1][i++] = read_be_16(curpos);
+		}
+	}
+	return pc;
+}
+
+uint8_t m68k_save_gst(m68k_context * context, uint32_t pc, FILE * gstfile)
+{
+	uint8_t buffer[4096];
+	uint8_t * curpos = buffer;
+	for (int i = 0; i < 8; i++) {
+		write_le_32(curpos, context->dregs[i]);
+		curpos += sizeof(uint32_t);
+	}
+	for (int i = 0; i < 8; i++) {
+		write_le_32(curpos, context->aregs[i]);
+		curpos += sizeof(uint32_t);
+	}
+	write_le_32(buffer + GST_68K_PC_OFFSET, pc);
+	uint16_t sr = context->status << 3;
+	for (int flag = 4; flag >= 0; flag--) {
+		sr <<= 1;
+		sr |= context->flags[flag];
+	}
+	write_le_16(buffer + GST_68K_SR_OFFSET, sr);
+	if (context->status & (1 << 5)) {
+		write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[8]);
+		write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[7]);
+	} else {
+		write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[7]);
+		write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[8]);
+	}
+	fseek(gstfile, GST_68K_REGS, SEEK_SET);
+	if (fwrite(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) {
+		fputs("Failed to write 68K registers to savestate\n", stderr);
+		return 0;
+	}
+
+	fseek(gstfile, GST_68K_RAM, SEEK_SET);
+	for (int i = 0; i < (32*1024);) {
+		for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) {
+			write_be_16(curpos, context->mem_pointers[1][i++]);
+		}
+		if (fwrite(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) {
+			fputs("Failed to write 68K RAM to savestate\n", stderr);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+uint8_t z80_load_gst(z80_context * context, FILE * gstfile)
+{
+	uint8_t regdata[GST_Z80_REG_SIZE];
+	fseek(gstfile, GST_Z80_REGS, SEEK_SET);
+	if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
+		fputs("Failed to read Z80 registers from savestate\n", stderr);
+		return 0;
+	}
+	uint8_t * curpos = regdata;
+	uint8_t f = *(curpos++);
+	context->flags[ZF_C] = f & 1;
+	f >>= 1;
+	context->flags[ZF_N] = f & 1;
+	f >>= 1;
+	context->flags[ZF_PV] = f & 1;
+	f >>= 2;
+	context->flags[ZF_H] = f & 1;
+	f >>= 2;
+	context->flags[ZF_Z] = f & 1;
+	f >>= 1;
+	context->flags[ZF_S] = f;
+
+	context->regs[Z80_A] = *curpos;
+	curpos += 3;
+	for (int reg = Z80_C; reg <= Z80_IYH; reg++) {
+		context->regs[reg++] = *(curpos++);
+		context->regs[reg] = *curpos;
+		curpos += 3;
+	}
+	context->pc = read_le_16(curpos);
+	curpos += 4;
+	context->sp = read_le_16(curpos);
+	curpos += 4;
+	f = *(curpos++);
+	context->alt_flags[ZF_C] = f & 1;
+	f >>= 1;
+	context->alt_flags[ZF_N] = f & 1;
+	f >>= 1;
+	context->alt_flags[ZF_PV] = f & 1;
+	f >>= 2;
+	context->alt_flags[ZF_H] = f & 1;
+	f >>= 2;
+	context->alt_flags[ZF_Z] = f & 1;
+	f >>= 1;
+	context->alt_flags[ZF_S] = f;
+	context->alt_regs[Z80_A] = *curpos;
+	curpos += 3;
+	for (int reg = Z80_C; reg <= Z80_H; reg++) {
+		context->alt_regs[reg++] = *(curpos++);
+		context->alt_regs[reg] = *curpos;
+		curpos += 3;
+	}
+	context->regs[Z80_I] = *curpos;
+	curpos += 2;
+	context->iff1 = context->iff2 = *curpos;
+	curpos += 2;
+	reset = !*(curpos++);
+	busreq = *curpos;
+	curpos += 3;
+	uint32_t bank = read_le_32(curpos);
+	if (bank < 0x400000) {
+		context->mem_pointers[1] = context->mem_pointers[2] + bank;
+	} else {
+		context->mem_pointers[1] = NULL;
+	}
+	context->bank_reg = bank >> 15;
+	fseek(gstfile, GST_Z80_RAM, SEEK_SET);
+	if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) {
+		fputs("Failed to read Z80 RAM from savestate\n", stderr);
+		return 0;
+	}
+	return 1;
+}
+
+uint8_t vdp_load_gst(vdp_context * context, FILE * state_file)
+{
+	uint8_t tmp_buf[CRAM_SIZE*2];
+	fseek(state_file, GST_VDP_REGS, SEEK_SET);
+	if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) {
+		fputs("Failed to read VDP registers from savestate\n", stderr);
+		return 0;
+	}
+	context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
+	if (!context->double_res) {
+		context->framebuf = context->oddbuf;
+	}
+	latch_mode(context);
+	if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) {
+		fputs("Failed to read CRAM from savestate\n", stderr);
+		return 0;
+	}
+	for (int i = 0; i < CRAM_SIZE; i++) {
+		uint16_t value;
+		context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
+		context->colors[i] = color_map[value & 0xEEE];
+		context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW];
+		context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT];
+	}
+	if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) {
+		fputs("Failed to read VSRAM from savestate\n", stderr);
+		return 0;
+	}
+	for (int i = 0; i < VSRAM_SIZE; i++) {
+		context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
+	}
+	fseek(state_file, GST_VDP_MEM, SEEK_SET);
+	if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) {
+		fputs("Failed to read VRAM from savestate\n", stderr);
+		return 0;
+	}
+	return 1;
+}
+
+uint8_t vdp_save_gst(vdp_context * context, FILE * outfile)
+{
+	uint8_t tmp_buf[CRAM_SIZE*2];
+	fseek(outfile, GST_VDP_REGS, SEEK_SET);
+	if(fwrite(context->regs, 1, VDP_REGS, outfile) != VDP_REGS) {
+		fputs("Error writing VDP regs to savestate\n", stderr);
+		return 0;
+	}
+	for (int i = 0; i < CRAM_SIZE; i++)
+	{
+		tmp_buf[i*2] = context->cram[i];
+		tmp_buf[i*2+1] = context->cram[i] >> 8;
+	}
+	if (fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile) != sizeof(tmp_buf)) {
+		fputs("Error writing CRAM to savestate\n", stderr);
+		return 0;
+	}
+	for (int i = 0; i < VSRAM_SIZE; i++)
+	{
+		tmp_buf[i*2] = context->vsram[i];
+		tmp_buf[i*2+1] = context->vsram[i] >> 8;
+	}
+	if (fwrite(tmp_buf, 2, VSRAM_SIZE, outfile) != VSRAM_SIZE) {
+		fputs("Error writing VSRAM to savestate\n", stderr);
+		return 0;
+	}
+	fseek(outfile, GST_VDP_MEM, SEEK_SET);
+	if (fwrite(context->vdpmem, 1, VRAM_SIZE, outfile) != VRAM_SIZE) {
+		fputs("Error writing VRAM to savestate\n", stderr);
+		return 0;
+	}
+	return 1;
+}
+
+uint8_t z80_save_gst(z80_context * context, FILE * gstfile)
+{
+	uint8_t regdata[GST_Z80_REG_SIZE];
+	uint8_t * curpos = regdata;
+	memset(regdata, 0, sizeof(regdata));
+	uint8_t f = context->flags[ZF_S];
+	f <<= 1;
+	f |= context->flags[ZF_Z] ;
+	f <<= 2;
+	f |= context->flags[ZF_H];
+	f <<= 2;
+	f |= context->flags[ZF_PV];
+	f <<= 1;
+	f |= context->flags[ZF_N];
+	f <<= 1;
+	f |= context->flags[ZF_C];
+	*(curpos++) = f;
+	*curpos = context->regs[Z80_A];
+
+	curpos += 3;
+	for (int reg = Z80_C; reg <= Z80_IYH; reg++) {
+		*(curpos++) = context->regs[reg++];
+		*curpos = context->regs[reg];
+		curpos += 3;
+	}
+	write_le_16(curpos, context->pc);
+	curpos += 4;
+	write_le_16(curpos, context->sp);
+	curpos += 4;
+	f = context->alt_flags[ZF_S];
+	f <<= 1;
+	f |= context->alt_flags[ZF_Z] ;
+	f <<= 2;
+	f |= context->alt_flags[ZF_H];
+	f <<= 2;
+	f |= context->alt_flags[ZF_PV];
+	f <<= 1;
+	f |= context->alt_flags[ZF_N];
+	f <<= 1;
+	f |= context->alt_flags[ZF_C];
+	*(curpos++) = f;
+	*curpos = context->alt_regs[Z80_A];
+	curpos += 3;
+	for (int reg = Z80_C; reg <= Z80_H; reg++) {
+		*(curpos++) = context->alt_regs[reg++];
+		*curpos = context->alt_regs[reg];
+		curpos += 3;
+	}
+	*curpos = context->regs[Z80_I];
+	curpos += 2;
+	*curpos = context->iff1;
+	curpos += 2;
+	*(curpos++) = !reset;
+	*curpos = busreq;
+	curpos += 3;
+	uint32_t bank = context->bank_reg << 15;
+	write_le_32(curpos, bank);
+	fseek(gstfile, GST_Z80_REGS, SEEK_SET);
+	if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
+		return 0;
+	}
+	fseek(gstfile, GST_Z80_RAM, SEEK_SET);
+	if(fwrite(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) {
+		fputs("Failed to write Z80 RAM to savestate\n", stderr);
+		return 0;
+	}
+	return 1;
+}
+
+uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile)
+{
+	uint8_t regdata[GST_YM_SIZE];
+	fseek(gstfile, GST_YM_OFFSET, SEEK_SET);
+	if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
+		return 0;
+	}
+	for (int i = 0; i < sizeof(regdata); i++) {
+		if (i & 0x100) {
+			ym_address_write_part2(context, i & 0xFF);
+		} else {
+			ym_address_write_part1(context, i);
+		}
+		ym_data_write(context, regdata[i]);
+	}
+	return 1;
+}
+
+uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile)
+{
+	uint8_t regdata[GST_YM_SIZE];
+	for (int i = 0; i < sizeof(regdata); i++) {
+		if (i & 0x100) {
+			int reg = (i & 0xFF);
+			if (reg >= YM_PART2_START && reg < YM_REG_END) {
+				regdata[i] = context->part2_regs[reg-YM_PART2_START];
+			} else {
+				regdata[i] = 0xFF;
+			}
+		} else {
+			if (i >= YM_PART1_START && i < YM_REG_END) {
+				regdata[i] = context->part1_regs[i-YM_PART1_START];
+			} else {
+				regdata[i] = 0xFF;
+			}
+		}
+	}
+	fseek(gstfile, GST_YM_OFFSET, SEEK_SET);
+	if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
+		return 0;
+	}
+	return 1;
+}
+
+uint32_t load_gst(genesis_context * gen, char * fname)
+{
+	FILE * gstfile = fopen(fname, "rb");
+	if (!gstfile) {
+		fprintf(stderr, "Could not open file %s for reading\n", fname);
+		goto error;
+	}
+	char ident[5];
+	if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) {
+		fprintf(stderr, "Could not read ident code from %s\n", fname);
+		goto error_close;
+	}
+	if (memcmp(ident, "GST\x40\xE0", 5) != 0) {
+		fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\x40\\xE0.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]);
+		goto error_close;
+	}
+	uint32_t pc = m68k_load_gst(gen->m68k, gstfile);
+	if (!pc) {
+		goto error_close;
+	}
+	if (!vdp_load_gst(gen->vdp, gstfile)) {
+		goto error_close;
+	}
+	if (!ym_load_gst(gen->ym, gstfile)) {
+		goto error_close;
+	}
+	if (!z80_load_gst(gen->z80, gstfile)) {
+		goto error_close;
+	}
+	gen->ports[0].control = 0x40;
+	gen->ports[1].control = 0x40;
+	fclose(gstfile);
+	return pc;
+
+error_close:
+	fclose(gstfile);
+error:
+	return 0;
+}
+
+uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc)
+{
+	FILE * gstfile = fopen(fname, "wb");
+	if (!gstfile) {
+		fprintf(stderr, "Could not open %s for writing\n", fname);
+		goto error;
+	}
+	if (fwrite("GST\x40\xE0", 1, 5, gstfile) != 5) {
+		fputs("Error writing signature to savestate\n", stderr);
+		goto error_close;
+	}
+	if (!m68k_save_gst(gen->m68k, m68k_pc, gstfile)) {
+		goto error_close;
+	}
+	if (!z80_save_gst(gen->z80, gstfile)) {
+		goto error_close;
+	}
+	if (!vdp_save_gst(gen->vdp, gstfile)) {
+		goto error_close;
+	}
+	if (!ym_save_gst(gen->ym, gstfile)) {
+		goto error_close;
+	}
+	return 1;
+
+error_close:
+	fclose(gstfile);
+error:
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gst.h	Fri Jul 26 19:55:04 2013 -0700
@@ -0,0 +1,8 @@
+#ifndef GST_H_
+#define GST_H_
+#include "blastem.h"
+
+uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc);
+uint32_t load_gst(genesis_context * gen, char * fname);
+
+#endif //GST_H_
--- a/io.c	Sat Jul 20 23:49:31 2013 -0700
+++ b/io.c	Fri Jul 26 19:55:04 2013 -0700
@@ -13,6 +13,7 @@
 	UI_DEBUG_MODE_INC,
 	UI_DEBUG_PAL_INC,
 	UI_ENTER_DEBUGGER,
+  UI_SAVE_STATE,
 	UI_EXIT
 } ui_action;
 
@@ -107,7 +108,7 @@
 
 void bind_gamepad(int keycode, int gamepadnum, int button)
 {
-	
+
 	if (gamepadnum < 1 || gamepadnum > 2) {
 		return;
 	}
@@ -210,6 +211,9 @@
 		case UI_ENTER_DEBUGGER:
 			break_on_sync = 1;
 			break;
+		case UI_SAVE_STATE:
+			save_state = 1;
+			break;
 		case UI_EXIT:
 			exit(0);
 		}
@@ -283,6 +287,8 @@
 			*ui_out = UI_DEBUG_PAL_INC;
 		} else if(!strcmp(target + 3, "enter_debugger")) {
 			*ui_out = UI_ENTER_DEBUGGER;
+		} else if(!strcmp(target + 3, "save_state")) {
+			*ui_out = UI_SAVE_STATE;
 		} else if(!strcmp(target + 3, "exit")) {
 			*ui_out = UI_EXIT;
 		} else {
@@ -345,7 +351,7 @@
 	special = tern_insert_int(special, "right", RENDERKEY_RIGHT);
 	special = tern_insert_int(special, "enter", '\r');
 		special = tern_insert_int(special, "esc", RENDERKEY_ESC);
-	
+
 	tern_node * padbuttons = tern_insert_int(NULL, ".up", DPAD_UP);
 	padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN);
 	padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT);
@@ -358,7 +364,7 @@
 	padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z);
 	padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START);
 	padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE);
-	
+
 	tern_node * keys = tern_find_prefix(config, "bindingskeys");
 	process_keys(keys, special, padbuttons, NULL);
 	char prefix[] = "bindingspads00";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testgst.c	Fri Jul 26 19:55:04 2013 -0700
@@ -0,0 +1,78 @@
+#include "gst.h"
+#include <string.h>
+#include <stdlib.h>
+
+uint8_t busreq;
+uint8_t reset;
+
+int32_t color_map[1 << 12];
+
+void latch_mode(vdp_context * context)
+{
+}
+void ym_data_write(ym2612_context * context, uint8_t value)
+{
+	if (context->selected_reg >= YM_REG_END) {
+		return;
+	}
+	if (context->selected_part) {
+		if (context->selected_reg < YM_PART2_START) {
+			return;
+		}
+		context->part2_regs[context->selected_reg - YM_PART2_START] = value;
+	} else {
+		if (context->selected_reg < YM_PART1_START) {
+			return;
+		}
+		context->part1_regs[context->selected_reg - YM_PART1_START] = value;
+	}
+}
+
+void ym_address_write_part1(ym2612_context * context, uint8_t address)
+{
+	//printf("address_write_part1: %X\n", address);
+	context->selected_reg = address;
+	context->selected_part = 0;
+}
+
+void ym_address_write_part2(ym2612_context * context, uint8_t address)
+{
+	//printf("address_write_part2: %X\n", address);
+	context->selected_reg = address;
+	context->selected_part = 1;
+}
+
+uint16_t ram[64*1024];
+uint8_t zram[8*1024];
+
+
+int main(int argc, char ** argv)
+{
+	vdp_context vdp;
+	ym2612_context ym;
+	psg_context psg;
+	m68k_context m68k;
+	z80_context z80;
+	genesis_context gen;
+	if (argc < 3) {
+		fputs("Usage: testgst infile outfile\n", stderr);
+		return 1;
+	}
+	memset(&gen, 0, sizeof(gen));
+	memset(&m68k, 0, sizeof(m68k));
+	memset(&z80, 0, sizeof(z80));
+	memset(&ym, 0, sizeof(ym));
+	memset(&vdp, 0, sizeof(vdp));
+	memset(&psg, 0, sizeof(psg));
+	m68k.mem_pointers[1] = ram;
+	z80.mem_pointers[0] = zram;
+	vdp.vdpmem = malloc(VRAM_SIZE);
+	gen.vdp = &vdp;
+	gen.ym = &ym;
+	gen.psg = &psg;
+	gen.m68k = &m68k;
+	gen.z80 = &z80;
+	uint32_t pc = load_gst(&gen, argv[1]);
+	save_gst(&gen, argv[2], pc);
+	return 0;
+}
--- a/vdp.c	Sat Jul 20 23:49:31 2013 -0700
+++ b/vdp.c	Fri Jul 26 19:55:04 2013 -0700
@@ -125,7 +125,7 @@
 {
 	if (context->cur_slot >= context->sprite_draws) {
 		sprite_draw * d = context->sprite_draw_list + context->cur_slot;
-		
+
 		uint16_t dir;
 		int16_t x;
 		if (d->h_flip) {
@@ -164,7 +164,7 @@
 		uint16_t link = context->vdpmem[address+3] & 0x7F;
 		uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3;
 		uint8_t pri = context->vdpmem[address + 4] >> 7;
-		uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;	
+		uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;
 		//printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
 		current_index = link;
 		count++;
@@ -179,9 +179,9 @@
 	       "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n"
 	       "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n"
 	       "0C: %.2X | Width: %d, Shadow/Highlight: %s\n",
-	       context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, 
+	       context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0,
 	           context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled",
-	       context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", 
+	       context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled",
 	           context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4,
 	       context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full",
 	           hscroll[context->regs[REG_MODE_3] & 0x3],
@@ -203,8 +203,8 @@
 	       "0A: %.2X | H-Int Counter: %u\n"
 	       "0F: %.2X | Auto-increment: $%X\n"
 	       "10: %.2X | Scroll A/B Size: %sx%s\n",
-	       context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, 
-	       context->regs[REG_HINT], context->regs[REG_HINT], 
+	       context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F,
+	       context->regs[REG_HINT], context->regs[REG_HINT],
 	       context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
 	       context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
 	printf("\n**Internal Group**\n"
@@ -212,8 +212,8 @@
 	       "CD:      %X\n"
 	       "Pending: %s\n",
 	       context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false");
-	       
-	//TODO: Window Group, DMA Group      
+
+	//TODO: Window Group, DMA Group
 }
 
 void scan_sprite_table(uint32_t line, vdp_context * context)
@@ -295,7 +295,7 @@
 				height *= 2;
 			}
 			uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
-			uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];		
+			uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
 			uint8_t pal_priority = (tileinfo >> 9) & 0x70;
 			uint8_t row;
 			if (tileinfo & MAP_BIT_V_FLIP) {
@@ -315,7 +315,7 @@
 			} else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) {
 				context->flags |= FLAG_MASKED;
 			}
-			
+
 			context->flags &= ~FLAG_DOT_OFLOW;
 			int16_t i;
 			if (context->flags & FLAG_MASKED) {
@@ -415,7 +415,7 @@
 				context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF);
 				break;
 			case CRAM_WRITE:
-				write_cram(context, context->address, context->dma_val);				
+				write_cram(context, context->address, context->dma_val);
 				//printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles);
 				break;
 			case VSRAM_WRITE:
@@ -575,7 +575,7 @@
 				address &= 0xF000;
 				line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF;
 				mask = 0x7F;
-				
+
 			} else {
 				address &= 0xF800;
 				line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF;
@@ -751,7 +751,7 @@
 		}
 		plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
 		//printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
-		
+
 		if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
 			for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
 				uint8_t pixel;
@@ -1402,9 +1402,9 @@
 		}
 		if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) {
 			//first sort-of active line is treated as 255 internally
-			//it's used for gathering sprite info for line 
+			//it's used for gathering sprite info for line
 			line = (line - 1) & 0xFF;
-			
+
 			//Convert to slot number
 			if (context->latched_mode & BIT_H40){
 				vdp_h40(line, slot, context);
@@ -1789,64 +1789,3 @@
 	}
 }
 
-#define GST_VDP_REGS 0xFA
-#define GST_VDP_MEM 0x12478
-
-uint8_t vdp_load_gst(vdp_context * context, FILE * state_file)
-{
-	uint8_t tmp_buf[CRAM_SIZE*2];
-	fseek(state_file, GST_VDP_REGS, SEEK_SET);
-	if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) {
-		fputs("Failed to read VDP registers from savestate\n", stderr);
-		return 0;
-	}
-	context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
-	if (!context->double_res) {
-		context->framebuf = context->oddbuf;
-	}
-	latch_mode(context);
-	if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) {
-		fputs("Failed to read CRAM from savestate\n", stderr);
-		return 0;
-	}
-	for (int i = 0; i < CRAM_SIZE; i++) {
-		uint16_t value;
-		context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
-		context->colors[i] = color_map[value & 0xEEE];
-		context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW];
-		context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT];
-	}
-	if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) {
-		fputs("Failed to read VSRAM from savestate\n", stderr);
-		return 0;
-	}
-	for (int i = 0; i < VSRAM_SIZE; i++) {
-		context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
-	}
-	fseek(state_file, GST_VDP_MEM, SEEK_SET);
-	if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) {
-		fputs("Failed to read VRAM from savestate\n", stderr);
-		return 0;
-	}
-	return 1;
-}
-
-void vdp_save_state(vdp_context * context, FILE * outfile)
-{
-	uint8_t tmp_buf[CRAM_SIZE*2];
-	fseek(outfile, GST_VDP_REGS, SEEK_SET);
-	fwrite(context->regs, 1, VDP_REGS, outfile);
-	for (int i = 0; i < CRAM_SIZE; i++) {
-		tmp_buf[i*2] = context->cram[i];
-		tmp_buf[i*2+1] = context->cram[i] >> 8;
-	}
-	fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile);
-	for (int i = 0; i < VSRAM_SIZE; i++) {
-		tmp_buf[i*2] = context->vsram[i];
-		tmp_buf[i*2+1] = context->vsram[i] >> 8;
-	}
-	fwrite(tmp_buf, 2, VSRAM_SIZE, outfile);
-	fseek(outfile, GST_VDP_MEM, SEEK_SET);
-	fwrite(context->vdpmem, 1, VRAM_SIZE, outfile);
-}
-
--- a/vdp.h	Sat Jul 20 23:49:31 2013 -0700
+++ b/vdp.h	Fri Jul 26 19:55:04 2013 -0700
@@ -164,7 +164,7 @@
 //runs until the target cycle is reached or the current DMA operation has completed, whicever comes first
 void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles);
 uint8_t vdp_load_gst(vdp_context * context, FILE * state_file);
-void vdp_save_state(vdp_context * context, FILE * outfile);
+uint8_t vdp_save_gst(vdp_context * context, FILE * outfile);
 int vdp_control_port_write(vdp_context * context, uint16_t value);
 int vdp_data_port_write(vdp_context * context, uint16_t value);
 uint16_t vdp_control_port_read(vdp_context * context);
@@ -177,5 +177,8 @@
 void vdp_int_ack(vdp_context * context, uint16_t int_num);
 void vdp_print_sprite_table(vdp_context * context);
 void vdp_print_reg_explain(vdp_context * context);
+void latch_mode(vdp_context * context);
+
+extern int32_t color_map[1 << 12];
 
 #endif //VDP_H_
--- a/ym2612.c	Sat Jul 20 23:49:31 2013 -0700
+++ b/ym2612.c	Fri Jul 26 19:55:04 2013 -0700
@@ -582,9 +582,20 @@
 
 void ym_data_write(ym2612_context * context, uint8_t value)
 {
-	if (context->selected_reg < 0x21 || context->selected_reg > 0xB6 || (context->selected_reg < 0x30 && context->selected_part)) {
+	if (context->selected_reg >= YM_REG_END) {
 		return;
 	}
+	if (context->selected_part) {
+		if (context->selected_reg < YM_PART2_START) {
+			return;
+		}
+		context->part2_regs[context->selected_reg - YM_PART2_START] = value;
+	} else {
+		if (context->selected_reg < YM_PART1_START) {
+			return;
+		}
+		context->part1_regs[context->selected_reg - YM_PART1_START] = value;
+	}
 	dfprintf(debug_file, "write of %X to reg %X in part %d\n", value, context->selected_reg, context->selected_part+1);
 	if (context->selected_reg < 0x30) {
 		//Shared regs
@@ -765,24 +776,3 @@
 	return context->status;
 }
 
-#define GST_YM_OFFSET 0x1E4
-#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET)
-
-uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile)
-{
-	uint8_t regdata[GST_YM_SIZE];
-	fseek(gstfile, GST_YM_OFFSET, SEEK_SET);
-	if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
-		return 0;
-	}
-	for (int i = 0; i < sizeof(regdata); i++) {
-		if (i & 0x100) {
-			ym_address_write_part2(context, i & 0xFF);
-		} else {
-			ym_address_write_part1(context, i);
-		}
-		ym_data_write(context, regdata[i]);
-	}
-	return 1;
-}
-
--- a/ym2612.h	Sat Jul 20 23:49:31 2013 -0700
+++ b/ym2612.h	Fri Jul 26 19:55:04 2013 -0700
@@ -45,6 +45,12 @@
 	uint8_t  keycode;
 } ym_supp;
 
+#define YM_PART1_START 0x21
+#define YM_PART2_START 0x30
+#define YM_REG_END     0xB8
+#define YM_PART1_REGS (YM_REG_END-YM_PART1_START)
+#define YM_PART2_REGS (YM_REG_END-YM_PART2_START)
+
 typedef struct {
     int16_t     *audio_buffer;
     int16_t     *back_buffer;
@@ -66,7 +72,7 @@
 	uint8_t     ch3_mode;
 	uint8_t     current_op;
 	uint8_t     current_env_op;
-	
+
 	uint8_t     timer_control;
 	uint8_t     dac_enable;
 	uint8_t     lfo_enable;
@@ -77,6 +83,8 @@
 	uint8_t     status;
 	uint8_t     selected_reg;
 	uint8_t     selected_part;
+	uint8_t     part1_regs[YM_PART1_REGS];
+	uint8_t     part2_regs[YM_PART2_REGS];
 } ym2612_context;
 
 void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options);
@@ -86,6 +94,7 @@
 void ym_data_write(ym2612_context * context, uint8_t value);
 uint8_t ym_read_status(ym2612_context * context);
 uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile);
+uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile);
 
 #endif //YM2612_H_
 
--- a/z80_to_x86.h	Sat Jul 20 23:49:31 2013 -0700
+++ b/z80_to_x86.h	Fri Jul 26 19:55:04 2013 -0700
@@ -50,6 +50,7 @@
 	void *            system;
 	uint8_t           ram_code_flags[(8 * 1024)/128/8];
 	uint32_t          int_enable_cycle;
+  uint16_t          pc;
 } z80_context;
 
 void translate_z80_stream(z80_context * context, uint32_t address);
--- a/zruntime.S	Sat Jul 20 23:49:31 2013 -0700
+++ b/zruntime.S	Fri Jul 26 19:55:04 2013 -0700
@@ -19,12 +19,13 @@
 	cmp 112(%rsi), %ebp
 	jb no_sync
 sync_io:
+	movw $0, 164(%rsi)
 	call z80_save_context_scratch
 	pop %rax /*return address in read/write func*/
 	pop 104(%rsi) /*return address in native code*/
 	sub $5, %rax /* adjust return addres to point to the call instruction that got us here */
 	mov %rax, (%rsi)
-	
+
 	pop %r15 /* restore callee saved regsiters */
 	pop %r14
 	pop %r13
@@ -32,7 +33,7 @@
 	pop %rbp
 	pop %rbx
 	ret /* return to caller of z80_run */
-	
+
 	.global z80_handle_cycle_limit_int
 z80_handle_cycle_limit_int:
 	cmp 116(%rsi), %ebp
@@ -63,6 +64,7 @@
 zskip_int:
 	cmp 112(%rsi), %ebp
 	jb zskip_sync
+mov %r13w, 164(%rsi)
 	.global z80_do_sync
 z80_do_sync:
 	call z80_save_context
@@ -244,7 +246,7 @@
 	call z_inccycles_io
 	/* genesis Z80 has no IO port hardware and writes have no effect */
 	ret
-	
+
 	.global z80_retrans_stub
 z80_retrans_stub:
 	pop %r14
@@ -264,7 +266,7 @@
 z80_native_addr:
 	call z80_save_context
 	push %rsi
-	mov %rsi, %rdi 
+	mov %rsi, %rdi
 	movzx %r13w, %esi
 	call z80_get_native_address_trans
 	mov %rax, %r13
@@ -275,7 +277,7 @@
 z80_save_context_scratch:
 	mov %r13w, 98(%rsi)  /* scratch1 */
 	mov %r14w, 100(%rsi) /* scratch2 */
-	
+
 	.global z80_save_context
 z80_save_context:
 	mov %r9w, 8(%rsi)    /* SP */
@@ -295,7 +297,7 @@
 z80_load_context_scratch:
 	mov 98(%rsi), %r13w  /* scratch1 */
 	mov 100(%rsi), %r14w /* scratch2 */
-	
+
 	.global z80_load_context
 z80_load_context:
 	mov 8(%rsi), %r9w    /* SP */
@@ -328,4 +330,4 @@
 	movq $0, 104(%rsi)
 no_extra:
 	jmp *(%rsi)
-	
+