#include "syn68k_private.h" #ifdef GENERATE_NATIVE_CODE #include "native.h" #include "i386-isa.h" #include "i386-aux.h" #include "hash.h" #include "trap.h" #include #include #include #include #ifdef DEBUG #define CHECK_ADDR_REG(n) \ assert (c->host_reg_to_guest_reg[n] == NO_REG \ || (c->host_reg_to_guest_reg[n] >= 8 \ && c->host_reg_to_guest_reg[n] < 16)) #else #define CHECK_ADDR_REG(n) #endif #ifndef offsetof #define offsetof(t, f) ((int32) &(((t *) 0)->f)) #endif #define HOST_TEST_REG(size) \ int \ host_test ## size ## _reg (COMMON_ARGS, int32 reg) \ { \ i386_test ## size ## _reg_reg (COMMON_ARG_NAMES, reg, reg); \ return 0; \ } HOST_TEST_REG (b) HOST_TEST_REG (l) int host_testw_reg (COMMON_ARGS, int32 reg) { /* Word ops are slow on the i386, so do a testb if we can; * if we don't need Z, testw %ax,%ax -> testb %ah,%ah. */ if (!(cc_to_compute & M68K_CCZ) && reg < 4) i386_testb_reg_reg (COMMON_ARG_NAMES, reg + 4, reg + 4); else i386_testw_reg_reg (COMMON_ARG_NAMES, reg, reg); return 0; } /* Tests a swapped register. The register is assumed to be SWAP16 for word * tests and SWAP32 for long tests. */ #define HOST_TEST_SWAPPED_REG(size, swap, save, restore) \ int \ host_test ## size ## _swapped_reg (COMMON_ARGS, int32 reg) \ { \ if (!(cc_to_compute & M68K_CCZ) && reg < 4) \ i386_testb_reg_reg (COMMON_ARG_NAMES, reg, reg); \ else if (!(cc_to_compute & M68K_CCN)) \ i386_test ## size ## _reg_reg (COMMON_ARG_NAMES, reg, reg); \ else \ { \ save; \ swap (COMMON_ARG_NAMES, reg); \ i386_test ## size ## _reg_reg (COMMON_ARG_NAMES, reg, reg); \ restore; \ } \ \ return 0; \ } HOST_TEST_SWAPPED_REG (w, host_swap16, i386_pushl (COMMON_ARG_NAMES, reg), i386_popl (COMMON_ARG_NAMES, reg)) HOST_TEST_SWAPPED_REG (l, host_swap32, if (!have_i486_p) i386_pushl (COMMON_ARG_NAMES, reg), (have_i486_p ? i386_bswap (COMMON_ARG_NAMES, reg) : i386_popl (COMMON_ARG_NAMES, reg))) /* This is used when we need to sign extend a SWAP16 address register * and possible compute cc bits. This happens often when we * do movew _addr,a0...m68k semantics demand sign extension. */ int host_swap16_sext_test_reg (COMMON_ARGS, int32 reg) { HOST_SWAP16 (reg); i386_movswl_reg_reg (COMMON_ARG_NAMES, reg, reg); if (cc_to_compute) i386_testl_reg_reg (COMMON_ARG_NAMES, reg, reg); return 0; } /* A special handler for adding, subtracting, or comparing two registers. * The problem is that we can get into situations like: * cmpl a0@-,a0 * where a0 becomes offset after the instruction starts. This * will properly handle offset registers (by unoffsetting them before * the compare). */ #define ADD_SUB_CMP_OFFSET(op) \ int \ host_ ## op ## l_reg_reg (COMMON_ARGS, int32 reg1, int32 reg2) \ { \ int32 gr1, gr2; \ \ gr1 = c->host_reg_to_guest_reg[reg1]; \ if (gr1 != NO_REG) \ host_unoffset_reg (c, codep, cc_spill_if_changed, gr1); \ \ gr2 = c->host_reg_to_guest_reg[reg2]; \ if (gr2 != NO_REG) \ host_unoffset_reg (c, codep, cc_spill_if_changed, gr2); \ \ return i386_ ## op ## l_reg_reg (COMMON_ARG_NAMES, reg1, reg2); \ } ADD_SUB_CMP_OFFSET (add) ADD_SUB_CMP_OFFSET (sub) ADD_SUB_CMP_OFFSET (cmp) /* This performs a binary bitwise op on a register when you are * either doing a byte sized op, or you don't care about the N bit. * This can only handle SWAP32 for long sized ops. */ #define HOST_BITWISE_IMM_REG(op, size, mask, size_mask, byte_p) \ int \ host_ ## op ## size ## _imm_reg (COMMON_ARGS, int32 val, int32 reg) \ { \ int guest_reg = c->host_reg_to_guest_reg[reg]; \ \ val = (val & (size_mask)) | (mask); \ \ switch (c->guest_reg_status[guest_reg].mapping) \ { \ case MAP_NATIVE: \ if (byte_p || (cc_to_compute & (M68K_CCN | M68K_CCZ))) \ i386_ ## op ## size ## _imm_reg (COMMON_ARG_NAMES, val, reg); \ else \ i386_ ## op ## l_imm_reg (COMMON_ARG_NAMES, val | (mask), reg); \ break; \ case MAP_SWAP16: \ /* andb $12,%ah instead of andb $12,%al? Since we're doing \ * a byte op we know we must have a byte reg. \ */ \ if (byte_p) \ i386_ ## op ## b_imm_reg (COMMON_ARG_NAMES, val, reg + 4); \ else if (cc_to_compute & (M68K_CCN | M68K_CCZ)) \ i386_ ## op ## size ## _imm_reg (COMMON_ARG_NAMES, \ (SWAPUW_IFLE (val) \ | (val & 0xFFFF0000)), \ reg); \ else \ i386_ ## op ## l_imm_reg (COMMON_ARG_NAMES, \ (SWAPUW_IFLE (val) \ | (val & 0xFFFF0000)), \ reg); \ break; \ case MAP_SWAP32: \ if (cc_to_compute & (M68K_CCN | M68K_CCZ)) \ { \ if (byte_p) \ return 1; /* Can't easily get it. */ \ i386_ ## op ## size ## _imm_reg (COMMON_ARG_NAMES, \ SWAPUL_IFLE (val), reg); \ } \ else \ i386_ ## op ## l_imm_reg (COMMON_ARG_NAMES, \ SWAPUL_IFLE (val), reg); \ break; \ default: \ abort (); \ } \ \ c->guest_reg_status[guest_reg].dirty_without_offset_p = TRUE; \ \ return 0; \ } HOST_BITWISE_IMM_REG (and, b, 0xFFFFFF00, 0x000000FF, TRUE) HOST_BITWISE_IMM_REG (and, w, 0xFFFF0000, 0x0000FFFF, FALSE) HOST_BITWISE_IMM_REG (and, l, 0x00000000, 0xFFFFFFFF, FALSE) HOST_BITWISE_IMM_REG (or, b, 0, 0x000000FF, TRUE) HOST_BITWISE_IMM_REG (or, w, 0, 0x0000FFFF, FALSE) HOST_BITWISE_IMM_REG (or, l, 0, 0xFFFFFFFF, FALSE) HOST_BITWISE_IMM_REG (xor, b, 0, 0x000000FF, TRUE) HOST_BITWISE_IMM_REG (xor, w, 0, 0x0000FFFF, FALSE) HOST_BITWISE_IMM_REG (xor, l, 0, 0xFFFFFFFF, FALSE) #define HOST_BITWISE_IMM_ABS(op, size, swapfunc) \ int \ host_ ## op ## size ## _imm_abs (COMMON_ARGS, int32 val, int32 addr) \ { \ /* Only guaranteed to compute CVZ carry bits; N will of course be \ * botched. For cmp this only computes Z correctly. \ */ \ return i386_ ## op ## size ## _imm_abs (COMMON_ARG_NAMES, swapfunc (val), \ (int32) SYN68K_TO_US (addr)); \ } HOST_BITWISE_IMM_ABS (and, w, SWAPUW) HOST_BITWISE_IMM_ABS (and, l, SWAPUL) HOST_BITWISE_IMM_ABS (or, w, SWAPUW) HOST_BITWISE_IMM_ABS (or, l, SWAPUL) HOST_BITWISE_IMM_ABS (xor, w, SWAPUW) HOST_BITWISE_IMM_ABS (xor, l, SWAPUL) HOST_BITWISE_IMM_ABS (cmp, w, SWAPUW) HOST_BITWISE_IMM_ABS (cmp, l, SWAPUL) #define HOST_BITWISE_IMM_MEM(size, op, amode, swapfunc, preoff, postoff, \ adjust_p) \ int \ host_ ## op ## size ## _imm_ ## amode (COMMON_ARGS, int32 val, OFF int32 reg)\ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[reg]; \ \ CHECK_ADDR_REG (reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Actually perform the operation. */ \ if (regoff == 0) \ i386_ ## op ## size ## _imm_ind (COMMON_ARG_NAMES, \ swapfunc (val), reg); \ else \ i386_ ## op ## size ## _imm_indoff (COMMON_ARG_NAMES, swapfunc (val), \ regoff, reg); \ /* Adjust the address register. */ \ if (postoff && adjust_p) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_BITWISE_IMM_MEM(b, and, ind, SWAPUB, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(w, and, ind, SWAPUW, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(l, and, ind, SWAPUL, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(b, or, ind, SWAPUB, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(w, or, ind, SWAPUW, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(l, or, ind, SWAPUL, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(b, xor, ind, SWAPUB, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(w, xor, ind, SWAPUW, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(l, xor, ind, SWAPUL, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(b, add, ind, SWAPUB, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(b, sub, ind, SWAPUB, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(b, cmp, ind, SWAPUB, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(w, cmp, ind, SWAPUW, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(l, cmp, ind, SWAPUL, 0, 0, FALSE) HOST_BITWISE_IMM_MEM(b, and, predec, SWAPUB, -1, -1, TRUE) HOST_BITWISE_IMM_MEM(w, and, predec, SWAPUW, -2, -2, TRUE) HOST_BITWISE_IMM_MEM(l, and, predec, SWAPUL, -4, -4, TRUE) HOST_BITWISE_IMM_MEM(b, or, predec, SWAPUB, -1, -1, TRUE) HOST_BITWISE_IMM_MEM(w, or, predec, SWAPUW, -2, -2, TRUE) HOST_BITWISE_IMM_MEM(l, or, predec, SWAPUL, -4, -4, TRUE) HOST_BITWISE_IMM_MEM(b, xor, predec, SWAPUB, -1, -1, TRUE) HOST_BITWISE_IMM_MEM(w, xor, predec, SWAPUW, -2, -2, TRUE) HOST_BITWISE_IMM_MEM(l, xor, predec, SWAPUL, -4, -4, TRUE) HOST_BITWISE_IMM_MEM(b, add, predec, SWAPUB, -1, -1, TRUE) HOST_BITWISE_IMM_MEM(b, sub, predec, SWAPUB, -1, -1, TRUE) HOST_BITWISE_IMM_MEM(b, cmp, predec, SWAPUB, -1, -1, TRUE) HOST_BITWISE_IMM_MEM(w, cmp, predec, SWAPUW, -2, -2, TRUE) HOST_BITWISE_IMM_MEM(l, cmp, predec, SWAPUL, -4, -4, TRUE) HOST_BITWISE_IMM_MEM(b, and, postinc, SWAPUB, 0, 1, TRUE) HOST_BITWISE_IMM_MEM(w, and, postinc, SWAPUW, 0, 2, TRUE) HOST_BITWISE_IMM_MEM(l, and, postinc, SWAPUL, 0, 4, TRUE) HOST_BITWISE_IMM_MEM(b, or, postinc, SWAPUB, 0, 1, TRUE) HOST_BITWISE_IMM_MEM(w, or, postinc, SWAPUW, 0, 2, TRUE) HOST_BITWISE_IMM_MEM(l, or, postinc, SWAPUL, 0, 4, TRUE) HOST_BITWISE_IMM_MEM(b, xor, postinc, SWAPUB, 0, 1, TRUE) HOST_BITWISE_IMM_MEM(w, xor, postinc, SWAPUW, 0, 2, TRUE) HOST_BITWISE_IMM_MEM(l, xor, postinc, SWAPUL, 0, 4, TRUE) HOST_BITWISE_IMM_MEM(b, add, postinc, SWAPUB, 0, 1, TRUE) HOST_BITWISE_IMM_MEM(b, sub, postinc, SWAPUB, 0, 1, TRUE) HOST_BITWISE_IMM_MEM(b, cmp, postinc, SWAPUB, 0, 1, TRUE) HOST_BITWISE_IMM_MEM(w, cmp, postinc, SWAPUW, 0, 2, TRUE) HOST_BITWISE_IMM_MEM(l, cmp, postinc, SWAPUL, 0, 4, TRUE) #undef OFF #define OFF int32 offset, HOST_BITWISE_IMM_MEM(b, and, indoff, SWAPUB, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(w, and, indoff, SWAPUW, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(l, and, indoff, SWAPUL, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(b, or, indoff, SWAPUB, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(w, or, indoff, SWAPUW, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(l, or, indoff, SWAPUL, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(b, xor, indoff, SWAPUB, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(w, xor, indoff, SWAPUW, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(l, xor, indoff, SWAPUL, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(b, add, indoff, SWAPUB, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(b, sub, indoff, SWAPUB, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(b, cmp, indoff, SWAPUB, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(w, cmp, indoff, SWAPUW, offset, 0, FALSE) HOST_BITWISE_IMM_MEM(l, cmp, indoff, SWAPUL, offset, 0, FALSE) #define HOST_UNARY_MEM(size, op, amode, swapfunc, preoff, postoff, adjust_p) \ int \ host_ ## op ## size ## _ ## amode (COMMON_ARGS, OFF int32 reg) \ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[reg]; \ \ CHECK_ADDR_REG (reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Actually perform the operation. */ \ if (regoff == 0) \ i386_ ## op ## size ## _ind (COMMON_ARG_NAMES, reg); \ else \ i386_ ## op ## size ## _indoff (COMMON_ARG_NAMES, regoff, reg); \ /* Adjust the address register. */ \ if (postoff && adjust_p) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_UNARY_MEM(b, neg, ind, SWAPUB, 0, 0, FALSE) HOST_UNARY_MEM(b, neg, predec, SWAPUB, -1, -1, TRUE) HOST_UNARY_MEM(b, neg, postinc, SWAPUB, 0, 1, TRUE) #undef OFF #define OFF int32 offset, HOST_UNARY_MEM(b, neg, indoff, SWAPUB, offset, 0, FALSE) static inline void test_imm (COMMON_ARGS, int32 val, uint32 size_mask) { /* Write out all four cc bits at once. */ i386_movl_imm_indoff (COMMON_ARG_NAMES, ((val != 0) | ((val & (size_mask ^ (size_mask >> 1))) ? 0x100 : 0)), offsetof (CPUState, ccnz), REG_EBP); c->cached_cc &= ~cc_to_compute; c->dirty_cc &= ~cc_to_compute; } #define HOST_MOV_IMM_REG(size, size_mask) \ int \ host_move ## size ## _imm_reg (COMMON_ARGS, int32 val, int32 reg) \ { \ if (val == 0 && (!cc_spill_if_changed || cc_to_compute)) \ { \ i386_xor ## size ## _reg_reg (COMMON_ARG_NAMES, reg, reg); \ if (cc_to_compute) \ { \ test_imm (COMMON_ARG_NAMES, 0, size_mask); \ c->cached_cc |= cc_to_compute; /* xor cached them. */ \ } \ } \ else \ { \ i386_mov ## size ## _imm_reg (COMMON_ARG_NAMES, val, reg); \ if (cc_to_compute) \ test_imm (COMMON_ARG_NAMES, val, size_mask); \ } \ return 0; \ } HOST_MOV_IMM_REG (b, 0xFF) HOST_MOV_IMM_REG (w, 0xFFFF) HOST_MOV_IMM_REG (l, 0xFFFFFFFF) #define HOST_MOV_IMM_ABS(size, swapfunc, size_mask) \ int \ host_move ## size ## _imm_abs (COMMON_ARGS, int32 val, int32 addr) \ { \ if (cc_to_compute) \ test_imm (COMMON_ARG_NAMES, val, size_mask); \ i386_mov ## size ## _imm_abs (COMMON_ARG_NAMES, swapfunc (val), \ (int32) SYN68K_TO_US (addr)); \ return 0; \ } HOST_MOV_IMM_ABS (b, SWAPUB, 0xFF) HOST_MOV_IMM_ABS (w, SWAPUW, 0xFFFF) HOST_MOV_IMM_ABS (l, SWAPUL, 0xFFFFFFFF) #define HOST_MOV_REG_ABS_SWAP(size, swap) \ int \ host_move ## size ## _reg_abs_swap (COMMON_ARGS, int32 src_reg, int32 addr) \ { \ cc_spill_if_changed |= cc_to_compute; \ swap (src_reg); \ return i386_mov ## size ## _reg_abs (COMMON_ARG_NAMES, src_reg, \ (int32) SYN68K_TO_US (addr)); \ } HOST_MOV_REG_ABS_SWAP (w, HOST_SWAP16) HOST_MOV_REG_ABS_SWAP (l, HOST_SWAP32) #define HOST_MOV_ABS_REG_SWAP(size, swap, long_p) \ int \ host_move ## size ## _abs_reg_swap (COMMON_ARGS, int32 addr, int32 dst_reg) \ { \ i386_mov ## size ## _abs_reg (COMMON_ARG_NAMES, \ (int32) SYN68K_TO_US (addr), \ dst_reg); \ swap (dst_reg); \ return 0; \ } HOST_MOV_ABS_REG_SWAP (w, HOST_SWAP16, FALSE) HOST_MOV_ABS_REG_SWAP (l, HOST_SWAP32, TRUE) #define HOST_MOV_IMM_MEM(size, amode, swapfunc, preoff, postoff, adjust_p, \ size_mask) \ int \ host_move ## size ## _imm_ ## amode (COMMON_ARGS, int32 val, OFF int32 reg) \ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[reg]; \ \ CHECK_ADDR_REG (reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Set cc bits according to this value, if necessary. */ \ if (cc_to_compute) \ { \ test_imm (COMMON_ARG_NAMES, val, size_mask); \ cc_to_compute = M68K_CC_NONE; \ } \ \ /* Actually write the value out to memory. */ \ if (regoff == 0) \ i386_mov ## size ## _imm_ind (COMMON_ARG_NAMES, \ swapfunc (val), reg); \ else \ i386_mov ## size ## _imm_indoff (COMMON_ARG_NAMES, swapfunc (val), \ regoff, reg); \ /* Adjust the address register. */ \ if (postoff && adjust_p) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_MOV_IMM_MEM(b, ind, SWAPUB, 0, 0, FALSE, 0xFF) HOST_MOV_IMM_MEM(w, ind, SWAPUW, 0, 0, FALSE, 0xFFFF) HOST_MOV_IMM_MEM(l, ind, SWAPUL, 0, 0, FALSE, 0xFFFFFFFF) HOST_MOV_IMM_MEM(b, predec, SWAPUB, -1, -1, TRUE, 0xFF) HOST_MOV_IMM_MEM(w, predec, SWAPUW, -2, -2, TRUE, 0xFFFF) HOST_MOV_IMM_MEM(l, predec, SWAPUL, -4, -4, TRUE, 0xFFFFFFFF) HOST_MOV_IMM_MEM(b, postinc, SWAPUB, 0, 1, TRUE, 0xFF) HOST_MOV_IMM_MEM(w, postinc, SWAPUW, 0, 2, TRUE, 0xFFFF) HOST_MOV_IMM_MEM(l, postinc, SWAPUL, 0, 4, TRUE, 0xFFFFFFFF) #undef OFF #define OFF int32 offset, HOST_MOV_IMM_MEM(b, indoff, SWAPUB, offset, 0, FALSE, 0xFF) HOST_MOV_IMM_MEM(w, indoff, SWAPUW, offset, 0, FALSE, 0xFFFF) HOST_MOV_IMM_MEM(l, indoff, SWAPUL, offset, 0, FALSE, 0xFFFFFFFF) #define COMPLEX_P(n) ((n) & (1 << 8)) #define LONG_INDEX_REG_P(n) ((n) & (1 << 11)) #define SCALE(n) (((n) >> 9) & 3) #define DISPLACEMENT(n) ((int8) ((n) & 0xFF)) #define INDEX_REG(n) (((n) >> 12) & 0xF) #define HOST_OP_IMM_INDIX(size, name, op, swapfunc, test_imm_p, \ size_mask) \ int \ host_ ## name ## size ## _imm_indix (COMMON_ARGS, int32 val, \ int32 base_addr_reg, \ uint16 *m68k_addr) \ { \ uint16 ext_word; \ int index_reg, guest_index_reg, orig_index_reg; \ int32 disp; \ guest_reg_status_t *base_status, *index_status; \ \ /* Fetch the extension word and make sure it's a "simple" reference. */ \ ext_word = SWAPUW (m68k_addr[1]); \ if (COMPLEX_P (ext_word)) \ return 2; \ \ /* Set up the index register. */ \ guest_index_reg = INDEX_REG (ext_word); \ index_status = &c->guest_reg_status[guest_index_reg]; \ if (index_status->host_reg == NO_REG \ && !LONG_INDEX_REG_P (ext_word)) \ { \ i386_movswl_indoff_reg (COMMON_ARG_NAMES, \ offsetof (CPUState, \ regs[guest_index_reg].sw.n), \ REG_EBP, scratch_reg); \ index_reg = scratch_reg; \ } \ else \ { \ host_reg_mask_t legal_regs; \ legal_regs = ((~((1L << base_addr_reg) | (1L << scratch_reg))) \ & ((guest_index_reg < 8) \ ? REGSET_BYTE : REGSET_ALL)); \ if (c->guest_reg_status[guest_index_reg].host_reg \ == base_addr_reg) \ legal_regs |= (1L << base_addr_reg); \ orig_index_reg = host_setup_cached_reg (COMMON_ARG_NAMES, \ guest_index_reg, \ MAP_NATIVE_MASK, \ legal_regs); \ if (orig_index_reg == NO_REG) \ return 3; \ if (LONG_INDEX_REG_P (ext_word)) \ index_reg = orig_index_reg; \ else \ { \ i386_movswl_reg_reg (COMMON_ARG_NAMES, orig_index_reg, \ scratch_reg); \ index_reg = scratch_reg; \ } \ } \ \ /* Compute the total displacement. */ \ disp = DISPLACEMENT (ext_word); \ base_status = &c->guest_reg_status \ [c->host_reg_to_guest_reg[base_addr_reg]]; \ if (base_status->mapping == MAP_OFFSET) \ disp += base_status->offset; \ \ /* Compensate for offset memory. */ \ disp = (int32) SYN68K_TO_US (disp); \ \ /* Write the value out. */ \ switch (SCALE (ext_word)) \ { \ case 0: \ if (disp == 0) \ op ## size ##_imm_indix_no_offset (COMMON_ARG_NAMES, \ swapfunc (val), \ base_addr_reg, index_reg); \ else \ op ## size ##_imm_indix (COMMON_ARG_NAMES, swapfunc (val), \ disp, base_addr_reg, index_reg); \ break; \ case 1: \ if (disp == 0) \ op ## size ##_imm_indix_scale2_no_offset (COMMON_ARG_NAMES, \ swapfunc (val), \ base_addr_reg, \ index_reg); \ else \ op ## size ##_imm_indix_scale2 (COMMON_ARG_NAMES, \ swapfunc (val), \ disp, base_addr_reg, \ index_reg); \ break; \ case 2: \ if (disp == 0) \ op ## size ##_imm_indix_scale4_no_offset (COMMON_ARG_NAMES, \ swapfunc (val), \ base_addr_reg, \ index_reg); \ else \ op ## size ##_imm_indix_scale4 (COMMON_ARG_NAMES, \ swapfunc (val), \ disp, base_addr_reg, \ index_reg); \ break; \ case 3: \ if (disp == 0) \ op ## size ##_imm_indix_scale8_no_offset (COMMON_ARG_NAMES, \ swapfunc (val), \ base_addr_reg, \ index_reg); \ else \ op ## size ##_imm_indix_scale8 (COMMON_ARG_NAMES, \ swapfunc (val), \ disp, base_addr_reg, \ index_reg); \ break; \ default: \ abort (); \ } \ \ /* If we need cc bits, compute them. */ \ if ((test_imm_p) && cc_to_compute) \ test_imm (COMMON_ARG_NAMES, val, size_mask); \ \ return 0; \ } HOST_OP_IMM_INDIX (b, move, i386_mov, SWAPUB, TRUE, 0xFF) HOST_OP_IMM_INDIX (w, move, i386_mov, SWAPUW, TRUE, 0xFFFF) HOST_OP_IMM_INDIX (l, move, i386_mov, SWAPUL, TRUE, 0xFFFFFFFF) HOST_OP_IMM_INDIX (b, cmp, i386_cmp, SWAPUB, FALSE, 0xFF) HOST_OP_IMM_INDIX (w, cmp, i386_cmp, SWAPUW, FALSE, 0xFFFF) HOST_OP_IMM_INDIX (l, cmp, i386_cmp, SWAPUL, FALSE, 0xFFFFFFFF) HOST_OP_IMM_INDIX (b, add, i386_add, SWAPUB, FALSE, 0xFF) HOST_OP_IMM_INDIX (b, sub, i386_sub, SWAPUB, FALSE, 0xFF) #define HOST_ARITH_IMM_INDIX(size, name, op, swapfunc, byte_p, word_p) \ int \ host_ ## name ## size ## _imm_indix (COMMON_ARGS, int32 val, \ int32 base_addr_reg, \ uint16 *m68k_addr) \ { \ uint16 ext_word; \ int tmp_val_reg; \ \ /* Fetch the extension word and make sure it's a "simple" reference. */ \ ext_word = SWAPUW (m68k_addr[1]); \ if (COMPLEX_P (ext_word)) \ return 1; \ \ if (host_leal_indix_reg (COMMON_ARG_NAMES, base_addr_reg, \ scratch_reg, m68k_addr)) \ return 2; \ \ /* Grab a temporary register to hold the memory value. */ \ tmp_val_reg = host_alloc_reg (c, codep, cc_spill_if_changed, \ ((byte_p ? REGSET_BYTE : REGSET_ALL) \ & ~(1L << scratch_reg))); \ if (tmp_val_reg == NO_REG) \ return 3; \ \ /* We've now got the address of the value in scratch_reg. Load the \ * value, swap it, operate on it, swap it back, and write it out \ * to memory. \ */ \ i386_mov ## size ## _indoff_reg (COMMON_ARG_NAMES, \ (int32) SYN68K_TO_US (0), \ scratch_reg, tmp_val_reg); \ swapfunc (tmp_val_reg); \ if (word_p && !cc_to_compute) \ op ## size ## _imm_reg (COMMON_ARG_NAMES, val, tmp_val_reg); \ else \ op ## size ## _imm_reg (COMMON_ARG_NAMES, val, tmp_val_reg); \ cc_spill_if_changed |= cc_to_compute; \ swapfunc (tmp_val_reg); \ i386_mov ## size ## _reg_indoff (COMMON_ARG_NAMES, tmp_val_reg, \ (int32) SYN68K_TO_US (0), scratch_reg); \ \ return 0; \ } HOST_ARITH_IMM_INDIX (w, add, i386_add, HOST_SWAP16, FALSE, TRUE) HOST_ARITH_IMM_INDIX (l, add, i386_add, HOST_SWAP32, FALSE, FALSE) HOST_ARITH_IMM_INDIX (w, sub, i386_sub, HOST_SWAP16, FALSE, TRUE) HOST_ARITH_IMM_INDIX (l, sub, i386_sub, HOST_SWAP32, FALSE, FALSE) #define HOST_OP_REG_INDIX(size, name, op, swapfunc, test_src_p, byte_p) \ int \ host_ ## name ## size ## _reg_indix (COMMON_ARGS, int32 src_reg, \ int32 base_addr_reg, \ uint16 *m68k_addr) \ { \ uint16 ext_word; \ int index_reg, guest_index_reg, orig_index_reg; \ int32 disp; \ guest_reg_status_t *base_status; \ \ /* Fetch the extension word and make sure it's a "simple" reference. */ \ ext_word = SWAPUW (m68k_addr[1]); \ if (COMPLEX_P (ext_word)) \ return 1; \ \ /* Sometimes we need another scratch register, like for \ * memory->memory moves. This works around that problem by \ * allocating another one if the source reg is the scratch reg. \ */ \ if (src_reg == scratch_reg) \ { \ host_reg_mask_t scratch_regs; \ scratch_regs = ((~((1L << base_addr_reg) | (1L << scratch_reg) \ | (1L << src_reg))) \ & REGSET_ALL); \ scratch_reg = host_alloc_reg (c, codep, cc_spill_if_changed, \ scratch_regs); \ if (scratch_reg == NO_REG) \ return 6; \ } \ \ /* Set up the index register. */ \ guest_index_reg = INDEX_REG (ext_word); \ if (c->guest_reg_status[guest_index_reg].host_reg == NO_REG \ && !LONG_INDEX_REG_P (ext_word)) \ { \ i386_movswl_indoff_reg (COMMON_ARG_NAMES, \ offsetof (CPUState, \ regs[guest_index_reg].sw.n), \ REG_EBP, scratch_reg); \ index_reg = scratch_reg; \ } \ else \ { \ int host_index_reg; \ host_reg_mask_t legal_regs; \ \ legal_regs = ((~((1L << base_addr_reg) | (1L << scratch_reg) \ | (1L << src_reg))) \ & ((guest_index_reg < 8) \ ? REGSET_BYTE : REGSET_ALL)); \ host_index_reg = c->guest_reg_status[guest_index_reg].host_reg; \ if (host_index_reg == base_addr_reg \ || (byte_p && host_index_reg == src_reg)) \ legal_regs |= (1L << host_index_reg); \ if (host_index_reg == src_reg) \ { \ i386_movl_reg_reg (COMMON_ARG_NAMES, src_reg, scratch_reg); \ swapfunc (scratch_reg); \ host_index_reg = orig_index_reg = scratch_reg; \ } \ else \ { \ if (host_index_reg != NO_REG \ && !(legal_regs & (1L << host_index_reg))) \ return 5; \ orig_index_reg = host_setup_cached_reg (COMMON_ARG_NAMES, \ guest_index_reg, \ MAP_NATIVE_MASK, \ legal_regs); \ if (orig_index_reg == NO_REG) \ return 3; \ } \ if (LONG_INDEX_REG_P (ext_word)) \ index_reg = orig_index_reg; \ else \ { \ i386_movswl_reg_reg (COMMON_ARG_NAMES, orig_index_reg, \ scratch_reg); \ index_reg = scratch_reg; \ } \ } \ \ /* Compute the total displacement. */ \ disp = DISPLACEMENT (ext_word); \ base_status = &c->guest_reg_status \ [c->host_reg_to_guest_reg[base_addr_reg]]; \ if (base_status->mapping == MAP_OFFSET) \ disp += base_status->offset; \ \ /* Compensate for offset memory. */ \ disp = (int32) SYN68K_TO_US (disp); \ \ /* Write the value out. */ \ switch (SCALE (ext_word)) \ { \ case 0: \ if (disp == 0) \ op ## size ##_reg_indix_no_offset (COMMON_ARG_NAMES, \ src_reg, \ base_addr_reg, index_reg); \ else \ op ## size ##_reg_indix (COMMON_ARG_NAMES, src_reg, \ disp, base_addr_reg, index_reg); \ break; \ case 1: \ if (disp == 0) \ op ## size ##_reg_indix_scale2_no_offset (COMMON_ARG_NAMES, \ src_reg, \ base_addr_reg, \ index_reg); \ else \ op ## size ##_reg_indix_scale2 (COMMON_ARG_NAMES, \ src_reg, \ disp, base_addr_reg, \ index_reg); \ break; \ case 2: \ if (disp == 0) \ op ## size ##_reg_indix_scale4_no_offset (COMMON_ARG_NAMES, \ src_reg, \ base_addr_reg, \ index_reg); \ else \ op ## size ##_reg_indix_scale4 (COMMON_ARG_NAMES, \ src_reg, \ disp, base_addr_reg, \ index_reg); \ break; \ case 3: \ if (disp == 0) \ op ## size ##_reg_indix_scale8_no_offset (COMMON_ARG_NAMES, \ src_reg, \ base_addr_reg, \ index_reg); \ else \ op ## size ##_reg_indix_scale8 (COMMON_ARG_NAMES, \ src_reg, \ disp, base_addr_reg, \ index_reg); \ break; \ default: \ abort (); \ } \ \ /* If we need cc bits, compute them. */ \ if ((test_src_p) && cc_to_compute) \ { \ if ((byte_p) || !(cc_to_compute & M68K_CCN)) \ i386_test ## size ## _reg_reg (COMMON_ARG_NAMES, src_reg, \ src_reg); \ else \ { \ i386_movl_reg_reg (COMMON_ARG_NAMES, src_reg, scratch_reg); \ swapfunc (scratch_reg); \ i386_test ## size ## _reg_reg (COMMON_ARG_NAMES, \ scratch_reg, scratch_reg); \ } \ } \ \ return 0; \ } #define NOSWAP(n) HOST_OP_REG_INDIX (b, move, i386_mov, NOSWAP, TRUE, TRUE) HOST_OP_REG_INDIX (w, move, i386_mov, HOST_SWAP16, TRUE, FALSE) HOST_OP_REG_INDIX (l, move, i386_mov, HOST_SWAP32, TRUE, FALSE) #define HOST_OP_INDIX_REG(size, name, op, long_p, cmp_p, lea_p) \ int \ host_ ## name ## size ## _indix_reg (COMMON_ARGS, int32 base_addr_reg, \ int32 dst_reg, \ uint16 *m68k_addr) \ { \ uint16 ext_word; \ int index_reg, guest_index_reg, orig_index_reg; \ int32 disp; \ guest_reg_status_t *base_status; \ \ /* Fetch the extension word and make sure it's a "simple" reference. */ \ ext_word = SWAPUW (m68k_addr[1]); \ if (COMPLEX_P (ext_word)) \ return 1; \ \ /* Set up the index register. */ \ guest_index_reg = INDEX_REG (ext_word); \ if (c->guest_reg_status[guest_index_reg].host_reg == NO_REG) \ { \ if (LONG_INDEX_REG_P (ext_word)) \ i386_movl_indoff_reg (COMMON_ARG_NAMES, \ offsetof (CPUState, \ regs[guest_index_reg].ul.n), \ REG_EBP, scratch_reg); \ else \ i386_movswl_indoff_reg (COMMON_ARG_NAMES, \ offsetof (CPUState, \ regs[guest_index_reg].sw.n), \ REG_EBP, scratch_reg); \ index_reg = scratch_reg; \ } \ else \ { \ host_reg_mask_t legal_regs; \ int host_index_reg; \ legal_regs = ((~((1L << base_addr_reg) | (1L << scratch_reg) \ | (1L << dst_reg))) \ & ((guest_index_reg < 8) \ ? REGSET_BYTE : REGSET_ALL)); \ host_index_reg = c->guest_reg_status[guest_index_reg].host_reg; \ if (host_index_reg == base_addr_reg \ || (!cmp_p && host_index_reg == dst_reg)) \ legal_regs |= (1L << host_index_reg); \ if (cmp_p) \ legal_regs &= ~(1L << dst_reg); \ if (host_index_reg != NO_REG \ && !(legal_regs & (1L << host_index_reg))) \ return 5; \ \ /* When moving a long, the dest register's old value might be \ * invalid (since we are about to smash it). If that register \ * is also the index register, we don't want to trust the junk \ * that's in it. To be safe, reload the value. \ */ \ if (long_p && !cmp_p && host_index_reg == dst_reg \ && !c->guest_reg_status[guest_index_reg].dirty_without_offset_p \ && c->guest_reg_status[guest_index_reg].mapping == MAP_NATIVE) \ { \ i386_movl_indoff_reg (COMMON_ARG_NAMES, \ offsetof (CPUState, \ regs[guest_index_reg].ul.n), \ REG_EBP, host_index_reg); \ orig_index_reg = host_index_reg; \ } \ else \ orig_index_reg = host_setup_cached_reg (COMMON_ARG_NAMES, \ guest_index_reg, \ MAP_NATIVE_MASK, \ legal_regs); \ \ if (orig_index_reg == NO_REG) \ return 2; \ if (LONG_INDEX_REG_P (ext_word)) \ index_reg = orig_index_reg; \ else \ { \ i386_movswl_reg_reg (COMMON_ARG_NAMES, orig_index_reg, \ scratch_reg); \ index_reg = scratch_reg; \ } \ } \ \ /* Compute the total displacement. */ \ disp = DISPLACEMENT (ext_word); \ base_status = &c->guest_reg_status \ [c->host_reg_to_guest_reg[base_addr_reg]]; \ if (base_status->mapping == MAP_OFFSET) \ disp += base_status->offset; \ \ /* Compensate for offset memory. */ \ if (!(lea_p)) \ disp = (int32) SYN68K_TO_US (disp); \ \ /* Write the value out. */ \ switch (SCALE (ext_word)) \ { \ case 0: \ if (disp == 0) \ op ## size ##_indix_reg_no_offset (COMMON_ARG_NAMES, \ base_addr_reg, index_reg, \ dst_reg); \ else \ op ## size ##_indix_reg (COMMON_ARG_NAMES, \ disp, base_addr_reg, index_reg, \ dst_reg); \ break; \ case 1: \ if (disp == 0) \ op ## size ##_indix_reg_scale2_no_offset (COMMON_ARG_NAMES, \ base_addr_reg, \ index_reg, \ dst_reg); \ else \ op ## size ##_indix_reg_scale2 (COMMON_ARG_NAMES, \ disp, base_addr_reg, index_reg, \ dst_reg); \ break; \ case 2: \ if (disp == 0) \ op ## size ##_indix_reg_scale4_no_offset (COMMON_ARG_NAMES, \ base_addr_reg, \ index_reg, \ dst_reg); \ else \ op ## size ##_indix_reg_scale4 (COMMON_ARG_NAMES, \ disp, base_addr_reg, \ index_reg, \ dst_reg); \ break; \ case 3: \ if (disp == 0) \ op ## size ##_indix_reg_scale8_no_offset (COMMON_ARG_NAMES, \ base_addr_reg, \ index_reg, \ dst_reg); \ else \ op ## size ##_indix_reg_scale8 (COMMON_ARG_NAMES, \ disp, base_addr_reg, \ index_reg, \ dst_reg); \ break; \ default: \ abort (); \ } \ \ return 0; \ } HOST_OP_INDIX_REG (b, move, i386_mov, FALSE, FALSE, FALSE) HOST_OP_INDIX_REG (w, move, i386_mov, FALSE, FALSE, FALSE) HOST_OP_INDIX_REG (l, move, i386_mov, TRUE, FALSE, FALSE) HOST_OP_INDIX_REG (b, cmp, i386_cmp, FALSE, TRUE, FALSE) HOST_OP_INDIX_REG (w, cmp, i386_cmp, FALSE, TRUE, FALSE) HOST_OP_INDIX_REG (l, cmp, i386_cmp, TRUE, TRUE, FALSE) HOST_OP_INDIX_REG (l, lea, i386_lea, TRUE, FALSE, TRUE) #define HOST_MOV_REG_MEM(size, amode, preoff, postoff, adjust_p) \ int \ host_move ## size ## _reg_ ## amode (COMMON_ARGS, int32 src_reg, \ OFF int32 dst_addr_reg) \ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[dst_addr_reg]; \ \ CHECK_ADDR_REG (dst_addr_reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Actually write the value out to memory. */ \ if (regoff == 0) \ i386_mov ## size ## _reg_ind (COMMON_ARG_NAMES, \ src_reg, dst_addr_reg); \ else \ i386_mov ## size ## _reg_indoff (COMMON_ARG_NAMES, src_reg, \ regoff, dst_addr_reg); \ /* Adjust the address register. */ \ if (postoff && adjust_p) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_MOV_REG_MEM(b, ind, 0, 0, FALSE) HOST_MOV_REG_MEM(w, ind, 0, 0, FALSE) HOST_MOV_REG_MEM(l, ind, 0, 0, FALSE) HOST_MOV_REG_MEM(b, predec, -1, -1, TRUE) HOST_MOV_REG_MEM(w, predec, -2, -2, TRUE) HOST_MOV_REG_MEM(l, predec, -4, -4, TRUE) HOST_MOV_REG_MEM(b, postinc, 0, 1, TRUE) HOST_MOV_REG_MEM(w, postinc, 0, 2, TRUE) HOST_MOV_REG_MEM(l, postinc, 0, 4, TRUE) #undef OFF #define OFF int32 offset, HOST_MOV_REG_MEM(b, indoff, offset, 0, FALSE) HOST_MOV_REG_MEM(w, indoff, offset, 0, FALSE) HOST_MOV_REG_MEM(l, indoff, offset, 0, FALSE) #define HOST_MOV_REG_MEM_SWAP(size, amode, swap) \ int \ host_move ## size ## _reg_ ## amode ## _swap (COMMON_ARGS, int32 src_reg,\ OFF int32 addr_reg) \ { \ /* Bail out if the two registers are the same! */ \ if (src_reg == addr_reg) \ return 1; \ \ CHECK_ADDR_REG (addr_reg); \ \ cc_spill_if_changed |= cc_to_compute; \ swap (src_reg); \ return host_move ## size ## _reg_ ## amode (COMMON_ARG_NAMES, src_reg, \ OFFNAME addr_reg); \ } #undef OFF #define OFF #undef OFFNAME #define OFFNAME HOST_MOV_REG_MEM_SWAP(w, ind, HOST_SWAP16) HOST_MOV_REG_MEM_SWAP(l, ind, HOST_SWAP32) HOST_MOV_REG_MEM_SWAP(w, predec, HOST_SWAP16) HOST_MOV_REG_MEM_SWAP(l, predec, HOST_SWAP32) HOST_MOV_REG_MEM_SWAP(w, postinc, HOST_SWAP16) HOST_MOV_REG_MEM_SWAP(l, postinc, HOST_SWAP32) #undef OFF #define OFF int32 offset, #undef OFFNAME #define OFFNAME offset, HOST_MOV_REG_MEM_SWAP(w, indoff, HOST_SWAP16) HOST_MOV_REG_MEM_SWAP(l, indoff, HOST_SWAP32) #define HOST_MOV_MEM_REG(size, amode, preoff, postoff, adjust_p, byte_p) \ int \ host_move ## size ## _ ## amode ## _reg (COMMON_ARGS, OFF int32 src_addr_reg,\ int32 dst_reg) \ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[src_addr_reg]; \ \ CHECK_ADDR_REG (src_addr_reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Read the value in from memory. */ \ if (regoff == 0) \ i386_mov ## size ## _ind_reg (COMMON_ARG_NAMES, src_addr_reg, dst_reg); \ else \ i386_mov ## size ## _indoff_reg (COMMON_ARG_NAMES, regoff, src_addr_reg, \ dst_reg); \ \ /* Adjust the address register. */ \ if (postoff && adjust_p && (byte_p || src_addr_reg != dst_reg)) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_MOV_MEM_REG(b, ind, 0, 0, FALSE, TRUE) HOST_MOV_MEM_REG(w, ind, 0, 0, FALSE, FALSE) HOST_MOV_MEM_REG(l, ind, 0, 0, FALSE, FALSE) HOST_MOV_MEM_REG(b, predec, -1, -1, TRUE, TRUE) HOST_MOV_MEM_REG(w, predec, -2, -2, TRUE, FALSE) HOST_MOV_MEM_REG(l, predec, -4, -4, TRUE, FALSE) HOST_MOV_MEM_REG(b, postinc, 0, 1, TRUE, TRUE) HOST_MOV_MEM_REG(w, postinc, 0, 2, TRUE, FALSE) HOST_MOV_MEM_REG(l, postinc, 0, 4, TRUE, FALSE) #undef OFF #define OFF int32 offset, HOST_MOV_MEM_REG(b, indoff, offset, 0, FALSE, TRUE) HOST_MOV_MEM_REG(w, indoff, offset, 0, FALSE, FALSE) HOST_MOV_MEM_REG(l, indoff, offset, 0, FALSE, FALSE) #define HOST_MOV_MEM_REG_SWAP(size, amode, swap) \ int \ host_move ## size ## _ ## amode ## _reg_swap (COMMON_ARGS, \ OFF int32 addr_reg, \ int32 dst_reg) \ { \ CHECK_ADDR_REG (addr_reg); \ \ host_move ## size ## _ ## amode ## _reg (COMMON_ARG_NAMES, \ OFFNAME addr_reg, dst_reg); \ swap (dst_reg); \ return 0; \ } #undef OFF #define OFF #undef OFFNAME #define OFFNAME HOST_MOV_MEM_REG_SWAP(w, ind, HOST_SWAP16) HOST_MOV_MEM_REG_SWAP(l, ind, HOST_SWAP32) HOST_MOV_MEM_REG_SWAP(w, predec, HOST_SWAP16) HOST_MOV_MEM_REG_SWAP(l, predec, HOST_SWAP32) HOST_MOV_MEM_REG_SWAP(w, postinc, HOST_SWAP16) HOST_MOV_MEM_REG_SWAP(l, postinc, HOST_SWAP32) #undef OFF #define OFF int32 offset, #undef OFFNAME #define OFFNAME offset, HOST_MOV_MEM_REG_SWAP(w, indoff, HOST_SWAP16) HOST_MOV_MEM_REG_SWAP(l, indoff, HOST_SWAP32) #define HOST_OP_MEM_REG(size, op, amode, preoff, postoff, adjust_p, byte_p) \ int \ host_ ## op ## size ## _ ## amode ## _reg (COMMON_ARGS, \ OFF int32 src_addr_reg, \ int32 dst_reg) \ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[src_addr_reg]; \ \ CHECK_ADDR_REG (src_addr_reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Read the value in from memory. */ \ if (regoff == 0) \ i386_ ## op ## size ## _ind_reg (COMMON_ARG_NAMES, src_addr_reg, \ dst_reg); \ else \ i386_ ## op ## size ## _indoff_reg (COMMON_ARG_NAMES, regoff, \ src_addr_reg, dst_reg); \ \ /* Adjust the address register. */ \ if (postoff && adjust_p && (byte_p || src_addr_reg != dst_reg)) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_OP_MEM_REG(b, and, ind, 0, 0, FALSE, TRUE) HOST_OP_MEM_REG(w, and, ind, 0, 0, FALSE, FALSE) HOST_OP_MEM_REG(l, and, ind, 0, 0, FALSE, FALSE) HOST_OP_MEM_REG(b, and, predec, -1, -1, TRUE, TRUE) HOST_OP_MEM_REG(w, and, predec, -2, -2, TRUE, FALSE) HOST_OP_MEM_REG(l, and, predec, -4, -4, TRUE, FALSE) HOST_OP_MEM_REG(b, and, postinc, 0, 1, TRUE, TRUE) HOST_OP_MEM_REG(w, and, postinc, 0, 2, TRUE, FALSE) HOST_OP_MEM_REG(l, and, postinc, 0, 4, TRUE, FALSE) #undef OFF #define OFF int32 offset, HOST_OP_MEM_REG(b, and, indoff, offset, 0, FALSE, TRUE) HOST_OP_MEM_REG(w, and, indoff, offset, 0, FALSE, FALSE) HOST_OP_MEM_REG(l, and, indoff, offset, 0, FALSE, FALSE) #undef OFF #define OFF HOST_OP_MEM_REG(b, or, ind, 0, 0, FALSE, TRUE) HOST_OP_MEM_REG(w, or, ind, 0, 0, FALSE, FALSE) HOST_OP_MEM_REG(l, or, ind, 0, 0, FALSE, FALSE) HOST_OP_MEM_REG(b, or, predec, -1, -1, TRUE, TRUE) HOST_OP_MEM_REG(w, or, predec, -2, -2, TRUE, FALSE) HOST_OP_MEM_REG(l, or, predec, -4, -4, TRUE, FALSE) HOST_OP_MEM_REG(b, or, postinc, 0, 1, TRUE, TRUE) HOST_OP_MEM_REG(w, or, postinc, 0, 2, TRUE, FALSE) HOST_OP_MEM_REG(l, or, postinc, 0, 4, TRUE, FALSE) #undef OFF #define OFF int32 offset, HOST_OP_MEM_REG(b, or, indoff, offset, 0, FALSE, TRUE) HOST_OP_MEM_REG(w, or, indoff, offset, 0, FALSE, FALSE) HOST_OP_MEM_REG(l, or, indoff, offset, 0, FALSE, FALSE) #undef OFF #define OFF HOST_OP_MEM_REG(b, add, ind, 0, 0, FALSE, TRUE) HOST_OP_MEM_REG(b, add, predec, -1, -1, TRUE, TRUE) HOST_OP_MEM_REG(b, add, postinc, 0, 1, TRUE, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_MEM_REG(b, add, indoff, offset, 0, FALSE, TRUE) #undef OFF #define OFF HOST_OP_MEM_REG(b, sub, ind, 0, 0, FALSE, TRUE) HOST_OP_MEM_REG(b, sub, predec, -1, -1, TRUE, TRUE) HOST_OP_MEM_REG(b, sub, postinc, 0, 1, TRUE, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_MEM_REG(b, sub, indoff, offset, 0, FALSE, TRUE) #undef OFF #define OFF HOST_OP_MEM_REG(b, cmp, ind, 0, 0, FALSE, TRUE) HOST_OP_MEM_REG(w, cmp, ind, 0, 0, FALSE, FALSE) HOST_OP_MEM_REG(l, cmp, ind, 0, 0, FALSE, FALSE) HOST_OP_MEM_REG(b, cmp, predec, -1, -1, TRUE, TRUE) HOST_OP_MEM_REG(w, cmp, predec, -2, -2, TRUE, FALSE) HOST_OP_MEM_REG(l, cmp, predec, -4, -4, TRUE, FALSE) HOST_OP_MEM_REG(b, cmp, postinc, 0, 1, TRUE, TRUE) HOST_OP_MEM_REG(w, cmp, postinc, 0, 2, TRUE, FALSE) HOST_OP_MEM_REG(l, cmp, postinc, 0, 4, TRUE, FALSE) #undef OFF #define OFF int32 offset, HOST_OP_MEM_REG(b, cmp, indoff, offset, 0, FALSE, TRUE) HOST_OP_MEM_REG(w, cmp, indoff, offset, 0, FALSE, FALSE) HOST_OP_MEM_REG(l, cmp, indoff, offset, 0, FALSE, FALSE) #define HOST_OP_REG_MEM(size, op, amode, preoff, postoff, adjust_p) \ int \ host_ ## op ## size ## _reg_ ## amode (COMMON_ARGS, int32 src_reg, \ OFF int32 dst_addr_reg) \ { \ int32 regoff; \ int offset_p; \ int guest_reg = c->host_reg_to_guest_reg[dst_addr_reg]; \ \ CHECK_ADDR_REG (dst_addr_reg); \ \ /* Compensate for a7 predec/postinc byte size means +-2 nonsense. */ \ if (adjust_p && ((preoff) & 1) && guest_reg == 15) /* a7? */ \ regoff = (preoff) * 2; \ else \ regoff = preoff; \ \ offset_p = (c->guest_reg_status[guest_reg].mapping == MAP_OFFSET); \ if (offset_p) \ regoff += c->guest_reg_status[guest_reg].offset; \ \ /* Compensate for offset memory. */ \ regoff = (int32) SYN68K_TO_US (regoff); \ \ /* Read the value in from memory. */ \ if (regoff == 0) \ i386_ ## op ## size ## _reg_ind (COMMON_ARG_NAMES, src_reg, \ dst_addr_reg); \ else \ i386_ ## op ## size ## _reg_indoff (COMMON_ARG_NAMES, src_reg, regoff, \ dst_addr_reg); \ \ /* Adjust the address register. */ \ if (postoff && adjust_p) \ { \ int32 newoff; \ \ if (((postoff) & 1) && guest_reg == 15) /* a7? Compensate... */ \ newoff = (postoff) * 2; \ else \ newoff = postoff; \ \ if (offset_p) \ { \ /* If we're already offset, adjust our offset some more. */ \ if ((c->guest_reg_status[guest_reg].offset += newoff) == 0) \ c->guest_reg_status[guest_reg].mapping = MAP_NATIVE; \ } \ else \ { \ /* We now become offset from our original value. */ \ c->guest_reg_status[guest_reg].offset = newoff; \ c->guest_reg_status[guest_reg].mapping = MAP_OFFSET; \ } \ } \ \ return 0; \ } #undef OFF #define OFF HOST_OP_REG_MEM(b, and, ind, 0, 0, FALSE) HOST_OP_REG_MEM(w, and, ind, 0, 0, FALSE) HOST_OP_REG_MEM(l, and, ind, 0, 0, FALSE) HOST_OP_REG_MEM(b, and, predec, -1, -1, TRUE) HOST_OP_REG_MEM(w, and, predec, -2, -2, TRUE) HOST_OP_REG_MEM(l, and, predec, -4, -4, TRUE) HOST_OP_REG_MEM(b, and, postinc, 0, 1, TRUE) HOST_OP_REG_MEM(w, and, postinc, 0, 2, TRUE) HOST_OP_REG_MEM(l, and, postinc, 0, 4, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_REG_MEM(b, and, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(w, and, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(l, and, indoff, offset, 0, FALSE) #undef OFF #define OFF HOST_OP_REG_MEM(b, or, ind, 0, 0, FALSE) HOST_OP_REG_MEM(w, or, ind, 0, 0, FALSE) HOST_OP_REG_MEM(l, or, ind, 0, 0, FALSE) HOST_OP_REG_MEM(b, or, predec, -1, -1, TRUE) HOST_OP_REG_MEM(w, or, predec, -2, -2, TRUE) HOST_OP_REG_MEM(l, or, predec, -4, -4, TRUE) HOST_OP_REG_MEM(b, or, postinc, 0, 1, TRUE) HOST_OP_REG_MEM(w, or, postinc, 0, 2, TRUE) HOST_OP_REG_MEM(l, or, postinc, 0, 4, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_REG_MEM(b, or, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(w, or, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(l, or, indoff, offset, 0, FALSE) #undef OFF #define OFF HOST_OP_REG_MEM(b, xor, ind, 0, 0, FALSE) HOST_OP_REG_MEM(w, xor, ind, 0, 0, FALSE) HOST_OP_REG_MEM(l, xor, ind, 0, 0, FALSE) HOST_OP_REG_MEM(b, xor, predec, -1, -1, TRUE) HOST_OP_REG_MEM(w, xor, predec, -2, -2, TRUE) HOST_OP_REG_MEM(l, xor, predec, -4, -4, TRUE) HOST_OP_REG_MEM(b, xor, postinc, 0, 1, TRUE) HOST_OP_REG_MEM(w, xor, postinc, 0, 2, TRUE) HOST_OP_REG_MEM(l, xor, postinc, 0, 4, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_REG_MEM(b, xor, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(w, xor, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(l, xor, indoff, offset, 0, FALSE) #undef OFF #define OFF HOST_OP_REG_MEM(b, add, ind, 0, 0, FALSE) HOST_OP_REG_MEM(b, add, predec, -1, -1, TRUE) HOST_OP_REG_MEM(b, add, postinc, 0, 1, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_REG_MEM(b, add, indoff, offset, 0, FALSE) #undef OFF #define OFF HOST_OP_REG_MEM(b, sub, ind, 0, 0, FALSE) HOST_OP_REG_MEM(b, sub, predec, -1, -1, TRUE) HOST_OP_REG_MEM(b, sub, postinc, 0, 1, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_REG_MEM(b, sub, indoff, offset, 0, FALSE) #undef OFF #define OFF HOST_OP_REG_MEM(b, cmp, ind, 0, 0, FALSE) HOST_OP_REG_MEM(w, cmp, ind, 0, 0, FALSE) HOST_OP_REG_MEM(l, cmp, ind, 0, 0, FALSE) HOST_OP_REG_MEM(b, cmp, predec, -1, -1, TRUE) HOST_OP_REG_MEM(w, cmp, predec, -2, -2, TRUE) HOST_OP_REG_MEM(l, cmp, predec, -4, -4, TRUE) HOST_OP_REG_MEM(b, cmp, postinc, 0, 1, TRUE) HOST_OP_REG_MEM(w, cmp, postinc, 0, 2, TRUE) HOST_OP_REG_MEM(l, cmp, postinc, 0, 4, TRUE) #undef OFF #define OFF int32 offset, HOST_OP_REG_MEM(b, cmp, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(w, cmp, indoff, offset, 0, FALSE) HOST_OP_REG_MEM(l, cmp, indoff, offset, 0, FALSE) int host_cmpmb_postinc_postinc (COMMON_ARGS, int32 reg1, int32 reg2) { host_moveb_postinc_reg (COMMON_ARG_NAMES, reg1, scratch_reg); host_moveb_postinc_reg (COMMON_ARG_NAMES, reg2, scratch_reg + 4); host_unoffset_reg (c, codep, cc_spill_if_changed, c->host_reg_to_guest_reg[reg1]); host_unoffset_reg (c, codep, cc_spill_if_changed, c->host_reg_to_guest_reg[reg2]); i386_cmpb_reg_reg (COMMON_ARG_NAMES, scratch_reg, scratch_reg + 4); return 0; } int host_cmpmw_postinc_postinc (COMMON_ARGS, int32 reg1, int32 reg2) { int32 scratch_reg_2; scratch_reg_2 = host_alloc_reg (c, codep, cc_spill_if_changed, REGSET_ALL & ~((1L << reg1) | (1L << reg2) | (1L << scratch_reg))); if (scratch_reg_2 == NO_REG) return 1; host_movew_postinc_reg (COMMON_ARG_NAMES, reg1, scratch_reg); host_movew_postinc_reg (COMMON_ARG_NAMES, reg2, scratch_reg_2); /* We choose to unoffset the registers _now_, because we'll almost * certainly need the cc bits after the compare and the adds to unoffset * will clobber the i386 cc's. */ /* Add and swap back to back, for Pentium pairability. */ host_unoffset_reg (c, codep, cc_spill_if_changed, c->host_reg_to_guest_reg[reg1]); if (cc_to_compute & (M68K_CCN | M68K_CCC | M68K_CCV)) HOST_SWAP16 (scratch_reg); /* Add and swap back to back, for Pentium pairability. */ host_unoffset_reg (c, codep, cc_spill_if_changed, c->host_reg_to_guest_reg[reg2]); if (cc_to_compute & (M68K_CCN | M68K_CCC | M68K_CCV)) HOST_SWAP16 (scratch_reg_2); /* Finally do the compare. */ i386_cmpw_reg_reg (COMMON_ARG_NAMES, scratch_reg, scratch_reg_2); return 0; } int host_cmpml_postinc_postinc (COMMON_ARGS, int32 reg1, int32 reg2) { int32 scratch_reg_2; scratch_reg_2 = host_alloc_reg (c, codep, cc_spill_if_changed, REGSET_ALL & ~((1L << reg1) | (1L << reg2) | (1L << scratch_reg))); if (scratch_reg_2 == NO_REG) return 1; host_movel_postinc_reg (COMMON_ARG_NAMES, reg1, scratch_reg); host_movel_postinc_reg (COMMON_ARG_NAMES, reg2, scratch_reg_2); /* We choose to unoffset the registers _now_, because we'll almost * certainly need the cc bits after the compare and the adds to unoffset * will clobber the i386 cc's. */ /* Adds back to back for Pentium pairability since bswap isn't pairable. */ host_unoffset_reg (c, codep, cc_spill_if_changed, c->host_reg_to_guest_reg[reg1]); host_unoffset_reg (c, codep, cc_spill_if_changed, c->host_reg_to_guest_reg[reg2]); /* Swap the two temp guys around to native endian byte order. */ if (cc_to_compute & (M68K_CCN | M68K_CCC | M68K_CCV)) { host_swap32 (COMMON_ARG_NAMES, scratch_reg); host_swap32 (COMMON_ARG_NAMES, scratch_reg_2); } /* Finally do the compare. */ i386_cmpl_reg_reg (COMMON_ARG_NAMES, scratch_reg, scratch_reg_2); return 0; } /* We special case this so we can deal with offset registers efficiently. */ #define ADD_SUB_CMPL_IMM_REG(op, v, adj, compare_p) \ int \ host_ ## op ## l_imm_reg (COMMON_ARGS, int32 val, int32 host_reg) \ { \ int guest_reg; \ guest_reg_status_t *r; \ \ guest_reg = c->host_reg_to_guest_reg[host_reg]; \ r = &c->guest_reg_status[guest_reg]; \ \ if (!(cc_to_compute & ~(M68K_CCZ | M68K_CCN))) \ { \ if (r->mapping == MAP_OFFSET) \ { \ val adj r->offset; \ r->mapping = MAP_NATIVE; \ if (val == 0 && !cc_to_compute) \ /*-->*/ return 0; \ } \ \ if (compare_p \ || !(cc_spill_if_changed & ~M68K_CCX) \ || cc_to_compute) \ { \ if (!compare_p && (v) == 1) \ i386_incl_reg (COMMON_ARG_NAMES, host_reg); \ else if (!compare_p && (v) == -1) \ i386_decl_reg (COMMON_ARG_NAMES, host_reg); \ else \ i386_ ## op ## l_imm_reg (COMMON_ARG_NAMES, val, host_reg); \ } \ else \ { \ /* We can add without touching any cc's with leal. */ \ i386_leal_indoff (c, codep, M68K_CC_NONE, M68K_CC_NONE, \ NO_REG, (v), host_reg, host_reg); \ } \ } \ else /* We need the tricky cc bits. */ \ { \ if (r->mapping == MAP_OFFSET) \ { \ int32 offset = r->offset; \ if (offset != 0) \ { \ if (offset == 1) \ i386_incl_reg (COMMON_ARG_NAMES, host_reg); \ else if (offset == -1) \ i386_decl_reg (COMMON_ARG_NAMES, host_reg); \ else \ i386_addl_imm_reg (COMMON_ARG_NAMES, offset, host_reg); \ } \ \ r->mapping = MAP_NATIVE; \ } \ \ i386_ ## op ## l_imm_reg (COMMON_ARG_NAMES, val, host_reg); \ } \ \ return 0; \ } ADD_SUB_CMPL_IMM_REG (add, val, +=, FALSE) ADD_SUB_CMPL_IMM_REG (sub, -val, -=, FALSE) ADD_SUB_CMPL_IMM_REG (cmp, -val, -=, TRUE) int host_bclr_imm_reg (COMMON_ARGS, int32 bitnum, int32 reg) { return host_andl_imm_reg (c, codep, cc_spill_if_changed, M68K_CC_NONE, scratch_reg, ~(1L << (bitnum & 31)), reg); } int host_bset_imm_reg (COMMON_ARGS, int32 bitnum, int32 reg) { return host_orl_imm_reg (c, codep, cc_spill_if_changed, M68K_CC_NONE, scratch_reg, 1L << (bitnum & 31), reg); } int host_bchg_imm_reg (COMMON_ARGS, int32 bitnum, int32 reg) { return host_xorl_imm_reg (c, codep, cc_spill_if_changed, M68K_CC_NONE, scratch_reg, 1L << (bitnum & 31), reg); } int host_btst_imm_reg (COMMON_ARGS, int32 bitnum, int32 reg) { uint32 mask; mask = 1L << (bitnum & 31); switch (c->guest_reg_status[c->host_reg_to_guest_reg[reg]].mapping) { case MAP_NATIVE: break; case MAP_SWAP16: mask = (mask & 0xFFFF0000) | SWAPUW_IFLE (mask); break; case MAP_SWAP32: mask = SWAPUL_IFLE (mask); break; default: abort (); } if (reg < 4 && (mask & 0xFFFF)) { /* Do either "testb $0x4,%al" or "testb $0x8,%ah" (for example). */ if (mask & 0xFF) i386_testb_imm_reg (COMMON_ARG_NAMES, mask, reg); else i386_testb_imm_reg (COMMON_ARG_NAMES, mask >> 8, reg + 4); } else i386_testl_imm_reg (COMMON_ARG_NAMES, mask, reg); return 0; } #define CONDL_BRANCH(name, cc_in, i386_insn, i386_reverse_insn, \ noncached_variant_p, \ two_noncached_cc_p, cc1, njmp1, cc2, njmp2, \ first_test_child) \ int \ host_ ## name (COMMON_ARGS, Block *b) \ { \ unsigned cc_to_spill, cc[2]; \ host_code_t *start_code; \ int reverse_branch_p, cc_diff; \ static const int8 bits_set[32] = \ { \ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, \ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 \ }; \ \ /* Record the start so we can provide accurate backpatch information. */ \ start_code = *codep; \ \ /* First spill any dirty or offset registers. */ \ host_spill_regs (c, codep, cc_spill_if_changed | (cc_in)); \ \ cc[0] = b->child[0]->cc_needed & c->cached_cc & c->dirty_cc; \ cc[1] = b->child[1]->cc_needed & c->cached_cc & c->dirty_cc; \ \ /* We'll prefer branching when we don't need to spill ccs, and we'll \ * prefer branching to a block with children (e.g. avoid rts since \ * it's not likely to be in a loop.) \ */ \ cc_diff = bits_set[cc[0]] - bits_set[cc[1]]; \ reverse_branch_p = (cc_diff < 0 \ || (cc_diff == 0 && b->child[1]->num_parents == 0)); \ \ /* Make sure that the cc bits we need are still alive. */ \ if ((c->cached_cc & (cc_in)) == (cc_in)) \ { \ /* Spill any cc bits that are live and cached, but only those needed \ * if we take the TRUE arm of the branch. We assume here that our \ * SPILL_CC_BITS() routine leaves the cc bits cached in reality, even \ * though it marks them uncached. \ */ \ cc_to_spill = cc[!reverse_branch_p]; \ SPILL_CC_BITS (c, codep, cc_to_spill); \ \ /* Emit a conditional branch. */ \ if (reverse_branch_p) \ i386_reverse_insn (COMMON_ARG_NAMES, 0x12345678); \ else \ i386_insn (COMMON_ARG_NAMES, 0x12345678); \ } \ else if (noncached_variant_p) \ { \ cc_to_spill = ((b->child[0]->cc_needed | b->child[1]->cc_needed \ | (cc_in)) \ & c->cached_cc & c->dirty_cc); \ SPILL_CC_BITS (c, codep, cc_to_spill); \ \ cc_to_compute = M68K_CCZ; \ cc_spill_if_changed = M68K_CC_NONE; \ \ /* We can test up to two cc bits for known values. */ \ if (two_noncached_cc_p) \ { \ Block *child; \ \ i386_cmpb_imm_indoff (COMMON_ARG_NAMES, 0, \ offsetof (CPUState, cc1), REG_EBP); \ njmp1 (COMMON_ARG_NAMES, 0x12345678); \ \ /* Create a backpatch for this conditional branch. */ \ child = b->child[first_test_child]; \ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, \ -4 /* Branch is relative to end of jmp. */ \ + NATIVE_START_BYTE_OFFSET /* skip synth opcode */ \ + (child->m68k_start_address > b->m68k_start_address \ ? CHECK_INTERRUPT_STUB_BYTES : 0), \ child); \ } \ \ /* Check the secondary success condition. */ \ i386_cmpb_imm_indoff (COMMON_ARG_NAMES, 0, \ offsetof (CPUState, cc2), REG_EBP); \ njmp2 (COMMON_ARG_NAMES, 0x12345678); \ \ /* Note that we shouldn't try to save the cc bits output by the cmp \ * we just generated. \ */ \ c->cached_cc = M68K_CC_NONE; \ c->dirty_cc = M68K_CC_NONE; \ \ reverse_branch_p = FALSE; \ } \ else \ return 1; \ \ /* Create a backpatch for this conditional branch. Note that this \ * backpatch is only relative to the start of this native code; \ * the main compile loop will adjust the backpatch to be relative \ * to the block start once our code has been placed. \ */ \ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, \ -4 /* Branch is relative to end of jxx. */ \ + NATIVE_START_BYTE_OFFSET \ + ((b->child[!reverse_branch_p]->m68k_start_address \ > b->m68k_start_address) \ ? CHECK_INTERRUPT_STUB_BYTES : 0), \ b->child[!reverse_branch_p]); \ \ /* Spill any cc bits that are live and cached, but only those needed \ * if we take the FALSE arm of the branch. \ */ \ cc_to_spill = (cc[reverse_branch_p]); \ SPILL_CC_BITS (c, codep, cc_to_spill); \ \ /* Emit a jmp to the failure block. */ \ i386_jmp (COMMON_ARG_NAMES, 0x12345678); \ \ /* Create a backpatch for this unconditional jmp. */ \ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, \ -4 /* Branch is relative to end of jmp. */ \ + NATIVE_START_BYTE_OFFSET /* Skip initial synth opcode.*/ \ + ((b->child[reverse_branch_p]->m68k_start_address \ > b->m68k_start_address) \ ? CHECK_INTERRUPT_STUB_BYTES : 0), \ b->child[reverse_branch_p]); \ return 0; \ } CONDL_BRANCH (bcc, M68K_CCC, i386_jnc, i386_jc, TRUE, FALSE, ccc, i386_jz, ccc, i386_jz, 0) CONDL_BRANCH (bcs, M68K_CCC, i386_jc, i386_jnc, TRUE, FALSE, ccc, i386_jz, ccc, i386_jnz, 0) CONDL_BRANCH (beq, M68K_CCZ, i386_jz, i386_jnz, TRUE, FALSE, ccc, i386_jz, ccnz, i386_jz, 0) CONDL_BRANCH (bne, M68K_CCZ, i386_jnz, i386_jz, TRUE, FALSE, ccc, i386_jz, ccnz, i386_jnz, 0) CONDL_BRANCH (bge, M68K_CCN | M68K_CCV, i386_jge, i386_jl, FALSE, FALSE, ccc, i386_jz, ccnz, i386_jz, 0) CONDL_BRANCH (blt, M68K_CCN | M68K_CCV, i386_jl, i386_jge, FALSE, FALSE, ccc, i386_jz, ccnz, i386_jz, 0) CONDL_BRANCH (bgt, M68K_CCN | M68K_CCV | M68K_CCZ, i386_jnle, i386_jle, FALSE, FALSE, ccc, i386_jz, ccnz, i386_jz, 0) CONDL_BRANCH (ble, M68K_CCN | M68K_CCV | M68K_CCZ, i386_jle, i386_jnle, FALSE, FALSE, ccc, i386_jz, ccnz, i386_jz, 0) CONDL_BRANCH (bhi, M68K_CCC | M68K_CCZ, i386_jnbe, i386_jbe, TRUE, TRUE, ccc, i386_jnz, ccnz, i386_jnz, 0) CONDL_BRANCH (bls, M68K_CCC | M68K_CCZ, i386_jbe, i386_jnbe, TRUE, TRUE, ccc, i386_jnz, ccnz, i386_jz, 1) CONDL_BRANCH (bpl, M68K_CCN, i386_jns, i386_js, TRUE, FALSE, ccc, i386_jz, ccn, i386_jz, 0) CONDL_BRANCH (bmi, M68K_CCN, i386_js, i386_jns, TRUE, FALSE, ccc, i386_jz, ccn, i386_jnz, 0) CONDL_BRANCH (bvc, M68K_CCV, i386_jno, i386_jo, TRUE, FALSE, ccc, i386_jz, ccv, i386_jz, 0) CONDL_BRANCH (bvs, M68K_CCV, i386_jo, i386_jno, TRUE, FALSE, ccc, i386_jz, ccv, i386_jnz, 0) int host_jmp (COMMON_ARGS, Block *b) { unsigned cc_to_spill; host_code_t *start_code; /* Record the start so we can provide accurate backpatch information. */ start_code = *codep; /* Spill any needed cc bits. */ cc_to_spill = (b->child[0]->cc_needed & c->cached_cc & c->dirty_cc); SPILL_CC_BITS (c, codep, cc_to_spill); /* Spill any dirty or offset registers. */ host_spill_regs (c, codep, M68K_CC_NONE); /* Emit a jmp to the target block. */ i386_jmp (COMMON_ARG_NAMES, 0x12345678); /* Create a backpatch for this jmp. */ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of jmp. */ + NATIVE_START_BYTE_OFFSET /* Skip initial synth opcode.*/ + ((b->child[0]->m68k_start_address > b->m68k_start_address) ? CHECK_INTERRUPT_STUB_BYTES : 0), b->child[0]); return 0; } int host_dbra (COMMON_ARGS, int32 guest_reg, Block *b) { unsigned cc_to_spill; host_code_t *start_code; int32 host_reg; /* Record the start so we can provide accurate backpatch information. */ start_code = *codep; /* Spill any cc bits that are live and cached. */ cc_to_spill = (c->cached_cc & c->dirty_cc); SPILL_CC_BITS (c, codep, cc_to_spill); /* Canonicalize all registers. If the loop register is cached, * pretend like it's dirty so it gets canonicalized. It will be * decremented below, so we won't be doing any extra work or * anything. */ host_reg = c->guest_reg_status[guest_reg].host_reg; if (host_reg != NO_REG) c->guest_reg_status[guest_reg].dirty_without_offset_p = TRUE; make_dirty_regs_native (c, codep, M68K_CC_NONE); /* Decrement the loop register, whether it's cached or in memory. */ if (host_reg == NO_REG) { /* It's in memory. We've got to use subw since we need the carry. */ i386_subw_imm_indoff (c, codep, M68K_CC_NONE, M68K_CCC, NO_REG, 1, offsetof (CPUState, regs[guest_reg].uw.n), REG_EBP); } else { /* It's in a reg. We've got to use subw since we need the carry. * We already marked the reg dirty, above. */ i386_subw_imm_reg (c, codep, M68K_CC_NONE, M68K_CCC, NO_REG, 1, host_reg); } /* Spill all registers back to memory. */ host_spill_regs (c, codep, M68K_CCC); /* Make sure that we've still got the carry from the decw. Of course, * the real m68k doesn't have this carry value, so remember that. */ if (!(c->cached_cc & M68K_CCC)) return 1; c->cached_cc = M68K_CC_NONE; /* Emit a conditional branch. */ i386_jnc (COMMON_ARG_NAMES, 0x12345678); /* Create a backpatch for this conditional branch. Note that this * backpatch is only relative to the start of this native code; * the main compile loop will adjust the backpatch to be relative * to the block start once our code has been placed. */ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of jxx. */ + NATIVE_START_BYTE_OFFSET, /* Skip initial synth opcode. */ b->child[1]); /* Emit a jmp to the failure block. */ i386_jmp (COMMON_ARG_NAMES, 0x12345678); /* Create a backpatch for this unconditional jmp. */ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of jmp. */ + NATIVE_START_BYTE_OFFSET /* Skip initial synth opcode.*/ + ((b->child[0]->m68k_start_address > b->m68k_start_address) ? CHECK_INTERRUPT_STUB_BYTES : 0), b->child[0]); return 0; } int host_swap (COMMON_ARGS, int32 reg) { return i386_rorl_imm_reg (c, codep, cc_spill_if_changed, M68K_CC_NONE, NO_REG, 16, reg); } int host_extbw (COMMON_ARGS, int32 reg) { /* movb %al,%ah * sarb $7,%ah * testb %al,%al (optional) */ i386_movb_reg_reg (COMMON_ARG_NAMES, reg, reg + 4); i386_sarb_imm_reg (COMMON_ARG_NAMES, 7, reg + 4); if (cc_to_compute) i386_testb_reg_reg (COMMON_ARG_NAMES, reg, reg); return 0; } int host_extwl (COMMON_ARGS, int32 reg) { i386_movswl_reg_reg (COMMON_ARG_NAMES, reg, reg); if (cc_to_compute) i386_testl_reg_reg (COMMON_ARG_NAMES, reg, reg); return 0; } int host_extbl (COMMON_ARGS, int32 reg) { if (cc_to_compute) i386_testb_reg_reg (COMMON_ARG_NAMES, reg, reg); return i386_movsbl_reg_reg (COMMON_ARG_NAMES, reg, reg); } int host_unlk (COMMON_ARGS, int32 reg, int32 a7_reg) { if (reg == a7_reg) /* unlk a7 wedges the real 68040...heh */ return 1; if (host_leal_indoff_areg (COMMON_ARG_NAMES, 4, reg, a7_reg)) return 1; if (host_movel_ind_reg (COMMON_ARG_NAMES, reg, reg)) return 1; return 0; } int host_link (COMMON_ARGS, int32 offset, int32 reg, int32 a7_reg) { guest_reg_status_t *ga7; ga7 = &c->guest_reg_status[15]; /* Push the old value on the stack, and move a7 into the frame pointer. */ host_subl_imm_reg (COMMON_ARG_NAMES, 4, a7_reg); assert (ga7->mapping == MAP_NATIVE); i386_movl_reg_indoff (COMMON_ARG_NAMES, reg, (int32) SYN68K_TO_US (0), a7_reg); i386_movl_reg_reg (COMMON_ARG_NAMES, a7_reg, reg); /* Add the specified offset to a7. */ ga7->mapping = MAP_OFFSET; ga7->offset = offset; return 0; } int host_moveml_reg_predec (COMMON_ARGS, int32 host_addr_reg, int32 reg_mask) { int i, guest_addr_reg; guest_reg_status_t *g; int32 off; guest_addr_reg = c->host_reg_to_guest_reg[host_addr_reg]; g = &c->guest_reg_status[guest_addr_reg]; if (g->mapping == MAP_OFFSET) off = g->offset; else off = 0; /* Compensate for offset memory. */ off = (int32) SYN68K_TO_US (off); for (i = 15; i >= 0; i--) if (reg_mask & (0x8000 >> i)) { int reg_to_move, host_reg; g = &c->guest_reg_status[i]; host_reg = g->host_reg; if (host_reg == host_addr_reg) { /* Here we move the real value of the register minus 4; * this behavior is correct for the 68020 and up. */ i386_leal_indoff (COMMON_ARG_NAMES, ((g->mapping == MAP_OFFSET) ? g->offset : 0) - 4, host_addr_reg, scratch_reg); host_swap32 (COMMON_ARG_NAMES, scratch_reg); reg_to_move = scratch_reg; } else if (host_reg == NO_REG) { i386_movl_indoff_reg (COMMON_ARG_NAMES, offsetof (CPUState, regs[i].ul.n), REG_EBP, scratch_reg); host_swap32 (COMMON_ARG_NAMES, scratch_reg); reg_to_move = scratch_reg; } else { switch (g->mapping) { case MAP_OFFSET: host_unoffset_reg (c, codep, cc_spill_if_changed, i); /* Fall through to MAP_NATIVE case... */ case MAP_NATIVE: host_swap32 (COMMON_ARG_NAMES, host_reg); g->mapping = MAP_SWAP32; break; case MAP_SWAP16: host_swap16_to_32 (COMMON_ARG_NAMES, host_reg); g->mapping = MAP_SWAP32; break; case MAP_SWAP32: break; default: abort (); } reg_to_move = host_reg; } off -= 4; if (off != 0) i386_movl_reg_indoff (COMMON_ARG_NAMES, reg_to_move, off, host_addr_reg); else i386_movl_reg_ind (COMMON_ARG_NAMES, reg_to_move, host_addr_reg); } g = &c->guest_reg_status[guest_addr_reg]; /* Undo fake memory offset. */ off = US_TO_SYN68K (off); if (off == 0) g->mapping = MAP_NATIVE; else { g->mapping = MAP_OFFSET; g->offset = off; } return 0; } int host_moveml_postinc_reg (COMMON_ARGS, int32 host_addr_reg, int32 reg_mask) { int i, guest_addr_reg, off; guest_reg_status_t *g; host_reg_mask_t locked_host_regs; guest_addr_reg = c->host_reg_to_guest_reg[host_addr_reg]; locked_host_regs = ((~ALLOCATABLE_REG_MASK) | (1L << host_addr_reg) | (1L << scratch_reg)); g = &c->guest_reg_status[guest_addr_reg]; if (g->mapping == MAP_OFFSET) off = g->offset; else off = 0; /* Compensate for offset memory. */ off = (int32) SYN68K_TO_US (off); for (i = 0; i < 16; i++) if (reg_mask & (1L << i)) { int host_reg; if (i == guest_addr_reg) { /* Don't load up our address register. */ off += 4; continue; } g = &c->guest_reg_status[i]; host_reg = g->host_reg; if (host_reg == NO_REG) { host_reg = host_alloc_reg (c, codep, cc_spill_if_changed, ((i < 8) ? (REGSET_BYTE & ~locked_host_regs) : ~locked_host_regs)); if (host_reg == NO_REG) host_reg = scratch_reg; else { locked_host_regs |= (1L << host_reg); c->host_reg_to_guest_reg[host_reg] = i; g->host_reg = host_reg; } } if (off == 0) i386_movl_ind_reg (COMMON_ARG_NAMES, host_addr_reg, host_reg); else i386_movl_indoff_reg (COMMON_ARG_NAMES, off, host_addr_reg, host_reg); if (host_reg == scratch_reg) { host_swap32 (COMMON_ARG_NAMES, scratch_reg); i386_movl_reg_indoff (COMMON_ARG_NAMES, scratch_reg, offsetof (CPUState, regs[i].ul.n), REG_EBP); } else { g->mapping = MAP_SWAP32; g->dirty_without_offset_p = TRUE; } off += 4; } g = &c->guest_reg_status[guest_addr_reg]; /* Undo fake memory offset. */ off = US_TO_SYN68K (off); if (off == 0) g->mapping = MAP_NATIVE; else { g->mapping = MAP_OFFSET; g->offset = off; } return 0; } int host_pea_indoff (COMMON_ARGS, int32 offset, int32 host_reg, int32 a7_reg) { int guest_reg; guest_reg_status_t *g; guest_reg = c->host_reg_to_guest_reg[host_reg]; g = &c->guest_reg_status[guest_reg]; if (g->mapping == MAP_OFFSET) offset += g->offset; if (offset == 0) i386_movl_reg_reg (COMMON_ARG_NAMES, host_reg, scratch_reg); else i386_leal_indoff (COMMON_ARG_NAMES, offset, host_reg, scratch_reg); host_swap32 (COMMON_ARG_NAMES, scratch_reg); return host_movel_reg_predec (COMMON_ARG_NAMES, scratch_reg, a7_reg); } int host_leal_indoff_areg (COMMON_ARGS, int32 offset, int32 base_reg, int32 dst_reg) { guest_reg_status_t *g; g = &c->guest_reg_status[c->host_reg_to_guest_reg[base_reg]]; if (g->mapping == MAP_OFFSET) offset += g->offset; if (offset == 0) i386_movl_reg_reg (COMMON_ARG_NAMES, base_reg, dst_reg); else i386_leal_indoff (COMMON_ARG_NAMES, offset, base_reg, dst_reg); return 0; } int host_rts (COMMON_ARGS, Block *b, int32 a7_reg) { int jsr_sp_reg; host_code_t *jnz_end_addr, *start_code; /* Keep track of original codep so we can backpatch, below. */ start_code = *codep; /* Spill all dirty cc bits. */ host_spill_cc_bits (c, codep, M68K_CC_ALL); cc_spill_if_changed = M68K_CC_NONE; /* Grab the return address. */ host_movel_postinc_reg (COMMON_ARG_NAMES, a7_reg, scratch_reg); /* Spill all dirty regs. */ host_spill_regs (c, codep, M68K_CC_NONE); /* Allocate a register to hold the jsr stack pointer. */ jsr_sp_reg = host_alloc_reg (c, codep, M68K_CC_NONE, ALLOCATABLE_REG_MASK & ~(1L << scratch_reg)); if (jsr_sp_reg == NO_REG) return 1; /* Grab the jsr stack pointer. */ i386_movl_indoff_reg (COMMON_ARG_NAMES, offsetof (CPUState, jsr_stack_byte_index), REG_EBP, jsr_sp_reg); /* Compare the return address to the tag at the top of the jsr * stack cache. The tags are kept in big endian order, so we don't * need to byte swap here. */ i386_cmpl_reg_indoff (COMMON_ARG_NAMES, scratch_reg, (int32)&cpu_state.jsr_stack[0].tag, jsr_sp_reg); /* If we don't get a hit, skip this code. */ i386_jnz (COMMON_ARG_NAMES, 5); /* One byte offset patched below. */ jnz_end_addr = *codep; /* Fetch the code pointer. Clobber scratch_reg. */ i386_movl_indoff_reg (COMMON_ARG_NAMES, (int32)&cpu_state.jsr_stack[0].code, jsr_sp_reg, scratch_reg); /* Pop the jsr stack and jump to the target address. */ i386_addl_imm_reg (COMMON_ARG_NAMES, sizeof (jsr_stack_elt_t), jsr_sp_reg); i386_addl_imm_reg (COMMON_ARG_NAMES, NATIVE_START_BYTE_OFFSET, scratch_reg); i386_andl_imm_reg (COMMON_ARG_NAMES, (sizeof cpu_state.jsr_stack) - 1, jsr_sp_reg); i386_movl_reg_indoff (COMMON_ARG_NAMES, jsr_sp_reg, offsetof (CPUState, jsr_stack_byte_index), REG_EBP); i386_jmp_reg (COMMON_ARG_NAMES, scratch_reg); /* Patch up the jnz to jump to here. */ ((int8 *)jnz_end_addr)[-1] = *codep - jnz_end_addr; /* Call hash_lookup_code_and_create_if_needed(). */ host_swap32 (COMMON_ARG_NAMES, scratch_reg); i386_pushl (COMMON_ARG_NAMES, scratch_reg); i386_call_abs (COMMON_ARG_NAMES, 0x1234567A); /* hash_lookup_code_and... */ /* Note a backpatch for the call, since it's a relative displacement. */ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of call. */ + (int32)hash_lookup_code_and_create_if_needed, NULL); /* Jump to the native code entry point for the code returned by * hash_lookup_code_and_create_if_needed (). */ i386_addl_imm_reg (COMMON_ARG_NAMES, NATIVE_START_BYTE_OFFSET, REG_EAX); i386_addl_imm_reg (COMMON_ARG_NAMES, 4, REG_ESP); /* Pop off call arg. */ i386_jmp_reg (COMMON_ARG_NAMES, REG_EAX); return 0; } static void host_recompile_jsr (Block *b, host_code_t *code_start, int32 target_addr, int32 rts_addr, int32 dynamic_address_reg) { int old_immortal, old_target_immortal, old_rts_immortal; int jsr_sp_reg; Block *target, *rts; cache_info_t empty, *c; host_code_t *code, **codep; int cc_spill_if_changed, cc_to_compute, scratch_reg; /* Set up some bogus COMMON_ARGS for the native code generation * we're about to do. */ empty = empty_cache_info; c = ∅ code = code_start; codep = &code; cc_spill_if_changed = cc_to_compute = M68K_CC_NONE; scratch_reg = NO_REG; /* Make the jsr block temporarily immortal so it doesn't accidentally * get trashed while compiling the target block. */ old_immortal = b->immortal; b->immortal = TRUE; /* Actually create the target code and then grab the block. */ hash_lookup_code_and_create_if_needed (target_addr); target = hash_lookup (target_addr); assert (target != NULL); old_target_immortal = target->immortal; target->immortal = TRUE; /* Actually create the rts addr code and then grab the block. */ hash_lookup_code_and_create_if_needed (rts_addr); rts = hash_lookup (rts_addr); assert (rts != NULL); old_rts_immortal = rts->immortal; rts->immortal = TRUE; /* Tie the blocks together. */ b->num_children = 2; b->child[0] = target; b->child[1] = rts; block_add_parent (target, b); block_add_parent (rts, b); /* Replace all of the recompile gunk with some code that adjusts the * jsr stack and then jumps to the target address. We can use any * i386 registers we want here as long as we don't clobber * dynamic_address_reg. */ jsr_sp_reg = (dynamic_address_reg == REG_EAX) ? REG_EBX : REG_EAX; /* Grab the jsr stack pointer. */ i386_movl_indoff_reg (COMMON_ARG_NAMES, offsetof (CPUState, jsr_stack_byte_index), REG_EBP, jsr_sp_reg); /* Push the return code onto the jsr stack. */ i386_subl_imm_reg (COMMON_ARG_NAMES, sizeof (jsr_stack_elt_t), jsr_sp_reg); i386_andl_imm_reg (COMMON_ARG_NAMES, (sizeof cpu_state.jsr_stack) - 1, jsr_sp_reg); i386_movl_reg_indoff (COMMON_ARG_NAMES, jsr_sp_reg, offsetof (CPUState, jsr_stack_byte_index), REG_EBP); i386_movl_imm_indoff (COMMON_ARG_NAMES, SWAPUL (rts_addr), (int32)&cpu_state.jsr_stack[0].tag, jsr_sp_reg); i386_movl_imm_indoff (COMMON_ARG_NAMES, (int32)rts->compiled_code, (int32)&cpu_state.jsr_stack[0].code, jsr_sp_reg); if (dynamic_address_reg != NO_REG) { cc_to_compute = M68K_CCZ; i386_cmpl_imm_reg (COMMON_ARG_NAMES, target_addr, dynamic_address_reg); cc_to_compute = M68K_CC_NONE; i386_jz (COMMON_ARG_NAMES, 0x1234567D); *(int32 *)(code - 4) = (((char *)target->compiled_code + NATIVE_START_BYTE_OFFSET) - (char *)code); i386_pushl (COMMON_ARG_NAMES, dynamic_address_reg); /* Call hash_lookup_code_and_create_if_needed(). */ i386_call_abs (COMMON_ARG_NAMES, 0x1234567A); *(int32 *)(code - 4) = ((char *)hash_lookup_code_and_create_if_needed - (char *)code); /* Jump to the native code entry point for the code returned by * hash_lookup_code_and_create_if_needed (). */ i386_addl_imm_reg (COMMON_ARG_NAMES, NATIVE_START_BYTE_OFFSET, REG_EAX); i386_addl_imm_reg (COMMON_ARG_NAMES, 4, REG_ESP); /* Pop off call arg. */ i386_jmp_reg (COMMON_ARG_NAMES, REG_EAX); } else { /* jmp to the target address. */ i386_jmp (COMMON_ARG_NAMES, 0x1234567D); *(int32 *)(code - 4) = (((char *)target->compiled_code + NATIVE_START_BYTE_OFFSET) - (char *)code); } b->immortal = old_immortal; rts->immortal = old_rts_immortal; target->immortal = old_target_immortal; } static int host_jsr_abs_common (COMMON_ARGS, int32 a7_reg, int32 target_addr, Block *b, int32 return_addr, int jsr_d16_base_reg) { host_code_t *orig_code, *replaced_code, *end_call; int temp_reg; orig_code = *codep; /* Spill all dirty cc bits. */ host_spill_cc_bits (c, codep, M68K_CC_ALL); cc_spill_if_changed = M68K_CC_NONE; /* Push the m68k return address on the stack. */ host_movel_imm_predec (COMMON_ARG_NAMES, return_addr, a7_reg); if (jsr_d16_base_reg != NO_REG) { if (host_leal_indoff_areg (COMMON_ARG_NAMES, target_addr, jsr_d16_base_reg, scratch_reg)) return 1; } /* Spill all registers back to their memory homes. */ host_spill_regs (c, codep, M68K_CC_NONE); /* NOTE!!!! All code from this point on is about to smash itself * with a faster jsr that knows where its target block is. We do it * this way so we can lazily compile through jsrs. */ replaced_code = *codep; /* Save the scratch register. */ if (scratch_reg != NO_REG) i386_pushl (COMMON_ARG_NAMES, scratch_reg); /* Push call args on the stack. If you add more args or delete any, * be sure to adjust ESP differently, below, to pop off the args. */ i386_pushl_imm (COMMON_ARG_NAMES, scratch_reg); i386_pushl_imm (COMMON_ARG_NAMES, return_addr); if (jsr_d16_base_reg != NO_REG) i386_pushl (COMMON_ARG_NAMES, scratch_reg); else i386_pushl_imm (COMMON_ARG_NAMES, target_addr); i386_call_abs (COMMON_ARG_NAMES, 0); /* Push current PC on the stack. */ end_call = *codep; temp_reg = (scratch_reg == REG_EAX) ? REG_EBX : REG_EAX; i386_popl (COMMON_ARG_NAMES, temp_reg); i386_subl_imm_reg (COMMON_ARG_NAMES, end_call - replaced_code, temp_reg); i386_pushl (COMMON_ARG_NAMES, temp_reg); i386_pushl_imm (COMMON_ARG_NAMES, (int32)b); /* Add some more dead space so there will be room for this code * to be overwritten. */ memset (*codep, 0x90 /* NOP */, 48); *codep += 48; /* Call a routine that will rewrite this jsr into a direct one. */ i386_call_abs (COMMON_ARG_NAMES, 0x1234567C); /* Note a backpatch for the call, since it's a relative displacement. */ backpatch_add (b, (*codep - orig_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of call. */ + (int32)host_recompile_jsr, NULL); /* Pop off all call args. */ i386_addl_imm_reg (COMMON_ARG_NAMES, 5 * 4, REG_ESP); /* Restore the scratch reg. */ if (scratch_reg != NO_REG) i386_popl (COMMON_ARG_NAMES, scratch_reg); /* Jump back to the beginning and do the fast jsr this time * (and every subsequent time). */ i386_jmp (COMMON_ARG_NAMES, 5); ((int8 *)*codep)[-1] = replaced_code - *codep; return 0; } int host_jsr_abs (COMMON_ARGS, int32 a7_reg, int32 target_addr, Block *b, uint16 *m68k_code) { int32 return_addr; /* Compute the real return address. */ switch (SWAPUW (*m68k_code) & 63) { case 0x38: /* absw */ return_addr = (syn68k_addr_t) (m68k_code + 2); break; case 0x39: /* absl */ return_addr = (syn68k_addr_t) (m68k_code + 3); break; default: return_addr = 0; /* Avoid compiler warnings. */ abort (); break; } return host_jsr_abs_common (COMMON_ARG_NAMES, a7_reg, target_addr, b, US_TO_SYN68K (return_addr), NO_REG); } int host_jsr_pcd16 (COMMON_ARGS, int32 a7_reg, Block *b, uint16 *m68k_code) { /* Map this to a jsr_abs. */ return host_jsr_abs_common (COMMON_ARG_NAMES, a7_reg, (int32) (US_TO_SYN68K (m68k_code) + 2 + SWAPSW (m68k_code[1])), b, US_TO_SYN68K (m68k_code + 2), NO_REG); } int host_jsr_d16 (COMMON_ARGS, int32 a7_reg, int32 base_addr_reg, Block *b, uint16 *m68k_code) { return host_jsr_abs_common (COMMON_ARG_NAMES, a7_reg, SWAPSW (m68k_code[1]), b, US_TO_SYN68K (m68k_code + 2), base_addr_reg); } int host_bsr (COMMON_ARGS, int32 a7_reg, Block *b, uint16 *m68k_code) { int32 addr, return_addr; /* Compute the bsr destination address. */ switch (((const uint8 *)m68k_code)[1]) { case 0x00: addr = (int32)(m68k_code + 1) + SWAPSW (m68k_code[1]); return_addr = (int32) (m68k_code + 2); break; case 0xFF: addr = (int32)(m68k_code + 1) + READSL (US_TO_SYN68K (m68k_code + 1)); return_addr = (int32) (m68k_code + 3); break; default: addr = (int32)(m68k_code + 1) + ((const int8 *)m68k_code)[1]; return_addr = (int32) (m68k_code + 1); break; } return host_jsr_abs_common (COMMON_ARG_NAMES, a7_reg, US_TO_SYN68K (addr), b, US_TO_SYN68K (return_addr), NO_REG); } /* Divides %eax by scratch_reg, and stores the quotient in * %ax and the remainder in the high 16 bits of %eax, unless there * is overflow in which case no action is taken. Division by zero * causes a trap. */ int host_divsw (COMMON_ARGS, uint16 *m68k_addr, uint16 *next_addr, Block *b, int32 might_overflow_i386_p) { host_code_t *overflow_branch_end, *jmp_end, *br_end_1, *br_end_2; int real_cc_to_compute; host_code_t *start_code; /* Record the start so we can provide accurate backpatch information. */ start_code = *codep; real_cc_to_compute = cc_to_compute; cc_to_compute = M68K_CC_NONE; assert (scratch_reg != -1 && scratch_reg != REG_EDX); /* Pick up %edx as another scratch register. */ if (host_alloc_reg (c, codep, cc_spill_if_changed, 1L << REG_EDX) != REG_EDX) return 3; /* Check for division by zero. */ if (m68k_addr != NULL) { host_code_t *divzero_branch_end; cache_info_t save; save = *c; i386_testl_reg_reg (COMMON_ARG_NAMES, scratch_reg, scratch_reg); i386_jnz (COMMON_ARG_NAMES, 5); divzero_branch_end = *codep; /* Ack! Division by zero! */ host_spill_cc_bits (c, codep, M68K_CCN | M68K_CCV | M68K_CCX | M68K_CCZ); i386_movb_imm_indoff (COMMON_ARG_NAMES, 0, offsetof (CPUState, ccc), REG_EBP); host_spill_regs (c, codep, M68K_CC_NONE); i386_pushl_imm (COMMON_ARG_NAMES, US_TO_SYN68K (m68k_addr)); i386_pushl_imm (COMMON_ARG_NAMES, US_TO_SYN68K (next_addr)); i386_pushl_imm (COMMON_ARG_NAMES, 5); /* Division by zero exception. */ /* Trap. */ i386_call_abs (COMMON_ARG_NAMES, 0x1234567A); /* trap_direct. */ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of call. */ + (int32)trap_direct, NULL); /* Jump to the specified address. */ i386_pushl (COMMON_ARG_NAMES, REG_EAX); i386_call_abs (COMMON_ARG_NAMES, 0x1234567A); /* hash_lookup_code... */ backpatch_add (b, (*codep - start_code - 4) * 8, 32, TRUE, -4 /* Branch is relative to end of call. */ + (int32)hash_lookup_code_and_create_if_needed, NULL); /* Jump to the native code entry point for the code returned by * hash_lookup_code_and_create_if_needed (). */ i386_addl_imm_reg (COMMON_ARG_NAMES, NATIVE_START_BYTE_OFFSET, REG_EAX); i386_addl_imm_reg (COMMON_ARG_NAMES, 4 * 4, REG_ESP); /* Pop off args. */ i386_jmp_reg (COMMON_ARG_NAMES, REG_EAX); /* Here's where we go if there was no problem. */ divzero_branch_end[-1] = *codep - divzero_branch_end; *c = save; } /* Save the dividend in case we overflow. */ i386_pushl (COMMON_ARG_NAMES, REG_EAX); /* The i386 cannot handle 0x80000000 / -1 without crashing, so test for * that case and signal overflow. */ if (might_overflow_i386_p) { i386_cmpl_imm_reg (COMMON_ARG_NAMES, -1, scratch_reg); i386_jnz (COMMON_ARG_NAMES, 5); br_end_1 = *codep; i386_cmpl_imm_reg (COMMON_ARG_NAMES, 0x80000000, REG_EAX); i386_jz (COMMON_ARG_NAMES, 5); br_end_2 = *codep; br_end_1[-1] = *codep - br_end_1; } else br_end_2 = NULL; /* Do the divide. */ i386_cltd (COMMON_ARG_NAMES); i386_idivl (COMMON_ARG_NAMES, scratch_reg); /* Implicitly uses %eax */ if (br_end_2 != NULL) br_end_2[-1] = *codep - br_end_2; /* The result of the divide is now in %eax:%edx. First we have to * check for overflow, since the m68k leaves the operand unaffected * if there is overflow. */ i386_addl_imm_reg (COMMON_ARG_NAMES, 32768, REG_EAX); i386_testl_imm_reg (COMMON_ARG_NAMES, 0xFFFF0000, REG_EAX); i386_jz (COMMON_ARG_NAMES, 5); /* Filled in below to skip overflow code. */ overflow_branch_end = *codep; /* Handle the overflow. */ if (real_cc_to_compute & M68K_CC_CNVZ) { cc_to_compute = real_cc_to_compute; /* N and Z are technically undefined here, but they seem to actually * be set based on the result on the 68040. */ if (real_cc_to_compute & (M68K_CCC | M68K_CCN | M68K_CCZ)) { i386_testl_reg_reg (COMMON_ARG_NAMES, REG_EAX, REG_EAX); i386_lahf (COMMON_ARG_NAMES); } /* Set the V flag, if so requested. */ if (real_cc_to_compute & M68K_CCV) { i386_movl_imm_reg (COMMON_ARG_NAMES, 0x80000000, REG_EDX); i386_addl_reg_reg (COMMON_ARG_NAMES, REG_EDX, REG_EDX); } if (real_cc_to_compute & (M68K_CCC | M68K_CCN | M68K_CCZ)) i386_sahf (COMMON_ARG_NAMES); cc_to_compute = M68K_CC_NONE; } /* Restore the original dividend. */ i386_popl (COMMON_ARG_NAMES, REG_EAX); i386_jmp (COMMON_ARG_NAMES, 5); jmp_end = *codep; /* Backpatch the conditional branch. */ overflow_branch_end[-1] = *codep - overflow_branch_end; /* Now we place the low-order 16 bits of %edx into the high-order 16 * bits of %eax. */ i386_subl_imm_reg (COMMON_ARG_NAMES, 32768, REG_EAX); i386_rorl_imm_reg (COMMON_ARG_NAMES, 16, REG_EAX); i386_movw_reg_reg (COMMON_ARG_NAMES, REG_DX, REG_AX); i386_rorl_imm_reg (COMMON_ARG_NAMES, 16, REG_EAX); /* Pop off the saved overflow value (we didn't need it). */ i386_addl_imm_reg (COMMON_ARG_NAMES, 4, REG_ESP); /* Set the CC bits appropriately, if so desired. */ if (real_cc_to_compute) { cc_to_compute = real_cc_to_compute; i386_testw_reg_reg (COMMON_ARG_NAMES, REG_AX, REG_AX); } /* Backpatch the jump. */ jmp_end[-1] = *codep - jmp_end; c->cached_cc |= real_cc_to_compute; c->dirty_cc |= real_cc_to_compute; return 0; } int host_divsw_imm_reg (COMMON_ARGS, int32 val) { if (val == 0) return 1; i386_movl_imm_reg (COMMON_ARG_NAMES, val, scratch_reg); return host_divsw (COMMON_ARG_NAMES, NULL, NULL, NULL, val == -1); } int host_negb_abs (COMMON_ARGS, int32 dst_addr) { return i386_negb_abs(COMMON_ARG_NAMES, (int32) SYN68K_TO_US (dst_addr)); } #define HOST_OP_IMM_ABS(size, op, x86_op) \ int \ host_ ## op ## size ## _imm_abs (COMMON_ARGS, int32 src, int32 dst_addr) \ { \ return i386_ ## x86_op ## size ## _imm_abs (COMMON_ARG_NAMES, src, \ ((int32) \ SYN68K_TO_US (dst_addr))); \ } HOST_OP_IMM_ABS(b, add, add) HOST_OP_IMM_ABS(b, sub, sub) #define HOST_OP_REG_ABS(size, op, x86_op) \ int \ host_ ## op ## size ## _reg_abs (COMMON_ARGS, int32 src, int32 dst_addr) \ { \ return i386_ ## x86_op ## size ## _reg_abs (COMMON_ARG_NAMES, src, \ ((int32) \ SYN68K_TO_US (dst_addr))); \ } HOST_OP_REG_ABS(b, and, and) HOST_OP_REG_ABS(w, and, and) HOST_OP_REG_ABS(l, and, and) HOST_OP_REG_ABS(b, or, or) HOST_OP_REG_ABS(w, or, or) HOST_OP_REG_ABS(l, or, or) HOST_OP_REG_ABS(b, xor, xor) HOST_OP_REG_ABS(w, xor, xor) HOST_OP_REG_ABS(l, xor, xor) HOST_OP_REG_ABS(b, cmp, cmp) HOST_OP_REG_ABS(w, cmp, cmp) HOST_OP_REG_ABS(l, cmp, cmp) HOST_OP_REG_ABS(b, add, add) HOST_OP_REG_ABS(b, sub, sub) HOST_OP_REG_ABS(b, move, mov) HOST_OP_REG_ABS(w, move, mov) HOST_OP_REG_ABS(l, move, mov) #define HOST_OP_ABS_REG(size, op, x86_op) \ int \ host_ ## op ## size ## _abs_reg (COMMON_ARGS, int32 src_addr, int32 dst) \ { \ return i386_ ## x86_op ## size ## _abs_reg (COMMON_ARG_NAMES, \ ((int32) \ SYN68K_TO_US (src_addr)), \ dst); \ } HOST_OP_ABS_REG(b, and, and) HOST_OP_ABS_REG(w, and, and) HOST_OP_ABS_REG(l, and, and) HOST_OP_ABS_REG(b, or, or) HOST_OP_ABS_REG(w, or, or) HOST_OP_ABS_REG(l, or, or) HOST_OP_ABS_REG(b, xor, xor) HOST_OP_ABS_REG(w, xor, xor) HOST_OP_ABS_REG(l, xor, xor) HOST_OP_ABS_REG(b, cmp, cmp) HOST_OP_ABS_REG(w, cmp, cmp) HOST_OP_ABS_REG(l, cmp, cmp) HOST_OP_ABS_REG(b, add, add) HOST_OP_ABS_REG(b, sub, sub) HOST_OP_ABS_REG(b, move, mov) HOST_OP_ABS_REG(w, move, mov) HOST_OP_ABS_REG(l, move, mov) #define HOST_OPB_IMM_ABS(op, x86_op) \ int \ host_ ## op ## b_imm_abs (COMMON_ARGS, int32 val, int32 addr) \ { \ return i386_ ## x86_op ## b_imm_abs (COMMON_ARG_NAMES, \ val, \ ((int32) \ SYN68K_TO_US (addr))); \ } HOST_OPB_IMM_ABS(and, and) HOST_OPB_IMM_ABS(or , or ) HOST_OPB_IMM_ABS(xor, xor) HOST_OPB_IMM_ABS(cmp, cmp) #endif /* GENERATE_NATIVE_CODE */