Mercurial > repos > blastem
view m68k_to_x86.c @ 561:666adeba5a61
Save and restore guest address in the write function for a code memory chunk in the "slow" path for inconvenient host addresses. This fixes an intermittent crash on OSX in the code that checks whether the memory written may contain code
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 23 Jun 2014 11:46:56 -0400 |
parents | f9431cb3a39c |
children |
line wrap: on
line source
/* Copyright 2013 Michael Pavone This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "gen_x86.h" #include "m68k_to_x86.h" #include "68kinst.h" #include "mem.h" #include "backend.h" #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #define BUS 4 #define PREDEC_PENALTY 2 #define CYCLES RAX #define LIMIT RBP #define CONTEXT RSI #define SCRATCH1 RCX #ifdef X86_64 #define SCRATCH2 RDI #else #define SCRATCH2 RBX #endif enum { FLAG_X, FLAG_N, FLAG_Z, FLAG_V, FLAG_C }; char disasm_buf[1024]; m68k_context * sync_components(m68k_context * context, uint32_t address); extern void bcd_add() asm("bcd_add"); extern void bcd_sub() asm("bcd_sub"); void m68k_invalid(uint32_t address, m68k_context * context) { printf("Invalid instruction at %X\n", address); exit(1); } code_ptr cycles(code_ptr dst, uint32_t num) { dst = add_ir(dst, num, CYCLES, SZ_D); return dst; } code_ptr check_cycles_int(code_ptr dst, uint32_t address, x86_68k_options * opts) { dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D); code_ptr jmp_off = dst+1; dst = jcc(dst, CC_NC, dst + 7); dst = mov_ir(dst, address, SCRATCH1, SZ_D); dst = call(dst, opts->gen.handle_cycle_limit_int); *jmp_off = dst - (jmp_off+1); return dst; } code_ptr check_cycles(code_ptr dst, cpu_options * opts) { dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D); code_ptr jmp_off = dst+1; dst = jcc(dst, CC_NC, dst + 7); dst = call(dst, opts->handle_cycle_limit); *jmp_off = dst - (jmp_off+1); return dst; } code_ptr set_flag(code_ptr dst, uint8_t val, uint8_t flag, x86_68k_options * opts) { if (opts->flag_regs[flag] >= 0) { dst = mov_ir(dst, val, opts->flag_regs[flag], SZ_B); } else { int8_t offset = offsetof(m68k_context, flags) + flag; if (offset) { dst = mov_irdisp8(dst, val, CONTEXT, offset, SZ_B); } else { dst = mov_irind(dst, val, CONTEXT, SZ_B); } } return dst; } code_ptr set_flag_cond(code_ptr dst, uint8_t cond, uint8_t flag, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = setcc_r(dst, cond, opts->flag_regs[flag]); } else { int8_t offset = offsetof(m68k_context, flags) + flag; if (offset) { dst = setcc_rdisp8(dst, cond, CONTEXT, offset); } else { dst = setcc_rind(dst, cond, CONTEXT); } } return dst; } code_ptr check_flag(code_ptr dst, uint8_t flag, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = cmp_ir(dst, 0, opts->flag_regs[flag], SZ_B); } else { dst = cmp_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, flags) + flag, SZ_B); } return dst; } code_ptr flag_to_reg(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = mov_rr(dst, opts->flag_regs[flag], reg, SZ_B); } else { int8_t offset = offsetof(m68k_context, flags) + flag; if (offset) { dst = mov_rdisp8r(dst, CONTEXT, offset, reg, SZ_B); } else { dst = mov_rindr(dst, CONTEXT, reg, SZ_B); } } return dst; } code_ptr reg_to_flag(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = mov_rr(dst, reg, opts->flag_regs[flag], SZ_B); } else { int8_t offset = offsetof(m68k_context, flags) + flag; if (offset) { dst = mov_rrdisp8(dst, reg, CONTEXT, offset, SZ_B); } else { dst = mov_rrind(dst, reg, CONTEXT, SZ_B); } } return dst; } code_ptr flag_to_flag(code_ptr dst, uint8_t flag1, uint8_t flag2, x86_68k_options *opts) { if (opts->flag_regs[flag1] >= 0 && opts->flag_regs[flag2] >= 0) { dst = mov_rr(dst, opts->flag_regs[flag1], opts->flag_regs[flag2], SZ_B); } else if(opts->flag_regs[flag1] >= 0) { dst = mov_rrdisp8(dst, opts->flag_regs[flag1], CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B); } else if (opts->flag_regs[flag2] >= 0) { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag1, opts->flag_regs[flag2], SZ_B); } else { dst = push_r(dst, SCRATCH1); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag1, SCRATCH1, SZ_B); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B); dst = pop_r(dst, SCRATCH1); } return dst; } code_ptr flag_to_carry(code_ptr dst, uint8_t flag, x86_68k_options * opts) { if (opts->flag_regs[flag] >= 0) { dst = bt_ir(dst, 0, opts->flag_regs[flag], SZ_B); } else { dst = bt_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, flags) + flag, SZ_B); } return dst; } code_ptr or_flag_to_reg(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = or_rr(dst, opts->flag_regs[flag], reg, SZ_B); } else { dst = or_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag, reg, SZ_B); } return dst; } code_ptr xor_flag_to_reg(code_ptr dst, uint8_t flag, uint8_t reg, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = xor_rr(dst, opts->flag_regs[flag], reg, SZ_B); } else { dst = xor_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag, reg, SZ_B); } return dst; } code_ptr xor_flag(code_ptr dst, uint8_t val, uint8_t flag, x86_68k_options *opts) { if (opts->flag_regs[flag] >= 0) { dst = xor_ir(dst, val, opts->flag_regs[flag], SZ_B); } else { dst = xor_irdisp8(dst, val, CONTEXT, offsetof(m68k_context, flags) + flag, SZ_B); } return dst; } code_ptr cmp_flags(code_ptr dst, uint8_t flag1, uint8_t flag2, x86_68k_options *opts) { if (opts->flag_regs[flag1] >= 0 && opts->flag_regs[flag2] >= 0) { dst = cmp_rr(dst, opts->flag_regs[flag1], opts->flag_regs[flag2], SZ_B); } else if(opts->flag_regs[flag1] >= 0 || opts->flag_regs[flag2] >= 0) { if (opts->flag_regs[flag2] >= 0) { uint8_t tmp = flag1; flag1 = flag2; flag2 = tmp; } dst = cmp_rrdisp8(dst, opts->flag_regs[flag1], CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag1, SCRATCH1, SZ_B); dst = cmp_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, flags) + flag2, SZ_B); } return dst; } int8_t native_reg(m68k_op_info * op, x86_68k_options * opts) { if (op->addr_mode == MODE_REG) { return opts->dregs[op->params.regs.pri]; } if (op->addr_mode == MODE_AREG) { return opts->aregs[op->params.regs.pri]; } return -1; } //must be called with an m68k_op_info that uses a register size_t reg_offset(m68k_op_info *op) { if (op->addr_mode == MODE_REG) { return offsetof(m68k_context, dregs) + sizeof(uint32_t) * op->params.regs.pri; } return offsetof(m68k_context, aregs) + sizeof(uint32_t) * op->params.regs.pri; } void print_regs_exit(m68k_context * context) { printf("XNZVC\n%d%d%d%d%d\n", context->flags[0], context->flags[1], context->flags[2], context->flags[3], context->flags[4]); for (int i = 0; i < 8; i++) { printf("d%d: %X\n", i, context->dregs[i]); } for (int i = 0; i < 8; i++) { printf("a%d: %X\n", i, context->aregs[i]); } exit(0); } code_ptr translate_m68k_src(m68kinst * inst, x86_ea * ea, code_ptr out, x86_68k_options * opts) { int8_t reg = native_reg(&(inst->src), opts); uint8_t sec_reg; int32_t dec_amount,inc_amount; if (reg >= 0) { ea->mode = MODE_REG_DIRECT; if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) { out = movsx_rr(out, reg, SCRATCH1, SZ_W, SZ_D); ea->base = SCRATCH1; } else { ea->base = reg; } return out; } switch (inst->src.addr_mode) { case MODE_REG: case MODE_AREG: //We only get one memory parameter, so if the dst operand is a register in memory, //we need to copy this to a temp register first reg = native_reg(&(inst->dst), opts); if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || !(inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) || inst->op == M68K_EXG) { ea->mode = MODE_REG_DISPLACE8; ea->base = CONTEXT; ea->disp = reg_offset(&(inst->src)); } else { if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) { out = movsx_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_W, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, inst->extra.size); } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; //we're explicitly handling the areg dest here, so we exit immediately return out; } break; case MODE_AREG_PREDEC: dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->src.params.regs.pri == 7 ? 2 :1)); out = cycles(out, PREDEC_PENALTY); if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = sub_ir(out, dec_amount, opts->aregs[inst->src.params.regs.pri], SZ_D); } else { out = sub_irdisp8(out, dec_amount, CONTEXT, reg_offset(&(inst->src)), SZ_D); } case MODE_AREG_INDIRECT: case MODE_AREG_POSTINC: if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } if (inst->src.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->src.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = add_ir(out, inc_amount, opts->aregs[inst->src.params.regs.pri], SZ_D); } else { out = add_irdisp8(out, inc_amount, CONTEXT, reg_offset(&(inst->src)), SZ_D); } } ea->mode = MODE_REG_DIRECT; ea->base = (inst->dst.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) ? SCRATCH2 : SCRATCH1; break; case MODE_AREG_DISPLACE: out = cycles(out, BUS); if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } out = add_ir(out, inst->src.params.regs.displacement, SCRATCH1, SZ_D); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_AREG_INDEX_DISP8: out = cycles(out, 6); if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { out = add_ir(out, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_PC_DISPLACE: out = cycles(out, BUS); out = mov_ir(out, inst->src.params.regs.displacement + inst->address+2, SCRATCH1, SZ_D); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_PC_INDEX_DISP8: out = cycles(out, 6); out = mov_ir(out, inst->address+2, SCRATCH1, SZ_D); sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { out = add_ir(out, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: if (inst->src.addr_mode == MODE_ABSOLUTE) { out = cycles(out, BUS*2); } else { out = cycles(out, BUS); } out = mov_ir(out, inst->src.params.immed, SCRATCH1, SZ_D); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_IMMEDIATE: case MODE_IMMEDIATE_WORD: if (inst->variant != VAR_QUICK) { out = cycles(out, (inst->extra.size == OPSIZE_LONG && inst->src.addr_mode == MODE_IMMEDIATE) ? BUS*2 : BUS); } ea->mode = MODE_IMMED; ea->disp = inst->src.params.immed; if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD && ea->disp & 0x8000) { ea->disp |= 0xFFFF0000; } return out; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (src)\n", inst->address, disasm_buf, inst->src.addr_mode); exit(1); } if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) { if (ea->mode == MODE_REG_DIRECT) { out = movsx_rr(out, ea->base, SCRATCH1, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, ea->base, ea->disp, SCRATCH1, SZ_W, SZ_D); ea->mode = MODE_REG_DIRECT; } ea->base = SCRATCH1; } return out; } code_ptr translate_m68k_dst(m68kinst * inst, x86_ea * ea, code_ptr out, x86_68k_options * opts, uint8_t fake_read) { int8_t reg = native_reg(&(inst->dst), opts), sec_reg; int32_t dec_amount, inc_amount; if (reg >= 0) { ea->mode = MODE_REG_DIRECT; ea->base = reg; return out; } switch (inst->dst.addr_mode) { case MODE_REG: case MODE_AREG: ea->mode = MODE_REG_DISPLACE8; ea->base = CONTEXT; ea->disp = reg_offset(&(inst->dst)); break; case MODE_AREG_PREDEC: if (inst->src.addr_mode == MODE_AREG_PREDEC) { out = push_r(out, SCRATCH1); } dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = sub_ir(out, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); } else { out = sub_irdisp8(out, dec_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } case MODE_AREG_INDIRECT: case MODE_AREG_POSTINC: if (fake_read) { out = cycles(out, inst->extra.size == OPSIZE_LONG ? 8 : 4); } else { if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH1, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH1, SZ_D); } switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } } if (inst->src.addr_mode == MODE_AREG_PREDEC) { //restore src operand to SCRATCH2 out =pop_r(out, SCRATCH2); } else { //save reg value in SCRATCH2 so we can use it to save the result in memory later if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } } if (inst->dst.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = add_ir(out, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); } else { out = add_irdisp8(out, inc_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_AREG_DISPLACE: out = cycles(out, fake_read ? BUS+(inst->extra.size == OPSIZE_LONG ? BUS*2 : BUS) : BUS); reg = fake_read ? SCRATCH2 : SCRATCH1; if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], reg, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), reg, SZ_D); } out = add_ir(out, inst->dst.params.regs.displacement, reg, SZ_D); if (!fake_read) { out = push_r(out, SCRATCH1); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } out = pop_r(out, SCRATCH2); } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_AREG_INDEX_DISP8: out = cycles(out, fake_read ? (6 + inst->extra.size == OPSIZE_LONG ? 8 : 4) : 6); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH1, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH1, SZ_D); } sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7; if (inst->dst.params.regs.sec & 1) { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D); } if (inst->dst.params.regs.displacement) { out = add_ir(out, inst->dst.params.regs.displacement, SCRATCH1, SZ_D); } if (fake_read) { out = mov_rr(out, SCRATCH1, SCRATCH2, SZ_D); } else { out = push_r(out, SCRATCH1); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } out = pop_r(out, SCRATCH2); } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_PC_DISPLACE: out = cycles(out, fake_read ? BUS+(inst->extra.size == OPSIZE_LONG ? BUS*2 : BUS) : BUS); out = mov_ir(out, inst->dst.params.regs.displacement + inst->address+2, fake_read ? SCRATCH2 : SCRATCH1, SZ_D); if (!fake_read) { out = push_r(out, SCRATCH1); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } out = pop_r(out, SCRATCH2); } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_PC_INDEX_DISP8: out = cycles(out, fake_read ? (6 + inst->extra.size == OPSIZE_LONG ? 8 : 4) : 6); out = mov_ir(out, inst->address+2, SCRATCH1, SZ_D); sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7; if (inst->dst.params.regs.sec & 1) { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = add_rr(out, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = add_rr(out, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { out = add_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { out = movsx_rr(out, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { out = movsx_rr(out, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { out = movsx_rdisp8r(out, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } out = add_rr(out, SCRATCH2, SCRATCH1, SZ_D); } if (inst->dst.params.regs.displacement) { out = add_ir(out, inst->dst.params.regs.displacement, SCRATCH1, SZ_D); } if (fake_read) { out = mov_rr(out, SCRATCH1, SCRATCH2, SZ_D); } else { out = push_r(out, SCRATCH1); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } out = pop_r(out, SCRATCH2); } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: //Add cycles for reading address from instruction stream out = cycles(out, (inst->dst.addr_mode == MODE_ABSOLUTE ? BUS*2 : BUS) + (fake_read ? (inst->extra.size == OPSIZE_LONG ? BUS*2 : BUS) : 0)); out = mov_ir(out, inst->dst.params.immed, fake_read ? SCRATCH2 : SCRATCH1, SZ_D); if (!fake_read) { out = push_r(out, SCRATCH1); switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->read_8); break; case OPSIZE_WORD: out = call(out, opts->read_16); break; case OPSIZE_LONG: out = call(out, opts->read_32); break; } out = pop_r(out, SCRATCH2); } ea->mode = MODE_REG_DIRECT; ea->base = SCRATCH1; break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (dst)\n", inst->address, disasm_buf, inst->dst.addr_mode); exit(1); } return out; } code_ptr m68k_save_result(m68kinst * inst, code_ptr out, x86_68k_options * opts) { if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG) { if (inst->dst.addr_mode == MODE_AREG_PREDEC && inst->src.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) { if (opts->aregs[inst->dst.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } } switch (inst->extra.size) { case OPSIZE_BYTE: out = call(out, opts->write_8); break; case OPSIZE_WORD: out = call(out, opts->write_16); break; case OPSIZE_LONG: out = call(out, opts->write_32_lowfirst); break; } } return out; } code_ptr get_native_address(native_map_slot * native_code_map, uint32_t address) { address &= 0xFFFFFF; address /= 2; uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { return NULL; } uint32_t offset = address % NATIVE_CHUNK_SIZE; if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET || native_code_map[chunk].offsets[offset] == EXTENSION_WORD) { return NULL; } return native_code_map[chunk].base + native_code_map[chunk].offsets[offset]; } code_ptr get_native_from_context(m68k_context * context, uint32_t address) { return get_native_address(context->native_code_map, address); } uint32_t get_instruction_start(native_map_slot * native_code_map, uint32_t address) { address &= 0xFFFFFF; address /= 2; uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { return 0; } uint32_t offset = address % NATIVE_CHUNK_SIZE; if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) { return 0; } while (native_code_map[chunk].offsets[offset] == EXTENSION_WORD) { --address; chunk = address / NATIVE_CHUNK_SIZE; offset = address % NATIVE_CHUNK_SIZE; } return address*2; } void map_native_address(m68k_context * context, uint32_t address, code_ptr native_addr, uint8_t size, uint8_t native_size) { native_map_slot * native_code_map = context->native_code_map; x86_68k_options * opts = context->options; address &= 0xFFFFFF; if (address > 0xE00000) { context->ram_code_flags[(address & 0xC000) >> 14] |= 1 << ((address & 0x3800) >> 11); if (((address & 0x3FFF) + size) & 0xC000) { context->ram_code_flags[((address+size) & 0xC000) >> 14] |= 1 << (((address+size) & 0x3800) >> 11); } uint32_t slot = (address & 0xFFFF)/1024; if (!opts->gen.ram_inst_sizes[slot]) { opts->gen.ram_inst_sizes[slot] = malloc(sizeof(uint8_t) * 512); } opts->gen.ram_inst_sizes[slot][((address & 0xFFFF)/2)%512] = native_size; } address/= 2; uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { native_code_map[chunk].base = native_addr; native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE); memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE); } uint32_t offset = address % NATIVE_CHUNK_SIZE; native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base; for(address++,size-=2; size; address++,size-=2) { chunk = address / NATIVE_CHUNK_SIZE; offset = address % NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { native_code_map[chunk].base = native_addr; native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE); memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE); } native_code_map[chunk].offsets[offset] = EXTENSION_WORD; } } uint8_t get_native_inst_size(x86_68k_options * opts, uint32_t address) { if (address < 0xE00000) { return 0; } uint32_t slot = (address & 0xFFFF)/1024; return opts->gen.ram_inst_sizes[slot][((address & 0xFFFF)/2)%512]; } code_ptr translate_m68k_move(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { int8_t reg, flags_reg, sec_reg; uint8_t dir = 0; int32_t offset; int32_t inc_amount, dec_amount; x86_ea src; dst = translate_m68k_src(inst, &src, dst, opts); reg = native_reg(&(inst->dst), opts); if (inst->dst.addr_mode != MODE_AREG) { //update statically set flags dst = set_flag(dst, 0, FLAG_V, opts); dst = set_flag(dst, 0, FLAG_C, opts); } if (inst->dst.addr_mode != MODE_AREG) { if (src.mode == MODE_REG_DIRECT) { flags_reg = src.base; } else { if (reg >= 0) { flags_reg = reg; } else { if(src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } src.mode = MODE_REG_DIRECT; flags_reg = src.base = SCRATCH1; } } } uint8_t size = inst->extra.size; switch(inst->dst.addr_mode) { case MODE_AREG: size = OPSIZE_LONG; case MODE_REG: if (reg >= 0) { if (src.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src.base, reg, size); } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, reg, size); } else { dst = mov_ir(dst, src.disp, reg, size); } } else if(src.mode == MODE_REG_DIRECT) { dst = mov_rrdisp8(dst, src.base, CONTEXT, reg_offset(&(inst->dst)), size); } else { dst = mov_irdisp8(dst, src.disp, CONTEXT, reg_offset(&(inst->dst)), size); } if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } break; case MODE_AREG_PREDEC: dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = sub_ir(dst, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); } else { dst = sub_irdisp8(dst, dec_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } case MODE_AREG_INDIRECT: case MODE_AREG_POSTINC: if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } if (src.mode == MODE_REG_DIRECT) { if (src.base != SCRATCH1) { dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); } } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } switch (inst->extra.size) { case OPSIZE_BYTE: dst = call(dst, opts->write_8); break; case OPSIZE_WORD: dst = call(dst, opts->write_16); break; case OPSIZE_LONG: dst = call(dst, opts->write_32_highfirst); break; } if (inst->dst.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = add_ir(dst, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); } else { dst = add_irdisp8(dst, inc_amount, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } } break; case MODE_AREG_DISPLACE: dst = cycles(dst, BUS); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); if (src.mode == MODE_REG_DIRECT) { if (src.base != SCRATCH1) { dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); } } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } switch (inst->extra.size) { case OPSIZE_BYTE: dst = call(dst, opts->write_8); break; case OPSIZE_WORD: dst = call(dst, opts->write_16); break; case OPSIZE_LONG: dst = call(dst, opts->write_32_highfirst); break; } break; case MODE_AREG_INDEX_DISP8: dst = cycles(dst, 6);//TODO: Check to make sure this is correct if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7; if (inst->dst.params.regs.sec & 1) { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } } else { if (src.base == SCRATCH1) { dst = push_r(dst, SCRATCH1); } if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D); if (src.base == SCRATCH1) { dst = pop_r(dst, SCRATCH1); } } if (inst->dst.params.regs.displacement) { dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); } if (src.mode == MODE_REG_DIRECT) { if (src.base != SCRATCH1) { dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); } } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } switch (inst->extra.size) { case OPSIZE_BYTE: dst = call(dst, opts->write_8); break; case OPSIZE_WORD: dst = call(dst, opts->write_16); break; case OPSIZE_LONG: dst = call(dst, opts->write_32_highfirst); break; } break; case MODE_PC_DISPLACE: dst = cycles(dst, BUS); dst = mov_ir(dst, inst->dst.params.regs.displacement + inst->address+2, SCRATCH2, SZ_D); if (src.mode == MODE_REG_DIRECT) { if (src.base != SCRATCH1) { dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); } } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } switch (inst->extra.size) { case OPSIZE_BYTE: dst = call(dst, opts->write_8); break; case OPSIZE_WORD: dst = call(dst, opts->write_16); break; case OPSIZE_LONG: dst = call(dst, opts->write_32_highfirst); break; } break; case MODE_PC_INDEX_DISP8: dst = cycles(dst, 6);//TODO: Check to make sure this is correct dst = mov_ir(dst, inst->address, SCRATCH2, SZ_D); sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7; if (inst->dst.params.regs.sec & 1) { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } } else { if (src.base == SCRATCH1) { dst = push_r(dst, SCRATCH1); } if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D); if (src.base == SCRATCH1) { dst = pop_r(dst, SCRATCH1); } } if (inst->dst.params.regs.displacement) { dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); } if (src.mode == MODE_REG_DIRECT) { if (src.base != SCRATCH1) { dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); } } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } switch (inst->extra.size) { case OPSIZE_BYTE: dst = call(dst, opts->write_8); break; case OPSIZE_WORD: dst = call(dst, opts->write_16); break; case OPSIZE_LONG: dst = call(dst, opts->write_32_highfirst); break; } break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: if (src.mode == MODE_REG_DIRECT) { if (src.base != SCRATCH1) { dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); } } else if (src.mode == MODE_REG_DISPLACE8) { dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); } else { dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); } if (inst->dst.addr_mode == MODE_ABSOLUTE) { dst = cycles(dst, BUS*2); } else { dst = cycles(dst, BUS); } dst = mov_ir(dst, inst->dst.params.immed, SCRATCH2, SZ_D); if (inst->dst.addr_mode != MODE_AREG) { dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } switch (inst->extra.size) { case OPSIZE_BYTE: dst = call(dst, opts->write_8); break; case OPSIZE_WORD: dst = call(dst, opts->write_16); break; case OPSIZE_LONG: dst = call(dst, opts->write_32_highfirst); break; } break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (move dst)\n", inst->address, disasm_buf, inst->dst.addr_mode); exit(1); } //add cycles for prefetch dst = cycles(dst, BUS); return dst; } code_ptr translate_m68k_movem(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { int8_t bit,reg,sec_reg; uint8_t early_cycles; if(inst->src.addr_mode == MODE_REG) { //reg to mem early_cycles = 8; int8_t dir; switch (inst->dst.addr_mode) { case MODE_AREG_INDIRECT: case MODE_AREG_PREDEC: if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } break; case MODE_AREG_DISPLACE: early_cycles += BUS; reg = SCRATCH2; if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); break; case MODE_AREG_INDEX_DISP8: early_cycles += 6; if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7; if (inst->dst.params.regs.sec & 1) { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } } else { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D); } if (inst->dst.params.regs.displacement) { dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); } break; case MODE_PC_DISPLACE: early_cycles += BUS; dst = mov_ir(dst, inst->dst.params.regs.displacement + inst->address+2, SCRATCH2, SZ_D); break; case MODE_PC_INDEX_DISP8: early_cycles += 6; dst = mov_ir(dst, inst->address+2, SCRATCH2, SZ_D); sec_reg = (inst->dst.params.regs.sec >> 1) & 0x7; if (inst->dst.params.regs.sec & 1) { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } } else { if (inst->dst.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D); } if (inst->dst.params.regs.displacement) { dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); } break; case MODE_ABSOLUTE: early_cycles += 4; case MODE_ABSOLUTE_SHORT: early_cycles += 4; dst = mov_ir(dst, inst->dst.params.immed, SCRATCH2, SZ_D); break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (movem dst)\n", inst->address, disasm_buf, inst->dst.addr_mode); exit(1); } if (inst->dst.addr_mode == MODE_AREG_PREDEC) { reg = 15; dir = -1; } else { reg = 0; dir = 1; } dst = cycles(dst, early_cycles); for(bit=0; reg < 16 && reg >= 0; reg += dir, bit++) { if (inst->src.params.immed & (1 << bit)) { if (inst->dst.addr_mode == MODE_AREG_PREDEC) { dst = sub_ir(dst, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, SCRATCH2, SZ_D); } dst = push_r(dst, SCRATCH2); if (reg > 7) { if (opts->aregs[reg-8] >= 0) { dst = mov_rr(dst, opts->aregs[reg-8], SCRATCH1, inst->extra.size); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * (reg-8), SCRATCH1, inst->extra.size); } } else { if (opts->dregs[reg] >= 0) { dst = mov_rr(dst, opts->dregs[reg], SCRATCH1, inst->extra.size); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * (reg), SCRATCH1, inst->extra.size); } } if (inst->extra.size == OPSIZE_LONG) { dst = call(dst, opts->write_32_lowfirst); } else { dst = call(dst, opts->write_16); } dst = pop_r(dst, SCRATCH2); if (inst->dst.addr_mode != MODE_AREG_PREDEC) { dst = add_ir(dst, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, SCRATCH2, SZ_D); } } } if (inst->dst.addr_mode == MODE_AREG_PREDEC) { if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, SCRATCH2, opts->aregs[inst->dst.params.regs.pri], SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } } } else { //mem to reg early_cycles = 4; switch (inst->src.addr_mode) { case MODE_AREG_INDIRECT: case MODE_AREG_POSTINC: if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } break; case MODE_AREG_DISPLACE: early_cycles += BUS; reg = SCRATCH2; if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); break; case MODE_AREG_INDEX_DISP8: early_cycles += 6; if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } break; case MODE_PC_DISPLACE: early_cycles += BUS; dst = mov_ir(dst, inst->src.params.regs.displacement + inst->address+2, SCRATCH1, SZ_D); break; case MODE_PC_INDEX_DISP8: early_cycles += 6; dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D); sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } break; case MODE_ABSOLUTE: early_cycles += 4; case MODE_ABSOLUTE_SHORT: early_cycles += 4; dst = mov_ir(dst, inst->src.params.immed, SCRATCH1, SZ_D); break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (movem src)\n", inst->address, disasm_buf, inst->src.addr_mode); exit(1); } dst = cycles(dst, early_cycles); for(reg = 0; reg < 16; reg ++) { if (inst->dst.params.immed & (1 << reg)) { dst = push_r(dst, SCRATCH1); if (inst->extra.size == OPSIZE_LONG) { dst = call(dst, opts->read_32); } else { dst = call(dst, opts->read_16); } if (inst->extra.size == OPSIZE_WORD) { dst = movsx_rr(dst, SCRATCH1, SCRATCH1, SZ_W, SZ_D); } if (reg > 7) { if (opts->aregs[reg-8] >= 0) { dst = mov_rr(dst, SCRATCH1, opts->aregs[reg-8], SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * (reg-8), SZ_D); } } else { if (opts->dregs[reg] >= 0) { dst = mov_rr(dst, SCRATCH1, opts->dregs[reg], SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * (reg), SZ_D); } } dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, SCRATCH1, SZ_D); } } if (inst->src.addr_mode == MODE_AREG_POSTINC) { if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, SCRATCH1, opts->aregs[inst->src.params.regs.pri], SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->src)), SZ_D); } } } //prefetch dst = cycles(dst, 4); return dst; } code_ptr translate_m68k_clr(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { dst = set_flag(dst, 0, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag(dst, 1, FLAG_Z, opts); int8_t reg = native_reg(&(inst->dst), opts); if (reg >= 0) { dst = cycles(dst, (inst->extra.size == OPSIZE_LONG ? 6 : 4)); return xor_rr(dst, reg, reg, inst->extra.size); } x86_ea dst_op; dst = translate_m68k_dst(inst, &dst_op, dst, opts, 1); if (dst_op.mode == MODE_REG_DIRECT) { dst = xor_rr(dst, dst_op.base, dst_op.base, inst->extra.size); } else { dst = mov_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } dst = m68k_save_result(inst, dst, opts); return dst; } code_ptr translate_m68k_ext(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { x86_ea dst_op; uint8_t dst_size = inst->extra.size; inst->extra.size--; dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0); if (dst_op.mode == MODE_REG_DIRECT) { dst = movsx_rr(dst, dst_op.base, dst_op.base, inst->extra.size, dst_size); dst = cmp_ir(dst, 0, dst_op.base, dst_size); } else { dst = movsx_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, inst->extra.size, dst_size); dst = cmp_ir(dst, 0, SCRATCH1, dst_size); dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, dst_size); } inst->extra.size = dst_size; dst = set_flag(dst, 0, FLAG_V, opts); dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); //M68K EXT only operates on registers so no need for a call to save result here return dst; } code_ptr translate_m68k_lea(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { int8_t dst_reg = native_reg(&(inst->dst), opts), sec_reg; switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: dst = cycles(dst, BUS); if (opts->aregs[inst->src.params.regs.pri] >= 0) { if (dst_reg >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], dst_reg, SZ_D); } else { dst = mov_rrdisp8(dst, opts->aregs[inst->src.params.regs.pri], CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); } } else { if (dst_reg >= 0) { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, dst_reg, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); } } break; case MODE_AREG_DISPLACE: dst = cycles(dst, 8); if (dst_reg >= 0) { if (inst->src.params.regs.pri != inst->dst.params.regs.pri) { if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], dst_reg, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), dst_reg, SZ_D); } } dst = add_ir(dst, inst->src.params.regs.displacement, dst_reg, SZ_D); } else { if (inst->src.params.regs.pri != inst->dst.params.regs.pri) { if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rrdisp8(dst, opts->aregs[inst->src.params.regs.pri], CONTEXT, reg_offset(&(inst->dst)), SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } } dst = add_irdisp8(dst, inst->src.params.regs.displacement, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } break; case MODE_AREG_INDEX_DISP8: dst = cycles(dst, 12); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH2, SZ_D); } sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH2, SZ_D); } if (dst_reg >= 0) { dst = mov_rr(dst, SCRATCH2, dst_reg, SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } break; case MODE_PC_DISPLACE: dst = cycles(dst, 8); if (dst_reg >= 0) { dst = mov_ir(dst, inst->src.params.regs.displacement + inst->address+2, dst_reg, SZ_D); } else { dst = mov_irdisp8(dst, inst->src.params.regs.displacement + inst->address+2, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); } break; case MODE_PC_INDEX_DISP8: dst = cycles(dst, BUS*3); dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D); sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } if (dst_reg >= 0) { dst = mov_rr(dst, SCRATCH1, dst_reg, SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: dst = cycles(dst, (inst->src.addr_mode == MODE_ABSOLUTE) ? BUS * 3 : BUS * 2); if (dst_reg >= 0) { dst = mov_ir(dst, inst->src.params.immed, dst_reg, SZ_D); } else { dst = mov_irdisp8(dst, inst->src.params.immed, CONTEXT, reg_offset(&(inst->dst)), SZ_D); } break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode); exit(1); } return dst; } code_ptr translate_m68k_pea(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { uint8_t sec_reg; switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: dst = cycles(dst, BUS); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); } break; case MODE_AREG_DISPLACE: dst = cycles(dst, 8); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); break; case MODE_AREG_INDEX_DISP8: dst = cycles(dst, 6);//TODO: Check to make sure this is correct if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } break; case MODE_PC_DISPLACE: dst = cycles(dst, 8); dst = mov_ir(dst, inst->src.params.regs.displacement + inst->address+2, SCRATCH1, SZ_D); break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: dst = cycles(dst, (inst->src.addr_mode == MODE_ABSOLUTE) ? BUS * 3 : BUS * 2); dst = mov_ir(dst, inst->src.params.immed, SCRATCH1, SZ_D); break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode); exit(1); } dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_lowfirst); return dst; } code_ptr translate_m68k_bsr(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { int32_t disp = inst->src.params.immed; uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : 4); //TODO: Add cycles in the right place relative to pushing the return address on the stack dst = cycles(dst, 10); dst = mov_ir(dst, after, SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); code_ptr dest_addr = get_native_address(opts->gen.native_code_map, (inst->address+2) + disp); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, (inst->address+2) + disp, dst + 1); //dummy address to be replaced later dest_addr = dst + 256; } dst = jmp(dst, (char *)dest_addr); return dst; } code_ptr translate_m68k_bcc(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { dst = cycles(dst, 10);//TODO: Adjust this for branch not taken case int32_t disp = inst->src.params.immed; uint32_t after = inst->address + 2; code_ptr dest_addr = get_native_address(opts->gen.native_code_map, after + disp); if (inst->extra.cond == COND_TRUE) { if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, after + disp, dst + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jmp(dst, dest_addr); } else { uint8_t cond = CC_NZ; switch (inst->extra.cond) { case COND_HIGH: cond = CC_Z; case COND_LOW_SAME: dst = flag_to_reg(dst, FLAG_Z, SCRATCH1, opts); dst = or_flag_to_reg(dst, FLAG_C, SCRATCH1, opts); break; case COND_CARRY_CLR: cond = CC_Z; case COND_CARRY_SET: dst = check_flag(dst, FLAG_C, opts); break; case COND_NOT_EQ: cond = CC_Z; case COND_EQ: dst = check_flag(dst, FLAG_Z, opts); break; case COND_OVERF_CLR: cond = CC_Z; case COND_OVERF_SET: dst = check_flag(dst, FLAG_V, opts); break; case COND_PLUS: cond = CC_Z; case COND_MINUS: dst = check_flag(dst, FLAG_N, opts); break; case COND_GREATER_EQ: cond = CC_Z; case COND_LESS: dst = cmp_flags(dst, FLAG_N, FLAG_V, opts); break; case COND_GREATER: cond = CC_Z; case COND_LESS_EQ: dst = flag_to_reg(dst, FLAG_V, SCRATCH1, opts); dst = xor_flag_to_reg(dst, FLAG_N, SCRATCH1, opts); dst = or_flag_to_reg(dst, FLAG_Z, SCRATCH1, opts); break; } if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, after + disp, dst + 2); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jcc(dst, cond, dest_addr); } return dst; } code_ptr translate_m68k_scc(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { uint8_t cond = inst->extra.cond; x86_ea dst_op; inst->extra.size = OPSIZE_BYTE; dst = translate_m68k_dst(inst, &dst_op, dst, opts, 1); if (cond == COND_TRUE || cond == COND_FALSE) { if ((inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) && inst->extra.cond == COND_TRUE) { dst = cycles(dst, 6); } else { dst = cycles(dst, BUS); } if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_ir(dst, cond == COND_TRUE ? 0xFF : 0, dst_op.base, SZ_B); } else { dst = mov_irdisp8(dst, cond == COND_TRUE ? 0xFF : 0, dst_op.base, dst_op.disp, SZ_B); } } else { uint8_t cc = CC_NZ; switch (cond) { case COND_HIGH: cc = CC_Z; case COND_LOW_SAME: dst = flag_to_reg(dst, FLAG_Z, SCRATCH1, opts); dst = or_flag_to_reg(dst, FLAG_C, SCRATCH1, opts); break; case COND_CARRY_CLR: cc = CC_Z; case COND_CARRY_SET: dst = check_flag(dst, FLAG_C, opts); break; case COND_NOT_EQ: cc = CC_Z; case COND_EQ: dst = check_flag(dst, FLAG_Z, opts); break; case COND_OVERF_CLR: cc = CC_Z; case COND_OVERF_SET: dst = check_flag(dst, FLAG_V, opts); break; case COND_PLUS: cc = CC_Z; case COND_MINUS: dst = check_flag(dst, FLAG_N, opts); break; case COND_GREATER_EQ: cc = CC_Z; case COND_LESS: dst = cmp_flags(dst, FLAG_N, FLAG_V, opts); break; case COND_GREATER: cc = CC_Z; case COND_LESS_EQ: dst = flag_to_reg(dst, FLAG_V, SCRATCH1, opts); dst = xor_flag_to_reg(dst, FLAG_N, SCRATCH1, opts); dst = or_flag_to_reg(dst, FLAG_Z, SCRATCH1, opts); break; } code_ptr true_off = dst + 1; dst = jcc(dst, cc, dst+2); dst = cycles(dst, BUS); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_ir(dst, 0, dst_op.base, SZ_B); } else { dst = mov_irdisp8(dst, 0, dst_op.base, dst_op.disp, SZ_B); } code_ptr end_off = dst+1; dst = jmp(dst, dst+2); *true_off = dst - (true_off+1); dst = cycles(dst, 6); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_ir(dst, 0xFF, dst_op.base, SZ_B); } else { dst = mov_irdisp8(dst, 0xFF, dst_op.base, dst_op.disp, SZ_B); } *end_off = dst - (end_off+1); } dst = m68k_save_result(inst, dst, opts); return dst; } code_ptr translate_m68k_jmp(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { code_ptr dest_addr; uint8_t sec_reg; uint32_t m68k_addr; switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: dst = cycles(dst, BUS*2); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); } dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_AREG_INDEX_DISP8: dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { //32-bit index register if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { //16-bit index register if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_PC_DISPLACE: dst = cycles(dst, 10); m68k_addr = inst->src.params.regs.displacement + inst->address + 2; if ((m68k_addr & 0xFFFFFF) < 0x400000) { dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jmp(dst, dest_addr); } else { dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D); dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); } break; case MODE_PC_INDEX_DISP8: dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D); sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: dst = cycles(dst, inst->src.addr_mode == MODE_ABSOLUTE ? 12 : 10); m68k_addr = inst->src.params.immed; if ((m68k_addr & 0xFFFFFF) < 0x400000) { dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jmp(dst, dest_addr); } else { dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D); dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); } break; default: m68k_disasm(inst, disasm_buf); printf("%s\naddress mode %d not yet supported (jmp)\n", disasm_buf, inst->src.addr_mode); exit(1); } return dst; } code_ptr translate_m68k_jsr(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { code_ptr dest_addr; uint8_t sec_reg; uint32_t after; uint32_t m68k_addr; switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: dst = cycles(dst, BUS*2); dst = mov_ir(dst, inst->address + 2, SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); } dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_AREG_DISPLACE: dst = cycles(dst, BUS*2); dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); } dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_AREG_INDEX_DISP8: dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_PC_DISPLACE: //TODO: Add cycles in the right place relative to pushing the return address on the stack dst = cycles(dst, 10); dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); m68k_addr = inst->src.params.regs.displacement + inst->address + 2; if ((m68k_addr & 0xFFFFFF) < 0x400000) { dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jmp(dst, dest_addr); } else { dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D); dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); } break; case MODE_PC_INDEX_DISP8: dst = cycles(dst, BUS*3);//TODO: CHeck that this is correct dst = mov_ir(dst, inst->address + 4, SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D); sec_reg = (inst->src.params.regs.sec >> 1) & 0x7; if (inst->src.params.regs.sec & 1) { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = add_rr(dst, opts->aregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = add_rr(dst, opts->dregs[sec_reg], SCRATCH1, SZ_D); } else { dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH1, SZ_D); } } } else { if (inst->src.params.regs.sec & 0x10) { if (opts->aregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->aregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } else { if (opts->dregs[sec_reg] >= 0) { dst = movsx_rr(dst, opts->dregs[sec_reg], SCRATCH2, SZ_W, SZ_D); } else { dst = movsx_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t)*sec_reg, SCRATCH2, SZ_W, SZ_D); } } dst = add_rr(dst, SCRATCH2, SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: //TODO: Add cycles in the right place relative to pushing the return address on the stack dst = cycles(dst, inst->src.addr_mode == MODE_ABSOLUTE ? 12 : 10); dst = mov_ir(dst, inst->address + (inst->src.addr_mode == MODE_ABSOLUTE ? 6 : 4), SCRATCH1, SZ_D); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_highfirst); m68k_addr = inst->src.params.immed; if ((m68k_addr & 0xFFFFFF) < 0x400000) { dest_addr = get_native_address(opts->gen.native_code_map, m68k_addr); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, m68k_addr, dst + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jmp(dst, dest_addr); } else { dst = mov_ir(dst, m68k_addr, SCRATCH1, SZ_D); dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); } break; default: m68k_disasm(inst, disasm_buf); printf("%s\naddress mode %d not yet supported (jsr)\n", disasm_buf, inst->src.addr_mode); exit(1); } return dst; } code_ptr translate_m68k_rts(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { //TODO: Add cycles dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = add_ir(dst, 4, opts->aregs[7], SZ_D); dst = call(dst, opts->read_32); dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); return dst; } code_ptr translate_m68k_dbcc(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { //best case duration dst = cycles(dst, 10); code_ptr skip_loc = NULL; //TODO: Check if COND_TRUE technically valid here even though //it's basically a slow NOP if (inst->extra.cond != COND_FALSE) { uint8_t cond = CC_NZ; switch (inst->extra.cond) { case COND_HIGH: cond = CC_Z; case COND_LOW_SAME: dst = flag_to_reg(dst, FLAG_Z, SCRATCH1, opts); dst = or_flag_to_reg(dst, FLAG_C, SCRATCH1, opts); break; case COND_CARRY_CLR: cond = CC_Z; case COND_CARRY_SET: dst = check_flag(dst, FLAG_C, opts); break; case COND_NOT_EQ: cond = CC_Z; case COND_EQ: dst = check_flag(dst, FLAG_Z, opts); break; case COND_OVERF_CLR: cond = CC_Z; case COND_OVERF_SET: dst = check_flag(dst, FLAG_V, opts); break; case COND_PLUS: cond = CC_Z; case COND_MINUS: dst = check_flag(dst, FLAG_N, opts); break; case COND_GREATER_EQ: cond = CC_Z; case COND_LESS: dst = cmp_flags(dst, FLAG_N, FLAG_V, opts); break; case COND_GREATER: cond = CC_Z; case COND_LESS_EQ: dst = flag_to_reg(dst, FLAG_V, SCRATCH1, opts); dst = xor_flag_to_reg(dst, FLAG_N, SCRATCH1, opts); dst = or_flag_to_reg(dst, FLAG_Z, SCRATCH1, opts); break; } skip_loc = dst + 1; dst = jcc(dst, cond, dst + 2); } if (opts->dregs[inst->dst.params.regs.pri] >= 0) { dst = sub_ir(dst, 1, opts->dregs[inst->dst.params.regs.pri], SZ_W); dst = cmp_ir(dst, -1, opts->dregs[inst->dst.params.regs.pri], SZ_W); } else { dst = sub_irdisp8(dst, 1, CONTEXT, offsetof(m68k_context, dregs) + 4 * inst->dst.params.regs.pri, SZ_W); dst = cmp_irdisp8(dst, -1, CONTEXT, offsetof(m68k_context, dregs) + 4 * inst->dst.params.regs.pri, SZ_W); } code_ptr loop_end_loc = dst+1; dst = jcc(dst, CC_Z, dst+2); uint32_t after = inst->address + 2; code_ptr dest_addr = get_native_address(opts->gen.native_code_map, after + inst->src.params.immed); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, after + inst->src.params.immed, dst + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = dst + 256; } dst = jmp(dst, dest_addr); *loop_end_loc = dst - (loop_end_loc+1); if (skip_loc) { dst = cycles(dst, 2); *skip_loc = dst - (skip_loc+1); dst = cycles(dst, 2); } else { dst = cycles(dst, 4); } return dst; } code_ptr translate_m68k_link(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { int8_t reg = native_reg(&(inst->src), opts); //compensate for displacement word dst = cycles(dst, BUS); dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); if (reg >= 0) { dst = mov_rr(dst, reg, SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } dst = call(dst, opts->write_32_highfirst); if (reg >= 0) { dst = mov_rr(dst, opts->aregs[7], reg, SZ_D); } else { dst = mov_rrdisp8(dst, opts->aregs[7], CONTEXT, reg_offset(&(inst->src)), SZ_D); } dst = add_ir(dst, inst->dst.params.immed, opts->aregs[7], SZ_D); //prefetch dst = cycles(dst, BUS); return dst; } code_ptr translate_m68k_movep(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { int8_t reg; dst = cycles(dst, BUS*2); if (inst->src.addr_mode == MODE_REG) { if (opts->aregs[inst->dst.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } if (inst->dst.params.regs.displacement) { dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D); } reg = native_reg(&(inst->src), opts); if (inst->extra.size == OPSIZE_LONG) { if (reg >= 0) { dst = mov_rr(dst, reg, SCRATCH1, SZ_D); dst = shr_ir(dst, 24, SCRATCH1, SZ_D); dst = push_r(dst, SCRATCH2); dst = call(dst, opts->write_8); dst = pop_r(dst, SCRATCH2); dst = mov_rr(dst, reg, SCRATCH1, SZ_D); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+3, SCRATCH1, SZ_B); dst = push_r(dst, SCRATCH2); dst = call(dst, opts->write_8); dst = pop_r(dst, SCRATCH2); dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+2, SCRATCH1, SZ_B); } dst = add_ir(dst, 2, SCRATCH2, SZ_D); dst = push_r(dst, SCRATCH2); dst = call(dst, opts->write_8); dst = pop_r(dst, SCRATCH2); dst = add_ir(dst, 2, SCRATCH2, SZ_D); } if (reg >= 0) { dst = mov_rr(dst, reg, SCRATCH1, SZ_W); dst = shr_ir(dst, 8, SCRATCH1, SZ_W); dst = push_r(dst, SCRATCH2); dst = call(dst, opts->write_8); dst = pop_r(dst, SCRATCH2); dst = mov_rr(dst, reg, SCRATCH1, SZ_W); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+1, SCRATCH1, SZ_B); dst = push_r(dst, SCRATCH2); dst = call(dst, opts->write_8); dst = pop_r(dst, SCRATCH2); dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_B); } dst = add_ir(dst, 2, SCRATCH2, SZ_D); dst = call(dst, opts->write_8); } else { if (opts->aregs[inst->src.params.regs.pri] >= 0) { dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_D); } if (inst->src.params.regs.displacement) { dst = add_ir(dst, inst->src.params.regs.displacement, SCRATCH1, SZ_D); } reg = native_reg(&(inst->dst), opts); if (inst->extra.size == OPSIZE_LONG) { if (reg >= 0) { dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); dst = shl_ir(dst, 24, SCRATCH1, SZ_D); dst = mov_rr(dst, SCRATCH1, reg, SZ_D); dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH1, SZ_D); dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); dst = shl_ir(dst, 16, SCRATCH1, SZ_D); dst = or_rr(dst, SCRATCH1, reg, SZ_D); } else { dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst))+3, SZ_B); dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH1, SZ_D); dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst))+2, SZ_B); } dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH1, SZ_D); } dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); if (reg >= 0) { dst = shl_ir(dst, 8, SCRATCH1, SZ_W); dst = mov_rr(dst, SCRATCH1, reg, SZ_W); dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH1, SZ_D); dst = call(dst, opts->read_8); dst = mov_rr(dst, SCRATCH1, reg, SZ_B); } else { dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst))+1, SZ_B); dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH1, SZ_D); dst = call(dst, opts->read_8); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, reg_offset(&(inst->dst)), SZ_B); } } return dst; } code_ptr translate_m68k_cmp(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { uint8_t size = inst->extra.size; x86_ea src_op, dst_op; dst = translate_m68k_src(inst, &src_op, dst, opts); if (inst->dst.addr_mode == MODE_AREG_POSTINC) { dst = push_r(dst, SCRATCH1); dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0); dst = pop_r(dst, SCRATCH2); src_op.base = SCRATCH2; } else { dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0); if (inst->dst.addr_mode == MODE_AREG && size == OPSIZE_WORD) { size = OPSIZE_LONG; } } dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = cmp_rr(dst, src_op.base, dst_op.base, size); } else { dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = cmp_ir(dst, src_op.disp, dst_op.base, size); } else { dst = cmp_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); return dst; } typedef code_ptr (*shift_ir_t)(code_ptr out, uint8_t val, uint8_t dst, uint8_t size); typedef code_ptr (*shift_irdisp8_t)(code_ptr out, uint8_t val, uint8_t dst_base, int8_t disp, uint8_t size); typedef code_ptr (*shift_clr_t)(code_ptr out, uint8_t dst, uint8_t size); typedef code_ptr (*shift_clrdisp8_t)(code_ptr out, uint8_t dst_base, int8_t disp, uint8_t size); code_ptr translate_shift(code_ptr dst, m68kinst * inst, x86_ea *src_op, x86_ea * dst_op, x86_68k_options * opts, shift_ir_t shift_ir, shift_irdisp8_t shift_irdisp8, shift_clr_t shift_clr, shift_clrdisp8_t shift_clrdisp8, shift_ir_t special, shift_irdisp8_t special_disp8) { code_ptr end_off = NULL; code_ptr nz_off = NULL; code_ptr z_off = NULL; if (inst->src.addr_mode == MODE_UNUSED) { dst = cycles(dst, BUS); //Memory shift dst = shift_ir(dst, 1, dst_op->base, SZ_W); } else { dst = cycles(dst, inst->extra.size == OPSIZE_LONG ? 8 : 6); if (src_op->mode == MODE_IMMED) { if (src_op->disp != 1 && inst->op == M68K_ASL) { dst = set_flag(dst, 0, FLAG_V, opts); for (int i = 0; i < src_op->disp; i++) { if (dst_op->mode == MODE_REG_DIRECT) { dst = shift_ir(dst, 1, dst_op->base, inst->extra.size); } else { dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size); } //dst = setcc_r(dst, CC_O, FLAG_V); code_ptr after_flag_set = dst+1; dst = jcc(dst, CC_NO, dst+2); dst = set_flag(dst, 1, FLAG_V, opts); *after_flag_set = dst - (after_flag_set+1); } } else { if (dst_op->mode == MODE_REG_DIRECT) { dst = shift_ir(dst, src_op->disp, dst_op->base, inst->extra.size); } else { dst = shift_irdisp8(dst, src_op->disp, dst_op->base, dst_op->disp, inst->extra.size); } dst = set_flag_cond(dst, CC_O, FLAG_V, opts); } } else { if (src_op->base != RCX) { if (src_op->mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op->base, RCX, SZ_B); } else { dst = mov_rdisp8r(dst, src_op->base, src_op->disp, RCX, SZ_B); } } dst = and_ir(dst, 63, RCX, SZ_D); nz_off = dst+1; dst = jcc(dst, CC_NZ, dst+2); //Flag behavior for shift count of 0 is different for x86 than 68K if (dst_op->mode == MODE_REG_DIRECT) { dst = cmp_ir(dst, 0, dst_op->base, inst->extra.size); } else { dst = cmp_irdisp8(dst, 0, dst_op->base, dst_op->disp, inst->extra.size); } dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_C, opts); //For other instructions, this flag will be set below if (inst->op == M68K_ASL) { dst = set_flag(dst, 0, FLAG_V, opts); } z_off = dst+1; dst = jmp(dst, dst+2); *nz_off = dst - (nz_off + 1); //add 2 cycles for every bit shifted dst = add_rr(dst, RCX, CYCLES, SZ_D); dst = add_rr(dst, RCX, CYCLES, SZ_D); if (inst->op == M68K_ASL) { //ASL has Overflow flag behavior that depends on all of the bits shifted through the MSB //Easiest way to deal with this is to shift one bit at a time dst = set_flag(dst, 0, FLAG_V, opts); code_ptr loop_start = dst; if (dst_op->mode == MODE_REG_DIRECT) { dst = shift_ir(dst, 1, dst_op->base, inst->extra.size); } else { dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size); } //dst = setcc_r(dst, CC_O, FLAG_V); code_ptr after_flag_set = dst+1; dst = jcc(dst, CC_NO, dst+2); dst = set_flag(dst, 1, FLAG_V, opts); *after_flag_set = dst - (after_flag_set+1); dst = loop(dst, loop_start); } else { //x86 shifts modulo 32 for operand sizes less than 64-bits //but M68K shifts modulo 64, so we need to check for large shifts here dst = cmp_ir(dst, 32, RCX, SZ_B); code_ptr norm_shift_off = dst + 1; dst = jcc(dst, CC_L, dst+2); if (special) { code_ptr after_flag_set = NULL; if (inst->extra.size == OPSIZE_LONG) { code_ptr neq_32_off = dst + 1; dst = jcc(dst, CC_NZ, dst+2); //set the carry bit to the lsb if (dst_op->mode == MODE_REG_DIRECT) { dst = special(dst, 1, dst_op->base, SZ_D); } else { dst = special_disp8(dst, 1, dst_op->base, dst_op->disp, SZ_D); } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); after_flag_set = dst+1; dst = jmp(dst, dst+2); *neq_32_off = dst - (neq_32_off+1); } dst = set_flag(dst, 0, FLAG_C, opts); if (after_flag_set) { *after_flag_set = dst - (after_flag_set+1); } dst = set_flag(dst, 1, FLAG_Z, opts); dst = set_flag(dst, 0, FLAG_N, opts); if (dst_op->mode == MODE_REG_DIRECT) { dst = xor_rr(dst, dst_op->base, dst_op->base, inst->extra.size); } else { dst = mov_irdisp8(dst, 0, dst_op->base, dst_op->disp, inst->extra.size); } } else { if (dst_op->mode == MODE_REG_DIRECT) { dst = shift_ir(dst, 31, dst_op->base, inst->extra.size); dst = shift_ir(dst, 1, dst_op->base, inst->extra.size); } else { dst = shift_irdisp8(dst, 31, dst_op->base, dst_op->disp, inst->extra.size); dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size); } } end_off = dst+1; dst = jmp(dst, dst+2); *norm_shift_off = dst - (norm_shift_off+1); if (dst_op->mode == MODE_REG_DIRECT) { dst = shift_clr(dst, dst_op->base, inst->extra.size); } else { dst = shift_clrdisp8(dst, dst_op->base, dst_op->disp, inst->extra.size); } } } } if (!special && end_off) { *end_off = dst - (end_off + 1); } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); if (special && end_off) { *end_off = dst - (end_off + 1); } //set X flag to same as C flag if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } if (z_off) { *z_off = dst - (z_off + 1); } if (inst->op != M68K_ASL) { dst = set_flag(dst, 0, FLAG_V, opts); } if (inst->src.addr_mode == MODE_UNUSED) { dst = m68k_save_result(inst, dst, opts); } return dst; } #define BIT_SUPERVISOR 5 code_ptr translate_m68k(code_ptr dst, m68kinst * inst, x86_68k_options * opts) { code_ptr end_off, zero_off, norm_off; uint8_t dst_reg; dst = check_cycles_int(dst, inst->address, opts); if (inst->op == M68K_MOVE) { return translate_m68k_move(dst, inst, opts); } else if(inst->op == M68K_LEA) { return translate_m68k_lea(dst, inst, opts); } else if(inst->op == M68K_PEA) { return translate_m68k_pea(dst, inst, opts); } else if(inst->op == M68K_BSR) { return translate_m68k_bsr(dst, inst, opts); } else if(inst->op == M68K_BCC) { return translate_m68k_bcc(dst, inst, opts); } else if(inst->op == M68K_JMP) { return translate_m68k_jmp(dst, inst, opts); } else if(inst->op == M68K_JSR) { return translate_m68k_jsr(dst, inst, opts); } else if(inst->op == M68K_RTS) { return translate_m68k_rts(dst, inst, opts); } else if(inst->op == M68K_DBCC) { return translate_m68k_dbcc(dst, inst, opts); } else if(inst->op == M68K_CLR) { return translate_m68k_clr(dst, inst, opts); } else if(inst->op == M68K_MOVEM) { return translate_m68k_movem(dst, inst, opts); } else if(inst->op == M68K_LINK) { return translate_m68k_link(dst, inst, opts); } else if(inst->op == M68K_EXT) { return translate_m68k_ext(dst, inst, opts); } else if(inst->op == M68K_SCC) { return translate_m68k_scc(dst, inst, opts); } else if(inst->op == M68K_MOVEP) { return translate_m68k_movep(dst, inst, opts); } else if(inst->op == M68K_INVALID) { if (inst->src.params.immed == 0x7100) { return retn(dst); } dst = mov_ir(dst, inst->address, SCRATCH2, SZ_D); #ifdef X86_32 dst = push_r(dst, CONTEXT); dst = push_r(dst, SCRATCH2); #endif return call(dst, (code_ptr)m68k_invalid); } else if(inst->op == M68K_CMP) { return translate_m68k_cmp(dst, inst, opts); } x86_ea src_op, dst_op; if (inst->src.addr_mode != MODE_UNUSED) { dst = translate_m68k_src(inst, &src_op, dst, opts); } if (inst->dst.addr_mode != MODE_UNUSED) { dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0); } uint8_t size; switch(inst->op) { case M68K_ABCD: if (src_op.base != SCRATCH2) { if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_B); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_B); } } if (dst_op.base != SCRATCH1) { if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, dst_op.base, SCRATCH1, SZ_B); } else { dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, SZ_B); } } dst = flag_to_carry(dst, FLAG_X, opts); dst = jcc(dst, CC_NC, dst+5); dst = add_ir(dst, 1, SCRATCH1, SZ_B); dst = call(dst, (code_ptr)bcd_add); dst = reg_to_flag(dst, CH, FLAG_C, opts); dst = reg_to_flag(dst, CH, FLAG_X, opts); dst = cmp_ir(dst, 0, SCRATCH1, SZ_B); dst = jcc(dst, CC_Z, dst+4); dst = set_flag(dst, 0, FLAG_Z, opts); if (dst_op.base != SCRATCH1) { if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_B); } else { dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_B); } } dst = m68k_save_result(inst, dst, opts); break; case M68K_ADD: dst = cycles(dst, BUS); size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size; if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = add_rr(dst, src_op.base, dst_op.base, size); } else { dst = add_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = add_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = add_ir(dst, src_op.disp, dst_op.base, size); } else { dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size); } } if (inst->dst.addr_mode != MODE_AREG) { dst = set_flag_cond(dst, CC_C, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } } dst = m68k_save_result(inst, dst, opts); break; case M68K_ADDX: { dst = cycles(dst, BUS); dst = flag_to_carry(dst, FLAG_X, opts); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = adc_rr(dst, src_op.base, dst_op.base, inst->extra.size); } else { dst = adc_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = adc_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = adc_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = adc_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); code_ptr after_flag_set = dst+1; dst = jcc(dst, CC_Z, dst+2); dst = set_flag(dst, 0, FLAG_Z, opts); *after_flag_set = dst - (after_flag_set+1); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } dst = m68k_save_result(inst, dst, opts); break; } case M68K_AND: dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = and_rr(dst, src_op.base, dst_op.base, inst->extra.size); } else { dst = and_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = and_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = and_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = and_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); dst = m68k_save_result(inst, dst, opts); break; case M68K_ANDI_CCR: case M68K_ANDI_SR: dst = cycles(dst, 20); //TODO: If ANDI to SR, trap if not in supervisor mode if (!(inst->src.params.immed & 0x1)) { dst = set_flag(dst, 0, FLAG_C, opts); } if (!(inst->src.params.immed & 0x2)) { dst = set_flag(dst, 0, FLAG_V, opts); } if (!(inst->src.params.immed & 0x4)) { dst = set_flag(dst, 0, FLAG_Z, opts); } if (!(inst->src.params.immed & 0x8)) { dst = set_flag(dst, 0, FLAG_N, opts); } if (!(inst->src.params.immed & 0x10)) { dst = set_flag(dst, 0, FLAG_X, opts); } if (inst->op == M68K_ANDI_SR) { dst = and_irdisp8(dst, inst->src.params.immed >> 8, CONTEXT, offsetof(m68k_context, status), SZ_B); if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) { //leave supervisor mode dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_B); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_B); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_B); } if (inst->src.params.immed & 0x700) { dst = call(dst, opts->do_sync); } } break; case M68K_ASL: case M68K_LSL: dst = translate_shift(dst, inst, &src_op, &dst_op, opts, shl_ir, shl_irdisp8, shl_clr, shl_clrdisp8, shr_ir, shr_irdisp8); break; case M68K_ASR: dst = translate_shift(dst, inst, &src_op, &dst_op, opts, sar_ir, sar_irdisp8, sar_clr, sar_clrdisp8, NULL, NULL); break; case M68K_LSR: dst = translate_shift(dst, inst, &src_op, &dst_op, opts, shr_ir, shr_irdisp8, shr_clr, shr_clrdisp8, shl_ir, shl_irdisp8); break; case M68K_BCHG: case M68K_BCLR: case M68K_BSET: case M68K_BTST: dst = cycles(dst, inst->extra.size == OPSIZE_BYTE ? 4 : ( inst->op == M68K_BTST ? 6 : (inst->op == M68K_BCLR ? 10 : 8)) ); if (src_op.mode == MODE_IMMED) { if (inst->extra.size == OPSIZE_BYTE) { src_op.disp &= 0x7; } if (inst->op == M68K_BTST) { if (dst_op.mode == MODE_REG_DIRECT) { dst = bt_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = bt_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } else if (inst->op == M68K_BSET) { if (dst_op.mode == MODE_REG_DIRECT) { dst = bts_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = bts_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } else if (inst->op == M68K_BCLR) { if (dst_op.mode == MODE_REG_DIRECT) { dst = btr_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = btr_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = btc_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = btc_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } } else { if (src_op.mode == MODE_REG_DISPLACE8 || (inst->dst.addr_mode != MODE_REG && src_op.base != SCRATCH1 && src_op.base != SCRATCH2)) { if (dst_op.base == SCRATCH1) { dst = push_r(dst, SCRATCH2); if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_B); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_B); } src_op.base = SCRATCH2; } else { if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_B); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_B); } src_op.base = SCRATCH1; } } uint8_t size = inst->extra.size; if (dst_op.mode == MODE_REG_DISPLACE8) { if (src_op.base != SCRATCH1 && src_op.base != SCRATCH2) { if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_D); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D); src_op.mode = MODE_REG_DIRECT; } src_op.base = SCRATCH1; } //b### with register destination is modulo 32 //x86 with a memory destination isn't modulo anything //so use an and here to force the value to be modulo 32 dst = and_ir(dst, 31, SCRATCH1, SZ_D); } else if(inst->dst.addr_mode != MODE_REG) { //b### with memory destination is modulo 8 //x86-64 doesn't support 8-bit bit operations //so we fake it by forcing the bit number to be modulo 8 dst = and_ir(dst, 7, src_op.base, SZ_D); size = SZ_D; } if (inst->op == M68K_BTST) { if (dst_op.mode == MODE_REG_DIRECT) { dst = bt_rr(dst, src_op.base, dst_op.base, size); } else { dst = bt_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } else if (inst->op == M68K_BSET) { if (dst_op.mode == MODE_REG_DIRECT) { dst = bts_rr(dst, src_op.base, dst_op.base, size); } else { dst = bts_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } else if (inst->op == M68K_BCLR) { if (dst_op.mode == MODE_REG_DIRECT) { dst = btr_rr(dst, src_op.base, dst_op.base, size); } else { dst = btr_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = btc_rr(dst, src_op.base, dst_op.base, size); } else { dst = btc_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } if (src_op.base == SCRATCH2) { dst = pop_r(dst, SCRATCH2); } } //x86 sets the carry flag to the value of the bit tested //68K sets the zero flag to the complement of the bit tested dst = set_flag_cond(dst, CC_NC, FLAG_Z, opts); if (inst->op != M68K_BTST) { dst = m68k_save_result(inst, dst, opts); } break; case M68K_CHK: { dst = cycles(dst, 6); if (dst_op.mode == MODE_REG_DIRECT) { dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size); } else { dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } uint32_t isize; switch(inst->src.addr_mode) { case MODE_AREG_DISPLACE: case MODE_AREG_INDEX_DISP8: case MODE_ABSOLUTE_SHORT: case MODE_PC_INDEX_DISP8: case MODE_PC_DISPLACE: case MODE_IMMEDIATE: isize = 4; break; case MODE_ABSOLUTE: isize = 6; break; default: isize = 2; } code_ptr passed = dst+1; dst = jcc(dst, CC_GE, dst+2); dst = set_flag(dst, 1, FLAG_N, opts); dst = mov_ir(dst, VECTOR_CHK, SCRATCH2, SZ_D); dst = mov_ir(dst, inst->address+isize, SCRATCH1, SZ_D); dst = jmp(dst, opts->trap); *passed = dst - (passed+1); if (dst_op.mode == MODE_REG_DIRECT) { if (src_op.mode == MODE_REG_DIRECT) { dst = cmp_rr(dst, src_op.base, dst_op.base, inst->extra.size); } else if(src_op.mode == MODE_REG_DISPLACE8) { dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); } else { dst = cmp_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } } else if(dst_op.mode == MODE_REG_DISPLACE8) { if (src_op.mode == MODE_REG_DIRECT) { dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = cmp_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } passed = dst+1; dst = jcc(dst, CC_LE, dst+2); dst = set_flag(dst, 0, FLAG_N, opts); dst = mov_ir(dst, VECTOR_CHK, SCRATCH2, SZ_D); dst = mov_ir(dst, inst->address+isize, SCRATCH1, SZ_D); dst = jmp(dst, opts->trap); *passed = dst - (passed+1); dst = cycles(dst, 4); break; } case M68K_DIVS: case M68K_DIVU: { //TODO: cycle exact division dst = cycles(dst, inst->op == M68K_DIVS ? 158 : 140); dst = set_flag(dst, 0, FLAG_C, opts); dst = push_r(dst, RDX); dst = push_r(dst, RAX); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, dst_op.base, RAX, SZ_D); } else { dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, RAX, SZ_D); } if (src_op.mode == MODE_IMMED) { dst = mov_ir(dst, (src_op.disp & 0x8000) && inst->op == M68K_DIVS ? src_op.disp | 0xFFFF0000 : src_op.disp, SCRATCH2, SZ_D); } else if (src_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_DIVS) { dst = movsx_rr(dst, src_op.base, SCRATCH2, SZ_W, SZ_D); } else { dst = movzx_rr(dst, src_op.base, SCRATCH2, SZ_W, SZ_D); } } else if (src_op.mode == MODE_REG_DISPLACE8) { if (inst->op == M68K_DIVS) { dst = movsx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_W, SZ_D); } else { dst = movzx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_W, SZ_D); } } dst = cmp_ir(dst, 0, SCRATCH2, SZ_D); code_ptr not_zero = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = pop_r(dst, RAX); dst = pop_r(dst, RDX); dst = mov_ir(dst, VECTOR_INT_DIV_ZERO, SCRATCH2, SZ_D); dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D); dst = jmp(dst, opts->trap); *not_zero = dst - (not_zero+1); if (inst->op == M68K_DIVS) { dst = cdq(dst); } else { dst = xor_rr(dst, RDX, RDX, SZ_D); } if (inst->op == M68K_DIVS) { dst = idiv_r(dst, SCRATCH2, SZ_D); } else { dst = div_r(dst, SCRATCH2, SZ_D); } code_ptr skip_sec_check; if (inst->op == M68K_DIVS) { dst = cmp_ir(dst, 0x8000, RAX, SZ_D); skip_sec_check = dst + 1; dst = jcc(dst, CC_GE, dst+2); dst = cmp_ir(dst, -0x8000, RAX, SZ_D); norm_off = dst+1; dst = jcc(dst, CC_L, dst+2); } else { dst = cmp_ir(dst, 0x10000, RAX, SZ_D); norm_off = dst+1; dst = jcc(dst, CC_NC, dst+2); } if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, RDX, dst_op.base, SZ_W); dst = shl_ir(dst, 16, dst_op.base, SZ_D); dst = mov_rr(dst, RAX, dst_op.base, SZ_W); } else { dst = mov_rrdisp8(dst, RDX, dst_op.base, dst_op.disp, SZ_W); dst = shl_irdisp8(dst, 16, dst_op.base, dst_op.disp, SZ_D); dst = mov_rrdisp8(dst, RAX, dst_op.base, dst_op.disp, SZ_W); } dst = cmp_ir(dst, 0, RAX, SZ_W); dst = pop_r(dst, RAX); dst = pop_r(dst, RDX); dst = set_flag(dst, 0, FLAG_V, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); end_off = dst+1; dst = jmp(dst, dst+2); *norm_off = dst - (norm_off + 1); if (inst->op == M68K_DIVS) { *skip_sec_check = dst - (skip_sec_check+1); } dst = pop_r(dst, RAX); dst = pop_r(dst, RDX); dst = set_flag(dst, 1, FLAG_V, opts); *end_off = dst - (end_off + 1); break; } case M68K_EOR: dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = xor_rr(dst, src_op.base, dst_op.base, inst->extra.size); } else { dst = xor_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = xor_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = xor_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = xor_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); dst = m68k_save_result(inst, dst, opts); break; case M68K_EORI_CCR: case M68K_EORI_SR: dst = cycles(dst, 20); //TODO: If ANDI to SR, trap if not in supervisor mode if (inst->src.params.immed & 0x1) { dst = xor_flag(dst, 1, FLAG_C, opts); } if (inst->src.params.immed & 0x2) { dst = xor_flag(dst, 1, FLAG_V, opts); } if (inst->src.params.immed & 0x4) { dst = xor_flag(dst, 1, FLAG_Z, opts); } if (inst->src.params.immed & 0x8) { dst = xor_flag(dst, 1, FLAG_N, opts); } if (inst->src.params.immed & 0x10) { dst = xor_flag(dst, 1, FLAG_X, opts); } if (inst->op == M68K_ORI_SR) { dst = xor_irdisp8(dst, inst->src.params.immed >> 8, CONTEXT, offsetof(m68k_context, status), SZ_B); if (inst->src.params.immed & 0x700) { dst = call(dst, opts->do_sync); } } break; case M68K_EXG: dst = cycles(dst, 6); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, dst_op.base, SCRATCH2, SZ_D); if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, dst_op.base, SZ_D); dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, SZ_D); dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D); } } else { dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_D); if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, SZ_D); dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D); dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D); } } break; case M68K_ILLEGAL: dst = call(dst, opts->gen.save_context); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); #else dst = push_r(dst, CONTEXT); #endif dst = call(dst, (code_ptr)print_regs_exit); break; case M68K_MOVE_FROM_SR: //TODO: Trap if not in system mode dst = call(dst, opts->get_sr); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_W); } else { dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_W); } dst = m68k_save_result(inst, dst, opts); break; case M68K_MOVE_CCR: case M68K_MOVE_SR: //TODO: Privilege check for MOVE to SR if (src_op.mode == MODE_IMMED) { dst = set_flag(dst, src_op.disp & 0x1, FLAG_C, opts); dst = set_flag(dst, (src_op.disp >> 1) & 0x1, FLAG_V, opts); dst = set_flag(dst, (src_op.disp >> 2) & 0x1, FLAG_Z, opts); dst = set_flag(dst, (src_op.disp >> 3) & 0x1, FLAG_N, opts); dst = set_flag(dst, (src_op.disp >> 4) & 0x1, FLAG_X, opts); if (inst->op == M68K_MOVE_SR) { dst = mov_irdisp8(dst, (src_op.disp >> 8), CONTEXT, offsetof(m68k_context, status), SZ_B); if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) { //leave supervisor mode dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); } dst = call(dst, opts->do_sync); } dst = cycles(dst, 12); } else { if (src_op.base != SCRATCH1) { if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_W); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W); } } dst = call(dst, inst->op == M68K_MOVE_SR ? opts->set_sr : opts->set_ccr); dst = cycles(dst, 12); } break; case M68K_MOVE_USP: dst = cycles(dst, BUS); //TODO: Trap if not in supervisor mode //dst = bt_irdisp8(dst, BIT_SUPERVISOR, CONTEXT, offsetof(m68k_context, status), SZ_B); if (inst->src.addr_mode == MODE_UNUSED) { if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, dst_op.base, SZ_D); } else { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SCRATCH1, SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D); } } else { if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rrdisp8(dst, src_op.base, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); } } break; //case M68K_MOVEP: case M68K_MULS: case M68K_MULU: dst = cycles(dst, 70); //TODO: Calculate the actual value based on the value of the <ea> parameter if (src_op.mode == MODE_IMMED) { dst = mov_ir(dst, inst->op == M68K_MULU ? (src_op.disp & 0xFFFF) : ((src_op.disp & 0x8000) ? src_op.disp | 0xFFFF0000 : src_op.disp), SCRATCH1, SZ_D); } else if (src_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_MULS) { dst = movsx_rr(dst, src_op.base, SCRATCH1, SZ_W, SZ_D); } else { dst = movzx_rr(dst, src_op.base, SCRATCH1, SZ_W, SZ_D); } } else { if (inst->op == M68K_MULS) { dst = movsx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W, SZ_D); } else { dst = movzx_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W, SZ_D); } } if (dst_op.mode == MODE_REG_DIRECT) { dst_reg = dst_op.base; if (inst->op == M68K_MULS) { dst = movsx_rr(dst, dst_reg, dst_reg, SZ_W, SZ_D); } else { dst = movzx_rr(dst, dst_reg, dst_reg, SZ_W, SZ_D); } } else { dst_reg = SCRATCH2; if (inst->op == M68K_MULS) { dst = movsx_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_W, SZ_D); } else { dst = movzx_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_W, SZ_D); } } dst = imul_rr(dst, SCRATCH1, dst_reg, SZ_D); if (dst_op.mode == MODE_REG_DISPLACE8) { dst = mov_rrdisp8(dst, dst_reg, dst_op.base, dst_op.disp, SZ_D); } dst = set_flag(dst, 0, FLAG_V, opts); dst = set_flag(dst, 0, FLAG_C, opts); dst = cmp_ir(dst, 0, dst_reg, SZ_D); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); break; //case M68K_NBCD: case M68K_NEG: dst = cycles(dst, BUS); if (dst_op.mode == MODE_REG_DIRECT) { dst = neg_r(dst, dst_op.base, inst->extra.size); } else { dst = neg_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } dst = m68k_save_result(inst, dst, opts); break; case M68K_NEGX: { dst = cycles(dst, BUS); if (dst_op.mode == MODE_REG_DIRECT) { if (dst_op.base == SCRATCH1) { dst = push_r(dst, SCRATCH2); dst = xor_rr(dst, SCRATCH2, SCRATCH2, inst->extra.size); dst = flag_to_carry(dst, FLAG_X, opts); dst = sbb_rr(dst, dst_op.base, SCRATCH2, inst->extra.size); dst = mov_rr(dst, SCRATCH2, dst_op.base, inst->extra.size); dst = pop_r(dst, SCRATCH2); } else { dst = xor_rr(dst, SCRATCH1, SCRATCH1, inst->extra.size); dst = flag_to_carry(dst, FLAG_X, opts); dst = sbb_rr(dst, dst_op.base, SCRATCH1, inst->extra.size); dst = mov_rr(dst, SCRATCH1, dst_op.base, inst->extra.size); } } else { dst = xor_rr(dst, SCRATCH1, SCRATCH1, inst->extra.size); dst = flag_to_carry(dst, FLAG_X, opts); dst = sbb_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, inst->extra.size); dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, inst->extra.size); } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); code_ptr after_flag_set = dst+1; dst = jcc(dst, CC_Z, dst+2); dst = set_flag(dst, 0, FLAG_Z, opts); *after_flag_set = dst - (after_flag_set+1); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } dst = m68k_save_result(inst, dst, opts); break; } case M68K_NOP: dst = cycles(dst, BUS); break; case M68K_NOT: if (dst_op.mode == MODE_REG_DIRECT) { dst = not_r(dst, dst_op.base, inst->extra.size); dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size); } else { dst = not_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); dst = m68k_save_result(inst, dst, opts); break; case M68K_OR: dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = or_rr(dst, src_op.base, dst_op.base, inst->extra.size); } else { dst = or_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = or_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = or_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = or_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); dst = m68k_save_result(inst, dst, opts); break; case M68K_ORI_CCR: case M68K_ORI_SR: dst = cycles(dst, 20); //TODO: If ANDI to SR, trap if not in supervisor mode if (inst->src.params.immed & 0x1) { dst = set_flag(dst, 1, FLAG_C, opts); } if (inst->src.params.immed & 0x2) { dst = set_flag(dst, 1, FLAG_V, opts); } if (inst->src.params.immed & 0x4) { dst = set_flag(dst, 1, FLAG_Z, opts); } if (inst->src.params.immed & 0x8) { dst = set_flag(dst, 1, FLAG_N, opts); } if (inst->src.params.immed & 0x10) { dst = set_flag(dst, 1, FLAG_X, opts); } if (inst->op == M68K_ORI_SR) { dst = or_irdisp8(dst, inst->src.params.immed >> 8, CONTEXT, offsetof(m68k_context, status), SZ_B); if (inst->src.params.immed & 0x700) { dst = call(dst, opts->do_sync); } } break; case M68K_RESET: dst = call(dst, opts->gen.save_context); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); #else dst = push_r(dst, CONTEXT); #endif dst = call(dst, (code_ptr)print_regs_exit); break; case M68K_ROL: case M68K_ROR: dst = set_flag(dst, 0, FLAG_V, opts); if (inst->src.addr_mode == MODE_UNUSED) { dst = cycles(dst, BUS); //Memory rotate if (inst->op == M68K_ROL) { dst = rol_ir(dst, 1, dst_op.base, inst->extra.size); } else { dst = ror_ir(dst, 1, dst_op.base, inst->extra.size); } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = m68k_save_result(inst, dst, opts); } else { if (src_op.mode == MODE_IMMED) { dst = cycles(dst, (inst->extra.size == OPSIZE_LONG ? 8 : 6) + src_op.disp*2); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { dst = rol_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = ror_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } } else { if (inst->op == M68K_ROL) { dst = rol_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = ror_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); } else { if (src_op.mode == MODE_REG_DIRECT) { if (src_op.base != SCRATCH1) { dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_B); } } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_B); } dst = and_ir(dst, 63, SCRATCH1, SZ_D); zero_off = dst+1; dst = jcc(dst, CC_Z, dst+2); dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D); dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D); dst = cmp_ir(dst, 32, SCRATCH1, SZ_B); norm_off = dst+1; dst = jcc(dst, CC_L, dst+2); dst = sub_ir(dst, 32, SCRATCH1, SZ_B); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { dst = rol_ir(dst, 31, dst_op.base, inst->extra.size); dst = rol_ir(dst, 1, dst_op.base, inst->extra.size); } else { dst = ror_ir(dst, 31, dst_op.base, inst->extra.size); dst = ror_ir(dst, 1, dst_op.base, inst->extra.size); } } else { if (inst->op == M68K_ROL) { dst = rol_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size); dst = rol_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = ror_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size); dst = ror_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size); } } *norm_off = dst - (norm_off+1); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { dst = rol_clr(dst, dst_op.base, inst->extra.size); } else { dst = ror_clr(dst, dst_op.base, inst->extra.size); } } else { if (inst->op == M68K_ROL) { dst = rol_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = ror_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); end_off = dst + 1; dst = jmp(dst, dst+2); *zero_off = dst - (zero_off+1); dst = set_flag(dst, 0, FLAG_C, opts); *end_off = dst - (end_off+1); } if (dst_op.mode == MODE_REG_DIRECT) { dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size); } else { dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } break; case M68K_ROXL: case M68K_ROXR: dst = set_flag(dst, 0, FLAG_V, opts); if (inst->src.addr_mode == MODE_UNUSED) { dst = cycles(dst, BUS); //Memory rotate dst = flag_to_carry(dst, FLAG_X, opts); if (inst->op == M68K_ROXL) { dst = rcl_ir(dst, 1, dst_op.base, inst->extra.size); } else { dst = rcr_ir(dst, 1, dst_op.base, inst->extra.size); } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); if (opts->flag_regs[FLAG_C] < 0) { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } dst = m68k_save_result(inst, dst, opts); } else { if (src_op.mode == MODE_IMMED) { dst = cycles(dst, (inst->extra.size == OPSIZE_LONG ? 8 : 6) + src_op.disp*2); dst = flag_to_carry(dst, FLAG_X, opts); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROXL) { dst = rcl_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = rcr_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } } else { if (inst->op == M68K_ROXL) { dst = rcl_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = rcr_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } } else { if (src_op.mode == MODE_REG_DIRECT) { if (src_op.base != SCRATCH1) { dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_B); } } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_B); } dst = and_ir(dst, 63, SCRATCH1, SZ_D); zero_off = dst+1; dst = jcc(dst, CC_Z, dst+2); dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D); dst = add_rr(dst, SCRATCH1, CYCLES, SZ_D); dst = cmp_ir(dst, 32, SCRATCH1, SZ_B); norm_off = dst+1; dst = jcc(dst, CC_L, dst+2); dst = flag_to_carry(dst, FLAG_X, opts); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROXL) { dst = rcl_ir(dst, 31, dst_op.base, inst->extra.size); dst = rcl_ir(dst, 1, dst_op.base, inst->extra.size); } else { dst = rcr_ir(dst, 31, dst_op.base, inst->extra.size); dst = rcr_ir(dst, 1, dst_op.base, inst->extra.size); } } else { if (inst->op == M68K_ROXL) { dst = rcl_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size); dst = rcl_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = rcr_irdisp8(dst, 31, dst_op.base, dst_op.disp, inst->extra.size); dst = rcr_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_X, opts); dst = sub_ir(dst, 32, SCRATCH1, SZ_B); *norm_off = dst - (norm_off+1); dst = flag_to_carry(dst, FLAG_X, opts); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROXL) { dst = rcl_clr(dst, dst_op.base, inst->extra.size); } else { dst = rcr_clr(dst, dst_op.base, inst->extra.size); } } else { if (inst->op == M68K_ROXL) { dst = rcl_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); } else { dst = rcr_clrdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } end_off = dst + 1; dst = jmp(dst, dst+2); *zero_off = dst - (zero_off+1); //Carry flag is set to X flag when count is 0, this is different from ROR/ROL dst = flag_to_flag(dst, FLAG_X, FLAG_C, opts); *end_off = dst - (end_off+1); } if (dst_op.mode == MODE_REG_DIRECT) { dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size); } else { dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); } break; case M68K_RTE: //TODO: Trap if not in system mode //Read saved SR dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = call(dst, opts->read_16); dst = add_ir(dst, 2, opts->aregs[7], SZ_D); dst = call(dst, opts->set_sr); //Read saved PC dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = call(dst, opts->read_32); dst = add_ir(dst, 4, opts->aregs[7], SZ_D); //Check if we've switched to user mode and swap stack pointers if needed dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B); end_off = dst+1; dst = jcc(dst, CC_C, dst+2); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D); dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); *end_off = dst - (end_off+1); //Get native address, sync components, recalculate integer points and jump to returned address dst = call(dst, opts->native_addr_and_sync); dst = jmp_r(dst, SCRATCH1); break; case M68K_RTR: //Read saved CCR dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = call(dst, opts->read_16); dst = add_ir(dst, 2, opts->aregs[7], SZ_D); dst = call(dst, opts->set_ccr); //Read saved PC dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = call(dst, opts->read_32); dst = add_ir(dst, 4, opts->aregs[7], SZ_D); //Get native address and jump to it dst = call(dst, opts->native_addr); dst = jmp_r(dst, SCRATCH1); break; case M68K_SBCD: { if (src_op.base != SCRATCH2) { if (src_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_B); } else { dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_B); } } if (dst_op.base != SCRATCH1) { if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, dst_op.base, SCRATCH1, SZ_B); } else { dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH1, SZ_B); } } dst = flag_to_carry(dst, FLAG_X, opts); dst = jcc(dst, CC_NC, dst+5); dst = sub_ir(dst, 1, SCRATCH1, SZ_B); dst = call(dst, (code_ptr)bcd_sub); dst = reg_to_flag(dst, CH, FLAG_C, opts); dst = reg_to_flag(dst, CH, FLAG_X, opts); dst = cmp_ir(dst, 0, SCRATCH1, SZ_B); code_ptr after_flag_set = dst+1; dst = jcc(dst, CC_Z, dst+2); dst = set_flag(dst, 0, FLAG_Z, opts); *after_flag_set = dst - (after_flag_set+1); if (dst_op.base != SCRATCH1) { if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_B); } else { dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_B); } } dst = m68k_save_result(inst, dst, opts); break; } case M68K_STOP: { //TODO: Trap if not in system mode //manual says 4 cycles, but it has to be at least 8 since it's a 2-word instruction //possibly even 12 since that's how long MOVE to SR takes dst = cycles(dst, BUS*2); dst = set_flag(dst, src_op.disp & 0x1, FLAG_C, opts); dst = set_flag(dst, (src_op.disp >> 1) & 0x1, FLAG_V, opts); dst = set_flag(dst, (src_op.disp >> 2) & 0x1, FLAG_Z, opts); dst = set_flag(dst, (src_op.disp >> 3) & 0x1, FLAG_N, opts); dst = set_flag(dst, (src_op.disp >> 4) & 0x1, FLAG_X, opts); dst = mov_irdisp8(dst, (src_op.disp >> 8), CONTEXT, offsetof(m68k_context, status), SZ_B); if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) { //leave supervisor mode dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); } code_ptr loop_top = dst; dst = call(dst, opts->do_sync); dst = cmp_rr(dst, LIMIT, CYCLES, SZ_D); code_ptr normal_cycle_up = dst + 1; dst = jcc(dst, CC_A, dst+2); dst = cycles(dst, BUS); code_ptr after_cycle_up = dst + 1; dst = jmp(dst, dst+2); *normal_cycle_up = dst - (normal_cycle_up + 1); dst = mov_rr(dst, LIMIT, CYCLES, SZ_D); *after_cycle_up = dst - (after_cycle_up+1); dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); dst = jcc(dst, CC_C, loop_top); break; } case M68K_SUB: size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size; dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = sub_rr(dst, src_op.base, dst_op.base, size); } else { dst = sub_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = sub_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = sub_ir(dst, src_op.disp, dst_op.base, size); } else { dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size); } } if (inst->dst.addr_mode != MODE_AREG) { dst = set_flag_cond(dst, CC_C, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } else { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } } dst = m68k_save_result(inst, dst, opts); break; case M68K_SUBX: { dst = cycles(dst, BUS); dst = flag_to_carry(dst, FLAG_X, opts); if (src_op.mode == MODE_REG_DIRECT) { if (dst_op.mode == MODE_REG_DIRECT) { dst = sbb_rr(dst, src_op.base, dst_op.base, inst->extra.size); } else { dst = sbb_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); } } else if (src_op.mode == MODE_REG_DISPLACE8) { dst = sbb_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); } else { if (dst_op.mode == MODE_REG_DIRECT) { dst = sbb_ir(dst, src_op.disp, dst_op.base, inst->extra.size); } else { dst = sbb_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); } } dst = set_flag_cond(dst, CC_C, FLAG_C, opts); if (opts->flag_regs[FLAG_C] < 0) { dst = set_flag_cond(dst, CC_C, FLAG_X, opts); } code_ptr after_flag_set = dst+1; dst = jcc(dst, CC_Z, dst+2); dst = set_flag(dst, 0, FLAG_Z, opts); *after_flag_set = dst - (after_flag_set+1); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag_cond(dst, CC_O, FLAG_V, opts); if (opts->flag_regs[FLAG_C] >= 0) { dst = flag_to_flag(dst, FLAG_C, FLAG_X, opts); } dst = m68k_save_result(inst, dst, opts); break; } case M68K_SWAP: dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { dst = rol_ir(dst, 16, src_op.base, SZ_D); dst = cmp_ir(dst, 0, src_op.base, SZ_D); } else{ dst = rol_irdisp8(dst, 16, src_op.base, src_op.disp, SZ_D); dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, SZ_D); } dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); break; //case M68K_TAS: case M68K_TRAP: dst = mov_ir(dst, src_op.disp + VECTOR_TRAP_0, SCRATCH2, SZ_D); dst = mov_ir(dst, inst->address+2, SCRATCH1, SZ_D); dst = jmp(dst, opts->trap); break; //case M68K_TRAPV: case M68K_TST: dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { dst = cmp_ir(dst, 0, src_op.base, inst->extra.size); } else { //M68000 doesn't support immedate operand for tst, so this must be MODE_REG_DISPLACE8 dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, inst->extra.size); } dst = set_flag(dst, 0, FLAG_C, opts); dst = set_flag_cond(dst, CC_Z, FLAG_Z, opts); dst = set_flag_cond(dst, CC_S, FLAG_N, opts); dst = set_flag(dst, 0, FLAG_V, opts); break; case M68K_UNLK: dst = cycles(dst, BUS); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, dst_op.base, opts->aregs[7], SZ_D); } else { dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, opts->aregs[7], SZ_D); } dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); dst = call(dst, opts->read_32); if (dst_op.mode == MODE_REG_DIRECT) { dst = mov_rr(dst, SCRATCH1, dst_op.base, SZ_D); } else { dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D); } dst = add_ir(dst, 4, opts->aregs[7], SZ_D); break; default: m68k_disasm(inst, disasm_buf); printf("%X: %s\ninstruction %d not yet implemented\n", inst->address, disasm_buf, inst->op); exit(1); } return dst; } uint8_t m68k_is_terminal(m68kinst * inst) { return inst->op == M68K_RTS || inst->op == M68K_RTE || inst->op == M68K_RTR || inst->op == M68K_JMP || inst->op == M68K_TRAP || inst->op == M68K_ILLEGAL || inst->op == M68K_INVALID || inst->op == M68K_RESET || (inst->op == M68K_BCC && inst->extra.cond == COND_TRUE); } void m68k_handle_deferred(m68k_context * context) { x86_68k_options * opts = context->options; process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context); if (opts->gen.deferred) { translate_m68k_stream(opts->gen.deferred->address, context); } } code_ptr translate_m68k_stream(uint32_t address, m68k_context * context) { m68kinst instbuf; x86_68k_options * opts = context->options; code_ptr dst = opts->gen.cur_code; code_ptr dst_end = opts->gen.code_end; address &= 0xFFFFFF; if(get_native_address(opts->gen.native_code_map, address)) { return dst; } char disbuf[1024]; uint16_t *encoded, *next; if ((address & 0xFFFFFF) < 0x400000) { encoded = context->mem_pointers[0] + (address & 0xFFFFFF)/2; } else if ((address & 0xFFFFFF) > 0xE00000) { encoded = context->mem_pointers[1] + (address & 0xFFFF)/2; } else { printf("attempt to translate non-memory address: %X\n", address); exit(1); } do { if (opts->address_log) { fprintf(opts->address_log, "%X\n", address); } do { if (dst_end-dst < MAX_NATIVE_SIZE) { if (dst_end-dst < 5) { puts("out of code memory, not enough space for jmp to next chunk"); exit(1); } size_t size = 1024*1024; opts->gen.cur_code = alloc_code(&size); opts->gen.code_end = opts->gen.cur_code + size; jmp(dst, opts->gen.cur_code); dst = opts->gen.cur_code; dst_end = opts->gen.code_end; } if (address >= 0x400000 && address < 0xE00000) { dst = xor_rr(dst, RDI, RDI, SZ_D); #ifdef X86_32 dst = push_r(dst, RDI); #endif dst = call(dst, (code_ptr)exit); break; } code_ptr existing = get_native_address(opts->gen.native_code_map, address); if (existing) { dst = jmp(dst, existing); break; } next = m68k_decode(encoded, &instbuf, address); if (instbuf.op == M68K_INVALID) { instbuf.src.params.immed = *encoded; } uint16_t m68k_size = (next-encoded)*2; address += m68k_size; encoded = next; //m68k_disasm(&instbuf, disbuf); //printf("%X: %s\n", instbuf.address, disbuf); code_ptr after = translate_m68k(dst, &instbuf, opts); map_native_address(context, instbuf.address, dst, m68k_size, after-dst); dst = after; } while(!m68k_is_terminal(&instbuf)); process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context); if (opts->gen.deferred) { address = opts->gen.deferred->address; if ((address & 0xFFFFFF) < 0x400000) { encoded = context->mem_pointers[0] + (address & 0xFFFFFF)/2; } else if ((address & 0xFFFFFF) > 0xE00000) { encoded = context->mem_pointers[1] + (address & 0xFFFF)/2; } else { printf("attempt to translate non-memory address: %X\n", address); exit(1); } } else { encoded = NULL; } } while(encoded != NULL); opts->gen.cur_code = dst; return dst; } code_ptr get_native_address_trans(m68k_context * context, uint32_t address) { address &= 0xFFFFFF; code_ptr ret = get_native_address(context->native_code_map, address); if (!ret) { translate_m68k_stream(address, context); ret = get_native_address(context->native_code_map, address); } return ret; } void * m68k_retranslate_inst(uint32_t address, m68k_context * context) { x86_68k_options * opts = context->options; uint8_t orig_size = get_native_inst_size(opts, address); code_ptr orig_start = get_native_address(context->native_code_map, address); uint32_t orig = address; address &= 0xFFFF; code_ptr dst = opts->gen.cur_code; code_ptr dst_end = opts->gen.code_end; uint16_t *after, *inst = context->mem_pointers[1] + address/2; m68kinst instbuf; after = m68k_decode(inst, &instbuf, orig); if (orig_size != MAX_NATIVE_SIZE) { if (dst_end - dst < 128) { size_t size = 1024*1024; dst = alloc_code(&size); opts->gen.code_end = dst_end = dst + size; opts->gen.cur_code = dst; } deferred_addr * orig_deferred = opts->gen.deferred; code_ptr native_end = translate_m68k(dst, &instbuf, opts); uint8_t is_terminal = m68k_is_terminal(&instbuf); if ((native_end - dst) <= orig_size) { code_ptr native_next; if (!is_terminal) { native_next = get_native_address(context->native_code_map, orig + (after-inst)*2); } if (is_terminal || (native_next && ((native_next == orig_start + orig_size) || (orig_size - (native_end - dst)) > 5))) { remove_deferred_until(&opts->gen.deferred, orig_deferred); native_end = translate_m68k(orig_start, &instbuf, opts); if (!is_terminal) { if (native_next == orig_start + orig_size && (native_next-native_end) < 2) { while (native_end < orig_start + orig_size) { *(native_end++) = 0x90; //NOP } } else { jmp(native_end, native_next); } } m68k_handle_deferred(context); return orig_start; } } map_native_address(context, instbuf.address, dst, (after-inst)*2, MAX_NATIVE_SIZE); opts->gen.cur_code = dst+MAX_NATIVE_SIZE; jmp(orig_start, dst); if (!m68k_is_terminal(&instbuf)) { jmp(native_end, get_native_address_trans(context, orig + (after-inst)*2)); } m68k_handle_deferred(context); return dst; } else { dst = translate_m68k(orig_start, &instbuf, opts); if (!m68k_is_terminal(&instbuf)) { dst = jmp(dst, get_native_address_trans(context, orig + (after-inst)*2)); } m68k_handle_deferred(context); return orig_start; } } m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context) { uint32_t inst_start = get_instruction_start(context->native_code_map, address | 0xFF0000); if (inst_start) { code_ptr dst = get_native_address(context->native_code_map, inst_start); dst = mov_ir(dst, inst_start, SCRATCH2, SZ_D); x86_68k_options * options = context->options; if (!options->retrans_stub) { if (options->gen.code_end - options->gen.cur_code < 32) { size_t size = 1024*1024; options->gen.cur_code = alloc_code(&size); options->gen.code_end = options->gen.cur_code + size; } code_ptr rdst = options->retrans_stub = options->gen.cur_code; rdst = call(rdst, options->gen.save_context); rdst = push_r(rdst, CONTEXT); #ifdef X86_32 rdst = push_r(rdst, CONTEXT); rdst = push_r(rdst, SCRATCH2); #endif rdst = call(rdst, (code_ptr)m68k_retranslate_inst); #ifdef X86_32 rdst = add_ir(rdst, 8, RSP, SZ_D); #endif rdst = pop_r(rdst, CONTEXT); rdst = mov_rr(rdst, RAX, SCRATCH1, SZ_PTR); rdst = call(rdst, options->gen.load_context); rdst = jmp_r(rdst, SCRATCH1); options->gen.cur_code = rdst; } dst = jmp(dst, options->retrans_stub); } return context; } void insert_breakpoint(m68k_context * context, uint32_t address, code_ptr bp_handler) { static code_ptr bp_stub = NULL; code_ptr native = get_native_address_trans(context, address); code_ptr start_native = native; native = mov_ir(native, address, SCRATCH1, SZ_D); if (!bp_stub) { x86_68k_options * opts = context->options; code_ptr dst = opts->gen.cur_code; code_ptr dst_end = opts->gen.code_end; if (dst_end - dst < 128) { size_t size = 1024*1024; dst = alloc_code(&size); opts->gen.code_end = dst_end = dst + size; } bp_stub = dst; native = call(native, bp_stub); //Calculate length of prologue dst = check_cycles_int(dst, address, opts); int check_int_size = dst-bp_stub; dst = bp_stub; //Save context and call breakpoint handler dst = call(dst, opts->gen.save_context); dst = push_r(dst, SCRATCH1); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); dst = mov_rr(dst, SCRATCH1, RSI, SZ_D); #else dst = push_r(dst, SCRATCH1); dst = push_r(dst, CONTEXT); #endif dst = call(dst, bp_handler); #ifdef X86_32 dst = add_ir(dst, 8, RSP, SZ_D); #endif dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR); //Restore context dst = call(dst, opts->gen.load_context); dst = pop_r(dst, SCRATCH1); //do prologue stuff dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D); code_ptr jmp_off = dst+1; dst = jcc(dst, CC_NC, dst + 7); dst = call(dst, opts->gen.handle_cycle_limit_int); *jmp_off = dst - (jmp_off+1); //jump back to body of translated instruction dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, check_int_size - (native-start_native), SCRATCH1, SZ_PTR); dst = jmp_r(dst, SCRATCH1); opts->gen.cur_code = dst; } else { native = call(native, bp_stub); } } void remove_breakpoint(m68k_context * context, uint32_t address) { code_ptr native = get_native_address(context->native_code_map, address); check_cycles_int(native, address, context->options); } void start_68k_context(m68k_context * context, uint32_t address) { code_ptr addr = get_native_address_trans(context, address); x86_68k_options * options = context->options; options->start_context(addr, context); } void m68k_reset(m68k_context * context) { //TODO: Make this actually use the normal read functions context->aregs[7] = context->mem_pointers[0][0] << 16 | context->mem_pointers[0][1]; uint32_t address = context->mem_pointers[0][2] << 16 | context->mem_pointers[0][3]; start_68k_context(context, address); } code_ptr gen_mem_fun(cpu_options * opts, memmap_chunk * memmap, uint32_t num_chunks, ftype fun_type) { code_ptr dst = opts->cur_code; code_ptr start = dst; dst = check_cycles(dst, opts); dst = cycles(dst, BUS); dst = and_ir(dst, 0xFFFFFF, SCRATCH1, SZ_D); code_ptr lb_jcc = NULL, ub_jcc = NULL; uint8_t is_write = fun_type == WRITE_16 || fun_type == WRITE_8; uint8_t adr_reg = is_write ? SCRATCH2 : SCRATCH1; uint16_t access_flag = is_write ? MMAP_WRITE : MMAP_READ; uint8_t size = (fun_type == READ_16 || fun_type == WRITE_16) ? SZ_W : SZ_B; for (uint32_t chunk = 0; chunk < num_chunks; chunk++) { if (memmap[chunk].start > 0) { dst = cmp_ir(dst, memmap[chunk].start, adr_reg, SZ_D); lb_jcc = dst + 1; dst = jcc(dst, CC_C, dst+2); } if (memmap[chunk].end < 0x1000000) { dst = cmp_ir(dst, memmap[chunk].end, adr_reg, SZ_D); ub_jcc = dst + 1; dst = jcc(dst, CC_NC, dst+2); } if (memmap[chunk].mask != 0xFFFFFF) { dst = and_ir(dst, memmap[chunk].mask, adr_reg, SZ_D); } void * cfun; switch (fun_type) { case READ_16: cfun = memmap[chunk].read_16; break; case READ_8: cfun = memmap[chunk].read_8; break; case WRITE_16: cfun = memmap[chunk].write_16; break; case WRITE_8: cfun = memmap[chunk].write_8; break; default: cfun = NULL; } if(memmap[chunk].buffer && memmap[chunk].flags & access_flag) { if (memmap[chunk].flags & MMAP_PTR_IDX) { if (memmap[chunk].flags & MMAP_FUNC_NULL) { dst = cmp_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, SZ_PTR); code_ptr not_null = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, opts->save_context); #ifdef X86_64 if (is_write) { if (SCRATCH2 != RDI) { dst = mov_rr(dst, SCRATCH2, RDI, SZ_D); } dst = mov_rr(dst, SCRATCH1, RDX, size); } else { dst = push_r(dst, CONTEXT); dst = mov_rr(dst, SCRATCH1, RDI, SZ_D); } dst = test_ir(dst, 8, RSP, SZ_D); code_ptr adjust_rsp = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, cfun); code_ptr no_adjust = dst+1; dst = jmp(dst, dst+2); *adjust_rsp = dst - (adjust_rsp + 1); dst = sub_ir(dst, 8, RSP, SZ_PTR); dst = call(dst, cfun); dst = add_ir(dst, 8, RSP, SZ_PTR); *no_adjust = dst - (no_adjust + 1); #else if (is_write) { dst = push_r(dst, SCRATCH1); } else { dst = push_r(dst, CONTEXT);//save CONTEXT for later } dst = push_r(dst, CONTEXT); dst = push_r(dst, is_write ? SCRATCH2 : SCRATCH1); dst = call(dst, cfun); dst = add_ir(dst, is_write ? 12 : 8, RSP, SZ_D); #endif if (is_write) { dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR); } else { dst = pop_r(dst, CONTEXT); dst = mov_rr(dst, RAX, SCRATCH1, size); } dst = jmp(dst, opts->load_context); *not_null = dst - (not_null + 1); } if (size == SZ_B) { dst = xor_ir(dst, 1, adr_reg, SZ_D); } dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, adr_reg, SZ_PTR); if (is_write) { dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size); } else { dst = mov_rindr(dst, SCRATCH1, SCRATCH1, size); } } else { uint8_t tmp_size = size; if (size == SZ_B) { if ((memmap[chunk].flags & MMAP_ONLY_ODD) || (memmap[chunk].flags & MMAP_ONLY_EVEN)) { dst = bt_ir(dst, 0, adr_reg, SZ_D); code_ptr good_addr = dst + 1; dst = jcc(dst, (memmap[chunk].flags & MMAP_ONLY_ODD) ? CC_C : CC_NC, dst+2); if (!is_write) { dst = mov_ir(dst, 0xFF, SCRATCH1, SZ_B); } dst = retn(dst); *good_addr = dst - (good_addr + 1); dst = shr_ir(dst, 1, adr_reg, SZ_D); } else { dst = xor_ir(dst, 1, adr_reg, SZ_D); } } else if ((memmap[chunk].flags & MMAP_ONLY_ODD) || (memmap[chunk].flags & MMAP_ONLY_EVEN)) { tmp_size = SZ_B; dst = shr_ir(dst, 1, adr_reg, SZ_D); if ((memmap[chunk].flags & MMAP_ONLY_EVEN) && is_write) { dst = shr_ir(dst, 8, SCRATCH1, SZ_W); } } if ((intptr_t)memmap[chunk].buffer <= 0x7FFFFFFF && (intptr_t)memmap[chunk].buffer >= -2147483648) { if (is_write) { dst = mov_rrdisp32(dst, SCRATCH1, SCRATCH2, (intptr_t)memmap[chunk].buffer, tmp_size); } else { dst = mov_rdisp32r(dst, SCRATCH1, (intptr_t)memmap[chunk].buffer, SCRATCH1, tmp_size); } } else { if (is_write) { if (memmap[chunk].flags & MMAP_CODE) { dst = push_r(dst, SCRATCH2); } dst = push_r(dst, SCRATCH1); dst = mov_ir(dst, (intptr_t)memmap[chunk].buffer, SCRATCH1, SZ_PTR); dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_PTR); dst = pop_r(dst, SCRATCH1); dst = mov_rrind(dst, SCRATCH1, SCRATCH2, tmp_size); if (memmap[chunk].flags & MMAP_CODE) { dst = pop_r(dst, SCRATCH2); } } else { dst = mov_ir(dst, (intptr_t)memmap[chunk].buffer, SCRATCH2, SZ_PTR); dst = mov_rindexr(dst, SCRATCH2, SCRATCH1, 0, SCRATCH1, tmp_size); } } if (size != tmp_size && !is_write) { if (memmap[chunk].flags & MMAP_ONLY_EVEN) { dst = shl_ir(dst, 8, SCRATCH1, SZ_W); dst = mov_ir(dst, 0xFF, SCRATCH1, SZ_B); } else { dst = or_ir(dst, 0xFF00, SCRATCH1, SZ_W); } } } if (is_write && (memmap[chunk].flags & MMAP_CODE)) { dst = mov_rr(dst, SCRATCH2, SCRATCH1, SZ_D); dst = shr_ir(dst, 11, SCRATCH1, SZ_D); dst = bt_rrdisp32(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, ram_code_flags), SZ_D); code_ptr not_code = dst+1; dst = jcc(dst, CC_NC, dst+2); dst = call(dst, opts->save_context); #ifdef X86_32 dst = push_r(dst, CONTEXT); dst = push_r(dst, SCRATCH2); #endif dst = call(dst, (code_ptr)m68k_handle_code_write); #ifdef X86_32 dst = add_ir(dst, 8, RSP, SZ_D); #endif dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR); dst = call(dst, opts->load_context); *not_code = dst - (not_code+1); } dst = retn(dst); } else if (cfun) { dst = call(dst, opts->save_context); #ifdef X86_64 if (is_write) { if (SCRATCH2 != RDI) { dst = mov_rr(dst, SCRATCH2, RDI, SZ_D); } dst = mov_rr(dst, SCRATCH1, RDX, size); } else { dst = push_r(dst, CONTEXT); dst = mov_rr(dst, SCRATCH1, RDI, SZ_D); } dst = test_ir(dst, 8, RSP, SZ_D); code_ptr adjust_rsp = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, cfun); code_ptr no_adjust = dst+1; dst = jmp(dst, dst+2); *adjust_rsp = dst - (adjust_rsp + 1); dst = sub_ir(dst, 8, RSP, SZ_PTR); dst = call(dst, cfun); dst = add_ir(dst, 8, RSP, SZ_PTR); *no_adjust = dst - (no_adjust+1); #else if (is_write) { dst = push_r(dst, SCRATCH1); } else { dst = push_r(dst, CONTEXT);//save CONTEXT for later } dst = push_r(dst, CONTEXT); dst = push_r(dst, is_write ? SCRATCH2 : SCRATCH1); dst = call(dst, cfun); dst = add_ir(dst, is_write ? 12 : 8, RSP, SZ_D); #endif if (is_write) { dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR); } else { dst = pop_r(dst, CONTEXT); dst = mov_rr(dst, RAX, SCRATCH1, size); } dst = jmp(dst, opts->load_context); } else { //Not sure the best course of action here if (!is_write) { dst = mov_ir(dst, size == SZ_B ? 0xFF : 0xFFFF, SCRATCH1, size); } dst = retn(dst); } if (lb_jcc) { *lb_jcc = dst - (lb_jcc+1); lb_jcc = NULL; } if (ub_jcc) { *ub_jcc = dst - (ub_jcc+1); ub_jcc = NULL; } } if (!is_write) { dst = mov_ir(dst, size == SZ_B ? 0xFF : 0xFFFF, SCRATCH1, size); } dst = retn(dst); opts->cur_code = dst; return start; } void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks) { memset(opts, 0, sizeof(*opts)); for (int i = 0; i < 8; i++) opts->dregs[i] = opts->aregs[i] = -1; #ifdef X86_64 opts->dregs[0] = R10; opts->dregs[1] = R11; opts->dregs[2] = R12; opts->dregs[3] = R8; opts->aregs[0] = R13; opts->aregs[1] = R14; opts->aregs[2] = R9; opts->aregs[7] = R15; opts->flag_regs[0] = -1; opts->flag_regs[1] = RBX; opts->flag_regs[2] = RDX; opts->flag_regs[3] = BH; opts->flag_regs[4] = DH; #else opts->dregs[0] = RDX; opts->aregs[7] = RDI; for (int i = 0; i < 5; i++) opts->flag_regs[i] = -1; #endif opts->gen.native_code_map = malloc(sizeof(native_map_slot) * NATIVE_MAP_CHUNKS); memset(opts->gen.native_code_map, 0, sizeof(native_map_slot) * NATIVE_MAP_CHUNKS); opts->gen.deferred = NULL; size_t size = 1024 * 1024; opts->gen.cur_code = alloc_code(&size); opts->gen.code_end = opts->gen.cur_code + size; opts->gen.ram_inst_sizes = malloc(sizeof(code_ptr) * 64); memset(opts->gen.ram_inst_sizes, 0, sizeof(code_ptr) * 64); code_ptr dst = opts->gen.cur_code; opts->gen.save_context = dst; for (int i = 0; i < 5; i++) if (opts->flag_regs[i] >= 0) { dst = mov_rrdisp8(dst, opts->flag_regs[i], CONTEXT, offsetof(m68k_context, flags) + i, SZ_B); } for (int i = 0; i < 8; i++) { if (opts->dregs[i] >= 0) { dst = mov_rrdisp8(dst, opts->dregs[i], CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * i, SZ_D); } if (opts->aregs[i] >= 0) { dst = mov_rrdisp8(dst, opts->aregs[i], CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * i, SZ_D); } } dst = mov_rrdisp8(dst, CYCLES, CONTEXT, offsetof(m68k_context, current_cycle), SZ_D); dst = retn(dst); opts->gen.load_context = dst; for (int i = 0; i < 5; i++) if (opts->flag_regs[i] >= 0) { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + i, opts->flag_regs[i], SZ_B); } for (int i = 0; i < 8; i++) { if (opts->dregs[i] >= 0) { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + sizeof(uint32_t) * i, opts->dregs[i], SZ_D); } if (opts->aregs[i] >= 0) { dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * i, opts->aregs[i], SZ_D); } } dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, current_cycle), CYCLES, SZ_D); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, target_cycle), LIMIT, SZ_D); dst = retn(dst); opts->start_context = (start_fun)dst; #ifdef X86_64 if (SCRATCH2 != RDI) { dst = mov_rr(dst, RDI, SCRATCH2, SZ_PTR); } //save callee save registers dst = push_r(dst, RBP); dst = push_r(dst, R12); dst = push_r(dst, R13); dst = push_r(dst, R14); dst = push_r(dst, R15); #else //save callee save registers dst = push_r(dst, RBP); dst = push_r(dst, RBX); dst = push_r(dst, RSI); dst = push_r(dst, RDI); dst = mov_rdisp8r(dst, RSP, 20, SCRATCH2, SZ_D); dst = mov_rdisp8r(dst, RSP, 24, CONTEXT, SZ_D); #endif dst = call(dst, opts->gen.load_context); dst = call_r(dst, SCRATCH2); dst = call(dst, opts->gen.save_context); #ifdef X86_64 //restore callee save registers dst = pop_r(dst, R15); dst = pop_r(dst, R14); dst = pop_r(dst, R13); dst = pop_r(dst, R12); dst = pop_r(dst, RBP); #else dst = pop_r(dst, RDI); dst = pop_r(dst, RSI); dst = pop_r(dst, RBX); dst = pop_r(dst, RBP); #endif dst = retn(dst); opts->native_addr = dst; dst = call(dst, opts->gen.save_context); dst = push_r(dst, CONTEXT); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); //move context to 1st arg reg dst = mov_rr(dst, SCRATCH1, RSI, SZ_D); //move address to 2nd arg reg #else dst = push_r(dst, SCRATCH1); dst = push_r(dst, CONTEXT); #endif dst = call(dst, (code_ptr)get_native_address_trans); #ifdef X86_32 dst = add_ir(dst, 8, RSP, SZ_D); #endif dst = mov_rr(dst, RAX, SCRATCH1, SZ_PTR); //move result to scratch reg dst = pop_r(dst, CONTEXT); dst = call(dst, opts->gen.load_context); dst = retn(dst); opts->native_addr_and_sync = dst; dst = call(dst, opts->gen.save_context); dst = push_r(dst, SCRATCH1); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); dst = xor_rr(dst, RSI, RSI, SZ_D); dst = test_ir(dst, 8, RSP, SZ_PTR); //check stack alignment code_ptr do_adjust_rsp = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, (code_ptr)sync_components); code_ptr no_adjust_rsp = dst+1; dst = jmp(dst, dst+2); *do_adjust_rsp = dst - (do_adjust_rsp+1); dst = sub_ir(dst, 8, RSP, SZ_PTR); dst = call(dst, (code_ptr)sync_components); dst = add_ir(dst, 8, RSP, SZ_PTR); *no_adjust_rsp = dst - (no_adjust_rsp+1); dst = pop_r(dst, RSI); dst = push_r(dst, RAX); dst = mov_rr(dst, RAX, RDI, SZ_PTR); dst = call(dst, (code_ptr)get_native_address_trans); #else //TODO: Add support for pushing a constant in gen_x86 dst = xor_rr(dst, RAX, RAX, SZ_D); dst = push_r(dst, RAX); dst = push_r(dst, CONTEXT); dst = call(dst, (code_ptr)sync_components); dst = add_ir(dst, 8, RSP, SZ_D); dst = pop_r(dst, RSI); //restore saved address from SCRATCH1 dst = push_r(dst, RAX); //save context pointer for later dst = push_r(dst, RSI); //2nd arg -- address dst = push_r(dst, RAX); //1st arg -- context pointer dst = call(dst, (code_ptr)get_native_address_trans); dst = add_ir(dst, 8, RSP, SZ_D); #endif dst = mov_rr(dst, RAX, SCRATCH1, SZ_PTR); //move result to scratch reg dst = pop_r(dst, CONTEXT); dst = call(dst, opts->gen.load_context); dst = retn(dst); opts->gen.handle_cycle_limit = dst; dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, sync_cycle), CYCLES, SZ_D); code_ptr skip_sync = dst+1; dst = jcc(dst, CC_C, dst+2); opts->do_sync = dst; dst = push_r(dst, SCRATCH1); dst = push_r(dst, SCRATCH2); dst = call(dst, opts->gen.save_context); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); dst = xor_rr(dst, RSI, RSI, SZ_D); dst = test_ir(dst, 8, RSP, SZ_D); code_ptr adjust_rsp = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, (code_ptr)sync_components); code_ptr no_adjust = dst+1; dst = jmp(dst, dst+2); *adjust_rsp = dst - (adjust_rsp + 1); dst = sub_ir(dst, 8, RSP, SZ_PTR); dst = call(dst, (code_ptr)sync_components); dst = add_ir(dst, 8, RSP, SZ_PTR); *no_adjust = dst - (no_adjust+1); #else //TODO: Add support for pushing a constant in gen_x86 dst = xor_rr(dst, RAX, RAX, SZ_D); dst = push_r(dst, RAX); dst = push_r(dst, CONTEXT); dst = call(dst, (code_ptr)sync_components); dst = add_ir(dst, 8, RSP, SZ_D); #endif dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR); dst = call(dst, opts->gen.load_context); dst = pop_r(dst, SCRATCH2); dst = pop_r(dst, SCRATCH1); *skip_sync = dst - (skip_sync+1); dst = retn(dst); opts->gen.cur_code = dst; opts->read_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_16); opts->read_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_8); opts->write_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_16); opts->write_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_8); dst = opts->gen.cur_code; opts->read_32 = dst; dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_16); dst = mov_rr(dst, SCRATCH1, SCRATCH2, SZ_W); dst = pop_r(dst, SCRATCH1); dst = push_r(dst, SCRATCH2); dst = add_ir(dst, 2, SCRATCH1, SZ_D); dst = call(dst, opts->read_16); dst = pop_r(dst, SCRATCH2); dst = movzx_rr(dst, SCRATCH1, SCRATCH1, SZ_W, SZ_D); dst = shl_ir(dst, 16, SCRATCH2, SZ_D); dst = or_rr(dst, SCRATCH2, SCRATCH1, SZ_D); dst = retn(dst); opts->write_32_lowfirst = dst; dst = push_r(dst, SCRATCH2); dst = push_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH2, SZ_D); dst = call(dst, opts->write_16); dst = pop_r(dst, SCRATCH1); dst = pop_r(dst, SCRATCH2); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); dst = jmp(dst, opts->write_16); opts->write_32_highfirst = dst; dst = push_r(dst, SCRATCH1); dst = push_r(dst, SCRATCH2); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); dst = call(dst, opts->write_16); dst = pop_r(dst, SCRATCH2); dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH2, SZ_D); dst = jmp(dst, opts->write_16); opts->get_sr = dst; dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, status), SCRATCH1, SZ_B); dst = shl_ir(dst, 8, SCRATCH1, SZ_W); if (opts->flag_regs[FLAG_X] >= 0) { dst = mov_rr(dst, opts->flag_regs[FLAG_X], SCRATCH1, SZ_B); } else { int8_t offset = offsetof(m68k_context, flags); if (offset) { dst = mov_rdisp8r(dst, CONTEXT, offset, SCRATCH1, SZ_B); } else { dst = mov_rindr(dst, CONTEXT, SCRATCH1, SZ_B); } } for (int flag = FLAG_N; flag <= FLAG_C; flag++) { dst = shl_ir(dst, 1, SCRATCH1, SZ_B); if (opts->flag_regs[flag] >= 0) { dst = or_rr(dst, opts->flag_regs[flag], SCRATCH1, SZ_B); } else { dst = or_rdisp8r(dst, CONTEXT, offsetof(m68k_context, flags) + flag, SCRATCH1, SZ_B); } } dst = retn(dst); opts->set_sr = dst; for (int flag = FLAG_C; flag >= FLAG_X; flag--) { dst = rcr_ir(dst, 1, SCRATCH1, SZ_B); if (opts->flag_regs[flag] >= 0) { dst = setcc_r(dst, CC_C, opts->flag_regs[flag]); } else { int8_t offset = offsetof(m68k_context, flags) + flag; if (offset) { dst = setcc_rdisp8(dst, CC_C, CONTEXT, offset); } else { dst = setcc_rind(dst, CC_C, CONTEXT); } } } dst = shr_ir(dst, 8, SCRATCH1, SZ_W); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, status), SZ_B); dst = retn(dst); opts->set_ccr = dst; for (int flag = FLAG_C; flag >= FLAG_X; flag--) { dst = rcr_ir(dst, 1, SCRATCH1, SZ_B); if (opts->flag_regs[flag] >= 0) { dst = setcc_r(dst, CC_C, opts->flag_regs[flag]); } else { int8_t offset = offsetof(m68k_context, flags) + flag; if (offset) { dst = setcc_rdisp8(dst, CC_C, CONTEXT, offset); } else { dst = setcc_rind(dst, CC_C, CONTEXT); } } } dst = retn(dst); opts->gen.handle_cycle_limit_int = dst; dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); code_ptr do_int = dst+1; dst = jcc(dst, CC_NC, dst+2); dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, sync_cycle), CYCLES, SZ_D); skip_sync = dst+1; dst = jcc(dst, CC_C, dst+2); dst = call(dst, opts->gen.save_context); #ifdef X86_64 dst = mov_rr(dst, CONTEXT, RDI, SZ_PTR); dst = mov_rr(dst, SCRATCH1, RSI, SZ_D); dst = test_ir(dst, 8, RSP, SZ_D); adjust_rsp = dst+1; dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, (code_ptr)sync_components); no_adjust = dst+1; dst = jmp(dst, dst+2); *adjust_rsp = dst - (adjust_rsp + 1); dst = sub_ir(dst, 8, RSP, SZ_PTR); dst = call(dst, (code_ptr)sync_components); dst = add_ir(dst, 8, RSP, SZ_PTR); *no_adjust = dst - (no_adjust+1); #else dst = push_r(dst, SCRATCH1); dst = push_r(dst, CONTEXT); dst = call(dst, (code_ptr)sync_components); dst = add_ir(dst, 8, RSP, SZ_D); #endif dst = mov_rr(dst, RAX, CONTEXT, SZ_PTR); dst = jmp(dst, opts->gen.load_context); *skip_sync = dst - (skip_sync+1); dst = retn(dst); *do_int = dst - (do_int+1); //set target cycle to sync cycle dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, sync_cycle), LIMIT, SZ_D); //swap USP and SSP if not already in supervisor mode dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B); code_ptr already_supervisor = dst+1; dst = jcc(dst, CC_C, dst+2); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SCRATCH2, SZ_D); dst = mov_rrdisp8(dst, opts->aregs[7], CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); dst = mov_rr(dst, SCRATCH2, opts->aregs[7], SZ_D); *already_supervisor = dst - (already_supervisor+1); //save PC dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_lowfirst); //save status register dst = sub_ir(dst, 2, opts->aregs[7], SZ_D); dst = call(dst, opts->get_sr); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_16); //update status register dst = and_irdisp8(dst, 0xF8, CONTEXT, offsetof(m68k_context, status), SZ_B); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_num), SCRATCH1, SZ_B); dst = or_ir(dst, 0x20, SCRATCH1, SZ_B); dst = or_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, status), SZ_B); //calculate interrupt vector address dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_num), SCRATCH1, SZ_D); dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, int_ack), SZ_W); dst = shl_ir(dst, 2, SCRATCH1, SZ_D); dst = add_ir(dst, 0x60, SCRATCH1, SZ_D); dst = call(dst, opts->read_32); dst = call(dst, opts->native_addr_and_sync); dst = cycles(dst, 24); //discard function return address dst = pop_r(dst, SCRATCH2); dst = jmp_r(dst, SCRATCH1); opts->trap = dst; dst = push_r(dst, SCRATCH2); //swap USP and SSP if not already in supervisor mode dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B); already_supervisor = dst+1; dst = jcc(dst, CC_C, dst+2); dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SCRATCH2, SZ_D); dst = mov_rrdisp8(dst, opts->aregs[7], CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); dst = mov_rr(dst, SCRATCH2, opts->aregs[7], SZ_D); *already_supervisor = dst - (already_supervisor+1); //save PC dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_32_lowfirst); //save status register dst = sub_ir(dst, 2, opts->aregs[7], SZ_D); dst = call(dst, opts->get_sr); dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); dst = call(dst, opts->write_16); //set supervisor bit dst = or_irdisp8(dst, 0x20, CONTEXT, offsetof(m68k_context, status), SZ_B); //calculate vector address dst = pop_r(dst, SCRATCH1); dst = shl_ir(dst, 2, SCRATCH1, SZ_D); dst = call(dst, opts->read_32); dst = call(dst, opts->native_addr_and_sync); dst = cycles(dst, 18); dst = jmp_r(dst, SCRATCH1); opts->gen.cur_code = dst; } void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts) { memset(context, 0, sizeof(m68k_context)); context->native_code_map = native_code_map; context->options = opts; context->int_cycle = 0xFFFFFFFF; context->status = 0x27; }