changeset 343:467bfa17004a

Mostly working runtime generation of memory map read/write functions
author Mike Pavone <pavone@retrodev.com>
date Sat, 18 May 2013 11:44:42 -0700
parents 13f994c88c34
children b46771135442
files blastem.c gen_x86.c gen_x86.h m68k_to_x86.c m68k_to_x86.h runtime.S x86_backend.h
diffstat 7 files changed, 764 insertions(+), 206 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Thu May 16 09:37:53 2013 -0700
+++ b/blastem.c	Sat May 18 11:44:42 2013 -0700
@@ -235,12 +235,18 @@
 
 m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value)
 {
+	if (vdp_port & 0x2700E0) {
+		printf("machine freeze due to write to address %X\n", 0xC00000 | vdp_port);
+		exit(1);
+	}
+	vdp_port &= 0x1F;
 	//printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle);
 	sync_components(context, 0);
 	vdp_context * v_context = context->video_context;
 	if (vdp_port < 0x10) {
 		int blocked;
 		if (vdp_port < 4) {
+			uint32_t before_cycle = v_context->cycles;
 			while (vdp_data_port_write(v_context, value) < 0) {
 				while(v_context->flags & FLAG_DMA_RUN) {
 					vdp_run_dma_done(v_context, mclks_per_frame);
@@ -293,25 +299,46 @@
 	return context;
 }
 
-m68k_context * vdp_port_read(uint32_t vdp_port, m68k_context * context)
+m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value)
+{
+	return vdp_port_write(vdp_port, context, value | value << 8);
+}
+
+uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context)
 {
+	if (vdp_port & 0x2700E0) {
+		printf("machine freeze due to read from address %X\n", 0xC00000 | vdp_port);
+		exit(1);
+	}
+	vdp_port &= 0x1F;
+	uint16_t value;
 	sync_components(context, 0);
 	vdp_context * v_context = context->video_context;
 	if (vdp_port < 0x10) {
 		if (vdp_port < 4) {
-			context->value = vdp_data_port_read(v_context);
+			value = vdp_data_port_read(v_context);
 		} else if(vdp_port < 8) {
-			context->value = vdp_control_port_read(v_context);
+			value = vdp_control_port_read(v_context);
 		} else {
-			context->value = vdp_hv_counter_read(v_context);
-			//printf("HV Counter: %X at cycle %d\n", context->value, v_context->cycles);
+			value = vdp_hv_counter_read(v_context);
+			//printf("HV Counter: %X at cycle %d\n", value, v_context->cycles);
 		}
 		context->current_cycle = v_context->cycles/MCLKS_PER_68K;
 	} else {
 		printf("Illegal read from PSG or test register port %X\n", vdp_port);
 		exit(1);
 	}
-	return context;
+	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;
+	}
 }
 
 #define TH 0x40
@@ -351,7 +378,7 @@
 	pad->output = value;
 }
 
-void io_data_read(io_port * pad, m68k_context * context)
+uint8_t io_data_read(io_port * pad, m68k_context * context)
 {
 	uint8_t control = pad->control | 0x80;
 	uint8_t th = control & pad->output;
@@ -377,10 +404,11 @@
 			input = pad->input[GAMEPAD_TH0] | 0xC;
 		}
 	}
-	context->value = ((~input) & (~control)) | (pad->output & control);
+	uint8_t value = ((~input) & (~control)) | (pad->output & control);
 	/*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) {
-		printf ("value: %X\n", context->value);
+		printf ("value: %X\n", value);
 	}*/
+	return value;
 }
 
 uint32_t zram_counter = 0;
@@ -596,8 +624,9 @@
 #define NO_DISK 0x20
 uint8_t version_reg = NO_DISK | USA;
 
-m68k_context * io_read(uint32_t location, m68k_context * context)
+uint8_t io_read(uint32_t location, m68k_context * context)
 {
+	uint8_t value;
 	genesis_context *gen = context->system;
 	if (location < 0x10000) {
 		if (busack_cycle <= context->current_cycle) {
@@ -607,15 +636,83 @@
 		if (!(busack==Z80_REQ_BUSY || reset)) {
 			location &= 0x7FFF;
 			if (location < 0x4000) {
-				context->value = z80_ram[location & 0x1FFF];
+				value = z80_ram[location & 0x1FFF];
 			} else if (location < 0x6000) {
 				ym_run(gen->ym, context->current_cycle);
-				context->value = ym_read_status(gen->ym);
+				value = ym_read_status(gen->ym);
 			} else {
-				context->value = 0xFF;
+				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(&gamepad_1, context);
+				break;
+			case 0x2:
+				value = io_data_read(&gamepad_2, context);
+				break;
+			case 0x3://PORT C Data
+				break;
+			case 0x4:
+				value = gamepad_1.control;
+				break;
+			case 0x5:
+				value = gamepad_2.control;
+				break;
+			default:
+				value = 0xFF;
 			}
 		} else {
-			context->value = 0xFF;
+			if (location == 0x1100) {
+				if (busack_cycle <= context->current_cycle) {
+					busack = new_busack;
+					busack_cycle = CYCLE_NEVER;
+				}
+				value = Z80_RES_BUSACK || busack;
+				dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d, busack: %d, busack_cycle %d)\n", value, context->current_cycle, reset, busack, busack_cycle);
+			} else if (location == 0x1200) {
+				value = !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;
+	genesis_context * gen = context->system;
+	if (location < 0x10000) {
+		if (busack_cycle <= context->current_cycle) {
+			busack = new_busack;
+			busack_cycle = CYCLE_NEVER;
+		}
+		if (!(busack==Z80_REQ_BUSY || reset)) {
+			location &= 0x7FFF;
+			if (location < 0x4000) {
+				value = z80_ram[location & 0x1FFE];
+			} else if (location < 0x6000) {
+				ym_run(gen->ym, context->current_cycle);
+				value = ym_read_status(gen->ym);	
+			} else {
+				value = 0xFF;
+			}
+			value = value | (value << 8);
+		} else {
+			value = 0xFFFF;
 		}
 	} else {
 		location &= 0x1FFF;
@@ -625,111 +722,47 @@
 			case 0x0:
 				//version bits should be 0 for now since we're not emulating TMSS
 				//Not sure about the other bits
-				context->value = version_reg;
+				value = version_reg;
 				break;
 			case 0x1:
-				io_data_read(&gamepad_1, context);
+				value = io_data_read(&gamepad_1, context);
 				break;
 			case 0x2:
-				io_data_read(&gamepad_2, context);
+				value = io_data_read(&gamepad_2, context);
 				break;
 			case 0x3://PORT C Data
 				break;
 			case 0x4:
-				context->value = gamepad_1.control;
+				value = gamepad_1.control;
 				break;
 			case 0x5:
-				context->value = gamepad_2.control;
+				value = gamepad_2.control;
+				break;
+			case 0x6:
+				//PORT C Control
+				value = 0;
 				break;
+			default:
+				value = 0;
 			}
+			value = value | (value << 8);
+			//printf("Word read to %X returned %d\n", location, value);
 		} else {
 			if (location == 0x1100) {
 				if (busack_cycle <= context->current_cycle) {
 					busack = new_busack;
 					busack_cycle = CYCLE_NEVER;
 				}
-				context->value = Z80_RES_BUSACK || busack;
-				dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d, busack: %d, busack_cycle %d)\n", context->value, context->current_cycle, reset, busack, busack_cycle);
+				value = (Z80_RES_BUSACK || busack) << 8;
+				//printf("Word read of BUSREQ returned %d\n", value);
 			} else if (location == 0x1200) {
-				context->value = !reset;
-			} else {
-				printf("Byte read of unknown IO location: %X\n", location);
-			}
-		}
-	}
-	return context;
-}
-
-m68k_context * io_read_w(uint32_t location, m68k_context * context)
-{
-	genesis_context * gen = context->system;
-	if (location < 0x10000) {
-		if (busack_cycle <= context->current_cycle) {
-			busack = new_busack;
-			busack_cycle = CYCLE_NEVER;
-		}
-		if (!(busack==Z80_REQ_BUSY || reset)) {
-			location &= 0x7FFF;
-			uint16_t value;
-			if (location < 0x4000) {
-				value = z80_ram[location & 0x1FFE];
-			} else if (location < 0x6000) {
-				ym_run(gen->ym, context->current_cycle);
-				value = ym_read_status(gen->ym);	
-			} else {
-				value = 0xFF;
-			}
-			context->value = value | (value << 8);
-		} else {
-			context->value = 0xFFFF;
-		}
-	} else {
-		location &= 0x1FFF;
-		if (location < 0x100) {
-			switch(location/2)
-			{
-			case 0x0:
-				//version bits should be 0 for now since we're not emulating TMSS
-				//Not sure about the other bits
-				context->value = 0;
-				break;
-			case 0x1:
-				io_data_read(&gamepad_1, context);
-				break;
-			case 0x2:
-				io_data_read(&gamepad_2, context);
-				break;
-			case 0x3://PORT C Data
-				break;
-			case 0x4:
-				context->value = gamepad_1.control;
-				break;
-			case 0x5:
-				context->value = gamepad_2.control;
-				break;
-			case 0x6:
-				//PORT C Control
-				context->value = 0;
-				break;
-			}
-			context->value = context->value | (context->value << 8);
-			//printf("Word read to %X returned %d\n", location, context->value);
-		} else {
-			if (location == 0x1100) {
-				if (busack_cycle <= context->current_cycle) {
-					busack = new_busack;
-					busack_cycle = CYCLE_NEVER;
-				}
-				context->value = (Z80_RES_BUSACK || busack) << 8;
-				//printf("Word read of BUSREQ returned %d\n", context->value);
-			} else if (location == 0x1200) {
-				context->value = (!reset) << 8;
+				value = (!reset) << 8;
 			} else {
 				printf("Word read of unknown IO location: %X\n", location);
 			}
 		}
 	}
-	return context;
+	return value;
 }
 
 z80_context * z80_write_ym(uint16_t location, z80_context * context, uint8_t value)
@@ -1000,7 +1033,19 @@
 	m68k_context context;
 	x86_68k_options opts;
 	gen->m68k = &context;
-	init_x86_68k_opts(&opts);
+	memmap_chunk memmap[] = {
+		{0,        0x400000,  0xFFFFFF, 0, MMAP_READ | MMAP_WRITE,             cart,
+		           NULL,          NULL,         NULL,            NULL},
+		{0xE00000, 0x1000000, 0xFFFF,   0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, 
+		           NULL,          NULL,         NULL,            NULL},
+		{0xC00000, 0xE00000,  0x1FFFFF, 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,                                  NULL,
+		           (read_16_fun)io_read_w,      (write_16_fun)io_write_w,
+		           (read_8_fun)io_read,         (write_8_fun)io_write}
+	};
+	init_x86_68k_opts(&opts, memmap, sizeof(memmap)/sizeof(memmap_chunk));
 	opts.address_log = address_log;
 	init_68k_context(&context, opts.native_code_map, &opts);
 	
@@ -1068,11 +1113,11 @@
 
 void detect_region()
 {
-	if (detect_specific_region('U')) {
+	if (detect_specific_region('U')|| detect_specific_region('B') || detect_specific_region('4')) {
 		version_reg = NO_DISK | USA;
 	} else if (detect_specific_region('J')) {
 		version_reg = NO_DISK | JAP;
-	} if (detect_specific_region('E') || detect_specific_region('A') || detect_specific_region('B') || detect_specific_region('4')) {
+	} else if (detect_specific_region('E') || detect_specific_region('A')) {
 		version_reg = NO_DISK | EUR;
 	}
 }
--- a/gen_x86.c	Thu May 16 09:37:53 2013 -0700
+++ b/gen_x86.c	Sat May 18 11:44:42 2013 -0700
@@ -2,6 +2,7 @@
 #include "68kinst.h"
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #define REX_RM_FIELD 0x1
 #define REX_SIB_FIELD 0x2
@@ -220,6 +221,58 @@
 	return out;
 }
 
+uint8_t * x86_rrdisp32_sizedir(uint8_t * out, uint16_t opcode, uint8_t reg, uint8_t base, int32_t disp, uint8_t size, uint8_t dir)
+{
+	//TODO: Deal with the fact that AH, BH, CH and DH can only be in the R/M param when there's a REX prefix
+	uint8_t tmp;
+	if (size == SZ_W) {
+		*(out++) = PRE_SIZE;
+	}
+	if (size == SZ_Q || reg >= R8 || base >= R8 || (size == SZ_B && reg >= RSP && reg <= RDI)) {
+		*out = PRE_REX;
+		if (reg >= AH && reg <= BH) {
+			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
+			exit(1);
+		}
+		if (size == SZ_Q) {
+			*out |= REX_QUAD;
+		}
+		if (reg >= R8) {
+			*out |= REX_REG_FIELD;
+			reg -= (R8 - X86_R8);
+		}
+		if (base >= R8) {
+			*out |= REX_RM_FIELD;
+			base -= (R8 - X86_R8);
+		}
+		out++;
+	}
+	if (size == SZ_B) {
+		if (reg >= AH && reg <= BH) {
+			reg -= (AH-X86_AH);
+		}
+	} else {
+		opcode |= BIT_SIZE;
+	}
+	opcode |= dir;
+	if (opcode >= 0x100) {
+		*(out++) = opcode >> 8;
+		*(out++) = opcode;
+	} else {
+		*(out++) = opcode;
+	}
+	*(out++) = MODE_REG_DISPLACE32 | base | (reg << 3);
+	if (base == RSP) {
+		//add SIB byte, with no index and RSP as base
+		*(out++) = (RSP << 3) | RSP;
+	}
+	*(out++) = disp;
+	*(out++) = disp >> 8;
+	*(out++) = disp >> 16;
+	*(out++) = disp >> 24;
+	return out;
+}
+
 uint8_t * x86_rrind_sizedir(uint8_t * out, uint8_t opcode, uint8_t reg, uint8_t base, uint8_t size, uint8_t dir)
 {
 	//TODO: Deal with the fact that AH, BH, CH and DH can only be in the R/M param when there's a REX prefix
@@ -262,6 +315,54 @@
 	return out;
 }
 
+uint8_t * x86_rrindex_sizedir(uint8_t * out, uint8_t opcode, uint8_t reg, uint8_t base, uint8_t index, uint8_t scale, uint8_t size, uint8_t dir)
+{
+	//TODO: Deal with the fact that AH, BH, CH and DH can only be in the R/M param when there's a REX prefix
+	uint8_t tmp;
+	if (size == SZ_W) {
+		*(out++) = PRE_SIZE;
+	}
+	if (size == SZ_Q || reg >= R8 || base >= R8 || (size == SZ_B && reg >= RSP && reg <= RDI)) {
+		*out = PRE_REX;
+		if (reg >= AH && reg <= BH) {
+			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
+			exit(1);
+		}
+		if (size == SZ_Q) {
+			*out |= REX_QUAD;
+		}
+		if (reg >= R8) {
+			*out |= REX_REG_FIELD;
+			reg -= (R8 - X86_R8);
+		}
+		if (base >= R8) {
+			*out |= REX_RM_FIELD;
+			base -= (R8 - X86_R8);
+		}
+		if (index >= R8) {
+			*out |= REX_SIB_FIELD;
+			index -= (R8 - X86_R8);
+		}
+		out++;
+	}
+	if (size == SZ_B) {
+		if (reg >= AH && reg <= BH) {
+			reg -= (AH-X86_AH);
+		}
+	} else {
+		opcode |= BIT_SIZE;
+	}
+	*(out++) = opcode | dir;
+	*(out++) = MODE_REG_INDIRECT | base | (RSP << 3);
+	if (base == RSP) {
+		if (scale == 4) {
+			scale = 3;
+		}
+		*(out++) = scale << 6 | (index << 3) | base;
+	}
+	return out;
+}
+
 uint8_t * x86_r_size(uint8_t * out, uint8_t opcode, uint8_t opex, uint8_t dst, uint8_t size)
 {
 	uint8_t tmp;
@@ -949,6 +1050,16 @@
 	return x86_rrdisp8_sizedir(out, OP_MOV, dst, src_base, disp, size, BIT_DIR);
 }
 
+uint8_t * mov_rrdisp32(uint8_t * out, uint8_t src, uint8_t dst_base, int32_t disp, uint8_t size)
+{
+	return x86_rrdisp32_sizedir(out, OP_MOV, src, dst_base, disp, size, 0);
+}
+
+uint8_t * mov_rdisp32r(uint8_t * out, uint8_t src_base, int32_t disp, uint8_t dst, uint8_t size)
+{
+	return x86_rrdisp32_sizedir(out, OP_MOV, dst, src_base, disp, size, BIT_DIR);
+}
+
 uint8_t * mov_rrind(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size)
 {
 	return x86_rrind_sizedir(out, OP_MOV, src, dst, size, 0);
@@ -959,6 +1070,16 @@
 	return x86_rrind_sizedir(out, OP_MOV, dst, src, size, BIT_DIR);
 }
 
+uint8_t * mov_rrindex(uint8_t * out, uint8_t src, uint8_t dst_base, uint8_t dst_index, uint8_t scale, uint8_t size)
+{
+	return x86_rrindex_sizedir(out, OP_MOV, src, dst_base, dst_index, scale, size, 0);
+}
+
+uint8_t * mov_rindexr(uint8_t * out, uint8_t src_base, uint8_t src_index, uint8_t scale, uint8_t dst, uint8_t size)
+{
+	return x86_rrindex_sizedir(out, OP_MOV, dst, src_base, src_index, scale, size, BIT_DIR);
+}
+
 uint8_t * mov_ir(uint8_t * out, int64_t val, uint8_t dst, uint8_t size)
 {	
 	uint8_t sign_extend = 0;
@@ -1370,6 +1491,36 @@
 	return out;
 }
 
+uint8_t * bit_rrdisp32(uint8_t * out, uint8_t op2, uint8_t src, uint8_t dst_base, int32_t dst_disp, uint8_t size)
+{
+	if (size == SZ_W) {
+		*(out++) = PRE_SIZE;
+	}
+	if (size == SZ_Q || src >= R8 || dst_base >= R8) {
+		*out = PRE_REX;
+		if (size == SZ_Q) {
+			*out |= REX_QUAD;
+		}
+		if (src >= R8) {
+			*out |= REX_REG_FIELD;
+			src -= (R8 - X86_R8);
+		}
+		if (dst_base >= R8) {
+			*out |= REX_RM_FIELD;
+			dst_base -= (R8 - X86_R8);
+		}
+		out++;
+	}
+	*(out++) = PRE_2BYTE;
+	*(out++) = op2;
+	*(out++) = MODE_REG_DISPLACE32 | dst_base | (src << 3);
+	*(out++) = dst_disp;
+	*(out++) = dst_disp >> 8;
+	*(out++) = dst_disp >> 16;
+	*(out++) = dst_disp >> 24;
+	return out;
+}
+
 uint8_t * bit_ir(uint8_t * out, uint8_t op_ex, uint8_t val, uint8_t dst, uint8_t size)
 {
 	if (size == SZ_W) {
@@ -1427,6 +1578,11 @@
 	return bit_rrdisp8(out, OP2_BT, src, dst_base, dst_disp, size);
 }
 
+uint8_t * bt_rrdisp32(uint8_t * out, uint8_t src, uint8_t dst_base, int32_t dst_disp, uint8_t size)
+{
+	return bit_rrdisp32(out, OP2_BT, src, dst_base, dst_disp, size);
+}
+
 uint8_t * bt_ir(uint8_t * out, uint8_t val, uint8_t dst, uint8_t size)
 {
 	return bit_ir(out, OP_EX_BT, val, dst, size);
--- a/gen_x86.h	Thu May 16 09:37:53 2013 -0700
+++ b/gen_x86.h	Sat May 18 11:44:42 2013 -0700
@@ -57,7 +57,7 @@
 	MODE_REG_INDEXED = 4,
 	MODE_REG_DISPLACE8 = 0x40,
 	MODE_REG_INDEXED_DISPLACE8 = 0x44,
-	MODE_REG_DIPSLACE32 = 0x80,
+	MODE_REG_DISPLACE32 = 0x80,
 	MODE_REG_INDEXED_DIPSLACE32 = 0x84,
 	MODE_REG_DIRECT = 0xC0,
 //"phony" mode
@@ -151,6 +151,10 @@
 uint8_t * mov_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size);
 uint8_t * mov_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size);
 uint8_t * mov_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size);
+uint8_t * mov_rrdisp32(uint8_t * out, uint8_t src, uint8_t dst_base, int32_t disp, uint8_t size);
+uint8_t * mov_rdisp32r(uint8_t * out, uint8_t src_base, int32_t disp, uint8_t dst, uint8_t size);
+uint8_t * mov_rrindex(uint8_t * out, uint8_t src, uint8_t dst_base, uint8_t dst_index, uint8_t scale, uint8_t size);
+uint8_t * mov_rindexr(uint8_t * out, uint8_t src_base, uint8_t src_index, uint8_t scale, uint8_t dst, uint8_t size);
 uint8_t * mov_rrind(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size);
 uint8_t * mov_rindr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size);
 uint8_t * mov_ir(uint8_t * out, int64_t val, uint8_t dst, uint8_t size);
@@ -170,6 +174,7 @@
 uint8_t * setcc_rdisp8(uint8_t * out, uint8_t cc, uint8_t dst, int8_t disp);
 uint8_t * bt_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size);
 uint8_t * bt_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t dst_disp, uint8_t size);
+uint8_t * bt_rrdisp32(uint8_t * out, uint8_t src, uint8_t dst_base, int32_t dst_disp, uint8_t size);
 uint8_t * bt_ir(uint8_t * out, uint8_t val, uint8_t dst, uint8_t size);
 uint8_t * bt_irdisp8(uint8_t * out, uint8_t val, uint8_t dst_base, int8_t dst_disp, uint8_t size);
 uint8_t * bts_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size);
--- a/m68k_to_x86.c	Thu May 16 09:37:53 2013 -0700
+++ b/m68k_to_x86.c	Sat May 18 11:44:42 2013 -0700
@@ -24,13 +24,7 @@
 char disasm_buf[1024];
 
 void handle_cycle_limit_int();
-void m68k_read_word_scratch1();
-void m68k_read_long_scratch1();
-void m68k_read_byte_scratch1();
-void m68k_write_word();
-void m68k_write_long_lowfirst();
-void m68k_write_long_highfirst();
-void m68k_write_byte();
+void handle_cycle_limit();
 void m68k_save_context();
 void m68k_load_context();
 void m68k_modified_ret_addr();
@@ -65,6 +59,16 @@
 	return dst;
 }
 
+uint8_t * check_cycles(uint8_t * dst)
+{
+	dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D);
+	uint8_t * jmp_off = dst+1;
+	dst = jcc(dst, CC_NC, dst + 7);
+	dst = call(dst, (uint8_t *)handle_cycle_limit);
+	*jmp_off = dst - (jmp_off+1);
+	return dst;
+}
+
 int8_t native_reg(m68k_op_info * op, x86_68k_options * opts)
 {
 	if (op->addr_mode == MODE_REG) {
@@ -155,13 +159,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_read_byte_scratch1);
+			out = call(out, opts->read_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_read_word_scratch1);
+			out = call(out, opts->read_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_read_long_scratch1);
+			out = call(out, opts->read_32);
 			break;
 		}
 		
@@ -187,13 +191,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_read_byte_scratch1);
+			out = call(out, opts->read_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_read_word_scratch1);
+			out = call(out, opts->read_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_read_long_scratch1);
+			out = call(out, opts->read_32);
 			break;
 		}
 		ea->mode = MODE_REG_DIRECT;
@@ -243,13 +247,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_read_byte_scratch1);
+			out = call(out, opts->read_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_read_word_scratch1);
+			out = call(out, opts->read_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_read_long_scratch1);
+			out = call(out, opts->read_32);
 			break;
 		}
 		ea->mode = MODE_REG_DIRECT;
@@ -261,13 +265,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_read_byte_scratch1);
+			out = call(out, opts->read_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_read_word_scratch1);
+			out = call(out, opts->read_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_read_long_scratch1);
+			out = call(out, opts->read_32);
 			break;
 		}
 		ea->mode = MODE_REG_DIRECT;
@@ -313,13 +317,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_read_byte_scratch1);
+			out = call(out, opts->read_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_read_word_scratch1);
+			out = call(out, opts->read_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_read_long_scratch1);
+			out = call(out, opts->read_32);
 			break;
 		}
 		ea->mode = MODE_REG_DIRECT;
@@ -336,13 +340,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_read_byte_scratch1);
+			out = call(out, opts->read_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_read_word_scratch1);
+			out = call(out, opts->read_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_read_long_scratch1);
+			out = call(out, opts->read_32);
 			break;
 		}
 		ea->mode = MODE_REG_DIRECT;
@@ -416,13 +420,13 @@
 			switch (inst->extra.size)
 			{
 			case OPSIZE_BYTE:
-				out = call(out, (char *)m68k_read_byte_scratch1);
+				out = call(out, opts->read_8);
 				break;
 			case OPSIZE_WORD:
-				out = call(out, (char *)m68k_read_word_scratch1);
+				out = call(out, opts->read_16);
 				break;
 			case OPSIZE_LONG:
-				out = call(out, (char *)m68k_read_long_scratch1);
+				out = call(out, opts->read_32);
 				break;
 			}
 		}
@@ -463,13 +467,13 @@
 			switch (inst->extra.size)
 			{
 			case OPSIZE_BYTE:
-				out = call(out, (char *)m68k_read_byte_scratch1);
+				out = call(out, opts->read_8);
 				break;
 			case OPSIZE_WORD:
-				out = call(out, (char *)m68k_read_word_scratch1);
+				out = call(out, opts->read_16);
 				break;
 			case OPSIZE_LONG:
-				out = call(out, (char *)m68k_read_long_scratch1);
+				out = call(out, opts->read_32);
 				break;
 			}
 			out = pop_r(out, SCRATCH2);
@@ -525,13 +529,13 @@
 			switch (inst->extra.size)
 			{
 			case OPSIZE_BYTE:
-				out = call(out, (char *)m68k_read_byte_scratch1);
+				out = call(out, opts->read_8);
 				break;
 			case OPSIZE_WORD:
-				out = call(out, (char *)m68k_read_word_scratch1);
+				out = call(out, opts->read_16);
 				break;
 			case OPSIZE_LONG:
-				out = call(out, (char *)m68k_read_long_scratch1);
+				out = call(out, opts->read_32);
 				break;
 			}
 			out = pop_r(out, SCRATCH2);
@@ -547,13 +551,13 @@
 			switch (inst->extra.size)
 			{
 			case OPSIZE_BYTE:
-				out = call(out, (char *)m68k_read_byte_scratch1);
+				out = call(out, opts->read_8);
 				break;
 			case OPSIZE_WORD:
-				out = call(out, (char *)m68k_read_word_scratch1);
+				out = call(out, opts->read_16);
 				break;
 			case OPSIZE_LONG:
-				out = call(out, (char *)m68k_read_long_scratch1);
+				out = call(out, opts->read_32);
 				break;
 			}
 			out = pop_r(out, SCRATCH2);
@@ -605,13 +609,13 @@
 			switch (inst->extra.size)
 			{
 			case OPSIZE_BYTE:
-				out = call(out, (char *)m68k_read_byte_scratch1);
+				out = call(out, opts->read_8);
 				break;
 			case OPSIZE_WORD:
-				out = call(out, (char *)m68k_read_word_scratch1);
+				out = call(out, opts->read_16);
 				break;
 			case OPSIZE_LONG:
-				out = call(out, (char *)m68k_read_long_scratch1);
+				out = call(out, opts->read_32);
 				break;
 			}
 			out = pop_r(out, SCRATCH2);
@@ -629,13 +633,13 @@
 			switch (inst->extra.size)
 			{
 			case OPSIZE_BYTE:
-				out = call(out, (char *)m68k_read_byte_scratch1);
+				out = call(out, opts->read_8);
 				break;
 			case OPSIZE_WORD:
-				out = call(out, (char *)m68k_read_word_scratch1);
+				out = call(out, opts->read_16);
 				break;
 			case OPSIZE_LONG:
-				out = call(out, (char *)m68k_read_long_scratch1);
+				out = call(out, opts->read_32);
 				break;
 			}
 			out = pop_r(out, SCRATCH2);
@@ -664,13 +668,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			out = call(out, (char *)m68k_write_byte);
+			out = call(out, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			out = call(out, (char *)m68k_write_word);
+			out = call(out, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			out = call(out, (char *)m68k_write_long_lowfirst);
+			out = call(out, opts->write_32_lowfirst);
 			break;
 		}
 	}
@@ -851,13 +855,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			dst = call(dst, (char *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			dst = call(dst, (char *)m68k_write_word);
+			dst = call(dst, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			dst = call(dst, (char *)m68k_write_long_highfirst);
+			dst = call(dst, opts->write_32_highfirst);
 			break;
 		}
 		if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
@@ -894,13 +898,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			dst = call(dst, (char *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			dst = call(dst, (char *)m68k_write_word);
+			dst = call(dst, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			dst = call(dst, (char *)m68k_write_long_highfirst);
+			dst = call(dst, opts->write_32_highfirst);
 			break;
 		}
 		break;
@@ -968,13 +972,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			dst = call(dst, (char *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			dst = call(dst, (char *)m68k_write_word);
+			dst = call(dst, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			dst = call(dst, (char *)m68k_write_long_highfirst);
+			dst = call(dst, opts->write_32_highfirst);
 			break;
 		}
 		break;
@@ -998,13 +1002,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			dst = call(dst, (char *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			dst = call(dst, (char *)m68k_write_word);
+			dst = call(dst, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			dst = call(dst, (char *)m68k_write_long_highfirst);
+			dst = call(dst, opts->write_32_highfirst);
 			break;
 		}
 		break;
@@ -1068,13 +1072,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			dst = call(dst, (char *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			dst = call(dst, (char *)m68k_write_word);
+			dst = call(dst, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			dst = call(dst, (char *)m68k_write_long_highfirst);
+			dst = call(dst, opts->write_32_highfirst);
 			break;
 		}
 		break;
@@ -1103,13 +1107,13 @@
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
-			dst = call(dst, (char *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			break;
 		case OPSIZE_WORD:
-			dst = call(dst, (char *)m68k_write_word);
+			dst = call(dst, opts->write_16);
 			break;
 		case OPSIZE_LONG:
-			dst = call(dst, (char *)m68k_write_long_highfirst);
+			dst = call(dst, opts->write_32_highfirst);
 			break;
 		}
 		break;
@@ -1275,9 +1279,9 @@
 					}
 				}
 				if (inst->extra.size == OPSIZE_LONG) {
-					dst = call(dst, (uint8_t *)m68k_write_long_lowfirst);
+					dst = call(dst, opts->write_32_lowfirst);
 				} else {
-					dst = call(dst, (uint8_t *)m68k_write_word);
+					dst = call(dst, opts->write_16);
 				}
 				dst = pop_r(dst, SCRATCH2);
 				if (inst->dst.addr_mode != MODE_AREG_PREDEC) {
@@ -1415,9 +1419,9 @@
 			if (inst->dst.params.immed & (1 << reg)) {
 				dst = push_r(dst, SCRATCH1);
 				if (inst->extra.size == OPSIZE_LONG) {
-					dst = call(dst, (uint8_t *)m68k_read_long_scratch1);
+					dst = call(dst, opts->read_32);
 				} else {
-					dst = call(dst, (uint8_t *)m68k_read_word_scratch1);
+					dst = call(dst, opts->read_16);
 				}
 				if (inst->extra.size == OPSIZE_WORD) {
 					dst = movsx_rr(dst, SCRATCH1, SCRATCH1, SZ_W, SZ_D);
@@ -1737,7 +1741,7 @@
 	}
 	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-	dst = call(dst, (uint8_t *)m68k_write_long_lowfirst);
+	dst = call(dst, opts->write_32_lowfirst);
 	return dst;
 }
 
@@ -1753,7 +1757,7 @@
 	}
 	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-	dst = call(dst, (char *)m68k_write_long_highfirst);
+	dst = call(dst, opts->write_32_highfirst);
 	uint8_t * dest_addr = get_native_address(opts->native_code_map, (inst->address+2) + disp);
 	if (!dest_addr) {
 		opts->deferred = defer_address(opts->deferred, (inst->address+2) + disp, dst + 1);
@@ -2077,7 +2081,7 @@
 		}
 		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-		dst = call(dst, (char *)m68k_write_long_highfirst);
+		dst = call(dst, opts->write_32_highfirst);
 		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
 			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
 		} else {
@@ -2100,7 +2104,7 @@
 		}
 		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-		dst = call(dst, (char *)m68k_write_long_highfirst);
+		dst = call(dst, opts->write_32_highfirst);
 		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
 			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
 		} else {
@@ -2124,7 +2128,7 @@
 		}
 		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-		dst = call(dst, (char *)m68k_write_long_highfirst);
+		dst = call(dst, opts->write_32_highfirst);
 		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
 			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
 		} else {
@@ -2182,7 +2186,7 @@
 		}
 		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-		dst = call(dst, (char *)m68k_write_long_highfirst);
+		dst = call(dst, opts->write_32_highfirst);
 		m68k_addr = inst->src.params.regs.displacement + inst->address + 2;
 		if ((m68k_addr & 0xFFFFFF) < 0x400000) {
 			dest_addr = get_native_address(opts->native_code_map, m68k_addr);
@@ -2218,7 +2222,7 @@
 		}
 		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-		dst = call(dst, (char *)m68k_write_long_highfirst);
+		dst = call(dst, opts->write_32_highfirst);
 		dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D);
 		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
 		if (inst->src.params.regs.sec & 1) {
@@ -2273,7 +2277,7 @@
 		}
 		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
-		dst = call(dst, (char *)m68k_write_long_highfirst);
+		dst = call(dst, opts->write_32_highfirst);
 		m68k_addr = inst->src.params.immed;
 		if ((m68k_addr & 0xFFFFFF) < 0x400000) {
 			dest_addr = get_native_address(opts->native_code_map, m68k_addr);
@@ -2314,7 +2318,7 @@
 	//TODO: Add cycles
 	dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
 	dst = add_ir(dst, 4, opts->aregs[7], SZ_D);
-	dst = call(dst, (char *)m68k_read_long_scratch1);
+	dst = call(dst, opts->read_32);
 	if (opts->flags & OPT_NATIVE_CALL_STACK) {
 		dst = cmp_rdisp8r(dst, RSP, 8, SCRATCH1, SZ_D);
 		dst = jcc(dst, CC_NZ, dst+3);
@@ -2420,7 +2424,7 @@
 	} else {
 		dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D);
 	}
-	dst = call(dst, (char *)m68k_write_long_highfirst);
+	dst = call(dst, opts->write_32_highfirst);
 	if (reg >= 0) {
 		dst = mov_rr(dst, opts->aregs[7], reg, SZ_D);
 	} else {
@@ -2451,7 +2455,7 @@
 				dst = mov_rr(dst, reg, SCRATCH1, SZ_D);
 				dst = shr_ir(dst, 24, SCRATCH1, SZ_D);
 				dst = push_r(dst, SCRATCH2);
-				dst = call(dst, (uint8_t *)m68k_write_byte);
+				dst = call(dst, opts->write_8);
 				dst = pop_r(dst, SCRATCH2);
 				dst = mov_rr(dst, reg, SCRATCH1, SZ_D);
 				dst = shr_ir(dst, 16, SCRATCH1, SZ_D);
@@ -2459,13 +2463,13 @@
 			} else {
 				dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+3, SCRATCH1, SZ_B);
 				dst = push_r(dst, SCRATCH2);
-				dst = call(dst, (uint8_t *)m68k_write_byte);
+				dst = call(dst, opts->write_8);
 				dst = pop_r(dst, SCRATCH2);
 				dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+2, SCRATCH1, SZ_B);
 			}
 			dst = add_ir(dst, 2, SCRATCH2, SZ_D);
 			dst = push_r(dst, SCRATCH2);
-			dst = call(dst, (uint8_t *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			dst = pop_r(dst, SCRATCH2);
 			dst = add_ir(dst, 2, SCRATCH2, SZ_D);
 		}
@@ -2473,18 +2477,18 @@
 			dst = mov_rr(dst, reg, SCRATCH1, SZ_W);
 			dst = shr_ir(dst, 8, SCRATCH1, SZ_W);
 			dst = push_r(dst, SCRATCH2);
-			dst = call(dst, (uint8_t *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			dst = pop_r(dst, SCRATCH2);
 			dst = mov_rr(dst, reg, SCRATCH1, SZ_W);
 		} else {
 			dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+1, SCRATCH1, SZ_B);
 			dst = push_r(dst, SCRATCH2);
-			dst = call(dst, (uint8_t *)m68k_write_byte);
+			dst = call(dst, opts->write_8);
 			dst = pop_r(dst, SCRATCH2);
 			dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_B);
 		}
 		dst = add_ir(dst, 2, SCRATCH2, SZ_D);
-		dst = call(dst, (uint8_t *)m68k_write_byte);
+		dst = call(dst, opts->write_8);
 	} else {
 		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
 			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
@@ -2498,43 +2502,43 @@
 		if (inst->extra.size == OPSIZE_LONG) {
 			if (reg >= 0) {
 				dst = push_r(dst, SCRATCH1);
-				dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+				dst = call(dst, opts->read_8);
 				dst = shl_ir(dst, 24, SCRATCH1, SZ_D);
 				dst = mov_rr(dst, SCRATCH1, reg, SZ_D);
 				dst = pop_r(dst, SCRATCH1);
 				dst = add_ir(dst, 2, SCRATCH1, SZ_D);
 				dst = push_r(dst, SCRATCH1);
-				dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+				dst = call(dst, opts->read_8);
 				dst = shl_ir(dst, 16, SCRATCH1, SZ_D);
 				dst = or_rr(dst, SCRATCH1, reg, SZ_D);
 			} else {
 				dst = push_r(dst, SCRATCH1);
-				dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+				dst = call(dst, opts->read_8);
 				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst))+3, SZ_B);
 				dst = pop_r(dst, SCRATCH1);
 				dst = add_ir(dst, 2, SCRATCH1, SZ_D);
 				dst = push_r(dst, SCRATCH1);
-				dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+				dst = call(dst, opts->read_8);
 				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst))+2, SZ_B);
 			}
 			dst = pop_r(dst, SCRATCH1);
 			dst = add_ir(dst, 2, SCRATCH1, SZ_D);
 		}
 		dst = push_r(dst, SCRATCH1);
-		dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+		dst = call(dst, opts->read_8);
 		if (reg >= 0) {
 			
 			dst = shl_ir(dst, 8, SCRATCH1, SZ_W);
 			dst = mov_rr(dst, SCRATCH1, reg, SZ_W);
 			dst = pop_r(dst, SCRATCH1);
 			dst = add_ir(dst, 2, SCRATCH1, SZ_D);
-			dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+			dst = call(dst, opts->read_8);
 			dst = mov_rr(dst, SCRATCH1, reg, SZ_B);
 		} else {
 			dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst))+1, SZ_B);
 			dst = pop_r(dst, SCRATCH1);
 			dst = add_ir(dst, 2, SCRATCH1, SZ_D);
-			dst = call(dst, (uint8_t *)m68k_read_byte_scratch1);
+			dst = call(dst, opts->read_8);
 			dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_B);
 		}
 	}
@@ -3711,12 +3715,12 @@
 		//TODO: Trap if not in system mode
 		//Read saved SR
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
-		dst = call(dst, (uint8_t *)m68k_read_word_scratch1);
+		dst = call(dst, opts->read_16);
 		dst = add_ir(dst, 2, opts->aregs[7], SZ_D);
 		dst = call(dst, (uint8_t *)set_sr);
 		//Read saved PC
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
-		dst = call(dst, (uint8_t *)m68k_read_long_scratch1);
+		dst = call(dst, opts->read_32);
 		dst = add_ir(dst, 4, opts->aregs[7], SZ_D);
 		//Check if we've switched to user mode and swap stack pointers if needed
 		dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B);
@@ -3733,12 +3737,12 @@
 	case M68K_RTR:
 		//Read saved CCR
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
-		dst = call(dst, (uint8_t *)m68k_read_word_scratch1);
+		dst = call(dst, opts->read_16);
 		dst = add_ir(dst, 2, opts->aregs[7], SZ_D);
 		dst = call(dst, (uint8_t *)set_ccr);
 		//Read saved PC
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
-		dst = call(dst, (uint8_t *)m68k_read_long_scratch1);
+		dst = call(dst, opts->read_32);
 		dst = add_ir(dst, 4, opts->aregs[7], SZ_D);
 		//Get native address and jump to it
 		dst = call(dst, (uint8_t *)m68k_native_addr);
@@ -3871,7 +3875,7 @@
 			dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, opts->aregs[7], SZ_D);
 		}
 		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
-		dst = call(dst, (uint8_t *)m68k_read_long_scratch1);
+		dst = call(dst, opts->read_32);
 		if (dst_op.mode == MODE_REG_DIRECT) {
 			dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_D);
 		} else {
@@ -4135,7 +4139,7 @@
 	start_68k_context(context, address);
 }
 
-void init_x86_68k_opts(x86_68k_options * opts)
+void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks)
 {
 	opts->flags = 0;
 	for (int i = 0; i < 8; i++)
@@ -4154,6 +4158,317 @@
 	opts->code_end = opts->cur_code + size;
 	opts->ram_inst_sizes = malloc(sizeof(uint8_t *) * 64);
 	memset(opts->ram_inst_sizes, 0, sizeof(uint8_t *) * 64);
+	uint8_t * dst = opts->read_16 = opts->cur_code;
+	dst = check_cycles(dst);
+	dst = cycles(dst, BUS);
+	dst = and_ir(dst, 0xFFFFFF, SCRATCH1, SZ_D);
+	uint8_t *lb_jcc = NULL, *ub_jcc = NULL;
+	for (uint32_t chunk = 0; chunk < num_chunks; chunk++)
+	{
+		if (lb_jcc) {
+			*lb_jcc = dst - (lb_jcc+1);
+			lb_jcc = NULL;
+		}
+		if (ub_jcc) {
+			*ub_jcc = dst - (ub_jcc+1);
+			ub_jcc = NULL;
+		}
+		if (memmap[chunk].start > 0) {
+			dst = cmp_ir(dst, memmap[chunk].start, SCRATCH1, SZ_D);
+			lb_jcc = dst + 1;
+			dst = jcc(dst, CC_C, dst+2);
+		}
+		if (memmap[chunk].end < 0x1000000) {
+			dst = cmp_ir(dst, memmap[chunk].end, SCRATCH1, SZ_D);
+			ub_jcc = dst + 1;
+			dst = jcc(dst, CC_NC, dst+2);
+		}
+		
+		if (memmap[chunk].mask != 0xFFFFFF) {
+			dst = and_ir(dst, memmap[chunk].mask, SCRATCH1, SZ_D);
+		}
+		
+		if (memmap[chunk].read_16) {
+			dst = call(dst, (uint8_t *)m68k_save_context);
+			dst = push_r(dst, CONTEXT);
+			dst = mov_rr(dst, SCRATCH1, RDI, SZ_D);
+			dst = call(dst, (uint8_t *)memmap[chunk].read_16);
+			dst = pop_r(dst, CONTEXT);
+			dst = mov_rr(dst, RAX, SCRATCH1, SZ_W);
+			dst = jmp(dst, (uint8_t *)m68k_load_context);
+		} else if(memmap[chunk].buffer && memmap[chunk].flags & MMAP_READ) {
+			if ((int64_t)memmap[chunk].buffer <= 0x7FFFFFFF && (int64_t)memmap[chunk].buffer >= -2147483648) {
+				dst = mov_rdisp32r(dst, SCRATCH1, (int64_t)memmap[chunk].buffer, SCRATCH1, SZ_W);
+			} else {
+				dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH2, SZ_Q);
+				dst = mov_rindexr(dst, SCRATCH2, SCRATCH1, 1, SCRATCH1, SZ_W);
+			}
+			dst = retn(dst);
+		} else {
+			//Not sure the best course of action here
+			dst = mov_ir(dst, 0xFFFF, SCRATCH1, SZ_W);
+			dst = retn(dst);
+		}
+	}
+	if (lb_jcc) {
+		*lb_jcc = dst - (lb_jcc+1);
+		lb_jcc = NULL;
+	}
+	if (ub_jcc) {
+		*ub_jcc = dst - (ub_jcc+1);
+		ub_jcc = NULL;
+	}
+	dst = mov_ir(dst, 0xFFFF, SCRATCH1, SZ_W);
+	dst = retn(dst);
+	
+	opts->write_16 = dst;
+	dst = check_cycles(dst);
+	dst = cycles(dst, BUS);
+	dst = and_ir(dst, 0xFFFFFF, SCRATCH2, SZ_D);
+	for (uint32_t chunk = 0; chunk < num_chunks; chunk++)
+	{
+		if (lb_jcc) {
+			*lb_jcc = dst - (lb_jcc+1);
+			lb_jcc = NULL;
+		}
+		if (ub_jcc) {
+			*ub_jcc = dst - (ub_jcc+1);
+			ub_jcc = NULL;
+		}
+		if (memmap[chunk].start > 0) {
+			dst = cmp_ir(dst, memmap[chunk].start, SCRATCH2, SZ_D);
+			lb_jcc = dst + 1;
+			dst = jcc(dst, CC_C, dst+2);
+		}
+		if (memmap[chunk].end < 0x1000000) {
+			dst = cmp_ir(dst, memmap[chunk].end, SCRATCH2, SZ_D);
+			ub_jcc = dst + 1;
+			dst = jcc(dst, CC_NC, dst+2);
+		}
+		
+		if (memmap[chunk].mask != 0xFFFFFF) {
+			dst = and_ir(dst, memmap[chunk].mask, SCRATCH2, SZ_D);
+		}
+		
+		if (memmap[chunk].write_16) {
+			dst = call(dst, (uint8_t *)m68k_save_context);
+			//SCRATCH2 is RDI, so no need to move it there
+			dst = mov_rr(dst, SCRATCH1, RDX, SZ_W);
+			dst = call(dst, (uint8_t *)memmap[chunk].write_16);
+			dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+			dst = jmp(dst, (uint8_t *)m68k_load_context);
+		} else if(memmap[chunk].buffer && memmap[chunk].flags & MMAP_WRITE) {
+			if ((int64_t)memmap[chunk].buffer <= 0x7FFFFFFF && (int64_t)memmap[chunk].buffer >= -2147483648) {
+				dst = mov_rrdisp32(dst, SCRATCH1, SCRATCH2, (int64_t)memmap[chunk].buffer, SZ_W);
+			} else {
+				dst = push_r(dst, SCRATCH1);
+				dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH1, SZ_Q);
+				dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_Q);
+				dst = pop_r(dst, SCRATCH1);
+				dst = mov_rrind(dst, SCRATCH1, SCRATCH2, SZ_W);
+			}
+			if (memmap[chunk].flags & MMAP_CODE) {
+				dst = mov_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
+				dst = shr_ir(dst, 11, SCRATCH1, SZ_D);
+				dst = bt_rrdisp32(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, ram_code_flags), SZ_D);
+				uint8_t * not_code = dst+1;
+				dst = jcc(dst, CC_NC, dst+2);
+				dst = call(dst, (uint8_t *)m68k_save_context);
+				dst = call(dst, (uint8_t *)m68k_handle_code_write);
+				dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+				dst = call(dst, (uint8_t *)m68k_load_context);
+				*not_code = dst - (not_code+1);
+			}
+			dst = retn(dst);
+		} else {
+			//Not sure the best course of action here
+			dst = retn(dst);
+		}
+	}
+	if (lb_jcc) {
+		*lb_jcc = dst - (lb_jcc+1);
+		lb_jcc = NULL;
+	}
+	if (ub_jcc) {
+		*ub_jcc = dst - (ub_jcc+1);
+		ub_jcc = NULL;
+	}
+	dst = retn(dst);
+	
+	opts->read_8 = dst;
+	dst = check_cycles(dst);
+	dst = cycles(dst, BUS);
+	dst = and_ir(dst, 0xFFFFFF, SCRATCH1, SZ_D);
+	for (uint32_t chunk = 0; chunk < num_chunks; chunk++)
+	{
+		if (lb_jcc) {
+			*lb_jcc = dst - (lb_jcc+1);
+			lb_jcc = NULL;
+		}
+		if (ub_jcc) {
+			*ub_jcc = dst - (ub_jcc+1);
+			ub_jcc = NULL;
+		}
+		if (memmap[chunk].start > 0) {
+			dst = cmp_ir(dst, memmap[chunk].start, SCRATCH1, SZ_D);
+			lb_jcc = dst + 1;
+			dst = jcc(dst, CC_C, dst+2);
+		}
+		if (memmap[chunk].end < 0x1000000) {
+			dst = cmp_ir(dst, memmap[chunk].end, SCRATCH1, SZ_D);
+			ub_jcc = dst + 1;
+			dst = jcc(dst, CC_NC, dst+2);
+		}
+		
+		if (memmap[chunk].mask != 0xFFFFFF) {
+			dst = and_ir(dst, memmap[chunk].mask, SCRATCH1, SZ_D);
+		}
+		
+		if (memmap[chunk].read_8) {
+			dst = call(dst, (uint8_t *)m68k_save_context);
+			dst = push_r(dst, CONTEXT);
+			dst = mov_rr(dst, SCRATCH1, RDI, SZ_D);
+			dst = call(dst, (uint8_t *)memmap[chunk].read_8);
+			dst = pop_r(dst, CONTEXT);
+			dst = mov_rr(dst, RAX, SCRATCH1, SZ_B);
+			dst = jmp(dst, (uint8_t *)m68k_load_context);
+		} else if(memmap[chunk].buffer && memmap[chunk].flags & MMAP_READ) {
+			dst = xor_ir(dst, 1, SCRATCH1, SZ_D);
+			if ((int64_t)memmap[chunk].buffer <= 0x7FFFFFFF && (int64_t)memmap[chunk].buffer >= -2147483648) {
+				dst = mov_rdisp32r(dst, SCRATCH1, (int64_t)memmap[chunk].buffer, SCRATCH1, SZ_B);
+			} else {
+				dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH2, SZ_Q);
+				dst = mov_rindexr(dst, SCRATCH2, SCRATCH1, 1, SCRATCH1, SZ_B);
+			}
+			dst = retn(dst);
+		} else {
+			//Not sure the best course of action here
+			dst = mov_ir(dst, 0xFF, SCRATCH1, SZ_B);
+			dst = retn(dst);
+		}
+	}
+	if (lb_jcc) {
+		*lb_jcc = dst - (lb_jcc+1);
+		lb_jcc = NULL;
+	}
+	if (ub_jcc) {
+		*ub_jcc = dst - (ub_jcc+1);
+		ub_jcc = NULL;
+	}
+	dst = mov_ir(dst, 0xFFFF, SCRATCH1, SZ_W);
+	dst = retn(dst);
+	
+	opts->write_8 = dst;
+	dst = check_cycles(dst);
+	dst = cycles(dst, BUS);
+	dst = and_ir(dst, 0xFFFFFF, SCRATCH2, SZ_D);
+	for (uint32_t chunk = 0; chunk < num_chunks; chunk++)
+	{
+		if (lb_jcc) {
+			*lb_jcc = dst - (lb_jcc+1);
+			lb_jcc = NULL;
+		}
+		if (ub_jcc) {
+			*ub_jcc = dst - (ub_jcc+1);
+			ub_jcc = NULL;
+		}
+		if (memmap[chunk].start > 0) {
+			dst = cmp_ir(dst, memmap[chunk].start, SCRATCH2, SZ_D);
+			lb_jcc = dst + 1;
+			dst = jcc(dst, CC_C, dst+2);
+		}
+		if (memmap[chunk].end < 0x1000000) {
+			dst = cmp_ir(dst, memmap[chunk].end, SCRATCH2, SZ_D);
+			ub_jcc = dst + 1;
+			dst = jcc(dst, CC_NC, dst+2);
+		}
+		
+		if (memmap[chunk].mask != 0xFFFFFF) {
+			dst = and_ir(dst, memmap[chunk].mask, SCRATCH2, SZ_D);
+		}
+		
+		if (memmap[chunk].write_8) {
+			dst = call(dst, (uint8_t *)m68k_save_context);
+			//SCRATCH2 is RDI, so no need to move it there
+			dst = mov_rr(dst, SCRATCH1, RDX, SZ_B);
+			dst = call(dst, (uint8_t *)memmap[chunk].write_8);
+			dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+			dst = jmp(dst, (uint8_t *)m68k_load_context);
+		} else if(memmap[chunk].buffer && memmap[chunk].flags & MMAP_WRITE) {
+			dst = xor_ir(dst, 1, SCRATCH2, SZ_D);
+			if ((int64_t)memmap[chunk].buffer <= 0x7FFFFFFF && (int64_t)memmap[chunk].buffer >= -2147483648) {
+				dst = mov_rrdisp32(dst, SCRATCH1, SCRATCH2, (int64_t)memmap[chunk].buffer, SZ_B);
+			} else {
+				dst = push_r(dst, SCRATCH1);
+				dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH1, SZ_Q);
+				dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_Q);
+				dst = pop_r(dst, SCRATCH1);
+				dst = mov_rrind(dst, SCRATCH1, SCRATCH2, SZ_B);
+			}
+			if (memmap[chunk].flags & MMAP_CODE) {
+				dst = mov_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
+				dst = shr_ir(dst, 11, SCRATCH1, SZ_D);
+				dst = bt_rrdisp32(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, ram_code_flags), SZ_D);
+				uint8_t * not_code = dst+1;
+				dst = jcc(dst, CC_NC, dst+2);
+				dst = xor_ir(dst, 1, SCRATCH2, SZ_D);
+				dst = call(dst, (uint8_t *)m68k_save_context);
+				dst = call(dst, (uint8_t *)m68k_handle_code_write);
+				dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+				dst = call(dst, (uint8_t *)m68k_load_context);
+				*not_code = dst - (not_code+1);
+			}
+			dst = retn(dst);
+		} else {
+			//Not sure the best course of action here
+			dst = retn(dst);
+		}
+	}
+	if (lb_jcc) {
+		*lb_jcc = dst - (lb_jcc+1);
+		lb_jcc = NULL;
+	}
+	if (ub_jcc) {
+		*ub_jcc = dst - (ub_jcc+1);
+		ub_jcc = NULL;
+	}
+	dst = retn(dst);
+	
+	opts->read_32 = dst;
+	dst = push_r(dst, SCRATCH1);
+	dst = call(dst, opts->read_16);
+	dst = mov_rr(dst, SCRATCH1, SCRATCH2, SZ_W);
+	dst = pop_r(dst, SCRATCH1);
+	dst = push_r(dst, SCRATCH2);
+	dst = add_ir(dst, 2, SCRATCH1, SZ_D);
+	dst = call(dst, opts->read_16);
+	dst = pop_r(dst, SCRATCH2);
+	dst = movzx_rr(dst, SCRATCH1, SCRATCH1, SZ_W, SZ_D);
+	dst = shl_ir(dst, 16, SCRATCH2, SZ_D);
+	dst = or_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
+	dst = retn(dst);
+	
+	opts->write_32_lowfirst = dst;
+	dst = push_r(dst, SCRATCH2);
+	dst = push_r(dst, SCRATCH1);
+	dst = add_ir(dst, 2, SCRATCH2, SZ_D);
+	dst = call(dst, opts->write_16);
+	dst = pop_r(dst, SCRATCH1);
+	dst = pop_r(dst, SCRATCH2);
+	dst = shr_ir(dst, 16, SCRATCH1, SZ_D);
+	dst = jmp(dst, opts->write_16);
+	
+	opts->write_32_highfirst = dst;
+	dst = push_r(dst, SCRATCH1);
+	dst = push_r(dst, SCRATCH2);
+	dst = shr_ir(dst, 16, SCRATCH1, SZ_D);
+	dst = call(dst, opts->write_16);
+	dst = pop_r(dst, SCRATCH2);
+	dst = pop_r(dst, SCRATCH1);
+	dst = add_ir(dst, 2, SCRATCH2, SZ_D);
+	dst = jmp(dst, opts->write_16);
+	
+	opts->cur_code = dst;
 }
 
 void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts)
--- a/m68k_to_x86.h	Thu May 16 09:37:53 2013 -0700
+++ b/m68k_to_x86.h	Sat May 18 11:44:42 2013 -0700
@@ -23,6 +23,13 @@
 	uint8_t         *code_end;
 	uint8_t         **ram_inst_sizes;
 	FILE            *address_log;
+	uint8_t         *read_16;
+	uint8_t         *write_16;
+	uint8_t         *read_8;
+	uint8_t         *write_8;
+	uint8_t         *read_32;
+	uint8_t         *write_32_lowfirst;
+	uint8_t         *write_32_highfirst;
 } x86_68k_options;
 
 typedef struct {
@@ -38,7 +45,7 @@
 	uint32_t        int_num;
 	uint16_t        *mem_pointers[NUM_MEM_AREAS];
 	void            *video_context;
-	uint16_t        value;
+	uint16_t        reserved;
 	
 	native_map_slot *native_code_map;
 	void            *options;
@@ -49,7 +56,7 @@
 uint8_t * translate_m68k(uint8_t * dst, struct m68kinst * inst, x86_68k_options * opts);
 uint8_t * translate_m68k_stream(uint32_t address, m68k_context * context);
 void start_68k_context(m68k_context * context, uint32_t address);
-void init_x86_68k_opts(x86_68k_options * opts);
+void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks);
 void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts);
 void m68k_reset(m68k_context * context);
 void insert_breakpoint(m68k_context * context, uint32_t address, uint8_t * bp_handler);
--- a/runtime.S	Thu May 16 09:37:53 2013 -0700
+++ b/runtime.S	Sat May 18 11:44:42 2013 -0700
@@ -5,12 +5,16 @@
 	cmp 84(%rsi), %eax
 	jb skip_sync
 do_sync:
+	push %rcx
+	push %rdi
 	call m68k_save_context
 	mov %rsi, %rdi
 	xor %esi, %esi
 	call sync_components
 	mov %rax, %rsi
 	call m68k_load_context
+	pop %rdi
+	pop %rcx
 skip_sync:
 	ret
 
@@ -271,10 +275,11 @@
 do_vdp_port_read:
 	mov %ecx, %edi
 	call m68k_save_context
+	push %rsi
 	call vdp_port_read
-	mov %rax, %rsi
+	pop %rsi
+	mov %ax, %cx
 	call m68k_load_context
-	mov 136(%rsi), %cx
 	ret
 	
 do_io_write:
@@ -289,10 +294,11 @@
 	mov %ecx, %edi
 	and $0x1FFFF, %edi
 	call m68k_save_context
+	push %rsi
 	call io_read
-	mov %rax, %rsi
+	pop %rsi
+	mov %al, %cl
 	call m68k_load_context
-	mov 136(%rsi), %cl
 	ret
 
 do_io_write_w:
@@ -307,10 +313,11 @@
 	mov %ecx, %edi
 	and $0x1FFFF, %edi
 	call m68k_save_context
+	push %rsi
 	call io_read_w
-	mov %rax, %rsi
+	pop %rsi
+	mov %ax, %cx
 	call m68k_load_context
-	mov 136(%rsi), %cx
 	ret
 	
 bad_access_msg:
@@ -479,8 +486,7 @@
 	add $4, %eax
 	ret
 do_limit:
-	push %rcx
-	push %rdi
+	
 	call handle_cycle_limit
 	pop %rdi
 	pop %rcx
--- a/x86_backend.h	Thu May 16 09:37:53 2013 -0700
+++ b/x86_backend.h	Sat May 18 11:44:42 2013 -0700
@@ -25,6 +25,30 @@
 	uint32_t             address;
 } deferred_addr;
 
+
+#define MMAP_READ    0x1
+#define MMAP_WRITE   0x2
+#define MMAP_CODE    0x4
+#define MMAP_PTR_IDX 0x8
+
+typedef uint16_t (*read_16_fun)(uint32_t address, void * context);
+typedef uint8_t (*read_8_fun)(uint32_t address, void * context);
+typedef void * (*write_16_fun)(uint32_t address, void * context, uint16_t value);
+typedef void * (*write_8_fun)(uint32_t address, void * context, uint8_t value);
+
+typedef struct {
+	uint32_t     start;
+	uint32_t     end;
+	uint32_t     mask;
+	uint16_t     ptr_index;
+	uint16_t     flags;
+	void *       buffer;
+	read_16_fun  read_16;
+	write_16_fun write_16;
+	read_8_fun   read_8;
+	write_8_fun  write_8;
+} memmap_chunk;
+
 typedef uint8_t * (*native_addr_func)(void * context, uint32_t address);
 
 deferred_addr * defer_address(deferred_addr * old_head, uint32_t address, uint8_t *dest);