changeset 1044:1625555e346e

Properly handle redundant prefixes
author Michael Pavone <pavone@retrodev.com>
date Mon, 25 Jul 2016 23:16:04 -0700
parents 3980ef0f6307
children e0489abfdab0
files z80_to_x86.c z80inst.c z80inst.h
diffstat 3 files changed, 559 insertions(+), 537 deletions(-) [+]
line wrap: on
line diff
--- a/z80_to_x86.c	Sun Jul 24 18:53:22 2016 -0700
+++ b/z80_to_x86.c	Mon Jul 25 23:16:04 2016 -0700
@@ -343,6 +343,8 @@
 		if (context->breakpoint_flags[address / 8] & (1 << (address % 8))) {
 			zbreakpoint_patch(context, address, start);
 		}
+		num_cycles = 4 * inst->opcode_bytes;
+		//TODO: increment R register once for every opcode byte
 #ifdef Z80_LOG_ADDRESS
 		log_address(&opts->gen, address, "Z80: %X @ %d\n");
 #endif
@@ -355,28 +357,24 @@
 		{
 		case Z80_REG:
 		case Z80_REG_INDIRECT:
- 			num_cycles = size == SZ_B ? 4 : 6;
-			if (inst->ea_reg == Z80_IX || inst->ea_reg == Z80_IY || (inst->ea_reg >= Z80_IXL && inst->ea_reg <= Z80_IYH)) {
-				num_cycles += 4;
+			if (size != SZ_B) {
+				num_cycles += 2;
 			}
 			if (inst->reg == Z80_I || inst->ea_reg == Z80_I || inst->reg == Z80_R || inst->ea_reg == Z80_R) {
-				num_cycles += 5;
+				num_cycles += 1;
 			}
 			break;
 		case Z80_IMMED:
-			num_cycles = size == SZ_B ? 7 : 10;
+			num_cycles += size == SZ_B ? 3 : 6;
 			break;
 		case Z80_IMMED_INDIRECT:
-			num_cycles = 10;
+			num_cycles += 6;
 			break;
 		case Z80_IX_DISPLACE:
 		case Z80_IY_DISPLACE:
-			num_cycles = 16;
+			num_cycles = 8; //3 for displacement, 5 for address addition
 			break;
 		}
-		if ((inst->reg >= Z80_IXL && inst->reg <= Z80_IYH) || inst->reg == Z80_IX || inst->reg == Z80_IY) {
-			num_cycles += 4;
-		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode & Z80_DIR) {
 			translate_z80_ea(inst, &dst_op, opts, DONT_READ, MODIFY);
@@ -422,7 +420,7 @@
 		}
 		break;
 	case Z80_PUSH:
-		cycles(&opts->gen, (inst->reg == Z80_IX || inst->reg == Z80_IY) ? 9 : 5);
+		cycles(&opts->gen, num_cycles + 1);
 		sub_ir(code, 2, opts->regs[Z80_SP], SZ_W);
 		if (inst->reg == Z80_AF) {
 			zreg_to_native(opts, Z80_A, opts->gen.scratch1);
@@ -447,7 +445,7 @@
 		//the upper half of a register pair
 		break;
 	case Z80_POP:
-		cycles(&opts->gen, (inst->reg == Z80_IX || inst->reg == Z80_IY) ? 8 : 4);
+		cycles(&opts->gen, num_cycles);
 		mov_rr(code, opts->regs[Z80_SP], opts->gen.scratch1, SZ_W);
 		call(code, opts->read_16);
 		add_ir(code, 2, opts->regs[Z80_SP], SZ_W);
@@ -474,11 +472,6 @@
 		//the upper half of a register pair
 		break;
 	case Z80_EX:
-		if (inst->addr_mode == Z80_REG || inst->reg == Z80_HL) {
-			num_cycles = 4;
-		} else {
-			num_cycles = 8;
-		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode == Z80_REG) {
 			if(inst->reg == Z80_AF) {
@@ -543,7 +536,7 @@
 		}
 		break;
 	case Z80_EXX:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_BC, opts->gen.scratch1);
 		mov_rdispr(code, opts->gen.context_reg, zar_off(Z80_BC), opts->gen.scratch2, SZ_W);
 		mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, zar_off(Z80_BC), SZ_W);
@@ -560,7 +553,7 @@
 		native_to_zreg(opts, opts->gen.scratch2, Z80_DE);
 		break;
 	case Z80_LDI: {
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);
 		zreg_to_native(opts, Z80_DE, opts->gen.scratch2);
@@ -587,7 +580,7 @@
 		break;
 	}
 	case Z80_LDIR: {
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);
 		zreg_to_native(opts, Z80_DE, opts->gen.scratch2);
@@ -620,7 +613,7 @@
 		break;
 	}
 	case Z80_LDD: {
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);
 		zreg_to_native(opts, Z80_DE, opts->gen.scratch2);
@@ -647,7 +640,7 @@
 		break;
 	}
 	case Z80_LDDR: {
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);
 		zreg_to_native(opts, Z80_DE, opts->gen.scratch2);
@@ -680,7 +673,7 @@
 		break;
 	}
 	case Z80_CPI:
-		cycles(&opts->gen, 8);//T-States 4,4
+		cycles(&opts->gen, num_cycles);//T-States 4,4
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T-States 3
 		cmp_rr(code, opts->gen.scratch1, opts->regs[Z80_A], SZ_B);
@@ -702,7 +695,7 @@
 		setcc_rdisp(code, CC_NZ, opts->gen.context_reg, zf_off(ZF_PV));
 		break;
 	case Z80_CPIR: {
-		cycles(&opts->gen, 8);//T-States 4,4
+		cycles(&opts->gen, num_cycles);//T-States 4,4
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T-States 3
 		cmp_rr(code, opts->gen.scratch1, opts->regs[Z80_A], SZ_B);
@@ -735,7 +728,7 @@
 		break;
 	}
 	case Z80_CPD:
-		cycles(&opts->gen, 8);//T-States 4,4
+		cycles(&opts->gen, num_cycles);//T-States 4,4
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T-States 3
 		cmp_rr(code, opts->gen.scratch1, opts->regs[Z80_A], SZ_B);
@@ -757,7 +750,7 @@
 		setcc_rdisp(code, CC_NZ, opts->gen.context_reg, zf_off(ZF_PV));
 		break;
 	case Z80_CPDR: {
-		cycles(&opts->gen, 8);//T-States 4,4
+		cycles(&opts->gen, num_cycles);//T-States 4,4
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T-States 3
 		cmp_rr(code, opts->gen.scratch1, opts->regs[Z80_A], SZ_B);
@@ -790,9 +783,8 @@
 		break;
 	}
 	case Z80_ADD:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		} else if(z80_size(inst) == SZ_W) {
@@ -831,9 +823,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_ADC:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		} else if(z80_size(inst) == SZ_W) {
@@ -871,9 +862,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_SUB:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		}
@@ -908,9 +898,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_SBC:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		} else if(z80_size(inst) == SZ_W) {
@@ -948,9 +937,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_AND:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		} else if(z80_size(inst) == SZ_W) {
@@ -976,9 +964,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_OR:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		} else if(z80_size(inst) == SZ_W) {
@@ -1004,9 +991,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_XOR:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		} else if(z80_size(inst) == SZ_W) {
@@ -1032,9 +1018,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_CP:
-		num_cycles = 4;
 		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 12;
+			num_cycles += 8;
 		} else if(inst->addr_mode == Z80_IMMED) {
 			num_cycles += 3;
 		}
@@ -1058,13 +1043,8 @@
 		z80_save_ea(code, inst, opts);
 		break;
 	case Z80_INC:
-		num_cycles = 4;
-		if (inst->reg == Z80_IX || inst->reg == Z80_IY) {
-			num_cycles += 6;
-		} else if(z80_size(inst) == SZ_W) {
+		if(z80_size(inst) == SZ_W) {
 			num_cycles += 2;
-		} else if(inst->reg == Z80_IXH || inst->reg == Z80_IXL || inst->reg == Z80_IYH || inst->reg == Z80_IYL || inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 4;
 		}
 		cycles(&opts->gen, num_cycles);
 		translate_z80_reg(inst, &dst_op, opts);
@@ -1088,13 +1068,8 @@
 		z80_save_result(opts, inst);
 		break;
 	case Z80_DEC:
-		num_cycles = 4;
-		if (inst->reg == Z80_IX || inst->reg == Z80_IY) {
-			num_cycles += 6;
-		} else if(z80_size(inst) == SZ_W) {
+		if(z80_size(inst) == SZ_W) {
 			num_cycles += 2;
-		} else if(inst->reg == Z80_IXH || inst->reg == Z80_IXL || inst->reg == Z80_IYH || inst->reg == Z80_IYL || inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
-			num_cycles += 4;
 		}
 		cycles(&opts->gen, num_cycles);
 		translate_z80_reg(inst, &dst_op, opts);
@@ -1119,7 +1094,7 @@
 		z80_save_result(opts, inst);
 		break;
 	case Z80_DAA:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		xor_rr(code, opts->gen.scratch2, opts->gen.scratch2, SZ_B);
 		cmp_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_H), SZ_B);
 		code_ptr corf_low = code->cur+1;
@@ -1166,13 +1141,13 @@
 		//TODO: Implement half-carry flag
 		break;
 	case Z80_CPL:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		not_r(code, opts->regs[Z80_A], SZ_B);
 		mov_irdisp(code, 1, opts->gen.context_reg, zf_off(ZF_H), SZ_B);
 		mov_irdisp(code, 1, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		break;
 	case Z80_NEG:
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		neg_r(code, opts->regs[Z80_A], SZ_B);
 		//TODO: Implement half-carry flag
 		setcc_rdisp(code, CC_Z, opts->gen.context_reg, zf_off(ZF_Z));
@@ -1182,43 +1157,38 @@
 		mov_irdisp(code, 1, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		break;
 	case Z80_CCF:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		mov_rdispr(code, opts->gen.context_reg, zf_off(ZF_C), opts->gen.scratch1, SZ_B);
 		xor_irdisp(code, 1, opts->gen.context_reg, zf_off(ZF_C), SZ_B);
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, zf_off(ZF_H), SZ_B);
 		break;
 	case Z80_SCF:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		mov_irdisp(code, 1, opts->gen.context_reg, zf_off(ZF_C), SZ_B);
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_N), SZ_B);
 		mov_irdisp(code, 0, opts->gen.context_reg, zf_off(ZF_H), SZ_B);
 		break;
 	case Z80_NOP:
-		if (inst->immed == 42) {
-			call(code, opts->gen.save_context);
-			call_args(code, (code_ptr)z80_print_regs_exit, 1, opts->gen.context_reg);
-		} else {
-			cycles(&opts->gen, 4 * inst->immed);
-		}
+		cycles(&opts->gen, num_cycles);
 		break;
 	case Z80_HALT: {
 		code_ptr loop_top = code->cur;
 		//this isn't terribly efficient, but it's good enough for now
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		check_cycles_int(&opts->gen, address);
 		jmp(code, loop_top);
 		break;
 	}
 	case Z80_DI:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		mov_irdisp(code, 0, opts->gen.context_reg, offsetof(z80_context, iff1), SZ_B);
 		mov_irdisp(code, 0, opts->gen.context_reg, offsetof(z80_context, iff2), SZ_B);
 		mov_rdispr(code, opts->gen.context_reg, offsetof(z80_context, sync_cycle), opts->gen.limit, SZ_D);
 		mov_irdisp(code, 0xFFFFFFFF, opts->gen.context_reg, offsetof(z80_context, int_cycle), SZ_D);
 		break;
 	case Z80_EI:
-		cycles(&opts->gen, 4);
+		cycles(&opts->gen, num_cycles);
 		mov_rrdisp(code, opts->gen.cycles, opts->gen.context_reg, offsetof(z80_context, int_enable_cycle), SZ_D);
 		mov_irdisp(code, 1, opts->gen.context_reg, offsetof(z80_context, iff1), SZ_B);
 		mov_irdisp(code, 1, opts->gen.context_reg, offsetof(z80_context, iff2), SZ_B);
@@ -1227,11 +1197,13 @@
 		call(code, opts->do_sync);
 		break;
 	case Z80_IM:
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		mov_irdisp(code, inst->immed, opts->gen.context_reg, offsetof(z80_context, im), SZ_B);
 		break;
 	case Z80_RLC:
-		num_cycles = inst->immed == 0 ? 4 : (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8);
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1275,7 +1247,9 @@
 		}
 		break;
 	case Z80_RL:
-		num_cycles = inst->immed == 0 ? 4 : (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8);
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1320,7 +1294,9 @@
 		}
 		break;
 	case Z80_RRC:
-		num_cycles = inst->immed == 0 ? 4 : (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8);
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1364,7 +1340,9 @@
 		}
 		break;
 	case Z80_RR:
-		num_cycles = inst->immed == 0 ? 4 : (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8);
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1410,7 +1388,9 @@
 		break;
 	case Z80_SLA:
 	case Z80_SLL:
-		num_cycles = inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8;
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1458,7 +1438,9 @@
 		}
 		break;
 	case Z80_SRA:
-		num_cycles = inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8;
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1499,7 +1481,9 @@
 		}
 		break;
 	case Z80_SRL:
-		num_cycles = inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE ? 16 : 8;
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_UNUSED) {
 			translate_z80_ea(inst, &dst_op, opts, READ, MODIFY);
@@ -1540,7 +1524,7 @@
 		}
 		break;
 	case Z80_RLD:
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);
 		//Before: (HL) = 0x12, A = 0x34
@@ -1567,7 +1551,7 @@
 		call(code, opts->write_8);
 		break;
 	case Z80_RRD:
-		cycles(&opts->gen, 8);
+		cycles(&opts->gen, num_cycles);
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);
 		//Before: (HL) = 0x12, A = 0x34
@@ -1598,7 +1582,9 @@
 		call(code, opts->write_8);
 		break;
 	case Z80_BIT: {
-		num_cycles = (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) ? 8 : 16;
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		uint8_t bit;
 		if ((inst->addr_mode & 0x1F) == Z80_REG && opts->regs[inst->ea_reg] >= AH && opts->regs[inst->ea_reg] <= BH) {
@@ -1637,7 +1623,9 @@
 		break;
 	}
 	case Z80_SET: {
-		num_cycles = (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) ? 8 : 16;
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		uint8_t bit;
 		if ((inst->addr_mode & 0x1F) == Z80_REG && opts->regs[inst->ea_reg] >= AH && opts->regs[inst->ea_reg] <= BH) {
@@ -1704,7 +1692,9 @@
 		break;
 	}
 	case Z80_RES: {
-		num_cycles = (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) ? 8 : 16;
+		if (inst->addr_mode == Z80_IX_DISPLACE || inst->addr_mode == Z80_IY_DISPLACE) {
+			num_cycles += 8;
+		}
 		cycles(&opts->gen, num_cycles);
 		uint8_t bit;
 		if ((inst->addr_mode & 0x1F) == Z80_REG && opts->regs[inst->ea_reg] >= AH && opts->regs[inst->ea_reg] <= BH) {
@@ -1771,11 +1761,8 @@
 		break;
 	}
 	case Z80_JP: {
-		num_cycles = 4;
 		if (inst->addr_mode != Z80_REG_INDIRECT) {
 			num_cycles += 6;
-		} else if(inst->ea_reg == Z80_IX || inst->ea_reg == Z80_IY) {
-			num_cycles += 4;
 		}
 		cycles(&opts->gen, num_cycles);
 		if (inst->addr_mode != Z80_REG_INDIRECT) {
@@ -1798,7 +1785,7 @@
 		break;
 	}
 	case Z80_JPCC: {
-		cycles(&opts->gen, 7);//T States: 4,3
+		cycles(&opts->gen, num_cycles + 3);//T States: 4,3
 		uint8_t cond = CC_Z;
 		switch (inst->reg)
 		{
@@ -1838,7 +1825,7 @@
 		break;
 	}
 	case Z80_JR: {
-		cycles(&opts->gen, 12);//T States: 4,3,5
+		cycles(&opts->gen, num_cycles + 8);//T States: 4,3,5
 		uint16_t dest_addr = address + inst->immed + 2;
 		code_ptr call_dst = z80_get_native_address(context, dest_addr);
 			if (!call_dst) {
@@ -1850,7 +1837,7 @@
 		break;
 	}
 	case Z80_JRCC: {
-		cycles(&opts->gen, 7);//T States: 4,3
+		cycles(&opts->gen, num_cycles + 3);//T States: 4,3
 		uint8_t cond = CC_Z;
 		switch (inst->reg)
 		{
@@ -1880,7 +1867,7 @@
 		break;
 	}
 	case Z80_DJNZ: {
-		cycles(&opts->gen, 8);//T States: 5,3
+		cycles(&opts->gen, num_cycles + 4);//T States: 5,3
 		if (opts->regs[Z80_B] >= 0) {
 			sub_ir(code, 1, opts->regs[Z80_B], SZ_B);
 		} else {
@@ -1901,7 +1888,7 @@
 		break;
 		}
 	case Z80_CALL: {
-		cycles(&opts->gen, 11);//T States: 4,3,4
+		cycles(&opts->gen, num_cycles + 7);//T States: 4,3,4
 		sub_ir(code, 2, opts->regs[Z80_SP], SZ_W);
 		mov_ir(code, address + 3, opts->gen.scratch1, SZ_W);
 		mov_rr(code, opts->regs[Z80_SP], opts->gen.scratch2, SZ_W);
@@ -1916,7 +1903,7 @@
 		break;
 	}
 	case Z80_CALLCC: {
-		cycles(&opts->gen, 10);//T States: 4,3,3 (false case)
+		cycles(&opts->gen, num_cycles + 6);//T States: 4,3,3 (false case)
 		uint8_t cond = CC_Z;
 		switch (inst->reg)
 		{
@@ -1959,7 +1946,7 @@
 		break;
 		}
 	case Z80_RET:
-		cycles(&opts->gen, 4);//T States: 4
+		cycles(&opts->gen, num_cycles);//T States: 4
 		mov_rr(code, opts->regs[Z80_SP], opts->gen.scratch1, SZ_W);
 		call(code, opts->read_16);//T STates: 3, 3
 		add_ir(code, 2, opts->regs[Z80_SP], SZ_W);
@@ -1967,7 +1954,7 @@
 		jmp_r(code, opts->gen.scratch1);
 		break;
 	case Z80_RETCC: {
-		cycles(&opts->gen, 5);//T States: 5
+		cycles(&opts->gen, num_cycles + 1);//T States: 5
 		uint8_t cond = CC_Z;
 		switch (inst->reg)
 		{
@@ -2004,7 +1991,7 @@
 	}
 	case Z80_RETI:
 		//For some systems, this may need a callback for signalling interrupt routine completion
-		cycles(&opts->gen, 8);//T States: 4, 4
+		cycles(&opts->gen, num_cycles);//T States: 4, 4
 		mov_rr(code, opts->regs[Z80_SP], opts->gen.scratch1, SZ_W);
 		call(code, opts->read_16);//T STates: 3, 3
 		add_ir(code, 2, opts->regs[Z80_SP], SZ_W);
@@ -2012,7 +1999,7 @@
 		jmp_r(code, opts->gen.scratch1);
 		break;
 	case Z80_RETN:
-		cycles(&opts->gen, 8);//T States: 4, 4
+		cycles(&opts->gen, num_cycles);//T States: 4, 4
 		mov_rdispr(code, opts->gen.context_reg, offsetof(z80_context, iff2), opts->gen.scratch2, SZ_B);
 		mov_rr(code, opts->regs[Z80_SP], opts->gen.scratch1, SZ_W);
 		mov_rrdisp(code, opts->gen.scratch2, opts->gen.context_reg, offsetof(z80_context, iff1), SZ_B);
@@ -2023,7 +2010,7 @@
 		break;
 	case Z80_RST: {
 		//RST is basically CALL to an address in page 0
-		cycles(&opts->gen, 5);//T States: 5
+		cycles(&opts->gen, num_cycles + 1);//T States: 5
 		sub_ir(code, 2, opts->regs[Z80_SP], SZ_W);
 		mov_ir(code, address + 1, opts->gen.scratch1, SZ_W);
 		mov_rr(code, opts->regs[Z80_SP], opts->gen.scratch2, SZ_W);
@@ -2038,7 +2025,10 @@
 		break;
 	}
 	case Z80_IN:
-		cycles(&opts->gen, inst->reg == inst->addr_mode == Z80_IMMED_INDIRECT ? 7 : 8);//T States: 4 3/4
+		if (inst->addr_mode == Z80_IMMED_INDIRECT) {
+			num_cycles += 3;
+		}
+		cycles(&opts->gen, num_cycles);//T States: 4 3/4
 		if (inst->addr_mode == Z80_IMMED_INDIRECT) {
 			mov_ir(code, inst->immed, opts->gen.scratch1, SZ_B);
 		} else {
@@ -2068,7 +2058,10 @@
 	case Z80_IND:
 	case Z80_INDR:*/
 	case Z80_OUT:
-		cycles(&opts->gen, inst->reg == Z80_A ? 7 : 8);//T States: 4 3/4
+		if (inst->reg == Z80_A) {
+			num_cycles += 3;
+		}
+		cycles(&opts->gen, num_cycles);//T States: 4 3/4
 		if ((inst->addr_mode & 0x1F) == Z80_IMMED_INDIRECT) {
 			mov_ir(code, inst->immed, opts->gen.scratch2, SZ_B);
 		} else {
@@ -2087,7 +2080,7 @@
 		z80_save_reg(inst, opts);
 		break;
 	case Z80_OUTI:
-		cycles(&opts->gen, 9);//T States: 4, 5
+		cycles(&opts->gen, num_cycles + 1);//T States: 4, 5
 		//read from (HL)
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T states 3
@@ -2132,7 +2125,7 @@
 		break;
 	case Z80_OTIR: {
 		code_ptr start = code->cur;
-		cycles(&opts->gen, 9);//T States: 4, 5
+		cycles(&opts->gen, num_cycles + 1);//T States: 4, 5
 		//read from (HL)
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T states 3
@@ -2187,7 +2180,7 @@
 		break;
 	}
 	case Z80_OUTD:
-		cycles(&opts->gen, 9);//T States: 4, 5
+		cycles(&opts->gen, num_cycles + 1);//T States: 4, 5
 		//read from (HL)
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T states 3
@@ -2232,7 +2225,7 @@
 		break;
 	case Z80_OTDR: {
 		code_ptr start = code->cur;
-		cycles(&opts->gen, 9);//T States: 4, 5
+		cycles(&opts->gen, num_cycles + 1);//T States: 4, 5
 		//read from (HL)
 		zreg_to_native(opts, Z80_HL, opts->gen.scratch1);
 		call(code, opts->read_8);//T states 3
@@ -2335,10 +2328,9 @@
 	//TODO: make this play well with the breakpoint code
 	mov_ir(code, address, opts->gen.scratch1, SZ_W);
 	call(code, opts->read_8);
-	//normal opcode fetch is already factored into instruction timing
-	//back out the base 3 cycles from a read here
-	//not quite perfect, but it will have to do for now
-	cycles(&opts->gen, -3);
+	//opcode fetch M-cycles have one extra T-state
+	cycles(&opts->gen, 1);
+	//TODO: increment R
 	check_cycles_int(&opts->gen, address);
 	call(code, opts->gen.save_context);
 	mov_irdisp(code, address, opts->gen.context_reg, offsetof(z80_context, pc), SZ_W);
--- a/z80inst.c	Sun Jul 24 18:53:22 2016 -0700
+++ b/z80inst.c	Mon Jul 25 23:16:04 2016 -0700
@@ -6,9 +6,10 @@
 #include "z80inst.h"
 #include <string.h>
 #include <stdio.h>
+#include <stddef.h>
 
-#define NOP {Z80_NOP, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 1}
-#define NOP2 {Z80_NOP, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 2}
+#define NOP {Z80_NOP, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0}
+#define USE_MAIN {Z80_USE_MAIN, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0}
 
 z80inst z80_tbl_a[256] = {
 	//0
@@ -345,7 +346,7 @@
 	{Z80_NEG, Z80_A, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_RETN, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_IM, Z80_UNUSED, Z80_IMMED, Z80_UNUSED, 1},
-	NOP2,
+	NOP,
 	{Z80_IN, Z80_A, Z80_REG_INDIRECT, Z80_C, 0},
 	{Z80_OUT, Z80_A, Z80_REG_INDIRECT | Z80_DIR, Z80_C, 0},
 	{Z80_ADC, Z80_HL, Z80_REG, Z80_SP, 0},
@@ -353,75 +354,75 @@
 	{Z80_NEG, Z80_A, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_RETN, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_IM, Z80_UNUSED, Z80_IMMED, Z80_UNUSED, 2},
-	NOP2,
+	NOP,
 	//8
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
 	//9
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
 	//A
 	{Z80_LDI, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_CPI, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INI, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_OUTI, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
 	{Z80_LDD, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_CPD, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_IND, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_OUTD, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
 	//B
 	{Z80_LDIR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_CPIR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INIR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_OTIR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	NOP,
+	NOP,
+	NOP,
+	NOP,
 	{Z80_LDDR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_CPDR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INDR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_OTDR, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2
+	NOP,
+	NOP,
+	NOP,
+	NOP
 };
 
 #define SHIFT_BLOCK(op) \
@@ -497,107 +498,107 @@
 
 z80inst z80_tbl_ix[256] = {
 	//0
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IX, Z80_REG, Z80_BC, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//1
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IX, Z80_REG, Z80_DE, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//2
-	NOP2,
+	USE_MAIN,
 	{Z80_LD, Z80_IX, Z80_IMMED, Z80_UNUSED, 0},
 	{Z80_LD, Z80_IX, Z80_IMMED_INDIRECT | Z80_DIR, Z80_UNUSED, 0},
 	{Z80_INC, Z80_IX, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INC, Z80_IXH, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_DEC, Z80_IXH, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_LD, Z80_IXH, Z80_IMMED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IX, Z80_REG, Z80_IX, 0},
 	{Z80_LD, Z80_IX, Z80_IMMED_INDIRECT, Z80_UNUSED, 0},
 	{Z80_DEC, Z80_IX, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INC, Z80_IXL, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_DEC, Z80_IXL, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_LD, Z80_IXL, Z80_IMMED, Z80_UNUSED, 0},
-	NOP2,
+	USE_MAIN,
 	//3
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_INC, Z80_UNUSED, Z80_IX_DISPLACE, 0, 0},
 	{Z80_DEC, Z80_UNUSED, Z80_IX_DISPLACE, 0, 0},
 	{Z80_LD, Z80_USE_IMMED, Z80_IX_DISPLACE | Z80_DIR, 0, 0},
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IX, Z80_REG, Z80_SP, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//4
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_B, Z80_REG, Z80_IXH, 0},
 	{Z80_LD, Z80_B, Z80_REG, Z80_IXL, 0},
 	{Z80_LD, Z80_B, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_C, Z80_REG, Z80_IXH, 0},
 	{Z80_LD, Z80_C, Z80_REG, Z80_IXL, 0},
 	{Z80_LD, Z80_C, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//5
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_D, Z80_REG, Z80_IXH, 0},
 	{Z80_LD, Z80_D, Z80_REG, Z80_IXL, 0},
 	{Z80_LD, Z80_D, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_E, Z80_REG, Z80_IXH, 0},
 	{Z80_LD, Z80_E, Z80_REG, Z80_IXL, 0},
 	{Z80_LD, Z80_E, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//6
 	{Z80_LD, Z80_IXH, Z80_REG, Z80_B, 0},
 	{Z80_LD, Z80_IXH, Z80_REG, Z80_C, 0},
@@ -622,152 +623,152 @@
 	{Z80_LD, Z80_E, Z80_IX_DISPLACE | Z80_DIR, 0, 0},
 	{Z80_LD, Z80_H, Z80_IX_DISPLACE | Z80_DIR, 0, 0},
 	{Z80_LD, Z80_L, Z80_IX_DISPLACE | Z80_DIR, 0, 0},
-	NOP2,
+	USE_MAIN,
 	{Z80_LD, Z80_A, Z80_IX_DISPLACE | Z80_DIR, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_LD, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_LD, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//8
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_ADD, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_ADD, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADC, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_ADC, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_ADC, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//9
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_SUB, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_SUB, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_SUB, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_SBC, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_SBC, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_SBC, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//A
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_AND, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_AND, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_AND, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_XOR, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_XOR, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_XOR, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//B
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_OR, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_OR, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_OR, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_CP, Z80_A, Z80_REG, Z80_IXH, 0},
 	{Z80_CP, Z80_A, Z80_REG, Z80_IXL, 0},
 	{Z80_CP, Z80_A, Z80_IX_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//C
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//D
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//E
-	NOP2,
+	USE_MAIN,
 	{Z80_POP, Z80_IX, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
+	USE_MAIN,
 	{Z80_EX, Z80_IX, Z80_REG_INDIRECT | Z80_DIR, Z80_SP, 0},
-	NOP2,
+	USE_MAIN,
 	{Z80_PUSH, Z80_IX, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_JP, Z80_UNUSED, Z80_REG_INDIRECT, Z80_IX, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//F
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_SP, Z80_REG, Z80_IX, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN
 };
 
 #define SHIFT_BLOCK_IX(op) \
@@ -853,107 +854,107 @@
 
 z80inst z80_tbl_iy[256] = {
 	//0
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IY, Z80_REG, Z80_BC, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//1
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IY, Z80_REG, Z80_DE, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//2
-	NOP2,
+	USE_MAIN,
 	{Z80_LD, Z80_IY, Z80_IMMED, Z80_UNUSED, 0},
 	{Z80_LD, Z80_IY, Z80_IMMED_INDIRECT | Z80_DIR, Z80_UNUSED, 0},
 	{Z80_INC, Z80_IY, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INC, Z80_IYH, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_DEC, Z80_IYH, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_LD, Z80_IYH, Z80_IMMED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IY, Z80_REG, Z80_IY, 0},
 	{Z80_LD, Z80_IY, Z80_IMMED_INDIRECT, Z80_UNUSED, 0},
 	{Z80_DEC, Z80_IY, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_INC, Z80_IYL, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_DEC, Z80_IYL, Z80_UNUSED, Z80_UNUSED, 0},
 	{Z80_LD, Z80_IYL, Z80_IMMED, Z80_UNUSED, 0},
-	NOP2,
+	USE_MAIN,
 	//3
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_INC, Z80_UNUSED, Z80_IY_DISPLACE, 0, 0},
 	{Z80_DEC, Z80_UNUSED, Z80_IY_DISPLACE, 0, 0},
 	{Z80_LD, Z80_USE_IMMED, Z80_IY_DISPLACE | Z80_DIR, 0, 0},
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_IY, Z80_REG, Z80_SP, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//4
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_B, Z80_REG, Z80_IYH, 0},
 	{Z80_LD, Z80_B, Z80_REG, Z80_IYL, 0},
 	{Z80_LD, Z80_B, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_C, Z80_REG, Z80_IYH, 0},
 	{Z80_LD, Z80_C, Z80_REG, Z80_IYL, 0},
 	{Z80_LD, Z80_C, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//5
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_D, Z80_REG, Z80_IYH, 0},
 	{Z80_LD, Z80_D, Z80_REG, Z80_IYL, 0},
 	{Z80_LD, Z80_D, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_E, Z80_REG, Z80_IYH, 0},
 	{Z80_LD, Z80_E, Z80_REG, Z80_IYL, 0},
 	{Z80_LD, Z80_E, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//6
 	{Z80_LD, Z80_IYH, Z80_REG, Z80_B, 0},
 	{Z80_LD, Z80_IYH, Z80_REG, Z80_C, 0},
@@ -978,154 +979,152 @@
 	{Z80_LD, Z80_E, Z80_IY_DISPLACE | Z80_DIR, 0, 0},
 	{Z80_LD, Z80_H, Z80_IY_DISPLACE | Z80_DIR, 0, 0},
 	{Z80_LD, Z80_L, Z80_IY_DISPLACE | Z80_DIR, 0, 0},
-	NOP2,
+	USE_MAIN,
 	{Z80_LD, Z80_A, Z80_IY_DISPLACE | Z80_DIR, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_LD, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_LD, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//8
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADD, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_ADD, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_ADD, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_ADC, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_ADC, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_ADC, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//9
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_SUB, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_SUB, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_SUB, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_SBC, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_SBC, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_SBC, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//A
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_AND, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_AND, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_AND, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_XOR, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_XOR, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_XOR, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//B
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_OR, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_OR, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_OR, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_CP, Z80_A, Z80_REG, Z80_IYH, 0},
 	{Z80_CP, Z80_A, Z80_REG, Z80_IYL, 0},
 	{Z80_CP, Z80_A, Z80_IY_DISPLACE, 0, 0},
-	NOP2,
+	USE_MAIN,
 	//C
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//D
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//E
-	NOP2,
+	USE_MAIN,
 	{Z80_POP, Z80_IY, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
+	USE_MAIN,
 	{Z80_EX, Z80_IY, Z80_REG_INDIRECT | Z80_DIR, Z80_SP, 0},
-	NOP2,
+	USE_MAIN,
 	{Z80_PUSH, Z80_IY, Z80_UNUSED, Z80_UNUSED, 0},
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_JP, Z80_UNUSED, Z80_REG_INDIRECT, Z80_IY, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	//F
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
 	{Z80_LD, Z80_SP, Z80_REG, Z80_IY, 0},
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	NOP2,
-	//TODO: Enable this based on a define
-	{Z80_NOP, Z80_UNUSED, Z80_UNUSED, Z80_UNUSED, 42}
-	//NOP2
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN,
+	USE_MAIN
 };
 
 #define SHIFT_BLOCK_IY(op) \
@@ -1209,49 +1208,78 @@
 	BIT_BLOCK_IY_REG(Z80_SET, 7),
 };
 
+enum {
+	NORMAL,
+	USE_IX,
+	USE_IY
+};
+
 uint8_t * z80_decode(uint8_t * istream, z80inst * decoded)
 {
 	uint8_t tmp;
-	if (*istream == 0xCB) {
-		istream++;
-		memcpy(decoded, z80_tbl_bit + *istream, sizeof(z80inst));
-	} else if (*istream == 0xDD) {
-		istream++;
-		if (*istream == 0xCB) {
-			tmp = *(++istream);
+	uint8_t *start = istream;
+	uint8_t ixiy = NORMAL;
+	uint16_t ixiy_disp = 0x100;
+	z80inst *base = NULL;
+	while(!base)
+	{
+		switch (*istream)
+		{
+		case 0xCB:
+			istream++;
+			if (ixiy == NORMAL) {
+				base = z80_tbl_bit + *istream;
+			} else if (ixiy == USE_IX) {
+				ixiy_disp = *(istream++);
+				base = z80_tbl_ix_bit + *istream;
+			} else {
+				ixiy_disp = *(istream++);
+				base = z80_tbl_iy_bit + *istream;
+			}
+			break;
+		case 0xED:
 			istream++;
-			memcpy(decoded, z80_tbl_ix_bit + *istream, sizeof(z80inst));
-			decoded->ea_reg = tmp;
-		} else {
-			memcpy(decoded, z80_tbl_ix + *istream, sizeof(z80inst));
-			if ((decoded->addr_mode & 0x1F) == Z80_IX_DISPLACE) {
-				decoded->ea_reg = *(++istream);
+			ixiy = NORMAL;
+			if (*istream < 0x40 || *istream >= 0xC0) {
+				base = z80_tbl_extd + 0xBF;
+			} else {
+				base = z80_tbl_extd + *istream - 0x40;
+			}
+			break;
+		case 0xDD:
+			istream++;
+			ixiy = USE_IX;
+			break;
+		case 0xFD:
+			istream++;
+			ixiy = USE_IY;
+			break;
+		default:
+			if (ixiy == NORMAL) {
+				base = z80_tbl_a + *istream;
+			} else if (ixiy == USE_IX) {
+				base = z80_tbl_ix + *istream;
+			} else {
+				base = z80_tbl_iy + *istream;
 			}
 		}
-	} else if (*istream == 0xED) {
-		istream++;
-		if (*istream < 0x40 || *istream >= 0xC0) {
-			memcpy(decoded, z80_tbl_extd + 0xBF, sizeof(z80inst));
-		} else {
-			memcpy(decoded, z80_tbl_extd + *istream-0x40, sizeof(z80inst));
+	}
+	if (base->op == Z80_USE_MAIN) {
+		base = z80_tbl_a + *istream;
+		ixiy = NORMAL;
+	}
+	memcpy(decoded, base, offsetof(z80inst, opcode_bytes));
+	decoded->opcode_bytes = istream - start + 1;
+	if (ixiy != NORMAL) {
+		if (ixiy_disp < 0x100) {
+			decoded->ea_reg = ixiy_disp;
+			//don't count displacement byte as an opcode byte
+			decoded->opcode_bytes--;
+		} else if ((decoded->addr_mode & 0x1F) == Z80_IX_DISPLACE || (decoded->addr_mode & 0x1F) == Z80_IY_DISPLACE) {
+			decoded->ea_reg = *(++istream);
 		}
-	} else if (*istream == 0xFD) {
-		istream++;
-		if (*istream == 0xCB) {
-			tmp = *(++istream);
-			istream++;
-			memcpy(decoded, z80_tbl_iy_bit + *istream, sizeof(z80inst));
-			decoded->ea_reg = tmp;
-		} else {
-			memcpy(decoded, z80_tbl_iy + *istream, sizeof(z80inst));
-			if ((decoded->addr_mode & 0x1F) == Z80_IY_DISPLACE) {
-				decoded->ea_reg = *(++istream);
-			}
-		}
-	} else {
-		memcpy(decoded, z80_tbl_a + *istream, sizeof(z80inst));
-
 	}
+	
 	if ((decoded->addr_mode & 0x1F) == Z80_IMMED && decoded->op != Z80_RST && decoded->op != Z80_IM) {
 		decoded->immed = *(++istream);
 		if ((decoded->reg >= Z80_BC && decoded->reg < Z80_UNUSED) || decoded->op == Z80_CALL || decoded->op == Z80_CALLCC || decoded->op == Z80_JP || decoded->op == Z80_JPCC) {
--- a/z80inst.h	Sun Jul 24 18:53:22 2016 -0700
+++ b/z80inst.h	Mon Jul 25 23:16:04 2016 -0700
@@ -76,7 +76,8 @@
 	Z80_OUTI,
 	Z80_OTIR,
 	Z80_OUTD,
-	Z80_OTDR
+	Z80_OTDR,
+	Z80_USE_MAIN
 };
 
 enum {
@@ -133,6 +134,7 @@
 	uint8_t  addr_mode;
 	uint8_t  ea_reg;
 	uint16_t immed;
+	uint16_t  opcode_bytes;
 } z80inst;
 
 uint8_t * z80_decode(uint8_t * istream, z80inst * decoded);