/*************************************************************************** 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 else emit_jcc_near_link(DRCTOP, condition, &oob->link); // jcc } /*------------------------------------------------- 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 } /* 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 } /* 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 } } /* 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); // } /* 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], 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); // /* jump to the exception generator */ emit_jmp(DRCTOP, param); // jmp } /*------------------------------------------------- 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, 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); // /* jump to the exception generator */ emit_jmp(DRCTOP, param); // jmp } /*************************************************************************** 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 ,constant else emit_mov_r64_m64(DRCTOP, regnum, REGADDR(mipsreg)); // mov ,[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], } /* 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=%08llX Cause=%08llX BadVAddr=%08llX 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, ¬ram1); // 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, ¬ram2); // jb notram2 } /* handle a fast RAM read */ if (!(flags & ARW_WRITE)) { append_direct_ram_read(drc, size, flags, raminfo->base, raminfo->start); // emit_ret(DRCTOP); // ret } /* handle a fast RAM write */ else { append_direct_ram_write(drc, size, flags, raminfo->base, raminfo->start); // emit_ret(DRCTOP); // ret } /* resolve links that skipped over us */ if (raminfo->end != 0xffffffff) resolve_link(DRCTOP, ¬ram1); // notram1: if (raminfo->start != 0x00000000) resolve_link(DRCTOP, ¬ram2); // 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, emit_cmovcc_r64_m64(DRCTOP, COND_NZ, REG_RAX, MDRCD(&mips3.drcdata->uncached, ptroffs)); // mov rax, 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, 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 ,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)); // = pc + 8 /* compile the delay slot using temporary compiler state */ assert(desc->delay != NULL); compile_one(drc, &compiler_temp, desc->delay); // /* 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); // else drc_append_dispatcher(drc); // } /*------------------------------------------------------------------ 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 ,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 ,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 ,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 ,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, else if (X86REG(srcreg) != REG_P2) emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2, 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], } /* 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); // 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; memset(&link1, 0, sizeof(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 , } 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); // } 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 , } 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, } 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 , } 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, } 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); // 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 ,eax else emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov ,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 ,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, 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, 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); // drc_append_dispatcher(drc); // 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 ,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 ,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 ,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 , emit_mov_m32_r32(DRCTOP, FPR32ADDR(RDREG), tempreg); // mov cpr1[rd], } 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 , emit_mov_m64_r64(DRCTOP, FPR64ADDR(RDREG), tempreg); // mov cpr1[rd], } 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, 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); // 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); // 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, emit_load_register_value(drc, compiler, X86REG(rd), rt); // mov , /* 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 , else if (shift_type == 2) emit_shr_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // shr , else emit_sar_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // sar , } /* 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 ,cl else if (shift_type == 2) emit_shr_r32_cl(DRCTOP, X86REG(rd)); // shr ,cl else emit_sar_r32_cl(DRCTOP, X86REG(rd)); // sar ,cl } emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd , } 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, emit_load_register_value(drc, compiler, X86REG(rd), rt); // mov , /* 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 , else if (shift_type == 2) emit_shr_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // shr , else emit_sar_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // sar , } /* 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 ,cl else if (shift_type == 2) emit_shr_r64_cl(DRCTOP, X86REG(rd)); // shr ,cl else emit_sar_r64_cl(DRCTOP, X86REG(rd)); // sar ,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 , 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 , 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 , 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 , condition = condtype[condition] ^ 1; } /* otherwise, compare the two registers with a normal condition */ else { emit_cmp_r64_imm(DRCTOP, X86REG(rs), X86REG(rt)); // cmp , 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 , 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 , 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 , emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip } /* append the branch logic */ emit_delay_slot_and_branch(drc, compiler, desc, 0); // 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 ,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); // 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, if (!ISCONST(rt)) emit_add_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // add eax, else if (CONSTVAL(rt) != 0) emit_add_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rt)); // add eax, 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 ,eax } else { if (ISCONST(rs) && CONSTVAL(rs) == 0) emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rt)); // movsxd , else if (ISCONST(rt) && CONSTVAL(rt) == 0) emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rs)); // movsxd , else if (ISCONST(rs)) { emit_lea_r32_m32(DRCTOP, X86REG(rd), MBD(X86REG(rt), CONSTVAL(rs))); // lea ,[ + ] emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd , } else if (ISCONST(rt)) { emit_lea_r32_m32(DRCTOP, X86REG(rd), MBD(X86REG(rs), CONSTVAL(rt))); // lea ,[ + ] emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd , } else { emit_lea_r32_m32(DRCTOP, X86REG(rd), MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea ,[ + ] emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd , } } /* 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, if (!ISCONST(rt)) emit_add_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // add rax, else if (CONSTVAL(rt) != 0) emit_add_r64_imm(DRCTOP, REG_RAX, CONSTVAL(rt)); // add rax, 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 ,rax } else { if (ISCONST(rs)) emit_lea_r64_m64(DRCTOP, X86REG(rd), MBD(X86REG(rt), CONSTVAL(rs))); // lea ,[ + ] else if (ISCONST(rt)) emit_lea_r64_m64(DRCTOP, X86REG(rd), MBD(X86REG(rs), CONSTVAL(rt))); // lea ,[ + ] else emit_lea_r64_m64(DRCTOP, X86REG(rd), MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea ,[ + ] } /* 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, if (!ISCONST(rt)) emit_sub_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // sub eax, else if (CONSTVAL(rt) != 0) emit_sub_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rt)); // sub eax, 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 ,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 , if (!ISCONST(rt)) emit_sub_r32_r32(DRCTOP, X86REG(rd), X86REG(rt)); // sub , else if (CONSTVAL(rt) != 0) emit_sub_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // sub , } /* otherwise, we negate rT and add rS */ else { emit_neg_r32(DRCTOP, X86REG(rd)); // neg if (!ISCONST(rs)) emit_add_r32_r32(DRCTOP, X86REG(rd), X86REG(rs)); // add , else if (CONSTVAL(rs) != 0) emit_add_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs)); // add , } emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd , } /* 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, if (!ISCONST(rt)) emit_sub_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // sub rax, else if (CONSTVAL(rt) != 0) emit_sub_r64_imm(DRCTOP, REG_RAX, CONSTVAL(rt)); // sub rax, 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 ,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 , if (!ISCONST(rt)) emit_sub_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // sub , else if (CONSTVAL(rt) != 0) emit_sub_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // sub , } /* otherwise, we negate rT and add rS */ else { emit_neg_r64(DRCTOP, X86REG(rd)); // neg if (!ISCONST(rs)) emit_add_r64_r64(DRCTOP, X86REG(rd), X86REG(rs)); // add , else if (CONSTVAL(rs) != 0) emit_add_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs)); // add , } } /* 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, emit_load_register_value(drc, compiler, mulreg, rt); // mov , if (is_unsigned) emit_mul_r32(DRCTOP, mulreg); // mul else emit_imul_r32(DRCTOP, mulreg); // imul emit_movsxd_r64_r32(DRCTOP, X86REG(hi), REG_EDX); // movsxd ,edx emit_movsxd_r64_r32(DRCTOP, X86REG(lo), REG_EAX); // movsxd ,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, emit_load_register_value(drc, compiler, mulreg, rt); // mov , if (is_unsigned) emit_mul_r64(DRCTOP, mulreg); // mul else emit_imul_r64(DRCTOP, mulreg); // imul emit_mov_r64_r64(DRCTOP, X86REG(hi), REG_RDX); // mov ,rdx emit_mov_r64_r64(DRCTOP, X86REG(lo), REG_RAX); // mov ,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, emit_load_register_value(drc, compiler, divreg, rt); // mov , if (!ISCONST(rt)) { emit_test_r32_r32(DRCTOP, divreg, divreg); // test , 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 } else { emit_cdq(DRCTOP); // cdq emit_idiv_r32(DRCTOP, divreg); // idiv } emit_movsxd_r64_r32(DRCTOP, X86REG(hi), REG_EDX); // movsxd ,edx emit_movsxd_r64_r32(DRCTOP, X86REG(lo), REG_EAX); // movsxd ,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, emit_load_register_value(drc, compiler, divreg, rt); // mov , emit_test_r64_r64(DRCTOP, divreg, divreg); // test , 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 } else { emit_cqo(DRCTOP); // cqo emit_idiv_r64(DRCTOP, divreg); // idiv } emit_mov_r64_r64(DRCTOP, X86REG(hi), REG_EDX); // mov ,edx emit_mov_r64_r64(DRCTOP, X86REG(lo), REG_EAX); // mov ,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 , /* 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 , else if (operation == 1) emit_or_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // or , else if (operation == 2) emit_xor_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // xor , else if (operation == 3) { emit_or_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // or , emit_not_r64(DRCTOP, X86REG(rd)); // not } } else { if (operation == 0) emit_and_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // and ,CONSTVAL(rt) else if (operation == 1 && CONSTVAL(rt) != 0) emit_or_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // or ,CONSTVAL(rt) else if (operation == 2 && CONSTVAL(rt) != 0) emit_xor_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // xor ,CONSTVAL(rt) else if (operation == 3) { if (CONSTVAL(rt) != 0) emit_or_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // or ,CONSTVAL(rt) emit_not_r64(DRCTOP, X86REG(rd)); // not } } } 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 , if (ISCONST(rt)) emit_cmp_r64_imm(DRCTOP, rsreg, CONSTVAL(rt)); // cmp , else emit_cmp_r64_r64(DRCTOP, rsreg, X86REG(rt)); // cmp , emit_setcc_r8(DRCTOP, is_unsigned ? COND_B : COND_L, X86REG(rd)); // setb/l emit_movzx_r32_r8(DRCTOP, X86REG(rd), X86REG(rd)); // movzx , } 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, + SIMMVAL else emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[ + 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 ,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, + SIMMVAL else emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[ + 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, + else if (ISCONST(rs)) emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rt), CONSTVAL(rs))); // lea p1,[ + ] else if (ISCONST(rt)) emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), CONSTVAL(rt))); // lea p1,[ + ] else emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea p1,[ + ] 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 ,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 ,eax emit_movsxd_r64_r32(DRCTOP, X86REG(rt), X86REG(rt)); // movsxd , } } /* 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, + SIMMVAL else emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[ + 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 ,p2 emit_or_r32_r32(DRCTOP, X86REG(rt), REG_RAX); // or ,rax emit_movsxd_r64_r32(DRCTOP, X86REG(rt), X86REG(rt)); // movsxd , } } /* 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 ,mask else { emit_mov_r64_imm(DRCTOP, REG_P2, mask); // mov p2,mask emit_and_r64_r64(DRCTOP, X86REG(rt), REG_P2); // and ,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 ,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, + SIMMVAL else emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[ + 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 ,p2 emit_or_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // or ,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, + SIMMVAL else emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[ + SIMMVAL] emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2, 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, + SIMMVAL else emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[ + 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, + else if (ISCONST(rs)) emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rt), CONSTVAL(rs))); // lea p1,[ + ] else if (ISCONST(rt)) emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), CONSTVAL(rt))); // lea p1,[ + ] else emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea p1,[ + ] 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,[ + 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, 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,[ + 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, 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; }