/*
 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_core.h"
#include "m68k_internal.h"
#include "68kinst.h"
#include "mem.h"
#include "backend.h"
#include "util.h"
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

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

void set_flag(m68k_options * opts, uint8_t val, uint8_t flag)
{
	if (opts->flag_regs[flag] >= 0) {
		mov_ir(&opts->gen.code, val, opts->flag_regs[flag], SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			mov_irdisp(&opts->gen.code, val, opts->gen.context_reg, offset, SZ_B);
		} else {
			mov_irind(&opts->gen.code, val, opts->gen.context_reg, SZ_B);
		}
	}
}

void set_flag_cond(m68k_options *opts, uint8_t cond, uint8_t flag)
{
	if (opts->flag_regs[flag] >= 0) {
		setcc_r(&opts->gen.code, cond, opts->flag_regs[flag]);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			setcc_rdisp(&opts->gen.code, cond, opts->gen.context_reg, offset);
		} else {
			setcc_rind(&opts->gen.code, cond, opts->gen.context_reg);
		}
	}
}

void check_flag(m68k_options *opts, uint8_t flag)
{
	if (opts->flag_regs[flag] >= 0) {
		cmp_ir(&opts->gen.code, 0, opts->flag_regs[flag], SZ_B);
	} else {
		cmp_irdisp(&opts->gen.code, 0, opts->gen.context_reg, offsetof(m68k_context, flags) + flag, SZ_B);
	}
}

void flag_to_reg(m68k_options *opts, uint8_t flag, uint8_t reg)
{
	if (opts->flag_regs[flag] >= 0) {
		mov_rr(&opts->gen.code, opts->flag_regs[flag], reg, SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			mov_rdispr(&opts->gen.code, opts->gen.context_reg, offset, reg, SZ_B);
		} else {
			mov_rindr(&opts->gen.code, opts->gen.context_reg, reg, SZ_B);
		}
	}
}

void reg_to_flag(m68k_options *opts, uint8_t reg, uint8_t flag)
{
	if (opts->flag_regs[flag] >= 0) {
		mov_rr(&opts->gen.code, reg, opts->flag_regs[flag], SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags) + flag;
		if (offset) {
			mov_rrdisp(&opts->gen.code, reg, opts->gen.context_reg, offset, SZ_B);
		} else {
			mov_rrind(&opts->gen.code, reg, opts->gen.context_reg, SZ_B);
		}
	}
}

void flag_to_flag(m68k_options *opts, uint8_t flag1, uint8_t flag2)
{
	code_info *code = &opts->gen.code;
	if (opts->flag_regs[flag1] >= 0 && opts->flag_regs[flag2] >= 0) {
		mov_rr(code, opts->flag_regs[flag1], opts->flag_regs[flag2], SZ_B);
	} else if(opts->flag_regs[flag1] >= 0) {
		mov_rrdisp(code, opts->flag_regs[flag1], opts->gen.context_reg, offsetof(m68k_context, flags) + flag2, SZ_B);
	} else if (opts->flag_regs[flag2] >= 0) {
		mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, flags) + flag1, opts->flag_regs[flag2], SZ_B);
	} else {
		push_r(code, opts->gen.scratch1);
		mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, flags) + flag1, opts->gen.scratch1, SZ_B);
		mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, flags) + flag2, SZ_B);
		pop_r(code, opts->gen.scratch1);
	}
}

void update_flags(m68k_options *opts, uint32_t update_mask)
{
	uint8_t native_flags[] = {0, CC_S, CC_Z, CC_O, CC_C};
	for (int8_t flag = FLAG_C; flag >= FLAG_X; --flag)
	{
		if (update_mask & X0 << (flag*3)) {
			set_flag(opts, 0, flag);
		} else if(update_mask & X1 << (flag*3)) {
			set_flag(opts, 1, flag);
		} else if(update_mask & X << (flag*3)) {
			if (flag == FLAG_X) {
				if (opts->flag_regs[FLAG_C] >= 0 || !(update_mask & (C0|C1|C))) {
					flag_to_flag(opts, FLAG_C, FLAG_X);
				} else if(update_mask & C0) {
					set_flag(opts, 0, flag);
				} else if(update_mask & C1) {
					set_flag(opts, 1, flag);
				} else {
					set_flag_cond(opts, CC_C, flag);
				}
			} else {
				set_flag_cond(opts, native_flags[flag], flag);
			}
		}
	}
}

void flag_to_carry(m68k_options * opts, uint8_t flag)
{
	if (opts->flag_regs[flag] >= 0) {
		bt_ir(&opts->gen.code, 0, opts->flag_regs[flag], SZ_B);
	} else {
		bt_irdisp(&opts->gen.code, 0, opts->gen.context_reg, offsetof(m68k_context, flags) + flag, SZ_B);
	}
}

void or_flag_to_reg(m68k_options *opts, uint8_t flag, uint8_t reg)
{
	if (opts->flag_regs[flag] >= 0) {
		or_rr(&opts->gen.code, opts->flag_regs[flag], reg, SZ_B);
	} else {
		or_rdispr(&opts->gen.code, opts->gen.context_reg, offsetof(m68k_context, flags) + flag, reg, SZ_B);
	}
}

void xor_flag_to_reg(m68k_options *opts, uint8_t flag, uint8_t reg)
{
	if (opts->flag_regs[flag] >= 0) {
		xor_rr(&opts->gen.code, opts->flag_regs[flag], reg, SZ_B);
	} else {
		xor_rdispr(&opts->gen.code, opts->gen.context_reg, offsetof(m68k_context, flags) + flag, reg, SZ_B);
	}
}

void xor_flag(m68k_options *opts, uint8_t val, uint8_t flag)
{
	if (opts->flag_regs[flag] >= 0) {
		xor_ir(&opts->gen.code, val, opts->flag_regs[flag], SZ_B);
	} else {
		xor_irdisp(&opts->gen.code, val, opts->gen.context_reg, offsetof(m68k_context, flags) + flag, SZ_B);
	}
}

void cmp_flags(m68k_options *opts, uint8_t flag1, uint8_t flag2)
{
	code_info *code = &opts->gen.code;
	if (opts->flag_regs[flag1] >= 0 && opts->flag_regs[flag2] >= 0) {
		cmp_rr(code, 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;
		}
		cmp_rrdisp(code, opts->flag_regs[flag1], opts->gen.context_reg, offsetof(m68k_context, flags) + flag2, SZ_B);
	} else {
		mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, flags) + flag1, opts->gen.scratch1, SZ_B);
		cmp_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, flags) + flag2, SZ_B);
	}
}

void areg_to_native(m68k_options *opts, uint8_t reg, uint8_t native_reg)
{
	if (opts->aregs[reg] >= 0) {
		mov_rr(&opts->gen.code, opts->aregs[reg], native_reg, SZ_D);
	} else {
		mov_rdispr(&opts->gen.code, opts->gen.context_reg,  areg_offset(reg), native_reg, SZ_D);
	}
}

void dreg_to_native(m68k_options *opts, uint8_t reg, uint8_t native_reg)
{
	if (opts->dregs[reg] >= 0) {
		mov_rr(&opts->gen.code, opts->dregs[reg], native_reg, SZ_D);
	} else {
		mov_rdispr(&opts->gen.code, opts->gen.context_reg,  dreg_offset(reg), native_reg, SZ_D);
	}
}

void areg_to_native_sx(m68k_options *opts, uint8_t reg, uint8_t native_reg)
{
	if (opts->aregs[reg] >= 0) {
		movsx_rr(&opts->gen.code, opts->aregs[reg], native_reg, SZ_W, SZ_D);
	} else {
		movsx_rdispr(&opts->gen.code, opts->gen.context_reg,  areg_offset(reg), native_reg, SZ_W, SZ_D);
	}
}

void dreg_to_native_sx(m68k_options *opts, uint8_t reg, uint8_t native_reg)
{
	if (opts->dregs[reg] >= 0) {
		movsx_rr(&opts->gen.code, opts->dregs[reg], native_reg, SZ_W, SZ_D);
	} else {
		movsx_rdispr(&opts->gen.code, opts->gen.context_reg,  dreg_offset(reg), native_reg, SZ_W, SZ_D);
	}
}

void native_to_areg(m68k_options *opts, uint8_t native_reg, uint8_t reg)
	{
	if (opts->aregs[reg] >= 0) {
		mov_rr(&opts->gen.code, native_reg, opts->aregs[reg], SZ_D);
	} else {
		mov_rrdisp(&opts->gen.code, native_reg, opts->gen.context_reg, areg_offset(reg), SZ_D);
	}
}

void native_to_dreg(m68k_options *opts, uint8_t native_reg, uint8_t reg)
{
	if (opts->dregs[reg] >= 0) {
		mov_rr(&opts->gen.code, native_reg, opts->dregs[reg], SZ_D);
	} else {
		mov_rrdisp(&opts->gen.code, native_reg, opts->gen.context_reg, dreg_offset(reg), SZ_D);
	}
}

void ldi_areg(m68k_options *opts, int32_t value, uint8_t reg)
{
	if (opts->aregs[reg] >= 0) {
		mov_ir(&opts->gen.code, value, opts->aregs[reg], SZ_D);
	} else {
		mov_irdisp(&opts->gen.code, value, opts->gen.context_reg, areg_offset(reg), SZ_D);
	}
}

void ldi_native(m68k_options *opts, int32_t value, uint8_t reg)
{
	mov_ir(&opts->gen.code, value, reg, SZ_D);
}

void addi_native(m68k_options *opts, int32_t value, uint8_t reg)
{
	add_ir(&opts->gen.code, value, reg, SZ_D);
			}

void subi_native(m68k_options *opts, int32_t value, uint8_t reg)
{
	sub_ir(&opts->gen.code, value, reg, SZ_D);
}

void push_native(m68k_options *opts, uint8_t reg)
{
	push_r(&opts->gen.code, reg);
}

void pop_native(m68k_options *opts, uint8_t reg)
{
	pop_r(&opts->gen.code, reg);
}

void sign_extend16_native(m68k_options *opts, uint8_t reg)
{
	movsx_rr(&opts->gen.code, reg, reg, SZ_W, SZ_D);
}

void addi_areg(m68k_options *opts, int32_t val, uint8_t reg)
{
	if (opts->aregs[reg] >= 0) {
		add_ir(&opts->gen.code, val, opts->aregs[reg], SZ_D);
	} else {
		add_irdisp(&opts->gen.code, val, opts->gen.context_reg, areg_offset(reg), SZ_D);
	}
}

void subi_areg(m68k_options *opts, int32_t val, uint8_t reg)
{
	if (opts->aregs[reg] >= 0) {
		sub_ir(&opts->gen.code, val, opts->aregs[reg], SZ_D);
	} else {
		sub_irdisp(&opts->gen.code, val, opts->gen.context_reg, areg_offset(reg), SZ_D);
	}
}

void add_areg_native(m68k_options *opts, uint8_t reg, uint8_t native_reg)
{
	if (opts->aregs[reg] >= 0) {
		add_rr(&opts->gen.code, opts->aregs[reg], native_reg, SZ_D);
	} else {
		add_rdispr(&opts->gen.code, opts->gen.context_reg, areg_offset(reg), native_reg, SZ_D);
	}
}

void add_dreg_native(m68k_options *opts, uint8_t reg, uint8_t native_reg)
{
	if (opts->dregs[reg] >= 0) {
		add_rr(&opts->gen.code, opts->dregs[reg], native_reg, SZ_D);
	} else {
		add_rdispr(&opts->gen.code, opts->gen.context_reg, dreg_offset(reg), native_reg, SZ_D);
	}
}

void calc_areg_displace(m68k_options *opts, m68k_op_info *op, uint8_t native_reg)
{
	areg_to_native(opts, op->params.regs.pri, native_reg);
	add_ir(&opts->gen.code, op->params.regs.displacement & 0x8000 ? op->params.regs.displacement | 0xFFFF0000 : op->params.regs.displacement, native_reg, SZ_D);
}

void calc_index_disp8(m68k_options *opts, m68k_op_info *op, uint8_t native_reg)
{
	uint8_t sec_reg = (op->params.regs.sec >> 1) & 0x7;
	if (op->params.regs.sec & 1) {
		if (op->params.regs.sec & 0x10) {
			add_areg_native(opts, sec_reg, native_reg);
		} else {
			add_dreg_native(opts, sec_reg, native_reg);
		}
	} else {
		uint8_t other_reg = native_reg == opts->gen.scratch1 ? opts->gen.scratch2 : opts->gen.scratch1;
		if (op->params.regs.sec & 0x10) {
			areg_to_native_sx(opts, sec_reg, other_reg);
		} else {
			dreg_to_native_sx(opts, sec_reg, other_reg);
		}
		add_rr(&opts->gen.code, other_reg, native_reg, SZ_D);
	}
	if (op->params.regs.displacement) {
		add_ir(&opts->gen.code, op->params.regs.displacement, native_reg, SZ_D);
	}
}

void calc_areg_index_disp8(m68k_options *opts, m68k_op_info *op, uint8_t native_reg)
{
	areg_to_native(opts, op->params.regs.pri, native_reg);
	calc_index_disp8(opts, op, native_reg);
}

void m68k_check_cycles_int_latch(m68k_options *opts)
{
	code_info *code = &opts->gen.code;
	check_alloc_code(code, 3*MAX_INST_LEN);
	uint8_t cc;
	if (opts->gen.limit < 0) {
		cmp_ir(code, 1, opts->gen.cycles, SZ_D);
		cc = CC_NS;
	} else {
		cmp_rr(code, opts->gen.cycles, opts->gen.limit, SZ_D);
		cc = CC_A;
	}
	code_ptr jmp_off = code->cur+1;
	jcc(code, cc, jmp_off+1);
	call(code, opts->handle_int_latch);
	*jmp_off = code->cur - (jmp_off+1);
}

uint8_t translate_m68k_op(m68kinst * inst, host_ea * ea, m68k_options * opts, uint8_t dst)
{
	code_info *code = &opts->gen.code;
	m68k_op_info *op = dst ? &inst->dst : &inst->src;
	int8_t reg = native_reg(op, opts);
	uint8_t sec_reg;
	uint8_t ret = 1;
	int32_t dec_amount, inc_amount;
	if (reg >= 0) {
		ea->mode = MODE_REG_DIRECT;
		if (!dst && inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
			movsx_rr(code, reg, opts->gen.scratch1, SZ_W, SZ_D);
			ea->base = opts->gen.scratch1;
#ifdef X86_32
		} else if (reg > RBX && inst->extra.size == OPSIZE_BYTE) {
			mov_rr(code, reg, opts->gen.scratch1, SZ_D);
			ea->base = opts->gen.scratch1;
#endif
		} else {
			ea->base = reg;
		}
		return 0;
	}
	switch (op->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 if we're translating the src operand
		if (dst || native_reg(&(inst->dst), opts) >= 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 = opts->gen.context_reg;
			ea->disp = reg_offset(op);
		} else {
			if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
				movsx_rdispr(code, opts->gen.context_reg, reg_offset(op), opts->gen.scratch1, SZ_W, SZ_D);
			} else {
				mov_rdispr(code, opts->gen.context_reg, reg_offset(op), opts->gen.scratch1, inst->extra.size);
			}
			ea->mode = MODE_REG_DIRECT;
			ea->base = opts->gen.scratch1;
			//we're explicitly handling the areg dest here, so we exit immediately
			return 0;
		}
		ret = 0;
		break;
	case MODE_AREG_PREDEC:
		if (dst && inst->src.addr_mode == MODE_AREG_PREDEC) {
			push_r(code, opts->gen.scratch1);
		}
		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (op->params.regs.pri == 7 ? 2 :1));
		if (!dst || (
			inst->op != M68K_MOVE && inst->op != M68K_MOVEM 
			&& inst->op != M68K_SUBX && inst->op != M68K_ADDX 
			&& inst->op != M68K_ABCD && inst->op != M68K_SBCD
		)) {
			cycles(&opts->gen, PREDEC_PENALTY);
		}
		subi_areg(opts, dec_amount, op->params.regs.pri);
	case MODE_AREG_INDIRECT:
	case MODE_AREG_POSTINC:
		areg_to_native(opts, op->params.regs.pri, opts->gen.scratch1);
		m68k_read_size(opts, inst->extra.size);

		if (dst) {
			if (inst->src.addr_mode == MODE_AREG_PREDEC) {
				//restore src operand to opts->gen.scratch2
				pop_r(code, opts->gen.scratch2);
			} else {
				//save reg value in opts->gen.scratch2 so we can use it to save the result in memory later
				areg_to_native(opts, op->params.regs.pri, opts->gen.scratch2);
			}
		}

		if (op->addr_mode == MODE_AREG_POSTINC) {
			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (op->params.regs.pri == 7 ? 2 : 1));
			addi_areg(opts, inc_amount, op->params.regs.pri);
		}
		ea->mode = MODE_REG_DIRECT;
		ea->base = (!dst && inst->dst.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) ? opts->gen.scratch2 : opts->gen.scratch1;
		break;
	case MODE_AREG_DISPLACE:
		cycles(&opts->gen, BUS);
		calc_areg_displace(opts, op, opts->gen.scratch1);
		if (dst) {
			push_r(code, opts->gen.scratch1);
		}
		m68k_read_size(opts, inst->extra.size);
		if (dst) {
			pop_r(code, opts->gen.scratch2);
		}

		ea->mode = MODE_REG_DIRECT;
		ea->base = opts->gen.scratch1;
		break;
	case MODE_AREG_INDEX_DISP8:
		cycles(&opts->gen, 6);
		calc_areg_index_disp8(opts, op, opts->gen.scratch1);
		if (dst) {
			push_r(code, opts->gen.scratch1);
		}
		m68k_read_size(opts, inst->extra.size);
		if (dst) {
			pop_r(code, opts->gen.scratch2);
		}

		ea->mode = MODE_REG_DIRECT;
		ea->base = opts->gen.scratch1;
		break;
	case MODE_PC_DISPLACE:
		cycles(&opts->gen, BUS);
		mov_ir(code, op->params.regs.displacement + inst->address+2, opts->gen.scratch1, SZ_D);
		if (dst) {
			push_r(code, opts->gen.scratch1);
		}
		m68k_read_size(opts, inst->extra.size);
		if (dst) {
			pop_r(code, opts->gen.scratch2);
		}

		ea->mode = MODE_REG_DIRECT;
		ea->base = opts->gen.scratch1;
		break;
	case MODE_PC_INDEX_DISP8:
		cycles(&opts->gen, 6);
		mov_ir(code, inst->address+2, opts->gen.scratch1, SZ_D);
		calc_index_disp8(opts, op, opts->gen.scratch1);
		if (dst) {
			push_r(code, opts->gen.scratch1);
		}
		m68k_read_size(opts, inst->extra.size);
		if (dst) {
			pop_r(code, opts->gen.scratch2);
		}

		ea->mode = MODE_REG_DIRECT;
		ea->base = opts->gen.scratch1;
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		cycles(&opts->gen, op->addr_mode == MODE_ABSOLUTE ? BUS*2 : BUS);
		mov_ir(code, op->params.immed, opts->gen.scratch1, SZ_D);
		if (dst) {
			push_r(code, opts->gen.scratch1);
		}
		m68k_read_size(opts, inst->extra.size);
		if (dst) {
			pop_r(code, opts->gen.scratch2);
		}

		ea->mode = MODE_REG_DIRECT;
		ea->base = opts->gen.scratch1;
		break;
	case MODE_IMMEDIATE:
	case MODE_IMMEDIATE_WORD:
		if (inst->variant != VAR_QUICK) {
			cycles(&opts->gen, (inst->extra.size == OPSIZE_LONG && op->addr_mode == MODE_IMMEDIATE) ? BUS*2 : BUS);
		}
		ea->mode = MODE_IMMED;
		ea->disp = op->params.immed;
		//sign extend value when the destination is an address register
		if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD && ea->disp & 0x8000) {
			ea->disp |= 0xFFFF0000;
		}
		return inst->variant != VAR_QUICK;
	default:
		m68k_disasm(inst, disasm_buf);
		fatal_error("%X: %s\naddress mode %d not implemented (%s)\n", inst->address, disasm_buf, op->addr_mode, dst ? "dst" : "src");
	}
	if (!dst && inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
		if (ea->mode == MODE_REG_DIRECT) {
			movsx_rr(code, ea->base, opts->gen.scratch1, SZ_W, SZ_D);
		} else {
			movsx_rdispr(code, ea->base, ea->disp, opts->gen.scratch1, SZ_W, SZ_D);
			ea->mode = MODE_REG_DIRECT;
		}
		ea->base = opts->gen.scratch1;
	}
	return ret;
}

void check_user_mode_swap_ssp_usp(m68k_options *opts)
{
	code_info * code = &opts->gen.code;
	//Check if we've switched to user mode and swap stack pointers if needed
	bt_irdisp(code, 5, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	code_ptr end_off = code->cur + 1;
	jcc(code, CC_C, code->cur + 2);
	swap_ssp_usp(opts);
	*end_off = code->cur - (end_off + 1);
}

void translate_m68k_move(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	int8_t reg, flags_reg, sec_reg;
	uint8_t dir = 0;
	int32_t offset;
	int32_t inc_amount, dec_amount;
	host_ea src;
	uint8_t needs_int_latch = translate_m68k_op(inst, &src, opts, 0);
	reg = native_reg(&(inst->dst), 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) {
					mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
				} else {
					mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
				}
				src.mode = MODE_REG_DIRECT;
				flags_reg = src.base = opts->gen.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) {
				mov_rr(code, src.base, reg, size);
			} else if (src.mode == MODE_REG_DISPLACE8) {
				mov_rdispr(code, src.base, src.disp, reg, size);
			} else {
				mov_ir(code, src.disp, reg, size);
			}
		} else if(src.mode == MODE_REG_DIRECT) {
			mov_rrdisp(code, src.base, opts->gen.context_reg, reg_offset(&(inst->dst)), size);
		} else {
			mov_irdisp(code, src.disp, opts->gen.context_reg, reg_offset(&(inst->dst)), size);
		}
		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));
	case MODE_AREG_INDIRECT:
	case MODE_AREG_POSTINC:
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != opts->gen.scratch1) {
				mov_rr(code, src.base, opts->gen.scratch1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
		} else {
			mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
		}
		if (inst->dst.addr_mode == MODE_AREG_PREDEC) {
			subi_areg(opts, dec_amount, inst->dst.params.regs.pri);
		}
		areg_to_native(opts, inst->dst.params.regs.pri, opts->gen.scratch2);
		break;
	case MODE_AREG_DISPLACE:
		cycles(&opts->gen, BUS);
		calc_areg_displace(opts, &inst->dst, opts->gen.scratch2);
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != opts->gen.scratch1) {
				mov_rr(code, src.base, opts->gen.scratch1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
		} else {
			mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
		}
		break;
	case MODE_AREG_INDEX_DISP8:
		cycles(&opts->gen, 6);//TODO: Check to make sure this is correct
		//calc_areg_index_disp8 will clober scratch1 when a 16-bit index is used
		if (src.base == opts->gen.scratch1 && !(inst->dst.params.regs.sec & 1)) {
			push_r(code, opts->gen.scratch1);
		}
		calc_areg_index_disp8(opts, &inst->dst, opts->gen.scratch2);
		if (src.base == opts->gen.scratch1 && !(inst->dst.params.regs.sec & 1)) {
			pop_r(code, opts->gen.scratch1);
		}
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != opts->gen.scratch1) {
				mov_rr(code, src.base, opts->gen.scratch1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
		} else {
			mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
		}
		break;
	case MODE_PC_DISPLACE:
		cycles(&opts->gen, BUS);
		mov_ir(code, inst->dst.params.regs.displacement + inst->address+2, opts->gen.scratch2, SZ_D);
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != opts->gen.scratch1) {
				mov_rr(code, src.base, opts->gen.scratch1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
		} else {
			mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
		}
		break;
	case MODE_PC_INDEX_DISP8:
		cycles(&opts->gen, 6);//TODO: Check to make sure this is correct
		mov_ir(code, inst->address, opts->gen.scratch2, SZ_D);
		if (src.base == opts->gen.scratch1 && !(inst->dst.params.regs.sec & 1)) {
			push_r(code, opts->gen.scratch1);
		}
		calc_index_disp8(opts, &inst->dst, opts->gen.scratch2);
		if (src.base == opts->gen.scratch1 && !(inst->dst.params.regs.sec & 1)) {
			pop_r(code, opts->gen.scratch1);
		}
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != opts->gen.scratch1) {
				mov_rr(code, src.base, opts->gen.scratch1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
		} else {
			mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
		}
		break;
	case MODE_ABSOLUTE:
	case MODE_ABSOLUTE_SHORT:
		if (src.mode == MODE_REG_DIRECT) {
			if (src.base != opts->gen.scratch1) {
				mov_rr(code, src.base, opts->gen.scratch1, inst->extra.size);
			}
		} else if (src.mode == MODE_REG_DISPLACE8) {
			mov_rdispr(code, src.base, src.disp, opts->gen.scratch1, inst->extra.size);
		} else {
			mov_ir(code, src.disp, opts->gen.scratch1, inst->extra.size);
		}
		if (inst->dst.addr_mode == MODE_ABSOLUTE) {
			cycles(&opts->gen, BUS*2);
		} else {
			cycles(&opts->gen, BUS);
		}
		mov_ir(code, inst->dst.params.immed, opts->gen.scratch2, SZ_D);
		break;
	default:
		m68k_disasm(inst, disasm_buf);
		fatal_error("%X: %s\naddress mode %d not implemented (move dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
	}

	if (inst->dst.addr_mode != MODE_AREG) {
		cmp_ir(code, 0, flags_reg, inst->extra.size);
		update_flags(opts, N|Z|V0|C0);
	}
	if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG) {
		if (inst->extra.size == OPSIZE_LONG) {
			//We want the int latch to occur between the two writes,
			//but that's a pain to do without refactoring how 32-bit writes work
			//workaround it by temporarily increasing the cycle count before the check
			cycles(&opts->gen, BUS);
		}
		m68k_check_cycles_int_latch(opts);
		if (inst->extra.size == OPSIZE_LONG) {
			//and then backing out that extra increment here before the write happens
			cycles(&opts->gen, -BUS);
		}
		m68k_write_size(opts, inst->extra.size, inst->dst.addr_mode == MODE_AREG_PREDEC);
		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));
			addi_areg(opts, inc_amount, inst->dst.params.regs.pri);
		}
	} else if (needs_int_latch) {
		m68k_check_cycles_int_latch(opts);
	}

	//add cycles for prefetch
	cycles(&opts->gen, BUS);
}

void translate_m68k_ext(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	host_ea dst_op;
	uint8_t dst_size = inst->extra.size;
	inst->extra.size--;
	translate_m68k_op(inst, &dst_op, opts, 1);
	if (dst_op.mode == MODE_REG_DIRECT) {
		movsx_rr(code, dst_op.base, dst_op.base, inst->extra.size, dst_size);
		cmp_ir(code, 0, dst_op.base, dst_size);
	} else {
		movsx_rdispr(code, dst_op.base, dst_op.disp, opts->gen.scratch1, inst->extra.size, dst_size);
		cmp_ir(code, 0, opts->gen.scratch1, dst_size);
		mov_rrdisp(code, opts->gen.scratch1, dst_op.base, dst_op.disp, dst_size);
	}
	inst->extra.size = dst_size;
	update_flags(opts, N|V0|C0|Z);
	cycles(&opts->gen, BUS);
	//M68K EXT only operates on registers so no need for a call to save result here
}

uint8_t m68k_eval_cond(m68k_options * opts, uint8_t cc)
{
	uint8_t cond = CC_NZ;
	switch (cc)
	{
	case COND_HIGH:
		cond = CC_Z;
	case COND_LOW_SAME:
		flag_to_reg(opts, FLAG_Z, opts->gen.scratch1);
		or_flag_to_reg(opts, FLAG_C, opts->gen.scratch1);
		break;
	case COND_CARRY_CLR:
		cond = CC_Z;
	case COND_CARRY_SET:
		check_flag(opts, FLAG_C);
		break;
	case COND_NOT_EQ:
		cond = CC_Z;
	case COND_EQ:
		check_flag(opts, FLAG_Z);
		break;
	case COND_OVERF_CLR:
		cond = CC_Z;
	case COND_OVERF_SET:
		check_flag(opts, FLAG_V);
		break;
	case COND_PLUS:
		cond = CC_Z;
	case COND_MINUS:
		check_flag(opts, FLAG_N);
		break;
	case COND_GREATER_EQ:
		cond = CC_Z;
	case COND_LESS:
		cmp_flags(opts, FLAG_N, FLAG_V);
		break;
	case COND_GREATER:
		cond = CC_Z;
	case COND_LESS_EQ:
		flag_to_reg(opts, FLAG_V, opts->gen.scratch1);
		xor_flag_to_reg(opts, FLAG_N, opts->gen.scratch1);
		or_flag_to_reg(opts, FLAG_Z, opts->gen.scratch1);
		break;
	}
	return cond;
}

void translate_m68k_bcc(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	
	int32_t disp = inst->src.params.immed;
	uint32_t after = inst->address + 2;
	if (inst->extra.cond == COND_TRUE) {
		cycles(&opts->gen, 10);
		jump_m68k_abs(opts, after + disp);
	} else {
		uint8_t cond = m68k_eval_cond(opts, inst->extra.cond);
		code_ptr do_branch = code->cur + 1;
		jcc(code, cond, do_branch);
		
		cycles(&opts->gen, inst->variant == VAR_BYTE ? 8 : 12);
		code_ptr done = code->cur + 1;
		jmp(code, done);
		
		*do_branch = code->cur - (do_branch + 1);
		cycles(&opts->gen, 10);
		code_ptr dest_addr = get_native_address(opts, after + disp);
		if (!dest_addr) {
			opts->gen.deferred = defer_address(opts->gen.deferred, after + disp, code->cur + 1);
			//dummy address to be replaced later, make sure it generates a 4-byte displacement
			dest_addr = code->cur + 256;
		}
		jmp(code, dest_addr);
		
		*done = code->cur - (done + 1);
	}
}

void translate_m68k_scc(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	uint8_t cond = inst->extra.cond;
	host_ea dst_op;
	inst->extra.size = OPSIZE_BYTE;
	translate_m68k_op(inst, &dst_op, 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) {
			cycles(&opts->gen, 6);
		} else {
			cycles(&opts->gen, BUS);
		}
		if (dst_op.mode == MODE_REG_DIRECT) {
			mov_ir(code, cond == COND_TRUE ? 0xFF : 0, dst_op.base, SZ_B);
		} else {
			mov_irdisp(code, cond == COND_TRUE ? 0xFF : 0, dst_op.base, dst_op.disp, SZ_B);
		}
	} else {
		uint8_t cc = m68k_eval_cond(opts, cond);
		check_alloc_code(code, 6*MAX_INST_LEN);
		code_ptr true_off = code->cur + 1;
		jcc(code, cc, code->cur+2);
		cycles(&opts->gen, BUS);
		if (dst_op.mode == MODE_REG_DIRECT) {
			mov_ir(code, 0, dst_op.base, SZ_B);
		} else {
			mov_irdisp(code, 0, dst_op.base, dst_op.disp, SZ_B);
		}
		code_ptr end_off = code->cur+1;
		jmp(code, code->cur+2);
		*true_off = code->cur - (true_off+1);
		cycles(&opts->gen, inst->dst.addr_mode == MODE_REG ? 6 : 4);
		if (dst_op.mode == MODE_REG_DIRECT) {
			mov_ir(code, 0xFF, dst_op.base, SZ_B);
		} else {
			mov_irdisp(code, 0xFF, dst_op.base, dst_op.disp, SZ_B);
		}
		*end_off = code->cur - (end_off+1);
	}
	m68k_save_result(inst, opts);
}

void translate_m68k_dbcc(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	//best case duration
	cycles(&opts->gen, 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 = m68k_eval_cond(opts, inst->extra.cond);
		check_alloc_code(code, 6*MAX_INST_LEN);
		skip_loc = code->cur + 1;
		jcc(code, cond, code->cur + 2);
	}
	if (opts->dregs[inst->dst.params.regs.pri] >= 0) {
		sub_ir(code, 1, opts->dregs[inst->dst.params.regs.pri], SZ_W);
		cmp_ir(code, -1, opts->dregs[inst->dst.params.regs.pri], SZ_W);
	} else {
		sub_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, dregs) + 4 * inst->dst.params.regs.pri, SZ_W);
		cmp_irdisp(code, -1, opts->gen.context_reg, offsetof(m68k_context, dregs) + 4 * inst->dst.params.regs.pri, SZ_W);
	}
	code_ptr loop_end_loc = code->cur + 1;
	jcc(code, CC_Z, code->cur + 2);
	uint32_t after = inst->address + 2;
	jump_m68k_abs(opts, after + inst->src.params.immed);
	*loop_end_loc = code->cur - (loop_end_loc+1);
	if (skip_loc) {
		cycles(&opts->gen, 2);
		*skip_loc = code->cur - (skip_loc+1);
		cycles(&opts->gen, 2);
	} else {
		cycles(&opts->gen, 4);
	}
}

void translate_m68k_movep(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	int8_t reg;
	cycles(&opts->gen, BUS*2);
	if (inst->src.addr_mode == MODE_REG) {
		calc_areg_displace(opts, &inst->dst, opts->gen.scratch2);
		reg = native_reg(&(inst->src), opts);
		if (inst->extra.size == OPSIZE_LONG) {
			if (reg >= 0) {
				mov_rr(code, reg, opts->gen.scratch1, SZ_D);
				shr_ir(code, 24, opts->gen.scratch1, SZ_D);
				push_r(code, opts->gen.scratch2);
				call(code, opts->write_8);
				pop_r(code, opts->gen.scratch2);
				mov_rr(code, reg, opts->gen.scratch1, SZ_D);
				shr_ir(code, 16, opts->gen.scratch1, SZ_D);

			} else {
				mov_rdispr(code, opts->gen.context_reg, reg_offset(&(inst->src))+3, opts->gen.scratch1, SZ_B);
				push_r(code, opts->gen.scratch2);
				call(code, opts->write_8);
				pop_r(code, opts->gen.scratch2);
				mov_rdispr(code, opts->gen.context_reg, reg_offset(&(inst->src))+2, opts->gen.scratch1, SZ_B);
			}
			add_ir(code, 2, opts->gen.scratch2, SZ_D);
			push_r(code, opts->gen.scratch2);
			call(code, opts->write_8);
			pop_r(code, opts->gen.scratch2);
			add_ir(code, 2, opts->gen.scratch2, SZ_D);
		}
		if (reg >= 0) {
			mov_rr(code, reg, opts->gen.scratch1, SZ_W);
			shr_ir(code, 8, opts->gen.scratch1, SZ_W);
			push_r(code, opts->gen.scratch2);
			call(code, opts->write_8);
			pop_r(code, opts->gen.scratch2);
			mov_rr(code, reg, opts->gen.scratch1, SZ_W);
		} else {
			mov_rdispr(code, opts->gen.context_reg, reg_offset(&(inst->src))+1, opts->gen.scratch1, SZ_B);
			push_r(code, opts->gen.scratch2);
			call(code, opts->write_8);
			pop_r(code, opts->gen.scratch2);
			mov_rdispr(code, opts->gen.context_reg, reg_offset(&(inst->src)), opts->gen.scratch1, SZ_B);
		}
		add_ir(code, 2, opts->gen.scratch2, SZ_D);
		call(code, opts->write_8);
	} else {
		calc_areg_displace(opts, &inst->src, opts->gen.scratch1);
		reg = native_reg(&(inst->dst), opts);
		if (inst->extra.size == OPSIZE_LONG) {
			if (reg >= 0) {
				push_r(code, opts->gen.scratch1);
				call(code, opts->read_8);
				shl_ir(code, 24, opts->gen.scratch1, SZ_D);
				mov_rr(code, opts->gen.scratch1, reg, SZ_D);
				pop_r(code, opts->gen.scratch1);
				add_ir(code, 2, opts->gen.scratch1, SZ_D);
				push_r(code, opts->gen.scratch1);
				call(code, opts->read_8);
				movzx_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_B, SZ_W);
				shl_ir(code, 16, opts->gen.scratch1, SZ_D);
				or_rr(code, opts->gen.scratch1, reg, SZ_D);
			} else {
				push_r(code, opts->gen.scratch1);
				call(code, opts->read_8);
				mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, reg_offset(&(inst->dst))+3, SZ_B);
				pop_r(code, opts->gen.scratch1);
				add_ir(code, 2, opts->gen.scratch1, SZ_D);
				push_r(code, opts->gen.scratch1);
				call(code, opts->read_8);
				mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, reg_offset(&(inst->dst))+2, SZ_B);
			}
			pop_r(code, opts->gen.scratch1);
			add_ir(code, 2, opts->gen.scratch1, SZ_D);
		}
		push_r(code, opts->gen.scratch1);
		call(code, opts->read_8);
		if (reg >= 0) {

			shl_ir(code, 8, opts->gen.scratch1, SZ_W);
			mov_rr(code, opts->gen.scratch1, reg, SZ_W);
			pop_r(code, opts->gen.scratch1);
			add_ir(code, 2, opts->gen.scratch1, SZ_D);
			call(code, opts->read_8);
			mov_rr(code, opts->gen.scratch1, reg, SZ_B);
		} else {
			mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, reg_offset(&(inst->dst))+1, SZ_B);
			pop_r(code, opts->gen.scratch1);
			add_ir(code, 2, opts->gen.scratch1, SZ_D);
			call(code, opts->read_8);
			mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, reg_offset(&(inst->dst)), SZ_B);
		}
	}
}

typedef void (*shift_ir_t)(code_info *code, uint8_t val, uint8_t dst, uint8_t size);
typedef void (*shift_irdisp_t)(code_info *code, uint8_t val, uint8_t dst_base, int32_t disp, uint8_t size);
typedef void (*shift_clr_t)(code_info *code, uint8_t dst, uint8_t size);
typedef void (*shift_clrdisp_t)(code_info *code, uint8_t dst_base, int32_t disp, uint8_t size);

void translate_shift(m68k_options * opts, m68kinst * inst, host_ea *src_op, host_ea * dst_op, shift_ir_t shift_ir, shift_irdisp_t shift_irdisp, shift_clr_t shift_clr, shift_clrdisp_t shift_clrdisp, shift_ir_t special, shift_irdisp_t special_disp)
{
	code_info *code = &opts->gen.code;
	code_ptr end_off = NULL;
	code_ptr nz_off = NULL;
	code_ptr z_off = NULL;
	if (inst->src.addr_mode == MODE_UNUSED) {
		cycles(&opts->gen, BUS);
		//Memory shift
		shift_ir(code, 1, dst_op->base, SZ_W);
	} else {
		if (src_op->mode == MODE_IMMED) {
			cycles(&opts->gen, (inst->extra.size == OPSIZE_LONG ? 8 : 6) + 2 * src_op->disp);
			if (src_op->disp != 1 && inst->op == M68K_ASL) {
				set_flag(opts, 0, FLAG_V);
				for (int i = 0; i < src_op->disp; i++) {
					if (dst_op->mode == MODE_REG_DIRECT) {
						shift_ir(code, 1, dst_op->base, inst->extra.size);
					} else {
						shift_irdisp(code, 1, dst_op->base, dst_op->disp, inst->extra.size);
					}
					check_alloc_code(code, 2*MAX_INST_LEN);
					code_ptr after_flag_set = code->cur + 1;
					jcc(code, CC_NO, code->cur + 2);
					set_flag(opts, 1, FLAG_V);
					*after_flag_set = code->cur - (after_flag_set+1);
				}
			} else {
				if (dst_op->mode == MODE_REG_DIRECT) {
					shift_ir(code, src_op->disp, dst_op->base, inst->extra.size);
				} else {
					shift_irdisp(code, src_op->disp, dst_op->base, dst_op->disp, inst->extra.size);
				}
				set_flag_cond(opts, CC_O, FLAG_V);
			}
		} else {
			cycles(&opts->gen, inst->extra.size == OPSIZE_LONG ? 8 : 6);
			if (src_op->base != RCX) {
				if (src_op->mode == MODE_REG_DIRECT) {
					mov_rr(code, src_op->base, RCX, SZ_B);
				} else {
					mov_rdispr(code, src_op->base, src_op->disp, RCX, SZ_B);
				}

			}
			and_ir(code, 63, RCX, SZ_D);
			check_alloc_code(code, 7*MAX_INST_LEN);
			nz_off = code->cur + 1;
			jcc(code, CC_NZ, code->cur + 2);
			//Flag behavior for shift count of 0 is different for x86 than 68K
			if (dst_op->mode == MODE_REG_DIRECT) {
				cmp_ir(code, 0, dst_op->base, inst->extra.size);
			} else {
				cmp_irdisp(code, 0, dst_op->base, dst_op->disp, inst->extra.size);
			}
			set_flag_cond(opts, CC_Z, FLAG_Z);
			set_flag_cond(opts, CC_S, FLAG_N);
			set_flag(opts, 0, FLAG_C);
			//For other instructions, this flag will be set below
			if (inst->op == M68K_ASL) {
				set_flag(opts, 0, FLAG_V);
			}
			z_off = code->cur + 1;
			jmp(code, code->cur + 2);
			*nz_off = code->cur - (nz_off + 1);
			//add 2 cycles for every bit shifted
			mov_ir(code, 2 * opts->gen.clock_divider, opts->gen.scratch2, SZ_D);
			imul_rr(code, RCX, opts->gen.scratch2, SZ_D);
			add_rr(code, opts->gen.scratch2, opts->gen.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
				set_flag(opts, 0, FLAG_V);
				check_alloc_code(code, 5*MAX_INST_LEN);
				code_ptr loop_start = code->cur;
				if (dst_op->mode == MODE_REG_DIRECT) {
					shift_ir(code, 1, dst_op->base, inst->extra.size);
				} else {
					shift_irdisp(code, 1, dst_op->base, dst_op->disp, inst->extra.size);
				}
				code_ptr after_flag_set = code->cur + 1;
				jcc(code, CC_NO, code->cur + 2);
				set_flag(opts, 1, FLAG_V);
				*after_flag_set = code->cur - (after_flag_set+1);
				loop(code, 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
				cmp_ir(code, 32, RCX, SZ_B);
				check_alloc_code(code, 14*MAX_INST_LEN);
				code_ptr norm_shift_off = code->cur + 1;
				jcc(code, CC_L, code->cur + 2);
				if (special) {
					code_ptr after_flag_set = NULL;
					if (inst->extra.size == OPSIZE_LONG) {
						code_ptr neq_32_off = code->cur + 1;
						jcc(code, CC_NZ, code->cur + 2);

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

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

	}
	if (!special && end_off) {
		*end_off = code->cur - (end_off + 1);
	}
	update_flags(opts, C|Z|N);
	if (special && end_off) {
		*end_off = code->cur - (end_off + 1);
	}
	//set X flag to same as C flag
	if (opts->flag_regs[FLAG_C] >= 0) {
		flag_to_flag(opts, FLAG_C, FLAG_X);
	} else {
		set_flag_cond(opts, CC_C, FLAG_X);
	}
	if (z_off) {
		*z_off = code->cur - (z_off + 1);
	}
	if (inst->op != M68K_ASL) {
		set_flag(opts, 0, FLAG_V);
	}
	if (inst->src.addr_mode == MODE_UNUSED) {
		m68k_save_result(inst, opts);
	}
}

void translate_m68k_reset(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, reset_handler), opts->gen.scratch1, SZ_PTR);
	cmp_ir(code, 0, opts->gen.scratch1, SZ_PTR);
	code_ptr no_reset_handler = code->cur + 1;
	jcc(code, CC_Z, code->cur+2);
	call(code, opts->gen.save_context);
	call_args_r(code, opts->gen.scratch1, 1, opts->gen.context_reg);
	mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR);
	call(code, opts->gen.load_context);
	*no_reset_handler = code->cur - (no_reset_handler + 1);
	//RESET instructions take a long time to give peripherals time to reset themselves
	cycles(&opts->gen, 132);
}

void op_ir(code_info *code, m68kinst *inst, int32_t val, uint8_t dst, uint8_t size)
{
	switch (inst->op)
	{
	case M68K_ADD:  add_ir(code, val, dst, size); break;
	case M68K_ADDX: adc_ir(code, val, dst, size); break;
	case M68K_AND:  and_ir(code, val, dst, size); break;
	case M68K_BTST: bt_ir(code, val, dst, size); break;
	case M68K_BSET: bts_ir(code, val, dst, size); break;
	case M68K_BCLR: btr_ir(code, val, dst, size); break;
	case M68K_BCHG: btc_ir(code, val, dst, size); break;
	case M68K_CMP:  cmp_ir(code, val, dst, size); break;
	case M68K_EOR:  xor_ir(code, val, dst, size); break;
	case M68K_OR:   or_ir(code, val, dst, size); break;
	case M68K_ROL:  rol_ir(code, val, dst, size); break;
	case M68K_ROR:  ror_ir(code, val, dst, size); break;
	case M68K_ROXL: rcl_ir(code, val, dst, size); break;
	case M68K_ROXR: rcr_ir(code, val, dst, size); break;
	case M68K_SUB:  sub_ir(code, val, dst, size); break;
	case M68K_SUBX: sbb_ir(code, val, dst, size); break;
	}
}

void op_irdisp(code_info *code, m68kinst *inst, int32_t val, uint8_t dst, int32_t disp, uint8_t size)
{
	switch (inst->op)
	{
	case M68K_ADD:  add_irdisp(code, val, dst, disp, size); break;
	case M68K_ADDX: adc_irdisp(code, val, dst, disp, size); break;
	case M68K_AND:  and_irdisp(code, val, dst, disp, size); break;
	case M68K_BTST: bt_irdisp(code, val, dst, disp, size); break;
	case M68K_BSET: bts_irdisp(code, val, dst, disp, size); break;
	case M68K_BCLR: btr_irdisp(code, val, dst, disp, size); break;
	case M68K_BCHG: btc_irdisp(code, val, dst, disp, size); break;
	case M68K_CMP:  cmp_irdisp(code, val, dst, disp, size); break;
	case M68K_EOR:  xor_irdisp(code, val, dst, disp, size); break;
	case M68K_OR:   or_irdisp(code, val, dst, disp, size); break;
	case M68K_ROL:  rol_irdisp(code, val, dst, disp, size); break;
	case M68K_ROR:  ror_irdisp(code, val, dst, disp, size); break;
	case M68K_ROXL: rcl_irdisp(code, val, dst, disp, size); break;
	case M68K_ROXR: rcr_irdisp(code, val, dst, disp, size); break;
	case M68K_SUB:  sub_irdisp(code, val, dst, disp, size); break;
	case M68K_SUBX: sbb_irdisp(code, val, dst, disp, size); break;
	}
}

void op_rr(code_info *code, m68kinst *inst, uint8_t src, uint8_t dst, uint8_t size)
{
	switch (inst->op)
	{
	case M68K_ADD:  add_rr(code, src, dst, size); break;
	case M68K_ADDX: adc_rr(code, src, dst, size); break;
	case M68K_AND:  and_rr(code, src, dst, size); break;
	case M68K_BTST: bt_rr(code, src, dst, size); break;
	case M68K_BSET: bts_rr(code, src, dst, size); break;
	case M68K_BCLR: btr_rr(code, src, dst, size); break;
	case M68K_BCHG: btc_rr(code, src, dst, size); break;
	case M68K_CMP:  cmp_rr(code, src, dst, size); break;
	case M68K_EOR:  xor_rr(code, src, dst, size); break;
	case M68K_OR:   or_rr(code, src, dst, size); break;
	case M68K_SUB:  sub_rr(code, src, dst, size); break;
	case M68K_SUBX: sbb_rr(code, src, dst, size); break;
	}
}

void op_rrdisp(code_info *code, m68kinst *inst, uint8_t src, uint8_t dst, int32_t disp, uint8_t size)
{
	switch(inst->op)
	{
	case M68K_ADD:  add_rrdisp(code, src, dst, disp, size); break;
	case M68K_ADDX: adc_rrdisp(code, src, dst, disp, size); break;
	case M68K_AND:  and_rrdisp(code, src, dst, disp, size); break;
	case M68K_BTST: bt_rrdisp(code, src, dst, disp, size); break;
	case M68K_BSET: bts_rrdisp(code, src, dst, disp, size); break;
	case M68K_BCLR: btr_rrdisp(code, src, dst, disp, size); break;
	case M68K_BCHG: btc_rrdisp(code, src, dst, disp, size); break;
	case M68K_CMP:  cmp_rrdisp(code, src, dst, disp, size); break;
	case M68K_EOR:  xor_rrdisp(code, src, dst, disp, size); break;
	case M68K_OR:   or_rrdisp(code, src, dst, disp, size); break;
	case M68K_SUB:  sub_rrdisp(code, src, dst, disp, size); break;
	case M68K_SUBX: sbb_rrdisp(code, src, dst, disp, size); break;
	}
}

void op_rdispr(code_info *code, m68kinst *inst, uint8_t src, int32_t disp, uint8_t dst, uint8_t size)
{
	switch (inst->op)
	{
	case M68K_ADD:  add_rdispr(code, src, disp, dst, size); break;
	case M68K_ADDX: adc_rdispr(code, src, disp, dst, size); break;
	case M68K_AND:  and_rdispr(code, src, disp, dst, size); break;
	case M68K_CMP:  cmp_rdispr(code, src, disp, dst, size); break;
	case M68K_EOR:  xor_rdispr(code, src, disp, dst, size); break;
	case M68K_OR:   or_rdispr(code, src, disp, dst, size); break;
	case M68K_SUB:  sub_rdispr(code, src, disp, dst, size); break;
	case M68K_SUBX: sbb_rdispr(code, src, disp, dst, size); break;
	}
}

void translate_m68k_arith(m68k_options *opts, m68kinst * inst, uint32_t flag_mask, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	uint8_t size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size;
	
	uint32_t numcycles;
	if ((inst->op == M68K_ADDX || inst->op == M68K_SUBX) && inst->src.addr_mode != MODE_REG) {
		numcycles = 4;
	} else if (size == OPSIZE_LONG) {
		if (inst->op == M68K_CMP) {
			numcycles = inst->src.addr_mode > MODE_AREG && inst->dst.addr_mode > MODE_AREG ? 4 : 6;
		} else if (inst->op == M68K_AND && inst->variant == VAR_IMMEDIATE && inst->dst.addr_mode == MODE_REG) {
			numcycles = 6;
		} else if (inst->dst.addr_mode == MODE_REG) {
			numcycles = inst->src.addr_mode <= MODE_AREG || inst->src.addr_mode == MODE_IMMEDIATE ? 8 : 6;
		} else if (inst->dst.addr_mode == MODE_AREG) {
			numcycles = numcycles = inst->src.addr_mode <= MODE_AREG || inst->src.addr_mode == MODE_IMMEDIATE  
				|| inst->extra.size == OPSIZE_WORD ? 8 : 6;
		} else {
			numcycles = 4;
		}
	} else {
		numcycles = 4;
	}
	cycles(&opts->gen, numcycles);
	
	if (inst->op == M68K_ADDX || inst->op == M68K_SUBX) {
		flag_to_carry(opts, FLAG_X);
	}
	
	if (src_op->mode == MODE_REG_DIRECT) {
		if (dst_op->mode == MODE_REG_DIRECT) {
			op_rr(code, inst, src_op->base, dst_op->base, size);
		} else {
			op_rrdisp(code, inst, src_op->base, dst_op->base, dst_op->disp, size);
		}
	} else if (src_op->mode == MODE_REG_DISPLACE8) {
		op_rdispr(code, inst, src_op->base, src_op->disp, dst_op->base, size);
	} else {
		if (dst_op->mode == MODE_REG_DIRECT) {
			op_ir(code, inst, src_op->disp, dst_op->base, size);
		} else {
			op_irdisp(code, inst, src_op->disp, dst_op->base, dst_op->disp, size);
		}
	}
	if (inst->dst.addr_mode != MODE_AREG || inst->op == M68K_CMP) {
		update_flags(opts, flag_mask);
		if (inst->op == M68K_ADDX || inst->op == M68K_SUBX) {
			check_alloc_code(code, 2*MAX_INST_LEN);
			code_ptr after_flag_set = code->cur + 1;
			jcc(code, CC_Z, code->cur + 2);
			set_flag(opts, 0, FLAG_Z);
			*after_flag_set = code->cur - (after_flag_set+1);
		}
	}
	if (inst->op != M68K_CMP) {
		m68k_save_result(inst, opts);
	}
}

void translate_m68k_cmp(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	uint8_t size = inst->extra.size;
	host_ea src_op, dst_op;
	translate_m68k_op(inst, &src_op, opts, 0);
	if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
		push_r(code, opts->gen.scratch1);
		translate_m68k_op(inst, &dst_op, opts, 1);
		pop_r(code, opts->gen.scratch2);
		src_op.base = opts->gen.scratch2;
	} else {
		translate_m68k_op(inst, &dst_op, opts, 1);
		if (inst->dst.addr_mode == MODE_AREG && size == OPSIZE_WORD) {
			size = OPSIZE_LONG;
		}
	}
	translate_m68k_arith(opts, inst, N|Z|V|C, &src_op, &dst_op);
}

void translate_m68k_tas(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	host_ea op;
	translate_m68k_op(inst, &op, opts, 1);
	if (op.mode == MODE_REG_DIRECT) {
		cmp_ir(code, 0, op.base, SZ_B);
	} else {
		cmp_irdisp(code, 0, op.base, op.disp, SZ_B);
	}
	update_flags(opts, N|Z|V0|C0);
	if (inst->dst.addr_mode == MODE_REG) {
		cycles(&opts->gen, BUS);
		if (op.mode == MODE_REG_DIRECT) {
			bts_ir(code, 7, op.base, SZ_B);
		} else {
			bts_irdisp(code, 7, op.base, op.disp, SZ_B);
		}
	} else {
		if (opts->gen.flags & M68K_OPT_BROKEN_READ_MODIFY) {
			//2 cycles for processing
			//4 for failed writeback
			//4 for prefetch
			cycles(&opts->gen, BUS * 2 + 2);
		} else {
			cycles(&opts->gen, 2);
			bts_ir(code, 7, op.base, SZ_B);
			m68k_save_result(inst, opts);
			cycles(&opts->gen, BUS);
		}
	}
}

void op_r(code_info *code, m68kinst *inst, uint8_t dst, uint8_t size)
{
	switch(inst->op)
	{
	case M68K_CLR:   xor_rr(code, dst, dst, size); break;
	case M68K_NEG:   neg_r(code, dst, size); break;
	case M68K_NOT:   not_r(code, dst, size); cmp_ir(code, 0, dst, size); break;
	case M68K_ROL:   rol_clr(code, dst, size); break;
	case M68K_ROR:   ror_clr(code, dst, size); break;
	case M68K_ROXL:  rcl_clr(code, dst, size); break;
	case M68K_ROXR:  rcr_clr(code, dst, size); break;
	case M68K_SWAP:  rol_ir(code, 16, dst, SZ_D); cmp_ir(code, 0, dst, SZ_D); break;
	case M68K_TST:   cmp_ir(code, 0, dst, size); break;
	}
}

void op_rdisp(code_info *code, m68kinst *inst, uint8_t dst, int32_t disp, uint8_t size)
{
	switch(inst->op)
	{
	case M68K_CLR:   mov_irdisp(code, 0, dst, disp, size); break;
	case M68K_NEG:   neg_rdisp(code, dst, disp, size); break;
	case M68K_NOT:   not_rdisp(code, dst, disp, size); cmp_irdisp(code, 0, dst, disp, size); break;
	case M68K_ROL:   rol_clrdisp(code, dst, disp, size); break;
	case M68K_ROR:   ror_clrdisp(code, dst, disp, size); break;
	case M68K_ROXL:  rcl_clrdisp(code, dst, disp, size); break;
	case M68K_ROXR:  rcr_clrdisp(code, dst, disp, size); break;
	case M68K_SWAP:  rol_irdisp(code, 16, dst, disp, SZ_D); cmp_irdisp(code, 0, dst, disp, SZ_D); break;
	case M68K_TST:   cmp_irdisp(code, 0, dst, disp, size); break;
	}
}

void translate_m68k_unary(m68k_options *opts, m68kinst *inst, uint32_t flag_mask, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	uint32_t num_cycles = BUS;
	if (inst->extra.size == OPSIZE_LONG && (inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG)) {
		num_cycles += 2;
	}
	cycles(&opts->gen, num_cycles);
	if (dst_op->mode == MODE_REG_DIRECT) {
		op_r(code, inst, dst_op->base, inst->extra.size);
	} else {
		op_rdisp(code, inst, dst_op->base, dst_op->disp, inst->extra.size);
	}
	update_flags(opts, flag_mask);
	m68k_save_result(inst, opts);
}

void translate_m68k_abcd_sbcd(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	if (inst->op == M68K_NBCD) {
		if (dst_op->base != opts->gen.scratch2) {
			if (dst_op->mode == MODE_REG_DIRECT) {
				mov_rr(code, dst_op->base, opts->gen.scratch2, SZ_B);
			} else {
				mov_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch2, SZ_B);
			}
		}
		xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_B);
	} else {
		if (src_op->base != opts->gen.scratch2) {
			if (src_op->mode == MODE_REG_DIRECT) {
				mov_rr(code, src_op->base, opts->gen.scratch2, SZ_B);
			} else {
				mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch2, SZ_B);
			}
		}
		if (dst_op->base != opts->gen.scratch1) {
			if (dst_op->mode == MODE_REG_DIRECT) {
				mov_rr(code, dst_op->base, opts->gen.scratch1, SZ_B);
			} else {
				mov_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch1, SZ_B);
			}
		}
	}
	if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG && inst->dst.addr_mode != MODE_AREG_PREDEC) {
		//destination is in memory so we need to preserve scratch2 for the write at the end
		push_r(code, opts->gen.scratch2);
	}
	
	//reg to reg takes 6 cycles, mem to mem is 4 cycles + all the operand fetch/writing (including 2 cycle predec penalty for first operand)
	cycles(&opts->gen, inst->dst.addr_mode != MODE_REG ? BUS : BUS + 2);
	uint8_t other_reg;
	//WARNING: This may need adjustment if register assignments change
	if (opts->gen.scratch2 > RBX) {
		other_reg = RAX;
		xchg_rr(code, opts->gen.scratch2, RAX, SZ_D);
	} else {
		other_reg = opts->gen.scratch2;
	}
	mov_rr(code, opts->gen.scratch1, opts->gen.scratch1 + (AH-RAX), SZ_B);
	mov_rr(code, other_reg, other_reg + (AH-RAX), SZ_B);
	and_ir(code, 0xF, opts->gen.scratch1 + (AH-RAX), SZ_B);
	and_ir(code, 0xF, other_reg + (AH-RAX), SZ_B);
	//do op on low nibble so we can determine if an adjustment is necessary
	flag_to_carry(opts, FLAG_X);
	if (inst->op == M68K_ABCD) {
		adc_rr(code, other_reg + (AH-RAX), opts->gen.scratch1 + (AH-RAX), SZ_B);
	} else {
		sbb_rr(code, other_reg + (AH-RAX), opts->gen.scratch1 + (AH-RAX), SZ_B);
	}
	cmp_ir(code, inst->op == M68K_SBCD ? 0x10 : 0xA, opts->gen.scratch1 + (AH-RAX), SZ_B);
	mov_ir(code, 0xA0, other_reg + (AH-RAX), SZ_B);
	code_ptr no_adjust = code->cur+1;
	//add correction factor if necessary
	jcc(code, CC_B, no_adjust);
	mov_ir(code, 6, opts->gen.scratch1 + (AH-RAX), SZ_B);
	mov_ir(code, inst->op == M68K_ABCD ? 0x9A : 0xA6, other_reg + (AH-RAX), SZ_B);
	code_ptr after_adjust = code->cur+1;
	jmp(code, after_adjust);

	*no_adjust = code->cur - (no_adjust+1);
	xor_rr(code, opts->gen.scratch1 + (AH-RAX), opts->gen.scratch1 + (AH-RAX), SZ_B);
	*after_adjust = code->cur - (after_adjust+1);

	//do op on full byte
	flag_to_carry(opts, FLAG_X);
	if (inst->op == M68K_ABCD) {
		adc_rr(code, other_reg, opts->gen.scratch1, SZ_B);
	} else {
		sbb_rr(code, other_reg, opts->gen.scratch1, SZ_B);
	}
	set_flag(opts, 0, FLAG_C);
	//determine if we need a correction on the upper nibble
	code_ptr def_adjust = code->cur+1;
	jcc(code, CC_C, def_adjust);
	if (inst->op == M68K_SBCD) {
		no_adjust = code->cur+1;
		jmp(code, no_adjust);
	} else {
		cmp_rr(code, other_reg + (AH-RAX), opts->gen.scratch1, SZ_B);
		no_adjust = code->cur+1;
		jcc(code, CC_B, no_adjust);
	}
	*def_adjust = code->cur - (def_adjust + 1);
	set_flag(opts, 1, FLAG_C);
	or_ir(code, 0x60, opts->gen.scratch1 + (AH-RAX), SZ_B);
	*no_adjust = code->cur - (no_adjust+1);
	if (inst->op == M68K_ABCD) {
		add_rr(code, opts->gen.scratch1 + (AH-RAX), opts->gen.scratch1, SZ_B);
	} else {
		sub_rr(code, opts->gen.scratch1 + (AH-RAX), opts->gen.scratch1, SZ_B);
	}
	code_ptr no_ensure_carry = code->cur+1;
	jcc(code, CC_NC, no_ensure_carry);
	set_flag(opts, 1, FLAG_C);
	*no_ensure_carry = code->cur - (no_ensure_carry+1);
	//restore RAX if necessary
	if (opts->gen.scratch2 > RBX) {
		mov_rr(code, opts->gen.scratch2, RAX, SZ_D);
	}
	//V flag is set based on the result of the addition/subtraction of the
	//result and the correction factor
	set_flag_cond(opts, CC_O, FLAG_V);

	flag_to_flag(opts, FLAG_C, FLAG_X);

	cmp_ir(code, 0, opts->gen.scratch1, SZ_B);
	set_flag_cond(opts, CC_S, FLAG_N);
	code_ptr no_setz = code->cur+1;
	jcc(code, CC_Z, no_setz);
	set_flag(opts, 0, FLAG_Z);
	*no_setz = code->cur - (no_setz + 1);
	if (dst_op->base != opts->gen.scratch1) {
		if (dst_op->mode == MODE_REG_DIRECT) {
			mov_rr(code, opts->gen.scratch1, dst_op->base, SZ_B);
		} else {
			mov_rrdisp(code, opts->gen.scratch1, dst_op->base, dst_op->disp, SZ_B);
		}
	}
	if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG && inst->dst.addr_mode != MODE_AREG_PREDEC) {
		//destination is in memory so we need to restore scratch2 for the write at the end
		pop_r(code, opts->gen.scratch2);
	}
	m68k_save_result(inst, opts);
}

void translate_m68k_sl(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	translate_shift(opts, inst, src_op, dst_op, shl_ir, shl_irdisp, shl_clr, shl_clrdisp, shr_ir, shr_irdisp);
}

void translate_m68k_asr(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	translate_shift(opts, inst, src_op, dst_op, sar_ir, sar_irdisp, sar_clr, sar_clrdisp, NULL, NULL);
}

void translate_m68k_lsr(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	translate_shift(opts, inst, src_op, dst_op, shr_ir, shr_irdisp, shr_clr, shr_clrdisp, shl_ir, shl_irdisp);
}

void translate_m68k_bit(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, 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 (dst_op->mode == MODE_REG_DIRECT) {
			op_ir(code, inst, src_op->disp, dst_op->base, inst->extra.size);
		} else {
			op_irdisp(code, inst, 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 != opts->gen.scratch1 && src_op->base != opts->gen.scratch2)) {
			if (dst_op->base == opts->gen.scratch1) {
				push_r(code, opts->gen.scratch2);
				if (src_op->mode == MODE_REG_DIRECT) {
					mov_rr(code, src_op->base, opts->gen.scratch2, SZ_B);
				} else {
					mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch2, SZ_B);
				}
				src_op->base = opts->gen.scratch2;
			} else {
				if (src_op->mode == MODE_REG_DIRECT) {
					mov_rr(code, src_op->base, opts->gen.scratch1, SZ_B);
				} else {
					mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_B);
				}
				src_op->base = opts->gen.scratch1;
				}
			}
			uint8_t size = inst->extra.size;
		if (dst_op->mode == MODE_REG_DISPLACE8) {
			if (src_op->base != opts->gen.scratch1 && src_op->base != opts->gen.scratch2) {
				if (src_op->mode == MODE_REG_DIRECT) {
					mov_rr(code, src_op->base, opts->gen.scratch1, SZ_D);
				} else {
					mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_D);
					src_op->mode = MODE_REG_DIRECT;
				}
				src_op->base = opts->gen.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
			and_ir(code, 31, opts->gen.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
			and_ir(code, 7, src_op->base, SZ_D);
			size = SZ_D;
		}
		if (dst_op->mode == MODE_IMMED) {
			dst_op->base = src_op->base == opts->gen.scratch1 ? opts->gen.scratch2 : opts->gen.scratch1;
			mov_ir(code, dst_op->disp, dst_op->base, SZ_B);
			dst_op->mode = MODE_REG_DIRECT;
		}
		if (dst_op->mode == MODE_REG_DIRECT) {
			op_rr(code, inst, src_op->base, dst_op->base, size);
		} else {
			op_rrdisp(code, inst, src_op->base, dst_op->base, dst_op->disp, size);
		}
		if (src_op->base == opts->gen.scratch2) {
			pop_r(code, opts->gen.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
	set_flag_cond(opts, CC_NC, FLAG_Z);
	if (inst->op != M68K_BTST) {
		m68k_save_result(inst, opts);
	}
}

void translate_m68k_chk(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
	{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, 6);
	if (dst_op->mode == MODE_REG_DIRECT) {
		cmp_ir(code, 0, dst_op->base, inst->extra.size);
	} else {
		cmp_irdisp(code, 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;
	}
	//make sure we won't start a new chunk in the middle of these branches
	check_alloc_code(code, MAX_INST_LEN * 11);
	code_ptr passed = code->cur + 1;
	jcc(code, CC_GE, code->cur + 2);
	set_flag(opts, 1, FLAG_N);
	mov_ir(code, VECTOR_CHK, opts->gen.scratch2, SZ_D);
	mov_ir(code, inst->address+isize, opts->gen.scratch1, SZ_D);
	jmp(code, opts->trap);
	*passed = code->cur - (passed+1);
	if (dst_op->mode == MODE_REG_DIRECT) {
		if (src_op->mode == MODE_REG_DIRECT) {
			cmp_rr(code, src_op->base, dst_op->base, inst->extra.size);
		} else if(src_op->mode == MODE_REG_DISPLACE8) {
			cmp_rdispr(code, src_op->base, src_op->disp, dst_op->base, inst->extra.size);
		} else {
			cmp_ir(code, src_op->disp, dst_op->base, inst->extra.size);
		}
	} else if(dst_op->mode == MODE_REG_DISPLACE8) {
		if (src_op->mode == MODE_REG_DIRECT) {
			cmp_rrdisp(code, src_op->base, dst_op->base, dst_op->disp, inst->extra.size);
		} else {
			cmp_irdisp(code, src_op->disp, dst_op->base, dst_op->disp, inst->extra.size);
		}
	}
	passed = code->cur + 1;
	jcc(code, CC_LE, code->cur + 2);
	set_flag(opts, 0, FLAG_N);
	mov_ir(code, VECTOR_CHK, opts->gen.scratch2, SZ_D);
	mov_ir(code, inst->address+isize, opts->gen.scratch1, SZ_D);
	jmp(code, opts->trap);
	*passed = code->cur - (passed+1);
	cycles(&opts->gen, 4);
}

static uint32_t divu(uint32_t dividend, m68k_context *context, uint32_t divisor_shift)
{
	uint16_t quotient = 0;
	uint8_t force = 0;
	uint16_t bit = 0;
	uint32_t cycles = 6;
	for (int i = 0; i < 16; i++)
	{
		force = dividend >> 31;
		quotient = quotient << 1 | bit;
		dividend = dividend << 1;
		
		if (force || dividend >= divisor_shift) {
			dividend -= divisor_shift;
			cycles += force ? 4 : 6;
			bit = 1;
		} else {
			bit = 0;
			cycles += 8;
		}
	}
	cycles += force ? 6 : bit ? 4 : 2;
	context->current_cycle += cycles * context->options->gen.clock_divider;
	quotient = quotient << 1 | bit;
	return dividend | quotient;
}

static uint32_t divs(uint32_t dividend, m68k_context *context, uint32_t divisor_shift)
{
	uint32_t orig_divisor = divisor_shift, orig_dividend = dividend;
	if (divisor_shift & 0x80000000) {
		divisor_shift = 0 - divisor_shift;
	}
	
	uint32_t cycles = 12;
	if (dividend & 0x80000000) {
		//dvs10
		dividend = 0 - dividend;
		cycles += 2;
	}
	if (divisor_shift <= dividend) {
		context->flags[FLAG_V] = 1;
		context->flags[FLAG_N] = 1;
		context->flags[FLAG_Z] = 0;
		cycles += 2;
		context->current_cycle += cycles * context->options->gen.clock_divider;
		return orig_dividend;
	}
	uint16_t quotient = 0;
	uint16_t bit = 0;
	for (int i = 0; i < 15; i++)
	{
		quotient = quotient << 1 | bit;
		dividend = dividend << 1;
		
		if (dividend >= divisor_shift) {
			dividend -= divisor_shift;
			cycles += 6;
			bit = 1;
		} else {
			bit = 0;
			cycles += 8;
		}
	}
	quotient = quotient << 1 | bit;
	dividend = dividend << 1;
	if (dividend >= divisor_shift) {
		dividend -= divisor_shift;
		quotient = quotient << 1 | 1;
	} else {
		quotient = quotient << 1;
	}
	cycles += 4;
	
	context->flags[FLAG_V] = 0;
	if (orig_divisor & 0x80000000) {
		cycles += 16; //was 10
		if (orig_dividend & 0x80000000) {
			if (quotient & 0x8000) {
				context->flags[FLAG_V] = 1;
				context->flags[FLAG_N] = 1;
				context->flags[FLAG_Z] = 0;
				context->current_cycle += cycles * context->options->gen.clock_divider;
				return orig_dividend;
			} else {
				dividend = -dividend;
			}
		} else {
			quotient = -quotient;
			if (quotient && !(quotient & 0x8000)) {
				context->flags[FLAG_V] = 1;
			}
		}
	} else if (orig_dividend & 0x80000000) {
		cycles += 18; // was 12
		quotient = -quotient;
		if (quotient && !(quotient & 0x8000)) {
			context->flags[FLAG_V] = 1;
		} else {
			dividend = -dividend;
		}
	} else {
		cycles += 14; //was 10
		if (quotient & 0x8000) {
			context->flags[FLAG_V] = 1;
		}
	}
	if (context->flags[FLAG_V]) {
		context->flags[FLAG_N] = 1;
		context->flags[FLAG_Z] = 0;
		context->current_cycle += cycles * context->options->gen.clock_divider;
		return orig_dividend;
	}
	context->flags[FLAG_N] = (quotient & 0x8000) ? 1 : 0;
	context->flags[FLAG_Z] = quotient == 0;
	//V was cleared above, C is cleared by the generated machine code
	context->current_cycle += cycles * context->options->gen.clock_divider;
	return dividend | quotient;
}

void translate_m68k_div(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	check_alloc_code(code, MAX_NATIVE_SIZE);
	set_flag(opts, 0, FLAG_C);
	if (dst_op->mode == MODE_REG_DIRECT) {
		mov_rr(code, dst_op->base, opts->gen.scratch2, SZ_D);
	} else {
		mov_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch2, SZ_D);
	}
	if (src_op->mode == MODE_IMMED) {
		mov_ir(code, src_op->disp << 16, opts->gen.scratch1, SZ_D);
	} else {
		if (src_op->mode == MODE_REG_DISPLACE8) {
			movzx_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_W, SZ_D);
		} else if (src_op->base != opts->gen.scratch1) {
			movzx_rr(code, src_op->base, opts->gen.scratch1, SZ_W, SZ_D);
		}
		shl_ir(code, 16, opts->gen.scratch1, SZ_D);
	}
	cmp_ir(code, 0, opts->gen.scratch1, SZ_D);
	code_ptr not_zero = code->cur+1;
	jcc(code, CC_NZ, not_zero);
	
	//TODO: Check that opts->trap includes the cycles conumed by the first trap0 microinstruction
	cycles(&opts->gen, 4);
	uint32_t isize = 2;
	switch(inst->src.addr_mode)
	{
	case MODE_AREG_DISPLACE:
	case MODE_AREG_INDEX_DISP8:
	case MODE_ABSOLUTE_SHORT:
	case MODE_PC_DISPLACE:
	case MODE_PC_INDEX_DISP8:
	case MODE_IMMEDIATE:
		isize = 4;
		break;
	case MODE_ABSOLUTE:
		isize = 6;
		break;
	}
	//zero seems to clear all flags
	update_flags(opts, N0|Z0|V0);
	mov_ir(code, VECTOR_INT_DIV_ZERO, opts->gen.scratch2, SZ_D);
	mov_ir(code, inst->address+isize, opts->gen.scratch1, SZ_D);
	jmp(code, opts->trap);
	
	*not_zero = code->cur - (not_zero + 1);
	code_ptr end = NULL;
	if (inst->op == M68K_DIVU) {
		//initial overflow check needs to be done in the C code for divs
		//but can be done before dumping state to mem in divu as an optimization
		cmp_rr(code, opts->gen.scratch1, opts->gen.scratch2, SZ_D);
		code_ptr not_overflow = code->cur+1;
		jcc(code, CC_C, not_overflow);
		
		//overflow seems to always set the N and clear Z
		update_flags(opts, N1|Z0|V1);
		cycles(&opts->gen, 10);
		end = code->cur+1;
		jmp(code, end);
		
		*not_overflow = code->cur - (not_overflow + 1);
	}
	call(code, opts->gen.save_context);
	push_r(code, opts->gen.context_reg);
	//TODO: inline the functionality of divudivs/ so we don't need to dump context to memory
	call_args(code, (code_ptr)(inst->op == M68K_DIVU ? divu : divs), 3, opts->gen.scratch2, opts->gen.context_reg, opts->gen.scratch1);
	pop_r(code, opts->gen.context_reg);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_D);
	
	call(code, opts->gen.load_context);
	
	if (inst->op == M68K_DIVU) {
		cmp_ir(code, 0, opts->gen.scratch1, SZ_W);
		update_flags(opts, V0|Z|N);
	}
	
	if (dst_op->mode == MODE_REG_DIRECT) {
		mov_rr(code, opts->gen.scratch1, dst_op->base, SZ_D);
	} else {
		mov_rrdisp(code, opts->gen.scratch1, dst_op->base, dst_op->disp, SZ_D);
	}
	if (end) {
		*end = code->cur - (end + 1);
	}
}

void translate_m68k_exg(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, 6);
	if (dst_op->mode == MODE_REG_DIRECT) {
		mov_rr(code, dst_op->base, opts->gen.scratch2, SZ_D);
		if (src_op->mode == MODE_REG_DIRECT) {
			mov_rr(code, src_op->base, dst_op->base, SZ_D);
			mov_rr(code, opts->gen.scratch2, src_op->base, SZ_D);
		} else {
			mov_rdispr(code, src_op->base, src_op->disp, dst_op->base, SZ_D);
			mov_rrdisp(code, opts->gen.scratch2, src_op->base, src_op->disp, SZ_D);
		}
	} else {
		mov_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch2, SZ_D);
		if (src_op->mode == MODE_REG_DIRECT) {
			mov_rrdisp(code, src_op->base, dst_op->base, dst_op->disp, SZ_D);
			mov_rr(code, opts->gen.scratch2, src_op->base, SZ_D);
		} else {
			mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_D);
			mov_rrdisp(code, opts->gen.scratch1, dst_op->base, dst_op->disp, SZ_D);
			mov_rrdisp(code, opts->gen.scratch2, src_op->base, src_op->disp, SZ_D);
		}
	}
}



static uint32_t mulu_cycles(uint16_t value)
{
	//4 for prefetch, 2-cycles per bit x 16, 2 for cleanup
	uint32_t cycles = 38;
	uint16_t a = (value & 0b1010101010101010) >> 1;
	uint16_t b = value & 0b0101010101010101;
	value = a + b;
	a = (value & 0b1100110011001100) >> 2;
	b = value & 0b0011001100110011;
	value = a + b;
	a = (value & 0b1111000011110000) >> 4;
	b = value & 0b0000111100001111;
	value = a + b;
	a = (value & 0b1111111100000000) >> 8;
	b = value & 0b0000000011111111;
	value = a + b;
	return cycles + 2*value;
}

static uint32_t muls_cycles(uint16_t value)
{
	//muls timing is essentially the same as muls, but it's based on the number of 0/1
	//transitions rather than the number of 1 bits. xoring the value with itself shifted
	//by one effectively sets one bit for every transition
	return mulu_cycles((value << 1) ^ value);
}

void translate_m68k_mul(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	if (src_op->mode == MODE_IMMED) {
		cycles(&opts->gen, inst->op == M68K_MULU ? mulu_cycles(src_op->disp) : muls_cycles(src_op->disp));
		mov_ir(code, inst->op == M68K_MULU ? (src_op->disp & 0xFFFF) : ((src_op->disp & 0x8000) ? src_op->disp | 0xFFFF0000 : src_op->disp), opts->gen.scratch1, SZ_D);
	} else if (src_op->mode == MODE_REG_DIRECT) {
		if (inst->op == M68K_MULS) {
			movsx_rr(code, src_op->base, opts->gen.scratch1, SZ_W, SZ_D);
		} else {
			movzx_rr(code, src_op->base, opts->gen.scratch1, SZ_W, SZ_D);
		}
	} else {
		if (inst->op == M68K_MULS) {
			movsx_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_W, SZ_D);
		} else {
			movzx_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_W, SZ_D);
		}
	}
	if (src_op->mode != MODE_IMMED) {
		//TODO: Inline cycle calculation so we don't need to save/restore a bunch of registers
		//save context to memory and call the relevant C function for calculating the cycle count
		call(code, opts->gen.save_context);
		push_r(code, opts->gen.scratch1);
		push_r(code, opts->gen.context_reg);
		call_args(code, (code_ptr)(inst->op == M68K_MULS ? muls_cycles : mulu_cycles), 1, opts->gen.scratch1);
		pop_r(code, opts->gen.context_reg);
		//turn 68K cycles into master clock cycles and add to the current cycle count
		imul_irr(code, opts->gen.clock_divider, RAX, RAX, SZ_D);
		add_rrdisp(code, RAX, opts->gen.context_reg, offsetof(m68k_context, current_cycle), SZ_D);
		//restore context and scratch1
		call(code, opts->gen.load_context);
		pop_r(code, opts->gen.scratch1);
	}
	
	uint8_t dst_reg;
	if (dst_op->mode == MODE_REG_DIRECT) {
		dst_reg = dst_op->base;
		if (inst->op == M68K_MULS) {
			movsx_rr(code, dst_reg, dst_reg, SZ_W, SZ_D);
		} else {
			movzx_rr(code, dst_reg, dst_reg, SZ_W, SZ_D);
		}
	} else {
		dst_reg = opts->gen.scratch2;
		if (inst->op == M68K_MULS) {
			movsx_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch2, SZ_W, SZ_D);
		} else {
			movzx_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch2, SZ_W, SZ_D);
		}
	}
	imul_rr(code, opts->gen.scratch1, dst_reg, SZ_D);
	if (dst_op->mode == MODE_REG_DISPLACE8) {
		mov_rrdisp(code, dst_reg, dst_op->base, dst_op->disp, SZ_D);
	}
	cmp_ir(code, 0, dst_reg, SZ_D);
	update_flags(opts, N|Z|V0|C0);
}

void translate_m68k_negx(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, inst->extra.size == OPSIZE_LONG && inst->dst.addr_mode == MODE_REG ? BUS+2 : BUS);
	if (dst_op->mode == MODE_REG_DIRECT) {
		if (dst_op->base == opts->gen.scratch1) {
			push_r(code, opts->gen.scratch2);
			xor_rr(code, opts->gen.scratch2, opts->gen.scratch2, inst->extra.size);
			flag_to_carry(opts, FLAG_X);
			sbb_rr(code, dst_op->base, opts->gen.scratch2, inst->extra.size);
			mov_rr(code, opts->gen.scratch2, dst_op->base, inst->extra.size);
			pop_r(code, opts->gen.scratch2);
		} else {
			xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, inst->extra.size);
			flag_to_carry(opts, FLAG_X);
			sbb_rr(code, dst_op->base, opts->gen.scratch1, inst->extra.size);
			mov_rr(code, opts->gen.scratch1, dst_op->base, inst->extra.size);
		}
	} else {
		xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, inst->extra.size);
		flag_to_carry(opts, FLAG_X);
		sbb_rdispr(code, dst_op->base, dst_op->disp, opts->gen.scratch1, inst->extra.size);
		mov_rrdisp(code, opts->gen.scratch1, dst_op->base, dst_op->disp, inst->extra.size);
	}
	set_flag_cond(opts, CC_C, FLAG_C);
	code_ptr after_flag_set = code->cur + 1;
	jcc(code, CC_Z, code->cur + 2);
	set_flag(opts, 0, FLAG_Z);
	*after_flag_set = code->cur - (after_flag_set+1);
	set_flag_cond(opts, CC_S, FLAG_N);
	set_flag_cond(opts, CC_O, FLAG_V);
	if (opts->flag_regs[FLAG_C] >= 0) {
		flag_to_flag(opts, FLAG_C, FLAG_X);
	} else {
		set_flag_cond(opts, CC_C, FLAG_X);
	}
	m68k_save_result(inst, opts);
}

void translate_m68k_rot(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	int32_t init_flags = C|V0;
	if (inst->src.addr_mode == MODE_UNUSED) {
		cycles(&opts->gen, BUS);
		//Memory rotate
		if (inst->op == M68K_ROXR || inst->op == M68K_ROXL) {
			flag_to_carry(opts, FLAG_X);
			init_flags |= X;
		}
		op_ir(code, inst, 1, dst_op->base, inst->extra.size);
		update_flags(opts, init_flags);
		cmp_ir(code, 0, dst_op->base, inst->extra.size);
		update_flags(opts, Z|N);
		m68k_save_result(inst, opts);
	} else {
		if (src_op->mode == MODE_IMMED) {
			cycles(&opts->gen, (inst->extra.size == OPSIZE_LONG ? 8 : 6) + src_op->disp*2);
			if (inst->op == M68K_ROXR || inst->op == M68K_ROXL) {
				flag_to_carry(opts, FLAG_X);
				init_flags |= X;
			}
			if (dst_op->mode == MODE_REG_DIRECT) {
				op_ir(code, inst, src_op->disp, dst_op->base, inst->extra.size);
			} else {
				op_irdisp(code, inst, src_op->disp, dst_op->base, dst_op->disp, inst->extra.size);
			}
			update_flags(opts, init_flags);
		} else {
			cycles(&opts->gen, inst->extra.size == OPSIZE_LONG ? 8 : 6);
			if (src_op->mode == MODE_REG_DIRECT) {
				if (src_op->base != opts->gen.scratch1) {
					mov_rr(code, src_op->base, opts->gen.scratch1, SZ_B);
				}
			} else {
				mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_B);
			}
			and_ir(code, 63, opts->gen.scratch1, SZ_D);
			code_ptr zero_off = code->cur + 1;
			jcc(code, CC_Z, code->cur + 2);
			//add 2 cycles for every bit shifted
			mov_ir(code, 2 * opts->gen.clock_divider, opts->gen.scratch2, SZ_D);
			imul_rr(code, RCX, opts->gen.scratch2, SZ_D);
			add_rr(code, opts->gen.scratch2, opts->gen.cycles, SZ_D);
			cmp_ir(code, 32, opts->gen.scratch1, SZ_B);
			code_ptr norm_off = code->cur + 1;
			jcc(code, CC_L, code->cur + 2);
			if (inst->op == M68K_ROXR || inst->op == M68K_ROXL) {
				flag_to_carry(opts, FLAG_X);
				init_flags |= X;
			} else {
				sub_ir(code, 32, opts->gen.scratch1, SZ_B);
			}
			if (dst_op->mode == MODE_REG_DIRECT) {
				op_ir(code, inst, 31, dst_op->base, inst->extra.size);
				op_ir(code, inst, 1, dst_op->base, inst->extra.size);
			} else {
				op_irdisp(code, inst, 31, dst_op->base, dst_op->disp, inst->extra.size);
				op_irdisp(code, inst, 1, dst_op->base, dst_op->disp, inst->extra.size);
			}

			if (inst->op == M68K_ROXR || inst->op == M68K_ROXL) {
				set_flag_cond(opts, CC_C, FLAG_X);
				sub_ir(code, 32, opts->gen.scratch1, SZ_B);
				*norm_off = code->cur - (norm_off+1);
				flag_to_carry(opts, FLAG_X);
			} else {
				*norm_off = code->cur - (norm_off+1);
			}
			if (dst_op->mode == MODE_REG_DIRECT) {
				op_r(code, inst, dst_op->base, inst->extra.size);
			} else {
				op_rdisp(code, inst, dst_op->base, dst_op->disp, inst->extra.size);
			}
			update_flags(opts, init_flags);
			code_ptr end_off = code->cur + 1;
			jmp(code, code->cur + 2);
			*zero_off = code->cur - (zero_off+1);
			if (inst->op == M68K_ROXR || inst->op == M68K_ROXL) {
				//Carry flag is set to X flag when count is 0, this is different from ROR/ROL
				flag_to_flag(opts, FLAG_X, FLAG_C);
			} else {
				set_flag(opts, 0, FLAG_C);
			}
			*end_off = code->cur - (end_off+1);
		}
		if (dst_op->mode == MODE_REG_DIRECT) {
			cmp_ir(code, 0, dst_op->base, inst->extra.size);
		} else {
			cmp_irdisp(code, 0, dst_op->base, dst_op->disp, inst->extra.size);
		}
		update_flags(opts, Z|N);
	}
}

#define BIT_SUPERVISOR 5

void m68k_trap_if_not_supervisor(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	//check supervisor bit in SR and trap if not in supervisor mode
	bt_irdisp(code, BIT_SUPERVISOR, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	code_ptr in_sup_mode = code->cur + 1;
	jcc(code, CC_C, code->cur + 2);
	
	ldi_native(opts, VECTOR_PRIV_VIOLATION, opts->gen.scratch2);
	ldi_native(opts, inst->address, opts->gen.scratch1);
	jmp(code, opts->trap);
	
	*in_sup_mode = code->cur - (in_sup_mode + 1);
}

void translate_m68k_andi_ori_ccr_sr(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	if (inst->op == M68K_ANDI_SR || inst->op == M68K_ORI_SR) {
		m68k_trap_if_not_supervisor(opts, inst);
	}
	cycles(&opts->gen, 20);
	uint32_t flag_mask = 0;
	uint32_t base_flag = inst->op == M68K_ANDI_SR || inst->op == M68K_ANDI_CCR ? X0 : X1;
	for (int i = 0; i < 5; i++)
	{
		if ((base_flag == X0) ^ ((inst->src.params.immed & 1 << i) > 0))
		{
			flag_mask |= base_flag << ((4 - i) * 3);
		}
	}
	update_flags(opts, flag_mask);
	if (inst->op == M68K_ANDI_SR || inst->op == M68K_ORI_SR) {
		if (inst->op == M68K_ANDI_SR) {
			and_irdisp(code, inst->src.params.immed >> 8, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
		} else {
			or_irdisp(code, inst->src.params.immed >> 8, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
		}
		if (inst->op == M68K_ANDI_SR && !(inst->src.params.immed & (1 << (BIT_SUPERVISOR + 8)))) {
			//leave supervisor mode
			swap_ssp_usp(opts);
		}
		if ((inst->op == M68K_ANDI_SR && (inst->src.params.immed  & 0x700) != 0x700)
		    || (inst->op == M68K_ORI_SR && inst->src.params.immed & 0x8700)) {
			if (inst->op == M68K_ANDI_SR) {
				//set int pending flag in case we trigger an interrupt as a result of the mask change
				mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
			}
			call(code, opts->do_sync);
		}
	}
}

void translate_m68k_eori_ccr_sr(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	if (inst->op == M68K_EORI_SR) {
		m68k_trap_if_not_supervisor(opts, inst);
	}
	cycles(&opts->gen, 20);
	if (inst->src.params.immed & 0x1) {
		xor_flag(opts, 1, FLAG_C);
	}
	if (inst->src.params.immed & 0x2) {
		xor_flag(opts, 1, FLAG_V);
	}
	if (inst->src.params.immed & 0x4) {
		xor_flag(opts, 1, FLAG_Z);
	}
	if (inst->src.params.immed & 0x8) {
		xor_flag(opts, 1, FLAG_N);
	}
	if (inst->src.params.immed & 0x10) {
		xor_flag(opts, 1, FLAG_X);
	}
	if (inst->op == M68K_EORI_SR) {
		xor_irdisp(code, inst->src.params.immed >> 8, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
		if (inst->src.params.immed & 0x8700) {
			//set int pending flag in case we trigger an interrupt as a result of the mask change
			mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
			call(code, opts->do_sync);
		}
	}
}

void set_all_flags(m68k_options *opts, uint8_t flags)
{
	uint32_t flag_mask = flags & 0x10 ? X1 : X0;
	flag_mask |= flags & 0x8 ? N1 : N0;
	flag_mask |= flags & 0x4 ? Z1 : Z0;
	flag_mask |= flags & 0x2 ? V1 : V0;
	flag_mask |= flags & 0x1 ? C1 : C0;
	update_flags(opts, flag_mask);
}

void translate_m68k_move_ccr_sr(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	if (inst->op == M68K_MOVE_SR) {
		m68k_trap_if_not_supervisor(opts, inst);
	}
	if (src_op->mode == MODE_IMMED) {
		set_all_flags(opts, src_op->disp);
		if (inst->op == M68K_MOVE_SR) {
			mov_irdisp(code, (src_op->disp >> 8), opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
			if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) {
				//leave supervisor mode
				swap_ssp_usp(opts);
			}
			if (((src_op->disp >> 8) & 7) < 7) {
				//set int pending flag in case we trigger an interrupt as a result of the mask change
				mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
			}
			call(code, opts->do_sync);
		}
		cycles(&opts->gen, 12);
	} else {
		if (src_op->base != opts->gen.scratch1) {
			if (src_op->mode == MODE_REG_DIRECT) {
				mov_rr(code, src_op->base, opts->gen.scratch1, SZ_W);
			} else {
				mov_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch1, SZ_W);
			}
		}
		if (inst->op == M68K_MOVE_SR) {
			call(code, opts->set_sr);
			call(code, opts->do_sync);
		} else {
			call(code, opts->set_ccr);
		}
		cycles(&opts->gen, 12);
	}
}

void translate_m68k_stop(m68k_options *opts, m68kinst *inst)
{
	m68k_trap_if_not_supervisor(opts, inst);
	//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
	//On further thought prefetch + the fact that this stops the CPU may make
	//Motorola's accounting make sense here
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, BUS*2);
	set_all_flags(opts,  inst->src.params.immed);
	mov_irdisp(code, (inst->src.params.immed >> 8), opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) {
		//leave supervisor mode
		swap_ssp_usp(opts);
	}
	code_ptr loop_top = code->cur;
		call(code, opts->do_sync);
		cmp_rr(code, opts->gen.cycles, opts->gen.limit, SZ_D);
		code_ptr normal_cycle_up = code->cur + 1;
		jcc(code, CC_A, code->cur + 2);
			cycles(&opts->gen, BUS);
			code_ptr after_cycle_up = code->cur + 1;
			jmp(code, code->cur + 2);
		*normal_cycle_up = code->cur - (normal_cycle_up + 1);
			mov_rr(code, opts->gen.limit, opts->gen.cycles, SZ_D);
		*after_cycle_up = code->cur - (after_cycle_up+1);
		cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D);
	jcc(code, CC_C, loop_top);
	//set int pending flag so interrupt fires immediately after stop is done
	mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
}

void translate_m68k_trapv(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, BUS);
	flag_to_carry(opts, FLAG_V);
	code_ptr no_trap = code->cur + 1;
	jcc(code, CC_NC, no_trap);
	ldi_native(opts, VECTOR_TRAPV, opts->gen.scratch2);
	ldi_native(opts, inst->address+2, opts->gen.scratch1);
	jmp(code, opts->trap);
	*no_trap = code->cur - (no_trap + 1);
}

void translate_m68k_odd(m68k_options *opts, m68kinst *inst)
{
	code_info *code = &opts->gen.code;
	//swap USP and SSP if not already in supervisor mode
	check_user_mode_swap_ssp_usp(opts);
	//save PC
	subi_areg(opts, 4, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), opts->gen.scratch1, SZ_D);
	call(code, opts->write_32_lowfirst);
	//save status register
	subi_areg(opts, 2, 7);
	call(code, opts->get_sr);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//save instruction register
	subi_areg(opts, 2, 7);
	//calculate IR
	push_r(code, opts->gen.context_reg);
	call(code, opts->gen.save_context);
	call_args_abi(code, (code_ptr)m68k_get_ir, 1, opts->gen.context_reg);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_W);
	pop_r(code, opts->gen.context_reg);
	push_r(code, RAX); //save it for use in the "info" word
	call(code, opts->gen.load_context);
	//write it to the stack
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//save access address
	subi_areg(opts, 4, 7);
	mov_ir(code, inst->address, opts->gen.scratch1, SZ_D);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_32_lowfirst);
	//save FC, I/N and R/W word'
	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W);
	//FC3 is basically the same as the supervisor bit
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
	shr_ir(code, 3, opts->gen.scratch1, SZ_B);
	and_ir(code, 4, opts->gen.scratch1, SZ_B);
	//set FC1 to one to indicate instruction fetch, and R/W to indicate read
	or_ir(code, 0x12, opts->gen.scratch1, SZ_B);
	//set undefined bits to IR value
	pop_r(code, opts->gen.scratch2);
	and_ir(code, 0xFFE0, opts->gen.scratch2, SZ_W);
	or_rr(code, opts->gen.scratch2, opts->gen.scratch1, SZ_W);
	subi_areg(opts, 2, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//set supervisor bit
	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	//load vector address
	mov_ir(code, 4 * VECTOR_ADDRESS_ERROR, opts->gen.scratch1, SZ_D);
	call(code, opts->read_32);
	call(code, opts->native_addr_and_sync);
	cycles(&opts->gen, 18);
	jmp_r(code, opts->gen.scratch1);
}

void translate_m68k_move_from_sr(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, inst->dst.addr_mode == MODE_REG ? BUS+2 : BUS);
	call(code, opts->get_sr);
	if (dst_op->mode == MODE_REG_DIRECT) {
		mov_rr(code, opts->gen.scratch1, dst_op->base, SZ_W);
	} else {
		mov_rrdisp(code, opts->gen.scratch1, dst_op->base, dst_op->disp, SZ_W);
	}
	m68k_save_result(inst, opts);
}

void m68k_out_of_bounds_execution(uint32_t address)
{
	fatal_error("M68K attempted to execute code at unmapped or I/O address %X\n", address);
}

void translate_out_of_bounds(m68k_options *opts, uint32_t address)
{
	code_info *code = &opts->gen.code;
	check_cycles_int(&opts->gen, address);
	mov_ir(code, address, opts->gen.scratch1, SZ_D);
	call_args(code, (code_ptr)m68k_out_of_bounds_execution, 1, opts->gen.scratch1);
}

void m68k_set_last_prefetch(m68k_options *opts, uint32_t address)
{
	mov_irdisp(&opts->gen.code, address, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), SZ_D);
}

void nop_fill_or_jmp_next(code_info *code, code_ptr old_end, code_ptr next_inst)
{
	if (next_inst == old_end && next_inst - code->cur < 2) {
		while (code->cur < old_end) {
			*(code->cur++) = 0x90; //NOP
		}
	} else {
		jmp(code, next_inst);
	}
}

#define M68K_MAX_INST_SIZE (2*(1+2+2))

m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context)
{
	m68k_options * options = context->options;
	uint32_t inst_start = get_instruction_start(options, address);
	while (inst_start && (address - inst_start) < M68K_MAX_INST_SIZE) {
		code_ptr dst = get_native_address(context->options, inst_start);
		patch_for_retranslate(&options->gen, dst, options->retrans_stub);
		inst_start = get_instruction_start(options, inst_start - 2);
	}
	return context;
}

void m68k_invalidate_code_range(m68k_context *context, uint32_t start, uint32_t end)
{
	m68k_options *opts = context->options;
	native_map_slot *native_code_map = opts->gen.native_code_map;
	memmap_chunk const *mem_chunk = find_map_chunk(start, &opts->gen, 0, NULL);
	if (mem_chunk) {
		//calculate the lowest alias for this address
		start = mem_chunk->start + ((start - mem_chunk->start) & mem_chunk->mask);
	}
	mem_chunk = find_map_chunk(end, &opts->gen, 0, NULL);
	if (mem_chunk) {
		//calculate the lowest alias for this address
		end = mem_chunk->start + ((end - mem_chunk->start) & mem_chunk->mask);
	}
	uint32_t start_chunk = start / NATIVE_CHUNK_SIZE, end_chunk = end / NATIVE_CHUNK_SIZE;
	for (uint32_t chunk = start_chunk; chunk <= end_chunk; chunk++)
	{
		if (native_code_map[chunk].base) {
			uint32_t start_offset = chunk == start_chunk ? start % NATIVE_CHUNK_SIZE : 0;
			uint32_t end_offset = chunk == end_chunk ? end % NATIVE_CHUNK_SIZE : NATIVE_CHUNK_SIZE;
			for (uint32_t offset = start_offset; offset < end_offset; offset++)
			{
				if (native_code_map[chunk].offsets[offset] != INVALID_OFFSET && native_code_map[chunk].offsets[offset] != EXTENSION_WORD) {
					patch_for_retranslate(&opts->gen, native_code_map[chunk].base + native_code_map[chunk].offsets[offset], opts->retrans_stub);
					/*code_info code;
					code.cur = native_code_map[chunk].base + native_code_map[chunk].offsets[offset];
					code.last = code.cur + 32;
					code.stack_off = 0;
					mov_ir(&code, chunk * NATIVE_CHUNK_SIZE + offset, opts->gen.scratch2, SZ_D);
					jmp(&code, opts->retrans_stub);*/
				}
			}
		}
	}
}

void m68k_breakpoint_patch(m68k_context *context, uint32_t address, m68k_debug_handler bp_handler, code_ptr native_addr)
{
	m68k_options * opts = context->options;
	code_info native;
	native.cur = native_addr ? native_addr : get_native_address(context->options, address);
	
	if (!native.cur) {
		return;
	}
	
	if (*native.cur != opts->prologue_start) {
		//instruction has already been patched, probably for retranslation
		return;
	}
	native.last = native.cur + 128;
	native.stack_off = 0;
	code_ptr start_native = native.cur;
	mov_ir(&native, address, opts->gen.scratch1, SZ_D);
	
	
	call(&native, opts->bp_stub);
}

void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider)
{
	memset(opts, 0, sizeof(*opts));
	opts->gen.memmap = memmap;
	opts->gen.memmap_chunks = num_chunks;
	opts->gen.address_size = SZ_D;
	opts->gen.address_mask = 0xFFFFFF;
	opts->gen.byte_swap = 1;
	opts->gen.max_address = 0x1000000;
	opts->gen.bus_cycles = BUS;
	opts->gen.clock_divider = clock_divider;
	opts->gen.mem_ptr_off = offsetof(m68k_context, mem_pointers);
	opts->gen.ram_flags_off = offsetof(m68k_context, ram_code_flags);
	opts->gen.ram_flags_shift = 11;
	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;

	opts->gen.scratch2 = RDI;
#else
	opts->dregs[0] = RDX;
	opts->aregs[7] = RDI;

	for (int i = 0; i < 5; i++)
	{
		opts->flag_regs[i] = -1;
	}
	opts->gen.scratch2 = RBX;
#endif
	opts->gen.context_reg = RSI;
	opts->gen.cycles = RAX;
	opts->gen.limit = RBP;
	opts->gen.scratch1 = RCX;
	opts->gen.align_error_mask = 1;


	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;

	uint32_t inst_size_size = sizeof(uint8_t *) * ram_size(&opts->gen) / 1024;
	opts->gen.ram_inst_sizes = malloc(inst_size_size);
	memset(opts->gen.ram_inst_sizes, 0, inst_size_size);

	code_info *code = &opts->gen.code;
	init_code_info(code);

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

	opts->gen.load_context = code->cur;
	for (int i = 0; i < 5; i++)
	{
		if (opts->flag_regs[i] >= 0) {
			mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, flags) + i, opts->flag_regs[i], SZ_B);
		}
	}
	for (int i = 0; i < 8; i++)
	{
		if (opts->dregs[i] >= 0) {
			mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, dregs) + sizeof(uint32_t) * i, opts->dregs[i], SZ_D);
		}
		if (opts->aregs[i] >= 0) {
			mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, aregs) + sizeof(uint32_t) * i, opts->aregs[i], SZ_D);
		}
	}
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, current_cycle), opts->gen.cycles, SZ_D);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, target_cycle), opts->gen.limit, SZ_D);
	retn(code);

	opts->start_context = (start_fun)code->cur;
	save_callee_save_regs(code);
#ifdef X86_64
	if (opts->gen.scratch2 != FIRST_ARG_REG) {
		mov_rr(code, FIRST_ARG_REG, opts->gen.scratch2, SZ_PTR);
	}
	if (opts->gen.context_reg != SECOND_ARG_REG) {
		mov_rr(code, SECOND_ARG_REG, opts->gen.context_reg, SZ_PTR);
	}
#else
	mov_rdispr(code, RSP, 20, opts->gen.scratch2, SZ_D);
	mov_rdispr(code, RSP, 24, opts->gen.context_reg, SZ_D);
#endif
	call(code, opts->gen.load_context);
	call_r(code, opts->gen.scratch2);
	call(code, opts->gen.save_context);
	restore_callee_save_regs(code);
	retn(code);

	opts->native_addr = code->cur;
	call(code, opts->gen.save_context);
	push_r(code, opts->gen.context_reg);
	call_args(code, (code_ptr)get_native_address_trans, 2, opts->gen.context_reg, opts->gen.scratch1);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_PTR); //move result to scratch reg
	pop_r(code, opts->gen.context_reg);
	call(code, opts->gen.load_context);
	retn(code);

	opts->native_addr_and_sync = code->cur;
	call(code, opts->gen.save_context);
	push_r(code, opts->gen.scratch1);

	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_D);
	call_args_abi(code, (code_ptr)sync_components, 2, opts->gen.context_reg, opts->gen.scratch1);
	pop_r(code, RSI); //restore saved address from opts->gen.scratch1
	push_r(code, RAX); //save context pointer for later
	call_args(code, (code_ptr)get_native_address_trans, 2, RAX, RSI);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_PTR); //move result to scratch reg
	pop_r(code, opts->gen.context_reg);
	call(code, opts->gen.load_context);
	retn(code);

	opts->gen.handle_cycle_limit = code->cur;
	cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, sync_cycle), opts->gen.cycles, SZ_D);
	code_ptr skip_sync = code->cur + 1;
	jcc(code, CC_C, code->cur + 2);
	opts->do_sync = code->cur;
	push_r(code, opts->gen.scratch1);
	push_r(code, opts->gen.scratch2);
	call(code, opts->gen.save_context);
	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_D);
	call_args_abi(code, (code_ptr)sync_components, 2, opts->gen.context_reg, opts->gen.scratch1);
	mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR);
	call(code, opts->gen.load_context);
	pop_r(code, opts->gen.scratch2);
	pop_r(code, opts->gen.scratch1);
	*skip_sync = code->cur - (skip_sync+1);
	retn(code);

	opts->gen.handle_code_write = (code_ptr)m68k_handle_code_write;
	
	check_alloc_code(code, 256);
	opts->gen.handle_align_error_write = code->cur;
	code->cur += 256;
	check_alloc_code(code, 256);
	opts->gen.handle_align_error_read = code->cur;
	code->cur += 256;
	
	opts->read_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_16, NULL);
	opts->read_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_8, NULL);
	opts->write_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_16, NULL);
	opts->write_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_8, NULL);

	opts->read_32 = code->cur;
	push_r(code, opts->gen.scratch1);
	call(code, opts->read_16);
	mov_rr(code, opts->gen.scratch1, opts->gen.scratch2, SZ_W);
	pop_r(code, opts->gen.scratch1);
	push_r(code, opts->gen.scratch2);
	add_ir(code, 2, opts->gen.scratch1, SZ_D);
	call(code, opts->read_16);
	pop_r(code, opts->gen.scratch2);
	movzx_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W, SZ_D);
	shl_ir(code, 16, opts->gen.scratch2, SZ_D);
	or_rr(code, opts->gen.scratch2, opts->gen.scratch1, SZ_D);
	retn(code);

	opts->write_32_lowfirst = code->cur;
	push_r(code, opts->gen.scratch2);
	push_r(code, opts->gen.scratch1);
	add_ir(code, 2, opts->gen.scratch2, SZ_D);
	call(code, opts->write_16);
	pop_r(code, opts->gen.scratch1);
	pop_r(code, opts->gen.scratch2);
	shr_ir(code, 16, opts->gen.scratch1, SZ_D);
	jmp(code, opts->write_16);

	opts->write_32_highfirst = code->cur;
	push_r(code, opts->gen.scratch1);
	push_r(code, opts->gen.scratch2);
	shr_ir(code, 16, opts->gen.scratch1, SZ_D);
	call(code, opts->write_16);
	pop_r(code, opts->gen.scratch2);
	pop_r(code, opts->gen.scratch1);
	add_ir(code, 2, opts->gen.scratch2, SZ_D);
	jmp(code, opts->write_16);

	opts->get_sr = code->cur;
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
	shl_ir(code, 8, opts->gen.scratch1, SZ_W);
	if (opts->flag_regs[FLAG_X] >= 0) {
		mov_rr(code, opts->flag_regs[FLAG_X], opts->gen.scratch1, SZ_B);
	} else {
		int8_t offset = offsetof(m68k_context, flags);
		if (offset) {
			mov_rdispr(code, opts->gen.context_reg, offset, opts->gen.scratch1, SZ_B);
		} else {
			mov_rindr(code, opts->gen.context_reg, opts->gen.scratch1, SZ_B);
		}
	}
	for (int flag = FLAG_N; flag <= FLAG_C; flag++)
	{
		shl_ir(code, 1, opts->gen.scratch1, SZ_B);
		if (opts->flag_regs[flag] >= 0) {
			or_rr(code, opts->flag_regs[flag], opts->gen.scratch1, SZ_B);
		} else {
			or_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, flags) + flag, opts->gen.scratch1, SZ_B);
		}
	}
	retn(code);

	opts->set_sr = code->cur;
	for (int flag = FLAG_C; flag >= FLAG_X; flag--)
	{
		rcr_ir(code, 1, opts->gen.scratch1, SZ_B);
		if (opts->flag_regs[flag] >= 0) {
			setcc_r(code, CC_C, opts->flag_regs[flag]);
		} else {
			int8_t offset = offsetof(m68k_context, flags) + flag;
			if (offset) {
				setcc_rdisp(code, CC_C, opts->gen.context_reg, offset);
			} else {
				setcc_rind(code, CC_C, opts->gen.context_reg);
			}
		}
	}
	shr_ir(code, 8, opts->gen.scratch1, SZ_W);
	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	//set int pending flag in case we trigger an interrupt as a result of the mask change
	mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	retn(code);

	opts->set_ccr = code->cur;
	for (int flag = FLAG_C; flag >= FLAG_X; flag--)
	{
		rcr_ir(code, 1, opts->gen.scratch1, SZ_B);
		if (opts->flag_regs[flag] >= 0) {
			setcc_r(code, CC_C, opts->flag_regs[flag]);
		} else {
			int8_t offset = offsetof(m68k_context, flags) + flag;
			if (offset) {
				setcc_rdisp(code, CC_C, opts->gen.context_reg, offset);
			} else {
				setcc_rind(code, CC_C, opts->gen.context_reg);
			}
		}
	}
	retn(code);
	
	code_info tmp_code = *code;
	code->cur = opts->gen.handle_align_error_write;
	code->last = code->cur + 256;
	//unwind the stack one functinon call
	add_ir(code, 16, RSP, SZ_PTR);
	//save address that triggered error so we can write it to the 68K stack at the appropriate place
	push_r(code, opts->gen.scratch2);
	//swap USP and SSP if not already in supervisor mode
	check_user_mode_swap_ssp_usp(opts);
	//save PC
	subi_areg(opts, 4, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), opts->gen.scratch1, SZ_D);
	call(code, opts->write_32_lowfirst);
	//save status register
	subi_areg(opts, 2, 7);
	call(code, opts->get_sr);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//save instruction register
	subi_areg(opts, 2, 7);
	//calculate IR
	push_r(code, opts->gen.context_reg);
	call(code, opts->gen.save_context);
	call_args_abi(code, (code_ptr)m68k_get_ir, 1, opts->gen.context_reg);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_W);
	pop_r(code, opts->gen.context_reg);
	pop_r(code, opts->gen.scratch2); //access address
	push_r(code, RAX); //save it for use in the "info" word
	push_r(code, opts->gen.scratch2); //access address
	call(code, opts->gen.load_context);
	//write it to the stack
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//save access address
	subi_areg(opts, 4, 7);
	pop_r(code, opts->gen.scratch1);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_32_lowfirst);
	//save FC, I/N and R/W word'
	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W);
	//FC3 is basically the same as the supervisor bit
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
	shr_ir(code, 3, opts->gen.scratch1, SZ_B);
	and_ir(code, 4, opts->gen.scratch1, SZ_B);
	//set FC0 to one to indicate data access
	or_ir(code, 1, opts->gen.scratch1, SZ_B);
	//set undefined bits to IR value
	pop_r(code, opts->gen.scratch2);
	and_ir(code, 0xFFE0, opts->gen.scratch2, SZ_W);
	or_rr(code, opts->gen.scratch2, opts->gen.scratch1, SZ_W);
	subi_areg(opts, 2, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//set supervisor bit
	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	//load vector address
	mov_ir(code, 4 * VECTOR_ADDRESS_ERROR, opts->gen.scratch1, SZ_D);
	call(code, opts->read_32);
	call(code, opts->native_addr_and_sync);
	cycles(&opts->gen, 18);
	jmp_r(code, opts->gen.scratch1);
	
	code->cur = opts->gen.handle_align_error_read;
	code->last = code->cur + 256;
	//unwind the stack one functinon call
	add_ir(code, 16, RSP, SZ_PTR);
	//save address that triggered error so we can write it to the 68K stack at the appropriate place
	push_r(code, opts->gen.scratch1);
	//swap USP and SSP if not already in supervisor mode
	check_user_mode_swap_ssp_usp(opts);
	//save PC
	subi_areg(opts, 4, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), opts->gen.scratch1, SZ_D);
	call(code, opts->write_32_lowfirst);
	//save status register
	subi_areg(opts, 2, 7);
	call(code, opts->get_sr);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//save instruction register
	subi_areg(opts, 2, 7);
	//calculate IR
	push_r(code, opts->gen.context_reg);
	call(code, opts->gen.save_context);
	call_args_abi(code, (code_ptr)m68k_get_ir, 1, opts->gen.context_reg);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_W);
	pop_r(code, opts->gen.context_reg);
	pop_r(code, opts->gen.scratch2); //access address
	push_r(code, RAX); //save it for use in the "info" word
	push_r(code, opts->gen.scratch2); //access address
	call(code, opts->gen.load_context);
	//write it to the stack
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//save access address
	subi_areg(opts, 4, 7);
	pop_r(code, opts->gen.scratch1);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_32_lowfirst);
	//save FC, I/N and R/W word'
	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W);
	//FC3 is basically the same as the supervisor bit
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
	shr_ir(code, 3, opts->gen.scratch1, SZ_B);
	and_ir(code, 4, opts->gen.scratch1, SZ_B);
	//set FC0 to one to indicate data access, and R/W to indicate read
	or_ir(code, 0x11, opts->gen.scratch1, SZ_B);
	//set undefined bits to IR value
	pop_r(code, opts->gen.scratch2);
	and_ir(code, 0xFFE0, opts->gen.scratch2, SZ_W);
	or_rr(code, opts->gen.scratch2, opts->gen.scratch1, SZ_W);
	subi_areg(opts, 2, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//set supervisor bit
	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	//load vector address
	mov_ir(code, 4 * VECTOR_ADDRESS_ERROR, opts->gen.scratch1, SZ_D);
	call(code, opts->read_32);
	call(code, opts->native_addr_and_sync);
	cycles(&opts->gen, 18);
	jmp_r(code, opts->gen.scratch1);
	
	*code = tmp_code;

	opts->gen.handle_cycle_limit_int = code->cur;
	//calculate stack adjust size
	add_ir(code, 16-sizeof(void*), RSP, SZ_PTR);
	uint32_t adjust_size = code->cur - opts->gen.handle_cycle_limit_int;
	code->cur = opts->gen.handle_cycle_limit_int;
	//handle trace mode
	cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, trace_pending), SZ_B);
	code_ptr do_trace = code->cur + 1;
	jcc(code, CC_NZ, do_trace);
	bt_irdisp(code, 7, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	code_ptr no_trace = code->cur + 1;
	jcc(code, CC_NC, no_trace);
	mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, trace_pending), SZ_B);
	*no_trace = code->cur - (no_trace + 1);
	//handle interrupts
	cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D);
	code_ptr do_int = code->cur + 2; 
	jcc(code, CC_NC, do_int+512);//force 32-bit displacement
	//handle component synchronization
	cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, sync_cycle), opts->gen.cycles, SZ_D);
	skip_sync = code->cur + 1;
	jcc(code, CC_C, code->cur + 2);
	call(code, opts->gen.save_context);
	call_args_abi(code, (code_ptr)sync_components, 2, opts->gen.context_reg, opts->gen.scratch1);
	mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR);
	jmp(code, opts->gen.load_context);
	*skip_sync = code->cur - (skip_sync+1);
	cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, should_return), SZ_B);
	code_ptr do_ret = code->cur + 1;
	jcc(code, CC_NZ, do_ret);
	retn(code);
	*do_ret = code->cur - (do_ret+1);
	uint32_t tmp_stack_off = code->stack_off;
	//fetch return address and adjust RSP
	pop_r(code, opts->gen.scratch1);
	add_ir(code, 16-sizeof(void *), RSP, SZ_PTR);
	add_ir(code, adjust_size, opts->gen.scratch1, SZ_PTR);
	//save return address for restoring later
	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, resume_pc), SZ_PTR);
	retn(code);
	code->stack_off = tmp_stack_off;
	*do_trace = code->cur - (do_trace + 1);
	//clear out trace pending flag
	mov_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, trace_pending), SZ_B);
	//save PC as stored in scratch1 for later
	push_r(code, opts->gen.scratch1);
	//swap USP and SSP if not already in supervisor mode
	check_user_mode_swap_ssp_usp(opts);
	//save status register
	subi_areg(opts, 6, 7);
	call(code, opts->get_sr);
	cycles(&opts->gen, 6);
	//save SR to stack
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//update the status register
	and_irdisp(code, 0x7F, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	//save PC
	areg_to_native(opts, 7, opts->gen.scratch2);
	add_ir(code, 2, opts->gen.scratch2, SZ_D);
	pop_r(code, opts->gen.scratch1);
	call(code, opts->write_32_lowfirst);
	//read vector
	mov_ir(code, 0x24, opts->gen.scratch1, SZ_D);
	call(code, opts->read_32);
	call(code, opts->native_addr_and_sync);
	//2 prefetch bus operations + 2 idle bus cycles
	cycles(&opts->gen, 10);
	//discard function return address
	pop_r(code, opts->gen.scratch2);
	add_ir(code, 16-sizeof(void *), RSP, SZ_PTR);
	jmp_r(code, opts->gen.scratch1);
	
	code->stack_off = tmp_stack_off;
	
	*((uint32_t *)do_int) = code->cur - (do_int+4);
	//implement 1 instruction latency
	cmp_irdisp(code, INT_PENDING_NONE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	do_int = code->cur + 1;
	jcc(code, CC_NZ, do_int);
	//store current interrupt number so it doesn't change before we start processing the vector
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch1, SZ_B);
	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	retn(code);
	*do_int = code->cur - (do_int + 1);
	//Check if int_pending has an actual interrupt priority in it
	cmp_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	code_ptr already_int_num = code->cur + 1;
	jcc(code, CC_NZ, already_int_num);
	
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch2, SZ_B);
	mov_rrdisp(code, opts->gen.scratch2, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	
	*already_int_num = code->cur - (already_int_num + 1);
	//save PC as stored in scratch1 for later
	push_r(code, opts->gen.scratch1);
	//set target cycle to sync cycle
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, sync_cycle), opts->gen.limit, SZ_D);
	//swap USP and SSP if not already in supervisor mode
	check_user_mode_swap_ssp_usp(opts);
	//save status register
	subi_areg(opts, 6, 7);
	call(code, opts->get_sr);
	//6 cycles before SR gets saved
	cycles(&opts->gen, 6);
	//save SR to stack
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//interrupt ack cycle
	//the Genesis responds to these exclusively with !VPA which means its a slow
	//6800 operation. documentation says these can take between 10 and 19 cycles.
	//actual results measurements seem to suggest it's actually between 9 and 18
	//WARNING: this code might break with register assignment changes
	//save RDX
	push_r(code, RDX);
	//save cycle count
	mov_rr(code, RAX, opts->gen.scratch1, SZ_D);
	//clear top doubleword of dividend
	xor_rr(code, RDX, RDX, SZ_D);
	//set divisor to clock divider
	mov_ir(code, opts->gen.clock_divider, opts->gen.scratch2, SZ_D);
	div_r(code, opts->gen.scratch2, SZ_D);
	//discard remainder
	xor_rr(code, RDX, RDX, SZ_D);
	//set divisor to 10, the period of E
	mov_ir(code, 10, opts->gen.scratch2, SZ_D);
	div_r(code, opts->gen.scratch2, SZ_D);
	//delay will be (9 + 4 + the remainder) * clock_divider
	//the extra 4 is to cover the idle bus period after the ack
	add_ir(code, 9 + 4, RDX, SZ_D);
	mov_ir(code, opts->gen.clock_divider, RAX, SZ_D);
	mul_r(code, RDX, SZ_D);
	pop_r(code, RDX);
	//add saved cycle count to result
	add_rr(code, opts->gen.scratch1, RAX, SZ_D);

	//update status register
	and_irdisp(code, 0x78, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch1, SZ_B);
	//clear trace pending flag
	mov_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, trace_pending), SZ_B);
	//need to separate int priority and interrupt vector, but for now mask out large interrupt numbers
	and_ir(code, 0x7, opts->gen.scratch1, SZ_B);
	or_ir(code, 0x20, opts->gen.scratch1, SZ_B);
	or_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);

	pop_r(code, opts->gen.scratch1);

	//save PC
	areg_to_native(opts, 7, opts->gen.scratch2);
	add_ir(code, 2, opts->gen.scratch2, SZ_D);
	call(code, opts->write_32_lowfirst);

	//grab saved interrupt number
	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_D);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_pending), opts->gen.scratch1, SZ_B);
	//ack the interrupt (happens earlier on hardware, but shouldn't be an observable difference)
	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, int_ack), SZ_W);
	//calculate the vector address
	shl_ir(code, 2, opts->gen.scratch1, SZ_D);
	add_ir(code, 0x60, opts->gen.scratch1, SZ_D);
	//clear out pending flag
	mov_irdisp(code, INT_PENDING_NONE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	//read vector
	call(code, opts->read_32);
	call(code, opts->native_addr_and_sync);
	//2 prefetch bus operations + 2 idle bus cycles
	cycles(&opts->gen, 10);
	tmp_stack_off = code->stack_off;
	//discard function return address
	pop_r(code, opts->gen.scratch2);
	add_ir(code, 16-sizeof(void *), RSP, SZ_PTR);
	jmp_r(code, opts->gen.scratch1);
	code->stack_off = tmp_stack_off;
	
	opts->handle_int_latch = code->cur;
	cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D);
	code_ptr do_latch = code->cur + 1; 
	jcc(code, CC_NC, do_latch);
	retn(code);
	*do_latch = code->cur - (do_latch + 1);
	cmp_irdisp(code, INT_PENDING_NONE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	do_latch = code->cur + 1;
	jcc(code, CC_Z, do_latch);
	retn(code);
	*do_latch = code->cur - (do_latch + 1);
	//store current interrupt number so it doesn't change before we start processing the vector
	push_r(code, opts->gen.scratch1);
	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch1, SZ_B);
	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	pop_r(code, opts->gen.scratch1);
	retn(code);

	opts->trap = code->cur;
	push_r(code, opts->gen.scratch2);
	//swap USP and SSP if not already in supervisor mode
	check_user_mode_swap_ssp_usp(opts);
	//save PC
	subi_areg(opts, 4, 7);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_32_lowfirst);
	//save status register
	subi_areg(opts, 2, 7);
	call(code, opts->get_sr);
	areg_to_native(opts, 7, opts->gen.scratch2);
	call(code, opts->write_16);
	//set supervisor bit
	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	//clear trace bit
	and_irdisp(code, 0x7F, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
	mov_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, trace_pending), SZ_B);
	//calculate vector address
	pop_r(code, opts->gen.scratch1);
	shl_ir(code, 2, opts->gen.scratch1, SZ_D);
	call(code, opts->read_32);
	call(code, opts->native_addr_and_sync);
	cycles(&opts->gen, 18);
	jmp_r(code, opts->gen.scratch1);
	
	opts->retrans_stub = code->cur;
	call(code, opts->gen.save_context);
	push_r(code, opts->gen.context_reg);
	call_args(code,(code_ptr)m68k_retranslate_inst, 2, opts->gen.scratch1, opts->gen.context_reg);
	pop_r(code, opts->gen.context_reg);
	mov_rr(code, RAX, opts->gen.scratch1, SZ_PTR);
	call(code, opts->gen.load_context);
	jmp_r(code, opts->gen.scratch1);
	
	
	check_code_prologue(code);
	opts->bp_stub = code->cur;

	tmp_stack_off = code->stack_off;
	//Calculate length of prologue
	check_cycles_int(&opts->gen, 0x1234);
	int check_int_size = code->cur-opts->bp_stub;
	code->cur = opts->bp_stub;
	code->stack_off = tmp_stack_off;
	opts->prologue_start = *opts->bp_stub;
	//Calculate length of patch
	mov_ir(code, 0x1234, opts->gen.scratch1, SZ_D);
	call(code, opts->bp_stub);
	int patch_size = code->cur - opts->bp_stub;
	code->cur = opts->bp_stub;
	code->stack_off = tmp_stack_off;

	//Save context and call breakpoint handler
	call(code, opts->gen.save_context);
	push_r(code, opts->gen.scratch1);
	call_args_abi(code, (code_ptr)m68k_bp_dispatcher, 2, opts->gen.context_reg, opts->gen.scratch1);
	mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR);
	//Restore context
	call(code, opts->gen.load_context);
	pop_r(code, opts->gen.scratch1);
	//do prologue stuff
	cmp_rr(code, opts->gen.cycles, opts->gen.limit, SZ_D);
	code_ptr jmp_off = code->cur + 1;
	jcc(code, CC_NC, code->cur + 7);
	call(code, opts->gen.handle_cycle_limit_int);
	*jmp_off = code->cur - (jmp_off+1);
	//jump back to body of translated instruction
	pop_r(code, opts->gen.scratch1);
	add_ir(code, check_int_size - patch_size, opts->gen.scratch1, SZ_PTR);
	jmp_r(code, opts->gen.scratch1);
	code->stack_off = tmp_stack_off;
	
	retranslate_calc(&opts->gen);
}
