view m68k_core_x86.c @ 995:2bc27415565b

Fix some stuff with interrupt timing. The change in adjust_int_cycle gets Overdrive working again (vint was not being preferred over hint in some cases). One of the changes seems to have broken Fatal Rewind again, but no other regressions that I can see.
author Michael Pavone <pavone@retrodev.com>
date Sat, 30 Apr 2016 08:37:55 -0700
parents 33a46d35b913
children 784bc1e45e80
line wrap: on
line source

/*
 Copyright 2013 Michael Pavone
 This file is part of BlastEm.
 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
*/
#include "gen_x86.h"
#include "m68k_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 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;
	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;
		} else {
			ea->base = reg;
		}
		return;
	}
	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;
		}
		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) {
			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;
	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;
	}
}

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;
	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) {
		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);
		}
	}

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

void translate_m68k_clr(m68k_options * opts, m68kinst * inst)
{
	code_info *code = &opts->gen.code;
	update_flags(opts, N0|V0|C0|Z1);
	int8_t reg = native_reg(&(inst->dst), opts);
	if (reg >= 0) {
		cycles(&opts->gen, (inst->extra.size == OPSIZE_LONG ? 6 : 4));
		xor_rr(code, reg, reg, inst->extra.size);
		return;
	}
	host_ea dst_op;
	//TODO: fix timing
	translate_m68k_op(inst, &dst_op, opts, 1);
	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);
	}
	m68k_save_result(inst, opts);
}

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);
	//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;
	cycles(&opts->gen, 10);//TODO: Adjust this for branch not taken case
	int32_t disp = inst->src.params.immed;
	uint32_t after = inst->address + 2;
	if (inst->extra.cond == COND_TRUE) {
		jump_m68k_abs(opts, after + disp);
	} else {
		code_ptr dest_addr = get_native_address(opts, after + disp);
		uint8_t cond = m68k_eval_cond(opts, inst->extra.cond);
		if (!dest_addr) {
			opts->gen.deferred = defer_address(opts->gen.deferred, after + disp, code->cur + 2);
			//dummy address to be replaced later, make sure it generates a 4-byte displacement
			dest_addr = code->cur + 256;
		}
		jcc(code, cond, dest_addr);
	}
}

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, 6);
		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);
				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 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;
	cycles(&opts->gen, BUS);
	if (inst->op == M68K_ADDX || inst->op == M68K_SUBX) {
		flag_to_carry(opts, FLAG_X);
	}
	uint8_t size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size;
	if (src_op->mode == MODE_REG_DIRECT) {
		if (dst_op->mode == MODE_REG_DIRECT) {
			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_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_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;
	cycles(&opts->gen, BUS);
	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);
			}
		}
	}
	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, 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);
	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);
		}
	}
	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);
}

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);
	//TODO: cycle exact division
	cycles(&opts->gen, inst->op == M68K_DIVS ? 158 : 140);
	set_flag(opts, 0, FLAG_C);
	push_r(code, RDX);
	push_r(code, RAX);
	uint32_t tmp_stack_off = code->stack_off;
	if (dst_op->mode == MODE_REG_DIRECT) {
		mov_rr(code, dst_op->base, RAX, SZ_D);
	} else {
		mov_rdispr(code, dst_op->base, dst_op->disp, RAX, SZ_D);
	}
	if (src_op->mode == MODE_IMMED) {
		mov_ir(code, (src_op->disp & 0x8000) && inst->op == M68K_DIVS ? src_op->disp | 0xFFFF0000 : src_op->disp, opts->gen.scratch2, SZ_D);
	} else if (src_op->mode == MODE_REG_DIRECT) {
		if (inst->op == M68K_DIVS) {
			movsx_rr(code, src_op->base, opts->gen.scratch2, SZ_W, SZ_D);
		} else {
			movzx_rr(code, src_op->base, opts->gen.scratch2, SZ_W, SZ_D);
		}
	} else if (src_op->mode == MODE_REG_DISPLACE8) {
		if (inst->op == M68K_DIVS) {
			movsx_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch2, SZ_W, SZ_D);
		} else {
			movzx_rdispr(code, src_op->base, src_op->disp, opts->gen.scratch2, SZ_W, SZ_D);
		}
	}
	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_INDEX_DISP8:
	case MODE_IMMEDIATE:
		isize = 4;
		break;
	case MODE_ABSOLUTE:
		isize = 6;
		break;
	}
	cmp_ir(code, 0, opts->gen.scratch2, SZ_D);
	check_alloc_code(code, 6*MAX_INST_LEN);
	code_ptr not_zero = code->cur + 1;
	jcc(code, CC_NZ, code->cur + 2);
	pop_r(code, RAX);
	pop_r(code, RDX);
	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);

	code->stack_off = tmp_stack_off;
	*not_zero = code->cur - (not_zero+1);
	if (inst->op == M68K_DIVS) {
		cdq(code);
	} else {
		xor_rr(code, RDX, RDX, SZ_D);
	}
	if (inst->op == M68K_DIVS) {
		idiv_r(code, opts->gen.scratch2, SZ_D);
	} else {
		div_r(code, opts->gen.scratch2, SZ_D);
	}
	code_ptr skip_sec_check, norm_off;
	if (inst->op == M68K_DIVS) {
		cmp_ir(code, 0x8000, RAX, SZ_D);
		skip_sec_check = code->cur + 1;
		jcc(code, CC_GE, code->cur + 2);
		cmp_ir(code, -0x8000, RAX, SZ_D);
		norm_off = code->cur + 1;
		jcc(code, CC_L, code->cur + 2);
	} else {
		cmp_ir(code, 0x10000, RAX, SZ_D);
		norm_off = code->cur + 1;
		jcc(code, CC_NC, code->cur + 2);
	}
	if (dst_op->mode == MODE_REG_DIRECT) {
		mov_rr(code, RDX, dst_op->base, SZ_W);
		shl_ir(code, 16, dst_op->base, SZ_D);
		mov_rr(code, RAX, dst_op->base, SZ_W);
	} else {
		mov_rrdisp(code, RDX, dst_op->base, dst_op->disp, SZ_W);
		shl_irdisp(code, 16, dst_op->base, dst_op->disp, SZ_D);
		mov_rrdisp(code, RAX, dst_op->base, dst_op->disp, SZ_W);
	}
	cmp_ir(code, 0, RAX, SZ_W);
	pop_r(code, RAX);
	if (dst_op->base == RDX) {
		update_flags(opts, V0|Z|N);
		add_ir(code, sizeof(void *), RSP, SZ_D);
	} else {
		pop_r(code, RDX);
		update_flags(opts, V0|Z|N);
	}
	code_ptr end_off = code->cur + 1;
	jmp(code, code->cur + 2);
	code->stack_off = tmp_stack_off;
	*norm_off = code->cur - (norm_off + 1);
	if (inst->op == M68K_DIVS) {
		*skip_sec_check = code->cur - (skip_sec_check+1);
	}
	pop_r(code, RAX);
	pop_r(code, RDX);
	set_flag(opts, 1, FLAG_V);
	*end_off = code->cur - (end_off + 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);
		}
	}
}

void translate_m68k_mul(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
{
	code_info *code = &opts->gen.code;
	cycles(&opts->gen, 70); //TODO: Calculate the actual value based on the value of the <ea> parameter
	if (src_op->mode == MODE_IMMED) {
		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);
		}
	}
	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, 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 {
			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 & 0x700)) {
			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, 1, 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 & 0x700) {
			//set int pending flag in case we trigger an interrupt as a result of the mask change
			mov_irdisp(code, 1, 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, 1, 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.limit, opts->gen.cycles, 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, 1, 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;
	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 translate_out_of_bounds(code_info *code)
{
	xor_rr(code, RDI, RDI, SZ_D);
	call_args(code, (code_ptr)exit, 1, RDI);
}

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);
	}
}

m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context)
{
	m68k_options * options = context->options;
	//TODO: Modify gen_mem_fun so that it passes the raw address instead of the masked one, then remove the OR below
	uint32_t inst_start = get_instruction_start(options, context->native_code_map, address | 0xE00000);
	if (inst_start) {
		code_info *code = &options->gen.code;
		code_ptr dst = get_native_address(context->options, inst_start);
		code_info orig = {dst, dst + 128, 0};
		mov_ir(&orig, inst_start, options->gen.scratch2, SZ_D);

		if (!options->retrans_stub) {
			options->retrans_stub = code->cur;
			call(code, options->gen.save_context);
			push_r(code, options->gen.context_reg);
			call_args(code,(code_ptr)m68k_retranslate_inst, 2, options->gen.scratch2, options->gen.context_reg);
			pop_r(code, options->gen.context_reg);
			mov_rr(code, RAX, options->gen.scratch1, SZ_PTR);
			call(code, options->gen.load_context);
			jmp_r(code, options->gen.scratch1);
		}
		jmp(&orig, options->retrans_stub);
	}
	return context;
}

void insert_breakpoint(m68k_context * context, uint32_t address, code_ptr bp_handler)
{
	static code_ptr bp_stub = NULL;
	m68k_options * opts = context->options;
	code_info native;
	native.cur = get_native_address_trans(context, address);
	native.last = native.cur + 128;
	native.stack_off = 0;
	code_ptr start_native = native.cur;
	mov_ir(&native, address, opts->gen.scratch1, SZ_D);
	if (!bp_stub) {
		code_info *code = &opts->gen.code;
		check_code_prologue(code);
		bp_stub = code->cur;
		call(&native, bp_stub);

		uint32_t tmp_stack_off = code->stack_off;
		//Calculate length of prologue
		check_cycles_int(&opts->gen, address);
		int check_int_size = code->cur-bp_stub;
		code->cur = 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, bp_handler, 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 - (native.cur-start_native), opts->gen.scratch1, SZ_PTR);
		jmp_r(code, opts->gen.scratch1);
		code->stack_off = tmp_stack_off;
	} else {
		call(&native, 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 != RDI) {
		mov_rr(code, RDI, opts->gen.scratch2, 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, 1, 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;

	cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D);
	code_ptr do_int = code->cur + 1;
	jcc(code, CC_NC, code->cur + 2);
	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_int = code->cur - (do_int+1);
	//implement 1 instruction latency
	cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	do_int = code->cur + 1;
	jcc(code, CC_NZ, do_int);
	mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
	retn(code);
	*do_int = code->cur - (do_int + 1);
	//save interrupt number so it can't change during interrupt processing
	push_rdisp(code, opts->gen.context_reg, offsetof(m68k_context, int_num));
	//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, 0xF8, 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);
	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);

	//restore saved interrupt number
	pop_r(code, opts->gen.scratch1);
	//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);
	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->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);
	//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);
}