summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpu/mips/mdrc64.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/cpu/mips/mdrc64.c')
-rw-r--r--src/emu/cpu/mips/mdrc64.c5731
1 files changed, 5731 insertions, 0 deletions
diff --git a/src/emu/cpu/mips/mdrc64.c b/src/emu/cpu/mips/mdrc64.c
new file mode 100644
index 00000000000..c2dca50e074
--- /dev/null
+++ b/src/emu/cpu/mips/mdrc64.c
@@ -0,0 +1,5731 @@
+/***************************************************************************
+
+ mdrc64.c
+
+ x64 MIPS III recompiler.
+
+ Copyright (c) 2007, Aaron Giles
+ Released for general use under the MAME license
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+****************************************************************************
+
+ Conventions/rules:
+
+ * Instructions are grouped into sequences by mips3fe; each
+ sequence of instructions is guaranteed to not contain any
+ branches to any instructions within the sequence apart from
+ the first.
+
+ * Because of the above rule, assumptions are made during the
+ execution of a sequence in order to optimize the generated
+ code.
+
+ * Because some state may be live and not flushed to memory at
+ the time an exception is generated, all exception-generating
+ instructions must jump to stub code that is appended to each
+ group of instructions which cleans up live registers before
+ running the exception code.
+
+ * Page faults are trickier because they are detected in a
+ subroutine. In order to handle this, special markers are added
+ to the code consisting of one qword EXCEPTION_ADDRESS_COOKIE
+ followed by the return address of the call. When a page fault
+ needs to be generated, the common code scans forward from
+ the return address for this cookie/address pair and jumps to
+ the code immediately following.
+
+ * Convention: jumps to non-constant addresses have their target
+ PC stored at [sp+SPOFFS_NEXTPC]
+
+****************************************************************************
+
+ Future improvements:
+ * problem:
+ lui r3,$b000
+ lw r3,$100(r3)
+ if exception happens, then r3 is trashed
+
+ * register allocator can use non-volatile registers if the
+ lifetime is only for this one instruction (only for
+ registers where (gpr.used & ~gpr.modified)
+
+ * spec says if registers aren't sign-extended, then 32-bit ops have
+ undefined results; this can simplify our code
+
+ * track which results are 32-bit sign extended versus 64-bit and
+ only promote when necessary
+
+ * register aliasing - move from one to another either doesn't do
+ anything and tracks both, or else shifts ownership to new
+ target
+
+ * generic drc:
+ * common code to load registers prior to instruction
+ - each register has a callback that generates the load
+ * generate code
+ * common code to write registers afterwards
+ * common register allocation routines (64 integer, 64 FPU)
+ * common OOB mechanism
+ * common self-mod code detection (via write bitmask)
+ * common code validation routines
+
+****************************************************************************
+
+ Temporary stack usage:
+ [esp+32].b = storage for CL in LWL/LWR/SWL/SWR
+ [esp+36].l = storage for new target PC in JR/JALR
+
+***************************************************************************/
+
+#include "mips3fe.h"
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define DISABLE_FIXED_RAM_LOAD (0)
+#define DISABLE_FIXED_RAM_STORE (0)
+#define FLUSH_AFTER_EACH_INSTRUCTION (0)
+#define PRINTF_EXCEPTIONS (0)
+#define PROBE_ADDRESS (0)
+#define LOG_HOTSPOTS (0)
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/* extra "conditions" used to augment the basic conditions for OOB handlers */
+#define COND_NONE 16
+#define COND_NO_JUMP 17
+
+/* 64-bit cookie that is scanned for during exception processing */
+#define EXCEPTION_ADDRESS_COOKIE U64(0xcc90cc90cc90cc90)
+
+/* append_readwrite flags */
+#define ARW_READ 0x0000
+#define ARW_WRITE 0x0001
+#define ARW_UNSIGNED 0x0000
+#define ARW_SIGNED 0x0002
+#define ARW_MASKED 0x0004
+#define ARW_CACHED 0x0008
+#define ARW_UNCACHED 0x0010
+
+/* compiler_register_allocate flags */
+#define ALLOC_SRC 0x0001
+#define ALLOC_DST 0x0002
+#define ALLOC_DIRTY 0x0004
+
+/* x86 register tracking flags */
+#define X86REG_LIVE 0x80 /* register is live and contains something */
+#define X86REG_MIPSMASK 0x3f /* low 6 bits indicate which MIPS register we hold */
+
+/* MIPS register tracking flags */
+#define MIPSREG_CONSTANT 0x8000 /* register is constant; low 10 bits select which one */
+#define MIPSREG_DIRTY 0x4000 /* register has been modified and needs to be flushed */
+#define MIPSREG_LIVE 0x2000 /* register is live in an x86 register */
+#define MIPSREG_X86MASK 0x000f /* low 4 bits indicate which x86 register holds us */
+
+
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+
+#define SPOFFS_SAVECL 32
+#define SPOFFS_NEXTPC 36
+
+#define REGADDR(reg) MDRC(&mips3.core->r[reg])
+#define HIADDR MDRC(&mips3.core->r[REG_HI])
+#define LOADDR MDRC(&mips3.core->r[REG_LO])
+#define CPR0ADDR(reg) MDRC(&mips3.core->cpr[0][reg])
+#define CCR0ADDR(reg) MDRC(&mips3.core->ccr[0][reg])
+#define CPR1ADDR(reg) MDRC(&mips3.core->cpr[1][reg])
+#define CCR1ADDR(reg) MDRC(&mips3.core->ccr[1][reg])
+#define CPR2ADDR(reg) MDRC(&mips3.core->cpr[2][reg])
+#define CCR2ADDR(reg) MDRC(&mips3.core->ccr[2][reg])
+#define FPR32(reg) (IS_FR0 ? &((float *)&mips3.core->cpr[1][0])[reg] : (float *)&mips3.core->cpr[1][reg])
+#define FPR32ADDR(reg) MDRC(FPR32(reg))
+#define FPR64(reg) (IS_FR0 ? (double *)&mips3.core->cpr[1][(reg)/2] : (double *)&mips3.core->cpr[1][reg])
+#define FPR64ADDR(reg) MDRC(FPR64(reg))
+#define CF1ADDR(which) MDRC(&mips3.core->cf[1][(mips3.core->flavor < MIPS3_TYPE_MIPS_IV) ? 0 : (which)])
+#define PCADDR MDRC(&mips3.core->pc)
+#define ICOUNTADDR MDRC(&mips3.core->icount)
+#define COREADDR MDRC(mips3.core)
+
+/* macros for querying/accessing the tracking flags */
+#define ISCONST(x) (((x) & MIPSREG_CONSTANT) != 0)
+#define ISDIRTY(x) (((x) & MIPSREG_DIRTY) != 0)
+#define ISLIVE(x) (((x) & MIPSREG_LIVE) != 0)
+#define CONSTVAL(x) ((INT32)((x) >> 32))
+#define X86REG(x) ((x) & MIPSREG_X86MASK)
+
+/* macro for defining a small positivec constant inline */
+#define MAKE_CONST(x) (MIPSREG_CONSTANT | ((INT64)(x) << 32))
+#define MAKE_DESTREG(x) (MIPSREG_DIRTY | ((x) & MIPSREG_X86MASK))
+
+
+
+/***************************************************************************
+ TYPEDEFS
+***************************************************************************/
+
+typedef struct _readwrite_handlers readwrite_handlers;
+struct _readwrite_handlers
+{
+ x86code * read_byte_signed;
+ x86code * read_byte_unsigned;
+ x86code * read_half_signed;
+ x86code * read_half_unsigned;
+ x86code * read_word_signed;
+ x86code * read_word_unsigned;
+ x86code * read_word_masked;
+ x86code * read_double;
+ x86code * read_double_masked;
+ x86code * write_byte;
+ x86code * write_half;
+ x86code * write_word;
+ x86code * write_word_masked;
+ x86code * write_double;
+ x86code * write_double_masked;
+};
+
+
+typedef INT64 mipsreg_state;
+typedef UINT8 x86reg_state;
+
+
+typedef struct _compiler_state compiler_state;
+struct _compiler_state
+{
+ UINT32 cycles; /* accumulated cycles */
+ x86reg_state x86regs[REG_MAX]; /* tracking what is in the x86 registers */
+ mipsreg_state mipsregs[35]; /* tracking what is in the MIPS registers */
+};
+
+
+typedef struct _oob_handler oob_handler;
+
+typedef void (*oob_callback)(drc_core *drc, oob_handler *oob, void *param);
+
+struct _oob_handler
+{
+ oob_callback callback; /* pointer to callback function */
+ void * param; /* callback parameter */
+ const opcode_desc * desc; /* pointer to description of relevant instruction */
+ emit_link link; /* link to source of the branch */
+ compiler_state compiler; /* state of the compiler at this time */
+};
+
+
+struct _mips3drc_data
+{
+ /* misc data */
+ UINT64 abs64mask[2];
+ UINT32 abs32mask[4];
+
+ /* C functions */
+ x86code * activecpu_gettotalcycles64;
+ x86code * mips3com_update_cycle_counting;
+ x86code * mips3com_tlbr;
+ x86code * mips3com_tlbwi;
+ x86code * mips3com_tlbwr;
+ x86code * mips3com_tlbp;
+
+ /* internal functions */
+ x86code * generate_interrupt_exception;
+ x86code * generate_cop_exception;
+ x86code * generate_overflow_exception;
+ x86code * generate_invalidop_exception;
+ x86code * generate_syscall_exception;
+ x86code * generate_break_exception;
+ x86code * generate_trap_exception;
+ x86code * generate_tlbload_exception;
+ x86code * generate_tlbstore_exception;
+ x86code * handle_pc_tlb_mismatch;
+ x86code * find_exception_handler;
+ x86code * explode_ccr31;
+ x86code * recover_ccr31;
+
+ /* read/write handlers */
+ readwrite_handlers general;
+ readwrite_handlers cached;
+ readwrite_handlers uncached;
+
+ /* out of bounds handlers */
+ oob_handler ooblist[COMPILE_MAX_INSTRUCTIONS*2];
+ int oobcount;
+
+ /* C functions */
+ x86code * execute_c_version;
+
+ /* hotspot tracking */
+#if LOG_HOTSPOTS
+ struct
+ {
+ offs_t pc;
+ UINT32 opcode;
+ UINT64 count;
+ } hotspot[100000];
+ int next_hotspot;
+#endif
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void drc_reset_callback(drc_core *drc);
+static void drc_recompile_callback(drc_core *drc);
+static void drc_entrygen_callback(drc_core *drc);
+
+static void compile_one(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+
+static void oob_exception_cleanup(drc_core *drc, oob_handler *oob, void *param);
+static void oob_interrupt_cleanup(drc_core *drc, oob_handler *oob, void *param);
+
+static void compiler_register_set_constant(compiler_state *compiler, UINT8 mipsreg, UINT64 constval);
+static mipsreg_state compiler_register_allocate(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 mipsreg, UINT8 flags);
+static void compiler_register_flush(drc_core *drc, compiler_state *compiler, UINT8 mipsreg, int free);
+static void compiler_register_flush_all(drc_core *drc, compiler_state *compiler);
+
+static void probe_printf(void);
+
+static void generate_read_write_handlers(drc_core *drc, readwrite_handlers *handlers, const char *type, UINT8 flags);
+static void append_generate_exception(drc_core *drc, UINT8 exception);
+static void append_readwrite_and_translate(drc_core *drc, int size, UINT8 flags, UINT32 ptroffs);
+static void append_handle_pc_tlb_mismatch(drc_core *drc);
+static void append_find_exception_handler(drc_core *drc);
+static void append_explode_ccr31(drc_core *drc);
+static void append_recover_ccr31(drc_core *drc);
+static void append_check_interrupts(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static void append_check_sw_interrupts(drc_core *drc, int inline_generate);
+
+static void emit_update_cycles_pc_and_flush(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, offs_t destpc);
+
+static int compile_instruction(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static int compile_special(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static int compile_regimm(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static int compile_idt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static int compile_cop0(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static int compile_cop1(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+static int compile_cop1x(drc_core *drc, compiler_state *compiler, const opcode_desc *desc);
+
+static int compile_shift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable);
+static int compile_dshift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable, int shift_const_plus32);
+static int compile_movzn(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_movn);
+static int compile_trapcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int immediate);
+static int compile_branchcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition);
+static int compile_branchcz(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int link);
+static int compile_add(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate);
+static int compile_dadd(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate);
+static int compile_sub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions);
+static int compile_dsub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions);
+static int compile_mult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned);
+static int compile_dmult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned);
+static int compile_div(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned);
+static int compile_ddiv(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned);
+static int compile_logical(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int operation, int immediate);
+static int compile_slt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned, int immediate);
+static int compile_load(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, int is_unsigned);
+static int compile_load_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest);
+static int compile_loadx_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest);
+static int compile_load_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right);
+static int compile_load_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right);
+static int compile_store(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size);
+static int compile_store_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src);
+static int compile_storex_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src);
+static int compile_store_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right);
+static int compile_store_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right);
+
+
+
+/***************************************************************************
+ PRIVATE GLOBAL VARIABLES
+***************************************************************************/
+
+static const UINT8 nvreg[] = { REG_NV0, REG_NV1, REG_NV2, REG_NV3, REG_NV4, REG_NV5, REG_NV6 };
+
+static const char *x86regname[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ oob_request_callback - request an out-of-
+ bounds callback for later
+-------------------------------------------------*/
+
+INLINE void oob_request_callback(drc_core *drc, UINT8 condition, oob_callback callback, const compiler_state *compiler, const opcode_desc *desc, void *param)
+{
+ oob_handler *oob = &mips3.drcdata->ooblist[mips3.drcdata->oobcount++];
+
+ /* fill in the next handler */
+ oob->callback = callback;
+ oob->desc = desc;
+ oob->compiler = *compiler;
+ oob->param = param;
+
+ /* emit an appropriate jump */
+ if (condition == COND_NO_JUMP)
+ {
+ oob->link.size = 0;
+ oob->link.target = drc->cache_top;
+ }
+ else if (condition == COND_NONE)
+ emit_jmp_near_link(DRCTOP, &oob->link); // jmp <target>
+ else
+ emit_jcc_near_link(DRCTOP, condition, &oob->link); // jcc <target>
+}
+
+
+/*-------------------------------------------------
+ compiler_state_reset - reset the compiler
+ state
+-------------------------------------------------*/
+
+INLINE void compiler_state_reset(compiler_state *compiler)
+{
+ memset(compiler, 0, sizeof(*compiler));
+ compiler_register_set_constant(compiler, 0, 0);
+}
+
+
+
+/***************************************************************************
+ CORE RECOMPILER SYSTEMS
+***************************************************************************/
+
+/*-------------------------------------------------
+ mips3drc_init - initialize the drc-specific
+ state
+-------------------------------------------------*/
+
+static void mips3drc_init(void)
+{
+ drc_config drconfig;
+
+ /* fill in the config */
+ memset(&drconfig, 0, sizeof(drconfig));
+ drconfig.cache_base = mips3.cache;
+ drconfig.cache_size = CACHE_SIZE;
+ drconfig.max_instructions = (512 + 128) / 4;
+ drconfig.address_bits = 32;
+ drconfig.lsbs_to_ignore = 2;
+ drconfig.baseptr = &mips3.core->r[17];
+ drconfig.pcptr = (UINT32 *)&mips3.core->pc;
+ drconfig.cb_reset = drc_reset_callback;
+ drconfig.cb_recompile = drc_recompile_callback;
+ drconfig.cb_entrygen = drc_entrygen_callback;
+
+ /* initialize the compiler */
+ mips3.drc = drc_init(cpu_getactivecpu(), &drconfig);
+ mips3.drcoptions = MIPS3DRC_FASTEST_OPTIONS;
+
+ /* allocate our data out of the cache */
+ mips3.drcdata = drc_alloc(mips3.drc, sizeof(*mips3.drcdata) + 16);
+ mips3.drcdata = (mips3drc_data *)((((FPTR)mips3.drcdata + 15) >> 4) << 4);
+ memset(mips3.drcdata, 0, sizeof(*mips3.drcdata));
+
+ /* set up constants */
+ mips3.drcdata->abs64mask[0] = U64(0x7fffffffffffffff);
+ mips3.drcdata->abs32mask[0] = 0x7fffffff;
+
+ /* get pointers to C functions */
+ mips3.drcdata->activecpu_gettotalcycles64 = (x86code *)activecpu_gettotalcycles64;
+ mips3.drcdata->mips3com_update_cycle_counting = (x86code *)mips3com_update_cycle_counting;
+ mips3.drcdata->mips3com_tlbr = (x86code *)mips3com_tlbr;
+ mips3.drcdata->mips3com_tlbwi = (x86code *)mips3com_tlbwi;
+ mips3.drcdata->mips3com_tlbwr = (x86code *)mips3com_tlbwr;
+ mips3.drcdata->mips3com_tlbp = (x86code *)mips3com_tlbp;
+
+#if COMPARE_AGAINST_C
+ mips3.drcdata->execute_c_version = (x86code *)execute_c_version;
+#endif
+}
+
+
+/*-------------------------------------------------
+ mips3drc_exit - clean up the drc-specific
+ state
+-------------------------------------------------*/
+
+static void mips3drc_exit(void)
+{
+ printf("Final code size = %d\n", (int)(mips3.drc->cache_top - mips3.drc->cache_base));
+#if LOG_HOTSPOTS
+ /* print top hotspots */
+ {
+ int maxspot = 0;
+ int hotnum;
+ int count;
+
+ for (count = 0; count < 10; count++)
+ {
+ for (hotnum = 1; hotnum < mips3.drcdata->next_hotspot; hotnum++)
+ if (mips3.drcdata->hotspot[hotnum].count > mips3.drcdata->hotspot[maxspot].count)
+ maxspot = hotnum;
+ printf("%08X%08X hits: add_speedup(0x%08X, 0x%08X);\n",
+ (UINT32)(mips3.drcdata->hotspot[maxspot].count >> 32),
+ (UINT32)mips3.drcdata->hotspot[maxspot].count,
+ mips3.drcdata->hotspot[maxspot].pc,
+ mips3.drcdata->hotspot[maxspot].opcode);
+ mips3.drcdata->hotspot[maxspot].count = 0;
+ }
+ }
+#endif
+}
+
+
+
+/***************************************************************************
+ RECOMPILER CALLBACKS
+***************************************************************************/
+
+/*------------------------------------------------------------------
+ drc_reset_callback
+------------------------------------------------------------------*/
+
+static void drc_reset_callback(drc_core *drc)
+{
+ if (LOG_CODE)
+ {
+ x86log_disasm_code_range(mips3.log, "entry_point:", (x86code *)drc->entry_point, drc->exit_point);
+ x86log_disasm_code_range(mips3.log, "exit_point:", drc->exit_point, drc->recompile);
+ x86log_disasm_code_range(mips3.log, "recompile:", drc->recompile, drc->dispatch);
+ x86log_disasm_code_range(mips3.log, "dispatch:", drc->dispatch, drc->flush);
+ x86log_disasm_code_range(mips3.log, "flush:", drc->flush, drc->cache_top);
+ }
+
+ mips3.drcdata->generate_interrupt_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_INTERRUPT);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_interrupt_exception:", mips3.drcdata->generate_interrupt_exception, drc->cache_top);
+
+ mips3.drcdata->generate_cop_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_BADCOP);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_cop_exception:", mips3.drcdata->generate_cop_exception, drc->cache_top);
+
+ mips3.drcdata->generate_overflow_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_OVERFLOW);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_overflow_exception:", mips3.drcdata->generate_overflow_exception, drc->cache_top);
+
+ mips3.drcdata->generate_invalidop_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_INVALIDOP);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_invalidop_exception:", mips3.drcdata->generate_invalidop_exception, drc->cache_top);
+
+ mips3.drcdata->generate_syscall_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_SYSCALL);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_syscall_exception:", mips3.drcdata->generate_syscall_exception, drc->cache_top);
+
+ mips3.drcdata->generate_break_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_BREAK);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_break_exception:", mips3.drcdata->generate_break_exception, drc->cache_top);
+
+ mips3.drcdata->generate_trap_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_TRAP);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_trap_exception:", mips3.drcdata->generate_trap_exception, drc->cache_top);
+
+ mips3.drcdata->generate_tlbload_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_TLBLOAD);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_tlbload_exception:", mips3.drcdata->generate_tlbload_exception, drc->cache_top);
+
+ mips3.drcdata->generate_tlbstore_exception = drc->cache_top;
+ append_generate_exception(drc, EXCEPTION_TLBSTORE);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "generate_tlbstore_exception:", mips3.drcdata->generate_tlbstore_exception, drc->cache_top);
+
+ mips3.drcdata->handle_pc_tlb_mismatch = drc->cache_top;
+ append_handle_pc_tlb_mismatch(drc);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "handle_pc_tlb_mismatch:", mips3.drcdata->handle_pc_tlb_mismatch, drc->cache_top);
+
+ mips3.drcdata->find_exception_handler = drc->cache_top;
+ append_find_exception_handler(drc);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "find_exception_handler:", mips3.drcdata->find_exception_handler, drc->cache_top);
+
+ mips3.drcdata->explode_ccr31 = drc->cache_top;
+ append_explode_ccr31(drc);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "explode_ccr31:", mips3.drcdata->explode_ccr31, drc->cache_top);
+
+ mips3.drcdata->recover_ccr31 = drc->cache_top;
+ append_recover_ccr31(drc);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, "recover_ccr31:", mips3.drcdata->recover_ccr31, drc->cache_top);
+
+ generate_read_write_handlers(drc, &mips3.drcdata->general, "general", 0);
+ generate_read_write_handlers(drc, &mips3.drcdata->cached, "cached", ARW_CACHED);
+ generate_read_write_handlers(drc, &mips3.drcdata->uncached, "uncached", ARW_UNCACHED);
+}
+
+
+/*------------------------------------------------------------------
+ drc_recompile_callback
+------------------------------------------------------------------*/
+
+static void drc_recompile_callback(drc_core *drc)
+{
+ int compiled_last_sequence = FALSE;
+ const opcode_desc *seqhead, *seqlast;
+ const opcode_desc *desclist;
+ void *start = drc->cache_top;
+ int override = FALSE;
+ int oobnum;
+
+ (void)start;
+
+ /* begin the sequence */
+ drc_begin_sequence(drc, mips3.core->pc);
+ mips3.drcdata->oobcount = 0;
+
+ /* get a description of this sequence */
+ desclist = drcfe_describe_code(mips3.drcfe, mips3.core->pc);
+ if (LOG_CODE)
+ log_opcode_desc(mips3.log, desclist, 0);
+
+ /* loop until we get through all instruction sequences */
+ for (seqhead = desclist; seqhead != NULL; seqhead = seqlast->next)
+ {
+ const opcode_desc *curdesc;
+ compiler_state compiler;
+
+ /* determine the last instruction in this sequence */
+ for (seqlast = seqhead; seqlast != NULL; seqlast = seqlast->next)
+ if (seqlast->flags & OPFLAG_END_SEQUENCE)
+ break;
+ assert(seqlast != NULL);
+
+ /* add this as an entry point */
+ if (drc_add_entry_point(drc, seqhead->pc, override) && !override)
+ {
+ /* if this is the first sequence, it is a recompile request; allow overrides */
+ if (seqhead == desclist)
+ {
+ override = TRUE;
+ drc_add_entry_point(drc, seqhead->pc, override);
+ }
+
+ /* otherwise, just emit a jump to existing code */
+ else
+ {
+ if (compiled_last_sequence)
+ drc_append_fixed_dispatcher(drc, seqhead->pc, TRUE);
+ compiled_last_sequence = FALSE;
+ continue;
+ }
+ }
+ compiled_last_sequence = TRUE;
+
+ /* add a code log entry */
+ if (LOG_CODE)
+ x86log_add_comment(mips3.log, drc->cache_top, "-------------------------");
+
+ /* validate this code block if we're not pointing into ROM */
+ /* note that we assume P1 still contains the PC */
+ if (memory_get_write_ptr(cpu_getactivecpu(), ADDRESS_SPACE_PROGRAM, seqhead->physpc) != NULL)
+ {
+ if (LOG_CODE)
+ x86log_add_comment(mips3.log, drc->cache_top, "[Validation for %08X]", seqhead->pc);
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)seqhead->opptr.v); // mov rax,seqhead->opptr
+ for (curdesc = seqhead; curdesc != seqlast->next; curdesc = curdesc->next)
+ {
+ emit_cmp_m32_imm(DRCTOP, MBD(REG_RAX, curdesc->opptr.b - seqhead->opptr.b), *curdesc->opptr.l);
+ // cmp [code],val
+ emit_jcc(DRCTOP, COND_NE, mips3.drc->recompile); // jne recompile
+ }
+ }
+
+ /* initialize the compiler state */
+ compiler_state_reset(&compiler);
+
+#if LOG_HOTSPOTS
+ /* add hotspot tracking */
+ {
+ int hotnum;
+
+ drc_register_code_at_cache_top(drc, seqhead->pc);
+
+ for (hotnum = 0; hotnum < mips3.drcdata->next_hotspot; hotnum++)
+ if (mips3.drcdata->hotspot[hotnum].pc == seqhead->pc && mips3.drcdata->hotspot[hotnum].opcode == *seqhead->opptr.l)
+ break;
+ if (hotnum == mips3.drcdata->next_hotspot)
+ {
+ assert(hotnum < ARRAY_LENGTH(mips3.drcdata->hotspot));
+ mips3.drcdata->hotspot[hotnum].pc = seqhead->pc;
+ mips3.drcdata->hotspot[hotnum].opcode = *seqhead->opptr.l;
+ mips3.drcdata->hotspot[hotnum].count = 0;
+ mips3.drcdata->next_hotspot++;
+ }
+ emit_add_m64_imm(DRCTOP, MDRC(&mips3.drcdata->hotspot[hotnum].count), 1);
+ }
+#endif
+
+ /* iterate over instructions in the sequence and compile them */
+ for (curdesc = seqhead; curdesc != seqlast->next; curdesc = curdesc->next)
+ compile_one(drc, &compiler, curdesc);
+
+ /* at the end of the sequence; update the PC in memory and check cycle counts */
+ if (!(seqlast->flags & (OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_WILL_CAUSE_EXCEPTION)))
+ {
+ UINT32 nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4;
+ emit_update_cycles_pc_and_flush(drc, &compiler, NULL, nextpc);
+ }
+
+ /* if this is a likely branch, the fall through case needs to skip the next instruction */
+ if ((seqlast->flags & OPFLAG_IS_CONDITIONAL_BRANCH) && seqlast->skipslots > 0)
+ if (seqlast->next != NULL && seqlast->next->pc != seqlast->pc + 8)
+ {
+ compiler_register_flush_all(drc, &compiler);
+ drc_append_tentative_fixed_dispatcher(drc, seqlast->pc + 8, TRUE); // jmp <pc+8>
+ }
+
+ /* if we need a redispatch, do it now */
+ if (seqlast->flags & OPFLAG_REDISPATCH)
+ {
+ UINT32 nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4;
+ compiler_register_flush_all(drc, &compiler);
+ drc_append_tentative_fixed_dispatcher(drc, nextpc, TRUE); // jmp <nextpc>
+ }
+
+ /* if we need to return to the start, do it */
+ if (seqlast->flags & OPFLAG_RETURN_TO_START)
+ {
+ compiler_register_flush_all(drc, &compiler);
+ drc_append_tentative_fixed_dispatcher(drc, mips3.core->pc, FALSE); // jmp <startpc>
+ }
+ }
+
+ /* end the sequence */
+ drc_end_sequence(drc);
+
+ /* run the out-of-bounds handlers */
+ for (oobnum = 0; oobnum < mips3.drcdata->oobcount; oobnum++)
+ {
+ oob_handler *oob = &mips3.drcdata->ooblist[oobnum];
+ (*oob->callback)(drc, oob, oob->param);
+ }
+
+ /* log the generated code */
+ if (LOG_CODE)
+ {
+ char label[60];
+ sprintf(label, "Code @ %08X (%08X physical)", desclist->pc, desclist->physpc);
+ x86log_disasm_code_range(mips3.log, label, start, drc->cache_top);
+ }
+}
+
+
+/*------------------------------------------------------------------
+ drc_entrygen_callback
+------------------------------------------------------------------*/
+
+static void drc_entrygen_callback(drc_core *drc)
+{
+ append_check_interrupts(drc, NULL, NULL);
+}
+
+
+
+/***************************************************************************
+ RECOMPILER CORE
+***************************************************************************/
+
+/*------------------------------------------------------------------
+ compile_one
+------------------------------------------------------------------*/
+
+static void compile_one(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ int hotnum;
+
+ /* register this instruction */
+ if (!(desc->flags & OPFLAG_IN_DELAY_SLOT))
+ drc_register_code_at_cache_top(drc, desc->pc);
+
+ /* add an entry for the log */
+ if (LOG_CODE)
+ log_add_disasm_comment(mips3.log, drc->cache_top, desc->pc, *desc->opptr.l);
+
+ /* if we want a probe, add it here */
+ if (desc->pc == PROBE_ADDRESS)
+ {
+ emit_mov_m32_imm(DRCTOP, PCADDR, desc->pc); // mov [pc],desc->pc
+ compiler_register_flush_all(drc, compiler);
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)probe_printf);
+ emit_call_r64(DRCTOP, REG_RAX);
+ }
+
+ /* if we are debugging, call the debugger */
+ if (Machine->debug_mode || COMPARE_AGAINST_C)
+ {
+ emit_mov_m32_imm(DRCTOP, PCADDR, desc->pc); // mov [pc],desc->pc
+ if (COMPARE_AGAINST_C && !(desc->flags & OPFLAG_IN_DELAY_SLOT))
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->execute_c_version));
+ if (Machine->debug_mode)
+ drc_append_call_debugger(drc); // <call debugger>
+ }
+
+ /* validate our TLB entry at this PC; if we fail, we need to recompile */
+ if ((desc->flags & OPFLAG_VALIDATE_TLB) && (desc->pc < 0x80000000 || desc->pc >= 0xc0000000))
+ {
+ emit_cmp_m32_imm(DRCTOP, MDRC(&mips3.core->tlb_table[desc->pc >> 12]), mips3.core->tlb_table[desc->pc >> 12]);
+ // cmp tlb_table[pc>>12],<original value>
+ oob_request_callback(drc, COND_NE, oob_exception_cleanup, compiler, desc, mips3.drcdata->handle_pc_tlb_mismatch);
+ // jne handle_pc_tlb_mismatch
+ }
+
+ /* accumulate total cycles */
+ compiler->cycles += desc->cycles;
+
+ /* is this a hotspot? */
+ for (hotnum = 0; hotnum < MIPS3_MAX_HOTSPOTS; hotnum++)
+ if (desc->pc == mips3.hotspot[hotnum].pc && *desc->opptr.l == mips3.hotspot[hotnum].opcode)
+ {
+ compiler->cycles += mips3.hotspot[hotnum].cycles;
+ break;
+ }
+
+ /* if this is an invalid opcode, generate the exception now */
+ if (desc->flags & OPFLAG_INVALID_OPCODE)
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_invalidop_exception);
+
+ /* otherwise, it's a regular instruction */
+ else
+ {
+ /* compile the instruction */
+ if (!compile_instruction(drc, compiler, desc))
+ fatalerror("Unimplemented op %08X (%02X,%02X)", *desc->opptr.l, *desc->opptr.l >> 26, *desc->opptr.l & 0x3f);
+
+ /* flush */
+ if (FLUSH_AFTER_EACH_INSTRUCTION)
+ compiler_register_flush_all(drc, compiler);
+ }
+}
+
+
+
+/***************************************************************************
+ OUT-OF-BOUNDS CODEGEN
+***************************************************************************/
+
+/*-------------------------------------------------
+ oob_exception_cleanup - out-of-bounds code
+ generator for cleaning up and generating an
+ exception with the EPC pointing to the
+ current instruction
+-------------------------------------------------*/
+
+static void oob_exception_cleanup(drc_core *drc, oob_handler *oob, void *param)
+{
+ /* if this is non-jumping error, emit a unique signature */
+ if (oob->link.size == 0)
+ {
+ /* align to an 8-byte boundary */
+ while (((UINT64)drc->cache_top & 7) != 0)
+ emit_int_3(DRCTOP);
+
+ /* logging */
+ if (LOG_CODE)
+ {
+ x86log_add_comment(mips3.log, drc->cache_top, "[Exception path for %08X]", oob->desc->pc);
+ x86log_mark_as_data(mips3.log, drc->cache_top, drc->cache_top + 15, 8);
+ }
+
+ /* output a pair of qwords */
+ emit_qword(DRCTOP, EXCEPTION_ADDRESS_COOKIE);
+ emit_qword(DRCTOP, (UINT64)oob->link.target);
+ }
+
+ /* otherwise, just resolve the link */
+ else
+ {
+ resolve_link(DRCTOP, &oob->link);
+ if (LOG_CODE)
+ x86log_add_comment(mips3.log, drc->cache_top, "[Exception path for %08X]", oob->desc->pc);
+ }
+
+ /* adjust for cycles */
+ if (oob->compiler.cycles != 0)
+ emit_sub_m32_imm(DRCTOP, ICOUNTADDR, oob->compiler.cycles); // sub [icount],cycles
+
+ /* update the PC with the instruction PC */
+ if (!(oob->desc->flags & OPFLAG_IN_DELAY_SLOT))
+ emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->pc); // mov p1,pc
+ else
+ emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->pc | 1); // mov p1,pc | 1
+
+ /* flush any dirty registers */
+ compiler_register_flush_all(drc, &oob->compiler); // <flush registers>
+
+ /* jump to the exception generator */
+ emit_jmp(DRCTOP, param); // jmp <generate exception>
+}
+
+
+/*-------------------------------------------------
+ oob_interrupt_cleanup - out-of-bounds code
+ generator for cleaning up and generating an
+ interrupt with the EPC pointing to the
+ following instruction
+-------------------------------------------------*/
+
+static void oob_interrupt_cleanup(drc_core *drc, oob_handler *oob, void *param)
+{
+ /* resolve the link */
+ resolve_link(DRCTOP, &oob->link);
+
+ /* add a code log entry */
+ if (LOG_CODE)
+ x86log_add_comment(mips3.log, drc->cache_top, "[Interrupt path for %08X]", oob->desc->pc);
+
+ /* adjust for cycles */
+ if (oob->compiler.cycles != 0)
+ emit_sub_m32_imm(DRCTOP, ICOUNTADDR, oob->compiler.cycles); // sub [icount],cycles
+
+ /* update the PC with the following instruction PC */
+ if (!(oob->desc->flags & OPFLAG_IN_DELAY_SLOT))
+ {
+ assert(!(oob->desc->flags & OPFLAG_IS_BRANCH));
+ emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->pc + 4); // mov p1,pc + 4
+ }
+ else
+ {
+ assert(oob->desc->branch != NULL);
+ if (oob->desc->branch->targetpc != BRANCH_TARGET_DYNAMIC)
+ emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->branch->targetpc); // mov p1,<targetpc>
+ else
+ emit_mov_r32_m32(DRCTOP, REG_P1, MBD(REG_RSP, SPOFFS_NEXTPC)); // mov p1,[rsp + nextpc]
+ }
+
+ /* flush any dirty registers */
+ compiler_register_flush_all(drc, &oob->compiler); // <flush registers>
+
+ /* jump to the exception generator */
+ emit_jmp(DRCTOP, param); // jmp <generate exception>
+}
+
+
+
+/***************************************************************************
+ REGISTER ALLOCATION ROUTINES
+***************************************************************************/
+
+/*-------------------------------------------------
+ compiler_register_set_constant - set a MIPS
+ register to a constant value
+-------------------------------------------------*/
+
+static void compiler_register_set_constant(compiler_state *compiler, UINT8 mipsreg, UINT64 constval)
+{
+ mipsreg_state reg = compiler->mipsregs[mipsreg];
+
+ assert((INT32)constval == (INT64)constval);
+
+ /* if the register was previously live, free it */
+ if (ISLIVE(reg))
+ {
+ assert(compiler->x86regs[X86REG(reg)] & X86REG_LIVE);
+ compiler->x86regs[X86REG(reg)] = 0;
+ }
+
+ /* set up the constant */
+ compiler->mipsregs[mipsreg] = MAKE_CONST(constval) | ((mipsreg == 0) ? 0 : MIPSREG_DIRTY);
+}
+
+
+/*-------------------------------------------------
+ compiler_register_allocate - allocate an x86
+ register to hold a MIPS register
+-------------------------------------------------*/
+
+static mipsreg_state compiler_register_allocate(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 mipsreg, UINT8 flags)
+{
+ UINT8 regindex, regnum = 0;
+ UINT64 livemask = 0;
+
+ /* register 0 is always constant */
+ if (mipsreg == 0)
+ return MAKE_CONST(0);
+
+ /* first see if we are already allocated */
+ if (ISLIVE(compiler->mipsregs[mipsreg]))
+ {
+ /* if we're not a source, make sure we are marked dirty */
+ if (flags & ALLOC_DIRTY)
+ compiler->mipsregs[mipsreg] |= MIPSREG_DIRTY;
+ return compiler->mipsregs[mipsreg];
+ }
+
+ /* if we just want a source register, return a constant directly */
+ if (flags == ALLOC_SRC && ISCONST(compiler->mipsregs[mipsreg]))
+ return compiler->mipsregs[mipsreg];
+
+ /* first look for a free entry */
+ for (regindex = 0; regindex < NUM_NVREG; regindex++)
+ {
+ regnum = nvreg[regindex];
+ if (!(compiler->x86regs[regnum] & X86REG_LIVE))
+ goto allocateit;
+ else
+ livemask |= (UINT64)1 << (compiler->x86regs[regnum] & X86REG_MIPSMASK);
+ }
+
+ /* can't have anything we need for this instruction, however */
+ livemask &= ~desc->gpr.used & ~desc->gpr.modified;
+ desc = desc->next;
+
+ /* next loop over the registers, figuring out who has the longest until being used */
+ while (desc != NULL && livemask != 0)
+ {
+ /* if this instruction uses the last remaining register, we have to pick from among those */
+ if ((livemask & ~desc->gpr.used) == 0)
+ break;
+ livemask &= ~desc->gpr.used;
+
+ /* if this instruction modifies any of the registers, we want those */
+ if ((livemask & desc->gpr.modified) != 0)
+ {
+ livemask &= desc->gpr.modified;
+ break;
+ }
+
+ /* advance to the next instruction; stop if we hit end-of-sequence */
+ if (desc->flags & OPFLAG_END_SEQUENCE)
+ break;
+ desc = desc->next;
+ }
+
+ /* pick the first one */
+ for (regindex = 0; regindex < NUM_NVREG; regindex++)
+ {
+ regnum = nvreg[regindex];
+ if (livemask & ((UINT64)1 << (compiler->x86regs[regnum] & X86REG_MIPSMASK)))
+ break;
+ }
+ assert(regindex != NUM_NVREG);
+
+ /* flush it */
+ compiler_register_flush(drc, compiler, compiler->x86regs[regnum] & X86REG_MIPSMASK, TRUE);
+
+allocateit:
+ /* load the contents if we are a source */
+ if (LOG_CODE)
+ x86log_add_comment(mips3.log, drc->cache_top, "allocate r%d (%s)", mipsreg, x86regname[regnum]);
+ if (flags & ALLOC_SRC)
+ {
+ mipsreg_state state = compiler->mipsregs[mipsreg];
+ if (ISCONST(state))
+ emit_mov_r64_imm(DRCTOP, regnum, CONSTVAL(state)); // mov <x86reg>,constant
+ else
+ emit_mov_r64_m64(DRCTOP, regnum, REGADDR(mipsreg)); // mov <x86reg>,[reg]
+ }
+
+ /* mark it allocated */
+ assert(!(compiler->x86regs[regnum] & X86REG_LIVE));
+ compiler->x86regs[regnum] = X86REG_LIVE | mipsreg;
+ compiler->mipsregs[mipsreg] = MIPSREG_LIVE | regnum | ((flags & ALLOC_DIRTY) ? MIPSREG_DIRTY : 0);
+ return compiler->mipsregs[mipsreg];
+}
+
+
+/*-------------------------------------------------
+ compiler_register_flush - flush and optionally
+ free a single MIPS register
+-------------------------------------------------*/
+
+static void compiler_register_flush(drc_core *drc, compiler_state *compiler, UINT8 mipsreg, int free)
+{
+ mipsreg_state reg = compiler->mipsregs[mipsreg];
+
+ assert(ISCONST(reg) || ISLIVE(reg));
+
+ /* flush the value if dirty */
+ if (ISDIRTY(reg))
+ {
+ if (LOG_CODE)
+ x86log_add_comment(mips3.log, drc->cache_top, "flush r%d (%s)", mipsreg, ISCONST(reg) ? "constant" : x86regname[X86REG(reg)]);
+
+ /* flush a constant value */
+ if (ISCONST(reg))
+ emit_mov_m64_imm(DRCTOP, REGADDR(mipsreg), CONSTVAL(reg)); // mov [reg],constant
+
+ /* flush a live value */
+ else if (ISLIVE(reg))
+ emit_mov_m64_r64(DRCTOP, REGADDR(mipsreg), X86REG(reg)); // mov [reg],<x86reg>
+ }
+
+ /* clear the dirty flag */
+ if (!free)
+ compiler->mipsregs[mipsreg] &= ~MIPSREG_DIRTY;
+ else
+ {
+ if (ISLIVE(reg))
+ compiler->x86regs[X86REG(reg)] = 0;
+ compiler->mipsregs[mipsreg] = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ compiler_register_flush_all - flush all dirty
+ registers
+-------------------------------------------------*/
+
+static void compiler_register_flush_all(drc_core *drc, compiler_state *compiler)
+{
+ int mipsreg;
+
+ /* flush all dirty registers */
+ for (mipsreg = 1; mipsreg < 35; mipsreg++)
+ if (ISDIRTY(compiler->mipsregs[mipsreg]))
+ compiler_register_flush(drc, compiler, mipsreg, FALSE);
+}
+
+
+
+/***************************************************************************
+ COMPILER CALLBACKS
+***************************************************************************/
+
+/*-------------------------------------------------
+ probe_printf - print the current CPU state
+ and return
+-------------------------------------------------*/
+
+static void probe_printf(void)
+{
+ printf(" PC=%08X r1=%08X%08X r2=%08X%08X r3=%08X%08X\n",
+ mips3.core->pc,
+ (UINT32)(mips3.core->r[1] >> 32), (UINT32)mips3.core->r[1],
+ (UINT32)(mips3.core->r[2] >> 32), (UINT32)mips3.core->r[2],
+ (UINT32)(mips3.core->r[3] >> 32), (UINT32)mips3.core->r[3]);
+ printf(" r4=%08X%08X r5=%08X%08X r6=%08X%08X r7=%08X%08X\n",
+ (UINT32)(mips3.core->r[4] >> 32), (UINT32)mips3.core->r[4],
+ (UINT32)(mips3.core->r[5] >> 32), (UINT32)mips3.core->r[5],
+ (UINT32)(mips3.core->r[6] >> 32), (UINT32)mips3.core->r[6],
+ (UINT32)(mips3.core->r[7] >> 32), (UINT32)mips3.core->r[7]);
+ printf(" r8=%08X%08X r9=%08X%08X r10=%08X%08X r11=%08X%08X\n",
+ (UINT32)(mips3.core->r[8] >> 32), (UINT32)mips3.core->r[8],
+ (UINT32)(mips3.core->r[9] >> 32), (UINT32)mips3.core->r[9],
+ (UINT32)(mips3.core->r[10] >> 32), (UINT32)mips3.core->r[10],
+ (UINT32)(mips3.core->r[11] >> 32), (UINT32)mips3.core->r[11]);
+ printf("r12=%08X%08X r13=%08X%08X r14=%08X%08X r15=%08X%08X\n",
+ (UINT32)(mips3.core->r[12] >> 32), (UINT32)mips3.core->r[12],
+ (UINT32)(mips3.core->r[13] >> 32), (UINT32)mips3.core->r[13],
+ (UINT32)(mips3.core->r[14] >> 32), (UINT32)mips3.core->r[14],
+ (UINT32)(mips3.core->r[15] >> 32), (UINT32)mips3.core->r[15]);
+ printf("r16=%08X%08X r17=%08X%08X r18=%08X%08X r19=%08X%08X\n",
+ (UINT32)(mips3.core->r[16] >> 32), (UINT32)mips3.core->r[16],
+ (UINT32)(mips3.core->r[17] >> 32), (UINT32)mips3.core->r[17],
+ (UINT32)(mips3.core->r[18] >> 32), (UINT32)mips3.core->r[18],
+ (UINT32)(mips3.core->r[19] >> 32), (UINT32)mips3.core->r[19]);
+ printf("r20=%08X%08X r21=%08X%08X r22=%08X%08X r23=%08X%08X\n",
+ (UINT32)(mips3.core->r[20] >> 32), (UINT32)mips3.core->r[20],
+ (UINT32)(mips3.core->r[21] >> 32), (UINT32)mips3.core->r[21],
+ (UINT32)(mips3.core->r[22] >> 32), (UINT32)mips3.core->r[22],
+ (UINT32)(mips3.core->r[23] >> 32), (UINT32)mips3.core->r[23]);
+ printf("r24=%08X%08X r25=%08X%08X r26=%08X%08X r27=%08X%08X\n",
+ (UINT32)(mips3.core->r[24] >> 32), (UINT32)mips3.core->r[24],
+ (UINT32)(mips3.core->r[25] >> 32), (UINT32)mips3.core->r[25],
+ (UINT32)(mips3.core->r[26] >> 32), (UINT32)mips3.core->r[26],
+ (UINT32)(mips3.core->r[27] >> 32), (UINT32)mips3.core->r[27]);
+ printf("r28=%08X%08X r29=%08X%08X r30=%08X%08X r31=%08X%08X\n",
+ (UINT32)(mips3.core->r[28] >> 32), (UINT32)mips3.core->r[28],
+ (UINT32)(mips3.core->r[29] >> 32), (UINT32)mips3.core->r[29],
+ (UINT32)(mips3.core->r[30] >> 32), (UINT32)mips3.core->r[30],
+ (UINT32)(mips3.core->r[31] >> 32), (UINT32)mips3.core->r[31]);
+ printf(" hi=%08X%08X lo=%08X%08X\n",
+ (UINT32)(mips3.core->r[REG_HI] >> 32), (UINT32)mips3.core->r[REG_HI],
+ (UINT32)(mips3.core->r[REG_LO] >> 32), (UINT32)mips3.core->r[REG_LO]);
+}
+
+
+/*-------------------------------------------------
+ exception_trap - log any exceptions that
+ aren't interrupts
+-------------------------------------------------*/
+
+static void exception_trap(void)
+{
+ if ((mips3.core->cpr[0][COP0_Cause] & 0xff) != 0)
+ printf("Exception: EPC=%08X Cause=%08X BadVAddr=%08X Jmp=%08X\n", mips3.core->cpr[0][COP0_EPC], mips3.core->cpr[0][COP0_Cause], mips3.core->cpr[0][COP0_BadVAddr], mips3.core->pc);
+}
+
+
+
+/***************************************************************************
+ COMMON ROUTINES
+***************************************************************************/
+
+/*------------------------------------------------------------------
+ generate_read_write_handlers
+------------------------------------------------------------------*/
+
+static void generate_read_write_handlers(drc_core *drc, readwrite_handlers *handlers, const char *type, UINT8 flags)
+{
+ char buffer[100];
+
+ handlers->read_byte_signed = drc->cache_top;
+ append_readwrite_and_translate(drc, 1, ARW_READ | ARW_SIGNED | flags, offsetof(readwrite_handlers, read_byte_signed));
+ sprintf(buffer, "%s.read_byte_signed:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_byte_signed, drc->cache_top);
+
+ handlers->read_byte_unsigned = drc->cache_top;
+ append_readwrite_and_translate(drc, 1, ARW_READ | ARW_UNSIGNED | flags, offsetof(readwrite_handlers, read_byte_unsigned));
+ sprintf(buffer, "%s.read_byte_unsigned:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_byte_unsigned, drc->cache_top);
+
+ handlers->read_half_signed = drc->cache_top;
+ append_readwrite_and_translate(drc, 2, ARW_READ | ARW_SIGNED | flags, offsetof(readwrite_handlers, read_half_signed));
+ sprintf(buffer, "%s.read_half_signed:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_half_signed, drc->cache_top);
+
+ handlers->read_half_unsigned = drc->cache_top;
+ append_readwrite_and_translate(drc, 2, ARW_READ | ARW_UNSIGNED | flags, offsetof(readwrite_handlers, read_half_unsigned));
+ sprintf(buffer, "%s.read_half_unsigned:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_half_unsigned, drc->cache_top);
+
+ handlers->read_word_signed = drc->cache_top;
+ append_readwrite_and_translate(drc, 4, ARW_READ | ARW_SIGNED | flags, offsetof(readwrite_handlers, read_word_signed));
+ sprintf(buffer, "%s.read_word_signed:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_word_signed, drc->cache_top);
+
+ handlers->read_word_unsigned = drc->cache_top;
+ append_readwrite_and_translate(drc, 4, ARW_READ | ARW_UNSIGNED | flags, offsetof(readwrite_handlers, read_word_unsigned));
+ sprintf(buffer, "%s.read_word_unsigned:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_word_unsigned, drc->cache_top);
+
+ handlers->read_word_masked = drc->cache_top;
+ append_readwrite_and_translate(drc, 4, ARW_READ | ARW_UNSIGNED | ARW_MASKED | flags, offsetof(readwrite_handlers, read_word_masked));
+ sprintf(buffer, "%s.read_word_masked:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_word_masked, drc->cache_top);
+
+ handlers->read_double = drc->cache_top;
+ append_readwrite_and_translate(drc, 8, ARW_READ | flags, offsetof(readwrite_handlers, read_double));
+ sprintf(buffer, "%s.read_double:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_double, drc->cache_top);
+
+ handlers->read_double_masked = drc->cache_top;
+ append_readwrite_and_translate(drc, 8, ARW_READ | ARW_MASKED | flags, offsetof(readwrite_handlers, read_double_masked));
+ sprintf(buffer, "%s.read_double_masked:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->read_double_masked, drc->cache_top);
+
+ handlers->write_byte = drc->cache_top;
+ append_readwrite_and_translate(drc, 1, ARW_WRITE | flags, offsetof(readwrite_handlers, write_byte));
+ sprintf(buffer, "%s.write_byte:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->write_byte, drc->cache_top);
+
+ handlers->write_half = drc->cache_top;
+ append_readwrite_and_translate(drc, 2, ARW_WRITE | flags, offsetof(readwrite_handlers, write_half));
+ sprintf(buffer, "%s.write_half:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->write_half, drc->cache_top);
+
+ handlers->write_word = drc->cache_top;
+ append_readwrite_and_translate(drc, 4, ARW_WRITE | flags, offsetof(readwrite_handlers, write_word));
+ sprintf(buffer, "%s.write_word:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->write_word, drc->cache_top);
+
+ handlers->write_word_masked = drc->cache_top;
+ append_readwrite_and_translate(drc, 4, ARW_WRITE | ARW_MASKED | flags, offsetof(readwrite_handlers, write_word_masked));
+ sprintf(buffer, "%s.write_word_masked:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->write_word_masked, drc->cache_top);
+
+ handlers->write_double = drc->cache_top;
+ append_readwrite_and_translate(drc, 8, ARW_WRITE | flags, offsetof(readwrite_handlers, write_double));
+ sprintf(buffer, "%s.write_double:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->write_double, drc->cache_top);
+
+ handlers->write_double_masked = drc->cache_top;
+ append_readwrite_and_translate(drc, 8, ARW_WRITE | ARW_MASKED | flags, offsetof(readwrite_handlers, write_double_masked));
+ sprintf(buffer, "%s.write_double_masked:", type);
+ if (LOG_CODE)
+ x86log_disasm_code_range(mips3.log, buffer, handlers->write_double_masked, drc->cache_top);
+}
+
+
+/*------------------------------------------------------------------
+ append_generate_exception
+------------------------------------------------------------------*/
+
+static void append_generate_exception(drc_core *drc, UINT8 exception)
+{
+ UINT32 offset = (exception >= EXCEPTION_TLBMOD && exception <= EXCEPTION_TLBSTORE) ? 0x00 : 0x180;
+ emit_link link1, link2;
+
+ if (exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE)
+ {
+ /* assumes failed address is in P2 */
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_BadVAddr), REG_P2); // mov [BadVAddr],p2
+ emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_EntryHi), 0x000000ff); // and [EntryHi],0x000000ff
+ emit_and_r32_imm(DRCTOP, REG_P2, 0xffffe000); // and p2,0xffffe000
+ emit_or_m32_r32(DRCTOP, CPR0ADDR(COP0_EntryHi), REG_P2); // or [EntryHi],p2
+ emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Context), 0xff800000); // and [Context],0xff800000
+ emit_shr_r32_imm(DRCTOP, REG_P2, 9); // shr p2,9
+ emit_or_m32_r32(DRCTOP, CPR0ADDR(COP0_Context), REG_P2); // or [Context],p2
+ }
+
+ /* on entry, exception PC must be in REG_P1, and low bit set if in a delay slot */
+ emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Cause)); // mov eax,[Cause]
+ emit_and_r32_imm(DRCTOP, REG_EAX, ~0x800000ff); // and eax,~0x800000ff
+ emit_test_r32_imm(DRCTOP, REG_P1, 1); // test p1,1
+ emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip
+ emit_or_r32_imm(DRCTOP, REG_EAX, 0x80000000); // or eax,0x80000000
+ emit_sub_r32_imm(DRCTOP, REG_P1, 5); // sub p1,5
+
+ resolve_link(DRCTOP, &link1); // skip:
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_EPC), REG_P1); // mov [EPC],p1
+ if (exception)
+ emit_or_r32_imm(DRCTOP, REG_EAX, exception << 2); // or eax,exception << 2
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Cause), REG_EAX); // mov [Cause],eax
+ emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // mov eax,[SR]
+ emit_or_r32_imm(DRCTOP, REG_EAX, SR_EXL); // or eax,SR_EXL
+ emit_test_r32_imm(DRCTOP, REG_EAX, SR_BEV); // test eax,SR_BEV
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Status), REG_EAX); // mov [SR],eax
+ emit_mov_r32_imm(DRCTOP, REG_P1, 0xbfc00200 + offset); // mov p1,0xbfc00200+offset
+ emit_jcc_short_link(DRCTOP, COND_NZ, &link2); // jnz skip2
+ emit_mov_r32_imm(DRCTOP, REG_P1, 0x80000000 + offset); // mov p1,0x80000000+offset
+
+ resolve_link(DRCTOP, &link2); // skip2:
+ if (PRINTF_EXCEPTIONS)
+ {
+ emit_mov_m32_r32(DRCTOP, PCADDR, REG_P1); // mov [PC],p1
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)exception_trap); // mov rax,exception_trap
+ emit_call_r64(DRCTOP, REG_RAX); // call rax
+ emit_mov_r32_m32(DRCTOP, REG_P1, PCADDR); // mov p1,[PC]
+ }
+ drc_append_dispatcher(drc); // dispatch
+}
+
+
+/*------------------------------------------------------------------
+ append_handle_pc_tlb_mismatch
+------------------------------------------------------------------*/
+
+static void append_handle_pc_tlb_mismatch(drc_core *drc)
+{
+#if 0
+ emit_mov_r32_r32(DRCTOP, REG_EAX, REG_R15D); // mov eax,edi
+ emit_shr_r32_imm(DRCTOP, REG_EAX, 12); // shr eax,12
+ emit_mov_r32_m32(DRCTOP, REG_EDX, MISD(REG_EAX, 4, &mips3.core->tlb_table)); // mov edx,tlb_table[eax*4]
+ emit_test_r32_imm(DRCTOP, REG_EDX, 2); // test edx,2
+ emit_mov_r32_r32(DRCTOP, REG_EAX, REG_R15D); // mov eax,edi
+ emit_jcc(DRCTOP, COND_NZ, mips3.drcdata->generate_tlbload_exception); // jnz generate_tlbload_exception
+ emit_mov_m32bd_r32(DRCTOP, drc->pcptr, REG_R15D); // mov [pcptr],edi
+ emit_jmp(DRCTOP, drc->recompile); // call recompile
+#endif
+}
+
+
+/*------------------------------------------------------------------
+ append_find_exception_handler
+------------------------------------------------------------------*/
+
+static void append_find_exception_handler(drc_core *drc)
+{
+ x86code *loop;
+
+ emit_mov_r64_m64(DRCTOP, REG_RAX, MBD(REG_RSP, 0)); // mov rax,[rsp]
+ emit_mov_r64_imm(DRCTOP, REG_V5, EXCEPTION_ADDRESS_COOKIE); // mov v5,EXCEPTION_ADDRESS_COOKIE
+ emit_mov_r64_r64(DRCTOP, REG_V6, REG_RAX); // mov v6,rax
+ emit_and_r64_imm(DRCTOP, REG_RAX, ~7); // and rax,~7
+ emit_add_r64_imm(DRCTOP, REG_RSP, 8); // add rsp,8
+ emit_add_r64_imm(DRCTOP, REG_RAX, 8); // add rax,8
+ loop = drc->cache_top; // loop:
+ emit_cmp_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_V5); // cmp [rax],v5
+ emit_lea_r64_m64(DRCTOP, REG_RAX, MBD(REG_RAX, 8)); // lea rax,[rax+8]
+ emit_jcc(DRCTOP, COND_NE, loop); // jne loop
+ emit_cmp_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_V6); // cmp [rax],v6
+ emit_lea_r64_m64(DRCTOP, REG_RAX, MBD(REG_RAX, 8)); // lea rax,[rax+8]
+ emit_jcc(DRCTOP, COND_NE, loop); // jne loop
+ emit_jmp_r64(DRCTOP, REG_RAX); // jmp rax
+}
+
+
+/*------------------------------------------------------------------
+ append_direct_ram_read
+------------------------------------------------------------------*/
+
+static void append_direct_ram_read(drc_core *drc, int size, UINT8 flags, void *rambase, offs_t ramstart)
+{
+ void *adjbase = (UINT8 *)rambase - ramstart;
+ INT64 adjdisp = (UINT8 *)adjbase - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)adjdisp != adjdisp);
+
+ /* if we can't fit the delta to adjbase within an INT32, load RAX with the full address here */
+ if (raxrel)
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)adjbase);
+
+ /* fast RAM byte read */
+ if (size == 1)
+ {
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_xor_r32_imm(DRCTOP, REG_P1, 3); // xor p1,3
+
+ /* read rax-relative */
+ if (raxrel)
+ {
+ if (flags & ARW_SIGNED)
+ emit_movsx_r64_m8(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movsx rax,byte ptr [p1 + rax]
+ else
+ emit_movzx_r64_m8(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movzx rax,byte ptr [p1 + rax]
+ }
+
+ /* read drc-relative */
+ else
+ {
+ if (flags & ARW_SIGNED)
+ emit_movsx_r64_m8(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movsx rax,byte ptr [p1 + adjbase]
+ else
+ emit_movzx_r64_m8(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movzx rax,byte ptr [p1 + adjbase]
+ }
+ }
+
+ /* fast RAM halfword read */
+ else if (size == 2)
+ {
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_xor_r32_imm(DRCTOP, REG_P1, 2); // xor p1,2
+
+ /* read rax-relative */
+ if (raxrel)
+ {
+ if (flags & ARW_SIGNED)
+ emit_movsx_r64_m16(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movsx rax,word ptr [p1 + rax]
+ else
+ emit_movzx_r64_m16(DRCTOP, REG_EAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movzx rax,word ptr [p1 + rax]
+ }
+
+ /* read drc-relative */
+ else
+ {
+ if (flags & ARW_SIGNED)
+ emit_movsx_r64_m16(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movsx rax,word ptr [p1 + adjbase]
+ else
+ emit_movzx_r64_m16(DRCTOP, REG_EAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movzx rax,word ptr [p1 + adjbase]
+ }
+ }
+
+ /* fast RAM dword read */
+ else if (size == 4)
+ {
+ /* read rax-relative */
+ if (raxrel)
+ {
+ if (flags & ARW_SIGNED)
+ emit_movsxd_r64_m32(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movsxd rax,dword ptr [p1 + rax]
+ else
+ emit_mov_r32_m32(DRCTOP, REG_EAX, MBISD(REG_RAX, REG_P1, 1, 0)); // mov eax,word ptr [p1 + rax]
+ }
+
+ /* read drc-relative */
+ else
+ {
+ if (flags & ARW_SIGNED)
+ emit_movsxd_r64_m32(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movsxd rax,dword ptr [p1 + adjbase]
+ else
+ emit_mov_r32_m32(DRCTOP, REG_EAX, MDRCISD(adjbase, REG_P1, 1, 0)); // mov eax,word ptr [p1 + adjbase]
+ }
+ }
+
+ /* fast RAM qword read */
+ else if (size == 8)
+ {
+ /* read rax-relative */
+ if (raxrel)
+ emit_mov_r64_m64(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // mov rax,[p1 + rax]
+
+ /* read drc-relative */
+ else
+ emit_mov_r64_m64(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // mov rax,[p1 + adjbase]
+
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_ror_r64_imm(DRCTOP, REG_RAX, 32); // ror rax,32
+ }
+}
+
+
+/*------------------------------------------------------------------
+ append_direct_ram_write
+------------------------------------------------------------------*/
+
+static void append_direct_ram_write(drc_core *drc, int size, UINT8 flags, void *rambase, offs_t ramstart)
+{
+ void *adjbase = (UINT8 *)rambase - ramstart;
+ INT64 adjdisp = (UINT8 *)adjbase - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)adjdisp != adjdisp);
+
+ /* if we can't fit the delta to adjbase within an INT32, load RAX with the full address here */
+ if (raxrel)
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)adjbase);
+
+ /* fast RAM byte write */
+ if (size == 1)
+ {
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_xor_r32_imm(DRCTOP, REG_P1, 3); // xor p1,3
+
+ /* write either rax-relative or drc-relative */
+ if (raxrel)
+ emit_mov_m8_r8(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2
+ else
+ emit_mov_m8_r8(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2
+ }
+
+ /* fast RAM halfword write */
+ else if (size == 2)
+ {
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_xor_r32_imm(DRCTOP, REG_P1, 2); // xor p1,2
+
+ /* write either rax-relative or drc-relative */
+ if (raxrel)
+ emit_mov_m16_r16(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2
+ else
+ emit_mov_m16_r16(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2
+ }
+
+ /* fast RAM dword write */
+ else if (size == 4)
+ {
+ /* unmasked */
+ if (!(flags & ARW_MASKED))
+ {
+ if (raxrel)
+ emit_mov_m32_r32(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2
+ else
+ emit_mov_m32_r32(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2
+ }
+
+ /* masked */
+ else
+ {
+ emit_mov_r32_r32(DRCTOP, REG_P4, REG_P3); // mov p4,p3
+ if (raxrel)
+ emit_and_r32_m32(DRCTOP, REG_P3, MBISD(REG_RAX, REG_P1, 1, 0)); // and p3,[p1 + rax]
+ else
+ emit_and_r32_m32(DRCTOP, REG_P3, MDRCISD(adjbase, REG_P1, 1, 0)); // and p3,[p1 + adjbase]
+ emit_not_r32(DRCTOP, REG_P4); // not p4
+ emit_and_r32_r32(DRCTOP, REG_P4, REG_P2); // and p4,p2
+ emit_or_r32_r32(DRCTOP, REG_P4, REG_P3); // or p4,p3
+ if (raxrel)
+ emit_mov_m32_r32(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P4); // mov [p1 + rax],p4
+ else
+ emit_mov_m32_r32(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P4); // mov [p1 + adjbase],p4
+ }
+ }
+
+ /* fast RAM qword write */
+ else if (size == 8)
+ {
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_ror_r64_imm(DRCTOP, REG_P2, 32); // ror p2,32
+
+ /* unmasked */
+ if (!(flags & ARW_MASKED))
+ {
+ if (raxrel)
+ emit_mov_m64_r64(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2
+ else
+ emit_mov_m64_r64(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2
+ }
+
+ /* masked */
+ else
+ {
+ if (mips3.core->bigendian)
+ emit_ror_r64_imm(DRCTOP, REG_P3, 32); // ror p3,32
+ emit_mov_r64_r64(DRCTOP, REG_P4, REG_P3); // mov p4,p3
+ if (raxrel)
+ emit_and_r64_m64(DRCTOP, REG_P3, MBISD(REG_RAX, REG_P1, 1, 0)); // and p3,[p1 + rax]
+ else
+ emit_and_r64_m64(DRCTOP, REG_P3, MDRCISD(adjbase, REG_P1, 1, 0)); // and p3,[p1 + adjbase]
+ emit_not_r64(DRCTOP, REG_P4); // not p4
+ emit_and_r64_r64(DRCTOP, REG_P4, REG_P2); // and p4,p2
+ emit_or_r64_r64(DRCTOP, REG_P4, REG_P3); // or p4,p3
+ if (raxrel)
+ emit_mov_m64_r64(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P4); // mov [p1 + rax],p4
+ else
+ emit_mov_m64_r64(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P4); // mov [p1 + adjbase],p4
+ }
+ }
+}
+
+
+/*------------------------------------------------------------------
+ append_readwrite_and_translate
+------------------------------------------------------------------*/
+
+static void append_readwrite_and_translate(drc_core *drc, int size, UINT8 flags, UINT32 ptroffs)
+{
+ UINT32 base = ((flags & ARW_CACHED) ? 0x80000000 : (flags & ARW_UNCACHED) ? 0xa0000000 : 0);
+ emit_link page_fault = { 0 }, handle_kernel = { 0 }, wrong_handler = { 0 };
+ x86code *continue_mapped = NULL;
+ int ramnum;
+
+ /* if the base is 0, this is a general case which may be paged; perform the translation first */
+ /* the result of the translation produces the physical address inline in P1 */
+ /* if a page fault occurs, we have to keep an original copy of the address in EAX */
+ /* note that we use RAX and P4 as scratch registers */
+ if (base == 0)
+ {
+ emit_test_r32_r32(DRCTOP, REG_P1, REG_P1); // test p1,p1
+ emit_mov_r32_r32(DRCTOP, REG_P4, REG_P1); // mov p4,p1d
+ emit_jcc_near_link(DRCTOP, COND_S, &handle_kernel); // js handle_kernel
+ continue_mapped = drc->cache_top; // continue_mapped:
+ emit_mov_r32_r32(DRCTOP, REG_V5, REG_P1); // mov v5,p1
+ emit_shr_r32_imm(DRCTOP, REG_P1, 12); // shr p1,12
+ emit_mov_r32_m32(DRCTOP, REG_P1, MDRCISD(mips3.core->tlb_table, REG_P1, 4, 0)); // mov p1,tlb_table[p1*4]
+ emit_and_r32_imm(DRCTOP, REG_P4, 0xfff); // and p4,0xfff
+ emit_shr_r32_imm(DRCTOP, REG_P1, (flags & ARW_WRITE) ? 1 : 2); // shr p1,2/1 (read/write)
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(REG_P4, REG_P1, (flags & ARW_WRITE) ? 2 : 4, 0));
+ // lea p1,[p4 + p1 * 4/2 (read/write)]
+ emit_jcc_near_link(DRCTOP, COND_C, &page_fault); // jc page_fault
+ }
+
+ /* if the base is non-zero, this is a cached/uncached direct-mapped case; no translation necessary */
+ /* just subtract the base from the address to get the physcial address */
+ else
+ emit_sub_r32_imm(DRCTOP, REG_P1, base); // sub p1,base
+
+ /* iterate over all fast RAM ranges looking for matches that can be handled directly */
+ for (ramnum = 0; ramnum < MIPS3_MAX_FASTRAM; ramnum++)
+ {
+ const fast_ram_info *raminfo = &mips3.fastram[ramnum];
+ if (!COMPARE_AGAINST_C && !Machine->debug_mode && raminfo->base != NULL && (!(flags & ARW_WRITE) || !raminfo->readonly))
+ {
+ emit_link notram1 = { 0 }, notram2 = { 0 };
+
+ /* if we have an end, check it first */
+ if (raminfo->end != 0xffffffff)
+ {
+ emit_cmp_r32_imm(DRCTOP, REG_P1, raminfo->end); // cmp p1,fastram_end
+ emit_jcc_short_link(DRCTOP, COND_A, &notram1); // ja notram1
+ }
+
+ /* if we have a non-0 start, check it next */
+ if (raminfo->start != 0x00000000)
+ {
+ emit_cmp_r32_imm(DRCTOP, REG_P1, raminfo->start); // cmp p1,fastram_start
+ emit_jcc_short_link(DRCTOP, COND_B, &notram2); // jb notram2
+ }
+
+ /* handle a fast RAM read */
+ if (!(flags & ARW_WRITE))
+ {
+ append_direct_ram_read(drc, size, flags, raminfo->base, raminfo->start); // <direct read>
+ emit_ret(DRCTOP); // ret
+ }
+
+ /* handle a fast RAM write */
+ else
+ {
+ append_direct_ram_write(drc, size, flags, raminfo->base, raminfo->start); // <direct write>
+ emit_ret(DRCTOP); // ret
+ }
+
+ /* resolve links that skipped over us */
+ if (raminfo->end != 0xffffffff)
+ resolve_link(DRCTOP, &notram1); // notram1:
+ if (raminfo->start != 0x00000000)
+ resolve_link(DRCTOP, &notram2); // notram2:
+ }
+ }
+
+ /* failed to find it in fast RAM; make sure we are within our expected range */
+ if (base != 0)
+ {
+ emit_cmp_r32_imm(DRCTOP, REG_P1, 0x20000000); // cmp p1,0x20000000
+ emit_jcc_short_link(DRCTOP, COND_AE, &wrong_handler); // jae wrong_handler
+ }
+
+ /* handle a write */
+ if (flags & ARW_WRITE)
+ {
+ if (size == 1)
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writebyte)); // jmp writebyte
+ else if (size == 2)
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writehalf)); // jmp writehalf
+ else if (size == 4)
+ {
+ if (!(flags & ARW_MASKED))
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writeword)); // jmp writeword
+ else
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writeword_masked)); // jmp writeword_masked
+ }
+ else
+ {
+ if (!(flags & ARW_MASKED))
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writedouble)); // jmp writedouble
+ else
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writedouble_masked)); // jmp writedouble_masked
+ }
+ }
+
+ /* handle a read */
+ else
+ {
+ if (size == 1)
+ {
+ emit_sub_r64_imm(DRCTOP, REG_RSP, 0x28); // sub rsp,0x28
+ emit_call_m64(DRCTOP, MDRC(&mips3.core->memory.readbyte)); // call readbyte
+ if (flags & ARW_SIGNED)
+ emit_movsx_r64_r8(DRCTOP, REG_RAX, REG_AL); // movsx rax,al
+ else
+ emit_movzx_r64_r8(DRCTOP, REG_RAX, REG_AL); // movzx rax,al
+ emit_add_r64_imm(DRCTOP, REG_RSP, 0x28); // add rsp,0x28
+ emit_ret(DRCTOP); // ret
+ }
+ else if (size == 2)
+ {
+ emit_sub_r64_imm(DRCTOP, REG_RSP, 0x28); // sub rsp,0x28
+ emit_call_m64(DRCTOP, MDRC(&mips3.core->memory.readhalf)); // call readhalf
+ if (flags & ARW_SIGNED)
+ emit_movsx_r64_r16(DRCTOP, REG_RAX, REG_AX); // movsx rax,ax
+ else
+ emit_movzx_r64_r16(DRCTOP, REG_RAX, REG_AX); // movzx rax,ax
+ emit_add_r64_imm(DRCTOP, REG_RSP, 0x28); // add rsp,0x28
+ emit_ret(DRCTOP); // ret
+ }
+ else if (size == 4)
+ {
+ if (!(flags & ARW_MASKED))
+ {
+ if (!(flags & ARW_SIGNED))
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readword)); // jmp readword
+ else
+ {
+ emit_sub_r64_imm(DRCTOP, REG_RSP, 0x28); // sub rsp,0x28
+ emit_call_m64(DRCTOP, MDRC(&mips3.core->memory.readword)); // call readword
+ emit_movsxd_r64_r32(DRCTOP, REG_RAX, REG_EAX); // movsxd rax,eax
+ emit_add_r64_imm(DRCTOP, REG_RSP, 0x28); // add rsp,0x28
+ emit_ret(DRCTOP); // ret
+ }
+ }
+ else
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readword_masked)); // jmp readword_masked
+ }
+ else
+ {
+ if (!(flags & ARW_MASKED))
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readdouble)); // jmp readdouble
+ else
+ emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readdouble_masked)); // jmp readdouble_masked
+ }
+ }
+
+ /* handle wrong handlers */
+ if (base == 0)
+ {
+ resolve_link(DRCTOP, &handle_kernel); // handle_kernel:
+ emit_cmp_r32_imm(DRCTOP, REG_P1, 0xc0000000); // cmp p1,0xc0000000
+ emit_jcc(DRCTOP, COND_AE, continue_mapped); // jae continue_mapped
+ emit_test_r32_imm(DRCTOP, REG_P1, 0x20000000); // test p1,0x20000000
+ emit_cmovcc_r64_m64(DRCTOP, COND_Z, REG_RAX, MDRCD(&mips3.drcdata->cached, ptroffs));
+ // mov rax,<cached version>
+ emit_cmovcc_r64_m64(DRCTOP, COND_NZ, REG_RAX, MDRCD(&mips3.drcdata->uncached, ptroffs));
+ // mov rax,<uncached version>
+ emit_mov_r64_m64(DRCTOP, REG_P4, MBD(REG_RSP, 0)); // mov p4,[rsp]
+ emit_push_r64(DRCTOP, REG_RAX); // push rax
+ emit_sub_r64_r64(DRCTOP, REG_RAX, REG_P4); // sub rax,p4
+ emit_mov_m32_r32(DRCTOP, MBD(REG_P4, -4), REG_EAX); // mov [p4-4],eax
+ emit_ret(DRCTOP); // ret
+ }
+ else
+ {
+ resolve_link(DRCTOP, &wrong_handler); // wrong_handler:
+ emit_mov_r64_m64(DRCTOP, REG_RAX, MDRCD(&mips3.drcdata->general, ptroffs));
+ // mov rax,<general version>
+ emit_mov_r64_m64(DRCTOP, REG_P4, MBD(REG_RSP, 0)); // mov p4,[rsp]
+ emit_push_r64(DRCTOP, REG_RAX); // push rax
+ emit_sub_r64_r64(DRCTOP, REG_RAX, REG_P4); // sub rax,p4
+ emit_mov_m32_r32(DRCTOP, MBD(REG_P4, -4), REG_EAX); // mov [p4-4],eax
+ emit_add_r32_imm(DRCTOP, REG_P1, base); // add p1,base
+ emit_ret(DRCTOP); // ret
+ }
+
+ /* handle page faults */
+ if (base == 0)
+ {
+ resolve_link(DRCTOP, &page_fault); // page_fault:
+ emit_mov_r32_r32(DRCTOP, REG_P2, REG_V5); // mov p2,v5
+ emit_jmp(DRCTOP, mips3.drcdata->find_exception_handler); // jmp find_exception_handler
+ }
+}
+
+
+/*------------------------------------------------------------------
+ append_explode_ccr31
+------------------------------------------------------------------*/
+
+static void append_explode_ccr31(drc_core *drc)
+{
+ emit_link link1;
+ int i;
+
+ /* new CCR31 value in EAX */
+ emit_test_r32_imm(DRCTOP, REG_EAX, 1 << 23); // test eax,1 << 23
+ emit_mov_m32_r32(DRCTOP, CCR1ADDR(31), REG_EAX); // mov ccr[31],eax
+ emit_setcc_m8(DRCTOP, COND_NZ, CF1ADDR(0)); // setnz fcc[0]
+ if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV)
+ for (i = 1; i <= 7; i++)
+ {
+ emit_test_r32_imm(DRCTOP, REG_EAX, 1 << (24 + i)); // test eax,1 << (24+i)
+ emit_setcc_m8(DRCTOP, COND_NZ, CF1ADDR(i)); // setnz fcc[i]
+ }
+ emit_and_r32_imm(DRCTOP, REG_EAX, 3); // and eax,3
+ emit_test_r32_imm(DRCTOP, REG_EAX, 1); // test eax,1
+ emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip
+ emit_xor_r32_imm(DRCTOP, REG_EAX, 2); // xor eax,2
+ resolve_link(DRCTOP, &link1); // skip:
+ drc_append_set_sse_rounding(drc, REG_EAX); // set_rounding(EAX)
+ emit_ret(DRCTOP); // ret
+}
+
+
+/*------------------------------------------------------------------
+ append_recover_ccr31
+------------------------------------------------------------------*/
+
+static void append_recover_ccr31(drc_core *drc)
+{
+ int i;
+
+ emit_movsxd_r64_m32(DRCTOP, REG_RAX, CCR1ADDR(31)); // movsxd rax,ccr1[rd]
+ emit_and_r64_imm(DRCTOP, REG_RAX, ~0xfe800000); // and rax,~0xfe800000
+ emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx
+ emit_cmp_m8_imm(DRCTOP, CF1ADDR(0), 0); // cmp fcc[0],0
+ emit_setcc_r8(DRCTOP, COND_NZ, REG_DL); // setnz dl
+ emit_shl_r32_imm(DRCTOP, REG_EDX, 23); // shl edx,23
+ emit_or_r64_r64(DRCTOP, REG_RAX, REG_RDX); // or rax,rdx
+ if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV)
+ for (i = 1; i <= 7; i++)
+ {
+ emit_cmp_m8_imm(DRCTOP, CF1ADDR(i), 0); // cmp fcc[i],0
+ emit_setcc_r8(DRCTOP, COND_NZ, REG_DL); // setnz dl
+ emit_shl_r32_imm(DRCTOP, REG_EDX, 24+i); // shl edx,24+i
+ emit_or_r64_r64(DRCTOP, REG_RAX, REG_RDX); // or rax,rdx
+ }
+ emit_ret(DRCTOP); // ret
+}
+
+
+/*------------------------------------------------------------------
+ append_check_interrupts
+------------------------------------------------------------------*/
+
+static void append_check_interrupts(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ emit_link link1, link2;
+
+ emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Cause)); // mov eax,[Cause]
+ emit_and_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // and eax,[Status]
+ emit_and_r32_imm(DRCTOP, REG_EAX, 0xfc00); // and eax,0xfc00
+ emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip
+ emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_IE); // test [Status],SR_IE
+ emit_jcc_short_link(DRCTOP, COND_Z, &link2); // jz skip
+ emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_EXL | SR_ERL); // test [Status],SR_EXL | SR_ERL
+ if (desc == NULL)
+ {
+ emit_mov_r32_m32(DRCTOP, REG_P1, MDRC(drc->pcptr)); // mov p1,[pc]
+ emit_jcc(DRCTOP, COND_Z, mips3.drcdata->generate_interrupt_exception); // jz generate_interrupt_exception
+ }
+ else
+ oob_request_callback(drc, COND_Z, oob_interrupt_cleanup, compiler, desc, mips3.drcdata->generate_interrupt_exception);
+ resolve_link(DRCTOP, &link1); // skip:
+ resolve_link(DRCTOP, &link2);
+}
+
+
+
+/***************************************************************************
+ RECOMPILATION HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ emit_load_register_value - load a register
+ either as a constant or as a register
+-------------------------------------------------*/
+
+static void emit_load_register_value(drc_core *drc, compiler_state *compiler, UINT8 dstreg, mipsreg_state regstate)
+{
+ /* must be live or constant */
+ assert(ISCONST(regstate) || ISLIVE(regstate));
+
+ /* if the register is constant, do an immediate load */
+ if (ISCONST(regstate))
+ {
+ INT32 mipsval = CONSTVAL(regstate);
+ if (mipsval != 0)
+ emit_mov_r64_imm(DRCTOP, dstreg, mipsval);
+ else
+ emit_xor_r32_r32(DRCTOP, dstreg, dstreg);
+ }
+
+ /* otherwise, load from the allocated register, but only if different */
+ else
+ {
+ UINT8 srcreg = X86REG(regstate);
+ if (dstreg != srcreg)
+ emit_mov_r64_r64(DRCTOP, dstreg, srcreg);
+ }
+}
+
+
+/*-------------------------------------------------
+ emit_set_constant_value - set a constant
+ value; if it is too large, allocate a
+ register and populate it
+-------------------------------------------------*/
+
+static void emit_set_constant_value(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 mipsreg, UINT64 constval)
+{
+ /* ignore reigster 0 destination */
+ if (mipsreg == 0)
+ return;
+
+ /* if we fit in 32-bits, just continue the constant charade */
+ if ((INT32)constval == (INT64)constval)
+ compiler_register_set_constant(compiler, mipsreg, constval);
+
+ /* otherwise, allocate a new register and move the result into it */
+ else
+ {
+ mipsreg_state resultreg = compiler_register_allocate(drc, compiler, desc, mipsreg, ALLOC_DST | ALLOC_DIRTY);
+ emit_mov_r64_imm(DRCTOP, X86REG(resultreg), constval); // mov <rt>,result
+ }
+}
+
+
+/*------------------------------------------------------------------
+ emit_flush_cycles_before_instruction
+------------------------------------------------------------------*/
+
+static void emit_flush_cycles_before_instruction(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT32 cycles_to_leave)
+{
+ /* if we have cycles, subtract them now */
+ if (compiler->cycles - cycles_to_leave != 0)
+ emit_sub_m32_imm(DRCTOP, ICOUNTADDR, compiler->cycles - cycles_to_leave); // sub [icount],cycles
+
+ /* clear out compiler counts */
+ compiler->cycles = cycles_to_leave;
+}
+
+
+/*------------------------------------------------------------------
+ emit_update_cycles_pc_and_flush
+------------------------------------------------------------------*/
+
+static void emit_update_cycles_pc_and_flush(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, offs_t destpc)
+{
+ /* if we have cycles, subtract them now */
+ emit_sub_m32_imm(DRCTOP, ICOUNTADDR, compiler->cycles); // sub [icount],cycles
+
+ /* clear out compiler counts */
+ compiler->cycles = 0;
+
+ /* if we have an opcode descriptor, make this an oob */
+ if (desc != NULL)
+ oob_request_callback(drc, COND_S, oob_interrupt_cleanup, compiler, desc, mips3.drc->exit_point);
+
+ /* otherwise, do it inline */
+ else
+ {
+ /* load the target PC into P1 */
+ if (destpc != BRANCH_TARGET_DYNAMIC)
+ emit_mov_r32_imm(DRCTOP, REG_P1, destpc); // mov p1,destpc
+ else
+ emit_mov_r32_m32(DRCTOP, REG_P1, MBD(REG_RSP, SPOFFS_NEXTPC)); // mov p1,[esp+nextpc]
+
+ /* flush any live registers */
+ compiler_register_flush_all(drc, compiler);
+
+ /* if we subtracted cycles and went negative, time to exit */
+ emit_jcc(DRCTOP, COND_S, mips3.drc->exit_point); // js exit_point
+ }
+}
+
+
+/*------------------------------------------------------------------
+ emit_delay_slot_and_branch
+------------------------------------------------------------------*/
+
+static void emit_delay_slot_and_branch(drc_core *drc, const compiler_state *compiler, const opcode_desc *desc, UINT8 linkreg)
+{
+ compiler_state compiler_temp = *compiler;
+
+ /* set the link if needed */
+ if (linkreg != 0)
+ compiler_register_set_constant(&compiler_temp, linkreg, (INT32)(desc->pc + 8)); // <link> = pc + 8
+
+ /* compile the delay slot using temporary compiler state */
+ assert(desc->delay != NULL);
+ compile_one(drc, &compiler_temp, desc->delay); // <next instruction>
+
+ /* update the cycles and load the new PC in case we need to stop here */
+ emit_update_cycles_pc_and_flush(drc, &compiler_temp, NULL, desc->targetpc);
+
+ /* otherwise, append a dispatcher */
+ if (desc->targetpc != BRANCH_TARGET_DYNAMIC)
+ drc_append_tentative_fixed_dispatcher(drc, desc->targetpc, FALSE); // <redispatch>
+ else
+ drc_append_dispatcher(drc); // <redispatch>
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_byte_read
+------------------------------------------------------------------*/
+
+static int emit_fixed_byte_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, int is_unsigned, offs_t address)
+{
+ void *fastram = fastram_ptr(address, 1, FALSE);
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address); // mov p1,address
+ if (is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_byte_unsigned); // call read_byte_unsigned
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_byte_signed); // call read_byte_signed
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ if (destreg != REG_RAX)
+ emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (is_unsigned)
+ emit_movzx_r64_m8(DRCTOP, destreg, MBD(REG_RAX, 0)); // movzx destreg,byte ptr [rax]
+ else
+ emit_movsx_r64_m8(DRCTOP, destreg, MBD(REG_RAX, 0)); // movsx destreg,byte ptr [rax]
+ }
+
+ /* read drc-relative */
+ else
+ {
+ if (is_unsigned)
+ emit_movzx_r64_m8(DRCTOP, destreg, MDRC(fastram)); // movzx destreg,byte ptr [p1 + adjbase]
+ else
+ emit_movsx_r64_m8(DRCTOP, destreg, MDRC(fastram)); // movsx destreg,byte ptr [p1 + adjbase]
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_half_read
+------------------------------------------------------------------*/
+
+static int emit_fixed_half_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, int is_unsigned, offs_t address)
+{
+ void *fastram = fastram_ptr(address & ~1, 2, FALSE);
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address & ~1); // mov p1,address & ~1
+ if (is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_half_unsigned); // call read_half_unsigned
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_half_signed); // call read_half_signed
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ if (destreg != REG_RAX)
+ emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* read rax-relative */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (is_unsigned)
+ emit_movzx_r64_m16(DRCTOP, destreg, MBD(REG_RAX, 0)); // movzx destreg,word ptr [rax]
+ else
+ emit_movsx_r64_m16(DRCTOP, destreg, MBD(REG_RAX, 0)); // movsx destreg,word ptr [rax]
+ }
+
+ /* read drc-relative */
+ else
+ {
+ if (is_unsigned)
+ emit_movzx_r64_m16(DRCTOP, destreg, MDRC(fastram)); // movzx destreg,word ptr [p1 + adjbase]
+ else
+ emit_movsx_r64_m16(DRCTOP, destreg, MDRC(fastram)); // movsx destreg,word ptr [p1 + adjbase]
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_word_read
+------------------------------------------------------------------*/
+
+static int emit_fixed_word_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, int is_unsigned, offs_t address, UINT32 mask)
+{
+ void *fastram = fastram_ptr(address & ~3, 4, FALSE);
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address & ~3); // mov p1,address & ~3
+ if (mask != 0)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P2, (INT32)mask); // mov p2,mask
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_masked); // call read_word_masked
+ }
+ else if (is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call read_word_unsigned
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_signed); // call read_word_signed
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ if (destreg != REG_RAX)
+ emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (is_unsigned)
+ emit_mov_r32_m32(DRCTOP, destreg, MBD(REG_RAX, 0)); // mov destreg,word ptr [rax]
+ else
+ emit_movsxd_r64_m32(DRCTOP, destreg, MBD(REG_RAX, 0)); // movsxd destreg,dword ptr [rax]
+ }
+
+ /* read drc-relative */
+ else
+ {
+ if (is_unsigned)
+ emit_mov_r32_m32(DRCTOP, destreg, MDRC(fastram)); // mov destreg,word ptr [p1 + adjbase]
+ else
+ emit_movsxd_r64_m32(DRCTOP, destreg, MDRC(fastram)); // movsxd destreg,dword ptr [p1 + adjbase]
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_double_read
+------------------------------------------------------------------*/
+
+static int emit_fixed_double_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, offs_t address, UINT64 mask)
+{
+ void *fastram = fastram_ptr(address & ~7, 8, FALSE);
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address & ~7); // mov p1,address & ~7
+ if (mask != 0)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_P2, mask); // mov p2,mask
+ emit_call(DRCTOP, mips3.drcdata->general.read_double_masked); // call read_double_masked
+ }
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_double); // call read_double
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ if (destreg != REG_RAX)
+ emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ emit_mov_r64_m64(DRCTOP, destreg, MBD(REG_RAX, 0)); // mov destreg,[rax]
+ }
+
+ /* read drc-relative */
+ else
+ emit_mov_r64_m64(DRCTOP, destreg, MDRC(fastram)); // mov destreg,[p1 + adjbase]
+
+ /* adjust for endianness */
+ if (mips3.core->bigendian)
+ emit_ror_r64_imm(DRCTOP, destreg, 32); // ror destreg,32
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_byte_write
+------------------------------------------------------------------*/
+
+static int emit_fixed_byte_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address)
+{
+ void *fastram = fastram_ptr(address, 1, TRUE);
+
+ assert(ISCONST(srcreg) || X86REG(srcreg != REG_P1));
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address); // mov p1,address
+ if (ISCONST(srcreg))
+ emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,<srcreg>
+ else if (X86REG(srcreg) != REG_P2)
+ emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,<srcreg>
+ emit_call(DRCTOP, mips3.drcdata->general.write_byte); // call write_byte
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (ISCONST(srcreg))
+ emit_mov_m8_imm(DRCTOP, MBD(REG_RAX, 0), CONSTVAL(srcreg)); // mov [rax],constval
+ else
+ emit_mov_m8_r8(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],<srcreg>
+ }
+
+ /* write drc-relative */
+ else
+ {
+ if (ISCONST(srcreg))
+ emit_mov_m8_imm(DRCTOP, MDRC(fastram), CONSTVAL(srcreg)); // mov [fastram],constval
+ else
+ emit_mov_m8_r8(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_half_write
+------------------------------------------------------------------*/
+
+static int emit_fixed_half_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address)
+{
+ void *fastram = fastram_ptr(address & ~1, 2, TRUE);
+
+ assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P1);
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address & ~1); // mov p1,address & ~1
+ if (ISCONST(srcreg))
+ emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,constval
+ else if (X86REG(srcreg) != REG_P2)
+ emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg
+ emit_call(DRCTOP, mips3.drcdata->general.write_half); // call write_half
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (ISCONST(srcreg))
+ emit_mov_m16_imm(DRCTOP, MBD(REG_RAX, 0), CONSTVAL(srcreg)); // mov [rax],constval
+ else
+ emit_mov_m16_r16(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],srcreg
+ }
+
+ /* write drc-relative */
+ else
+ {
+ if (ISCONST(srcreg))
+ emit_mov_m16_imm(DRCTOP, MDRC(fastram), CONSTVAL(srcreg)); // mov [fastram],constval
+ else
+ emit_mov_m16_r16(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_word_write
+------------------------------------------------------------------*/
+
+static int emit_fixed_word_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address, UINT32 mask)
+{
+ void *fastram = fastram_ptr(address & ~3, 4, TRUE);
+
+ assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P1);
+ assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P3);
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address & ~3); // mov p1,address & ~3
+ if (ISCONST(srcreg))
+ emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,constval
+ else if (X86REG(srcreg) != REG_P2)
+ emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg
+ if (mask == 0)
+ emit_call(DRCTOP, mips3.drcdata->general.write_word); // call write_word
+ else
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P3, mask); // mov p3,mask
+ emit_call(DRCTOP, mips3.drcdata->general.write_word_masked); // call write_word_masked
+ }
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+
+ /* non-masked case */
+ if (mask == 0)
+ {
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (ISCONST(srcreg))
+ emit_mov_m32_imm(DRCTOP, MBD(REG_RAX, 0), CONSTVAL(srcreg)); // mov [rax],constval
+ else
+ emit_mov_m32_r32(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],srcreg
+ }
+
+ /* write drc-relative */
+ else
+ {
+ if (ISCONST(srcreg))
+ emit_mov_m32_imm(DRCTOP, MDRC(fastram), CONSTVAL(srcreg)); // mov [fastram],constval
+ else
+ emit_mov_m32_r32(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg
+ }
+ }
+
+ /* masked case */
+ else
+ {
+ if (raxrel)
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ else
+ emit_lea_r64_m64(DRCTOP, REG_RAX, MDRC(fastram)); // lea rax,[fastram]
+
+ /* always write rax-relative */
+ if (ISCONST(srcreg))
+ {
+ emit_mov_r32_m32(DRCTOP, REG_P2, MBD(REG_RAX, 0)); // mov p2,[rax]
+ emit_and_r32_imm(DRCTOP, REG_P2, mask); // and p2,mask
+ emit_or_r32_imm(DRCTOP, REG_P2, CONSTVAL(srcreg) & ~mask); // or p2,constval & ~mask
+ emit_mov_m32_r32(DRCTOP, MBD(REG_RAX, 0), REG_P2); // mov [rax],p2
+ }
+ else
+ {
+ emit_mov_r32_m32(DRCTOP, REG_P3, MBD(REG_RAX, 0)); // mov p3,[rax]
+ if (X86REG(srcreg) != REG_P2)
+ emit_mov_r32_r32(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg
+ emit_and_r32_imm(DRCTOP, REG_P3, mask); // and p3,mask
+ emit_and_r32_imm(DRCTOP, REG_P2, ~mask); // and p2,~mask
+ emit_or_r32_r32(DRCTOP, REG_P3, REG_P2); // or p3,p2
+ emit_mov_m32_r32(DRCTOP, MBD(REG_RAX, 0), REG_P3); // mov [rax],p3
+ }
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+/*------------------------------------------------------------------
+ emit_fixed_double_write
+------------------------------------------------------------------*/
+
+static int emit_fixed_double_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address, UINT64 mask)
+{
+ void *fastram = fastram_ptr(address & ~7, 8, TRUE);
+
+ assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P1);
+ assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P3);
+ assert((INT32)CONSTVAL(srcreg) == (INT64)CONSTVAL(srcreg));
+
+ /* if we can't do fast RAM, we need to make the call */
+ if (fastram == NULL)
+ {
+ emit_mov_r32_imm(DRCTOP, REG_P1, address & ~7); // mov p1,address & ~7
+ if (ISCONST(srcreg))
+ emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,constval
+ else if (X86REG(srcreg) != REG_P2)
+ emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg
+ if (mask == 0)
+ emit_call(DRCTOP, mips3.drcdata->general.write_double); // call write_double
+ else
+ {
+ emit_mov_r64_imm(DRCTOP, REG_P3, mask); // mov p3,mask
+ emit_call(DRCTOP, mips3.drcdata->general.write_double_masked); // call write_double_masked
+ }
+
+ /* handle exceptions */
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* if we can do fastram, emit the right form */
+ else
+ {
+ INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr;
+ int raxrel = ((INT32)fastdisp != fastdisp);
+ INT64 constval = CONSTVAL(srcreg);
+
+ /* adjust values for endianness */
+ if (mips3.core->bigendian)
+ {
+ if (ISCONST(srcreg))
+ constval = (constval << 32) | (constval >> 32);
+ else
+ {
+ emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg
+ emit_ror_r64_imm(DRCTOP, REG_P2, 32); // ror p2,32
+ srcreg = REG_P2;
+ }
+ mask = (mask << 32) | (mask >> 32);
+ }
+
+ /* non-masked case */
+ if (mask == 0)
+ {
+ /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */
+ if (raxrel)
+ {
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ if (ISCONST(srcreg))
+ emit_mov_m64_imm(DRCTOP, MBD(REG_RAX, 0), constval); // mov [rax],constval
+ else
+ emit_mov_m64_r64(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],srcreg
+ }
+
+ /* write drc-relative */
+ else
+ {
+ if (ISCONST(srcreg))
+ emit_mov_m64_imm(DRCTOP, MDRC(fastram), constval); // mov [fastram],constval
+ else
+ emit_mov_m64_r64(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg
+ }
+ }
+
+ /* masked case */
+ else
+ {
+ if (raxrel)
+ emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram
+ else
+ emit_lea_r64_m64(DRCTOP, REG_RAX, MDRC(fastram)); // lea rax,[fastram]
+ emit_mov_r64_imm(DRCTOP, REG_P4, mask); // mov p4,mask
+
+ /* always write rax-relative */
+ if (ISCONST(srcreg))
+ {
+ emit_and_r64_m64(DRCTOP, REG_P4, MBD(REG_RAX, 0)); // and p4,[rax]
+ emit_mov_r64_imm(DRCTOP, REG_P3, constval & ~mask); // mov p3,constval & ~mask
+ emit_or_r64_r64(DRCTOP, REG_P4, REG_P3); // or p4,p3
+ emit_mov_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_P4); // mov [rax],p4
+ }
+ else
+ {
+ emit_mov_r64_m64(DRCTOP, REG_P3, MBD(REG_RAX, 0)); // mov p3,[rax]
+ if (X86REG(srcreg) != REG_P2)
+ emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg
+ emit_and_r64_r64(DRCTOP, REG_P3, REG_P4); // and p3,p4
+ emit_not_r64(DRCTOP, REG_P4); // not p4
+ emit_and_r64_r64(DRCTOP, REG_P2, REG_P4); // and p2,p4
+ emit_or_r64_r64(DRCTOP, REG_P3, REG_P2); // or p3,p2
+ emit_mov_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_P3); // mov [rax],p3
+ }
+ }
+ }
+ return (fastram != NULL);
+}
+
+
+
+/***************************************************************************
+ CORE RECOMPILATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ compile_instruction - master switch statement
+ for compiling a given opcode
+-------------------------------------------------*/
+
+static int compile_instruction(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 opswitch = op >> 26;
+
+ switch (opswitch)
+ {
+ /* ----- sub-groups ----- */
+
+ case 0x00: /* SPECIAL */
+ return compile_special(drc, compiler, desc);
+
+ case 0x01: /* REGIMM */
+ return compile_regimm(drc, compiler, desc);
+
+ case 0x1c: /* IDT-specific */
+ return compile_idt(drc, compiler, desc);
+
+
+ /* ----- jumps and branches ----- */
+
+ case 0x02: /* J */
+ case 0x03: /* JAL */
+ emit_delay_slot_and_branch(drc, compiler, desc, (opswitch == 0x02) ? 0 : 31); // <next instruction + redispatch>
+ return TRUE;
+
+ case 0x04: /* BEQ */
+ case 0x14: /* BEQL */
+ case 0x05: /* BNE */
+ case 0x15: /* BNEL */
+ return compile_branchcc(drc, compiler, desc, (opswitch & 1) ? COND_NE : COND_E);
+
+ case 0x06: /* BLEZ */
+ case 0x16: /* BLEZL */
+ case 0x07: /* BGTZ */
+ case 0x17: /* BGTZL */
+ return compile_branchcz(drc, compiler, desc, (opswitch & 1) ? COND_G : COND_LE, FALSE);
+
+
+ /* ----- immediate arithmetic ----- */
+
+ case 0x0f: /* LUI */
+ if (RTREG != 0)
+ emit_set_constant_value(drc, compiler, desc, RTREG, SIMMVAL << 16);
+ return TRUE;
+
+ case 0x08: /* ADDI */
+ case 0x09: /* ADDIU */
+ return compile_add(drc, compiler, desc, opswitch & 1, TRUE);
+
+ case 0x18: /* DADDI */
+ case 0x19: /* DADDIU */
+ return compile_dadd(drc, compiler, desc, opswitch & 1, TRUE);
+
+ case 0x0c: /* ANDI */
+ case 0x0d: /* ORI */
+ case 0x0e: /* XORI */
+ return compile_logical(drc, compiler, desc, opswitch & 3, TRUE);
+
+ case 0x0a: /* SLTI */
+ case 0x0b: /* SLTIU */
+ return compile_slt(drc, compiler, desc, opswitch & 1, TRUE);
+
+
+ /* ----- memory load operations ----- */
+
+ case 0x20: /* LB */
+ case 0x21: /* LH */
+ case 0x23: /* LW */
+ case 0x24: /* LBU */
+ case 0x25: /* LHU */
+ case 0x27: /* LWU */
+ return compile_load(drc, compiler, desc, (opswitch & 3) + 1, opswitch & 4);
+
+ case 0x37: /* LD */
+ return compile_load(drc, compiler, desc, 8, FALSE);
+
+ case 0x31: /* LWC1 */
+ return compile_load_cop(drc, compiler, desc, 4, FPR32(RTREG));
+
+ case 0x35: /* LDC1 */
+ return compile_load_cop(drc, compiler, desc, 8, FPR64(RTREG));
+
+ case 0x32: /* LWC2 */
+ case 0x36: /* LDC2 */
+ return compile_load_cop(drc, compiler, desc, (opswitch & 4) ? 8 : 4, &mips3.core->cpr[2][RTREG]);
+
+ case 0x1a: /* LDL */
+ case 0x1b: /* LDR */
+ return compile_load_double_partial(drc, compiler, desc, opswitch & 1);
+
+ case 0x22: /* LWL */
+ case 0x26: /* LWR */
+ return compile_load_word_partial(drc, compiler, desc, opswitch & 4);
+
+
+ /* ----- memory store operations ----- */
+
+ case 0x28: /* SB */
+ case 0x29: /* SH */
+ case 0x2b: /* SW */
+ return compile_store(drc, compiler, desc, (opswitch & 3) + 1);
+
+ case 0x3f: /* SD */
+ return compile_store(drc, compiler, desc, 8);
+
+ case 0x39: /* SWC1 */
+ return compile_store_cop(drc, compiler, desc, 4, FPR32(RTREG));
+
+ case 0x3d: /* SDC1 */
+ return compile_store_cop(drc, compiler, desc, 8, FPR64(RTREG));
+
+ case 0x3a: /* SWC2 */
+ return compile_store_cop(drc, compiler, desc, (opswitch & 4) ? 8 : 4, &mips3.core->cpr[2][RTREG]);
+
+ case 0x2a: /* SWL */
+ case 0x2e: /* SWR */
+ return compile_store_word_partial(drc, compiler, desc, opswitch & 4);
+
+ case 0x2c: /* SDL */
+ case 0x2d: /* SDR */
+ return compile_store_double_partial(drc, compiler, desc, opswitch & 1);
+
+
+ /* ----- effective no-ops ----- */
+
+ case 0x2f: /* CACHE */
+ case 0x33: /* PREF */
+ return TRUE;
+
+
+ /* ----- coprocessor instructions ----- */
+
+ case 0x10: /* COP0 */
+ return compile_cop0(drc, compiler, desc);
+
+ case 0x11: /* COP1 */
+ return compile_cop1(drc, compiler, desc);
+
+ case 0x13: /* COP1X - R5000 */
+ return compile_cop1x(drc, compiler, desc);
+
+ case 0x12: /* COP2 */
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_invalidop_exception);
+ // jmp generate_invalidop_exception
+ return TRUE;
+
+
+ /* ----- unimplemented/illegal instructions ----- */
+
+// case 0x30: /* LL */ logerror("mips3 Unhandled op: LL\n"); break;
+// case 0x34: /* LLD */ logerror("mips3 Unhandled op: LLD\n"); break;
+// case 0x38: /* SC */ logerror("mips3 Unhandled op: SC\n"); break;
+// case 0x3c: /* SCD */ logerror("mips3 Unhandled op: SCD\n"); break;
+// case 0x3b: /* SWC3 */ invalid_instruction(op); break;
+// default: /* ??? */ invalid_instruction(op); break;
+ }
+
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ compile_special - compile opcodes in the
+ 'SPECIAL' group
+-------------------------------------------------*/
+
+static int compile_special(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 opswitch = op & 63;
+ emit_link link1;
+
+ switch (opswitch)
+ {
+ /* ----- shift instructions ----- */
+
+ case 0x00: /* SLL - MIPS I */
+ case 0x02: /* SRL - MIPS I */
+ case 0x03: /* SRA - MIPS I */
+ case 0x04: /* SLLV - MIPS I */
+ case 0x06: /* SRLV - MIPS I */
+ case 0x07: /* SRAV - MIPS I */
+ return compile_shift(drc, compiler, desc, opswitch & 0x03, opswitch & 0x04);
+
+ case 0x14: /* DSLLV - MIPS III */
+ case 0x16: /* DSRLV - MIPS III */
+ case 0x17: /* DSRAV - MIPS III */
+ case 0x38: /* DSLL - MIPS III */
+ case 0x3a: /* DSRL - MIPS III */
+ case 0x3b: /* DSRA - MIPS III */
+ case 0x3c: /* DSLL32 - MIPS III */
+ case 0x3e: /* DSRL32 - MIPS III */
+ case 0x3f: /* DSRA32 - MIPS III */
+ return compile_dshift(drc, compiler, desc, opswitch & 0x03, ~opswitch & 0x20, opswitch & 0x04);
+
+
+ /* ----- basic arithmetic ----- */
+
+ case 0x20: /* ADD - MIPS I */
+ case 0x21: /* ADDU - MIPS I */
+ return compile_add(drc, compiler, desc, opswitch & 1, FALSE);
+
+ case 0x2c: /* DADD - MIPS III */
+ case 0x2d: /* DADDU - MIPS III */
+ return compile_dadd(drc, compiler, desc, opswitch & 1, FALSE);
+
+ case 0x22: /* SUB - MIPS I */
+ case 0x23: /* SUBU - MIPS I */
+ return compile_sub(drc, compiler, desc, opswitch & 1);
+
+ case 0x2e: /* DSUB - MIPS III */
+ case 0x2f: /* DSUBU - MIPS III */
+ return compile_dsub(drc, compiler, desc, opswitch & 1);
+
+ case 0x18: /* MULT - MIPS I */
+ case 0x19: /* MULTU - MIPS I */
+ return compile_mult(drc, compiler, desc, opswitch & 1);
+
+ case 0x1c: /* DMULT - MIPS III */
+ case 0x1d: /* DMULTU - MIPS III */
+ return compile_dmult(drc, compiler, desc, opswitch & 1);
+
+ case 0x1a: /* DIV - MIPS I */
+ case 0x1b: /* DIVU - MIPS I */
+ return compile_div(drc, compiler, desc, opswitch & 1);
+
+ case 0x1e: /* DDIV - MIPS III */
+ case 0x1f: /* DDIVU - MIPS III */
+ return compile_ddiv(drc, compiler, desc, opswitch & 1);
+
+
+ /* ----- basic logical ops ----- */
+
+ case 0x24: /* AND - MIPS I */
+ case 0x25: /* OR - MIPS I */
+ case 0x26: /* XOR - MIPS I */
+ case 0x27: /* NOR - MIPS I */
+ return compile_logical(drc, compiler, desc, opswitch & 3, FALSE);
+
+
+ /* ----- basic comparisons ----- */
+
+ case 0x2a: /* SLT - MIPS I */
+ case 0x2b: /* SLTU - MIPS I */
+ return compile_slt(drc, compiler, desc, opswitch & 1, FALSE);
+
+
+ /* ----- conditional traps ----- */
+
+ case 0x30: /* TGE - MIPS II */
+ case 0x31: /* TGEU - MIPS II */
+ case 0x32: /* TLT - MIPS II */
+ case 0x33: /* TLTU - MIPS II */
+ case 0x34: /* TEQ - MIPS II */
+ case 0x36: /* TNE - MIPS II */
+ return compile_trapcc(drc, compiler, desc, opswitch & 7, FALSE);
+
+
+ /* ----- conditional moves ----- */
+
+ case 0x0a: /* MOVZ - MIPS IV */
+ case 0x0b: /* MOVN - MIPS IV */
+ return compile_movzn(drc, compiler, desc, opswitch & 1);
+
+ case 0x01: /* MOVF - MIPS IV */
+ if (RDREG != 0)
+ {
+ mipsreg_state rs, rd;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY);
+
+ emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[x],0
+ emit_jcc_short_link(DRCTOP, ((op >> 16) & 1) ? COND_Z : COND_NZ, &link1); // jz/nz skip
+ emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs>
+ }
+ resolve_link(DRCTOP, &link1); // skip:
+ return TRUE;
+
+
+ /* ----- jumps and branches ----- */
+
+ case 0x08: /* JR - MIPS I */
+ case 0x09: /* JALR - MIPS I */
+ {
+ mipsreg_state rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ if (ISCONST(rs))
+ emit_mov_m32_imm(DRCTOP, MBD(REG_RSP, SPOFFS_NEXTPC), CONSTVAL(rs)); // mov [esp+nextpc],rs
+ else
+ emit_mov_m32_r32(DRCTOP, MBD(REG_RSP, SPOFFS_NEXTPC), X86REG(rs)); // mov [esp+nextpc],rs
+ emit_delay_slot_and_branch(drc, compiler, desc, (opswitch == 0x09) ? RDREG : 0);
+ // <next instruction + redispatch>
+ }
+ return TRUE;
+
+
+ /* ----- system calls ----- */
+
+ case 0x0c: /* SYSCALL - MIPS I */
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_syscall_exception);
+ // jmp generate_syscall_exception
+ return TRUE;
+
+ case 0x0d: /* BREAK - MIPS I */
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_break_exception);
+ // jmp generate_break_exception
+ return TRUE;
+
+
+ /* ----- effective no-ops ----- */
+
+ case 0x0f: /* SYNC - MIPS II */
+ return TRUE;
+
+
+ /* ----- hi/lo register access ----- */
+
+ case 0x10: /* MFHI - MIPS I */
+ if (RDREG != 0)
+ {
+ mipsreg_state hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_SRC);
+ mipsreg_state rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY);
+ emit_load_register_value(drc, compiler, X86REG(rd), hi); // mov <rd>,<hi>
+ }
+ return TRUE;
+
+ case 0x11: /* MTHI - MIPS I */
+ if (RSREG != 0)
+ {
+ mipsreg_state rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ mipsreg_state hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY);
+ emit_load_register_value(drc, compiler, X86REG(hi), rs); // mov hi,<rs>
+ }
+ return TRUE;
+
+ case 0x12: /* MFLO - MIPS I */
+ if (RDREG != 0)
+ {
+ mipsreg_state lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_SRC);
+ mipsreg_state rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY);
+ emit_load_register_value(drc, compiler, X86REG(rd), lo); // mov <rd>,<lo>
+ }
+ return TRUE;
+
+ case 0x13: /* MTLO - MIPS I */
+ if (RSREG != 0)
+ {
+ mipsreg_state rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ mipsreg_state lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY);
+ emit_load_register_value(drc, compiler, X86REG(lo), rs); // mov lo,<rs>
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ compile_regimm - compile opcodes in the
+ 'REGIMM' group
+-------------------------------------------------*/
+
+static int compile_regimm(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 opswitch = RTREG;
+
+ switch (opswitch)
+ {
+ case 0x00: /* BLTZ */
+ case 0x01: /* BGEZ */
+ case 0x02: /* BLTZL */
+ case 0x03: /* BGEZL */
+ case 0x10: /* BLTZAL */
+ case 0x11: /* BGEZAL */
+ case 0x12: /* BLTZALL */
+ case 0x13: /* BGEZALL */
+ return compile_branchcz(drc, compiler, desc, (opswitch & 1) ? COND_GE : COND_L, opswitch & 0x10);
+
+ case 0x08: /* TGEI */
+ case 0x09: /* TGEIU */
+ case 0x0a: /* TLTI */
+ case 0x0b: /* TLTIU */
+ case 0x0c: /* TEQI */
+ case 0x0e: /* TNEI */
+ return compile_trapcc(drc, compiler, desc, opswitch & 7, TRUE);
+ }
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ compile_idt - compile opcodes in the IDT-
+ specific group
+-------------------------------------------------*/
+
+static int compile_idt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ fatalerror("Unimplemented IDT instructions");
+#if 0
+ UINT32 op = *desc->opptr.l;
+ UINT8 opswitch = op & 0x1f;
+
+ switch (opswitch)
+ {
+ case 0: /* MAD */
+ if (RSREG != 0 && RTREG != 0)
+ {
+ emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg]
+ emit_mov_r32_m32(REG_EDX, REGADDR(RTREG)); // mov edx,[rtreg]
+ emit_imul_r32(DRCTOP, REG_EDX); // imul edx
+ emit_add_r32_m32(DRCTOP, LOADDR); // add eax,[lo]
+ emit_adc_r32_m32(DRCTOP, HIADDR); // adc edx,[hi]
+ emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx
+ emit_cdq(DRCTOP); // cdq
+ emit_mov_m64_r64(DRCTOP, LOADDR, REG_EAX); // mov [lo],edx:eax
+ emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx
+ emit_cdq(DRCTOP); // cdq
+ emit_mov_m64_r64(DRCTOP, HIADDR, REG_EAX); // mov [hi],edx:eax
+ }
+ return compile_SUCCESSFUL_CP(3,4);
+
+ case 1: /* MADU */
+ if (RSREG != 0 && RTREG != 0)
+ {
+ emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg]
+ emit_mov_r32_m32(REG_EDX, REGADDR(RTREG)); // mov edx,[rtreg]
+ emit_mul_r32(DRCTOP, REG_EDX); // mul edx
+ emit_add_r32_m32(DRCTOP, LOADDR); // add eax,[lo]
+ emit_adc_r32_m32(DRCTOP, HIADDR); // adc edx,[hi]
+ emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx
+ emit_cdq(DRCTOP); // cdq
+ emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax
+ emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx
+ emit_cdq(DRCTOP); // cdq
+ emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax
+ }
+ return compile_SUCCESSFUL_CP(3,4);
+
+ case 2: /* MUL */
+ if (RDREG != 0)
+ {
+ if (RSREG != 0 && RTREG != 0)
+ {
+ emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg]
+ emit_imul_r32_m32(REG_EAX, REGADDR(RTREG)); // imul eax,[rtreg]
+ emit_cdq(DRCTOP); // cdq
+ emit_mov_m64_r64(REG_ESI, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rd],edx:eax
+ }
+ else
+ emit_zero_m64(REG_ESI, REGADDR(RDREG));
+ }
+ return compile_SUCCESSFUL_CP(3,4);
+ }
+#endif
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ COP0 RECOMPILATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ compile_set_cop0_reg - generate code to
+ handle special COP0 registers
+-------------------------------------------------*/
+
+static int compile_set_cop0_reg(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 reg)
+{
+ emit_link link1;
+
+ switch (reg)
+ {
+ case COP0_Cause:
+ emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Cause)); // mov edx,[Cause]
+ emit_and_r32_imm(DRCTOP, REG_EAX, ~0xfc00); // and eax,~0xfc00
+ emit_and_r32_imm(DRCTOP, REG_EDX, 0xfc00); // and edx,0xfc00
+ emit_or_r32_r32(DRCTOP, REG_EAX, REG_EDX); // or eax,edx
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Cause), REG_EAX); // mov [Cause],eax
+ emit_and_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // and eax,[SR]
+ emit_test_r32_imm(DRCTOP, REG_EAX, 0x300); // test eax,0x300
+ oob_request_callback(drc, COND_NZ, oob_interrupt_cleanup, compiler, desc, mips3.drcdata->generate_interrupt_exception);
+ // jnz generate_interrupt_exception
+ return TRUE;
+
+ case COP0_Status:
+ emit_flush_cycles_before_instruction(drc, compiler, desc, 1);
+ emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Status)); // mov edx,[Status]
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Status), REG_EAX); // mov [Status],eax
+ emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EAX); // xor edx,eax
+ emit_test_r32_imm(DRCTOP, REG_EDX, 0x8000); // test edx,0x8000
+ emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_update_cycle_counting)); // call mips3com_update_cycle_counting
+ resolve_link(DRCTOP, &link1); // skip:
+ append_check_interrupts(drc, compiler, desc); // <check interrupts>
+ return TRUE;
+
+ case COP0_Count:
+ emit_flush_cycles_before_instruction(drc, compiler, desc, 1);
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Count), REG_EAX); // mov [Count],eax
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->activecpu_gettotalcycles64)); // call activecpu_gettotalcycles64
+ emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Count)); // mov edx,[Count]
+ emit_sub_r64_r64(DRCTOP, REG_RAX, REG_RDX); // sub rax,rdx
+ emit_sub_r64_r64(DRCTOP, REG_RAX, REG_RDX); // sub rax,rdx
+ emit_mov_m64_r64(DRCTOP, MDRC(&mips3.core->count_zero_time), REG_RAX); // mov [count_zero_time],rax
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_update_cycle_counting)); // call mips3com_update_cycle_counting
+ return TRUE;
+
+ case COP0_Compare:
+ emit_flush_cycles_before_instruction(drc, compiler, desc, 1);
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Compare), REG_EAX); // mov [Compare],eax
+ emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Cause), ~0x8000); // and [Cause],~0x8000
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_update_cycle_counting)); // call mips3com_update_cycle_counting
+ return TRUE;
+
+ case COP0_PRId:
+ return TRUE;
+
+ case COP0_Config:
+ emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Config)); // mov edx,[Config]
+ emit_and_r32_imm(DRCTOP, REG_EAX, 0x0007); // and eax,0x0007
+ emit_and_r32_imm(DRCTOP, REG_EDX, ~0x0007); // and edx,~0x0007
+ emit_or_r32_r32(DRCTOP, REG_EAX, REG_EDX); // or eax,edx
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Config), REG_EAX); // mov [Config],eax
+ return TRUE;
+
+ default:
+ emit_mov_m32_r32(DRCTOP, CPR0ADDR(reg), REG_EAX); // mov cpr0[reg],eax
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ compile_get_cop0_reg - generate code to
+ read special COP0 registers
+-------------------------------------------------*/
+
+static int compile_get_cop0_reg(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 reg)
+{
+ emit_link link1, link2;
+
+ switch (reg)
+ {
+ case COP0_Count:
+ compiler->cycles += MIPS3_COUNT_READ_CYCLES;
+ emit_flush_cycles_before_instruction(drc, compiler, desc, MIPS3_COUNT_READ_CYCLES);
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->activecpu_gettotalcycles64)); // call activecpu_gettotalcycles64
+ emit_sub_r64_m64(DRCTOP, REG_RAX, MDRC(&mips3.core->count_zero_time)); // sub rax,[count_zero_time]
+ emit_shr_r64_imm(DRCTOP, REG_RAX, 1); // shr rax,1
+ emit_movsxd_r64_r32(DRCTOP, REG_RAX, REG_EAX); // movsxd rax,eax
+ return TRUE;
+
+ case COP0_Cause:
+ compiler->cycles += MIPS3_CAUSE_READ_CYCLES;
+ emit_movsxd_r64_m32(DRCTOP, REG_RAX, CPR0ADDR(COP0_Cause)); // movsxd rax,[Cause]
+ return TRUE;
+
+ case COP0_Random:
+ emit_flush_cycles_before_instruction(drc, compiler, desc, 1);
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->activecpu_gettotalcycles64)); // call activecpu_gettotalcycles64
+ emit_mov_r32_m32(DRCTOP, REG_ECX, CPR0ADDR(COP0_Wired)); // mov ecx,[Wired]
+ emit_mov_r32_imm(DRCTOP, REG_R8D, 48); // mov r8d,48
+ emit_and_r32_imm(DRCTOP, REG_ECX, 0x3f); // and ecx,0x3f
+ emit_sub_r64_m64(DRCTOP, REG_RAX, MDRC(&mips3.core->count_zero_time)); // sub rax,[count_zero_time]
+ emit_sub_r32_r32(DRCTOP, REG_R8D, REG_ECX); // sub r8d,ecx
+ emit_jcc_short_link(DRCTOP, COND_BE, &link1); // jbe link1
+ emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx
+ emit_div_r64(DRCTOP, REG_R8); // div r8
+ emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDX); // mov eax,edx
+ emit_add_r32_r32(DRCTOP, REG_EAX, REG_ECX); // add eax,ecx
+ emit_and_r32_imm(DRCTOP, REG_EAX, 0x3f); // and eax,0x3f
+ emit_jmp_short_link(DRCTOP, &link2); // jmp link2
+ resolve_link(DRCTOP, &link1); // link1:
+ emit_mov_r32_imm(DRCTOP, REG_EAX, 47); // mov eax,47
+ resolve_link(DRCTOP, &link2); // link2:
+ return TRUE;
+
+ default:
+ emit_movsxd_r64_m32(DRCTOP, REG_RAX, CPR0ADDR(reg)); // movsxd rax,cpr0[reg]
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ compile_cop0 - compile COP0 opcodes
+-------------------------------------------------*/
+
+static int compile_cop0(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 opswitch = RSREG;
+
+ /* generate an exception if COP0 is disabled or we are not in kernel mode */
+ if (mips3.drcoptions & MIPS3DRC_STRICT_COP0)
+ {
+ emit_link checklink;
+
+ emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_KSU_MASK); // test [SR],SR_KSU_MASK
+ emit_jcc_short_link(DRCTOP, COND_Z, &checklink); // jz okay
+ emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP0); // test [SR],SR_COP0
+ oob_request_callback(drc, COND_Z, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_cop_exception);
+ // jz generate_cop_exception
+ resolve_link(DRCTOP, &checklink); // okay:
+ }
+
+ switch (opswitch)
+ {
+ case 0x00: /* MFCz */
+ case 0x01: /* DMFCz */
+ if (RTREG != 0)
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY);
+ compile_get_cop0_reg(drc, compiler, desc, RDREG); // mov eax,cpr0[rd])
+ if (!(opswitch & 1))
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rt), REG_EAX); // movsxd <rt>,eax
+ else
+ emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov <rt>,rax
+ }
+ return TRUE;
+
+ case 0x02: /* CFCz */
+ if (RTREG != 0)
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY);
+ emit_movsxd_r64_m32(DRCTOP, X86REG(rt), CCR0ADDR(RDREG)); // movsxd <rt>,ccr1[rd]
+ }
+ return TRUE;
+
+ case 0x04: /* MTCz */
+ case 0x05: /* DMTCz */
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+ emit_load_register_value(drc, compiler, REG_EAX, rt); // mov eax,<rt>
+ compile_set_cop0_reg(drc, compiler, desc, RDREG); // mov cpr0[rd],rax
+ }
+ return TRUE;
+
+ case 0x06: /* CTCz */
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+ emit_load_register_value(drc, compiler, REG_EAX, rt); // mov eax,<rt>
+ emit_mov_m32_r32(DRCTOP, CCR0ADDR(RDREG), REG_EAX); // mov ccr1[rd],eax
+ }
+ return TRUE;
+
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1a:
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0x1e:
+ case 0x1f: /* COP */
+ switch (op & 0x01ffffff)
+ {
+ case 0x01: /* TLBR */
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbr)); // call mips3com_tlbr
+ return TRUE;
+
+ case 0x02: /* TLBWI */
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbwi)); // call mips3com_tlbwi
+ return TRUE;
+
+ case 0x06: /* TLBWR */
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbwr)); // call mips3com_tlbwr
+ return TRUE;
+
+ case 0x08: /* TLBP */
+ emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core]
+ emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbp)); // call mips3com_tlbp
+ return TRUE;
+
+ case 0x18: /* ERET */
+ emit_mov_r32_m32(DRCTOP, REG_P1, CPR0ADDR(COP0_EPC)); // mov p1,[EPC]
+ emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), ~SR_EXL); // and [SR],~SR_EXL
+ compiler_register_flush_all(drc, compiler); // <flush registers>
+ drc_append_dispatcher(drc); // <dispatch>
+ return TRUE;
+
+ case 0x20: /* WAIT */
+ return TRUE;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ COP1 RECOMPILATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ compile_cop1 - compile COP1 opcodes
+-------------------------------------------------*/
+
+static int compile_cop1(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ UINT32 op = *desc->opptr.l;
+ emit_link link1;
+
+ /* generate an exception if COP1 is disabled */
+ if (mips3.drcoptions & MIPS3DRC_STRICT_COP1)
+ {
+ emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP1); // test [mips3.core->cpr[0][COP0_Status]],SR_COP1
+ oob_request_callback(drc, COND_Z, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_cop_exception);
+ // jz generate_cop_exception
+ }
+
+ switch (RSREG)
+ {
+ case 0x00: /* MFCz */
+ if (RTREG != 0)
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY);
+ emit_movsxd_r64_m32(DRCTOP, X86REG(rt), FPR32ADDR(RDREG)); // movsxd <rt>,cpr[rd]
+ }
+ return TRUE;
+
+ case 0x01: /* DMFCz */
+ if (RTREG != 0)
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY);
+ emit_mov_r64_m64(DRCTOP, X86REG(rt), FPR64ADDR(RDREG)); // mov <rt>,cpr[rd]
+ }
+ return TRUE;
+
+ case 0x02: /* CFCz */
+ if (RTREG != 0)
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY);
+ if (RDREG != 31)
+ emit_movsxd_r64_m32(DRCTOP, X86REG(rt), CCR1ADDR(RDREG)); // movsxd <rt>,ccr1[rd]
+ else
+ {
+ emit_call(DRCTOP, mips3.drcdata->recover_ccr31); // call recover_ccr31
+ emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov [X86REG(rt)],rax
+ }
+ }
+ return TRUE;
+
+ case 0x04: /* MTCz */
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+ UINT8 tempreg = ISCONST(rt) ? REG_EAX : X86REG(rt);
+ emit_load_register_value(drc, compiler, tempreg, rt); // mov <tempreg>,<rt>
+ emit_mov_m32_r32(DRCTOP, FPR32ADDR(RDREG), tempreg); // mov cpr1[rd],<tempreg>
+ }
+ return TRUE;
+
+ case 0x05: /* DMTCz */
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+ UINT8 tempreg = ISCONST(rt) ? REG_EAX : X86REG(rt);
+ emit_load_register_value(drc, compiler, tempreg, rt); // mov <tempreg>,<rt>
+ emit_mov_m64_r64(DRCTOP, FPR64ADDR(RDREG), tempreg); // mov cpr1[rd],<tempreg>
+ }
+ return TRUE;
+
+ case 0x06: /* CTCz */
+ {
+ mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+ emit_load_register_value(drc, compiler, REG_EAX, rt); // mov eax,<rt>
+ if (RDREG != 31)
+ emit_mov_m32_r32(DRCTOP, CCR1ADDR(RDREG), REG_EAX); // mov ccr1[rd],eax
+ else
+ emit_call(DRCTOP, mips3.drcdata->explode_ccr31); // call explode_ccr31
+ }
+ return TRUE;
+
+ case 0x08: /* BC */
+ switch ((op >> 16) & 3)
+ {
+ case 0x00: /* BCzF */
+ case 0x02: /* BCzFL */
+ emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[which],0
+ emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz link1
+ emit_delay_slot_and_branch(drc, compiler, desc, 0); // <next instruction + redispatch>
+ resolve_link(DRCTOP, &link1); // skip:
+ return TRUE;
+
+ case 0x01: /* BCzT */
+ case 0x03: /* BCzTL */
+ emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[which],0
+ emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz link1
+ emit_delay_slot_and_branch(drc, compiler, desc, 0); // <next instruction + redispatch>
+ resolve_link(DRCTOP, &link1); // skip:
+ return TRUE;
+ }
+ break;
+
+ default:
+ switch (op & 0x3f)
+ {
+ case 0x00:
+ if (IS_SINGLE(op)) /* ADD.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // addss xmm0,[ftreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* ADD.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // addsd xmm0,[ftreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x01:
+ if (IS_SINGLE(op)) /* SUB.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // subss xmm0,[ftreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* SUB.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // subsd xmm0,[ftreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x02:
+ if (IS_SINGLE(op)) /* MUL.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* MUL.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x03:
+ if (IS_SINGLE(op)) /* DIV.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_divss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // divss xmm0,[ftreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* DIV.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_divsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // divsd xmm0,[ftreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x04:
+ if (IS_SINGLE(op)) /* SQRT.S */
+ {
+ emit_sqrtss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // sqrtss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* SQRT.D */
+ {
+ emit_sqrtsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // sqrtsd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x05:
+ if (IS_SINGLE(op)) /* ABS.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_andps_r128_m128(DRCTOP, REG_XMM0, MDRC(mips3.drcdata->abs32mask));
+ // andps xmm0,[abs32mask]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* ABS.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_andps_r128_m128(DRCTOP, REG_XMM0, MDRC(mips3.drcdata->abs64mask));
+ // andpd xmm0,[abs64mask]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x06:
+ if (IS_SINGLE(op)) /* MOV.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* MOV.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x07:
+ if (IS_SINGLE(op)) /* NEG.S */
+ {
+ emit_xorps_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // xorps xmm0,xmm0
+ emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // subss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* NEG.D */
+ {
+ emit_xorps_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // xorps xmm0,xmm0
+ emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // subsd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x08:
+ /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */
+ drc_append_set_temp_sse_rounding(drc, FPRND_NEAR); // ldmxcsr [round]
+ if (IS_SINGLE(op)) /* ROUND.L.S */
+ emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg]
+ else /* ROUND.L.D */
+ emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg]
+ emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax
+ drc_append_restore_sse_rounding(drc); // ldmxcsr [mode]
+ return TRUE;
+
+ case 0x09:
+ if (IS_SINGLE(op)) /* TRUNC.L.S */
+ emit_cvttss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvttss2si rax,[fsreg]
+ else /* TRUNC.L.D */
+ emit_cvttsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvttsd2si rax,[fsreg]
+ emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax
+ return TRUE;
+
+ case 0x0a:
+ /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */
+ drc_append_set_temp_sse_rounding(drc, FPRND_UP); // ldmxcsr [round]
+ if (IS_SINGLE(op)) /* CEIL.L.S */
+ emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg]
+ else /* CEIL.L.D */
+ emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg]
+ emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax
+ drc_append_restore_sse_rounding(drc); // ldmxcsr [mode]
+ return TRUE;
+
+ case 0x0b:
+ /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */
+ drc_append_set_temp_sse_rounding(drc, FPRND_DOWN); // ldmxcsr [round]
+ if (IS_SINGLE(op)) /* FLOOR.L.S */
+ emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg]
+ else /* FLOOR.L.D */
+ emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg]
+ emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax
+ drc_append_restore_sse_rounding(drc); // ldmxcsr [mode]
+ return TRUE;
+
+ case 0x0c:
+ /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */
+ drc_append_set_temp_sse_rounding(drc, FPRND_NEAR); // ldmxcsr [round]
+ if (IS_SINGLE(op)) /* ROUND.W.S */
+ emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg]
+ else /* ROUND.W.D */
+ emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg]
+ emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax
+ drc_append_restore_sse_rounding(drc); // ldmxcsr [mode]
+ return TRUE;
+
+ case 0x0d:
+ if (IS_SINGLE(op)) /* TRUNC.W.S */
+ emit_cvttss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvttss2si eax,[fsreg]
+ else /* TRUNC.W.D */
+ emit_cvttsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvttsd2si eax,[fsreg]
+ emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax
+ return TRUE;
+
+ case 0x0e:
+ /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */
+ drc_append_set_temp_sse_rounding(drc, FPRND_UP); // ldmxcsr [round]
+ if (IS_SINGLE(op)) /* CEIL.W.S */
+ emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg]
+ else /* CEIL.W.D */
+ emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg]
+ emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax
+ drc_append_restore_sse_rounding(drc); // ldmxcsr [mode]
+ return TRUE;
+
+ case 0x0f:
+ /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */
+ drc_append_set_temp_sse_rounding(drc, FPRND_DOWN); // ldmxcsr [round]
+ if (IS_SINGLE(op)) /* FLOOR.W.S */
+ emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg]
+ else /* FLOOR.W.D */
+ emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg]
+ emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax
+ drc_append_restore_sse_rounding(drc); // ldmxcsr [mode]
+ return TRUE;
+
+ case 0x11: /* R5000 */
+ emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[which],0
+ emit_jcc_short_link(DRCTOP, ((op >> 16) & 1) ? COND_Z : COND_NZ, &link1);
+ // jz/nz skip
+ if (IS_SINGLE(op)) /* MOVT/F.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* MOVT/F.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ resolve_link(DRCTOP, &link1); // skip:
+ return TRUE;
+
+ case 0x12: /* R5000 */
+ emit_cmp_m64_imm(DRCTOP, REGADDR(RTREG), 0); // cmp [X86REG(rt)],0
+ emit_jcc_short_link(DRCTOP, COND_NZ, &link1); // jnz skip
+ if (IS_SINGLE(op)) /* MOVZ.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* MOVZ.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ resolve_link(DRCTOP, &link1); // skip:
+ return TRUE;
+
+ case 0x13: /* R5000 */
+ emit_cmp_m64_imm(DRCTOP, REGADDR(RTREG), 0); // cmp [X86REG(rt)],0
+ emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip
+ if (IS_SINGLE(op)) /* MOVN.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* MOVN.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ resolve_link(DRCTOP, &link1); // skip:
+ return TRUE;
+
+ case 0x15: /* R5000 */
+ if (IS_SINGLE(op)) /* RECIP.S */
+ {
+ emit_rcpss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // rcpss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* RECIP.D */
+ {
+ emit_cvtsd2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsd2ss xmm0,[fsreg]
+ emit_rcpss_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // rcpss xmm0,xmm0
+ emit_cvtss2sd_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // cvtss2sd xmm0,xmm0
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x16: /* R5000 */
+ if (IS_SINGLE(op)) /* RSQRT.S */
+ {
+ emit_rsqrtss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // rsqrtss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ }
+ else /* RSQRT.D */
+ {
+ emit_cvtsd2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtss2sd xmm0,[fsreg]
+ emit_rsqrtss_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // rsqrtss xmm0,xmm0
+ emit_cvtss2sd_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // cvtsd2ss xmm0,xmm0
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ }
+ return TRUE;
+
+ case 0x20:
+ if (IS_INTEGRAL(op))
+ {
+ if (IS_SINGLE(op)) /* CVT.S.W */
+ emit_cvtsi2ss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // cvtsi2ss xmm0,[fsreg]
+ else /* CVT.S.L */
+ emit_cvtsi2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsi2ss xmm0,[fsreg]
+ }
+ else /* CVT.S.D */
+ emit_cvtsd2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsd2ss xmm0,[fsreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ return TRUE;
+
+ case 0x21:
+ if (IS_INTEGRAL(op))
+ {
+ if (IS_SINGLE(op)) /* CVT.D.W */
+ emit_cvtsi2sd_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // cvtsi2sd xmm0,[fsreg]
+ else /* CVT.D.L */
+ emit_cvtsi2sd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsi2sd xmm0,[fsreg]
+ }
+ else /* CVT.D.S */
+ emit_cvtss2sd_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // cvtss2sd xmm0,[fsreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ return TRUE;
+
+ case 0x24:
+ if (IS_SINGLE(op)) /* CVT.W.S */
+ emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg]
+ else /* CVT.W.D */
+ emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg]
+ emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax
+ return TRUE;
+
+ case 0x25:
+ if (IS_SINGLE(op)) /* CVT.L.S */
+ emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg]
+ else /* CVT.L.D */
+ emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg]
+ emit_mov_m64_r64(DRCTOP, FPR32ADDR(FDREG), REG_RAX); // mov [fdreg],rax
+ return TRUE;
+
+ case 0x30:
+ case 0x38:
+ emit_mov_m8_imm(DRCTOP, CF1ADDR((op >> 8) & 7), 0); /* C.F.S/D */
+ return TRUE;
+
+ case 0x31:
+ case 0x39:
+ emit_mov_m8_imm(DRCTOP, CF1ADDR((op >> 8) & 7), 0); /* C.F.S/D */
+ return TRUE;
+
+ case 0x32:
+ case 0x3a:
+ if (IS_SINGLE(op)) /* C.EQ.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg]
+ }
+ else /* C.EQ.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg]
+ }
+ emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete fcc[x]
+ return TRUE;
+
+ case 0x33:
+ case 0x3b:
+ if (IS_SINGLE(op)) /* C.UEQ.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg]
+ }
+ else /* C.UEQ.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg]
+ }
+ emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete fcc[x]
+ return TRUE;
+
+ case 0x34:
+ case 0x3c:
+ if (IS_SINGLE(op)) /* C.OLT.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg]
+ }
+ else /* C.OLT.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg]
+ }
+ emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb fcc[x]
+ return TRUE;
+
+ case 0x35:
+ case 0x3d:
+ if (IS_SINGLE(op)) /* C.ULT.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg]
+ }
+ else /* C.ULT.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg]
+ }
+ emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb fcc[x]
+ return TRUE;
+
+ case 0x36:
+ case 0x3e:
+ if (IS_SINGLE(op)) /* C.OLE.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg]
+ }
+ else /* C.OLE.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg]
+ }
+ emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setbe fcc[x]
+ return TRUE;
+
+ case 0x37:
+ case 0x3f:
+ if (IS_SINGLE(op)) /* C.ULE.S */
+ {
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg]
+ }
+ else /* C.ULE.D */
+ {
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg]
+ }
+ emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setbe fcc[x]
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ COP1X RECOMPILATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ compile_cop1x - compile COP1X opcodes
+-------------------------------------------------*/
+
+static int compile_cop1x(drc_core *drc, compiler_state *compiler, const opcode_desc *desc)
+{
+ UINT32 op = *desc->opptr.l;
+
+ if (mips3.drcoptions & MIPS3DRC_STRICT_COP1)
+ {
+ emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP1); // test [mips3.core->cpr[0][COP0_Status]],SR_COP1
+ oob_request_callback(drc, COND_Z, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_cop_exception);
+ // jz generate_cop_exception
+ }
+
+ switch (op & 0x3f)
+ {
+ case 0x00: /* LWXC1 */
+ return compile_loadx_cop(drc, compiler, desc, 4, FPR32(FDREG));
+
+ case 0x01: /* LDXC1 */
+ return compile_loadx_cop(drc, compiler, desc, 8, FPR64(FDREG));
+
+ case 0x08: /* SWXC1 */
+ return compile_storex_cop(drc, compiler, desc, 4, FPR32(FDREG));
+
+ case 0x09: /* SDXC1 */
+ return compile_storex_cop(drc, compiler, desc, 8, FPR64(FDREG));
+
+ case 0x0f: /* PREFX */
+ return TRUE;
+
+ case 0x20: /* MADD.S */
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg]
+ emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // addss xmm0,[frreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ return TRUE;
+
+ case 0x21: /* MADD.D */
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg]
+ emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // addsd xmm0,[frreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ return TRUE;
+
+ case 0x28: /* MSUB.S */
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg]
+ emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // subss xmm0,[frreg]
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0
+ return TRUE;
+
+ case 0x29: /* MSUB.D */
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg]
+ emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // subsd xmm0,[frreg]
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0
+ return TRUE;
+
+ case 0x30: /* NMADD.S */
+ emit_xorps_r128_r128(DRCTOP, REG_XMM1, REG_XMM1); // xorps xmm1,xmm1
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg]
+ emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // addss xmm0,[frreg]
+ emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM1); // movss [fdreg],xmm1
+ return TRUE;
+
+ case 0x31: /* NMADD.D */
+ emit_xorps_r128_r128(DRCTOP, REG_XMM1, REG_XMM1); // xorps xmm1,xmm1
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg]
+ emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // addsd xmm0,[frreg]
+ emit_subsd_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subsd xmm1,xmm0
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM1); // movsd [fdreg],xmm1
+ return TRUE;
+
+ case 0x38: /* NMSUB.S */
+ emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg]
+ emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg]
+ emit_movss_r128_m32(DRCTOP, REG_XMM1, FPR32ADDR(FRREG)); // movss xmm1,[frreg]
+ emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0
+ emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM1); // movss [fdreg],xmm1
+ return TRUE;
+
+ case 0x39: /* NMSUB.D */
+ emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg]
+ emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg]
+ emit_movsd_r128_m64(DRCTOP, REG_XMM1, FPR64ADDR(FRREG)); // movsd xmm1,[frreg]
+ emit_subsd_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subsd xmm1,xmm0
+ emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM1); // movsd [fdreg],xmm1
+ return TRUE;
+
+ case 0x24: /* MADD.W */
+ case 0x25: /* MADD.L */
+ case 0x2c: /* MSUB.W */
+ case 0x2d: /* MSUB.L */
+ case 0x34: /* NMADD.W */
+ case 0x35: /* NMADD.L */
+ case 0x3c: /* NMSUB.W */
+ case 0x3d: /* NMSUB.L */
+ default:
+ fprintf(stderr, "cop1x %X\n", op);
+ break;
+ }
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ INDIVIDUAL INSTRUCTION COMPILATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ compile_shift - compile a 32-bit
+ shift, both fixed and variable counts
+
+ 0: sll/sllv rD,rT,rS
+ 2: srl/srlv rD,rT,rS
+ 3: sra/srav rD,rT,rS
+-------------------------------------------------*/
+
+static int compile_shift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rd, rs, rt;
+
+ assert(shift_type == 0 || shift_type == 2 || shift_type == 3);
+
+ /* if the destination is r0, bail */
+ if (RDREG == 0)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = shift_variable ? compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC) : MAKE_CONST(SHIFT);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rS and rT are both constant, we can compute the constant result */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ if (shift_type == 0)
+ emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)((UINT32)CONSTVAL(rt) << CONSTVAL(rs)));
+ else if (shift_type == 2)
+ emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)((UINT32)CONSTVAL(rt) >> CONSTVAL(rs)));
+ else
+ emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)((INT32)CONSTVAL(rt) >> CONSTVAL(rs)));
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ /* allocate the register and load rT into it */
+ /* if rS is non-const, must load it first in case rD == rS */
+ rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY);
+ if (!ISCONST(rs))
+ emit_load_register_value(drc, compiler, REG_ECX, rs); // mov ecx,<rs>
+ emit_load_register_value(drc, compiler, X86REG(rd), rt); // mov <rd>,<rt>
+
+ /* if rs is constant, use immediate forms of the shift */
+ if (ISCONST(rs))
+ {
+ if (shift_type == 0)
+ emit_shl_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // shl <rd>,<rs>
+ else if (shift_type == 2)
+ emit_shr_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // shr <rd>,<rs>
+ else
+ emit_sar_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // sar <rd>,<rs>
+ }
+
+ /* otherwise, load rs into cl so we can do a variable shift */
+ else
+ {
+ if (shift_type == 0)
+ emit_shl_r32_cl(DRCTOP, X86REG(rd)); // shl <rd>,cl
+ else if (shift_type == 2)
+ emit_shr_r32_cl(DRCTOP, X86REG(rd)); // shr <rd>,cl
+ else
+ emit_sar_r32_cl(DRCTOP, X86REG(rd)); // sar <rd>,cl
+ }
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd>
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_dshift - compile a 64-bit
+ shift, both fixed and variable counts
+
+ 0: dsll/dsll32/dsllv rD,rT,rS
+ 2: dsrl/dsrl32/dsrlv rD,rT,rS
+ 3: dsra/dsra32/dsrav rD,rT,rS
+-------------------------------------------------*/
+
+static int compile_dshift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable, int shift_const_plus32)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rd, rs, rt;
+
+ assert(shift_type == 0 || shift_type == 2 || shift_type == 3);
+
+ /* if the destination is r0, bail */
+ if (RDREG == 0)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = shift_variable ? compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC) : MAKE_CONST(SHIFT + (shift_const_plus32 ? 32 : 0));
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rS and rT are both constant, we can compute the constant result */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ if (shift_type == 0)
+ emit_set_constant_value(drc, compiler, desc, RDREG, (UINT64)CONSTVAL(rt) << CONSTVAL(rs));
+ else if (shift_type == 2)
+ emit_set_constant_value(drc, compiler, desc, RDREG, (UINT64)CONSTVAL(rt) >> CONSTVAL(rs));
+ else
+ emit_set_constant_value(drc, compiler, desc, RDREG, (INT64)CONSTVAL(rt) >> CONSTVAL(rs));
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ /* allocate the register and load rT into it */
+ /* if rS is non-const, must load it first in case rD == rS */
+ rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY);
+ if (!ISCONST(rs))
+ emit_load_register_value(drc, compiler, REG_ECX, rs); // mov ecx,<rs>
+ emit_load_register_value(drc, compiler, X86REG(rd), rt); // mov <rd>,<rt>
+
+ /* if rs is constant, use immediate forms of the shift */
+ if (ISCONST(rs))
+ {
+ if (shift_type == 0)
+ emit_shl_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // shl <rd>,<rs>
+ else if (shift_type == 2)
+ emit_shr_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // shr <rd>,<rs>
+ else
+ emit_sar_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // sar <rd>,<rs>
+ }
+
+ /* otherwise, load rs into cl so we can do a variable shift */
+ else
+ {
+ if (shift_type == 0)
+ emit_shl_r64_cl(DRCTOP, X86REG(rd)); // shl <rd>,cl
+ else if (shift_type == 2)
+ emit_shr_r64_cl(DRCTOP, X86REG(rd)); // shr <rd>,cl
+ else
+ emit_sar_r64_cl(DRCTOP, X86REG(rd)); // sar <rd>,cl
+ }
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_movzn - compile a conditional
+ move based on compare against 0
+
+ movz/movn rD,rS,rT
+-------------------------------------------------*/
+
+static int compile_movzn(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_movn)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rd, rs, rt;
+ emit_link skip = { 0 };
+
+ /* if the destination is r0, bail */
+ if (RDREG == 0)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+ rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY);
+
+ /* if rT is constant and fails the condition, we're done */
+ if (ISCONST(rt))
+ {
+ INT32 rtval = CONSTVAL(rt);
+ if ((is_movn && rtval == 0) || (!is_movn && rtval != 0))
+ return TRUE;
+ }
+
+ /* if rT is not constant, check the condition */
+ else
+ {
+ emit_test_r64_r64(DRCTOP, X86REG(rt), X86REG(rt)); // test <rt>,<rt>
+ emit_jcc_short_link(DRCTOP, is_movn ? COND_Z : COND_NZ, &skip); // jz/nz skip
+ }
+
+ /* load rS into rD */
+ emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs>
+ if (!ISCONST(rt))
+ resolve_link(DRCTOP, &skip); // skip:
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_trapcc - compile a conditional
+ trap comparing two registers
+
+ 0/1: tge/tgeu rS,rT / tgei/tgeiu rS,SIMM
+ 2/3: tlt/tltu rS,rT / tlti/tltiu rS,SIMM
+ 4: teq rS,rT / teqi rS,SIMM
+ 6: tne rS,rT / tnei rS,SIMM
+-------------------------------------------------*/
+
+static int compile_trapcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int immediate)
+{
+ static const UINT8 condtype[] = { COND_GE, COND_AE, COND_L, COND_B, COND_E, COND_NONE, COND_NE, COND_NONE };
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rs, rt;
+
+ assert(condition < ARRAY_LENGTH(condtype) && condtype[condition] != COND_NONE);
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT32 rsval = CONSTVAL(rs);
+ INT32 rtval = CONSTVAL(rt);
+ int result = FALSE;
+
+ /* determine the fixed answer */
+ switch (condition)
+ {
+ case 0: result = ((INT64)rsval >= (INT64)rtval); break;
+ case 1: result = ((UINT64)rsval >= (UINT64)rtval); break;
+ case 2: result = ((INT64)rsval < (INT64)rtval); break;
+ case 3: result = ((UINT64)rsval < (UINT64)rtval); break;
+ case 4: result = (rsval == rtval); break;
+ case 6: result = (rsval != rtval); break;
+ }
+
+ /* if we're not trapping, just return; otherwise, always trap */
+ if (!result)
+ return TRUE;
+ condition = COND_NONE;
+ }
+
+ /* if rT is constant do an immediate compare */
+ else if (ISCONST(rt))
+ {
+ emit_cmp_r64_imm(DRCTOP, X86REG(rs), CONSTVAL(rt)); // cmp <rs>,<rt>
+ condition = condtype[condition];
+ }
+
+ /* if rS is constant do an immediate compare with opposite condition */
+ else if (ISCONST(rs))
+ {
+ emit_cmp_r64_imm(DRCTOP, X86REG(rt), CONSTVAL(rs)); // cmp <rt>,<rs>
+ condition = condtype[condition] ^ 1;
+ }
+
+ /* otherwise, compare the two registers with a normal condition */
+ else
+ {
+ emit_cmp_r64_imm(DRCTOP, X86REG(rs), X86REG(rt)); // cmp <rs>,<rt>
+ condition = condtype[condition];
+ }
+
+ /* generate an OOB request if the trap is taken */
+ oob_request_callback(drc, condition, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_trap_exception);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_branchcc - compile a conditional
+ branch comparing two registers
+
+ beq rS,rT,dest
+ bne rS,rT,dest
+-------------------------------------------------*/
+
+static int compile_branchcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition)
+{
+ UINT32 op = *desc->opptr.l;
+ emit_link skip = { 0 };
+ mipsreg_state rs, rt;
+
+ assert(condition == COND_E || condition == COND_NE);
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT32 rsval = CONSTVAL(rs);
+ INT32 rtval = CONSTVAL(rt);
+ if ((condition == COND_E && rsval != rtval) || (condition == COND_NE && rsval == rtval))
+ return TRUE;
+ }
+
+ /* if rT is constant do an immediate compare */
+ else if (ISCONST(rt))
+ {
+ emit_cmp_r64_imm(DRCTOP, X86REG(rs), CONSTVAL(rt)); // cmp <rs>,<rt>
+ emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip
+ }
+
+ /* if rS is constant do an immediate compare */
+ else if (ISCONST(rs))
+ {
+ emit_cmp_r64_imm(DRCTOP, X86REG(rt), CONSTVAL(rs)); // cmp <rt>,<rs>
+ emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip
+ }
+
+ /* otherwise, compare the two registers with a normal condition */
+ else
+ {
+ emit_cmp_r64_r64(DRCTOP, X86REG(rs), X86REG(rt)); // cmp <rs>,<rt>
+ emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip
+ }
+
+ /* append the branch logic */
+ emit_delay_slot_and_branch(drc, compiler, desc, 0); // <next instruction + redispatch>
+
+ if (!(ISCONST(rs) && ISCONST(rt)))
+ resolve_link(DRCTOP, &skip); // skip:
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_branchcz - compile a conditional
+ branch comparing one register against 0
+
+ blez rS,dest
+ blezl rS,dest
+ bgtz rS,dest
+ bgtzl rS,dest
+ bltz rS,dest
+ bltzl rS,dest
+ bltzal rS,dest
+ bltzall rS,dest
+ bgez rS,dest
+ bgezl rS,dest
+ bgezal rS,dest
+ bgezall rS,dest
+-------------------------------------------------*/
+
+static int compile_branchcz(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int link)
+{
+ UINT32 op = *desc->opptr.l;
+ emit_link skip = { 0 };
+ mipsreg_state rs;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+
+ /* if the register is constant, we know the answer up front */
+ if (ISCONST(rs))
+ {
+ INT32 rsval = CONSTVAL(rs);
+ if ((condition == COND_LE && rsval > 0) || (condition == COND_G && rsval <= 0) ||
+ (condition == COND_L && rsval >= 0) || (condition == COND_GE && rsval < 0))
+ return TRUE;
+ }
+
+ /* otherwise, perform the compare */
+ else
+ {
+ emit_cmp_r64_imm(DRCTOP, X86REG(rs), 0); // cmp <rs>,0
+ emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip
+ }
+
+ /* append the branch logic */
+ emit_delay_slot_and_branch(drc, compiler, desc, link ? 31 : 0); // <next instruction + redispatch>
+
+ if (!ISCONST(rs))
+ resolve_link(DRCTOP, &skip); // skip:
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_add - compile an add with or without
+ overflow
+
+ add rD,rS,rT / addi rT,rS,SIMM
+ addu rD,rS,rT / addiu rT,rS,SIMM
+-------------------------------------------------*/
+
+static int compile_add(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 destreg = immediate ? RTREG : RDREG;
+ mipsreg_state rd, rs, rt;
+
+ /* note that we still must perform this operation even if destreg == 0
+ in the overflow case */
+ if (destreg == 0 && suppress_exceptions)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT64 result = (INT64)CONSTVAL(rs) + (INT64)CONSTVAL(rt);
+
+ /* if we overflow, always generate an exception */
+ if (!suppress_exceptions && (INT32)result != result)
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+
+ /* else just set the new constant value */
+ else if (destreg != 0)
+ emit_set_constant_value(drc, compiler, desc, destreg, (INT32)result);
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST);
+
+ /* perform the operating in EAX so we don't trash rD if an exception happens */
+ if (!suppress_exceptions)
+ {
+ emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs>
+ if (!ISCONST(rt))
+ emit_add_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // add eax,<rt>
+ else if (CONSTVAL(rt) != 0)
+ emit_add_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rt)); // add eax,<rt>
+ oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+ // jo generate_overflow_exception
+ if (destreg != 0)
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), REG_EAX); // movsxd <rd>,eax
+ }
+ else
+ {
+ if (ISCONST(rs) && CONSTVAL(rs) == 0)
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rt)); // movsxd <rd>,<rt>
+ else if (ISCONST(rt) && CONSTVAL(rt) == 0)
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rs)); // movsxd <rd>,<rs>
+ else if (ISCONST(rs))
+ {
+ emit_lea_r32_m32(DRCTOP, X86REG(rd), MBD(X86REG(rt), CONSTVAL(rs))); // lea <rd>,[<rt> + <rs>]
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd>
+ }
+ else if (ISCONST(rt))
+ {
+ emit_lea_r32_m32(DRCTOP, X86REG(rd), MBD(X86REG(rs), CONSTVAL(rt))); // lea <rd>,[<rs> + <rt>]
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd>
+ }
+ else
+ {
+ emit_lea_r32_m32(DRCTOP, X86REG(rd), MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea <rd>,[<rs> + <rt>]
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd>
+ }
+ }
+
+ /* only mark it dirty after the exception risk is over */
+ compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DIRTY);
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_dadd - compile an add with or without
+ overflow
+
+ dadd rD,rS,rT
+ daddu rD,rS,rT
+-------------------------------------------------*/
+
+static int compile_dadd(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 destreg = immediate ? RTREG : RDREG;
+ mipsreg_state rd, rs, rt;
+
+ /* note that we still must perform this operation even if destreg == 0
+ in the overflow case */
+ if (destreg == 0 && suppress_exceptions)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT64 result = (INT64)CONSTVAL(rs) + (INT64)CONSTVAL(rt);
+
+ /* if we overflow, always generate an exception */
+ if (!suppress_exceptions && (INT32)result != result)
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+
+ /* else just set the new constant value */
+ else if (destreg != 0)
+ emit_set_constant_value(drc, compiler, desc, destreg, result);
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST);
+
+ /* perform the operating in EAX so we don't trash RTREG if an exception happens */
+ if (!suppress_exceptions)
+ {
+ emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs>
+ if (!ISCONST(rt))
+ emit_add_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // add rax,<rt>
+ else if (CONSTVAL(rt) != 0)
+ emit_add_r64_imm(DRCTOP, REG_RAX, CONSTVAL(rt)); // add rax,<rt>
+ oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+ // jo generate_overflow_exception
+ if (destreg != 0)
+ emit_mov_r64_r64(DRCTOP, X86REG(rd), REG_RAX); // mov <rd>,rax
+ }
+ else
+ {
+ if (ISCONST(rs))
+ emit_lea_r64_m64(DRCTOP, X86REG(rd), MBD(X86REG(rt), CONSTVAL(rs))); // lea <rd>,[<rt> + <rs>]
+ else if (ISCONST(rt))
+ emit_lea_r64_m64(DRCTOP, X86REG(rd), MBD(X86REG(rs), CONSTVAL(rt))); // lea <rd>,[<rs> + <rt>]
+ else
+ emit_lea_r64_m64(DRCTOP, X86REG(rd), MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea <rd>,[<rs> + <rt>]
+ }
+
+ /* only mark it dirty after the exception risk is over */
+ compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DIRTY);
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_sub - compile an add with or without
+ overflow
+
+ sub rD,rS,rT
+ subu rD,rS,rT
+-------------------------------------------------*/
+
+static int compile_sub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rd, rs, rt;
+
+ /* note that we still must perform this operation even if RDREG == 0
+ in the overflow case */
+ if (RDREG == 0 && suppress_exceptions)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT64 result = (INT64)CONSTVAL(rs) - (INT64)CONSTVAL(rt);
+
+ /* if we overflow, always generate an exception */
+ if (!suppress_exceptions && (INT32)result != result)
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+
+ /* else just set the new constant value */
+ else if (RDREG != 0)
+ emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)result);
+ }
+
+ /* if rS == rT, the result is 0 */
+ else if (RSREG == RTREG)
+ {
+ if (RDREG != 0)
+ emit_set_constant_value(drc, compiler, desc, RDREG, 0);
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST);
+
+ /* perform the operating in EAX so we don't trash RTREG if an exception happens */
+ if (!suppress_exceptions)
+ {
+ emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs>
+ if (!ISCONST(rt))
+ emit_sub_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // sub eax,<rt>
+ else if (CONSTVAL(rt) != 0)
+ emit_sub_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rt)); // sub eax,<rt>
+ if (!ISCONST(rt) || CONSTVAL(rt) != 0)
+ oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+ // jo generate_overflow_exception
+ if (RDREG != 0)
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), REG_EAX); // movsxd <rd>,eax
+ }
+ else
+ {
+ /* if rD and rT aren't the same, it is pretty basic */
+ if (X86REG(rd) != X86REG(rt))
+ {
+ emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs>
+ if (!ISCONST(rt))
+ emit_sub_r32_r32(DRCTOP, X86REG(rd), X86REG(rt)); // sub <rd>,<rt>
+ else if (CONSTVAL(rt) != 0)
+ emit_sub_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // sub <rd>,<rt>
+ }
+
+ /* otherwise, we negate rT and add rS */
+ else
+ {
+ emit_neg_r32(DRCTOP, X86REG(rd)); // neg <rd>
+ if (!ISCONST(rs))
+ emit_add_r32_r32(DRCTOP, X86REG(rd), X86REG(rs)); // add <rd>,<rs>
+ else if (CONSTVAL(rs) != 0)
+ emit_add_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs)); // add <rd>,<rs>
+ }
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd>
+ }
+
+ /* only mark it dirty after the exception risk is over */
+ compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DIRTY);
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_dsub - compile an add with or without
+ overflow
+
+ dsub rD,rS,rT
+ dsubu rD,rS,rT
+-------------------------------------------------*/
+
+static int compile_dsub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rd, rs, rt;
+
+ /* note that we still must perform this operation even if RDREG == 0
+ in the overflow case */
+ if (RDREG == 0 && suppress_exceptions)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT64 result = (INT64)CONSTVAL(rs) - (INT64)CONSTVAL(rt);
+
+ /* if we overflow, always generate an exception */
+ if (!suppress_exceptions && (INT32)result != result)
+ oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+
+ /* else just set the new constant value */
+ else if (RDREG != 0)
+ emit_set_constant_value(drc, compiler, desc, RDREG, result);
+ }
+
+ /* if rS == rT, the result is 0 */
+ else if (RSREG == RTREG)
+ {
+ if (RDREG != 0)
+ emit_set_constant_value(drc, compiler, desc, RDREG, 0);
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST);
+
+ /* perform the operating in EAX so we don't trash RTREG if an exception happens */
+ if (!suppress_exceptions)
+ {
+ emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs>
+ if (!ISCONST(rt))
+ emit_sub_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // sub rax,<rt>
+ else if (CONSTVAL(rt) != 0)
+ emit_sub_r64_imm(DRCTOP, REG_RAX, CONSTVAL(rt)); // sub rax,<rt>
+ if (!ISCONST(rt) || CONSTVAL(rt) != 0)
+ oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception);
+ // jo generate_overflow_exception
+ if (RDREG != 0)
+ emit_mov_r64_r64(DRCTOP, X86REG(rd), REG_RAX); // mov <rd>,rax
+ }
+ else
+ {
+ /* if rD and rT aren't the same, it is pretty basic */
+ if (X86REG(rd) != X86REG(rt))
+ {
+ emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs>
+ if (!ISCONST(rt))
+ emit_sub_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // sub <rd>,<rt>
+ else if (CONSTVAL(rt) != 0)
+ emit_sub_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // sub <rd>,<rt>
+ }
+
+ /* otherwise, we negate rT and add rS */
+ else
+ {
+ emit_neg_r64(DRCTOP, X86REG(rd)); // neg <rd>
+ if (!ISCONST(rs))
+ emit_add_r64_r64(DRCTOP, X86REG(rd), X86REG(rs)); // add <rd>,<rs>
+ else if (CONSTVAL(rs) != 0)
+ emit_add_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs)); // add <rd>,<rs>
+ }
+ }
+
+ /* only mark it dirty after the exception risk is over */
+ compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DIRTY);
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_mult - compile a 32-bit multiply
+ instruction
+
+ mult rS,rT
+ multu rS,rT
+-------------------------------------------------*/
+
+static int compile_mult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rs, rt, lo, hi;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ UINT64 result = is_unsigned ? ((UINT64)CONSTVAL(rs) * (UINT64)CONSTVAL(rt)) : ((INT64)CONSTVAL(rs) * (INT64)CONSTVAL(rt));
+ emit_set_constant_value(drc, compiler, desc, REG_LO, (INT32)result);
+ emit_set_constant_value(drc, compiler, desc, REG_HI, (INT32)(result >> 32));
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ UINT8 mulreg = ISCONST(rt) ? REG_ECX : X86REG(rt);
+
+ hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY);
+ lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY);
+
+ emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs>
+ emit_load_register_value(drc, compiler, mulreg, rt); // mov <mulreg>,<rt>
+ if (is_unsigned)
+ emit_mul_r32(DRCTOP, mulreg); // mul <mulreg>
+ else
+ emit_imul_r32(DRCTOP, mulreg); // imul <mulreg>
+ emit_movsxd_r64_r32(DRCTOP, X86REG(hi), REG_EDX); // movsxd <hireg>,edx
+ emit_movsxd_r64_r32(DRCTOP, X86REG(lo), REG_EAX); // movsxd <loreg>,eax
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_dmult - compile a 64-bit multiply
+ instruction
+
+ dmult rS,rT
+ dmultu rS,rT
+-------------------------------------------------*/
+
+static int compile_dmult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rs, rt, lo, hi;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ UINT64 result = is_unsigned ? ((UINT64)CONSTVAL(rs) * (UINT64)CONSTVAL(rt)) : ((INT64)CONSTVAL(rs) * (INT64)CONSTVAL(rt));
+ emit_set_constant_value(drc, compiler, desc, REG_LO, (INT64)result);
+ emit_set_constant_value(drc, compiler, desc, REG_HI, (INT64)(result >> 63));
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ UINT8 mulreg = ISCONST(rt) ? REG_ECX : X86REG(rt);
+
+ hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY);
+ lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY);
+
+ emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs>
+ emit_load_register_value(drc, compiler, mulreg, rt); // mov <mulreg>,<rt>
+ if (is_unsigned)
+ emit_mul_r64(DRCTOP, mulreg); // mul <mulreg>
+ else
+ emit_imul_r64(DRCTOP, mulreg); // imul <mulreg>
+ emit_mov_r64_r64(DRCTOP, X86REG(hi), REG_RDX); // mov <hi>,rdx
+ emit_mov_r64_r64(DRCTOP, X86REG(lo), REG_RAX); // mov <lo>,rax
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_div - compile a 32-bit divide
+ instruction
+
+ div rS,rT
+ divu rS,rT
+-------------------------------------------------*/
+
+static int compile_div(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rs, rt, lo, hi;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rT is constant and 0, just skip */
+ if (ISCONST(rt) && CONSTVAL(rt) == 0)
+ return TRUE;
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ UINT32 result = is_unsigned ? ((UINT32)CONSTVAL(rs) / (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) / (INT32)CONSTVAL(rt));
+ UINT32 remainder = is_unsigned ? ((UINT32)CONSTVAL(rs) % (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) % (INT32)CONSTVAL(rt));
+
+ emit_set_constant_value(drc, compiler, desc, REG_LO, (INT32)result);
+ emit_set_constant_value(drc, compiler, desc, REG_HI, (INT32)remainder);
+ }
+
+ /* otherwise, handle generically */
+ else
+ {
+ UINT8 divreg = ISCONST(rt) ? REG_ECX : X86REG(rt);
+ emit_link skip = { 0 };
+
+ hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY);
+ lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY);
+
+ emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs>
+ emit_load_register_value(drc, compiler, divreg, rt); // mov <divreg>,<rt>
+ if (!ISCONST(rt))
+ {
+ emit_test_r32_r32(DRCTOP, divreg, divreg); // test <divreg>,<divreg>
+ emit_jcc_short_link(DRCTOP, COND_Z, &skip); // jz skip
+ }
+ if (is_unsigned)
+ {
+ emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx
+ emit_div_r32(DRCTOP, divreg); // div <divreg>
+ }
+ else
+ {
+ emit_cdq(DRCTOP); // cdq
+ emit_idiv_r32(DRCTOP, divreg); // idiv <divreg>
+ }
+ emit_movsxd_r64_r32(DRCTOP, X86REG(hi), REG_EDX); // movsxd <hi>,edx
+ emit_movsxd_r64_r32(DRCTOP, X86REG(lo), REG_EAX); // movsxd <lo>,eax
+ if (!ISCONST(rt))
+ resolve_link(DRCTOP, &skip); // skip:
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_ddiv - compile a 64-bit divide
+ instruction
+
+ ddiv rS,rT
+ ddivu rS,rT
+-------------------------------------------------*/
+
+static int compile_ddiv(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rs, rt, lo, hi;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rT is constant and 0, just skip */
+ if (ISCONST(rt) && CONSTVAL(rt) == 0)
+ return TRUE;
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ UINT32 result = is_unsigned ? ((UINT32)CONSTVAL(rs) / (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) / (INT32)CONSTVAL(rt));
+ UINT32 remainder = is_unsigned ? ((UINT32)CONSTVAL(rs) % (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) % (INT32)CONSTVAL(rt));
+
+ emit_set_constant_value(drc, compiler, desc, REG_LO, (INT32)result);
+ emit_set_constant_value(drc, compiler, desc, REG_HI, (INT32)remainder);
+ }
+
+ /* if one is a constant, load the constant into EAX and multiple from there */
+ else
+ {
+ UINT8 divreg = ISCONST(rt) ? REG_ECX : X86REG(rt);
+ emit_link skip = { 0 };
+
+ hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY);
+ lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY);
+
+ emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs>
+ emit_load_register_value(drc, compiler, divreg, rt); // mov <divreg>,<rt>
+ emit_test_r64_r64(DRCTOP, divreg, divreg); // test <divreg>,<divreg>
+ emit_jcc_short_link(DRCTOP, COND_Z, &skip); // jz skip
+ if (is_unsigned)
+ {
+ emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx
+ emit_div_r64(DRCTOP, divreg); // div <divreg>
+ }
+ else
+ {
+ emit_cqo(DRCTOP); // cqo
+ emit_idiv_r64(DRCTOP, divreg); // idiv <divreg>
+ }
+ emit_mov_r64_r64(DRCTOP, X86REG(hi), REG_EDX); // mov <hi>,edx
+ emit_mov_r64_r64(DRCTOP, X86REG(lo), REG_EAX); // mov <loreg>,eax
+ resolve_link(DRCTOP, &skip); // skip:
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_logical - compile a logical
+ operation between two registers
+
+ 0: and rD,rS,rT / and rT,rS,UIMM
+ 1: or rD,rS,rT / or rT,rS,UIMM
+ 2: xor rD,rS,rT / xor rT,rS,UIMM
+ 3: nor rD,rS,rT
+-------------------------------------------------*/
+
+static int compile_logical(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int operation, int immediate)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 destreg = immediate ? RTREG : RDREG;
+ mipsreg_state rd, rs, rt;
+
+ assert(operation >= 0 && operation <= 3);
+ assert(!immediate || operation != 3);
+
+ /* if the destination is r0, bail */
+ if (destreg == 0)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = immediate ? MAKE_CONST(UIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if both constant, we know the answer up front */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ UINT64 result = (operation == 0) ? (CONSTVAL(rs) & CONSTVAL(rt)) : (operation == 1) ? (CONSTVAL(rs) | CONSTVAL(rt)) : (operation == 2) ? (CONSTVAL(rs) ^ CONSTVAL(rt)) : ~(CONSTVAL(rs) | CONSTVAL(rt));
+ emit_set_constant_value(drc, compiler, desc, destreg, result);
+ }
+
+ /* an AND against 0 is constant as well */
+ else if (operation == 0 && ISCONST(rt) && CONSTVAL(rt) == 0)
+ emit_set_constant_value(drc, compiler, desc, destreg, 0);
+
+ /* else do the operation in code */
+ else
+ {
+ rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST | ALLOC_DIRTY);
+
+ /* if rD matches rT, or if rS is const and rT isn't, then swap rT and rS */
+ if (X86REG(rd) == X86REG(rt) || (ISCONST(rs) && !ISCONST(rt)))
+ {
+ mipsreg_state temp = rs;
+ rs = rt;
+ rt = temp;
+ }
+
+ /* load rS into rD */
+ emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs>
+
+ /* if rD does not match rT, we can work directly in rD */
+ if (!ISCONST(rt))
+ {
+ if (operation == 0)
+ emit_and_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // and <rd>,<rt>
+ else if (operation == 1)
+ emit_or_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // or <rd>,<rt>
+ else if (operation == 2)
+ emit_xor_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // xor <rd>,<rt>
+ else if (operation == 3)
+ {
+ emit_or_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // or <rd>,<rt>
+ emit_not_r64(DRCTOP, X86REG(rd)); // not <rd>
+ }
+ }
+ else
+ {
+ if (operation == 0)
+ emit_and_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // and <rd>,CONSTVAL(rt)
+ else if (operation == 1 && CONSTVAL(rt) != 0)
+ emit_or_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // or <rd>,CONSTVAL(rt)
+ else if (operation == 2 && CONSTVAL(rt) != 0)
+ emit_xor_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // xor <rd>,CONSTVAL(rt)
+ else if (operation == 3)
+ {
+ if (CONSTVAL(rt) != 0)
+ emit_or_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // or <rd>,CONSTVAL(rt)
+ emit_not_r64(DRCTOP, X86REG(rd)); // not <rd>
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_slt - compile a set if less
+ than btween two registers
+
+ slti rD,rS,rT / slti rT,rS,SIMM
+ sltiu rD,rS,rT / sltiu rT,rS,SIMM
+-------------------------------------------------*/
+
+static int compile_slt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned, int immediate)
+{
+ UINT32 op = *desc->opptr.l;
+ UINT8 destreg = immediate ? RTREG : RDREG;
+ mipsreg_state rd, rs, rt;
+
+ /* if the destination is r0, bail */
+ if (destreg == 0)
+ return TRUE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if the source is constant, compute as constant */
+ if (ISCONST(rs) && ISCONST(rt))
+ {
+ INT64 result = is_unsigned ? ((UINT32)CONSTVAL(rs) < (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) < (INT32)CONSTVAL(rt));
+ emit_set_constant_value(drc, compiler, desc, destreg, result);
+ }
+
+ /* else do the operation in code */
+ else
+ {
+ UINT8 rsreg = ISCONST(rs) ? REG_RAX : X86REG(rs);
+
+ rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST | ALLOC_DIRTY);
+
+ emit_load_register_value(drc, compiler, rsreg, rs); // mov <rsreg>,<rs>
+ if (ISCONST(rt))
+ emit_cmp_r64_imm(DRCTOP, rsreg, CONSTVAL(rt)); // cmp <rsreg>,<rt>
+ else
+ emit_cmp_r64_r64(DRCTOP, rsreg, X86REG(rt)); // cmp <rsreg>,<rt>
+ emit_setcc_r8(DRCTOP, is_unsigned ? COND_B : COND_L, X86REG(rd)); // setb/l <rd>
+ emit_movzx_r32_r8(DRCTOP, X86REG(rd), X86REG(rd)); // movzx <rd>,<rd>
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_load - compile a memory load
+ instruction
+
+ lb rT,SIMM(rS)
+ lbu rT,SIMM(rS)
+ lh rT,SIMM(rS)
+ lhu rT,SIMM(rS)
+ lw rT,SIMM(rS)
+ lwu rT,SIMM(rS)
+ ld rT,SIMM(rS)
+-------------------------------------------------*/
+
+static int compile_load(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, int is_unsigned)
+{
+ UINT32 op = *desc->opptr.l;
+ mipsreg_state rs, rt;
+ int was_fixed = FALSE;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD)
+ {
+ UINT8 destreg = (RTREG == 0) ? REG_RAX : X86REG(rt);
+
+ /* perform a fixed read if we can */
+ if (size == 1)
+ was_fixed = emit_fixed_byte_read(drc, compiler, desc, destreg, is_unsigned, CONSTVAL(rs) + SIMMVAL);
+ else if (size == 2)
+ was_fixed = emit_fixed_half_read(drc, compiler, desc, destreg, is_unsigned, CONSTVAL(rs) + SIMMVAL);
+ else if (size == 4)
+ was_fixed = emit_fixed_word_read(drc, compiler, desc, destreg, is_unsigned, CONSTVAL(rs) + SIMMVAL, 0);
+ else
+ was_fixed = emit_fixed_double_read(drc, compiler, desc, destreg, CONSTVAL(rs) + SIMMVAL, 0);
+ }
+
+ /* otherwise, we just need to call the appropriate subroutine */
+ else
+ {
+ if (ISCONST(rs))
+ emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL
+ else
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL]
+ if (size == 1 && is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_byte_unsigned); // call general.read_byte_unsigned
+ else if (size == 1 && !is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_byte_signed); // call general.read_byte_signed
+ else if (size == 2 && is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_half_unsigned); // call general.read_half_unsigned
+ else if (size == 2 && !is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_half_signed); // call general.read_half_signed
+ else if (size == 4 && is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call general.read_word_unsigned
+ else if (size == 4 && !is_unsigned)
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_signed); // call general.read_word_signed
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_double); // call general.read_double
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ if (RTREG != 0)
+ emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov <rt>,rax
+ }
+
+ /* only mark dirty after the exception risk is over */
+ compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DIRTY);
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_load_cop - compile a COP memory load
+ instruction
+
+ lwc1 rT,SIMM(rs)
+ lwc2 rT,SIMM(rs)
+ ldc1 rT,SIMM(rs)
+ ldc2 rT,SIMM(rs)
+-------------------------------------------------*/
+
+static int compile_load_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD)
+ {
+ /* perform a fixed read if we can */
+ if (size == 4)
+ was_fixed = emit_fixed_word_read(drc, compiler, desc, REG_RAX, FALSE, CONSTVAL(rs) + SIMMVAL, 0);
+ else
+ was_fixed = emit_fixed_double_read(drc, compiler, desc, REG_RAX, CONSTVAL(rs) + SIMMVAL, 0);
+ }
+
+ /* otherwise, we just need to call the appropriate subroutine */
+ else
+ {
+ if (ISCONST(rs))
+ emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL
+ else
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL]
+ if (size == 4)
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call general.read_word_unsigned
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_double); // call general.read_double
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ }
+
+ /* complete the load by moving to the destination address */
+ if (size == 4)
+ emit_mov_m32_r32(DRCTOP, MDRC(dest), REG_EAX); // mov [dest],eax
+ else
+ emit_mov_m64_r64(DRCTOP, MDRC(dest), REG_RAX); // mov [dest],rax
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_loadx_cop - compile a COP indexed
+ memory load instruction
+
+ lwxc1 fD,rS,rT
+ ldxc1 fD,rS,rT
+-------------------------------------------------*/
+
+static int compile_loadx_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && ISCONST(rt) && !DISABLE_FIXED_RAM_LOAD)
+ {
+ /* perform a fixed read if we can */
+ if (size == 4)
+ was_fixed = emit_fixed_word_read(drc, compiler, desc, REG_RAX, FALSE, CONSTVAL(rs) + CONSTVAL(rt), 0);
+ else
+ was_fixed = emit_fixed_double_read(drc, compiler, desc, REG_RAX, CONSTVAL(rs) + CONSTVAL(rt), 0);
+ }
+
+ /* otherwise, we just need to call the appropriate subroutine */
+ else
+ {
+ if (ISCONST(rs) && ISCONST(rt))
+ emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + CONSTVAL(rt)); // mov p1,<rs> + <rt>
+ else if (ISCONST(rs))
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rt), CONSTVAL(rs))); // lea p1,[<rt> + <rs>]
+ else if (ISCONST(rt))
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), CONSTVAL(rt))); // lea p1,[<rs> + <rt>]
+ else
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea p1,[<rs> + <rt>]
+ if (size == 4)
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call general.read_word_unsigned
+ else
+ emit_call(DRCTOP, mips3.drcdata->general.read_double); // call general.read_double
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+ }
+
+ /* complete the load by moving to the destination address */
+ if (size == 4)
+ emit_mov_m32_r32(DRCTOP, MDRC(dest), REG_EAX); // mov [dest],eax
+ else
+ emit_mov_m64_r64(DRCTOP, MDRC(dest), REG_RAX); // mov [dest],rax
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_load_word_partial - compile a lwl/lwr
+ instruction
+
+ lwl rT,SIMM(rS)
+ lwr rT,SIMM(rS)
+-------------------------------------------------*/
+
+static int compile_load_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source/dest registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD)
+ {
+ INT32 addr = CONSTVAL(rs) + SIMMVAL;
+ int shift = (addr & 3) * 8;
+
+ /* shift the opposite amount depending on right/left and endianness */
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ shift ^= 0x18;
+
+ /* read the double value into rax, handling exceptions */
+ was_fixed = emit_fixed_word_read(drc, compiler, desc, REG_RAX, FALSE, addr, is_right ? (~((UINT32)-1 << shift)) : (~((UINT32)-1 >> shift)));
+
+ /* handle writing back the register */
+ if (RTREG != 0)
+ {
+ UINT32 mask = is_right ? (~((UINT32)-1 >> shift)) : (~((UINT32)-1 << shift));
+
+ /* AND rT with the mask */
+ emit_and_r32_imm(DRCTOP, X86REG(rt), mask); // and <rt>,mask
+
+ /* shift and merge rax into the X86REG(rt) */
+ if (!is_right)
+ emit_shl_r32_imm(DRCTOP, REG_EAX, shift); // shl eax,shift
+ else
+ emit_shr_r32_imm(DRCTOP, REG_EAX, shift); // shr eax,shift
+ emit_or_r32_r32(DRCTOP, X86REG(rt), REG_EAX); // or <rtdst>,eax
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rt), X86REG(rt)); // movsxd <rt>,<rt>
+ }
+ }
+
+ /* otherwise, we need to compute everything at runtime */
+ else
+ {
+ /* compute the address and mask values */
+ if (ISCONST(rs))
+ emit_mov_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rs) + SIMMVAL); // mov eax,<rs> + SIMMVAL
+ else
+ emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL]
+ emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8]
+ emit_or_r32_imm(DRCTOP, REG_P2, -1); // or p2,-1
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18
+ emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3
+ if (!is_right)
+ emit_shr_r32_cl(DRCTOP, REG_P2); // shr p2,cl
+ else
+ emit_shl_r32_cl(DRCTOP, REG_P2); // shl p2,cl
+ emit_mov_m8_r8(DRCTOP, MBD(REG_RSP, SPOFFS_SAVECL), REG_CL); // mov [sp+savecl],cl
+ emit_not_r32(DRCTOP, REG_P2); // not p2
+ emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax
+ emit_call(DRCTOP, mips3.drcdata->general.read_word_masked); // call general.read_word_masked
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+
+ /* handle writing back the register */
+ if (RTREG != 0)
+ {
+ /* recompute the mask */
+ emit_mov_r8_m8(DRCTOP, REG_CL, MBD(REG_RSP, SPOFFS_SAVECL)); // mov cl,[sp+savecl]
+ emit_or_r32_imm(DRCTOP, REG_P2, -1); // or p2,-1
+ if (!is_right)
+ emit_shl_r32_cl(DRCTOP, REG_P2); // shl p2,cl
+ else
+ emit_shr_r32_cl(DRCTOP, REG_P2); // shr p2,cl
+
+ /* apply the shifts and masks */
+ emit_not_r32(DRCTOP, REG_P2); // not p2
+ if (!is_right)
+ emit_shl_r32_cl(DRCTOP, REG_RAX); // shl rax,cl
+ else
+ emit_shr_r32_cl(DRCTOP, REG_RAX); // shr rax,cl
+ emit_and_r32_r32(DRCTOP, X86REG(rt), REG_P2); // and <rt>,p2
+ emit_or_r32_r32(DRCTOP, X86REG(rt), REG_RAX); // or <rt>,rax
+ emit_movsxd_r64_r32(DRCTOP, X86REG(rt), X86REG(rt)); // movsxd <rt>,<rt>
+ }
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_load_double_partial - compile a
+ ldl/ldr instruction
+
+ ldl rT,SIMM(rS)
+ ldr rT,SIMM(rS)
+-------------------------------------------------*/
+
+static int compile_load_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source/dest registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD)
+ {
+ INT32 addr = CONSTVAL(rs) + SIMMVAL;
+ int shift = (addr & 7) * 8;
+
+ /* shift the opposite amount depending on right/left and endianness */
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ shift ^= 0x38;
+
+ /* read the double value into rax, handling exceptions */
+ was_fixed = emit_fixed_double_read(drc, compiler, desc, REG_RAX, addr, is_right ? (~((UINT64)-1 << shift)) : (~((UINT64)-1 >> shift)));
+
+ /* handle writing back the register */
+ if (RTREG != 0)
+ {
+ UINT64 mask = is_right ? (~((UINT64)-1 >> shift)) : (~((UINT64)-1 << shift));
+
+ /* AND rT with the mask, using 32 bits if we can */
+ if ((INT32)mask == (INT64)mask)
+ emit_and_r64_imm(DRCTOP, X86REG(rt), (INT32)mask); // and <rt>,mask
+ else
+ {
+ emit_mov_r64_imm(DRCTOP, REG_P2, mask); // mov p2,mask
+ emit_and_r64_r64(DRCTOP, X86REG(rt), REG_P2); // and <rt>,p2
+ }
+
+ /* shift and merge rax into the X86REG(rt) */
+ if (!is_right)
+ emit_shl_r64_imm(DRCTOP, REG_RAX, shift); // shl rax,shift
+ else
+ emit_shr_r64_imm(DRCTOP, REG_RAX, shift); // shr rax,shift
+ emit_or_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // or <rt>,rax
+ }
+ }
+
+ /* otherwise, we need to compute everything at runtime */
+ else
+ {
+ /* compute the address and mask values */
+ if (ISCONST(rs))
+ emit_mov_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rs) + SIMMVAL); // mov eax,<rs> + SIMMVAL
+ else
+ emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL]
+ emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8]
+ emit_or_r64_imm(DRCTOP, REG_P2, -1); // or p2,-1
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ emit_xor_r32_imm(DRCTOP, REG_ECX, 0x38); // xor ecx,0x38
+ emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7
+ if (!is_right)
+ emit_shr_r64_cl(DRCTOP, REG_P2); // shr p2,cl
+ else
+ emit_shl_r64_cl(DRCTOP, REG_P2); // shl p2,cl
+ emit_mov_m8_r8(DRCTOP, MBD(REG_RSP, SPOFFS_SAVECL), REG_CL); // mov [sp+savecl],cl
+ emit_not_r64(DRCTOP, REG_P2); // not p2
+ emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax
+ emit_call(DRCTOP, mips3.drcdata->general.read_double_masked); // call general.read_double_masked
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception);
+
+ /* handle writing back the register */
+ if (RTREG != 0)
+ {
+ /* recompute the mask */
+ emit_mov_r8_m8(DRCTOP, REG_CL, MBD(REG_RSP, SPOFFS_SAVECL)); // mov cl,[sp+savecl]
+ emit_or_r64_imm(DRCTOP, REG_P2, -1); // or p2,-1
+ if (!is_right)
+ emit_shl_r64_cl(DRCTOP, REG_P2); // shl p2,cl
+ else
+ emit_shr_r64_cl(DRCTOP, REG_P2); // shr p2,cl
+
+ /* apply the shifts and masks */
+ emit_not_r64(DRCTOP, REG_P2); // not p2
+ if (!is_right)
+ emit_shl_r64_cl(DRCTOP, REG_RAX); // shl rax,cl
+ else
+ emit_shr_r64_cl(DRCTOP, REG_RAX); // shr rax,cl
+ emit_and_r64_r64(DRCTOP, X86REG(rt), REG_P2); // and <rt>,p2
+ emit_or_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // or <rt>,rax
+ }
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_store - compile a memory store
+ instruction
+
+ sb rT,SIMM(rS)
+ sh rT,SIMM(rS)
+ sw rT,SIMM(rS)
+ sd rT,SIMM(rS)
+-------------------------------------------------*/
+
+static int compile_store(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE)
+ {
+ /* perform a fixed write if we can */
+ if (size == 1)
+ was_fixed = emit_fixed_byte_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL);
+ else if (size == 2)
+ was_fixed = emit_fixed_half_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL);
+ else if (size == 4)
+ was_fixed = emit_fixed_word_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL, 0);
+ else
+ was_fixed = emit_fixed_double_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL, 0);
+ }
+
+ /* otherwise, we just need to call the appropriate subroutine */
+ else
+ {
+ if (ISCONST(rs))
+ emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL
+ else
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL]
+ emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2,<rt>
+ if (size == 1)
+ emit_call(DRCTOP, mips3.drcdata->general.write_byte); // call general.write_byte
+ else if (size == 2)
+ emit_call(DRCTOP, mips3.drcdata->general.write_half); // call general.write_half
+ else if (size == 4)
+ emit_call(DRCTOP, mips3.drcdata->general.write_word); // call general.write_word
+ else if (size == 8)
+ emit_call(DRCTOP, mips3.drcdata->general.write_double); // call general.write_double
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_store_cop - compile a COP memory
+ store instruction
+
+ swc1 rT,SIMM(rs)
+ swc2 rT,SIMM(rs)
+ sdc1 rT,SIMM(rs)
+ sdc2 rT,SIMM(rs)
+-------------------------------------------------*/
+
+static int compile_store_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+
+ /* load the value into P2 */
+ if (size == 4)
+ emit_mov_r32_m32(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src]
+ else
+ emit_mov_r64_m64(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src]
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE)
+ {
+ /* perform a fixed write if we can */
+ if (size == 4)
+ was_fixed = emit_fixed_word_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + SIMMVAL, 0);
+ else
+ was_fixed = emit_fixed_double_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + SIMMVAL, 0);
+ }
+
+ /* otherwise, we just need to call the appropriate subroutine */
+ else
+ {
+ if (ISCONST(rs))
+ emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL
+ else
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL]
+ if (size == 4)
+ emit_call(DRCTOP, mips3.drcdata->general.write_word); // call general.write_word
+ else if (size == 8)
+ emit_call(DRCTOP, mips3.drcdata->general.write_double); // call general.write_double
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_storex_cop - compile a COP indexed
+ memory store instruction
+
+ swxc1 fD,rS,rT
+ sdxc1 fD,rS,rT
+-------------------------------------------------*/
+
+static int compile_storex_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* load the value into P2 */
+ if (size == 4)
+ emit_mov_r32_m32(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src]
+ else
+ emit_mov_r64_m64(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src]
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && ISCONST(rt) && !DISABLE_FIXED_RAM_STORE)
+ {
+ /* perform a fixed write if we can */
+ if (size == 4)
+ was_fixed = emit_fixed_word_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + CONSTVAL(rt), 0);
+ else
+ was_fixed = emit_fixed_double_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + CONSTVAL(rt), 0);
+ }
+
+ /* otherwise, we just need to call the appropriate subroutine */
+ else
+ {
+ if (ISCONST(rs) && ISCONST(rt))
+ emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + CONSTVAL(rt)); // mov p1,<rs> + <rt>
+ else if (ISCONST(rs))
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rt), CONSTVAL(rs))); // lea p1,[<rt> + <rs>]
+ else if (ISCONST(rt))
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), CONSTVAL(rt))); // lea p1,[<rs> + <rt>]
+ else
+ emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea p1,[<rs> + <rt>]
+ if (size == 4)
+ emit_call(DRCTOP, mips3.drcdata->general.write_word); // call general.write_word
+ else if (size == 8)
+ emit_call(DRCTOP, mips3.drcdata->general.write_double); // call general.write_double
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_store_word_partial - compile a swl/swr
+ instruction
+
+ swl rT,SIMM(rS)
+ swr rT,SIMM(rS)
+-------------------------------------------------*/
+
+static int compile_store_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE)
+ {
+ INT32 addr = CONSTVAL(rs) + SIMMVAL;
+ int shift = (addr & 3) * 8;
+ UINT32 mask;
+
+ /* shift the opposite amount depending on right/left and endianness */
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ shift ^= 0x18;
+
+ /* if rT is not constant, load and shift it */
+ if (!ISCONST(rt))
+ {
+ emit_mov_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // mov eax,X86REG(rt)
+ emit_shr_r32_imm(DRCTOP, REG_EAX, shift); // shr eax,shift
+ rt = MAKE_DESTREG(REG_EAX);
+ }
+
+ /* compute shifted value and mask */
+ if (!is_right)
+ {
+ if (ISCONST(rt))
+ rt = MAKE_CONST((UINT32)CONSTVAL(rt) >> shift);
+ mask = ~((UINT64)-1 >> shift);
+ }
+ else
+ {
+ if (ISCONST(rt))
+ rt = MAKE_CONST((UINT32)CONSTVAL(rt) << shift);
+ mask = ~((UINT64)-1 << shift);
+ }
+
+ /* perform a fixed write */
+ was_fixed = emit_fixed_word_write(drc, compiler, desc, rt, addr & ~3, mask);
+ }
+
+ /* otherwise, we need to compute everything at runtime */
+ else
+ {
+ /* compute the address and mask values */
+ emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL]
+ emit_or_r32_imm(DRCTOP, REG_P3, -1); // or p3,-1
+ emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8]
+ emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2,<rt>
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18
+ emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3
+ if (!is_right)
+ {
+ emit_shr_r32_cl(DRCTOP, REG_P3); // shr p3,cl
+ emit_shr_r32_cl(DRCTOP, REG_P2); // shr p2,cl
+ }
+ else
+ {
+ emit_shl_r32_cl(DRCTOP, REG_P3); // shl p3,cl
+ emit_shl_r32_cl(DRCTOP, REG_P2); // shl p2,cl
+ }
+ emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax
+ emit_not_r32(DRCTOP, REG_P3); // not p3
+
+ /* perform the write */
+ emit_call(DRCTOP, mips3.drcdata->general.write_word_masked); // call general.write_word_masked
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ compile_store_double_partial - compile a
+ sdl/sdr instruction
+
+ sdl rT,SIMM(rS)
+ sdr rT,SIMM(rS)
+-------------------------------------------------*/
+
+static int compile_store_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right)
+{
+ UINT32 op = *desc->opptr.l;
+ int was_fixed = FALSE;
+ mipsreg_state rs, rt;
+
+ /* allocate source registers */
+ rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC);
+ rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC);
+
+ /* if rS is constant, we know the address at compile time; this simplifies the code */
+ if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE)
+ {
+ INT32 addr = CONSTVAL(rs) + SIMMVAL;
+ int shift = (addr & 7) * 8;
+ UINT64 mask;
+
+ /* shift the opposite amount depending on right/left and endianness */
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ shift ^= 0x38;
+
+ /* if rT is not constant, load and shift it */
+ if (!ISCONST(rt))
+ {
+ emit_mov_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // mov rax,X86REG(rt)
+ emit_shr_r64_imm(DRCTOP, REG_RAX, shift); // shr rax,shift
+ rt = MAKE_DESTREG(REG_RAX);
+ }
+
+ /* compute shifted value and mask */
+ if (!is_right)
+ {
+ if (ISCONST(rt))
+ rt = MAKE_CONST((UINT64)CONSTVAL(rt) >> shift);
+ mask = ~((UINT64)-1 >> shift);
+ }
+ else
+ {
+ if (ISCONST(rt))
+ rt = MAKE_CONST((UINT64)CONSTVAL(rt) << shift);
+ mask = ~((UINT64)-1 << shift);
+ }
+
+ /* perform a fixed write */
+ was_fixed = emit_fixed_double_write(drc, compiler, desc, rt, addr & ~7, mask);
+ }
+
+ /* otherwise, we need to compute everything at runtime */
+ else
+ {
+ /* compute the address and mask values */
+ emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL]
+ emit_or_r64_imm(DRCTOP, REG_P3, -1); // or p3,-1
+ emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8]
+ emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2,<rt>
+ if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right))
+ emit_xor_r32_imm(DRCTOP, REG_ECX, 0x38); // xor ecx,0x38
+ emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7
+ if (!is_right)
+ {
+ emit_shr_r64_cl(DRCTOP, REG_P3); // shr p3,cl
+ emit_shr_r64_cl(DRCTOP, REG_P2); // shr p2,cl
+ }
+ else
+ {
+ emit_shl_r64_cl(DRCTOP, REG_P3); // shl p3,cl
+ emit_shl_r64_cl(DRCTOP, REG_P2); // shl p2,cl
+ }
+ emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax
+ emit_not_r64(DRCTOP, REG_P3); // not p3
+
+ /* perform the write */
+ emit_call(DRCTOP, mips3.drcdata->general.write_double_masked); // call general.write_double_masked
+ oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception);
+ }
+
+ /* update cycle counts */
+ if (!was_fixed)
+ emit_update_cycles_pc_and_flush(drc, compiler, desc, 0);
+ return TRUE;
+}