changeset 1752:d6d4c006a7b3

Initial attempt at interrupts in new Z80 core and integrating it into main executable
author Michael Pavone <pavone@retrodev.com>
date Sun, 10 Feb 2019 11:58:23 -0800
parents c5d4e1d14dac
children 33ec5df77fac
files Makefile blastem.c cpu_dsl.py debug.c debug.h gdb_remote.c genesis.c genesis.h gst.c sms.c sms.h z80.cpu z80_util.c
diffstat 13 files changed, 380 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Feb 09 11:52:43 2019 -0800
+++ b/Makefile	Sun Feb 10 11:58:23 2019 -0800
@@ -161,7 +161,7 @@
 endif
 
 ifdef NEW_CORE
-Z80OBJS=z80.o
+Z80OBJS=z80.o z80inst.o 
 CFLAGS+= -DNEW_CORE
 else
 Z80OBJS=z80inst.o z80_to_x86.o
@@ -302,7 +302,7 @@
 vos_prog_info : vos_prog_info.o vos_program_module.o
 	$(CC) -o vos_prog_info vos_prog_info.o vos_program_module.o
 	
-%.c : %.cpu
+%.c : %.cpu cpu_dsl.py
 	./cpu_dsl.py -d goto $< > $@
 
 %.o : %.S
--- a/blastem.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/blastem.c	Sun Feb 10 11:58:23 2019 -0800
@@ -11,7 +11,11 @@
 #include "system.h"
 #include "68kinst.h"
 #include "m68k_core.h"
+#ifdef NEW_CORE
+#include "z80.h"
+#else
 #include "z80_to_x86.h"
+#endif
 #include "mem.h"
 #include "vdp.h"
 #include "render.h"
--- a/cpu_dsl.py	Sat Feb 09 11:52:43 2019 -0800
+++ b/cpu_dsl.py	Sun Feb 10 11:58:23 2019 -0800
@@ -666,6 +666,9 @@
 	return decl + '\n\t{dst} = {a} >> {b} | {a} << ({size} + 1 - {b}) | ({check} ? 1 : 0) << ({size}-{b});'.format(dst = dst,
 		a = params[0], b = params[1], size=size, check=carryCheck
 	)
+	
+def _updateSyncCImpl(prog, params):
+	return '\n\tsync_cycle = {sync}(context, target_cycle);'.format(sync=prog.sync_cycle)
 
 _opMap = {
 	'mov': Op(lambda val: val).cUnaryOperator(''),
@@ -711,7 +714,8 @@
 	)),
 	'xchg': Op().addImplementation('c', (0,1), _xchgCImpl),
 	'dispatch': Op().addImplementation('c', None, _dispatchCImpl),
-	'update_flags': Op().addImplementation('c', None, _updateFlagsCImpl)
+	'update_flags': Op().addImplementation('c', None, _updateFlagsCImpl),
+	'update_sync': Op().addImplementation('c', None, _updateSyncCImpl)
 }
 
 #represents a simple DSL instruction
@@ -1028,6 +1032,7 @@
 		self.pointers = {}
 		self.regArrays = {}
 		self.regToArray = {}
+		self.addReg('cycles', 32)
 	
 	def addReg(self, name, size):
 		self.regs[name] = size
@@ -1290,6 +1295,8 @@
 		self.extra_tables = info.get('extra_tables', [])
 		self.context_type = self.prefix + 'context'
 		self.body = info.get('body', [None])[0]
+		self.interrupt = info.get('interrupt', [None])[0]
+		self.sync_cycle = info.get('sync_cycle', [None])[0]
 		self.includes = info.get('include', [])
 		self.flags = flags
 		self.lastDst = None
@@ -1324,7 +1331,6 @@
 		hFile.write('\n}} {0}options;'.format(self.prefix))
 		hFile.write('\n\ntypedef struct {')
 		hFile.write('\n\t{0}options *opts;'.format(self.prefix))
-		hFile.write('\n\tuint32_t cycles;')
 		self.regs.writeHeader(otype, hFile)
 		hFile.write('\n}} {0}context;'.format(self.prefix))
 		hFile.write('\n')
@@ -1380,7 +1386,15 @@
 	def nextInstruction(self, otype):
 		output = []
 		if self.dispatch == 'goto':
+			if self.interrupt in self.subroutines:
+				output.append('\n\tif (context->cycles >= sync_cycle) {')
 			output.append('\n\tif (context->cycles >= target_cycle) { return; }')
+			if self.interrupt in self.subroutines:
+				self.meta = {}
+				self.temp = {}
+				self.subroutines[self.interrupt].inline(self, [], output, otype, None)
+				output.append('\n\t}')
+			
 			self.meta = {}
 			self.temp = {}
 			self.subroutines[self.body].inline(self, [], output, otype, None)
@@ -1410,14 +1424,17 @@
 		if self.dispatch == 'call' and self.body in self.subroutines:
 			pieces.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type))
 			pieces.append('\n{')
+			pieces.append('\n\tuint32_t sync_cycle = {sync}(context, target_cycle);'.format(sync=self.sync_cycle))
 			pieces.append('\n\twhile (context->cycles < target_cycle)')
 			pieces.append('\n\t{')
+			#TODO: Handle interrupts in call dispatch mode
 			self.meta = {}
 			self.temp = {}
 			self.subroutines[self.body].inline(self, [], pieces, otype, None)
 			pieces.append('\n\t}')
 			pieces.append('\n}')
 		elif self.dispatch == 'goto':
+			body.append('\n\tuint32_t sync_cycle = {sync}(context, target_cycle);'.format(sync=self.sync_cycle))
 			body += self.nextInstruction(otype)
 			pieces.append('\nunimplemented:')
 			pieces.append('\n\tfatal_error("Unimplemented instruction\\n");')
--- a/debug.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/debug.c	Sun Feb 10 11:58:23 2019 -0800
@@ -9,6 +9,13 @@
 #include "render.h"
 #include "util.h"
 #include "terminal.h"
+#include "z80inst.h"
+
+#ifdef NEW_CORE
+#define Z80_OPTS opts
+#else
+#define Z80_OPTS options
+#endif
 
 static bp_def * breakpoints = NULL;
 static bp_def * zbreakpoints = NULL;
@@ -190,6 +197,7 @@
 	}
 	switch (param[0])
 	{
+#ifndef NEW_CORE
 	case 'a':
 		if (param[1] == 'f') {
 			if(param[2] == '\'') {
@@ -331,6 +339,7 @@
 			value = context->im;
 		}
 		break;
+#endif
 	case 's':
 		if (param[1] == 'p') {
 			value = context->sp;
@@ -342,7 +351,7 @@
 			if (p_addr < 0x4000) {
 				value = system->zram[p_addr & 0x1FFF];
 			} else if(p_addr >= 0x8000) {
-				uint32_t v_addr = context->bank_reg << 15;
+				uint32_t v_addr = system->z80_bank_reg << 15;
 				v_addr += p_addr & 0x7FFF;
 				if (v_addr < 0x400000) {
 					value = system->cart[v_addr/2];
@@ -377,7 +386,7 @@
 	} else {
 		zremove_breakpoint(context, address);
 	}
-	uint8_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->options->gen);
+	uint8_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->Z80_OPTS->gen);
 	if (!pc) {
 		fatal_error("Failed to get native pointer on entering Z80 debugger at address %X\n", address);
 	}
@@ -487,19 +496,21 @@
 					if (inst.addr_mode == Z80_IMMED) {
 						after = inst.immed;
 					} else if (inst.ea_reg == Z80_HL) {
+#ifndef NEW_CORE
 						after = context->regs[Z80_H] << 8 | context->regs[Z80_L];
 					} else if (inst.ea_reg == Z80_IX) {
 						after = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL];
 					} else if (inst.ea_reg == Z80_IY) {
 						after = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL];
+#endif
 					}
 				} else if(inst.op == Z80_JR) {
 					after += inst.immed;
 				} else if(inst.op == Z80_RET) {
-					uint8_t *sp = get_native_pointer(context->sp, (void **)context->mem_pointers, &context->options->gen);
+					uint8_t *sp = get_native_pointer(context->sp, (void **)context->mem_pointers, &context->Z80_OPTS->gen);
 					if (sp) {
 						after = *sp;
-						sp = get_native_pointer((context->sp + 1) & 0xFFFF, (void **)context->mem_pointers, &context->options->gen);
+						sp = get_native_pointer((context->sp + 1) & 0xFFFF, (void **)context->mem_pointers, &context->Z80_OPTS->gen);
 						if (sp) {
 							after |= *sp << 8;
 						}
@@ -527,9 +538,9 @@
 					break;
 				}
 				memmap_chunk const *ram_chunk = NULL;
-				for (int i = 0; i < context->options->gen.memmap_chunks; i++)
+				for (int i = 0; i < context->Z80_OPTS->gen.memmap_chunks; i++)
 				{
-					memmap_chunk const *cur = context->options->gen.memmap + i;
+					memmap_chunk const *cur = context->Z80_OPTS->gen.memmap + i;
 					if (cur->flags & MMAP_WRITE) {
 						ram_chunk = cur;
 						break;
@@ -540,7 +551,7 @@
 					if (size > ram_chunk->mask) {
 						size = ram_chunk->mask+1;
 					}
-					uint8_t *buf = get_native_pointer(ram_chunk->start, (void **)context->mem_pointers, &context->options->gen);
+					uint8_t *buf = get_native_pointer(ram_chunk->start, (void **)context->mem_pointers, &context->Z80_OPTS->gen);
 					FILE * f = fopen(param, "wb");
 					if (f) {
 						if(fwrite(buf, 1, size, f) != size) {
@@ -558,8 +569,8 @@
 			}
 			default:
 				if (
-					!context->options->gen.debug_cmd_handler
-					|| !context->options->gen.debug_cmd_handler(&system->header, input_buf)
+					!context->Z80_OPTS->gen.debug_cmd_handler
+					|| !context->Z80_OPTS->gen.debug_cmd_handler(&system->header, input_buf)
 				) {
 					fprintf(stderr, "Unrecognized debugger command %s\n", input_buf);
 				}
--- a/debug.h	Sat Feb 09 11:52:43 2019 -0800
+++ b/debug.h	Sun Feb 10 11:58:23 2019 -0800
@@ -3,7 +3,11 @@
 
 #include <stdint.h>
 #include "m68k_core.h"
+#ifdef NEW_CORE
+#include "z80.h"
+#else
 #include "z80_to_x86.h"
+#endif
 
 typedef struct disp_def {
 	struct disp_def * next;
--- a/gdb_remote.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/gdb_remote.c	Sun Feb 10 11:58:23 2019 -0800
@@ -132,7 +132,7 @@
 	}
 }
 
-uint8_t read_byte(m68k_context * context, uint32_t address)
+uint8_t m68k_read_byte(m68k_context * context, uint32_t address)
 {
 	
 	genesis_context *gen = context->system;
@@ -150,7 +150,7 @@
 	return 0;
 }
 
-void write_byte(m68k_context * context, uint32_t address, uint8_t value)
+void m68k_write_byte(m68k_context * context, uint32_t address, uint8_t value)
 {
 	genesis_context *gen = context->system;
 	//TODO: Use generated read/write functions so that memory map is properly respected
@@ -170,7 +170,7 @@
 	if (address >= 0xA00000 && address < 0xA04000) {
 		gen->zram[address & 0x1FFF] = value;
 		genesis_context * gen = context->system;
-#ifndef NO_Z80
+#if !defined(NO_Z80) && !defined(NEW_CORE)
 		z80_handle_code_write(address & 0x1FFF, gen->z80);
 #endif
 		return;
@@ -305,7 +305,7 @@
 		char *cur = send_buf;
 		while (size)
 		{
-			hex_8(read_byte(context, address), cur);
+			hex_8(m68k_read_byte(context, address), cur);
 			cur += 2;
 			address++;
 			size--;
@@ -326,7 +326,7 @@
 			tmp[0] = *(cur++);
 			tmp[1] = *(cur++);
 			tmp[2] = 0;
-			write_byte(context, address, strtoul(tmp, NULL, 16));
+			m68k_write_byte(context, address, strtoul(tmp, NULL, 16));
 			address++;
 			size--;
 		}
--- a/genesis.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/genesis.c	Sun Feb 10 11:58:23 2019 -0800
@@ -39,6 +39,15 @@
 #define MAX_SOUND_CYCLES 100000	
 #endif
 
+#ifdef NEW_CORE
+#define Z80_CYCLE cycles
+#define Z80_OPTS opts
+#define z80_handle_code_write(...)
+#else
+#define Z80_CYCLE current_cycle
+#define Z80_OPTS options
+#endif
+
 void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc)
 {
 	start_section(buf, SECTION_68000);
@@ -64,7 +73,7 @@
 	start_section(buf, SECTION_GEN_BUS_ARBITER);
 	save_int8(buf, gen->z80->reset);
 	save_int8(buf, gen->z80->busreq);
-	save_int16(buf, gen->z80->bank_reg);
+	save_int16(buf, gen->z80_bank_reg);
 	end_section(buf);
 	
 	start_section(buf, SECTION_SEGA_IO_1);
@@ -141,8 +150,8 @@
 
 static void update_z80_bank_pointer(genesis_context *gen)
 {
-	if (gen->z80->bank_reg < 0x140) {
-		gen->z80->mem_pointers[1] = get_native_pointer(gen->z80->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen);
+	if (gen->z80_bank_reg < 0x140) {
+		gen->z80->mem_pointers[1] = get_native_pointer(gen->z80_bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen);
 	} else {
 		gen->z80->mem_pointers[1] = NULL;
 	}
@@ -153,7 +162,7 @@
 	genesis_context *gen = vgen;
 	gen->z80->reset = load_int8(buf);
 	gen->z80->busreq = load_int8(buf);
-	gen->z80->bank_reg = load_int16(buf) & 0x1FF;
+	gen->z80_bank_reg = load_int16(buf) & 0x1FF;
 }
 
 static void adjust_int_cycle(m68k_context * context, vdp_context * v_context);
@@ -284,6 +293,7 @@
 #define dputs
 #endif
 
+#ifndef NEW_CORE
 static void z80_next_int_pulse(z80_context * z_context)
 {
 	genesis_context * gen = z_context->system;
@@ -291,6 +301,7 @@
 	z_context->int_pulse_end = z_context->int_pulse_start + Z80_INT_PULSE_MCLKS;
 	z_context->im2_vector = 0xFF;
 }
+#endif
 
 static void sync_z80(z80_context * z_context, uint32_t mclks)
 {
@@ -300,7 +311,7 @@
 	} else
 #endif
 	{
-		z_context->current_cycle = mclks;
+		z_context->Z80_CYCLE = mclks;
 	}
 }
 
@@ -410,9 +421,14 @@
 			gen->header.enter_debugger = 0;
 			debugger(context, address);
 		}
+#ifdef NEW_CORE
+		if (gen->header.save_state) {
+#else
 		if (gen->header.save_state && (z_context->pc || !z_context->native_pc || z_context->reset || !z_context->busreq)) {
+#endif
 			uint8_t slot = gen->header.save_state - 1;
 			gen->header.save_state = 0;
+#ifndef NEW_CORE
 			if (z_context->native_pc && !z_context->reset) {
 				//advance Z80 core to the start of an instruction
 				while (!z_context->pc)
@@ -420,6 +436,7 @@
 					sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80);
 				}
 			}
+#endif
 			char *save_path = slot == SERIALIZE_SLOT ? NULL : get_slot_name(&gen->header, slot, use_native_states ? "state" : "gst");
 			if (use_native_states || slot == SERIALIZE_SLOT) {
 				serialize_buffer state;
@@ -571,16 +588,16 @@
 	if (vdp_port < 0x10) {
 		//These probably won't currently interact well with the 68K accessing the VDP
 		if (vdp_port < 4) {
-			vdp_run_context(gen->vdp, context->current_cycle);
+			vdp_run_context(gen->vdp, context->Z80_CYCLE);
 			vdp_data_port_write(gen->vdp, value << 8 | value);
 		} else if (vdp_port < 8) {
-			vdp_run_context_full(gen->vdp, context->current_cycle);
+			vdp_run_context_full(gen->vdp, context->Z80_CYCLE);
 			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);
+		sync_sound(gen, context->Z80_CYCLE);
 		psg_write(gen->psg, value);
 	} else {
 		vdp_test_port_write(gen->vdp, value);
@@ -659,7 +676,7 @@
 	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;
+	context->Z80_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
@@ -670,7 +687,7 @@
 	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);
+		vdp_run_context(gen->vdp, context->Z80_CYCLE);
 		if (vdp_port < 4) {
 			ret = vdp_data_port_read(gen->vdp);
 		} else if (vdp_port < 8) {
@@ -711,9 +728,9 @@
 					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]);
+				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;
 				}
@@ -942,7 +959,7 @@
 {
 	z80_context * context = vcontext;
 	genesis_context * gen = context->system;
-	sync_sound(gen, context->current_cycle);
+	sync_sound(gen, context->Z80_CYCLE);
 	if (location & 1) {
 		ym_data_write(gen->ym, value);
 	} else if (location & 2) {
@@ -957,7 +974,7 @@
 {
 	z80_context * context = vcontext;
 	genesis_context * gen = context->system;
-	sync_sound(gen, context->current_cycle);
+	sync_sound(gen, context->Z80_CYCLE);
 	return ym_read_status(gen->ym);
 }
 
@@ -966,10 +983,10 @@
 	z80_context * context = vcontext;
 	genesis_context *gen = context->system;
 	if (gen->bus_busy) {
-		context->current_cycle = context->sync_cycle;
+		context->Z80_CYCLE = gen->m68k->current_cycle;
 	}
 	//typical delay from bus arbitration
-	context->current_cycle += 3 * MCLKS_PER_Z80;
+	context->Z80_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
@@ -979,11 +996,11 @@
 	if (context->mem_pointers[1]) {
 		return context->mem_pointers[1][location ^ 1];
 	}
-	uint32_t address = context->bank_reg << 15 | location;
+	uint32_t address = gen->z80_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);
+		fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, gen->z80_bank_reg << 15);
 	}
 	return 0;
 }
@@ -993,17 +1010,17 @@
 	z80_context * context = vcontext;
 	genesis_context *gen = context->system;
 	if (gen->bus_busy) {
-		context->current_cycle = context->sync_cycle;
+		context->Z80_CYCLE = gen->m68k->current_cycle;
 	}
 	//typical delay from bus arbitration
-	context->current_cycle += 3 * MCLKS_PER_Z80;
+	context->Z80_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;
+	uint32_t address = gen->z80_bank_reg << 15 | location;
 	if (address >= 0xE00000) {
 		address &= 0xFFFF;
 		((uint8_t *)gen->work_ram)[address ^ 1] = value;
@@ -1018,8 +1035,9 @@
 static void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value)
 {
 	z80_context * context = vcontext;
+	genesis_context *gen = context->system;
 
-	context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF;
+	gen->z80_bank_reg = (gen->z80_bank_reg >> 1 | value << 8) & 0x1FF;
 	update_z80_bank_pointer(context->system);
 
 	return context;
@@ -1240,7 +1258,7 @@
 	free(gen->cart);
 	free(gen->m68k);
 	free(gen->work_ram);
-	z80_options_free(gen->z80->options);
+	z80_options_free(gen->z80->Z80_OPTS);
 	free(gen->z80);
 	free(gen->zram);
 	ym_free(gen->ym);
@@ -1368,7 +1386,9 @@
 	z80_options *z_opts = malloc(sizeof(z80_options));
 	init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80, 0xFFFF);
 	gen->z80 = init_z80_context(z_opts);
+#ifndef NEW_CORE
 	gen->z80->next_int_pulse = z80_next_int_pulse;
+#endif
 	z80_assert_reset(gen->z80, 0);
 #else
 	gen->z80 = calloc(1, sizeof(z80_context));
--- a/genesis.h	Sat Feb 09 11:52:43 2019 -0800
+++ b/genesis.h	Sun Feb 10 11:58:23 2019 -0800
@@ -9,7 +9,11 @@
 #include <stdint.h>
 #include "system.h"
 #include "m68k_core.h"
+#ifdef NEW_CORE
+#include "z80.h"
+#else
 #include "z80_to_x86.h"
+#endif
 #include "ym2612.h"
 #include "vdp.h"
 #include "psg.h"
@@ -55,6 +59,7 @@
 	uint8_t         version_reg;
 	uint8_t         bus_busy;
 	uint8_t         reset_requested;
+	uint8_t         z80_bank_reg;
 	eeprom_state    eeprom;
 	nor_state       nor;
 };
--- a/gst.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/gst.c	Sun Feb 10 11:58:23 2019 -0800
@@ -144,6 +144,7 @@
 	}
 	uint8_t * curpos = regdata;
 	uint8_t f = *(curpos++);
+#ifndef NEW_CORE
 	context->flags[ZF_C] = f & 1;
 	f >>= 1;
 	context->flags[ZF_N] = f & 1;
@@ -200,6 +201,7 @@
 		context->mem_pointers[1] = NULL;
 	}
 	context->bank_reg = bank >> 15;
+#endif
 	uint8_t buffer[Z80_RAM_BYTES];
 	fseek(gstfile, GST_Z80_RAM, SEEK_SET);
 	if(fread(buffer, 1, sizeof(buffer), gstfile) != (8*1024)) {
@@ -210,11 +212,15 @@
 	{
 		if (context->mem_pointers[0][i] != buffer[i]) {
 			context->mem_pointers[0][i] = buffer[i];
+#ifndef NEW_CORE
 			z80_handle_code_write(i, context);
+#endif
 		}
 	}
+#ifndef NEW_CORE
 	context->native_pc = NULL;
 	context->extra_pc = NULL;
+#endif
 	return 1;
 }
 
@@ -296,6 +302,7 @@
 	uint8_t regdata[GST_Z80_REG_SIZE];
 	uint8_t * curpos = regdata;
 	memset(regdata, 0, sizeof(regdata));
+#ifndef NEW_CORE
 	uint8_t f = context->flags[ZF_S];
 	f <<= 1;
 	f |= context->flags[ZF_Z] ;
@@ -348,6 +355,7 @@
 	curpos += 3;
 	uint32_t bank = context->bank_reg << 15;
 	write_le_32(curpos, bank);
+#endif
 	fseek(gstfile, GST_Z80_REGS, SEEK_SET);
 	if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
 		return 0;
--- a/sms.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/sms.c	Sun Feb 10 11:58:23 2019 -0800
@@ -9,26 +9,35 @@
 #include "saves.h"
 #include "bindings.h"
 
+#ifdef NEW_CORE
+#define Z80_CYCLE cycles
+#define Z80_OPTS opts
+#define z80_handle_code_write(...)
+#else
+#define Z80_CYCLE current_cycle
+#define Z80_OPTS options
+#endif
+
 static void *memory_io_write(uint32_t location, void *vcontext, uint8_t value)
 {
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
 	if (location & 1) {
 		uint8_t fuzzy_ctrl_0 = sms->io.ports[0].control, fuzzy_ctrl_1 = sms->io.ports[1].control;
-		io_control_write(sms->io.ports, (~value) << 5 & 0x60, z80->current_cycle);
+		io_control_write(sms->io.ports, (~value) << 5 & 0x60, z80->Z80_CYCLE);
 		fuzzy_ctrl_0 |= sms->io.ports[0].control;
-		io_control_write(sms->io.ports+1, (~value) << 3 & 0x60, z80->current_cycle);
+		io_control_write(sms->io.ports+1, (~value) << 3 & 0x60, z80->Z80_CYCLE);
 		fuzzy_ctrl_1 |= sms->io.ports[1].control;
 		if (
 			(fuzzy_ctrl_0 & 0x40 & (sms->io.ports[0].output ^ (value << 1)) & (value << 1))
 			|| (fuzzy_ctrl_0 & 0x40 & (sms->io.ports[1].output ^ (value >> 1)) & (value >> 1))
 		) {
 			//TH is an output and it went from 0 -> 1
-			vdp_run_context(sms->vdp, z80->current_cycle);
+			vdp_run_context(sms->vdp, z80->Z80_CYCLE);
 			vdp_latch_hv(sms->vdp);
 		}
-		io_data_write(sms->io.ports, value << 1, z80->current_cycle);
-		io_data_write(sms->io.ports + 1, value >> 1, z80->current_cycle);
+		io_data_write(sms->io.ports, value << 1, z80->Z80_CYCLE);
+		io_data_write(sms->io.ports + 1, value >> 1, z80->Z80_CYCLE);
 	} else {
 		//TODO: memory control write
 	}
@@ -39,7 +48,7 @@
 {
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
-	vdp_run_context(sms->vdp, z80->current_cycle);
+	vdp_run_context(sms->vdp, z80->Z80_CYCLE);
 	uint16_t hv = vdp_hv_counter_read(sms->vdp);
 	if (location & 1) {
 		return hv;
@@ -52,7 +61,7 @@
 {
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
-	psg_run(sms->psg, z80->current_cycle);
+	psg_run(sms->psg, z80->Z80_CYCLE);
 	psg_write(sms->psg, value);
 	return vcontext;
 }
@@ -61,14 +70,18 @@
 {
 	uint32_t vint = vdp_next_vint(sms->vdp);
 	uint32_t hint = vdp_next_hint(sms->vdp);
+#ifdef NEW_CORE
+	sms->z80->int_cycle = vint < hint ? vint : hint;
+#else
 	sms->z80->int_pulse_start = vint < hint ? vint : hint;
+#endif
 }
 
 static uint8_t vdp_read(uint32_t location, void *vcontext)
 {
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
-	vdp_run_context(sms->vdp, z80->current_cycle);
+	vdp_run_context(sms->vdp, z80->Z80_CYCLE);
 	if (location & 1) {
 		uint8_t ret = vdp_control_port_read(sms->vdp);
 		sms->vdp->flags2 &= ~(FLAG2_VINT_PENDING|FLAG2_HINT_PENDING);
@@ -84,11 +97,11 @@
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
 	if (location & 1) {
-		vdp_run_context_full(sms->vdp, z80->current_cycle);
+		vdp_run_context_full(sms->vdp, z80->Z80_CYCLE);
 		vdp_control_port_write_pbc(sms->vdp, value);
 		update_interrupts(sms);
 	} else {
-		vdp_run_context(sms->vdp, z80->current_cycle);
+		vdp_run_context(sms->vdp, z80->Z80_CYCLE);
 		vdp_data_port_write_pbc(sms->vdp, value);
 	}
 	return vcontext;
@@ -99,13 +112,13 @@
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
 	if (location == 0xC0 || location == 0xDC) {
-		uint8_t port_a = io_data_read(sms->io.ports, z80->current_cycle);
-		uint8_t port_b = io_data_read(sms->io.ports+1, z80->current_cycle);
+		uint8_t port_a = io_data_read(sms->io.ports, z80->Z80_CYCLE);
+		uint8_t port_b = io_data_read(sms->io.ports+1, z80->Z80_CYCLE);
 		return (port_a & 0x3F) | (port_b << 6);
 	}
 	if (location == 0xC1 || location == 0xDD) {
-		uint8_t port_a = io_data_read(sms->io.ports, z80->current_cycle);
-		uint8_t port_b = io_data_read(sms->io.ports+1, z80->current_cycle);
+		uint8_t port_a = io_data_read(sms->io.ports, z80->Z80_CYCLE);
+		uint8_t port_b = io_data_read(sms->io.ports+1, z80->Z80_CYCLE);
 		return (port_a & 0x40) | (port_b >> 2 & 0xF) | (port_b << 1 & 0x80) | 0x10;
 	}
 	return 0xFF;
@@ -343,6 +356,7 @@
 	sms_context *sms = (sms_context *)system;
 	char *statepath = get_slot_name(system, slot, "state");
 	uint8_t ret;
+#ifndef NEW_CORE
 	if (!sms->z80->native_pc) {
 		ret = get_modification_time(statepath) != 0;
 		if (ret) {
@@ -351,6 +365,7 @@
 		goto done;
 		
 	}
+#endif
 	ret = load_state_path(sms, statepath);
 done:
 	free(statepath);
@@ -360,7 +375,7 @@
 static void run_sms(system_header *system)
 {
 	sms_context *sms = (sms_context *)system;
-	uint32_t target_cycle = sms->z80->current_cycle + 3420*16;
+	uint32_t target_cycle = sms->z80->Z80_CYCLE + 3420*16;
 	//TODO: PAL support
 	render_set_video_standard(VID_NTSC);
 	while (!sms->should_return)
@@ -374,7 +389,11 @@
 			system->enter_debugger = 0;
 			zdebugger(sms->z80, sms->z80->pc);
 		}
+#ifdef NEW_CORE
+		if (sms->z80->nmi_cycle == CYCLE_NEVER) {
+#else
 		if (sms->z80->nmi_start == CYCLE_NEVER) {
+#endif
 			uint32_t nmi = vdp_next_nmi(sms->vdp);
 			if (nmi != CYCLE_NEVER) {
 				z80_assert_nmi(sms->z80, nmi);
@@ -382,16 +401,16 @@
 		}
 		z80_run(sms->z80, target_cycle);
 		if (sms->z80->reset) {
-			z80_clear_reset(sms->z80, sms->z80->current_cycle + 128*15);
+			z80_clear_reset(sms->z80, sms->z80->Z80_CYCLE + 128*15);
 		}
-		target_cycle = sms->z80->current_cycle;
+		target_cycle = sms->z80->Z80_CYCLE;
 		vdp_run_context(sms->vdp, target_cycle);
 		psg_run(sms->psg, target_cycle);
 		
 		if (system->save_state) {
 			while (!sms->z80->pc) {
 				//advance Z80 to an instruction boundary
-				z80_run(sms->z80, sms->z80->current_cycle + 1);
+				z80_run(sms->z80, sms->z80->Z80_CYCLE + 1);
 			}
 			save_state(sms, system->save_state - 1);
 			system->save_state = 0;
@@ -399,9 +418,9 @@
 		
 		target_cycle += 3420*16;
 		if (target_cycle > 0x10000000) {
-			uint32_t adjust = sms->z80->current_cycle - 3420*262*2;
-			io_adjust_cycles(sms->io.ports, sms->z80->current_cycle, adjust);
-			io_adjust_cycles(sms->io.ports+1, sms->z80->current_cycle, adjust);
+			uint32_t adjust = sms->z80->Z80_CYCLE - 3420*262*2;
+			io_adjust_cycles(sms->io.ports, sms->z80->Z80_CYCLE, adjust);
+			io_adjust_cycles(sms->io.ports+1, sms->z80->Z80_CYCLE, adjust);
 			z80_adjust_cycles(sms->z80, adjust);
 			vdp_adjust_cycles(sms->vdp, adjust);
 			sms->psg->cycles -= adjust;
@@ -449,15 +468,17 @@
 static void soft_reset(system_header *system)
 {
 	sms_context *sms = (sms_context *)system;
-	z80_assert_reset(sms->z80, sms->z80->current_cycle);
-	sms->z80->target_cycle = sms->z80->sync_cycle = sms->z80->current_cycle;
+	z80_assert_reset(sms->z80, sms->z80->Z80_CYCLE);
+#ifndef NEW_CORE
+	sms->z80->target_cycle = sms->z80->sync_cycle = sms->z80->Z80_CYCLE;
+#endif
 }
 
 static void free_sms(system_header *system)
 {
 	sms_context *sms = (sms_context *)system;
 	vdp_free(sms->vdp);
-	z80_options_free(sms->z80->options);
+	z80_options_free(sms->z80->Z80_OPTS);
 	free(sms->z80);
 	psg_free(sms->psg);
 	free(sms);
@@ -472,7 +493,9 @@
 {
 	sms_context *sms = (sms_context *)system;
 	sms->should_return = 1;
-	sms->z80->target_cycle = sms->z80->sync_cycle = sms->z80->current_cycle;
+#ifndef NEW_CORE
+	sms->z80->target_cycle = sms->z80->sync_cycle = sms->z80->Z80_CYCLE;
+#endif
 }
 
 static void inc_debug_mode(system_header *system)
@@ -577,7 +600,7 @@
 	init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_map, 4, 15, 0xFF);
 	sms->z80 = init_z80_context(zopts);
 	sms->z80->system = sms;
-	sms->z80->options->gen.debug_cmd_handler = debug_commands;
+	sms->z80->Z80_OPTS->gen.debug_cmd_handler = debug_commands;
 	
 	sms->rom = media->buffer;
 	sms->rom_size = rom_size;
--- a/sms.h	Sat Feb 09 11:52:43 2019 -0800
+++ b/sms.h	Sun Feb 10 11:58:23 2019 -0800
@@ -4,7 +4,11 @@
 #include "system.h"
 #include "vdp.h"
 #include "psg.h"
+#ifdef NEW_CORE
+#include "z80.h"
+#else
 #include "z80_to_x86.h"
+#endif
 #include "io.h"
 
 #define SMS_RAM_SIZE (8*1024)
--- a/z80.cpu	Sat Feb 09 11:52:43 2019 -0800
+++ b/z80.cpu	Sun Feb 10 11:58:23 2019 -0800
@@ -3,13 +3,29 @@
 	opcode_size 8
 	extra_tables cb ed dded fded ddcb fdcb dd fd
 	body z80_run_op
+	sync_cycle z80_sync_cycle
+	interrupt z80_interrupt
 	include z80_util.c
 	header z80.h
 	
 declare
 	void init_z80_opts(z80_options * options, memmap_chunk const * chunks, uint32_t num_chunks, memmap_chunk const * io_chunks, uint32_t num_io_chunks, uint32_t clock_divider, uint32_t io_address_mask);
 	z80_context * init_z80_context(z80_options *options);
-	
+	void z80_run(z80_context *context, uint32_t target_cycle);
+	void z80_assert_reset(z80_context * context, uint32_t cycle);
+	void z80_clear_reset(z80_context * context, uint32_t cycle);
+	void z80_assert_busreq(z80_context * context, uint32_t cycle);
+	void z80_clear_busreq(z80_context * context, uint32_t cycle);
+	void z80_assert_nmi(z80_context *context, uint32_t cycle);
+	uint8_t z80_get_busack(z80_context * context, uint32_t cycle);
+	void z80_invalidate_code_range(z80_context *context, uint32_t start, uint32_t end);
+	void z80_adjust_cycles(z80_context * context, uint32_t deduction);
+	void z80_serialize(z80_context *context, serialize_buffer *buf);
+	void z80_deserialize(deserialize_buffer *buf, void *vcontext);
+	void zinsert_breakpoint(z80_context * context, uint16_t address, uint8_t * bp_handler);
+	void zremove_breakpoint(z80_context * context, uint16_t address);
+	void z80_options_free(z80_options *opts);
+
 regs
 	main 8 b c d e h l f a
 	alt 8 b' c' d' e' h' l' f' a'
@@ -31,10 +47,18 @@
 	zflag 8
 	scratch1 16
 	scratch2 16
+	busreq 8
+	busack 8
+	reset 8
 	io_map ptrmemmap_chunk
 	io_chunks 32
 	io_mask 32
+	int_cycle 32
+	int_value 8
+	nmi_cycle 32
+	system ptrvoid
 	fastmem ptr8 64
+	mem_pointers ptr8 4
 	
 flags
 	register f
@@ -59,6 +83,49 @@
 z80_run_op
 	z80_op_fetch
 	dispatch scratch1
+
+z80_interrupt
+	cmp int_cycle cycles
+	if >=U
+	
+	mov 0xFFFFFFFF int_cycle
+	mov 0 iff1
+	mov 0 iff2
+	cycles 6
+	update_sync
+	
+	switch imode
+	case 0
+	dispatch int_value
+	
+	case 1
+	dispatch 0xFF
+	
+	case 2
+	lsl i 8 pc
+	or int_value pc pc
+	#CD is call
+	dispatch 0xCD
+	end
+	
+	else
+	
+	cmp nmi_cycle cycles
+	if >=U
+	
+	mov 0xFFFFFFFF nmi_cycle
+	mov 0 iff1
+	local pch 8
+	lsr pc 8 pch
+	meta high pch
+	meta low pc
+	z80_push
+	mov 0x66 pc
+	update_sync
+	
+	end
+	end
+	
 	
 11001011 cb_prefix
 	z80_op_fetch
@@ -1485,7 +1552,9 @@
 	mov 2 imode
 	
 ed 01D01110 im3
-	mov 3 imode
+	#some sources call this mode 0/1, but unclear
+	#if the behavior is really different from im 0
+	mov 0 imode
 	
 11000011 jp
 	z80_fetch_immed16
--- a/z80_util.c	Sat Feb 09 11:52:43 2019 -0800
+++ b/z80_util.c	Sun Feb 10 11:58:23 2019 -0800
@@ -7,7 +7,7 @@
 	if (fast) {
 		context->scratch1 = fast[context->scratch1 & 0x3FF];
 	} else {
-		context->scratch1 = read_byte(context->scratch1, NULL, &context->opts->gen, context);
+		context->scratch1 = read_byte(context->scratch1, (void **)context->mem_pointers, &context->opts->gen, context);
 	}
 }
 
@@ -18,7 +18,7 @@
 	if (fast) {
 		fast[context->scratch2 & 0x3FF] = context->scratch1;
 	} else {
-		write_byte(context->scratch2, context->scratch1, NULL, &context->opts->gen, context);
+		write_byte(context->scratch2, context->scratch1, (void **)context->mem_pointers, &context->opts->gen, context);
 	}
 }
 
@@ -32,7 +32,7 @@
 	context->opts->gen.memmap = context->io_map;
 	context->opts->gen.memmap_chunks = context->io_chunks;
 	
-	context->scratch1 = read_byte(context->scratch1, NULL, &context->opts->gen, context);
+	context->scratch1 = read_byte(context->scratch1, (void **)context->mem_pointers, &context->opts->gen, context);
 	
 	context->opts->gen.address_mask = tmp_mask;
 	context->opts->gen.memmap = tmp_map;
@@ -49,7 +49,7 @@
 	context->opts->gen.memmap = context->io_map;
 	context->opts->gen.memmap_chunks = context->io_chunks;
 	
-	write_byte(context->scratch2, context->scratch1, NULL, &context->opts->gen, context);
+	write_byte(context->scratch2, context->scratch1, (void **)context->mem_pointers, &context->opts->gen, context);
 	
 	context->opts->gen.address_mask = tmp_mask;
 	context->opts->gen.memmap = tmp_map;
@@ -72,6 +72,11 @@
 	tmp_io_mask = io_address_mask;
 }
 
+void z80_options_free(z80_options *opts)
+{
+	free(opts);
+}
+
 z80_context * init_z80_context(z80_options *options)
 {
 	z80_context *context = calloc(1, sizeof(z80_context));
@@ -79,11 +84,12 @@
 	context->io_map = (memmap_chunk *)tmp_io_chunks;
 	context->io_chunks = tmp_num_io_chunks;
 	context->io_mask = tmp_io_mask;
+	context->int_cycle = context->nmi_cycle = 0xFFFFFFFFU;
 	for(uint32_t address = 0; address < 0x10000; address+=1024)
 	{
-		uint8_t *start = get_native_pointer(address, NULL, &options->gen);
+		uint8_t *start = get_native_pointer(address, (void**)context->mem_pointers, &options->gen);
 		if (start) {
-			uint8_t *end = get_native_pointer(address + 1023, NULL, &options->gen);
+			uint8_t *end = get_native_pointer(address + 1023, (void**)context->mem_pointers, &options->gen);
 			if (end && end - start == 1023) {
 				context->fastmem[address >> 10] = start;
 			}
@@ -92,3 +98,136 @@
 	return context;
 }
 
+uint32_t z80_sync_cycle(z80_context *context, uint32_t target_cycle)
+{
+	if (context->iff1 && context->int_cycle < target_cycle) {
+		target_cycle = context->int_cycle;
+	};
+	if (context->nmi_cycle < target_cycle) {
+		target_cycle = context->nmi_cycle;
+	}
+	return target_cycle;
+}
+
+void z80_run(z80_context *context, uint32_t target_cycle)
+{
+	if (context->reset || context->busack) {
+		context->cycles = target_cycle;
+	} else if (target_cycle > context->cycles) {
+		if (context->busreq) {
+			//busreq is sampled at the end of an m-cycle
+			//we can approximate that by running for a single m-cycle after a bus request
+			target_cycle = context->cycles + 4 * context->opts->gen.clock_divider;
+		}
+		z80_execute(context, target_cycle);
+		if (context->busreq) {
+			context->busack = 1;
+		}
+	}
+}
+
+void z80_assert_reset(z80_context * context, uint32_t cycle)
+{
+	z80_run(context, cycle);
+	context->reset = 1;
+}
+
+void z80_clear_reset(z80_context * context, uint32_t cycle)
+{
+	z80_run(context, cycle);
+	if (context->reset) {
+		context->imode = 0;
+		context->iff1 = context->iff2 = 0;
+		context->pc = 0;
+		context->reset = 0;
+		if (context->busreq) {
+			//TODO: Figure out appropriate delay
+			context->busack = 1;
+		}
+	}
+}
+
+#define MAX_MCYCLE_LENGTH 6
+void z80_assert_busreq(z80_context * context, uint32_t cycle)
+{
+	z80_run(context, cycle);
+	context->busreq = 1;
+	//this is an imperfect aproximation since most M-cycles take less tstates than the max
+	//and a short 3-tstate m-cycle can take an unbounded number due to wait states
+	if (context->cycles - cycle > MAX_MCYCLE_LENGTH * context->opts->gen.clock_divider) {
+		context->busack = 1;
+	}
+}
+
+void z80_clear_busreq(z80_context * context, uint32_t cycle)
+{
+	z80_run(context, cycle);
+	context->busreq = 0;
+	context->busack = 0;
+	//there appears to be at least a 1 Z80 cycle delay between busreq
+	//being released and resumption of execution
+	context->cycles += context->opts->gen.clock_divider;
+}
+
+void z80_assert_nmi(z80_context *context, uint32_t cycle)
+{
+	context->nmi_cycle = cycle;
+}
+
+uint8_t z80_get_busack(z80_context * context, uint32_t cycle)
+{
+	z80_run(context, cycle);
+	return context->busack;
+}
+
+void z80_invalidate_code_range(z80_context *context, uint32_t startA, uint32_t endA)
+{
+	for(startA &= ~0x3FF; startA += 1024; startA < endA)
+	{
+		uint8_t *start = get_native_pointer(startA, (void**)context->mem_pointers, &context->opts->gen);
+		if (start) {
+			uint8_t *end = get_native_pointer(startA + 1023, (void**)context->mem_pointers, &context->opts->gen);
+			if (!end || end - start != 1023) {
+				start = NULL;
+			}
+		}
+		context->fastmem[startA >> 10] = start;
+	}
+}
+
+void z80_adjust_cycles(z80_context * context, uint32_t deduction)
+{
+	context->cycles -= deduction;
+	if (context->int_cycle != 0xFFFFFFFFU) {
+		if (context->int_cycle > deduction) {
+			context->int_cycle -= deduction;
+		} else {
+			context->int_cycle = 0;
+		}
+	}
+	if (context->nmi_cycle != 0xFFFFFFFFU) {
+		if (context->nmi_cycle > deduction) {
+			context->nmi_cycle -= deduction;
+		} else {
+			context->nmi_cycle = 0;
+		}
+	}
+}
+
+void z80_serialize(z80_context *context, serialize_buffer *buf)
+{
+	//TODO: Implement me
+}
+
+void z80_deserialize(deserialize_buffer *buf, void *vcontext)
+{
+	//TODO: Implement me
+}
+
+void zinsert_breakpoint(z80_context * context, uint16_t address, uint8_t * bp_handler)
+{
+}
+
+void zremove_breakpoint(z80_context * context, uint16_t address)
+{
+}