view m68k_to_x86.c @ 561:666adeba5a61

Save and restore guest address in the write function for a code memory chunk in the "slow" path for inconvenient host addresses. This fixes an intermittent crash on OSX in the code that checks whether the memory written may contain code
author Mike Pavone <pavone@retrodev.com>
date Mon, 23 Jun 2014 11:46:56 -0400
parents f9431cb3a39c
children
line wrap: on
line source

/*
 Copyright 2013 Michael Pavone
 This file is part of BlastEm.
 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
*/
#include "gen_x86.h"
#include "m68k_to_x86.h"
#include "68kinst.h"
#include "mem.h"
#include "backend.h"
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#define BUS 4
#define PREDEC_PENALTY 2

#define CYCLES RAX
#define LIMIT RBP
#define CONTEXT RSI
#define SCRATCH1 RCX

#ifdef X86_64
#define SCRATCH2 RDI
#else
#define SCRATCH2 RBX
#endif

enum {
	FLAG_X,
	FLAG_N,
	FLAG_Z,
	FLAG_V,
	FLAG_C
};

char disasm_buf[1024];

m68k_context * sync_components(m68k_context * context, uint32_t address);

extern void bcd_add() asm("bcd_add");
extern void bcd_sub() asm("bcd_sub");

void m68k_invalid(uint32_t address, m68k_context * context)
{
	printf("Invalid instruction at %X\n", address);
	exit(1);
}

code_ptr cycles(code_ptr dst, uint32_t num)
{
	dst = add_ir(dst, num, CYCLES, SZ_D);
	return dst;
}

code_ptr check_cycles_int(code_ptr dst, uint32_t address, x86_68k_options * opts)
{
	dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D);
	code_ptr jmp_off = dst+1;
	dst = jcc(dst, CC_NC, dst + 7);
	dst = mov_ir(dst, address, SCRATCH1, SZ_D);
	dst = call(dst, opts->gen.handle_cycle_limit_int);
	*jmp_off = dst - (jmp_off+1);
	return dst;
}

code_ptr check_cycles(code_ptr dst, cpu_options * opts)
{
	dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D);
	code_ptr jmp_off = dst+1;
	dst = jcc(dst, CC_NC, dst + 7);
	dst = call(dst, opts->handle_cycle_limit);
	*jmp_off = dst - (jmp_off+1);
	return dst;
}

code_ptr set_flag(code_ptr dst, uint8_t val, uint8_t flag, x86_68k_options * opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = mov_ir(dst, val, opts->flag_regs[flag], SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			dst = mov_irdisp8(dst, val, CONTEXT, offset, SZ_B);
		} else {
			dst = mov_irind(dst, val, CONTEXT, SZ_B);
		}
	}

	return dst;
}

code_ptr set_flag_cond(code_ptr dst, uint8_t cond, uint8_t flag, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = setcc_r(dst, cond, opts->flag_regs[flag]);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			dst = setcc_rdisp8(dst, cond, CONTEXT, offset);
		} else {
			dst = setcc_rind(dst, cond, CONTEXT);
		}
	}

	return dst;
}

code_ptr check_flag(code_ptr dst, uint8_t flag, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = cmp_ir(dst, 0, opts->flag_regs[flag], SZ_B);
	} else {
		dst = cmp_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, flags) + flag, SZ_B);
	}
	return dst;
}

code_ptr flag_to_reg(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = mov_rr(dst, opts->flag_regs[flag], reg, SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			dst = mov_rdisp8r(dst, CONTEXT, offset, reg, SZ_B);
		} else {
			dst = mov_rindr(dst, CONTEXT, reg, SZ_B);
		}
	}
	return dst;
}

code_ptr reg_to_flag(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = mov_rr(dst, reg, opts->flag_regs[flag], SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			dst = mov_rrdisp8(dst, reg, CONTEXT, offset, SZ_B);
		} else {
			dst = mov_rrind(dst, reg, CONTEXT, SZ_B);
		}
	}
	return dst;
}

code_ptr flag_to_flag(code_ptr dst, uint8_t flag1, uint8_t flag2, x86_68k_options *opts)
{
	if (opts->flag_regs[flag1] >= 0 && opts->flag_regs[flag2] >= 0) {
		dst = mov_rr(dst, opts->flag_regs[flag1], opts->flag_regs[flag2], SZ_B);
	} else if(opts->flag_regs[flag1] >= 0) {
		dst = mov_rrdisp8(dst, opts->flag_regs[flag1], CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B);
	} else if (opts->flag_regs[flag2] >= 0) {
		dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag1, opts->flag_regs[flag2], SZ_B);
	} else {
		dst = push_r(dst, SCRATCH1);
		dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag1, SCRATCH1, SZ_B);
		dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B);
		dst = pop_r(dst, SCRATCH1);
	}
	return dst;
}

code_ptr flag_to_carry(code_ptr dst, uint8_t flag, x86_68k_options * opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = bt_ir(dst, 0, opts->flag_regs[flag], SZ_B);
	} else {
		dst = bt_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, flags) + flag, SZ_B);
	}
	return dst;
}

code_ptr or_flag_to_reg(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = or_rr(dst, opts->flag_regs[flag], reg, SZ_B);
	} else {
		dst = or_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag, reg, SZ_B);
	}
	return dst;
}

code_ptr xor_flag_to_reg(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = xor_rr(dst, opts->flag_regs[flag], reg, SZ_B);
	} else {
		dst = xor_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag, reg, SZ_B);
	}
	return dst;
}

code_ptr xor_flag(code_ptr dst, uint8_t val, uint8_t flag, x86_68k_options *opts)
{
	if (opts->flag_regs[flag] >= 0) {
		dst = xor_ir(dst, val, opts->flag_regs[flag], SZ_B);
	} else {
		dst = xor_irdisp8(dst, val, CONTEXT, offsetof(m68k_context, flags) + flag, SZ_B);
	}
	return dst;
}

code_ptr cmp_flags(code_ptr dst, uint8_t flag1, uint8_t flag2, x86_68k_options *opts)
{
	if (opts->flag_regs[flag1] >= 0 && opts->flag_regs[flag2] >= 0) {
		dst = cmp_rr(dst, opts->flag_regs[flag1], opts->flag_regs[flag2], SZ_B);
	} else if(opts->flag_regs[flag1] >= 0 || opts->flag_regs[flag2] >= 0) {
		if (opts->flag_regs[flag2] >= 0) {
			uint8_t tmp = flag1;
			flag1 = flag2;
			flag2 = tmp;
		}
		dst = cmp_rrdisp8(dst, opts->flag_regs[flag1], CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B);
	} else {
		dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag1, SCRATCH1, SZ_B);
		dst = cmp_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B);
	}
	return dst;
}

int8_t native_reg(m68k_op_info * op, x86_68k_options * opts)
{
	if (op->addr_mode == MODE_REG) {
		return opts->dregs[op->params.regs.pri];
	}
	if (op->addr_mode == MODE_AREG) {
		return opts->aregs[op->params.regs.pri];
	}
	return -1;
}

//must be called with an m68k_op_info that uses a register
size_t reg_offset(m68k_op_info *op)
{
	if (op->addr_mode == MODE_REG) {
		return offsetof(m68k_context, dregs) + sizeof(uint32_t) * op->params.regs.pri;
	}
	return offsetof(m68k_context, aregs) + sizeof(uint32_t) * op->params.regs.pri;
}

void print_regs_exit(m68k_context * context)
{
	printf("XNZVC\n%d%d%d%d%d\n", context->flags[0], context->flags[1], context->flags[2], context->flags[3], context->flags[4]);
	for (int i = 0; i < 8; i++) {
		printf("d%d: %X\n", i, context->dregs[i]);
	}
	for (int i = 0; i < 8; i++) {
		printf("a%d: %X\n", i, context->aregs[i]);
	}
	exit(0);
}

code_ptr translate_m68k_src(m68kinst * inst, x86_ea * ea, code_ptr out, x86_68k_options * opts)
{
	int8_t reg = native_reg(&(inst->src), opts);
	uint8_t sec_reg;
	int32_t dec_amount,inc_amount;
	if (reg >= 0) {
		ea->mode = MODE_REG_DIRECT;
		if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
			out = movsx_rr(out, reg, SCRATCH1, SZ_W, SZ_D);
			ea->base = SCRATCH1;
		} else {
			ea->base = reg;
		}
		return out;
	}
	switch (inst->src.addr_mode)
	{
	case MODE_REG:
	case MODE_AREG:
		//We only get one memory parameter, so if the dst operand is a register in memory,
		//we need to copy this to a temp register first
		reg = native_reg(&(inst->dst), opts);
		if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || !(inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG)
		    || inst->op == M68K_EXG) {

			ea->mode = MODE_REG_DISPLACE8;
			ea->base = CONTEXT;
			ea->disp = reg_offset(&(inst->src));
		} else {
			if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
				out = movsx_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_W, SZ_D);
			} else {
				out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, inst->extra.size);
			}
			ea->mode = MODE_REG_DIRECT;
			ea->base = SCRATCH1;
			//we're explicitly handling the areg dest here, so we exit immediately
			return out;
		}
		break;
	case MODE_AREG_PREDEC:
		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->src.params.regs.pri == 7 ? 2 :1));
		out = cycles(out, PREDEC_PENALTY);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			out = sub_ir(out, dec_amount, opts->aregs[inst->src.params.regs.pri], SZ_D);
		} else {
			out = sub_irdisp8(out, dec_amount, CONTEXT, reg_offset(&(inst->src)), SZ_D);
		}
	case MODE_AREG_INDIRECT:
	case MODE_AREG_POSTINC:
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			out = mov_rdisp8r(out, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->read_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->read_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->read_32);
			break;
		}

		if (inst->src.addr_mode == MODE_AREG_POSTINC) {
			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->src.params.regs.pri == 7 ? 2 : 1));
			if (opts->aregs[inst->src.params.regs.pri] >= 0) {
				out = add_ir(out, inc_amount, opts->aregs[inst->src.params.regs.pri], SZ_D);
			} else {
				out = add_irdisp8(out, inc_amount, CONTEXT, reg_offset(&(inst->src)), SZ_D);
			}
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = (inst->dst.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) ? SCRATCH2 : SCRATCH1;
		break;
	case MODE_AREG_DISPLACE:
		out = cycles(out, BUS);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			out = mov_rdisp8r(out, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		out = add_ir(out, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->read_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->read_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->read_32);
			break;
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_AREG_INDEX_DISP8:
		out = cycles(out, 6);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			out = mov_rdisp8r(out, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
		if (inst->src.params.regs.sec & 1) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			out = add_ir(out, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->read_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->read_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->read_32);
			break;
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_PC_DISPLACE:
		out = cycles(out, BUS);
		out = mov_ir(out, inst->src.params.regs.displacement + inst->address+2, SCRATCH1, SZ_D);
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->read_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->read_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->read_32);
			break;
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_PC_INDEX_DISP8:
		out = cycles(out, 6);
		out = mov_ir(out, inst->address+2, SCRATCH1, SZ_D);
		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
		if (inst->src.params.regs.sec & 1) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			out = add_ir(out, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->read_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->read_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->read_32);
			break;
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		if (inst->src.addr_mode == MODE_ABSOLUTE) {
			out = cycles(out, BUS*2);
		} else {
			out = cycles(out, BUS);
		}
		out = mov_ir(out, inst->src.params.immed, SCRATCH1, SZ_D);
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->read_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->read_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->read_32);
			break;
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_IMMEDIATE:
	case MODE_IMMEDIATE_WORD:
		if (inst->variant != VAR_QUICK) {
			out = cycles(out, (inst->extra.size == OPSIZE_LONG && inst->src.addr_mode == MODE_IMMEDIATE) ? BUS*2 : BUS);
		}
		ea->mode = MODE_IMMED;
		ea->disp = inst->src.params.immed;
		if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD && ea->disp & 0x8000) {
			ea->disp |= 0xFFFF0000;
		}
		return out;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%X: %s\naddress mode %d not implemented (src)\n", inst->address, disasm_buf, inst->src.addr_mode);
		exit(1);
	}
	if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
		if (ea->mode == MODE_REG_DIRECT) {
			out = movsx_rr(out, ea->base, SCRATCH1, SZ_W, SZ_D);
		} else {
			out = movsx_rdisp8r(out, ea->base, ea->disp, SCRATCH1, SZ_W, SZ_D);
			ea->mode = MODE_REG_DIRECT;
		}
		ea->base = SCRATCH1;
	}
	return out;
}

code_ptr translate_m68k_dst(m68kinst * inst, x86_ea * ea, code_ptr out, x86_68k_options * opts, uint8_t fake_read)
{
	int8_t reg = native_reg(&(inst->dst), opts), sec_reg;
	int32_t dec_amount, inc_amount;
	if (reg >= 0) {
		ea->mode = MODE_REG_DIRECT;
		ea->base = reg;
		return out;
	}
	switch (inst->dst.addr_mode)
	{
	case MODE_REG:
	case MODE_AREG:
		ea->mode = MODE_REG_DISPLACE8;
		ea->base = CONTEXT;
		ea->disp = reg_offset(&(inst->dst));
		break;
	case MODE_AREG_PREDEC:
		if (inst->src.addr_mode == MODE_AREG_PREDEC) {
			out = push_r(out, SCRATCH1);
		}
		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1));
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			out = sub_ir(out, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
		} else {
			out = sub_irdisp8(out, dec_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
		}
	case MODE_AREG_INDIRECT:
	case MODE_AREG_POSTINC:
		if (fake_read) {
			out = cycles(out, inst->extra.size == OPSIZE_LONG ? 8 : 4);
		} else {
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH1, SZ_D);
			} else {
				out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH1, SZ_D);
			}
			switch (inst->extra.size)
			{
			case OPSIZE_BYTE:
				out = call(out, opts->read_8);
				break;
			case OPSIZE_WORD:
				out = call(out, opts->read_16);
				break;
			case OPSIZE_LONG:
				out = call(out, opts->read_32);
				break;
			}
		}
		if (inst->src.addr_mode == MODE_AREG_PREDEC) {
			//restore src operand to SCRATCH2
			out =pop_r(out, SCRATCH2);
		} else {
			//save reg value in SCRATCH2 so we can use it to save the result in memory later
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
			} else {
				out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
			}
		}

		if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1));
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				out = add_ir(out, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
			} else {
				out = add_irdisp8(out, inc_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
			}
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_AREG_DISPLACE:
		out = cycles(out, fake_read ? BUS+(inst->extra.size == OPSIZE_LONG ? BUS*2 : BUS) : BUS);
		reg = fake_read ? SCRATCH2 : SCRATCH1;
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], reg, SZ_D);
		} else {
			out = mov_rdisp8r(out, CONTEXT,  reg_offset(&(inst->dst)), reg, SZ_D);
		}
		out = add_ir(out, inst->dst.params.regs.displacement, reg, SZ_D);
		if (!fake_read) {
			out = push_r(out, SCRATCH1);
			switch (inst->extra.size)
			{
			case OPSIZE_BYTE:
				out = call(out, opts->read_8);
				break;
			case OPSIZE_WORD:
				out = call(out, opts->read_16);
				break;
			case OPSIZE_LONG:
				out = call(out, opts->read_32);
				break;
			}
			out = pop_r(out, SCRATCH2);
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_AREG_INDEX_DISP8:
		out = cycles(out, fake_read ? (6 + inst->extra.size == OPSIZE_LONG ? 8 : 4) : 6);
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			out = mov_rdisp8r(out, CONTEXT,  reg_offset(&(inst->dst)), SCRATCH1, SZ_D);
		}
		sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7;
		if (inst->dst.params.regs.sec & 1) {
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->dst.params.regs.displacement) {
			out = add_ir(out, inst->dst.params.regs.displacement, SCRATCH1, SZ_D);
		}
		if (fake_read) {
			out = mov_rr(out, SCRATCH1, SCRATCH2, SZ_D);
		} else {
			out = push_r(out, SCRATCH1);
			switch (inst->extra.size)
			{
			case OPSIZE_BYTE:
				out = call(out, opts->read_8);
				break;
			case OPSIZE_WORD:
				out = call(out, opts->read_16);
				break;
			case OPSIZE_LONG:
				out = call(out, opts->read_32);
				break;
			}
			out = pop_r(out, SCRATCH2);
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_PC_DISPLACE:
		out = cycles(out, fake_read ? BUS+(inst->extra.size == OPSIZE_LONG ? BUS*2 : BUS) : BUS);
		out = mov_ir(out, inst->dst.params.regs.displacement + inst->address+2, fake_read ? SCRATCH2 : SCRATCH1, SZ_D);
		if (!fake_read) {
			out = push_r(out, SCRATCH1);
			switch (inst->extra.size)
			{
			case OPSIZE_BYTE:
				out = call(out, opts->read_8);
				break;
			case OPSIZE_WORD:
				out = call(out, opts->read_16);
				break;
			case OPSIZE_LONG:
				out = call(out, opts->read_32);
				break;
			}
			out = pop_r(out, SCRATCH2);
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_PC_INDEX_DISP8:
		out = cycles(out, fake_read ? (6 + inst->extra.size == OPSIZE_LONG ? 8 : 4) : 6);
		out = mov_ir(out, inst->address+2, SCRATCH1, SZ_D);
		sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7;
		if (inst->dst.params.regs.sec & 1) {
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->dst.params.regs.displacement) {
			out = add_ir(out, inst->dst.params.regs.displacement, SCRATCH1, SZ_D);
		}
		if (fake_read) {
			out = mov_rr(out, SCRATCH1, SCRATCH2, SZ_D);
		} else {
			out = push_r(out, SCRATCH1);
			switch (inst->extra.size)
			{
			case OPSIZE_BYTE:
				out = call(out, opts->read_8);
				break;
			case OPSIZE_WORD:
				out = call(out, opts->read_16);
				break;
			case OPSIZE_LONG:
				out = call(out, opts->read_32);
				break;
			}
			out = pop_r(out, SCRATCH2);
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		//Add cycles for reading address from instruction stream
		out = cycles(out, (inst->dst.addr_mode == MODE_ABSOLUTE ? BUS*2 : BUS) + (fake_read ? (inst->extra.size == OPSIZE_LONG ? BUS*2 : BUS) : 0));
		out = mov_ir(out, inst->dst.params.immed, fake_read ? SCRATCH2 : SCRATCH1, SZ_D);
		if (!fake_read) {
			out = push_r(out, SCRATCH1);
			switch (inst->extra.size)
			{
			case OPSIZE_BYTE:
				out = call(out, opts->read_8);
				break;
			case OPSIZE_WORD:
				out = call(out, opts->read_16);
				break;
			case OPSIZE_LONG:
				out = call(out, opts->read_32);
				break;
			}
			out = pop_r(out, SCRATCH2);
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = SCRATCH1;
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%X: %s\naddress mode %d not implemented (dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
		exit(1);
	}
	return out;
}

code_ptr m68k_save_result(m68kinst * inst, code_ptr out, x86_68k_options * opts)
{
	if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG) {
		if (inst->dst.addr_mode == MODE_AREG_PREDEC && inst->src.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) {
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
			} else {
				out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
			}
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			out = call(out, opts->write_8);
			break;
		case OPSIZE_WORD:
			out = call(out, opts->write_16);
			break;
		case OPSIZE_LONG:
			out = call(out, opts->write_32_lowfirst);
			break;
		}
	}
	return out;
}

code_ptr get_native_address(native_map_slot * native_code_map, uint32_t address)
{
	address &= 0xFFFFFF;
	address /= 2;
	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
	if (!native_code_map[chunk].base) {
		return NULL;
	}
	uint32_t offset = address % NATIVE_CHUNK_SIZE;
	if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET || native_code_map[chunk].offsets[offset] == EXTENSION_WORD) {
		return NULL;
	}
	return native_code_map[chunk].base + native_code_map[chunk].offsets[offset];
}

code_ptr get_native_from_context(m68k_context * context, uint32_t address)
{
	return get_native_address(context->native_code_map, address);
}

uint32_t get_instruction_start(native_map_slot * native_code_map, uint32_t address)
{
	address &= 0xFFFFFF;
	address /= 2;
	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
	if (!native_code_map[chunk].base) {
		return 0;
	}
	uint32_t offset = address % NATIVE_CHUNK_SIZE;
	if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) {
		return 0;
	}
	while (native_code_map[chunk].offsets[offset] == EXTENSION_WORD) {
		--address;
		chunk = address / NATIVE_CHUNK_SIZE;
		offset = address % NATIVE_CHUNK_SIZE;
	}
	return address*2;
}

void map_native_address(m68k_context * context, uint32_t address, code_ptr native_addr, uint8_t size, uint8_t native_size)
{
	native_map_slot * native_code_map = context->native_code_map;
	x86_68k_options * opts = context->options;
	address &= 0xFFFFFF;
	if (address > 0xE00000) {
		context->ram_code_flags[(address & 0xC000) >> 14] |= 1 << ((address & 0x3800) >> 11);
		if (((address & 0x3FFF) + size) & 0xC000) {
			context->ram_code_flags[((address+size) & 0xC000) >> 14] |= 1 << (((address+size) & 0x3800) >> 11);
		}
		uint32_t slot = (address & 0xFFFF)/1024;
		if (!opts->gen.ram_inst_sizes[slot]) {
			opts->gen.ram_inst_sizes[slot] = malloc(sizeof(uint8_t) * 512);
		}
		opts->gen.ram_inst_sizes[slot][((address & 0xFFFF)/2)%512] = native_size;
	}
	address/= 2;
	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
	if (!native_code_map[chunk].base) {
		native_code_map[chunk].base = native_addr;
		native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE);
		memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE);
	}
	uint32_t offset = address % NATIVE_CHUNK_SIZE;
	native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base;
	for(address++,size-=2; size; address++,size-=2) {
		chunk = address / NATIVE_CHUNK_SIZE;
		offset = address % NATIVE_CHUNK_SIZE;
		if (!native_code_map[chunk].base) {
			native_code_map[chunk].base = native_addr;
			native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE);
			memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE);
		}
		native_code_map[chunk].offsets[offset] = EXTENSION_WORD;
	}
}

uint8_t get_native_inst_size(x86_68k_options * opts, uint32_t address)
{
	if (address < 0xE00000) {
		return 0;
	}
	uint32_t slot = (address & 0xFFFF)/1024;
	return opts->gen.ram_inst_sizes[slot][((address & 0xFFFF)/2)%512];
}

code_ptr translate_m68k_move(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	int8_t reg, flags_reg, sec_reg;
	uint8_t dir = 0;
	int32_t offset;
	int32_t inc_amount, dec_amount;
	x86_ea src;
	dst = translate_m68k_src(inst, &src, dst, opts);
	reg = native_reg(&(inst->dst), opts);
	if (inst->dst.addr_mode != MODE_AREG) {
		//update statically set flags
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = set_flag(dst, 0, FLAG_C, opts);
	}

	if (inst->dst.addr_mode != MODE_AREG) {
		if (src.mode == MODE_REG_DIRECT) {
			flags_reg = src.base;
		} else {
			if (reg >= 0) {
				flags_reg = reg;
			} else {
				if(src.mode == MODE_REG_DISPLACE8) {
					dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
				} else {
					dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
				}
				src.mode = MODE_REG_DIRECT;
				flags_reg = src.base = SCRATCH1;
			}
		}
	}
	uint8_t size = inst->extra.size;
	switch(inst->dst.addr_mode)
	{
	case MODE_AREG:
		size = OPSIZE_LONG;
	case MODE_REG:
		if (reg >= 0) {
			if (src.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, src.base, reg, size);
			} else if (src.mode == MODE_REG_DISPLACE8) {
				dst = mov_rdisp8r(dst, src.base, src.disp, reg, size);
			} else {
				dst = mov_ir(dst, src.disp, reg, size);
			}
		} else if(src.mode == MODE_REG_DIRECT) {
			dst = mov_rrdisp8(dst, src.base, CONTEXT, reg_offset(&(inst->dst)), size);
		} else {
			dst = mov_irdisp8(dst, src.disp, CONTEXT, reg_offset(&(inst->dst)), size);
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		break;
	case MODE_AREG_PREDEC:
		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1));
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			dst = sub_ir(dst, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
		} else {
			dst = sub_irdisp8(dst, dec_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
		}
	case MODE_AREG_INDIRECT:
	case MODE_AREG_POSTINC:
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
		}
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != SCRATCH1) {
				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
		} else {
			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			dst = call(dst, opts->write_8);
			break;
		case OPSIZE_WORD:
			dst = call(dst, opts->write_16);
			break;
		case OPSIZE_LONG:
			dst = call(dst, opts->write_32_highfirst);
			break;
		}
		if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1));
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				dst = add_ir(dst, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
			} else {
				dst = add_irdisp8(dst, inc_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
			}
		}
		break;
	case MODE_AREG_DISPLACE:
		dst = cycles(dst, BUS);
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
		}
		dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != SCRATCH1) {
				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
		} else {
			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			dst = call(dst, opts->write_8);
			break;
		case OPSIZE_WORD:
			dst = call(dst, opts->write_16);
			break;
		case OPSIZE_LONG:
			dst = call(dst, opts->write_32_highfirst);
			break;
		}
		break;
	case MODE_AREG_INDEX_DISP8:
		dst = cycles(dst, 6);//TODO: Check to make sure this is correct
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
		}
		sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7;
		if (inst->dst.params.regs.sec & 1) {
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
				}
			}
		} else {
			if (src.base == SCRATCH1) {
				dst = push_r(dst, SCRATCH1);
			}
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D);
			if (src.base == SCRATCH1) {
				dst = pop_r(dst, SCRATCH1);
			}
		}
		if (inst->dst.params.regs.displacement) {
			dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
		}
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != SCRATCH1) {
				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
		} else {
			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			dst = call(dst, opts->write_8);
			break;
		case OPSIZE_WORD:
			dst = call(dst, opts->write_16);
			break;
		case OPSIZE_LONG:
			dst = call(dst, opts->write_32_highfirst);
			break;
		}
		break;
	case MODE_PC_DISPLACE:
		dst = cycles(dst, BUS);
		dst = mov_ir(dst, inst->dst.params.regs.displacement + inst->address+2, SCRATCH2, SZ_D);
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != SCRATCH1) {
				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
		} else {
			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			dst = call(dst, opts->write_8);
			break;
		case OPSIZE_WORD:
			dst = call(dst, opts->write_16);
			break;
		case OPSIZE_LONG:
			dst = call(dst, opts->write_32_highfirst);
			break;
		}
		break;
	case MODE_PC_INDEX_DISP8:
		dst = cycles(dst, 6);//TODO: Check to make sure this is correct
		dst = mov_ir(dst, inst->address, SCRATCH2, SZ_D);
		sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7;
		if (inst->dst.params.regs.sec & 1) {
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
				}
			}
		} else {
			if (src.base == SCRATCH1) {
				dst = push_r(dst, SCRATCH1);
			}
			if (inst->dst.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D);
			if (src.base == SCRATCH1) {
				dst = pop_r(dst, SCRATCH1);
			}
		}
		if (inst->dst.params.regs.displacement) {
			dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
		}
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != SCRATCH1) {
				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
		} else {
			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			dst = call(dst, opts->write_8);
			break;
		case OPSIZE_WORD:
			dst = call(dst, opts->write_16);
			break;
		case OPSIZE_LONG:
			dst = call(dst, opts->write_32_highfirst);
			break;
		}
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != SCRATCH1) {
				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
		} else {
			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
		}
		if (inst->dst.addr_mode == MODE_ABSOLUTE) {
			dst = cycles(dst, BUS*2);
		} else {
			dst = cycles(dst, BUS);
		}
		dst = mov_ir(dst, inst->dst.params.immed, SCRATCH2, SZ_D);
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		switch (inst->extra.size)
		{
		case OPSIZE_BYTE:
			dst = call(dst, opts->write_8);
			break;
		case OPSIZE_WORD:
			dst = call(dst, opts->write_16);
			break;
		case OPSIZE_LONG:
			dst = call(dst, opts->write_32_highfirst);
			break;
		}
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%X: %s\naddress mode %d not implemented (move dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
		exit(1);
	}

	//add cycles for prefetch
	dst = cycles(dst, BUS);
	return dst;
}

code_ptr translate_m68k_movem(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	int8_t bit,reg,sec_reg;
	uint8_t early_cycles;
	if(inst->src.addr_mode == MODE_REG) {
		//reg to mem
		early_cycles = 8;
		int8_t dir;
		switch (inst->dst.addr_mode)
		{
		case MODE_AREG_INDIRECT:
		case MODE_AREG_PREDEC:
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
			}
			break;
		case MODE_AREG_DISPLACE:
			early_cycles += BUS;
			reg = SCRATCH2;
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
			}
			dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
			break;
		case MODE_AREG_INDEX_DISP8:
			early_cycles += 6;
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
			}
			sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7;
			if (inst->dst.params.regs.sec & 1) {
				if (inst->dst.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
					}
				}
			} else {
				if (inst->dst.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
					}
				}
				dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D);
			}
			if (inst->dst.params.regs.displacement) {
				dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
			}
			break;
		case MODE_PC_DISPLACE:
			early_cycles += BUS;
			dst = mov_ir(dst, inst->dst.params.regs.displacement + inst->address+2, SCRATCH2, SZ_D);
			break;
		case MODE_PC_INDEX_DISP8:
			early_cycles += 6;
			dst = mov_ir(dst, inst->address+2, SCRATCH2, SZ_D);
			sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7;
			if (inst->dst.params.regs.sec & 1) {
				if (inst->dst.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
					}
				}
			} else {
				if (inst->dst.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
					}
				}
				dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D);
			}
			if (inst->dst.params.regs.displacement) {
				dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
			}
			break;
		case MODE_ABSOLUTE:
			early_cycles += 4;
		case MODE_ABSOLUTE_SHORT:
			early_cycles += 4;
			dst = mov_ir(dst, inst->dst.params.immed, SCRATCH2, SZ_D);
			break;
		default:
			m68k_disasm(inst, disasm_buf);
			printf("%X: %s\naddress mode %d not implemented (movem dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
			exit(1);
		}
		if (inst->dst.addr_mode == MODE_AREG_PREDEC) {
			reg = 15;
			dir = -1;
		} else {
			reg = 0;
			dir = 1;
		}
		dst = cycles(dst, early_cycles);
		for(bit=0; reg < 16 && reg >= 0; reg += dir, bit++) {
			if (inst->src.params.immed & (1 << bit)) {
				if (inst->dst.addr_mode == MODE_AREG_PREDEC) {
					dst = sub_ir(dst, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, SCRATCH2, SZ_D);
				}
				dst = push_r(dst, SCRATCH2);
				if (reg > 7) {
					if (opts->aregs[reg-8] >= 0) {
						dst = mov_rr(dst, opts->aregs[reg-8], SCRATCH1, inst->extra.size);
					} else {
						dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * (reg-8), SCRATCH1, inst->extra.size);
					}
				} else {
					if (opts->dregs[reg] >= 0) {
						dst = mov_rr(dst, opts->dregs[reg], SCRATCH1, inst->extra.size);
					} else {
						dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * (reg), SCRATCH1, inst->extra.size);
					}
				}
				if (inst->extra.size == OPSIZE_LONG) {
					dst = call(dst, opts->write_32_lowfirst);
				} else {
					dst = call(dst, opts->write_16);
				}
				dst = pop_r(dst, SCRATCH2);
				if (inst->dst.addr_mode != MODE_AREG_PREDEC) {
					dst = add_ir(dst, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, SCRATCH2, SZ_D);
				}
			}
		}
		if (inst->dst.addr_mode == MODE_AREG_PREDEC) {
			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
				dst = mov_rr(dst, SCRATCH2, opts->aregs[inst->dst.params.regs.pri], SZ_D);
			} else {
				dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
			}
		}
	} else {
		//mem to reg
		early_cycles = 4;
		switch (inst->src.addr_mode)
		{
		case MODE_AREG_INDIRECT:
		case MODE_AREG_POSTINC:
			if (opts->aregs[inst->src.params.regs.pri] >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D);
			}
			break;
		case MODE_AREG_DISPLACE:
			early_cycles += BUS;
			reg = SCRATCH2;
			if (opts->aregs[inst->src.params.regs.pri] >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
			}
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
			break;
		case MODE_AREG_INDEX_DISP8:
			early_cycles += 6;
			if (opts->aregs[inst->src.params.regs.pri] >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
			}
			sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
			if (inst->src.params.regs.sec & 1) {
				if (inst->src.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
					}
				}
			} else {
				if (inst->src.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
					}
				}
				dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
			}
			if (inst->src.params.regs.displacement) {
				dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
			}
			break;
		case MODE_PC_DISPLACE:
			early_cycles += BUS;
			dst = mov_ir(dst, inst->src.params.regs.displacement + inst->address+2, SCRATCH1, SZ_D);
			break;
		case MODE_PC_INDEX_DISP8:
			early_cycles += 6;
			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) {
				if (inst->src.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
					} else {
						dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
					}
				}
			} else {
				if (inst->src.params.regs.sec & 0x10) {
					if (opts->aregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
					}
				} else {
					if (opts->dregs[sec_reg] >= 0) {
						dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
					} else {
						dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
					}
				}
				dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
			}
			if (inst->src.params.regs.displacement) {
				dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
			}
			break;
		case MODE_ABSOLUTE:
			early_cycles += 4;
		case MODE_ABSOLUTE_SHORT:
			early_cycles += 4;
			dst = mov_ir(dst, inst->src.params.immed, SCRATCH1, SZ_D);
			break;
		default:
			m68k_disasm(inst, disasm_buf);
			printf("%X: %s\naddress mode %d not implemented (movem src)\n", inst->address, disasm_buf, inst->src.addr_mode);
			exit(1);
		}
		dst = cycles(dst, early_cycles);
		for(reg = 0; reg < 16; reg ++) {
			if (inst->dst.params.immed & (1 << reg)) {
				dst = push_r(dst, SCRATCH1);
				if (inst->extra.size == OPSIZE_LONG) {
					dst = call(dst, opts->read_32);
				} else {
					dst = call(dst, opts->read_16);
				}
				if (inst->extra.size == OPSIZE_WORD) {
					dst = movsx_rr(dst, SCRATCH1, SCRATCH1, SZ_W, SZ_D);
				}
				if (reg > 7) {
					if (opts->aregs[reg-8] >= 0) {
						dst = mov_rr(dst, SCRATCH1, opts->aregs[reg-8], SZ_D);
					} else {
						dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * (reg-8), SZ_D);
					}
				} else {
					if (opts->dregs[reg] >= 0) {
						dst = mov_rr(dst, SCRATCH1, opts->dregs[reg], SZ_D);
					} else {
						dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * (reg), SZ_D);
					}
				}
				dst = pop_r(dst, SCRATCH1);
				dst = add_ir(dst, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, SCRATCH1, SZ_D);
			}
		}
		if (inst->src.addr_mode == MODE_AREG_POSTINC) {
			if (opts->aregs[inst->src.params.regs.pri] >= 0) {
				dst = mov_rr(dst, SCRATCH1, opts->aregs[inst->src.params.regs.pri], SZ_D);
			} else {
				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->src)), SZ_D);
			}
		}
	}
	//prefetch
	dst = cycles(dst, 4);
	return dst;
}

code_ptr translate_m68k_clr(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	dst = set_flag(dst, 0, FLAG_N, opts);
	dst = set_flag(dst, 0, FLAG_V, opts);
	dst = set_flag(dst, 0, FLAG_C, opts);
	dst = set_flag(dst, 1, FLAG_Z, opts);
	int8_t reg = native_reg(&(inst->dst), opts);
	if (reg >= 0) {
		dst = cycles(dst, (inst->extra.size == OPSIZE_LONG ? 6 : 4));
		return  xor_rr(dst, reg, reg, inst->extra.size);
	}
	x86_ea dst_op;
	dst = translate_m68k_dst(inst, &dst_op, dst, opts, 1);
	if (dst_op.mode == MODE_REG_DIRECT) {
		dst = xor_rr(dst, dst_op.base, dst_op.base, inst->extra.size);
	} else {
		dst = mov_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size);
	}
	dst = m68k_save_result(inst, dst, opts);
	return dst;
}

code_ptr translate_m68k_ext(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	x86_ea dst_op;
	uint8_t dst_size = inst->extra.size;
	inst->extra.size--;
	dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
	if (dst_op.mode == MODE_REG_DIRECT) {
		dst = movsx_rr(dst, dst_op.base, dst_op.base, inst->extra.size, dst_size);
		dst = cmp_ir(dst, 0, dst_op.base, dst_size);
	} else {
		dst = movsx_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, inst->extra.size, dst_size);
		dst = cmp_ir(dst, 0, SCRATCH1, dst_size);
		dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, dst_size);
	}
	inst->extra.size = dst_size;
	dst = set_flag(dst, 0, FLAG_V, opts);
	dst = set_flag(dst, 0, FLAG_C, opts);
	dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
	dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
	//M68K EXT only operates on registers so no need for a call to save result here
	return dst;
}

code_ptr translate_m68k_lea(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	int8_t dst_reg = native_reg(&(inst->dst), opts), sec_reg;
	switch(inst->src.addr_mode)
	{
	case MODE_AREG_INDIRECT:
		dst = cycles(dst, BUS);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			if (dst_reg >= 0) {
				dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], dst_reg, SZ_D);
			} else {
				dst = mov_rrdisp8(dst, opts->aregs[inst->src.params.regs.pri], CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
			}
		} else {
			if (dst_reg >= 0) {
				dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, dst_reg, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
			}
		}
		break;
	case MODE_AREG_DISPLACE:
		dst = cycles(dst, 8);
		if (dst_reg >= 0) {
			if (inst->src.params.regs.pri != inst->dst.params.regs.pri) {
				if (opts->aregs[inst->src.params.regs.pri] >= 0) {
					dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], dst_reg, SZ_D);
				} else {
					dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), dst_reg, SZ_D);
				}
			}
			dst = add_ir(dst, inst->src.params.regs.displacement, dst_reg, SZ_D);
		} else {
			if (inst->src.params.regs.pri != inst->dst.params.regs.pri) {
				if (opts->aregs[inst->src.params.regs.pri] >= 0) {
					dst = mov_rrdisp8(dst, opts->aregs[inst->src.params.regs.pri], CONTEXT, reg_offset(&(inst->dst)), SZ_D);
				} else {
					dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D);
					dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
				}
			}
			dst = add_irdisp8(dst, inst->src.params.regs.displacement, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
		}
		break;
	case MODE_AREG_INDEX_DISP8:
		dst = cycles(dst, 12);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH2, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->src)), SCRATCH2, SZ_D);
		}
		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
		if (inst->src.params.regs.sec & 1) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH2, SZ_D);
		}
		if (dst_reg >= 0) {
			dst = mov_rr(dst, SCRATCH2, dst_reg, SZ_D);
		} else {
			dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
		}
		break;
	case MODE_PC_DISPLACE:
		dst = cycles(dst, 8);
		if (dst_reg >= 0) {
			dst = mov_ir(dst, inst->src.params.regs.displacement + inst->address+2, dst_reg, SZ_D);
		} else {
			dst = mov_irdisp8(dst, inst->src.params.regs.displacement + inst->address+2, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
		}
		break;
	case MODE_PC_INDEX_DISP8:
		dst = cycles(dst, BUS*3);
		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) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		if (dst_reg >= 0) {
			dst = mov_rr(dst, SCRATCH1, dst_reg, SZ_D);
		} else {
			dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
		}
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		dst = cycles(dst, (inst->src.addr_mode == MODE_ABSOLUTE) ? BUS * 3 : BUS * 2);
		if (dst_reg >= 0) {
			dst = mov_ir(dst, inst->src.params.immed, dst_reg, SZ_D);
		} else {
			dst = mov_irdisp8(dst, inst->src.params.immed, CONTEXT, reg_offset(&(inst->dst)), SZ_D);
		}
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode);
		exit(1);
	}
	return dst;
}

code_ptr translate_m68k_pea(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	uint8_t sec_reg;
	switch(inst->src.addr_mode)
	{
	case MODE_AREG_INDIRECT:
		dst = cycles(dst, BUS);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
		}
		break;
	case MODE_AREG_DISPLACE:
		dst = cycles(dst, 8);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		break;
	case MODE_AREG_INDEX_DISP8:
		dst = cycles(dst, 6);//TODO: Check to make sure this is correct
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
		if (inst->src.params.regs.sec & 1) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		break;
	case MODE_PC_DISPLACE:
		dst = cycles(dst, 8);
		dst = mov_ir(dst, inst->src.params.regs.displacement + inst->address+2, SCRATCH1, SZ_D);
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		dst = cycles(dst, (inst->src.addr_mode == MODE_ABSOLUTE) ? BUS * 3 : BUS * 2);
		dst = mov_ir(dst, inst->src.params.immed, SCRATCH1, SZ_D);
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode);
		exit(1);
	}
	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	dst = call(dst, opts->write_32_lowfirst);
	return dst;
}

code_ptr translate_m68k_bsr(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	int32_t disp = inst->src.params.immed;
	uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : 4);
	//TODO: Add cycles in the right place relative to pushing the return address on the stack
	dst = cycles(dst, 10);
	dst = mov_ir(dst, after, SCRATCH1, SZ_D);
	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	dst = call(dst, opts->write_32_highfirst);
	code_ptr dest_addr = get_native_address(opts->gen.native_code_map, (inst->address+2) + disp);
	if (!dest_addr) {
		opts->gen.deferred = defer_address(opts->gen.deferred, (inst->address+2) + disp, dst + 1);
		//dummy address to be replaced later
		dest_addr = dst + 256;
	}
	dst = jmp(dst, (char *)dest_addr);
	return dst;
}

code_ptr translate_m68k_bcc(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	dst = cycles(dst, 10);//TODO: Adjust this for branch not taken case
	int32_t disp = inst->src.params.immed;
	uint32_t after = inst->address + 2;
	code_ptr dest_addr = get_native_address(opts->gen.native_code_map, after + disp);
	if (inst->extra.cond == COND_TRUE) {
		if (!dest_addr) {
			opts->gen.deferred = defer_address(opts->gen.deferred, after + disp, dst + 1);
			//dummy address to be replaced later, make sure it generates a 4-byte displacement
			dest_addr = dst + 256;
		}
		dst = jmp(dst, dest_addr);
	} else {
		uint8_t cond = CC_NZ;
		switch (inst->extra.cond)
		{
		case COND_HIGH:
			cond = CC_Z;
		case COND_LOW_SAME:
			dst = flag_to_reg(dst, FLAG_Z, SCRATCH1, opts);
			dst = or_flag_to_reg(dst, FLAG_C, SCRATCH1, opts);
			break;
		case COND_CARRY_CLR:
			cond = CC_Z;
		case COND_CARRY_SET:
			dst = check_flag(dst, FLAG_C, opts);
			break;
		case COND_NOT_EQ:
			cond = CC_Z;
		case COND_EQ:
			dst = check_flag(dst, FLAG_Z, opts);
			break;
		case COND_OVERF_CLR:
			cond = CC_Z;
		case COND_OVERF_SET:
			dst = check_flag(dst, FLAG_V, opts);
			break;
		case COND_PLUS:
			cond = CC_Z;
		case COND_MINUS:
			dst = check_flag(dst, FLAG_N, opts);
			break;
		case COND_GREATER_EQ:
			cond = CC_Z;
		case COND_LESS:
			dst = cmp_flags(dst, FLAG_N, FLAG_V, opts);
			break;
		case COND_GREATER:
			cond = CC_Z;
		case COND_LESS_EQ:
			dst = flag_to_reg(dst, FLAG_V, SCRATCH1, opts);
			dst = xor_flag_to_reg(dst, FLAG_N, SCRATCH1, opts);
			dst = or_flag_to_reg(dst, FLAG_Z, SCRATCH1, opts);
			break;
		}
		if (!dest_addr) {
			opts->gen.deferred = defer_address(opts->gen.deferred, after + disp, dst + 2);
			//dummy address to be replaced later, make sure it generates a 4-byte displacement
			dest_addr = dst + 256;
		}
		dst = jcc(dst, cond, dest_addr);
	}
	return dst;
}

code_ptr translate_m68k_scc(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	uint8_t cond = inst->extra.cond;
	x86_ea dst_op;
	inst->extra.size = OPSIZE_BYTE;
	dst = translate_m68k_dst(inst, &dst_op, dst, opts, 1);
	if (cond == COND_TRUE || cond == COND_FALSE) {
		if ((inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) && inst->extra.cond == COND_TRUE) {
			dst = cycles(dst, 6);
		} else {
			dst = cycles(dst, BUS);
		}
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_ir(dst, cond == COND_TRUE ? 0xFF : 0, dst_op.base, SZ_B);
		} else {
			dst = mov_irdisp8(dst, cond == COND_TRUE ? 0xFF : 0, dst_op.base, dst_op.disp, SZ_B);
		}
	} else {
		uint8_t cc = CC_NZ;
		switch (cond)
		{
		case COND_HIGH:
			cc = CC_Z;
		case COND_LOW_SAME:
			dst = flag_to_reg(dst, FLAG_Z, SCRATCH1, opts);
			dst = or_flag_to_reg(dst, FLAG_C, SCRATCH1, opts);
			break;
		case COND_CARRY_CLR:
			cc = CC_Z;
		case COND_CARRY_SET:
			dst = check_flag(dst, FLAG_C, opts);
			break;
		case COND_NOT_EQ:
			cc = CC_Z;
		case COND_EQ:
			dst = check_flag(dst, FLAG_Z, opts);
			break;
		case COND_OVERF_CLR:
			cc = CC_Z;
		case COND_OVERF_SET:
			dst = check_flag(dst, FLAG_V, opts);
			break;
		case COND_PLUS:
			cc = CC_Z;
		case COND_MINUS:
			dst = check_flag(dst, FLAG_N, opts);
			break;
		case COND_GREATER_EQ:
			cc = CC_Z;
		case COND_LESS:
			dst = cmp_flags(dst, FLAG_N, FLAG_V, opts);
			break;
		case COND_GREATER:
			cc = CC_Z;
		case COND_LESS_EQ:
			dst = flag_to_reg(dst, FLAG_V, SCRATCH1, opts);
			dst = xor_flag_to_reg(dst, FLAG_N, SCRATCH1, opts);
			dst = or_flag_to_reg(dst, FLAG_Z, SCRATCH1, opts);
			break;
		}
		code_ptr true_off = dst + 1;
		dst = jcc(dst, cc, dst+2);
		dst = cycles(dst, BUS);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_ir(dst, 0, dst_op.base, SZ_B);
		} else {
			dst = mov_irdisp8(dst, 0, dst_op.base, dst_op.disp, SZ_B);
		}
		code_ptr end_off = dst+1;
		dst = jmp(dst, dst+2);
		*true_off = dst - (true_off+1);
		dst = cycles(dst, 6);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_ir(dst, 0xFF, dst_op.base, SZ_B);
		} else {
			dst = mov_irdisp8(dst, 0xFF, dst_op.base, dst_op.disp, SZ_B);
		}
		*end_off = dst - (end_off+1);
	}
	dst = m68k_save_result(inst, dst, opts);
	return dst;
}

code_ptr translate_m68k_jmp(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	code_ptr dest_addr;
	uint8_t sec_reg;
	uint32_t m68k_addr;
	switch(inst->src.addr_mode)
	{
	case MODE_AREG_INDIRECT:
		dst = cycles(dst, BUS*2);
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
		}
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_AREG_INDEX_DISP8:
		dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct
		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
		if (inst->src.params.regs.sec & 1) {
			//32-bit index register
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			//16-bit index register
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_PC_DISPLACE:
		dst = cycles(dst, 10);
		m68k_addr = inst->src.params.regs.displacement + inst->address + 2;
		if ((m68k_addr & 0xFFFFFF) < 0x400000) {
			dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr);
			if (!dest_addr) {
				opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1);
				//dummy address to be replaced later, make sure it generates a 4-byte displacement
				dest_addr = dst + 256;
			}
			dst = jmp(dst, dest_addr);
		} else {
			dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D);
			dst = call(dst, opts->native_addr);
			dst = jmp_r(dst, SCRATCH1);
		}
		break;
	case MODE_PC_INDEX_DISP8:
		dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct
		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) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		dst = cycles(dst, inst->src.addr_mode == MODE_ABSOLUTE ? 12 : 10);
		m68k_addr = inst->src.params.immed;
		if ((m68k_addr & 0xFFFFFF) < 0x400000) {
			dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr);
			if (!dest_addr) {
				opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1);
				//dummy address to be replaced later, make sure it generates a 4-byte displacement
				dest_addr = dst + 256;
			}
			dst = jmp(dst, dest_addr);
		} else {
			dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D);
			dst = call(dst, opts->native_addr);
			dst = jmp_r(dst, SCRATCH1);
		}
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%s\naddress mode %d not yet supported (jmp)\n", disasm_buf, inst->src.addr_mode);
		exit(1);
	}
	return dst;
}

code_ptr translate_m68k_jsr(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	code_ptr dest_addr;
	uint8_t sec_reg;
	uint32_t after;
	uint32_t m68k_addr;
	switch(inst->src.addr_mode)
	{
	case MODE_AREG_INDIRECT:
		dst = cycles(dst, BUS*2);
		dst = mov_ir(dst, inst->address + 2, SCRATCH1, SZ_D);
		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		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 {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
		}
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_AREG_DISPLACE:
		dst = cycles(dst, BUS*2);
		dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D);
		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		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 {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
		}
		dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_AREG_INDEX_DISP8:
		dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct
		dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D);
		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		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 {
			dst = mov_rdisp8r(dst, CONTEXT,  reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		sec_reg = (inst->src.params.regs.sec >> 1) & 0x7;
		if (inst->src.params.regs.sec & 1) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_PC_DISPLACE:
		//TODO: Add cycles in the right place relative to pushing the return address on the stack
		dst = cycles(dst, 10);
		dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D);
		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		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->gen.native_code_map, m68k_addr);
			if (!dest_addr) {
				opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1);
				//dummy address to be replaced later, make sure it generates a 4-byte displacement
				dest_addr = dst + 256;
			}
			dst = jmp(dst, dest_addr);
		} else {
			dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D);
			dst = call(dst, opts->native_addr);
			dst = jmp_r(dst, SCRATCH1);
		}
		break;
	case MODE_PC_INDEX_DISP8:
		dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct
		dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D);
		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		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) {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D);
				} else {
					dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D);
				}
			}
		} else {
			if (inst->src.params.regs.sec & 0x10) {
				if (opts->aregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			} else {
				if (opts->dregs[sec_reg] >= 0) {
					dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D);
				} else {
					dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D);
				}
			}
			dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		dst = call(dst, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		//TODO: Add cycles in the right place relative to pushing the return address on the stack
		dst = cycles(dst, inst->src.addr_mode == MODE_ABSOLUTE ? 12 : 10);
		dst = mov_ir(dst, inst->address + (inst->src.addr_mode == MODE_ABSOLUTE ? 6 : 4), SCRATCH1, SZ_D);
		dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		dst = call(dst, opts->write_32_highfirst);
		m68k_addr = inst->src.params.immed;
		if ((m68k_addr & 0xFFFFFF) < 0x400000) {
			dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr);
			if (!dest_addr) {
				opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1);
				//dummy address to be replaced later, make sure it generates a 4-byte displacement
				dest_addr = dst + 256;
			}
			dst = jmp(dst, dest_addr);
		} else {
			dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D);
			dst = call(dst, opts->native_addr);
			dst = jmp_r(dst, SCRATCH1);
		}
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%s\naddress mode %d not yet supported (jsr)\n", disasm_buf, inst->src.addr_mode);
		exit(1);
	}
	return dst;
}

code_ptr translate_m68k_rts(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	//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, opts->read_32);
	dst = call(dst, opts->native_addr);
	dst = jmp_r(dst, SCRATCH1);
	return dst;
}

code_ptr translate_m68k_dbcc(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	//best case duration
	dst = cycles(dst, 10);
	code_ptr skip_loc = NULL;
	//TODO: Check if COND_TRUE technically valid here even though
	//it's basically a slow NOP
	if (inst->extra.cond != COND_FALSE) {
		uint8_t cond = CC_NZ;
		switch (inst->extra.cond)
		{
		case COND_HIGH:
			cond = CC_Z;
		case COND_LOW_SAME:
			dst = flag_to_reg(dst, FLAG_Z, SCRATCH1, opts);
			dst = or_flag_to_reg(dst, FLAG_C, SCRATCH1, opts);
			break;
		case COND_CARRY_CLR:
			cond = CC_Z;
		case COND_CARRY_SET:
			dst = check_flag(dst, FLAG_C, opts);
			break;
		case COND_NOT_EQ:
			cond = CC_Z;
		case COND_EQ:
			dst = check_flag(dst, FLAG_Z, opts);
			break;
		case COND_OVERF_CLR:
			cond = CC_Z;
		case COND_OVERF_SET:
			dst = check_flag(dst, FLAG_V, opts);
			break;
		case COND_PLUS:
			cond = CC_Z;
		case COND_MINUS:
			dst = check_flag(dst, FLAG_N, opts);
			break;
		case COND_GREATER_EQ:
			cond = CC_Z;
		case COND_LESS:
			dst = cmp_flags(dst, FLAG_N, FLAG_V, opts);
			break;
		case COND_GREATER:
			cond = CC_Z;
		case COND_LESS_EQ:
			dst = flag_to_reg(dst, FLAG_V, SCRATCH1, opts);
			dst = xor_flag_to_reg(dst, FLAG_N, SCRATCH1, opts);
			dst = or_flag_to_reg(dst, FLAG_Z, SCRATCH1, opts);
			break;
		}
		skip_loc = dst + 1;
		dst = jcc(dst, cond, dst + 2);
	}
	if (opts->dregs[inst->dst.params.regs.pri] >= 0) {
		dst = sub_ir(dst, 1, opts->dregs[inst->dst.params.regs.pri], SZ_W);
		dst = cmp_ir(dst, -1, opts->dregs[inst->dst.params.regs.pri], SZ_W);
	} else {
		dst = sub_irdisp8(dst, 1, CONTEXT, offsetof(m68k_context, dregs) + 4 * inst->dst.params.regs.pri, SZ_W);
		dst = cmp_irdisp8(dst, -1, CONTEXT, offsetof(m68k_context, dregs) + 4 * inst->dst.params.regs.pri, SZ_W);
	}
	code_ptr loop_end_loc = dst+1;
	dst = jcc(dst, CC_Z, dst+2);
	uint32_t after = inst->address + 2;
	code_ptr dest_addr = get_native_address(opts->gen.native_code_map, after + inst->src.params.immed);
	if (!dest_addr) {
		opts->gen.deferred = defer_address(opts->gen.deferred, after + inst->src.params.immed, dst + 1);
		//dummy address to be replaced later, make sure it generates a 4-byte displacement
		dest_addr = dst + 256;
	}
	dst = jmp(dst, dest_addr);
	*loop_end_loc = dst - (loop_end_loc+1);
	if (skip_loc) {
		dst = cycles(dst, 2);
		*skip_loc = dst - (skip_loc+1);
		dst = cycles(dst, 2);
	} else {
		dst = cycles(dst, 4);
	}
	return dst;
}

code_ptr translate_m68k_link(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	int8_t reg = native_reg(&(inst->src), opts);
	//compensate for displacement word
	dst = cycles(dst, BUS);
	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	if (reg >= 0) {
		dst = mov_rr(dst, reg, SCRATCH1, SZ_D);
	} else {
		dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D);
	}
	dst = call(dst, opts->write_32_highfirst);
	if (reg >= 0) {
		dst = mov_rr(dst, opts->aregs[7], reg, SZ_D);
	} else {
		dst = mov_rrdisp8(dst, opts->aregs[7], CONTEXT, reg_offset(&(inst->src)), SZ_D);
	}
	dst = add_ir(dst, inst->dst.params.immed, opts->aregs[7], SZ_D);
	//prefetch
	dst = cycles(dst, BUS);
	return dst;
}

code_ptr translate_m68k_movep(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	int8_t reg;
	dst = cycles(dst, BUS*2);
	if (inst->src.addr_mode == MODE_REG) {
		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
			dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
		}
		if (inst->dst.params.regs.displacement) {
			dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
		}
		reg = native_reg(&(inst->src), opts);
		if (inst->extra.size == OPSIZE_LONG) {
			if (reg >= 0) {
				dst = mov_rr(dst, reg, SCRATCH1, SZ_D);
				dst = shr_ir(dst, 24, SCRATCH1, SZ_D);
				dst = push_r(dst, SCRATCH2);
				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);

			} else {
				dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+3, SCRATCH1, SZ_B);
				dst = push_r(dst, SCRATCH2);
				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, opts->write_8);
			dst = pop_r(dst, SCRATCH2);
			dst = add_ir(dst, 2, SCRATCH2, SZ_D);
		}
		if (reg >= 0) {
			dst = mov_rr(dst, reg, SCRATCH1, SZ_W);
			dst = shr_ir(dst, 8, SCRATCH1, SZ_W);
			dst = push_r(dst, SCRATCH2);
			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, 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, 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);
		} else {
			dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D);
		}
		if (inst->src.params.regs.displacement) {
			dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D);
		}
		reg = native_reg(&(inst->dst), opts);
		if (inst->extra.size == OPSIZE_LONG) {
			if (reg >= 0) {
				dst = push_r(dst, 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, 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, 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, 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, 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, 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, opts->read_8);
			dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_B);
		}
	}
	return dst;
}

code_ptr translate_m68k_cmp(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	uint8_t size = inst->extra.size;
	x86_ea src_op, dst_op;
	dst = translate_m68k_src(inst, &src_op, dst, opts);
	if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
		dst = push_r(dst, SCRATCH1);
		dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
		dst = pop_r(dst, SCRATCH2);
		src_op.base = SCRATCH2;
	} else {
		dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
		if (inst->dst.addr_mode == MODE_AREG && size == OPSIZE_WORD) {
			size = OPSIZE_LONG;
		}
	}
	dst = cycles(dst, BUS);
	if (src_op.mode == MODE_REG_DIRECT) {
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = cmp_rr(dst, src_op.base, dst_op.base, size);
		} else {
			dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
		}
	} else if (src_op.mode == MODE_REG_DISPLACE8) {
		dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size);
	} else {
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = cmp_ir(dst, src_op.disp, dst_op.base, size);
		} else {
			dst = cmp_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size);
		}
	}
	dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
	dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
	dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
	dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
	return dst;
}

typedef code_ptr (*shift_ir_t)(code_ptr out, uint8_t val, uint8_t dst, uint8_t size);
typedef code_ptr (*shift_irdisp8_t)(code_ptr out, uint8_t val, uint8_t dst_base, int8_t disp, uint8_t size);
typedef code_ptr (*shift_clr_t)(code_ptr out, uint8_t dst, uint8_t size);
typedef code_ptr (*shift_clrdisp8_t)(code_ptr out, uint8_t dst_base, int8_t disp, uint8_t size);

code_ptr translate_shift(code_ptr dst, m68kinst * inst, x86_ea *src_op, x86_ea * dst_op, x86_68k_options * opts, shift_ir_t shift_ir, shift_irdisp8_t shift_irdisp8, shift_clr_t shift_clr, shift_clrdisp8_t shift_clrdisp8, shift_ir_t special, shift_irdisp8_t special_disp8)
{
	code_ptr end_off = NULL;
	code_ptr nz_off = NULL;
	code_ptr z_off = NULL;
	if (inst->src.addr_mode == MODE_UNUSED) {
		dst = cycles(dst, BUS);
		//Memory shift
		dst = shift_ir(dst, 1, dst_op->base, SZ_W);
	} else {
		dst = cycles(dst, inst->extra.size == OPSIZE_LONG ? 8 : 6);
		if (src_op->mode == MODE_IMMED) {
			if (src_op->disp != 1 && inst->op == M68K_ASL) {
				dst = set_flag(dst, 0, FLAG_V, opts);
				for (int i = 0; i < src_op->disp; i++) {
					if (dst_op->mode == MODE_REG_DIRECT) {
						dst = shift_ir(dst, 1, dst_op->base, inst->extra.size);
					} else {
						dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size);
					}
					//dst = setcc_r(dst, CC_O, FLAG_V);
					code_ptr after_flag_set = dst+1;
					dst = jcc(dst, CC_NO, dst+2);
					dst = set_flag(dst, 1, FLAG_V, opts);
					*after_flag_set = dst - (after_flag_set+1);
				}
			} else {
				if (dst_op->mode == MODE_REG_DIRECT) {
					dst = shift_ir(dst, src_op->disp, dst_op->base, inst->extra.size);
				} else {
					dst = shift_irdisp8(dst, src_op->disp, dst_op->base, dst_op->disp, inst->extra.size);
				}
				dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
			}
		} else {
			if (src_op->base != RCX) {
				if (src_op->mode == MODE_REG_DIRECT) {
					dst = mov_rr(dst, src_op->base, RCX, SZ_B);
				} else {
					dst = mov_rdisp8r(dst, src_op->base, src_op->disp, RCX, SZ_B);
				}

			}
			dst = and_ir(dst, 63, RCX, SZ_D);
			nz_off = dst+1;
			dst = jcc(dst, CC_NZ, dst+2);
			//Flag behavior for shift count of 0 is different for x86 than 68K
			if (dst_op->mode == MODE_REG_DIRECT) {
				dst = cmp_ir(dst, 0, dst_op->base, inst->extra.size);
			} else {
				dst = cmp_irdisp8(dst, 0, dst_op->base, dst_op->disp, inst->extra.size);
			}
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
			dst = set_flag(dst, 0, FLAG_C, opts);
			//For other instructions, this flag will be set below
			if (inst->op == M68K_ASL) {
				dst = set_flag(dst, 0, FLAG_V, opts);
			}
			z_off = dst+1;
			dst = jmp(dst, dst+2);
			*nz_off = dst - (nz_off + 1);
			//add 2 cycles for every bit shifted
			dst = add_rr(dst, RCX, CYCLES, SZ_D);
			dst = add_rr(dst, RCX, CYCLES, SZ_D);
			if (inst->op == M68K_ASL) {
				//ASL has Overflow flag behavior that depends on all of the bits shifted through the MSB
				//Easiest way to deal with this is to shift one bit at a time
				dst = set_flag(dst, 0, FLAG_V, opts);
				code_ptr loop_start = dst;
				if (dst_op->mode == MODE_REG_DIRECT) {
					dst = shift_ir(dst, 1, dst_op->base, inst->extra.size);
				} else {
					dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size);
				}
				//dst = setcc_r(dst, CC_O, FLAG_V);
				code_ptr after_flag_set = dst+1;
				dst = jcc(dst, CC_NO, dst+2);
				dst = set_flag(dst, 1, FLAG_V, opts);
				*after_flag_set = dst - (after_flag_set+1);
				dst = loop(dst, loop_start);
			} else {
				//x86 shifts modulo 32 for operand sizes less than 64-bits
				//but M68K shifts modulo 64, so we need to check for large shifts here
				dst = cmp_ir(dst, 32, RCX, SZ_B);
				code_ptr norm_shift_off = dst + 1;
				dst = jcc(dst, CC_L, dst+2);
				if (special) {
					code_ptr after_flag_set = NULL;
					if (inst->extra.size == OPSIZE_LONG) {
						code_ptr neq_32_off = dst + 1;
						dst = jcc(dst, CC_NZ, dst+2);

						//set the carry bit to the lsb
						if (dst_op->mode == MODE_REG_DIRECT) {
							dst = special(dst, 1, dst_op->base, SZ_D);
						} else {
							dst = special_disp8(dst, 1, dst_op->base, dst_op->disp, SZ_D);
						}
						dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
						after_flag_set = dst+1;
						dst = jmp(dst, dst+2);
						*neq_32_off = dst - (neq_32_off+1);
					}
					dst = set_flag(dst, 0, FLAG_C, opts);
					if (after_flag_set) {
						*after_flag_set = dst - (after_flag_set+1);
					}
					dst = set_flag(dst, 1, FLAG_Z, opts);
					dst = set_flag(dst, 0, FLAG_N, opts);
					if (dst_op->mode == MODE_REG_DIRECT) {
						dst = xor_rr(dst, dst_op->base, dst_op->base, inst->extra.size);
					} else {
						dst = mov_irdisp8(dst, 0, dst_op->base, dst_op->disp, inst->extra.size);
					}
				} else {
					if (dst_op->mode == MODE_REG_DIRECT) {
						dst = shift_ir(dst, 31, dst_op->base, inst->extra.size);
						dst = shift_ir(dst, 1, dst_op->base, inst->extra.size);
					} else {
						dst = shift_irdisp8(dst, 31, dst_op->base, dst_op->disp, inst->extra.size);
						dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size);
					}

				}
				end_off = dst+1;
				dst = jmp(dst, dst+2);
				*norm_shift_off = dst - (norm_shift_off+1);
				if (dst_op->mode == MODE_REG_DIRECT) {
					dst = shift_clr(dst, dst_op->base, inst->extra.size);
				} else {
					dst = shift_clrdisp8(dst, dst_op->base, dst_op->disp, inst->extra.size);
				}
			}
		}

	}
	if (!special && end_off) {
		*end_off = dst - (end_off + 1);
	}
	dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
	dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
	dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
	if (special && end_off) {
		*end_off = dst - (end_off + 1);
	}
	//set X flag to same as C flag
	if (opts->flag_regs[FLAG_C] >= 0) {
		dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
	} else {
		dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
	}
	if (z_off) {
		*z_off = dst - (z_off + 1);
	}
	if (inst->op != M68K_ASL) {
		dst = set_flag(dst, 0, FLAG_V, opts);
	}
	if (inst->src.addr_mode == MODE_UNUSED) {
		dst = m68k_save_result(inst, dst, opts);
	}
	return dst;
}

#define BIT_SUPERVISOR 5

code_ptr translate_m68k(code_ptr dst, m68kinst * inst, x86_68k_options * opts)
{
	code_ptr end_off, zero_off, norm_off;
	uint8_t dst_reg;
	dst = check_cycles_int(dst, inst->address, opts);
	if (inst->op == M68K_MOVE) {
		return translate_m68k_move(dst, inst, opts);
	} else if(inst->op == M68K_LEA) {
		return translate_m68k_lea(dst, inst, opts);
	} else if(inst->op == M68K_PEA) {
		return translate_m68k_pea(dst, inst, opts);
	} else if(inst->op == M68K_BSR) {
		return translate_m68k_bsr(dst, inst, opts);
	} else if(inst->op == M68K_BCC) {
		return translate_m68k_bcc(dst, inst, opts);
	} else if(inst->op == M68K_JMP) {
		return translate_m68k_jmp(dst, inst, opts);
	} else if(inst->op == M68K_JSR) {
		return translate_m68k_jsr(dst, inst, opts);
	} else if(inst->op == M68K_RTS) {
		return translate_m68k_rts(dst, inst, opts);
	} else if(inst->op == M68K_DBCC) {
		return translate_m68k_dbcc(dst, inst, opts);
	} else if(inst->op == M68K_CLR) {
		return translate_m68k_clr(dst, inst, opts);
	} else if(inst->op == M68K_MOVEM) {
		return translate_m68k_movem(dst, inst, opts);
	} else if(inst->op == M68K_LINK) {
		return translate_m68k_link(dst, inst, opts);
	} else if(inst->op == M68K_EXT) {
		return translate_m68k_ext(dst, inst, opts);
	} else if(inst->op == M68K_SCC) {
		return translate_m68k_scc(dst, inst, opts);
	} else if(inst->op == M68K_MOVEP) {
		return translate_m68k_movep(dst, inst, opts);
	} else if(inst->op == M68K_INVALID) {
		if (inst->src.params.immed == 0x7100) {
			return retn(dst);
		}
		dst = mov_ir(dst, inst->address, SCRATCH2, SZ_D);
#ifdef X86_32
		dst = push_r(dst, CONTEXT);
		dst = push_r(dst, SCRATCH2);
#endif
		return call(dst, (code_ptr)m68k_invalid);
	} else if(inst->op == M68K_CMP) {
		return translate_m68k_cmp(dst, inst, opts);
	}
	x86_ea src_op, dst_op;
	if (inst->src.addr_mode != MODE_UNUSED) {
		dst = translate_m68k_src(inst, &src_op, dst, opts);
	}
	if (inst->dst.addr_mode != MODE_UNUSED) {
		dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
	}
	uint8_t size;
	switch(inst->op)
	{
	case M68K_ABCD:
		if (src_op.base != SCRATCH2) {
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_B);
			} else {
				dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_B);
			}
		}
		if (dst_op.base != SCRATCH1) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, dst_op.base, SCRATCH1, SZ_B);
			} else {
				dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, SZ_B);
			}
		}
		dst = flag_to_carry(dst, FLAG_X, opts);
		dst = jcc(dst, CC_NC, dst+5);
		dst = add_ir(dst, 1, SCRATCH1, SZ_B);
		dst = call(dst, (code_ptr)bcd_add);
		dst = reg_to_flag(dst, CH, FLAG_C, opts);
		dst = reg_to_flag(dst, CH, FLAG_X, opts);
		dst = cmp_ir(dst, 0, SCRATCH1, SZ_B);
		dst = jcc(dst, CC_Z, dst+4);
		dst = set_flag(dst, 0, FLAG_Z, opts);
		if (dst_op.base != SCRATCH1) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_B);
			} else {
				dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_B);
			}
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_ADD:
		dst = cycles(dst, BUS);
		size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size;
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = add_rr(dst, src_op.base, dst_op.base, size);
			} else {
				dst = add_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = add_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = add_ir(dst, src_op.disp, dst_op.base, size);
			} else {
				dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size);
			}
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
			dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
			if (opts->flag_regs[FLAG_C] >= 0) {
				dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
			} else {
				dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
			}
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_ADDX: {
		dst = cycles(dst, BUS);
		dst = flag_to_carry(dst, FLAG_X, opts);
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = adc_rr(dst, src_op.base, dst_op.base, inst->extra.size);
			} else {
				dst = adc_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = adc_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = adc_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
			} else {
				dst = adc_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
			}
		}
		dst = set_flag_cond(dst, CC_C, FLAG_C, opts);

		code_ptr after_flag_set = dst+1;
		dst = jcc(dst, CC_Z, dst+2);
		dst = set_flag(dst, 0, FLAG_Z, opts);
		*after_flag_set = dst - (after_flag_set+1);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
		if (opts->flag_regs[FLAG_C] >= 0) {
			dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
		} else {
			dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	}
	case M68K_AND:
		dst = cycles(dst, BUS);
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = and_rr(dst, src_op.base, dst_op.base, inst->extra.size);
			} else {
				dst = and_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = and_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = and_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
			} else {
				dst = and_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
			}
		}
		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_ANDI_CCR:
	case M68K_ANDI_SR:
		dst = cycles(dst, 20);
		//TODO: If ANDI to SR, trap if not in supervisor mode
		if (!(inst->src.params.immed & 0x1)) {
			dst = set_flag(dst, 0, FLAG_C, opts);
		}
		if (!(inst->src.params.immed & 0x2)) {
			dst = set_flag(dst, 0, FLAG_V, opts);
		}
		if (!(inst->src.params.immed & 0x4)) {
			dst = set_flag(dst, 0, FLAG_Z, opts);
		}
		if (!(inst->src.params.immed & 0x8)) {
			dst = set_flag(dst, 0, FLAG_N, opts);
		}
		if (!(inst->src.params.immed & 0x10)) {
			dst = set_flag(dst, 0, FLAG_X, opts);
		}
		if (inst->op == M68K_ANDI_SR) {
			dst = and_irdisp8(dst, inst->src.params.immed >> 8, CONTEXT, offsetof(m68k_context, status), SZ_B);
			if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) {
				//leave supervisor mode
				dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_B);
				dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_B);
				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_B);
			}
			if (inst->src.params.immed & 0x700) {
				dst = call(dst, opts->do_sync);
			}
		}
		break;
	case M68K_ASL:
	case M68K_LSL:
		dst = translate_shift(dst, inst, &src_op, &dst_op, opts, shl_ir, shl_irdisp8, shl_clr, shl_clrdisp8, shr_ir, shr_irdisp8);
		break;
	case M68K_ASR:
		dst = translate_shift(dst, inst, &src_op, &dst_op, opts, sar_ir, sar_irdisp8, sar_clr, sar_clrdisp8, NULL, NULL);
		break;
	case M68K_LSR:
		dst = translate_shift(dst, inst, &src_op, &dst_op, opts, shr_ir, shr_irdisp8, shr_clr, shr_clrdisp8, shl_ir, shl_irdisp8);
		break;
	case M68K_BCHG:
	case M68K_BCLR:
	case M68K_BSET:
	case M68K_BTST:
		dst = cycles(dst, inst->extra.size == OPSIZE_BYTE ? 4 : (
			inst->op == M68K_BTST ? 6 : (inst->op == M68K_BCLR ? 10 : 8))
		);
		if (src_op.mode == MODE_IMMED) {
			if (inst->extra.size == OPSIZE_BYTE) {
				src_op.disp &= 0x7;
			}
			if (inst->op == M68K_BTST) {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = bt_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
				} else {
					dst = bt_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
				}
			} else if (inst->op == M68K_BSET) {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = bts_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
				} else {
					dst = bts_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
				}
			} else if (inst->op == M68K_BCLR) {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = btr_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
				} else {
					dst = btr_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
				}
			} else {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = btc_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
				} else {
					dst = btc_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
				}
			}
		} else {
			if (src_op.mode == MODE_REG_DISPLACE8 || (inst->dst.addr_mode != MODE_REG && src_op.base != SCRATCH1 && src_op.base != SCRATCH2)) {
				if (dst_op.base == SCRATCH1) {
					dst = push_r(dst, SCRATCH2);
					if (src_op.mode == MODE_REG_DIRECT) {
						dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_B);
					} else {
						dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_B);
					}
					src_op.base = SCRATCH2;
				} else {
					if (src_op.mode == MODE_REG_DIRECT) {
						dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_B);
					} else {
						dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_B);
					}
					src_op.base = SCRATCH1;
				}
			}
			uint8_t size = inst->extra.size;
			if (dst_op.mode == MODE_REG_DISPLACE8) {
				if (src_op.base != SCRATCH1 && src_op.base != SCRATCH2) {
					if (src_op.mode == MODE_REG_DIRECT) {
						dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_D);
					} else {
						dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D);
						src_op.mode = MODE_REG_DIRECT;
					}
					src_op.base = SCRATCH1;
				}
				//b### with register destination is modulo 32
				//x86 with a memory destination isn't modulo anything
				//so use an and here to force the value to be modulo 32
				dst = and_ir(dst, 31, SCRATCH1, SZ_D);
			} else if(inst->dst.addr_mode != MODE_REG) {
				//b### with memory destination is modulo 8
				//x86-64 doesn't support 8-bit bit operations
				//so we fake it by forcing the bit number to be modulo 8
				dst = and_ir(dst, 7, src_op.base, SZ_D);
				size = SZ_D;
			}
			if (inst->op == M68K_BTST) {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = bt_rr(dst, src_op.base, dst_op.base, size);
				} else {
					dst = bt_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
				}
			} else if (inst->op == M68K_BSET) {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = bts_rr(dst, src_op.base, dst_op.base, size);
				} else {
					dst = bts_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
				}
			} else if (inst->op == M68K_BCLR) {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = btr_rr(dst, src_op.base, dst_op.base, size);
				} else {
					dst = btr_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
				}
			} else {
				if (dst_op.mode == MODE_REG_DIRECT) {
					dst = btc_rr(dst, src_op.base, dst_op.base, size);
				} else {
					dst = btc_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
				}
			}
			if (src_op.base == SCRATCH2) {
				dst = pop_r(dst, SCRATCH2);
			}
		}
		//x86 sets the carry flag to the value of the bit tested
		//68K sets the zero flag to the complement of the bit tested
		dst = set_flag_cond(dst, CC_NC, FLAG_Z, opts);
		if (inst->op != M68K_BTST) {
			dst = m68k_save_result(inst, dst, opts);
		}
		break;
	case M68K_CHK:
	{
		dst = cycles(dst, 6);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
		} else {
			dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size);
		}
		uint32_t isize;
		switch(inst->src.addr_mode)
		{
		case MODE_AREG_DISPLACE:
		case MODE_AREG_INDEX_DISP8:
		case MODE_ABSOLUTE_SHORT:
		case MODE_PC_INDEX_DISP8:
		case MODE_PC_DISPLACE:
		case MODE_IMMEDIATE:
			isize = 4;
			break;
		case MODE_ABSOLUTE:
			isize = 6;
			break;
		default:
			isize = 2;
		}
		code_ptr passed = dst+1;
		dst = jcc(dst, CC_GE, dst+2);
		dst = set_flag(dst, 1, FLAG_N, opts);
		dst = mov_ir(dst, VECTOR_CHK, SCRATCH2, SZ_D);
		dst = mov_ir(dst, inst->address+isize, SCRATCH1, SZ_D);
		dst = jmp(dst, opts->trap);
		*passed = dst - (passed+1);
		if (dst_op.mode == MODE_REG_DIRECT) {
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = cmp_rr(dst, src_op.base, dst_op.base, inst->extra.size);
			} else if(src_op.mode == MODE_REG_DISPLACE8) {
				dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
			} else {
				dst = cmp_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
			}
		} else if(dst_op.mode == MODE_REG_DISPLACE8) {
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
			} else {
				dst = cmp_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
			}
		}
		passed = dst+1;
		dst = jcc(dst, CC_LE, dst+2);
		dst = set_flag(dst, 0, FLAG_N, opts);
		dst = mov_ir(dst, VECTOR_CHK, SCRATCH2, SZ_D);
		dst = mov_ir(dst, inst->address+isize, SCRATCH1, SZ_D);
		dst = jmp(dst, opts->trap);
		*passed = dst - (passed+1);
		dst = cycles(dst, 4);
		break;
	}
	case M68K_DIVS:
	case M68K_DIVU:
	{
		//TODO: cycle exact division
		dst = cycles(dst, inst->op == M68K_DIVS ? 158 : 140);
		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = push_r(dst, RDX);
		dst = push_r(dst, RAX);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_rr(dst, dst_op.base, RAX, SZ_D);
		} else {
			dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, RAX, SZ_D);
		}
		if (src_op.mode == MODE_IMMED) {
			dst = mov_ir(dst, (src_op.disp & 0x8000) && inst->op == M68K_DIVS ? src_op.disp | 0xFFFF0000 : src_op.disp, SCRATCH2, SZ_D);
		} else if (src_op.mode == MODE_REG_DIRECT) {
			if (inst->op == M68K_DIVS) {
				dst = movsx_rr(dst, src_op.base, SCRATCH2, SZ_W, SZ_D);
			} else {
				dst = movzx_rr(dst, src_op.base, SCRATCH2, SZ_W, SZ_D);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			if (inst->op == M68K_DIVS) {
				dst = movsx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_W, SZ_D);
			} else {
				dst = movzx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_W, SZ_D);
			}
		}
		dst = cmp_ir(dst, 0, SCRATCH2, SZ_D);
		code_ptr not_zero = dst+1;
		dst = jcc(dst, CC_NZ, dst+2);
		dst = pop_r(dst, RAX);
		dst = pop_r(dst, RDX);
		dst = mov_ir(dst, VECTOR_INT_DIV_ZERO, SCRATCH2, SZ_D);
		dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D);
		dst = jmp(dst, opts->trap);
		*not_zero = dst - (not_zero+1);
		if (inst->op == M68K_DIVS) {
			dst = cdq(dst);
		} else {
			dst = xor_rr(dst, RDX, RDX, SZ_D);
		}
		if (inst->op == M68K_DIVS) {
			dst = idiv_r(dst, SCRATCH2, SZ_D);
		} else {
			dst = div_r(dst, SCRATCH2, SZ_D);
		}
		code_ptr skip_sec_check;
		if (inst->op == M68K_DIVS) {
			dst = cmp_ir(dst, 0x8000, RAX, SZ_D);
			skip_sec_check = dst + 1;
			dst = jcc(dst, CC_GE, dst+2);
			dst = cmp_ir(dst, -0x8000, RAX, SZ_D);
			norm_off = dst+1;
			dst = jcc(dst, CC_L, dst+2);
		} else {
			dst = cmp_ir(dst, 0x10000, RAX, SZ_D);
			norm_off = dst+1;
			dst = jcc(dst, CC_NC, dst+2);
		}
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_rr(dst, RDX, dst_op.base, SZ_W);
			dst = shl_ir(dst, 16, dst_op.base, SZ_D);
			dst = mov_rr(dst, RAX, dst_op.base, SZ_W);
		} else {
			dst = mov_rrdisp8(dst, RDX, dst_op.base, dst_op.disp, SZ_W);
			dst = shl_irdisp8(dst, 16, dst_op.base, dst_op.disp, SZ_D);
			dst = mov_rrdisp8(dst, RAX, dst_op.base, dst_op.disp, SZ_W);
		}
		dst = cmp_ir(dst, 0, RAX, SZ_W);
		dst = pop_r(dst, RAX);
		dst = pop_r(dst, RDX);
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		end_off = dst+1;
		dst = jmp(dst, dst+2);
		*norm_off = dst - (norm_off + 1);
		if (inst->op == M68K_DIVS) {
			*skip_sec_check = dst - (skip_sec_check+1);
		}
		dst = pop_r(dst, RAX);
		dst = pop_r(dst, RDX);
		dst = set_flag(dst, 1, FLAG_V, opts);
		*end_off = dst - (end_off + 1);
		break;
	}
	case M68K_EOR:
		dst = cycles(dst, BUS);
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = xor_rr(dst, src_op.base, dst_op.base, inst->extra.size);
			} else {
				dst = xor_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = xor_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = xor_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
			} else {
				dst = xor_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
			}
		}
		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_EORI_CCR:
	case M68K_EORI_SR:
		dst = cycles(dst, 20);
		//TODO: If ANDI to SR, trap if not in supervisor mode
		if (inst->src.params.immed & 0x1) {
			dst = xor_flag(dst, 1, FLAG_C, opts);
		}
		if (inst->src.params.immed & 0x2) {
			dst = xor_flag(dst, 1, FLAG_V, opts);
		}
		if (inst->src.params.immed & 0x4) {
			dst = xor_flag(dst, 1, FLAG_Z, opts);
		}
		if (inst->src.params.immed & 0x8) {
			dst = xor_flag(dst, 1, FLAG_N, opts);
		}
		if (inst->src.params.immed & 0x10) {
			dst = xor_flag(dst, 1, FLAG_X, opts);
		}
		if (inst->op == M68K_ORI_SR) {
			dst = xor_irdisp8(dst, inst->src.params.immed >> 8, CONTEXT, offsetof(m68k_context, status), SZ_B);
			if (inst->src.params.immed & 0x700) {
				dst = call(dst, opts->do_sync);
			}
		}
		break;
	case M68K_EXG:
		dst = cycles(dst, 6);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_rr(dst, dst_op.base, SCRATCH2, SZ_D);
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, src_op.base, dst_op.base, SZ_D);
				dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, SZ_D);
				dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D);
			}
		} else {
			dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_D);
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = mov_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, SZ_D);
				dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D);
				dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D);
				dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D);
			}
		}
		break;
	case M68K_ILLEGAL:
		dst = call(dst, opts->gen.save_context);
#ifdef X86_64
		dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR);
#else
		dst = push_r(dst, CONTEXT);
#endif
		dst = call(dst, (code_ptr)print_regs_exit);
		break;
	case M68K_MOVE_FROM_SR:
		//TODO: Trap if not in system mode
		dst = call(dst, opts->get_sr);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_W);
		} else {
			dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_W);
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_MOVE_CCR:
	case M68K_MOVE_SR:
		//TODO: Privilege check for MOVE to SR
		if (src_op.mode == MODE_IMMED) {
			dst = set_flag(dst, src_op.disp & 0x1, FLAG_C, opts);
			dst = set_flag(dst, (src_op.disp >> 1) & 0x1, FLAG_V, opts);
			dst = set_flag(dst, (src_op.disp >> 2) & 0x1, FLAG_Z, opts);
			dst = set_flag(dst, (src_op.disp >> 3) & 0x1, FLAG_N, opts);
			dst = set_flag(dst, (src_op.disp >> 4) & 0x1, FLAG_X, opts);
			if (inst->op == M68K_MOVE_SR) {
				dst = mov_irdisp8(dst, (src_op.disp >> 8), CONTEXT, offsetof(m68k_context, status), SZ_B);
				if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) {
					//leave supervisor mode
					dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
					dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D);
					dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
				}
				dst = call(dst, opts->do_sync);
			}
			dst = cycles(dst, 12);
		} else {
			if (src_op.base != SCRATCH1) {
				if (src_op.mode == MODE_REG_DIRECT) {
					dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_W);
				} else {
					dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W);
				}
			}
			dst = call(dst, inst->op == M68K_MOVE_SR ? opts->set_sr : opts->set_ccr);
			dst = cycles(dst, 12);

		}
		break;
	case M68K_MOVE_USP:
		dst = cycles(dst, BUS);
		//TODO: Trap if not in supervisor mode
		//dst = bt_irdisp8(dst, BIT_SUPERVISOR, CONTEXT, offsetof(m68k_context, status), SZ_B);
		if (inst->src.addr_mode == MODE_UNUSED) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, dst_op.base, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SCRATCH1, SZ_D);
				dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D);
			}
		} else {
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = mov_rrdisp8(dst, src_op.base, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
			} else {
				dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D);
				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
			}
		}
		break;
	//case M68K_MOVEP:
	case M68K_MULS:
	case M68K_MULU:
		dst = cycles(dst, 70); //TODO: Calculate the actual value based on the value of the <ea> parameter
		if (src_op.mode == MODE_IMMED) {
			dst = mov_ir(dst, inst->op == M68K_MULU ? (src_op.disp & 0xFFFF) : ((src_op.disp & 0x8000) ? src_op.disp | 0xFFFF0000 : src_op.disp), SCRATCH1, SZ_D);
		} else if (src_op.mode == MODE_REG_DIRECT) {
			if (inst->op == M68K_MULS) {
				dst = movsx_rr(dst, src_op.base, SCRATCH1, SZ_W, SZ_D);
			} else {
				dst = movzx_rr(dst, src_op.base, SCRATCH1, SZ_W, SZ_D);
			}
		} else {
			if (inst->op == M68K_MULS) {
				dst = movsx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W, SZ_D);
			} else {
				dst = movzx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W, SZ_D);
			}
		}
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst_reg = dst_op.base;
			if (inst->op == M68K_MULS) {
				dst = movsx_rr(dst, dst_reg, dst_reg, SZ_W, SZ_D);
			} else {
				dst = movzx_rr(dst, dst_reg, dst_reg, SZ_W, SZ_D);
			}
		} else {
			dst_reg = SCRATCH2;
			if (inst->op == M68K_MULS) {
				dst = movsx_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_W, SZ_D);
			} else {
				dst = movzx_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_W, SZ_D);
			}
		}
		dst = imul_rr(dst, SCRATCH1, dst_reg, SZ_D);
		if (dst_op.mode == MODE_REG_DISPLACE8) {
			dst = mov_rrdisp8(dst, dst_reg, dst_op.base, dst_op.disp, SZ_D);
		}
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = cmp_ir(dst, 0, dst_reg, SZ_D);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		break;
	//case M68K_NBCD:
	case M68K_NEG:
		dst = cycles(dst, BUS);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = neg_r(dst, dst_op.base, inst->extra.size);
		} else {
			dst = neg_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
		}
		dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
		if (opts->flag_regs[FLAG_C] >= 0) {
			dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
		} else {
			dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_NEGX: {
		dst = cycles(dst, BUS);
		if (dst_op.mode == MODE_REG_DIRECT) {
			if (dst_op.base == SCRATCH1) {
				dst = push_r(dst, SCRATCH2);
				dst = xor_rr(dst, SCRATCH2, SCRATCH2, inst->extra.size);
				dst = flag_to_carry(dst, FLAG_X, opts);
				dst = sbb_rr(dst, dst_op.base, SCRATCH2, inst->extra.size);
				dst = mov_rr(dst, SCRATCH2, dst_op.base, inst->extra.size);
				dst = pop_r(dst, SCRATCH2);
			} else {
				dst = xor_rr(dst, SCRATCH1, SCRATCH1, inst->extra.size);
				dst = flag_to_carry(dst, FLAG_X, opts);
				dst = sbb_rr(dst, dst_op.base, SCRATCH1, inst->extra.size);
				dst = mov_rr(dst, SCRATCH1, dst_op.base, inst->extra.size);
			}
		} else {
			dst = xor_rr(dst, SCRATCH1, SCRATCH1, inst->extra.size);
			dst = flag_to_carry(dst, FLAG_X, opts);
			dst = sbb_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, inst->extra.size);
			dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, inst->extra.size);
		}
		dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
		code_ptr after_flag_set = dst+1;
		dst = jcc(dst, CC_Z, dst+2);
		dst = set_flag(dst, 0, FLAG_Z, opts);
		*after_flag_set = dst - (after_flag_set+1);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
		if (opts->flag_regs[FLAG_C] >= 0) {
			dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
		} else {
			dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	}
	case M68K_NOP:
		dst = cycles(dst, BUS);
		break;
	case M68K_NOT:
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = not_r(dst, dst_op.base, inst->extra.size);
			dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
		} else {
			dst = not_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
			dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size);
		}

		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_OR:
		dst = cycles(dst, BUS);
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = or_rr(dst, src_op.base, dst_op.base, inst->extra.size);
			} else {
				dst = or_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = or_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = or_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
			} else {
				dst = or_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
			}
		}
		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag(dst, 0, FLAG_V, opts);
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_ORI_CCR:
	case M68K_ORI_SR:
		dst = cycles(dst, 20);
		//TODO: If ANDI to SR, trap if not in supervisor mode
		if (inst->src.params.immed & 0x1) {
			dst = set_flag(dst, 1, FLAG_C, opts);
		}
		if (inst->src.params.immed & 0x2) {
			dst = set_flag(dst, 1, FLAG_V, opts);
		}
		if (inst->src.params.immed & 0x4) {
			dst = set_flag(dst, 1, FLAG_Z, opts);
		}
		if (inst->src.params.immed & 0x8) {
			dst = set_flag(dst, 1, FLAG_N, opts);
		}
		if (inst->src.params.immed & 0x10) {
			dst = set_flag(dst, 1, FLAG_X, opts);
		}
		if (inst->op == M68K_ORI_SR) {
			dst = or_irdisp8(dst, inst->src.params.immed >> 8, CONTEXT, offsetof(m68k_context, status), SZ_B);
			if (inst->src.params.immed & 0x700) {
				dst = call(dst, opts->do_sync);
			}
		}
		break;
	case M68K_RESET:
		dst = call(dst, opts->gen.save_context);
#ifdef X86_64
		dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR);
#else
		dst = push_r(dst, CONTEXT);
#endif
		dst = call(dst, (code_ptr)print_regs_exit);
		break;
	case M68K_ROL:
	case M68K_ROR:
		dst = set_flag(dst, 0, FLAG_V, opts);
		if (inst->src.addr_mode == MODE_UNUSED) {
			dst = cycles(dst, BUS);
			//Memory rotate
			if (inst->op == M68K_ROL) {
				dst = rol_ir(dst, 1, dst_op.base, inst->extra.size);
			} else {
				dst = ror_ir(dst, 1, dst_op.base, inst->extra.size);
			}
			dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
			dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
			dst = m68k_save_result(inst, dst, opts);
		} else {
			if (src_op.mode == MODE_IMMED) {
				dst = cycles(dst, (inst->extra.size == OPSIZE_LONG ? 8 : 6) + src_op.disp*2);
				if (dst_op.mode == MODE_REG_DIRECT) {
					if (inst->op == M68K_ROL) {
						dst = rol_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
					} else {
						dst = ror_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
					}
				} else {
					if (inst->op == M68K_ROL) {
						dst = rol_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
					} else {
						dst = ror_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
					}
				}
				dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
			} else {
				if (src_op.mode == MODE_REG_DIRECT) {
					if (src_op.base != SCRATCH1) {
						dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_B);
					}
				} else {
					dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_B);
				}
				dst = and_ir(dst, 63, SCRATCH1, SZ_D);
				zero_off = dst+1;
				dst = jcc(dst, CC_Z, dst+2);
				dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D);
				dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D);
				dst = cmp_ir(dst, 32, SCRATCH1, SZ_B);
				norm_off = dst+1;
				dst = jcc(dst, CC_L, dst+2);
				dst = sub_ir(dst, 32, SCRATCH1, SZ_B);
				if (dst_op.mode == MODE_REG_DIRECT) {
					if (inst->op == M68K_ROL) {
						dst = rol_ir(dst, 31, dst_op.base, inst->extra.size);
						dst = rol_ir(dst, 1, dst_op.base, inst->extra.size);
					} else {
						dst = ror_ir(dst, 31, dst_op.base, inst->extra.size);
						dst = ror_ir(dst, 1, dst_op.base, inst->extra.size);
					}
				} else {
					if (inst->op == M68K_ROL) {
						dst = rol_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size);
						dst = rol_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size);
					} else {
						dst = ror_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size);
						dst = ror_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size);
					}
				}
				*norm_off = dst - (norm_off+1);
				if (dst_op.mode == MODE_REG_DIRECT) {
					if (inst->op == M68K_ROL) {
						dst = rol_clr(dst, dst_op.base, inst->extra.size);
					} else {
						dst = ror_clr(dst, dst_op.base, inst->extra.size);
					}
				} else {
					if (inst->op == M68K_ROL) {
						dst = rol_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
					} else {
						dst = ror_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
					}
				}
				dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
				end_off = dst + 1;
				dst = jmp(dst, dst+2);
				*zero_off = dst - (zero_off+1);
				dst = set_flag(dst, 0, FLAG_C, opts);
				*end_off = dst - (end_off+1);
			}
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
			} else {
				dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size);
			}
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		break;
	case M68K_ROXL:
	case M68K_ROXR:
		dst = set_flag(dst, 0, FLAG_V, opts);
		if (inst->src.addr_mode == MODE_UNUSED) {
			dst = cycles(dst, BUS);
			//Memory rotate
			dst = flag_to_carry(dst, FLAG_X, opts);
			if (inst->op == M68K_ROXL) {
				dst = rcl_ir(dst, 1, dst_op.base, inst->extra.size);
			} else {
				dst = rcr_ir(dst, 1, dst_op.base, inst->extra.size);
			}
			dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
			if (opts->flag_regs[FLAG_C] < 0) {
				dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
			}
			dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
			if (opts->flag_regs[FLAG_C] >= 0) {
				dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
			}
			dst = m68k_save_result(inst, dst, opts);
		} else {
			if (src_op.mode == MODE_IMMED) {
				dst = cycles(dst, (inst->extra.size == OPSIZE_LONG ? 8 : 6) + src_op.disp*2);
				dst = flag_to_carry(dst, FLAG_X, opts);
				if (dst_op.mode == MODE_REG_DIRECT) {
					if (inst->op == M68K_ROXL) {
						dst = rcl_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
					} else {
						dst = rcr_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
					}
				} else {
					if (inst->op == M68K_ROXL) {
						dst = rcl_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
					} else {
						dst = rcr_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
					}
				}
				dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
				if (opts->flag_regs[FLAG_C] >= 0) {
					dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
				} else {
					dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
				}
			} else {
				if (src_op.mode == MODE_REG_DIRECT) {
					if (src_op.base != SCRATCH1) {
						dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_B);
					}
				} else {
					dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_B);
				}
				dst = and_ir(dst, 63, SCRATCH1, SZ_D);
				zero_off = dst+1;
				dst = jcc(dst, CC_Z, dst+2);
				dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D);
				dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D);
				dst = cmp_ir(dst, 32, SCRATCH1, SZ_B);
				norm_off = dst+1;
				dst = jcc(dst, CC_L, dst+2);
				dst = flag_to_carry(dst, FLAG_X, opts);
				if (dst_op.mode == MODE_REG_DIRECT) {
					if (inst->op == M68K_ROXL) {
						dst = rcl_ir(dst, 31, dst_op.base, inst->extra.size);
						dst = rcl_ir(dst, 1, dst_op.base, inst->extra.size);
					} else {
						dst = rcr_ir(dst, 31, dst_op.base, inst->extra.size);
						dst = rcr_ir(dst, 1, dst_op.base, inst->extra.size);
					}
				} else {
					if (inst->op == M68K_ROXL) {
						dst = rcl_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size);
						dst = rcl_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size);
					} else {
						dst = rcr_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size);
						dst = rcr_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size);
					}
				}
				dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
				dst = sub_ir(dst, 32, SCRATCH1, SZ_B);
				*norm_off = dst - (norm_off+1);
				dst = flag_to_carry(dst, FLAG_X, opts);
				if (dst_op.mode == MODE_REG_DIRECT) {
					if (inst->op == M68K_ROXL) {
						dst = rcl_clr(dst, dst_op.base, inst->extra.size);
					} else {
						dst = rcr_clr(dst, dst_op.base, inst->extra.size);
					}
				} else {
					if (inst->op == M68K_ROXL) {
						dst = rcl_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
					} else {
						dst = rcr_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
					}
				}
				dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
				if (opts->flag_regs[FLAG_C] >= 0) {
					dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
				} else {
					dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
				}
				end_off = dst + 1;
				dst = jmp(dst, dst+2);
				*zero_off = dst - (zero_off+1);
				//Carry flag is set to X flag when count is 0, this is different from ROR/ROL
				dst = flag_to_flag(dst, FLAG_X, FLAG_C, opts);
				*end_off = dst - (end_off+1);
			}
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
			} else {
				dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size);
			}
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		}
		break;
	case M68K_RTE:
		//TODO: Trap if not in system mode
		//Read saved SR
		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
		dst = call(dst, opts->read_16);
		dst = add_ir(dst, 2, opts->aregs[7], SZ_D);
		dst = call(dst, opts->set_sr);
		//Read saved PC
		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
		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);
		end_off = dst+1;
		dst = jcc(dst, CC_C, dst+2);
		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
		dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D);
		dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
		*end_off = dst - (end_off+1);
		//Get native address, sync components, recalculate integer points and jump to returned address
		dst = call(dst, opts->native_addr_and_sync);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case M68K_RTR:
		//Read saved CCR
		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
		dst = call(dst, opts->read_16);
		dst = add_ir(dst, 2, opts->aregs[7], SZ_D);
		dst = call(dst, opts->set_ccr);
		//Read saved PC
		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
		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, opts->native_addr);
		dst = jmp_r(dst, SCRATCH1);
		break;
	case M68K_SBCD: {
		if (src_op.base != SCRATCH2) {
			if (src_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_B);
			} else {
				dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_B);
			}
		}
		if (dst_op.base != SCRATCH1) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, dst_op.base, SCRATCH1, SZ_B);
			} else {
				dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, SZ_B);
			}
		}
		dst = flag_to_carry(dst, FLAG_X, opts);
		dst = jcc(dst, CC_NC, dst+5);
		dst = sub_ir(dst, 1, SCRATCH1, SZ_B);
		dst = call(dst, (code_ptr)bcd_sub);
		dst = reg_to_flag(dst, CH, FLAG_C, opts);
		dst = reg_to_flag(dst, CH, FLAG_X, opts);
		dst = cmp_ir(dst, 0, SCRATCH1, SZ_B);
		code_ptr after_flag_set = dst+1;
		dst = jcc(dst, CC_Z, dst+2);
		dst = set_flag(dst, 0, FLAG_Z, opts);
		*after_flag_set = dst - (after_flag_set+1);
		if (dst_op.base != SCRATCH1) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_B);
			} else {
				dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_B);
			}
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	}
	case M68K_STOP: {
		//TODO: Trap if not in system mode
		//manual says 4 cycles, but it has to be at least 8 since it's a 2-word instruction
		//possibly even 12 since that's how long MOVE to SR takes
		dst = cycles(dst, BUS*2);
		dst = set_flag(dst, src_op.disp & 0x1, FLAG_C, opts);
		dst = set_flag(dst, (src_op.disp >> 1) & 0x1, FLAG_V, opts);
		dst = set_flag(dst, (src_op.disp >> 2) & 0x1, FLAG_Z, opts);
		dst = set_flag(dst, (src_op.disp >> 3) & 0x1, FLAG_N, opts);
		dst = set_flag(dst, (src_op.disp >> 4) & 0x1, FLAG_X, opts);
		dst = mov_irdisp8(dst, (src_op.disp >> 8), CONTEXT, offsetof(m68k_context, status), SZ_B);
		if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) {
			//leave supervisor mode
			dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D);
			dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
		}
		code_ptr loop_top = dst;
		dst = call(dst, opts->do_sync);
    dst = cmp_rr(dst, LIMIT, CYCLES, SZ_D);
    code_ptr normal_cycle_up = dst + 1;
    dst = jcc(dst, CC_A, dst+2);
    dst = cycles(dst, BUS);
    code_ptr after_cycle_up = dst + 1;
    dst = jmp(dst, dst+2);
    *normal_cycle_up = dst - (normal_cycle_up + 1);
		dst = mov_rr(dst, LIMIT, CYCLES, SZ_D);
    *after_cycle_up = dst - (after_cycle_up+1);
		dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D);
		dst = jcc(dst, CC_C, loop_top);
		break;
	}
	case M68K_SUB:
		size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size;
		dst = cycles(dst, BUS);
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = sub_rr(dst, src_op.base, dst_op.base, size);
			} else {
				dst = sub_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = sub_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = sub_ir(dst, src_op.disp, dst_op.base, size);
			} else {
				dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size);
			}
		}
		if (inst->dst.addr_mode != MODE_AREG) {
			dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
			dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
			dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
			dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
			if (opts->flag_regs[FLAG_C] >= 0) {
				dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
			} else {
				dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
			}
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	case M68K_SUBX: {
		dst = cycles(dst, BUS);
		dst = flag_to_carry(dst, FLAG_X, opts);
		if (src_op.mode == MODE_REG_DIRECT) {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = sbb_rr(dst, src_op.base, dst_op.base, inst->extra.size);
			} else {
				dst = sbb_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
			}
		} else if (src_op.mode == MODE_REG_DISPLACE8) {
			dst = sbb_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
		} else {
			if (dst_op.mode == MODE_REG_DIRECT) {
				dst = sbb_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
			} else {
				dst = sbb_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
			}
		}
		dst = set_flag_cond(dst, CC_C, FLAG_C, opts);
		if (opts->flag_regs[FLAG_C] < 0) {
			dst = set_flag_cond(dst, CC_C, FLAG_X, opts);
		}
		code_ptr after_flag_set = dst+1;
		dst = jcc(dst, CC_Z, dst+2);
		dst = set_flag(dst, 0, FLAG_Z, opts);
		*after_flag_set = dst - (after_flag_set+1);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag_cond(dst, CC_O, FLAG_V, opts);
		if (opts->flag_regs[FLAG_C] >= 0) {
			dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts);
		}
		dst = m68k_save_result(inst, dst, opts);
		break;
	}
	case M68K_SWAP:
		dst = cycles(dst, BUS);
		if (src_op.mode == MODE_REG_DIRECT) {
			dst = rol_ir(dst, 16, src_op.base, SZ_D);
			dst = cmp_ir(dst, 0, src_op.base, SZ_D);
		} else{
			dst = rol_irdisp8(dst, 16, src_op.base, src_op.disp, SZ_D);
			dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, SZ_D);
		}

		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag(dst, 0, FLAG_V, opts);
		break;
	//case M68K_TAS:
	case M68K_TRAP:
		dst = mov_ir(dst, src_op.disp + VECTOR_TRAP_0, SCRATCH2, SZ_D);
		dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D);
		dst = jmp(dst, opts->trap);
		break;
	//case M68K_TRAPV:
	case M68K_TST:
		dst = cycles(dst, BUS);
		if (src_op.mode == MODE_REG_DIRECT) {
			dst = cmp_ir(dst, 0, src_op.base, inst->extra.size);
		} else { //M68000 doesn't support immedate operand for tst, so this must be MODE_REG_DISPLACE8
			dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, inst->extra.size);
		}
		dst = set_flag(dst, 0, FLAG_C, opts);
		dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts);
		dst = set_flag_cond(dst, CC_S, FLAG_N, opts);
		dst = set_flag(dst, 0, FLAG_V, opts);
		break;
	case M68K_UNLK:
		dst = cycles(dst, BUS);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_rr(dst, dst_op.base, opts->aregs[7], SZ_D);
		} else {
			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, opts->read_32);
		if (dst_op.mode == MODE_REG_DIRECT) {
			dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_D);
		} else {
			dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D);
		}
		dst = add_ir(dst, 4, opts->aregs[7], SZ_D);
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		printf("%X: %s\ninstruction %d not yet implemented\n", inst->address, disasm_buf, inst->op);
		exit(1);
	}
	return dst;
}

uint8_t m68k_is_terminal(m68kinst * inst)
{
	return inst->op == M68K_RTS || inst->op == M68K_RTE || inst->op == M68K_RTR || inst->op == M68K_JMP
		|| inst->op == M68K_TRAP || inst->op == M68K_ILLEGAL || inst->op == M68K_INVALID || inst->op == M68K_RESET
		|| (inst->op == M68K_BCC && inst->extra.cond == COND_TRUE);
}

void m68k_handle_deferred(m68k_context * context)
{
	x86_68k_options * opts = context->options;
	process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context);
	if (opts->gen.deferred) {
		translate_m68k_stream(opts->gen.deferred->address, context);
	}
}

code_ptr translate_m68k_stream(uint32_t address, m68k_context * context)
{
	m68kinst instbuf;
	x86_68k_options * opts = context->options;
	code_ptr dst = opts->gen.cur_code;
	code_ptr dst_end = opts->gen.code_end;
	address &= 0xFFFFFF;
	if(get_native_address(opts->gen.native_code_map, address)) {
		return dst;
	}
	char disbuf[1024];
	uint16_t *encoded, *next;
	if ((address & 0xFFFFFF) < 0x400000) {
		encoded = context->mem_pointers[0] + (address & 0xFFFFFF)/2;
	} else if ((address & 0xFFFFFF) > 0xE00000) {
		encoded = context->mem_pointers[1] + (address  & 0xFFFF)/2;
	} else {
		printf("attempt to translate non-memory address: %X\n", address);
		exit(1);
	}
	do {
		if (opts->address_log) {
			fprintf(opts->address_log, "%X\n", address);
		}
		do {
			if (dst_end-dst < MAX_NATIVE_SIZE) {
				if (dst_end-dst < 5) {
					puts("out of code memory, not enough space for jmp to next chunk");
					exit(1);
				}
				size_t size = 1024*1024;
				opts->gen.cur_code = alloc_code(&size);
				opts->gen.code_end = opts->gen.cur_code + size;
				jmp(dst, opts->gen.cur_code);
				dst = opts->gen.cur_code;
				dst_end = opts->gen.code_end;
			}
			if (address >= 0x400000 && address < 0xE00000) {
				dst = xor_rr(dst, RDI, RDI, SZ_D);
#ifdef X86_32
				dst = push_r(dst, RDI);
#endif
				dst = call(dst, (code_ptr)exit);
				break;
			}
			code_ptr existing = get_native_address(opts->gen.native_code_map, address);
			if (existing) {
				dst = jmp(dst, existing);
				break;
			}
			next = m68k_decode(encoded, &instbuf, address);
			if (instbuf.op == M68K_INVALID) {
				instbuf.src.params.immed = *encoded;
			}
			uint16_t m68k_size = (next-encoded)*2;
			address += m68k_size;
			encoded = next;
			//m68k_disasm(&instbuf, disbuf);
			//printf("%X: %s\n", instbuf.address, disbuf);
			code_ptr after = translate_m68k(dst, &instbuf, opts);
			map_native_address(context, instbuf.address, dst, m68k_size, after-dst);
			dst = after;
		} while(!m68k_is_terminal(&instbuf));
		process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context);
		if (opts->gen.deferred) {
			address = opts->gen.deferred->address;
			if ((address & 0xFFFFFF) < 0x400000) {
				encoded = context->mem_pointers[0] + (address & 0xFFFFFF)/2;
			} else if ((address & 0xFFFFFF) > 0xE00000) {
				encoded = context->mem_pointers[1] + (address  & 0xFFFF)/2;
			} else {
				printf("attempt to translate non-memory address: %X\n", address);
				exit(1);
			}
		} else {
			encoded = NULL;
		}
	} while(encoded != NULL);
	opts->gen.cur_code = dst;
	return dst;
}

code_ptr get_native_address_trans(m68k_context * context, uint32_t address)
{
	address &= 0xFFFFFF;
	code_ptr ret = get_native_address(context->native_code_map, address);
	if (!ret) {
		translate_m68k_stream(address, context);
		ret = get_native_address(context->native_code_map, address);
	}
	return ret;
}

void * m68k_retranslate_inst(uint32_t address, m68k_context * context)
{
	x86_68k_options * opts = context->options;
	uint8_t orig_size = get_native_inst_size(opts, address);
	code_ptr orig_start = get_native_address(context->native_code_map, address);
	uint32_t orig = address;
	address &= 0xFFFF;
	code_ptr dst = opts->gen.cur_code;
	code_ptr dst_end = opts->gen.code_end;
	uint16_t *after, *inst = context->mem_pointers[1] + address/2;
	m68kinst instbuf;
	after = m68k_decode(inst, &instbuf, orig);
	if (orig_size != MAX_NATIVE_SIZE) {
		if (dst_end - dst < 128) {
			size_t size = 1024*1024;
			dst = alloc_code(&size);
			opts->gen.code_end = dst_end = dst + size;
			opts->gen.cur_code = dst;
		}
		deferred_addr * orig_deferred = opts->gen.deferred;
		code_ptr native_end = translate_m68k(dst, &instbuf, opts);
		uint8_t is_terminal = m68k_is_terminal(&instbuf);
		if ((native_end - dst) <= orig_size) {
			code_ptr native_next;
			if (!is_terminal) {
				native_next = get_native_address(context->native_code_map, orig + (after-inst)*2);
			}
			if (is_terminal || (native_next && ((native_next == orig_start + orig_size) || (orig_size - (native_end - dst)) > 5))) {
				remove_deferred_until(&opts->gen.deferred, orig_deferred);
				native_end = translate_m68k(orig_start, &instbuf, opts);
				if (!is_terminal) {
					if (native_next == orig_start + orig_size && (native_next-native_end) < 2) {
						while (native_end < orig_start + orig_size) {
							*(native_end++) = 0x90; //NOP
						}
					} else {
						jmp(native_end, native_next);
					}
				}
				m68k_handle_deferred(context);
				return orig_start;
			}
		}

		map_native_address(context, instbuf.address, dst, (after-inst)*2, MAX_NATIVE_SIZE);
		opts->gen.cur_code = dst+MAX_NATIVE_SIZE;
		jmp(orig_start, dst);
		if (!m68k_is_terminal(&instbuf)) {
			jmp(native_end, get_native_address_trans(context, orig + (after-inst)*2));
		}
		m68k_handle_deferred(context);
		return dst;
	} else {
		dst = translate_m68k(orig_start, &instbuf, opts);
		if (!m68k_is_terminal(&instbuf)) {
			dst = jmp(dst, get_native_address_trans(context, orig + (after-inst)*2));
		}
		m68k_handle_deferred(context);
		return orig_start;
	}
}

m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context)
{
	uint32_t inst_start = get_instruction_start(context->native_code_map, address | 0xFF0000);
	if (inst_start) {
		code_ptr dst = get_native_address(context->native_code_map, inst_start);
		dst = mov_ir(dst, inst_start, SCRATCH2, SZ_D);
		x86_68k_options * options = context->options;
		if (!options->retrans_stub) {
			if (options->gen.code_end - options->gen.cur_code < 32) {
				size_t size = 1024*1024;
				options->gen.cur_code = alloc_code(&size);
				options->gen.code_end = options->gen.cur_code + size;
			}
			code_ptr rdst = options->retrans_stub = options->gen.cur_code;
			rdst = call(rdst, options->gen.save_context);
			rdst = push_r(rdst, CONTEXT);
#ifdef X86_32
			rdst = push_r(rdst, CONTEXT);
			rdst = push_r(rdst, SCRATCH2);
#endif
			rdst = call(rdst, (code_ptr)m68k_retranslate_inst);
#ifdef X86_32
			rdst = add_ir(rdst, 8, RSP, SZ_D);
#endif
			rdst = pop_r(rdst, CONTEXT);
			rdst = mov_rr(rdst, RAX, SCRATCH1, SZ_PTR);
			rdst = call(rdst, options->gen.load_context);
			rdst = jmp_r(rdst, SCRATCH1);
			options->gen.cur_code = rdst;
		}
		dst = jmp(dst, options->retrans_stub);
	}
	return context;
}

void insert_breakpoint(m68k_context * context, uint32_t address, code_ptr bp_handler)
{
	static code_ptr bp_stub = NULL;
	code_ptr native = get_native_address_trans(context, address);
	code_ptr start_native = native;
	native = mov_ir(native, address, SCRATCH1, SZ_D);
	if (!bp_stub) {
		x86_68k_options * opts = context->options;
		code_ptr dst = opts->gen.cur_code;
		code_ptr dst_end = opts->gen.code_end;
		if (dst_end - dst < 128) {
			size_t size = 1024*1024;
			dst = alloc_code(&size);
			opts->gen.code_end = dst_end = dst + size;
		}
		bp_stub = dst;
		native = call(native, bp_stub);

		//Calculate length of prologue
		dst = check_cycles_int(dst, address, opts);
		int check_int_size = dst-bp_stub;
		dst = bp_stub;

		//Save context and call breakpoint handler
		dst = call(dst, opts->gen.save_context);
		dst = push_r(dst, SCRATCH1);
#ifdef X86_64
		dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR);
		dst = mov_rr(dst, SCRATCH1, RSI, SZ_D);
#else
		dst = push_r(dst, SCRATCH1);
		dst = push_r(dst, CONTEXT);
#endif
		dst = call(dst, bp_handler);
#ifdef X86_32
		dst = add_ir(dst, 8, RSP, SZ_D);
#endif
		dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR);
		//Restore context
		dst = call(dst, opts->gen.load_context);
		dst = pop_r(dst, SCRATCH1);
		//do prologue stuff
		dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D);
		code_ptr jmp_off = dst+1;
		dst = jcc(dst, CC_NC, dst + 7);
		dst = call(dst, opts->gen.handle_cycle_limit_int);
		*jmp_off = dst - (jmp_off+1);
		//jump back to body of translated instruction
		dst = pop_r(dst, SCRATCH1);
		dst = add_ir(dst, check_int_size - (native-start_native), SCRATCH1, SZ_PTR);
		dst = jmp_r(dst, SCRATCH1);
		opts->gen.cur_code = dst;
	} else {
		native = call(native, bp_stub);
	}
}

void remove_breakpoint(m68k_context * context, uint32_t address)
{
	code_ptr native = get_native_address(context->native_code_map, address);
	check_cycles_int(native, address, context->options);
}

void start_68k_context(m68k_context * context, uint32_t address)
{
	code_ptr addr = get_native_address_trans(context, address);
	x86_68k_options * options = context->options;
	options->start_context(addr, context);
}

void m68k_reset(m68k_context * context)
{
	//TODO: Make this actually use the normal read functions
	context->aregs[7] = context->mem_pointers[0][0] << 16 | context->mem_pointers[0][1];
	uint32_t address = context->mem_pointers[0][2] << 16 | context->mem_pointers[0][3];
	start_68k_context(context, address);
}

code_ptr gen_mem_fun(cpu_options * opts, memmap_chunk * memmap, uint32_t num_chunks, ftype fun_type)
{
	code_ptr dst = opts->cur_code;
	code_ptr start = dst;
	dst = check_cycles(dst, opts);
	dst = cycles(dst, BUS);
	dst = and_ir(dst, 0xFFFFFF, SCRATCH1, SZ_D);
	code_ptr lb_jcc = NULL, ub_jcc = NULL;
	uint8_t is_write = fun_type == WRITE_16 || fun_type == WRITE_8;
	uint8_t adr_reg = is_write ? SCRATCH2 : SCRATCH1;
	uint16_t access_flag = is_write ? MMAP_WRITE : MMAP_READ;
	uint8_t size =  (fun_type == READ_16 || fun_type == WRITE_16) ? SZ_W : SZ_B;
	for (uint32_t chunk = 0; chunk < num_chunks; chunk++)
	{
		if (memmap[chunk].start > 0) {
			dst = cmp_ir(dst, memmap[chunk].start, adr_reg, 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, adr_reg, 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, adr_reg, SZ_D);
		}
		void * cfun;
		switch (fun_type)
		{
		case READ_16:
			cfun = memmap[chunk].read_16;
			break;
		case READ_8:
			cfun = memmap[chunk].read_8;
			break;
		case WRITE_16:
			cfun = memmap[chunk].write_16;
			break;
		case WRITE_8:
			cfun = memmap[chunk].write_8;
			break;
		default:
			cfun = NULL;
		}
		if(memmap[chunk].buffer && memmap[chunk].flags & access_flag) {
			if (memmap[chunk].flags & MMAP_PTR_IDX) {
				if (memmap[chunk].flags & MMAP_FUNC_NULL) {
					dst = cmp_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, SZ_PTR);
					code_ptr not_null = dst+1;
					dst = jcc(dst, CC_NZ, dst+2);
					dst = call(dst, opts->save_context);
#ifdef X86_64
					if (is_write) {
						if (SCRATCH2 != RDI) {
							dst = mov_rr(dst, SCRATCH2, RDI, SZ_D);
						}
						dst = mov_rr(dst, SCRATCH1, RDX, size);
					} else {
						dst = push_r(dst, CONTEXT);
						dst = mov_rr(dst, SCRATCH1, RDI, SZ_D);
					}
					dst = test_ir(dst, 8, RSP, SZ_D);
					code_ptr adjust_rsp = dst+1;
					dst = jcc(dst, CC_NZ, dst+2);
					dst = call(dst, cfun);
					code_ptr no_adjust = dst+1;
					dst = jmp(dst, dst+2);
					*adjust_rsp = dst - (adjust_rsp + 1);
					dst = sub_ir(dst, 8, RSP, SZ_PTR);
					dst = call(dst, cfun);
					dst = add_ir(dst, 8, RSP, SZ_PTR);
					*no_adjust = dst - (no_adjust + 1);
#else
					if (is_write) {
						dst = push_r(dst, SCRATCH1);
					} else {
						dst = push_r(dst, CONTEXT);//save CONTEXT for later
					}
					dst = push_r(dst, CONTEXT);
					dst = push_r(dst, is_write ? SCRATCH2 : SCRATCH1);
					dst = call(dst, cfun);
					dst = add_ir(dst, is_write ? 12 : 8, RSP, SZ_D);
#endif
					if (is_write) {
						dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR);
					} else {
						dst = pop_r(dst, CONTEXT);
						dst = mov_rr(dst, RAX, SCRATCH1, size);
					}
					dst = jmp(dst, opts->load_context);

					*not_null = dst - (not_null + 1);
				}
				if (size == SZ_B) {
					dst = xor_ir(dst, 1, adr_reg, SZ_D);
				}
				dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, adr_reg, SZ_PTR);
				if (is_write) {
					dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size);

				} else {
					dst = mov_rindr(dst, SCRATCH1, SCRATCH1, size);
				}
			} else {
				uint8_t tmp_size = size;
				if (size == SZ_B) {
					if ((memmap[chunk].flags & MMAP_ONLY_ODD) || (memmap[chunk].flags & MMAP_ONLY_EVEN)) {
						dst = bt_ir(dst, 0, adr_reg, SZ_D);
						code_ptr good_addr = dst + 1;
						dst = jcc(dst, (memmap[chunk].flags & MMAP_ONLY_ODD) ? CC_C : CC_NC, dst+2);
						if (!is_write) {
							dst = mov_ir(dst, 0xFF, SCRATCH1, SZ_B);
						}
						dst = retn(dst);
						*good_addr = dst - (good_addr + 1);
						dst = shr_ir(dst, 1, adr_reg, SZ_D);
					} else {
						dst = xor_ir(dst, 1, adr_reg, SZ_D);
					}
				} else if ((memmap[chunk].flags & MMAP_ONLY_ODD) || (memmap[chunk].flags & MMAP_ONLY_EVEN)) {
					tmp_size = SZ_B;
					dst = shr_ir(dst, 1, adr_reg, SZ_D);
					if ((memmap[chunk].flags & MMAP_ONLY_EVEN) && is_write) {
						dst = shr_ir(dst, 8, SCRATCH1, SZ_W);
					}
				}
				if ((intptr_t)memmap[chunk].buffer <= 0x7FFFFFFF && (intptr_t)memmap[chunk].buffer >= -2147483648) {
					if (is_write) {
						dst = mov_rrdisp32(dst, SCRATCH1, SCRATCH2, (intptr_t)memmap[chunk].buffer, tmp_size);
					} else {
						dst = mov_rdisp32r(dst, SCRATCH1, (intptr_t)memmap[chunk].buffer, SCRATCH1, tmp_size);
					}
				} else {
					if (is_write) {
						if (memmap[chunk].flags & MMAP_CODE) {
							dst = push_r(dst, SCRATCH2);
						}
						dst = push_r(dst, SCRATCH1);
						dst = mov_ir(dst, (intptr_t)memmap[chunk].buffer, SCRATCH1, SZ_PTR);
						dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_PTR);
						dst = pop_r(dst, SCRATCH1);
						dst = mov_rrind(dst, SCRATCH1, SCRATCH2, tmp_size);
						if (memmap[chunk].flags & MMAP_CODE) {
							dst = pop_r(dst, SCRATCH2);
						}
					} else {
						dst = mov_ir(dst, (intptr_t)memmap[chunk].buffer, SCRATCH2, SZ_PTR);
						dst = mov_rindexr(dst, SCRATCH2, SCRATCH1, 0, SCRATCH1, tmp_size);
					}
				}
				if (size != tmp_size && !is_write) {
					if (memmap[chunk].flags & MMAP_ONLY_EVEN) {
						dst = shl_ir(dst, 8, SCRATCH1, SZ_W);
						dst = mov_ir(dst, 0xFF, SCRATCH1, SZ_B);
					} else {
						dst = or_ir(dst, 0xFF00, SCRATCH1, SZ_W);
					}
				}
			}
			if (is_write && (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);
				code_ptr not_code = dst+1;
				dst = jcc(dst, CC_NC, dst+2);
				dst = call(dst, opts->save_context);
#ifdef X86_32
				dst = push_r(dst, CONTEXT);
				dst = push_r(dst, SCRATCH2);
#endif
				dst = call(dst, (code_ptr)m68k_handle_code_write);
#ifdef X86_32
				dst = add_ir(dst, 8, RSP, SZ_D);
#endif
				dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR);
				dst = call(dst, opts->load_context);
				*not_code = dst - (not_code+1);
			}
			dst = retn(dst);
		} else if (cfun) {
			dst = call(dst, opts->save_context);
#ifdef X86_64
			if (is_write) {
				if (SCRATCH2 != RDI) {
					dst = mov_rr(dst, SCRATCH2, RDI, SZ_D);
				}
				dst = mov_rr(dst, SCRATCH1, RDX, size);
			} else {
				dst = push_r(dst, CONTEXT);
				dst = mov_rr(dst, SCRATCH1, RDI, SZ_D);
			}
			dst = test_ir(dst, 8, RSP, SZ_D);
			code_ptr adjust_rsp = dst+1;
			dst = jcc(dst, CC_NZ, dst+2);
			dst = call(dst, cfun);
			code_ptr no_adjust = dst+1;
			dst = jmp(dst, dst+2);
			*adjust_rsp = dst - (adjust_rsp + 1);
			dst = sub_ir(dst, 8, RSP, SZ_PTR);
			dst = call(dst, cfun);
			dst = add_ir(dst, 8, RSP, SZ_PTR);
			*no_adjust = dst - (no_adjust+1);
#else
			if (is_write) {
				dst = push_r(dst, SCRATCH1);
			} else {
				dst = push_r(dst, CONTEXT);//save CONTEXT for later
			}
			dst = push_r(dst, CONTEXT);
			dst = push_r(dst, is_write ? SCRATCH2 : SCRATCH1);
			dst = call(dst, cfun);
			dst = add_ir(dst, is_write ? 12 : 8, RSP, SZ_D);
#endif
			if (is_write) {
				dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR);
			} else {
				dst = pop_r(dst, CONTEXT);
				dst = mov_rr(dst, RAX, SCRATCH1, size);
			}
			dst = jmp(dst, opts->load_context);
		} else {
			//Not sure the best course of action here
			if (!is_write) {
				dst = mov_ir(dst, size == SZ_B ? 0xFF : 0xFFFF, SCRATCH1, size);
			}
			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;
		}
	}
	if (!is_write) {
		dst = mov_ir(dst, size == SZ_B ? 0xFF : 0xFFFF, SCRATCH1, size);
	}
	dst = retn(dst);
	opts->cur_code = dst;
	return start;
}

void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks)
{
	memset(opts, 0, sizeof(*opts));
	for (int i = 0; i < 8; i++)
		opts->dregs[i] = opts->aregs[i] = -1;
#ifdef X86_64
	opts->dregs[0] = R10;
	opts->dregs[1] = R11;
	opts->dregs[2] = R12;
	opts->dregs[3] = R8;
	opts->aregs[0] = R13;
	opts->aregs[1] = R14;
	opts->aregs[2] = R9;
	opts->aregs[7] = R15;

	opts->flag_regs[0] = -1;
	opts->flag_regs[1] = RBX;
	opts->flag_regs[2] = RDX;
	opts->flag_regs[3] = BH;
	opts->flag_regs[4] = DH;
#else
	opts->dregs[0] = RDX;
	opts->aregs[7] = RDI;

	for (int i = 0; i < 5; i++)
		opts->flag_regs[i] = -1;
#endif


	opts->gen.native_code_map = malloc(sizeof(native_map_slot) * NATIVE_MAP_CHUNKS);
	memset(opts->gen.native_code_map, 0, sizeof(native_map_slot) * NATIVE_MAP_CHUNKS);
	opts->gen.deferred = NULL;
	size_t size = 1024 * 1024;
	opts->gen.cur_code = alloc_code(&size);
	opts->gen.code_end = opts->gen.cur_code + size;
	opts->gen.ram_inst_sizes = malloc(sizeof(code_ptr) * 64);
	memset(opts->gen.ram_inst_sizes, 0, sizeof(code_ptr) * 64);

	code_ptr dst = opts->gen.cur_code;

	opts->gen.save_context = dst;
	for (int i = 0; i < 5; i++)
		if (opts->flag_regs[i] >= 0) {
			dst = mov_rrdisp8(dst, opts->flag_regs[i], CONTEXT, offsetof(m68k_context, flags) + i, SZ_B);
		}
	for (int i = 0; i < 8; i++)
	{
		if (opts->dregs[i] >= 0) {
			dst = mov_rrdisp8(dst, opts->dregs[i], CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * i, SZ_D);
		}
		if (opts->aregs[i] >= 0) {
			dst = mov_rrdisp8(dst, opts->aregs[i], CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * i, SZ_D);
		}
	}
	dst = mov_rrdisp8(dst, CYCLES, CONTEXT, offsetof(m68k_context, current_cycle), SZ_D);
	dst = retn(dst);

	opts->gen.load_context = dst;
	for (int i = 0; i < 5; i++)
		if (opts->flag_regs[i] >= 0) {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + i, opts->flag_regs[i], SZ_B);
		}
	for (int i = 0; i < 8; i++)
	{
		if (opts->dregs[i] >= 0) {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * i, opts->dregs[i], SZ_D);
		}
		if (opts->aregs[i] >= 0) {
			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * i, opts->aregs[i], SZ_D);
		}
	}
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, current_cycle), CYCLES, SZ_D);
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, target_cycle), LIMIT, SZ_D);
	dst = retn(dst);

	opts->start_context = (start_fun)dst;
#ifdef X86_64
	if (SCRATCH2 != RDI) {
		dst = mov_rr(dst, RDI, SCRATCH2, SZ_PTR);
	}
	//save callee save registers
	dst = push_r(dst, RBP);
	dst = push_r(dst, R12);
	dst = push_r(dst, R13);
	dst = push_r(dst, R14);
	dst = push_r(dst, R15);
#else
	//save callee save registers
	dst = push_r(dst, RBP);
	dst = push_r(dst, RBX);
	dst = push_r(dst, RSI);
	dst = push_r(dst, RDI);

	dst = mov_rdisp8r(dst, RSP, 20, SCRATCH2, SZ_D);
	dst = mov_rdisp8r(dst, RSP, 24, CONTEXT, SZ_D);
#endif
	dst = call(dst, opts->gen.load_context);
	dst = call_r(dst, SCRATCH2);
	dst = call(dst, opts->gen.save_context);
#ifdef X86_64
	//restore callee save registers
	dst = pop_r(dst, R15);
	dst = pop_r(dst, R14);
	dst = pop_r(dst, R13);
	dst = pop_r(dst, R12);
	dst = pop_r(dst, RBP);
#else
	dst = pop_r(dst, RDI);
	dst = pop_r(dst, RSI);
	dst = pop_r(dst, RBX);
	dst = pop_r(dst, RBP);
#endif
	dst = retn(dst);

	opts->native_addr = dst;
	dst = call(dst, opts->gen.save_context);
	dst = push_r(dst, CONTEXT);
#ifdef X86_64
	dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); //move context to 1st arg reg
	dst = mov_rr(dst, SCRATCH1, RSI, SZ_D); //move address to 2nd arg reg
#else
	dst = push_r(dst, SCRATCH1);
	dst = push_r(dst, CONTEXT);
#endif
	dst = call(dst, (code_ptr)get_native_address_trans);
#ifdef X86_32
	dst = add_ir(dst, 8, RSP, SZ_D);
#endif
	dst = mov_rr(dst, RAX, SCRATCH1, SZ_PTR); //move result to scratch reg
	dst = pop_r(dst, CONTEXT);
	dst = call(dst, opts->gen.load_context);
	dst = retn(dst);

	opts->native_addr_and_sync = dst;
	dst = call(dst, opts->gen.save_context);
	dst = push_r(dst, SCRATCH1);
#ifdef X86_64
	dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR);
	dst = xor_rr(dst, RSI, RSI, SZ_D);
	dst = test_ir(dst, 8, RSP, SZ_PTR); //check stack alignment
	code_ptr do_adjust_rsp = dst+1;
	dst = jcc(dst, CC_NZ, dst+2);
	dst = call(dst, (code_ptr)sync_components);
	code_ptr no_adjust_rsp = dst+1;
	dst = jmp(dst, dst+2);
	*do_adjust_rsp = dst - (do_adjust_rsp+1);
	dst = sub_ir(dst, 8, RSP, SZ_PTR);
	dst = call(dst, (code_ptr)sync_components);
	dst = add_ir(dst, 8, RSP, SZ_PTR);
	*no_adjust_rsp = dst - (no_adjust_rsp+1);
	dst = pop_r(dst, RSI);
	dst = push_r(dst, RAX);
	dst = mov_rr(dst, RAX, RDI, SZ_PTR);
	dst = call(dst, (code_ptr)get_native_address_trans);
#else
	//TODO: Add support for pushing a constant in gen_x86
	dst = xor_rr(dst, RAX, RAX, SZ_D);
	dst = push_r(dst, RAX);
	dst = push_r(dst, CONTEXT);
	dst = call(dst, (code_ptr)sync_components);
	dst = add_ir(dst, 8, RSP, SZ_D);
	dst = pop_r(dst, RSI); //restore saved address from SCRATCH1
	dst = push_r(dst, RAX); //save context pointer for later
	dst = push_r(dst, RSI); //2nd arg -- address
	dst = push_r(dst, RAX); //1st arg -- context pointer
	dst = call(dst, (code_ptr)get_native_address_trans);
	dst = add_ir(dst, 8, RSP, SZ_D);
#endif

	dst = mov_rr(dst, RAX, SCRATCH1, SZ_PTR); //move result to scratch reg
	dst = pop_r(dst, CONTEXT);
	dst = call(dst, opts->gen.load_context);
	dst = retn(dst);

	opts->gen.handle_cycle_limit = dst;
	dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, sync_cycle), CYCLES, SZ_D);
	code_ptr skip_sync = dst+1;
	dst = jcc(dst, CC_C, dst+2);
	opts->do_sync = dst;
	dst = push_r(dst, SCRATCH1);
	dst = push_r(dst, SCRATCH2);
	dst = call(dst, opts->gen.save_context);
#ifdef X86_64
	dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR);
	dst = xor_rr(dst, RSI, RSI, SZ_D);
	dst = test_ir(dst, 8, RSP, SZ_D);
	code_ptr adjust_rsp = dst+1;
	dst = jcc(dst, CC_NZ, dst+2);
	dst = call(dst, (code_ptr)sync_components);
	code_ptr no_adjust = dst+1;
	dst = jmp(dst, dst+2);
	*adjust_rsp = dst - (adjust_rsp + 1);
	dst = sub_ir(dst, 8, RSP, SZ_PTR);
	dst = call(dst, (code_ptr)sync_components);
	dst = add_ir(dst, 8, RSP, SZ_PTR);
	*no_adjust = dst - (no_adjust+1);
#else
	//TODO: Add support for pushing a constant in gen_x86
	dst = xor_rr(dst, RAX, RAX, SZ_D);
	dst = push_r(dst, RAX);
	dst = push_r(dst, CONTEXT);
	dst = call(dst, (code_ptr)sync_components);
	dst = add_ir(dst, 8, RSP, SZ_D);
#endif
	dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR);
	dst = call(dst, opts->gen.load_context);
	dst = pop_r(dst, SCRATCH2);
	dst = pop_r(dst, SCRATCH1);
	*skip_sync = dst - (skip_sync+1);
	dst = retn(dst);

	opts->gen.cur_code = dst;

	opts->read_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_16);
	opts->read_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_8);
	opts->write_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_16);
	opts->write_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_8);

	dst = opts->gen.cur_code;

	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->get_sr = dst;
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, status), SCRATCH1, SZ_B);
	dst = shl_ir(dst, 8, SCRATCH1, SZ_W);
	if (opts->flag_regs[FLAG_X] >= 0) {
		dst = mov_rr(dst, opts->flag_regs[FLAG_X], SCRATCH1, SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags);
		if (offset) {
			dst = mov_rdisp8r(dst, CONTEXT, offset, SCRATCH1, SZ_B);
		} else {
			dst = mov_rindr(dst, CONTEXT, SCRATCH1, SZ_B);
		}
	}
	for (int flag = FLAG_N; flag <= FLAG_C; flag++)
	{
		dst = shl_ir(dst, 1, SCRATCH1, SZ_B);
		if (opts->flag_regs[flag] >= 0) {
			dst = or_rr(dst, opts->flag_regs[flag], SCRATCH1, SZ_B);
		} else {
			dst = or_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag, SCRATCH1, SZ_B);
		}
	}
	dst = retn(dst);

	opts->set_sr = dst;
	for (int flag = FLAG_C; flag >= FLAG_X; flag--)
	{
		dst = rcr_ir(dst, 1, SCRATCH1, SZ_B);
		if (opts->flag_regs[flag] >= 0) {
			dst = setcc_r(dst, CC_C, opts->flag_regs[flag]);
		} else {
			int8_t offset = offsetof(m68k_context, flags) + flag;
			if (offset) {
				dst = setcc_rdisp8(dst, CC_C, CONTEXT, offset);
			} else {
				dst = setcc_rind(dst, CC_C, CONTEXT);
			}
		}
	}
	dst = shr_ir(dst, 8, SCRATCH1, SZ_W);
	dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, status), SZ_B);
	dst = retn(dst);

	opts->set_ccr = dst;
	for (int flag = FLAG_C; flag >= FLAG_X; flag--)
	{
		dst = rcr_ir(dst, 1, SCRATCH1, SZ_B);
		if (opts->flag_regs[flag] >= 0) {
			dst = setcc_r(dst, CC_C, opts->flag_regs[flag]);
		} else {
			int8_t offset = offsetof(m68k_context, flags) + flag;
			if (offset) {
				dst = setcc_rdisp8(dst, CC_C, CONTEXT, offset);
			} else {
				dst = setcc_rind(dst, CC_C, CONTEXT);
			}
		}
	}
	dst = retn(dst);

	opts->gen.handle_cycle_limit_int = dst;
	dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D);
	code_ptr do_int = dst+1;
	dst = jcc(dst, CC_NC, dst+2);
	dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, sync_cycle), CYCLES, SZ_D);
	skip_sync = dst+1;
	dst = jcc(dst, CC_C, dst+2);
	dst = call(dst, opts->gen.save_context);
#ifdef X86_64
	dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR);
	dst = mov_rr(dst, SCRATCH1, RSI, SZ_D);
	dst = test_ir(dst, 8, RSP, SZ_D);
	adjust_rsp = dst+1;
	dst = jcc(dst, CC_NZ, dst+2);
	dst = call(dst, (code_ptr)sync_components);
	no_adjust = dst+1;
	dst = jmp(dst, dst+2);
	*adjust_rsp = dst - (adjust_rsp + 1);
	dst = sub_ir(dst, 8, RSP, SZ_PTR);
	dst = call(dst, (code_ptr)sync_components);
	dst = add_ir(dst, 8, RSP, SZ_PTR);
	*no_adjust = dst - (no_adjust+1);
#else
	dst = push_r(dst, SCRATCH1);
	dst = push_r(dst, CONTEXT);
	dst = call(dst, (code_ptr)sync_components);
	dst = add_ir(dst, 8, RSP, SZ_D);
#endif
	dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR);
	dst = jmp(dst, opts->gen.load_context);
	*skip_sync = dst - (skip_sync+1);
	dst = retn(dst);
	*do_int = dst - (do_int+1);
	//set target cycle to sync cycle
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, sync_cycle), LIMIT, SZ_D);
	//swap USP and SSP if not already in supervisor mode
	dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B);
	code_ptr already_supervisor = dst+1;
	dst = jcc(dst, CC_C, dst+2);
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SCRATCH2, SZ_D);
	dst = mov_rrdisp8(dst, opts->aregs[7], CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
	dst = mov_rr(dst, SCRATCH2, opts->aregs[7], SZ_D);
	*already_supervisor = dst - (already_supervisor+1);
	//save PC
	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	dst = call(dst, opts->write_32_lowfirst);
	//save status register
	dst = sub_ir(dst, 2, opts->aregs[7], SZ_D);
	dst = call(dst, opts->get_sr);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	dst = call(dst, opts->write_16);
	//update status register
	dst = and_irdisp8(dst, 0xF8, CONTEXT, offsetof(m68k_context, status), SZ_B);
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_num), SCRATCH1, SZ_B);
	dst = or_ir(dst, 0x20, SCRATCH1, SZ_B);
	dst = or_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, status), SZ_B);
	//calculate interrupt vector address
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_num), SCRATCH1, SZ_D);
	dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, int_ack), SZ_W);
	dst = shl_ir(dst, 2, SCRATCH1, SZ_D);
	dst = add_ir(dst, 0x60, SCRATCH1, SZ_D);
	dst = call(dst, opts->read_32);
	dst = call(dst, opts->native_addr_and_sync);
	dst = cycles(dst, 24);
	//discard function return address
	dst = pop_r(dst, SCRATCH2);
	dst = jmp_r(dst, SCRATCH1);

	opts->trap = dst;
	dst = push_r(dst, SCRATCH2);
	//swap USP and SSP if not already in supervisor mode
	dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B);
	already_supervisor = dst+1;
	dst = jcc(dst, CC_C, dst+2);
	dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SCRATCH2, SZ_D);
	dst = mov_rrdisp8(dst, opts->aregs[7], CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
	dst = mov_rr(dst, SCRATCH2, opts->aregs[7], SZ_D);
	*already_supervisor = dst - (already_supervisor+1);
	//save PC
	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	dst = call(dst, opts->write_32_lowfirst);
	//save status register
	dst = sub_ir(dst, 2, opts->aregs[7], SZ_D);
	dst = call(dst, opts->get_sr);
	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
	dst = call(dst, opts->write_16);
	//set supervisor bit
	dst = or_irdisp8(dst, 0x20, CONTEXT, offsetof(m68k_context, status), SZ_B);
	//calculate vector address
	dst = pop_r(dst, SCRATCH1);
	dst = shl_ir(dst, 2, SCRATCH1, SZ_D);
	dst = call(dst, opts->read_32);
	dst = call(dst, opts->native_addr_and_sync);
	dst = cycles(dst, 18);
	dst = jmp_r(dst, SCRATCH1);

	opts->gen.cur_code = dst;
}

void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts)
{
	memset(context, 0, sizeof(m68k_context));
	context->native_code_map = native_code_map;
	context->options = opts;
	context->int_cycle = 0xFFFFFFFF;
	context->status = 0x27;
}