/*************************************************************************** drcbex86.c 32-bit x86 back-end for the universal machine language. **************************************************************************** Copyright Aaron Giles All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name 'MAME' nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************** Future improvements/changes: * Optimize to avoid unnecessary reloads - especially EDX for 64-bit operations - also FCMP/FLAGS has unnecessary PUSHF/POP EAX * Identify common pairs and optimize output * Convert SUB a,0,b to NEG * Optimize, e.g., and [r5],i0,$FF to use ebx as temporary register (avoid initial move) if i0 is not needed going forward **************************************************************************** --------------- ABI/conventions --------------- Registers: EAX - volatile, function return value EBX - non-volatile ECX - volatile EDX - volatile, function return value (upper 32 bits) ESI - non-volatile EDI - non-volatile EBP - non-volatile FP stack - volatile --------------- Execution model --------------- Registers: EAX - scratch register EBX - maps to I0 (low 32 bits) ECX - scratch register EDX - scratch register ESI - maps to I1 (low 32 bits) EDI - maps to I2 (low 32 bits) EBP - maps to I3 (low 32 bits) FP stack - scratch registers Entry point: Assumes 1 parameter passed, which is the codeptr of the code to execute once the environment is set up. Exit point: Assumes exit value is in EAX. Entry stack: [esp] - return [esp+4] - input parameter (entry handle) Runtime stack: [esp] - param 0 [esp+4] - param 1 [esp+8] - param 2 [esp+12] - param 3 [esp+16] - param 4 [esp+20] - alignment [esp+24] - alignment [esp+28] - saved ebp [esp+32] - saved edi [esp+36] - saved esi [esp+40] - saved ebx [esp+44] - ret [esp+48] - input parameter (entry handle) **************************************************************************/ #include "emu.h" #include "debugger.h" #include "drcuml.h" #include "drcbex86.h" using namespace uml; using namespace x86emit; //************************************************************************** // DEBUGGING //************************************************************************** #define LOG_HASHJMPS (0) //************************************************************************** // CONSTANTS //************************************************************************** const UINT32 PTYPE_M = 1 << parameter::PTYPE_MEMORY; const UINT32 PTYPE_I = 1 << parameter::PTYPE_IMMEDIATE; const UINT32 PTYPE_R = 1 << parameter::PTYPE_INT_REGISTER; const UINT32 PTYPE_F = 1 << parameter::PTYPE_FLOAT_REGISTER; const UINT32 PTYPE_MI = PTYPE_M | PTYPE_I; const UINT32 PTYPE_RI = PTYPE_R | PTYPE_I; const UINT32 PTYPE_MR = PTYPE_M | PTYPE_R; const UINT32 PTYPE_MRI = PTYPE_M | PTYPE_R | PTYPE_I; const UINT32 PTYPE_MF = PTYPE_M | PTYPE_F; //************************************************************************** // MACROS //************************************************************************** #define X86_CONDITION(condition) (condition_map[condition - uml::COND_Z]) #define X86_NOT_CONDITION(condition) (condition_map[condition - uml::COND_Z] ^ 1) #define assert_no_condition(inst) assert((inst).condition() == uml::COND_ALWAYS) #define assert_any_condition(inst) assert((inst).condition() == uml::COND_ALWAYS || ((inst).condition() >= uml::COND_Z && (inst).condition() < uml::COND_MAX)) #define assert_no_flags(inst) assert((inst).flags() == 0) #define assert_flags(inst, valid) assert(((inst).flags() & ~(valid)) == 0) //************************************************************************** // GLOBAL VARIABLES //************************************************************************** drcbe_x86::opcode_generate_func drcbe_x86::s_opcode_table[OP_MAX]; // size-to-mask table static const UINT64 size_to_mask[] = { 0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0, U64(0xffffffffffffffff) }; // register mapping tables static const UINT8 int_register_map[REG_I_COUNT] = { REG_EBX, REG_ESI, REG_EDI, REG_EBP }; // flags mapping tables static UINT8 flags_map[0x1000]; static UINT32 flags_unmap[0x20]; // condition mapping table static const UINT8 condition_map[uml::COND_MAX - uml::COND_Z] = { x86emit::COND_Z, // COND_Z = 0x80, requires Z x86emit::COND_NZ, // COND_NZ, requires Z x86emit::COND_S, // COND_S, requires S x86emit::COND_NS, // COND_NS, requires S x86emit::COND_C, // COND_C, requires C x86emit::COND_NC, // COND_NC, requires C x86emit::COND_O, // COND_V, requires V x86emit::COND_NO, // COND_NV, requires V x86emit::COND_P, // COND_U, requires U x86emit::COND_NP, // COND_NU, requires U x86emit::COND_A, // COND_A, requires CZ x86emit::COND_BE, // COND_BE, requires CZ x86emit::COND_G, // COND_G, requires SVZ x86emit::COND_LE, // COND_LE, requires SVZ x86emit::COND_L, // COND_L, requires SV x86emit::COND_GE, // COND_GE, requires SV }; // FPU control register mapping static const UINT16 fp_control[4] = { 0x0e3f, // ROUND_TRUNC 0x023f, // ROUND_ROUND 0x0a3f, // ROUND_CEIL 0x063f // ROUND_FLOOR }; //************************************************************************** // TABLES //************************************************************************** const drcbe_x86::opcode_table_entry drcbe_x86::s_opcode_table_source[] = { // Compile-time opcodes { uml::OP_HANDLE, &drcbe_x86::op_handle }, // HANDLE handle { uml::OP_HASH, &drcbe_x86::op_hash }, // HASH mode,pc { uml::OP_LABEL, &drcbe_x86::op_label }, // LABEL imm { uml::OP_COMMENT, &drcbe_x86::op_comment }, // COMMENT string { uml::OP_MAPVAR, &drcbe_x86::op_mapvar }, // MAPVAR mapvar,value // Control Flow Operations { uml::OP_NOP, &drcbe_x86::op_nop }, // NOP { uml::OP_DEBUG, &drcbe_x86::op_debug }, // DEBUG pc { uml::OP_EXIT, &drcbe_x86::op_exit }, // EXIT src1[,c] { uml::OP_HASHJMP, &drcbe_x86::op_hashjmp }, // HASHJMP mode,pc,handle { uml::OP_JMP, &drcbe_x86::op_jmp }, // JMP imm[,c] { uml::OP_EXH, &drcbe_x86::op_exh }, // EXH handle,param[,c] { uml::OP_CALLH, &drcbe_x86::op_callh }, // CALLH handle[,c] { uml::OP_RET, &drcbe_x86::op_ret }, // RET [c] { uml::OP_CALLC, &drcbe_x86::op_callc }, // CALLC func,ptr[,c] { uml::OP_RECOVER, &drcbe_x86::op_recover }, // RECOVER dst,mapvar // Internal Register Operations { uml::OP_SETFMOD, &drcbe_x86::op_setfmod }, // SETFMOD src { uml::OP_GETFMOD, &drcbe_x86::op_getfmod }, // GETFMOD dst { uml::OP_GETEXP, &drcbe_x86::op_getexp }, // GETEXP dst { uml::OP_GETFLGS, &drcbe_x86::op_getflgs }, // GETFLGS dst[,f] { uml::OP_SAVE, &drcbe_x86::op_save }, // SAVE dst { uml::OP_RESTORE, &drcbe_x86::op_restore }, // RESTORE dst // Integer Operations { uml::OP_LOAD, &drcbe_x86::op_load }, // LOAD dst,base,index,size { uml::OP_LOADS, &drcbe_x86::op_loads }, // LOADS dst,base,index,size { uml::OP_STORE, &drcbe_x86::op_store }, // STORE base,index,src,size { uml::OP_READ, &drcbe_x86::op_read }, // READ dst,src1,spacesize { uml::OP_READM, &drcbe_x86::op_readm }, // READM dst,src1,mask,spacesize { uml::OP_WRITE, &drcbe_x86::op_write }, // WRITE dst,src1,spacesize { uml::OP_WRITEM, &drcbe_x86::op_writem }, // WRITEM dst,src1,spacesize { uml::OP_CARRY, &drcbe_x86::op_carry }, // CARRY src,bitnum { uml::OP_SET, &drcbe_x86::op_set }, // SET dst,c { uml::OP_MOV, &drcbe_x86::op_mov }, // MOV dst,src[,c] { uml::OP_SEXT, &drcbe_x86::op_sext }, // SEXT dst,src { uml::OP_ROLAND, &drcbe_x86::op_roland }, // ROLAND dst,src1,src2,src3 { uml::OP_ROLINS, &drcbe_x86::op_rolins }, // ROLINS dst,src1,src2,src3 { uml::OP_ADD, &drcbe_x86::op_add }, // ADD dst,src1,src2[,f] { uml::OP_ADDC, &drcbe_x86::op_addc }, // ADDC dst,src1,src2[,f] { uml::OP_SUB, &drcbe_x86::op_sub }, // SUB dst,src1,src2[,f] { uml::OP_SUBB, &drcbe_x86::op_subc }, // SUBB dst,src1,src2[,f] { uml::OP_CMP, &drcbe_x86::op_cmp }, // CMP src1,src2[,f] { uml::OP_MULU, &drcbe_x86::op_mulu }, // MULU dst,edst,src1,src2[,f] { uml::OP_MULS, &drcbe_x86::op_muls }, // MULS dst,edst,src1,src2[,f] { uml::OP_DIVU, &drcbe_x86::op_divu }, // DIVU dst,edst,src1,src2[,f] { uml::OP_DIVS, &drcbe_x86::op_divs }, // DIVS dst,edst,src1,src2[,f] { uml::OP_AND, &drcbe_x86::op_and }, // AND dst,src1,src2[,f] { uml::OP_TEST, &drcbe_x86::op_test }, // TEST src1,src2[,f] { uml::OP_OR, &drcbe_x86::op_or }, // OR dst,src1,src2[,f] { uml::OP_XOR, &drcbe_x86::op_xor }, // XOR dst,src1,src2[,f] { uml::OP_LZCNT, &drcbe_x86::op_lzcnt }, // LZCNT dst,src[,f] { uml::OP_BSWAP, &drcbe_x86::op_bswap }, // BSWAP dst,src { uml::OP_SHL, &drcbe_x86::op_shl }, // SHL dst,src,count[,f] { uml::OP_SHR, &drcbe_x86::op_shr }, // SHR dst,src,count[,f] { uml::OP_SAR, &drcbe_x86::op_sar }, // SAR dst,src,count[,f] { uml::OP_ROL, &drcbe_x86::op_rol }, // ROL dst,src,count[,f] { uml::OP_ROLC, &drcbe_x86::op_rolc }, // ROLC dst,src,count[,f] { uml::OP_ROR, &drcbe_x86::op_ror }, // ROR dst,src,count[,f] { uml::OP_RORC, &drcbe_x86::op_rorc }, // RORC dst,src,count[,f] // Floating Point Operations { uml::OP_FLOAD, &drcbe_x86::op_fload }, // FLOAD dst,base,index { uml::OP_FSTORE, &drcbe_x86::op_fstore }, // FSTORE base,index,src { uml::OP_FREAD, &drcbe_x86::op_fread }, // FREAD dst,space,src1 { uml::OP_FWRITE, &drcbe_x86::op_fwrite }, // FWRITE space,dst,src1 { uml::OP_FMOV, &drcbe_x86::op_fmov }, // FMOV dst,src1[,c] { uml::OP_FTOINT, &drcbe_x86::op_ftoint }, // FTOINT dst,src1,size,round { uml::OP_FFRINT, &drcbe_x86::op_ffrint }, // FFRINT dst,src1,size { uml::OP_FFRFLT, &drcbe_x86::op_ffrflt }, // FFRFLT dst,src1,size { uml::OP_FRNDS, &drcbe_x86::op_frnds }, // FRNDS dst,src1 { uml::OP_FADD, &drcbe_x86::op_fadd }, // FADD dst,src1,src2 { uml::OP_FSUB, &drcbe_x86::op_fsub }, // FSUB dst,src1,src2 { uml::OP_FCMP, &drcbe_x86::op_fcmp }, // FCMP src1,src2 { uml::OP_FMUL, &drcbe_x86::op_fmul }, // FMUL dst,src1,src2 { uml::OP_FDIV, &drcbe_x86::op_fdiv }, // FDIV dst,src1,src2 { uml::OP_FNEG, &drcbe_x86::op_fneg }, // FNEG dst,src1 { uml::OP_FABS, &drcbe_x86::op_fabs }, // FABS dst,src1 { uml::OP_FSQRT, &drcbe_x86::op_fsqrt }, // FSQRT dst,src1 { uml::OP_FRECIP, &drcbe_x86::op_frecip }, // FRECIP dst,src1 { uml::OP_FRSQRT, &drcbe_x86::op_frsqrt } // FRSQRT dst,src1 }; //************************************************************************** // INLINE FUNCTIONS //************************************************************************** //------------------------------------------------- // param_normalize - convert a full parameter // into a reduced set //------------------------------------------------- drcbe_x86::be_parameter::be_parameter(drcbe_x86 &drcbe, const parameter ¶m, UINT32 allowed) { int regnum; switch (param.type()) { // immediates pass through case parameter::PTYPE_IMMEDIATE: assert(allowed & PTYPE_I); *this = param.immediate(); break; // memory passes through case parameter::PTYPE_MEMORY: assert(allowed & PTYPE_M); *this = make_memory(param.memory()); break; // if a register maps to a register, keep it as a register; otherwise map it to memory case parameter::PTYPE_INT_REGISTER: assert(allowed & PTYPE_R); assert(allowed & PTYPE_M); regnum = int_register_map[param.ireg() - REG_I0]; if (regnum != 0) *this = make_ireg(regnum); else *this = make_memory(&drcbe.m_state.r[param.ireg() - REG_I0]); break; // if a register maps to a register, keep it as a register; otherwise map it to memory case parameter::PTYPE_FLOAT_REGISTER: assert(allowed & PTYPE_F); assert(allowed & PTYPE_M); *this = make_memory(&drcbe.m_state.f[param.freg() - REG_F0]); break; // everything else is unexpected default: fatalerror("Unexpected parameter type\n"); break; } } //------------------------------------------------- // select_register - select a register to use, // avoiding conflicts with the optional // checkparam //------------------------------------------------- inline int drcbe_x86::be_parameter::select_register(int defreg) const { if (m_type == PTYPE_INT_REGISTER || m_type == PTYPE_FLOAT_REGISTER || m_type == PTYPE_VECTOR_REGISTER) return m_value; return defreg; } inline int drcbe_x86::be_parameter::select_register(int defreg, const be_parameter &checkparam) const { if (*this == checkparam) return defreg; return select_register(defreg); } inline int drcbe_x86::be_parameter::select_register(int defreg, const be_parameter &checkparam, const be_parameter &checkparam2) const { if (*this == checkparam || *this == checkparam2) return defreg; return select_register(defreg); } //------------------------------------------------- // select_register - select a register to use, // avoiding conflicts with the optional // checkparam //------------------------------------------------- inline void drcbe_x86::normalize_commutative(be_parameter &inner, be_parameter &outer) { // if the inner parameter is a memory operand, push it to the outer if (inner.is_memory()) { be_parameter temp = inner; inner = outer; outer = temp; } // if the inner parameter is an immediate, push it to the outer if (inner.is_immediate()) { be_parameter temp = inner; inner = outer; outer = temp; } } //------------------------------------------------- // emit_combine_z_flags - combine the Z flag from // two 32-bit operations //------------------------------------------------- inline void drcbe_x86::emit_combine_z_flags(x86code *&dst) { // this assumes that the flags from the low 32-bit op are on the stack // and the flags from the high 32-bit op are live emit_pushf(dst); // pushf emit_mov_r32_m32(dst, REG_ECX, MBD(REG_ESP, 4)); // mov ecx,[esp+4] emit_or_r32_imm(dst, REG_ECX, ~0x40); // or ecx,~0x40 emit_and_m32_r32(dst, MBD(REG_ESP, 0), REG_ECX); // and [esp],ecx emit_popf(dst); // popf emit_lea_r32_m32(dst, REG_ESP, MBD(REG_ESP, 4)); // lea esp,[esp+4] } //------------------------------------------------- // emit_combine_z_shl_flags - combine the Z // flags from two 32-bit shift left operations //------------------------------------------------- inline void drcbe_x86::emit_combine_z_shl_flags(x86code *&dst) { // this assumes that the flags from the high 32-bit op are on the stack // and the flags from the low 32-bit op are live emit_pushf(dst); // pushf emit_pop_r32(dst, REG_ECX); // pop ecx emit_or_r32_imm(dst, REG_ECX, ~0x40); // or ecx,~0x40 emit_and_m32_r32(dst, MBD(REG_ESP, 0), REG_ECX); // and [esp],ecx emit_popf(dst); // popf } //------------------------------------------------- // reset_last_upper_lower_reg - reset the last // upper/lower register state //------------------------------------------------- inline void drcbe_x86::reset_last_upper_lower_reg() { m_last_lower_reg = REG_NONE; m_last_upper_reg = REG_NONE; } //------------------------------------------------- // set_last_lower_reg - note that we have just // loaded a lower register //------------------------------------------------- inline void drcbe_x86::set_last_lower_reg(x86code *&dst, const be_parameter ¶m, UINT8 reglo) { if (param.is_memory()) { m_last_lower_reg = reglo; m_last_lower_addr = (UINT32 *)((FPTR)param.memory()); m_last_lower_pc = dst; } } //------------------------------------------------- // set_last_upper_reg - note that we have just // loaded an upper register //------------------------------------------------- inline void drcbe_x86::set_last_upper_reg(x86code *&dst, const be_parameter ¶m, UINT8 reghi) { m_last_upper_reg = reghi; m_last_upper_addr = (param.is_int_register()) ? m_reghi[param.ireg()] : (UINT32 *)((FPTR)param.memory(4)); m_last_upper_pc = dst; } //------------------------------------------------- // can_skip_lower_load - return true if we can // skip re-loading a lower half of a register //------------------------------------------------- inline bool drcbe_x86::can_skip_lower_load(x86code *&dst, UINT32 *memref, UINT8 reglo) { return (dst == m_last_lower_pc && memref == m_last_lower_addr && reglo == m_last_lower_reg); } //------------------------------------------------- // can_skip_upper_load - return true if we can // skip re-loading an upper half of a register //------------------------------------------------- inline bool drcbe_x86::can_skip_upper_load(x86code *&dst, UINT32 *memref, UINT8 reghi) { return (dst == m_last_upper_pc && memref == m_last_upper_addr && reghi == m_last_upper_reg); } //------------------------------------------------- // track_resolve_link - wrapper for resolve_link // that resets all register tracking info //------------------------------------------------- inline void drcbe_x86::track_resolve_link(x86code *&destptr, const emit_link &linkinfo) { reset_last_upper_lower_reg(); resolve_link(destptr, linkinfo); } #define resolve_link INVALID //************************************************************************** // BACKEND CALLBACKS //************************************************************************** //------------------------------------------------- // drcbe_x86 - constructor //------------------------------------------------- drcbe_x86::drcbe_x86(drcuml_state &drcuml, device_t &device, drc_cache &cache, UINT32 flags, int modes, int addrbits, int ignorebits) : drcbe_interface(drcuml, cache, device), m_hash(cache, modes, addrbits, ignorebits), m_map(cache, 0), m_labels(cache), m_log(NULL), m_logged_common(false), m_sse3(false), m_entry(NULL), m_exit(NULL), m_nocode(NULL), m_save(NULL), m_restore(NULL), m_last_lower_reg(REG_NONE), m_last_lower_pc(NULL), m_last_lower_addr(NULL), m_last_upper_reg(REG_NONE), m_last_upper_pc(NULL), m_last_upper_addr(NULL), m_fptemp(0), m_fpumode(0), m_fmodesave(0), m_stacksave(0), m_hashstacksave(0), m_reslo(0), m_reshi(0), m_fixup_label(FUNC(drcbe_x86::fixup_label), this), m_fixup_exception(FUNC(drcbe_x86::fixup_exception), this) { // compute hi pointers for each register for (int regnum = 0; regnum < ARRAY_LENGTH(int_register_map); regnum++) if (int_register_map[regnum] != 0) { m_reglo[int_register_map[regnum]] = &m_state.r[regnum].w.l; m_reghi[int_register_map[regnum]] = &m_state.r[regnum].w.h; } // build the flags map (static but it doesn't hurt to regenerate it) for (int entry = 0; entry < ARRAY_LENGTH(flags_map); entry++) { UINT8 flags = 0; if (entry & 0x001) flags |= FLAG_C; if (entry & 0x004) flags |= FLAG_U; if (entry & 0x040) flags |= FLAG_Z; if (entry & 0x080) flags |= FLAG_S; if (entry & 0x800) flags |= FLAG_V; flags_map[entry] = flags; } for (int entry = 0; entry < ARRAY_LENGTH(flags_unmap); entry++) { UINT32 flags = 0; if (entry & FLAG_C) flags |= 0x001; if (entry & FLAG_U) flags |= 0x004; if (entry & FLAG_Z) flags |= 0x040; if (entry & FLAG_S) flags |= 0x080; if (entry & FLAG_V) flags |= 0x800; flags_unmap[entry] = flags; } // build the opcode table (static but it doesn't hurt to regenerate it) for (int opnum = 0; opnum < ARRAY_LENGTH(s_opcode_table_source); opnum++) s_opcode_table[s_opcode_table_source[opnum].opcode] = s_opcode_table_source[opnum].func; // create the log if (flags & DRCUML_OPTION_LOG_NATIVE) m_log = x86log_create_context("drcbex86.asm"); } //------------------------------------------------- // ~drcbe_x86 - destructor //------------------------------------------------- drcbe_x86::~drcbe_x86() { // free the log context if (m_log != NULL) x86log_free_context(m_log); } //------------------------------------------------- // reset - reset back-end specific state //------------------------------------------------- void drcbe_x86::reset() { // output a note to the log if (m_log != NULL) x86log_printf(m_log, "\n\n===========\nCACHE RESET\n===========\n\n"); // generate a little bit of glue code to set up the environment drccodeptr *cachetop = m_cache.begin_codegen(500); if (cachetop == NULL) fatalerror("Out of cache space after a reset!\n"); x86code *dst = (x86code *)*cachetop; // generate a simple CPUID stub UINT32 (*cpuid_ecx_stub)(void) = (UINT32 (*)(void))dst; emit_push_r32(dst, REG_EBX); // push ebx emit_mov_r32_imm(dst, REG_EAX, 1); // mov eax,1 emit_cpuid(dst); // cpuid emit_mov_r32_r32(dst, REG_EAX, REG_ECX); // mov eax,ecx emit_pop_r32(dst, REG_EBX); // pop ebx emit_ret(dst); // ret // call it to determine if we have SSE3 support m_sse3 = (((*cpuid_ecx_stub)() & 1) != 0); // generate an entry point m_entry = (x86_entry_point_func)dst; emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ESP, 4)); // mov eax,[esp+4] emit_push_r32(dst, REG_EBX); // push ebx emit_push_r32(dst, REG_ESI); // push esi emit_push_r32(dst, REG_EDI); // push edi emit_push_r32(dst, REG_EBP); // push ebp emit_sub_r32_imm(dst, REG_ESP, 24); // sub esp,24 emit_mov_m32_r32(dst, MABS(&m_hashstacksave), REG_ESP); // mov [hashstacksave],esp emit_sub_r32_imm(dst, REG_ESP, 4); // sub esp,4 emit_mov_m32_r32(dst, MABS(&m_stacksave), REG_ESP); // mov [stacksave],esp emit_fstcw_m16(dst, MABS(&m_fpumode)); // fstcw [fpumode] emit_jmp_r32(dst, REG_EAX); // jmp eax if (m_log != NULL && !m_logged_common) x86log_disasm_code_range(m_log, "entry_point", (x86code *)m_entry, dst); // generate an exit point m_exit = dst; emit_fldcw_m16(dst, MABS(&m_fpumode)); // fldcw [fpumode] emit_mov_r32_m32(dst, REG_ESP, MABS(&m_hashstacksave)); // mov esp,[hashstacksave] emit_add_r32_imm(dst, REG_ESP, 24); // add esp,24 emit_pop_r32(dst, REG_EBP); // pop ebp emit_pop_r32(dst, REG_EDI); // pop edi emit_pop_r32(dst, REG_ESI); // pop esi emit_pop_r32(dst, REG_EBX); // pop ebx emit_ret(dst); // ret if (m_log != NULL && !m_logged_common) x86log_disasm_code_range(m_log, "exit_point", m_exit, dst); // generate a no code point m_nocode = dst; emit_ret(dst); // ret if (m_log != NULL && !m_logged_common) x86log_disasm_code_range(m_log, "nocode", m_nocode, dst); // generate a save subroutine m_save = dst; emit_pushf(dst); // pushf emit_pop_r32(dst, REG_EAX); // pop eax emit_and_r32_imm(dst, REG_EAX, 0x8c5); // and eax,0x8c5 emit_mov_r8_m8(dst, REG_AL, MABSI(flags_map, REG_EAX)); // mov al,[flags_map] emit_mov_m8_r8(dst, MBD(REG_ECX, offsetof(drcuml_machine_state, flags)), REG_AL); // mov state->flags,al emit_mov_r8_m8(dst, REG_AL, MABS(&m_state.fmod)); // mov al,[fmod] emit_mov_m8_r8(dst, MBD(REG_ECX, offsetof(drcuml_machine_state, fmod)), REG_AL); // mov state->fmod,al emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.exp)); // mov eax,[exp] emit_mov_m32_r32(dst, MBD(REG_ECX, offsetof(drcuml_machine_state, exp)), REG_EAX); // mov state->exp,eax for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.r); regnum++) { FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.l; FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.h; if (int_register_map[regnum] != 0) emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsl), int_register_map[regnum]); else { emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.r[regnum].w.l)); emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsl), REG_EAX); } emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.r[regnum].w.h)); emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsh), REG_EAX); } for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.f); regnum++) { FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.l; FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.h; emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.f[regnum].s.l)); emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsl), REG_EAX); emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.f[regnum].s.h)); emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsh), REG_EAX); } emit_ret(dst); // ret if (m_log != NULL && !m_logged_common) x86log_disasm_code_range(m_log, "save", m_save, dst); // generate a restore subroutine m_restore = dst; for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.r); regnum++) { FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.l; FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.h; if (int_register_map[regnum] != 0) emit_mov_r32_m32(dst, int_register_map[regnum], MBD(REG_ECX, regoffsl)); else { emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsl)); emit_mov_m32_r32(dst, MABS(&m_state.r[regnum].w.l), REG_EAX); } emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsh)); emit_mov_m32_r32(dst, MABS(&m_state.r[regnum].w.h), REG_EAX); } for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.f); regnum++) { FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.l; FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.h; emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsl)); emit_mov_m32_r32(dst, MABS(&m_state.f[regnum].s.l), REG_EAX); emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsh)); emit_mov_m32_r32(dst, MABS(&m_state.f[regnum].s.h), REG_EAX); } emit_movzx_r32_m8(dst, REG_EAX, MBD(REG_ECX, offsetof(drcuml_machine_state, fmod)));// movzx eax,state->fmod emit_and_r32_imm(dst, REG_EAX, 3); // and eax,3 emit_mov_m8_r8(dst, MABS(&m_state.fmod), REG_AL); // mov [fmod],al emit_fldcw_m16(dst, MABSI(&fp_control[0], REG_EAX, 2)); // fldcw fp_control[eax] emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, offsetof(drcuml_machine_state, exp))); // mov eax,state->exp emit_mov_m32_r32(dst, MABS(&m_state.exp), REG_EAX); // mov [exp],eax emit_movzx_r32_m8(dst, REG_EAX, MBD(REG_ECX, offsetof(drcuml_machine_state, flags)));// movzx eax,state->flags emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4)); // push flags_unmap[eax*4] emit_popf(dst); // popf emit_ret(dst); // ret if (m_log != NULL && !m_logged_common) x86log_disasm_code_range(m_log, "restore", m_restore, dst); // finish up codegen *cachetop = dst; m_cache.end_codegen(); m_logged_common = true; // reset our hash tables m_hash.reset(); m_hash.set_default_codeptr(m_nocode); } //------------------------------------------------- // drcbex86_execute - execute a block of code // referenced by the given handle //------------------------------------------------- int drcbe_x86::execute(code_handle &entry) { // call our entry point which will jump to the destination return (*m_entry)((x86code *)entry.codeptr()); } //------------------------------------------------- // drcbex86_generate - generate code //------------------------------------------------- void drcbe_x86::generate(drcuml_block &block, const instruction *instlist, UINT32 numinst) { // tell all of our utility objects that a block is beginning m_hash.block_begin(block, instlist, numinst); m_labels.block_begin(block); m_map.block_begin(block); // begin codegen; fail if we can't drccodeptr *cachetop = m_cache.begin_codegen(numinst * 8 * 4); if (cachetop == NULL) block.abort(); // compute the base by aligning the cache top to a cache line (assumed to be 64 bytes) x86code *base = (x86code *)(((FPTR)*cachetop + 63) & ~63); x86code *dst = base; // generate code astring tempstring; const char *blockname = NULL; for (int inum = 0; inum < numinst; inum++) { const instruction &inst = instlist[inum]; assert(inst.opcode() < ARRAY_LENGTH(s_opcode_table)); // add a comment if (m_log != NULL) { astring dasm; inst.disasm(dasm, &m_drcuml); x86log_add_comment(m_log, dst, "%s", dasm.cstr()); } // extract a blockname if (blockname == NULL) { if (inst.opcode() == OP_HANDLE) blockname = inst.param(0).handle().string(); else if (inst.opcode() == OP_HASH) blockname = tempstring.format("Code: mode=%d PC=%08X", (UINT32)inst.param(0).immediate(), (offs_t)inst.param(1).immediate()); } // generate code (this->*s_opcode_table[inst.opcode()])(dst, inst); } // complete codegen *cachetop = (drccodeptr)dst; m_cache.end_codegen(); // log it if (m_log != NULL) x86log_disasm_code_range(m_log, (blockname == NULL) ? "Unknown block" : blockname, base, m_cache.top()); // tell all of our utility objects that the block is finished m_hash.block_end(block); m_labels.block_end(block); m_map.block_end(block); } //------------------------------------------------- // drcbex86_hash_exists - return true if the // given mode/pc exists in the hash table //------------------------------------------------- bool drcbe_x86::hash_exists(UINT32 mode, UINT32 pc) { return m_hash.code_exists(mode, pc); } //------------------------------------------------- // drcbex86_get_info - return information about // the back-end implementation //------------------------------------------------- void drcbe_x86::get_info(drcbe_info &info) { for (info.direct_iregs = 0; info.direct_iregs < REG_I_COUNT; info.direct_iregs++) if (int_register_map[info.direct_iregs] == 0) break; info.direct_fregs = 0; } //************************************************************************** // EMITTERS FOR 32-BIT OPERATIONS WITH PARAMETERS //************************************************************************** //------------------------------------------------- // emit_mov_r32_p32 - move a 32-bit parameter // into a register //------------------------------------------------- void drcbe_x86::emit_mov_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m) { if (param.is_immediate()) { if (param.immediate() == 0) emit_xor_r32_r32(dst, reg, reg); // xor reg,reg else emit_mov_r32_imm(dst, reg, param.immediate()); // mov reg,param } else if (param.is_memory()) emit_mov_r32_m32(dst, reg, MABS(param.memory())); // mov reg,[param] else if (param.is_int_register()) { if (reg != param.ireg()) emit_mov_r32_r32(dst, reg, param.ireg()); // mov reg,param } } //------------------------------------------------- // emit_mov_r32_p32_keepflags - move a 32-bit // parameter into a register without affecting // any flags //------------------------------------------------- void drcbe_x86::emit_mov_r32_p32_keepflags(x86code *&dst, UINT8 reg, const be_parameter ¶m) { if (param.is_immediate()) emit_mov_r32_imm(dst, reg, param.immediate()); // mov reg,param else if (param.is_memory()) { if (!can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), reg)) emit_mov_r32_m32(dst, reg, MABS(param.memory())); // mov reg,[param] } else if (param.is_int_register()) { if (reg != param.ireg()) emit_mov_r32_r32(dst, reg, param.ireg()); // mov reg,param } } //------------------------------------------------- // emit_mov_m32_p32 - move a 32-bit parameter // into a memory location //------------------------------------------------- void drcbe_x86::emit_mov_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m) { if (param.is_immediate()) emit_mov_m32_imm(dst, memref, param.immediate()); // mov [mem],param else if (param.is_memory()) { if (!can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), REG_EAX)) emit_mov_r32_m32(dst, REG_EAX, MABS(param.memory())); // mov eax,[param] emit_mov_m32_r32(dst, memref, REG_EAX); // mov [mem],eax } else if (param.is_int_register()) emit_mov_m32_r32(dst, memref, param.ireg()); // mov [mem],param } //------------------------------------------------- // emit_mov_p32_r32 - move a register into a // 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_mov_p32_r32(x86code *&dst, const be_parameter ¶m, UINT8 reg) { assert(!param.is_immediate()); if (param.is_memory()) { emit_mov_m32_r32(dst, MABS(param.memory()), reg); // mov [param],reg set_last_lower_reg(dst, param, reg); } else if (param.is_int_register()) { if (reg != param.ireg()) emit_mov_r32_r32(dst, param.ireg(), reg); // mov param,reg } } //------------------------------------------------- // emit_add_r32_p32 - add operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_add_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() != 0 || param.immediate() != 0) emit_add_r32_imm(dst, reg, param.immediate()); // add reg,param } else if (param.is_memory()) emit_add_r32_m32(dst, reg, MABS(param.memory())); // add reg,[param] else if (param.is_int_register()) emit_add_r32_r32(dst, reg, param.ireg()); // add reg,param } //------------------------------------------------- // emit_add_m32_p32 - add operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_add_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() != 0 || param.immediate() != 0) emit_add_m32_imm(dst, memref, param.immediate()); // add [dest],param } else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32(dst, reg, param); // mov reg,param emit_add_m32_r32(dst, memref, reg); // add [dest],reg } } //------------------------------------------------- // emit_adc_r32_p32 - adc operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_adc_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_adc_r32_imm(dst, reg, param.immediate()); // adc reg,param else if (param.is_memory()) emit_adc_r32_m32(dst, reg, MABS(param.memory())); // adc reg,[param] else if (param.is_int_register()) emit_adc_r32_r32(dst, reg, param.ireg()); // adc reg,param } //------------------------------------------------- // emit_adc_m32_p32 - adc operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_adc_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_adc_m32_imm(dst, memref, param.immediate()); // adc [dest],param else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32_keepflags(dst, reg, param); // mov reg,param emit_adc_m32_r32(dst, memref, reg); // adc [dest],reg } } //------------------------------------------------- // emit_sub_r32_p32 - sub operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_sub_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() != 0 || param.immediate() != 0) emit_sub_r32_imm(dst, reg, param.immediate()); // sub reg,param } else if (param.is_memory()) emit_sub_r32_m32(dst, reg, MABS(param.memory())); // sub reg,[param] else if (param.is_int_register()) emit_sub_r32_r32(dst, reg, param.ireg()); // sub reg,param } //------------------------------------------------- // emit_sub_m32_p32 - sub operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_sub_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() != 0 || param.immediate() != 0) emit_sub_m32_imm(dst, memref, param.immediate()); // sub [dest],param } else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32(dst, reg, param); // mov reg,param emit_sub_m32_r32(dst, memref, reg); // sub [dest],reg } } //------------------------------------------------- // emit_sbb_r32_p32 - sbb operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_sbb_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_sbb_r32_imm(dst, reg, param.immediate()); // sbb reg,param else if (param.is_memory()) emit_sbb_r32_m32(dst, reg, MABS(param.memory())); // sbb reg,[param] else if (param.is_int_register()) emit_sbb_r32_r32(dst, reg, param.ireg()); // sbb reg,param } //------------------------------------------------- // emit_sbb_m32_p32 - sbb operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_sbb_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_sbb_m32_imm(dst, memref, param.immediate()); // sbb [dest],param else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32_keepflags(dst, reg, param); // mov reg,param emit_sbb_m32_r32(dst, memref, reg); // sbb [dest],reg } } //------------------------------------------------- // emit_cmp_r32_p32 - cmp operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_cmp_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_cmp_r32_imm(dst, reg, param.immediate()); // cmp reg,param else if (param.is_memory()) emit_cmp_r32_m32(dst, reg, MABS(param.memory())); // cmp reg,[param] else if (param.is_int_register()) emit_cmp_r32_r32(dst, reg, param.ireg()); // cmp reg,param } //------------------------------------------------- // emit_cmp_m32_p32 - cmp operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_cmp_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_cmp_m32_imm(dst, memref, param.immediate()); // cmp [dest],param else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32(dst, reg, param); // mov reg,param emit_cmp_m32_r32(dst, memref, reg); // cmp [dest],reg } } //------------------------------------------------- // emit_and_r32_p32 - and operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_and_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0) emit_xor_r32_r32(dst, reg, reg); // xor reg,reg else emit_and_r32_imm(dst, reg, param.immediate()); // and reg,param } else if (param.is_memory()) emit_and_r32_m32(dst, reg, MABS(param.memory())); // and reg,[param] else if (param.is_int_register()) emit_and_r32_r32(dst, reg, param.ireg()); // and reg,param } //------------------------------------------------- // emit_and_m32_p32 - and operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_and_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0) emit_mov_m32_imm(dst, memref, 0); // mov [dest],0 else emit_and_m32_imm(dst, memref, param.immediate()); // and [dest],param } else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32(dst, reg, param); // mov reg,param emit_and_m32_r32(dst, memref, reg); // and [dest],reg } } //------------------------------------------------- // emit_test_r32_p32 - test operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_test_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_test_r32_imm(dst, reg, param.immediate()); // test reg,param else if (param.is_memory()) emit_test_m32_r32(dst, MABS(param.memory()), reg); // test [param],reg else if (param.is_int_register()) emit_test_r32_r32(dst, reg, param.ireg()); // test reg,param } //------------------------------------------------- // emit_test_m32_p32 - test operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_test_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) emit_test_m32_imm(dst, memref, param.immediate()); // test [dest],param else if (param.is_memory()) { emit_mov_r32_p32(dst, REG_EAX, param); // mov reg,param emit_test_m32_r32(dst, memref, REG_EAX); // test [dest],reg } else if (param.is_int_register()) emit_test_m32_r32(dst, memref, param.ireg()); // test [dest],param } //------------------------------------------------- // emit_or_r32_p32 - or operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_or_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_mov_r32_imm(dst, reg, ~0); // mov reg,-1 else emit_or_r32_imm(dst, reg, param.immediate()); // or reg,param } else if (param.is_memory()) emit_or_r32_m32(dst, reg, MABS(param.memory())); // or reg,[param] else if (param.is_int_register()) emit_or_r32_r32(dst, reg, param.ireg()); // or reg,param } //------------------------------------------------- // emit_or_m32_p32 - or operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_or_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_mov_m32_imm(dst, memref, ~0); // mov [dest],-1 else emit_or_m32_imm(dst, memref, param.immediate()); // or [dest],param } else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32(dst, reg, param); // mov reg,param emit_or_m32_r32(dst, memref, reg); // or [dest],reg } } //------------------------------------------------- // emit_xor_r32_p32 - xor operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_xor_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_not_r32(dst, reg); // not reg else emit_xor_r32_imm(dst, reg, param.immediate()); // xor reg,param } else if (param.is_memory()) emit_xor_r32_m32(dst, reg, MABS(param.memory())); // xor reg,[param] else if (param.is_int_register()) emit_xor_r32_r32(dst, reg, param.ireg()); // xor reg,param } //------------------------------------------------- // emit_xor_m32_p32 - xor operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_xor_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_not_m32(dst, memref); // not [dest] else emit_xor_m32_imm(dst, memref, param.immediate()); // xor [dest],param } else { int reg = param.select_register(REG_EAX); emit_mov_r32_p32(dst, reg, param); // mov reg,param emit_xor_m32_r32(dst, memref, reg); // xor [dest],reg } } //------------------------------------------------- // emit_shl_r32_p32 - shl operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_shl_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_shl_r32_imm(dst, reg, param.immediate()); // shl reg,param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_shl_r32_cl(dst, reg); // shl reg,cl } } //------------------------------------------------- // emit_shl_m32_p32 - shl operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_shl_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_shl_m32_imm(dst, memref, param.immediate()); // shl [dest],param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_shl_m32_cl(dst, memref); // shl [dest],cl } } //------------------------------------------------- // emit_shr_r32_p32 - shr operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_shr_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_shr_r32_imm(dst, reg, param.immediate()); // shr reg,param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_shr_r32_cl(dst, reg); // shr reg,cl } } //------------------------------------------------- // emit_shr_m32_p32 - shr operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_shr_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_shr_m32_imm(dst, memref, param.immediate()); // shr [dest],param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_shr_m32_cl(dst, memref); // shr [dest],cl } } //------------------------------------------------- // emit_sar_r32_p32 - sar operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_sar_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_sar_r32_imm(dst, reg, param.immediate()); // sar reg,param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_sar_r32_cl(dst, reg); // sar reg,cl } } //------------------------------------------------- // emit_sar_m32_p32 - sar operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_sar_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_sar_m32_imm(dst, memref, param.immediate()); // sar [dest],param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_sar_m32_cl(dst, memref); // sar [dest],cl } } //------------------------------------------------- // emit_rol_r32_p32 - rol operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_rol_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_rol_r32_imm(dst, reg, param.immediate()); // rol reg,param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_rol_r32_cl(dst, reg); // rol reg,cl } } //------------------------------------------------- // emit_rol_m32_p32 - rol operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_rol_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_rol_m32_imm(dst, memref, param.immediate()); // rol [dest],param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_rol_m32_cl(dst, memref); // rol [dest],cl } } //------------------------------------------------- // emit_ror_r32_p32 - ror operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_ror_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_ror_r32_imm(dst, reg, param.immediate()); // ror reg,param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_ror_r32_cl(dst, reg); // ror reg,cl } } //------------------------------------------------- // emit_ror_m32_p32 - ror operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_ror_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_ror_m32_imm(dst, memref, param.immediate()); // ror [dest],param } else { emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_ror_m32_cl(dst, memref); // ror [dest],cl } } //------------------------------------------------- // emit_rcl_r32_p32 - rcl operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_rcl_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_rcl_r32_imm(dst, reg, param.immediate()); // rcl reg,param } else { emit_mov_r32_p32_keepflags(dst, REG_ECX, param); // mov ecx,param emit_rcl_r32_cl(dst, reg); // rcl reg,cl } } //------------------------------------------------- // emit_rcl_m32_p32 - rcl operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_rcl_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_rcl_m32_imm(dst, memref, param.immediate()); // rcl [dest],param } else { emit_mov_r32_p32_keepflags(dst, REG_ECX, param); // mov ecx,param emit_rcl_m32_cl(dst, memref); // rcl [dest],cl } } //------------------------------------------------- // emit_rcr_r32_p32 - rcr operation to a 32-bit // register from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_rcr_r32_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_rcr_r32_imm(dst, reg, param.immediate()); // rcr reg,param } else { emit_mov_r32_p32_keepflags(dst, REG_ECX, param); // mov ecx,param emit_rcr_r32_cl(dst, reg); // rcr reg,cl } } //------------------------------------------------- // emit_rcr_m32_p32 - rcr operation to a 32-bit // memory location from a 32-bit parameter //------------------------------------------------- void drcbe_x86::emit_rcr_m32_p32(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else emit_rcr_m32_imm(dst, memref, param.immediate()); // rcr [dest],param } else { emit_mov_r32_p32_keepflags(dst, REG_ECX, param); // mov ecx,param emit_rcr_m32_cl(dst, memref); // rcr [dest],cl } } //************************************************************************** // EMITTERS FOR 64-BIT OPERATIONS WITH PARAMETERS //************************************************************************** //------------------------------------------------- // emit_mov_r64_p64 - move a 64-bit parameter // into a pair of registers //------------------------------------------------- void drcbe_x86::emit_mov_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m) { if (param.is_immediate()) { if (reglo == REG_NONE) ; else if ((UINT32)param.immediate() == 0) emit_xor_r32_r32(dst, reglo, reglo); // xor reglo,reglo else emit_mov_r32_imm(dst, reglo, param.immediate()); // mov reglo,param if (reghi == REG_NONE) ; else if ((UINT32)(param.immediate() >> 32) == 0) emit_xor_r32_r32(dst, reghi, reghi); // xor reghi,reghi else emit_mov_r32_imm(dst, reghi, param.immediate() >> 32); // mov reghi,param >> 32 } else if (param.is_memory()) { int skip_lower = can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), reglo); int skip_upper = can_skip_upper_load(dst, (UINT32 *)((FPTR)param.memory(4)), reghi); if (reglo != REG_NONE && !skip_lower) emit_mov_r32_m32(dst, reglo, MABS(param.memory())); // mov reglo,[param] if (reghi != REG_NONE && !skip_upper) emit_mov_r32_m32(dst, reghi, MABS(param.memory(4))); // mov reghi,[param+4] } else if (param.is_int_register()) { int skip_upper = can_skip_upper_load(dst, m_reghi[param.ireg()], reghi); if (reglo != REG_NONE && reglo != param.ireg()) emit_mov_r32_r32(dst, reglo, param.ireg()); // mov reglo,param if (reghi != REG_NONE && !skip_upper) emit_mov_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // mov reghi,reghi[param] } } //------------------------------------------------- // emit_mov_r64_p64_keepflags - move a 64-bit // parameter into a pair of registers without // affecting any flags //------------------------------------------------- void drcbe_x86::emit_mov_r64_p64_keepflags(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m) { if (param.is_immediate()) { if (reglo != REG_NONE) emit_mov_r32_imm(dst, reglo, param.immediate()); // mov reglo,param if (reghi != REG_NONE) emit_mov_r32_imm(dst, reghi, param.immediate() >> 32); // mov reghi,param >> 32 } else if (param.is_memory()) { int skip_lower = can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), reglo); int skip_upper = can_skip_upper_load(dst, (UINT32 *)((FPTR)param.memory(4)), reghi); if (reglo != REG_NONE && !skip_lower) emit_mov_r32_m32(dst, reglo, MABS(param.memory())); // mov reglo,[param] if (reghi != REG_NONE && !skip_upper) emit_mov_r32_m32(dst, reghi, MABS(param.memory(4))); // mov reghi,[param+4] } else if (param.is_int_register()) { int skip_upper = can_skip_upper_load(dst, m_reghi[param.ireg()], reghi); if (reglo != REG_NONE && reglo != param.ireg()) emit_mov_r32_r32(dst, reglo, param.ireg()); // mov reglo,param if (reghi != REG_NONE && !skip_upper) emit_mov_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // mov reghi,reghi[param] } } //------------------------------------------------- // emit_mov_m64_p64 - move a 64-bit parameter // into a memory location //------------------------------------------------- void drcbe_x86::emit_mov_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m) { if (param.is_immediate()) { emit_mov_m32_imm(dst, memref + 0, param.immediate()); // mov [mem],param emit_mov_m32_imm(dst, memref + 4, param.immediate() >> 32); // mov [mem],param >> 32 } else if (param.is_memory()) { int skip_lower = can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), REG_EAX); if (!skip_lower) emit_mov_r32_m32(dst, REG_EAX, MABS(param.memory())); // mov eax,[param] emit_mov_m32_r32(dst, memref + 0, REG_EAX); // mov [mem],eax emit_mov_r32_m32(dst, REG_EAX, MABS(param.memory(4))); // mov eax,[param+4] emit_mov_m32_r32(dst, memref + 4, REG_EAX); // mov [mem+4],eax } else if (param.is_int_register()) { emit_mov_m32_r32(dst, memref + 0, param.ireg()); // mov [mem],param emit_mov_r32_m32(dst, REG_EAX, MABS(m_reghi[param.ireg()])); // mov eax,[param.hi] emit_mov_m32_r32(dst, memref + 4, REG_EAX); // mov [mem+4],eax } } //------------------------------------------------- // emit_mov_p64_r64 - move a pair of registers // into a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_mov_p64_r64(x86code *&dst, const be_parameter ¶m, UINT8 reglo, UINT8 reghi) { assert(!param.is_immediate()); if (param.is_memory()) { emit_mov_m32_r32(dst, MABS(param.memory()), reglo); // mov [param],reglo emit_mov_m32_r32(dst, MABS(param.memory(4)), reghi); // mov [param+4],reghi } else if (param.is_int_register()) { if (reglo != param.ireg()) emit_mov_r32_r32(dst, param.ireg(), reglo); // mov param,reglo emit_mov_m32_r32(dst, MABS(m_reghi[param.ireg()]), reghi); // mov reghi[param],reghi } set_last_lower_reg(dst, param, reglo); set_last_upper_reg(dst, param, reghi); } //------------------------------------------------- // emit_add_r64_p64 - add operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_add_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_add_r32_m32(dst, reglo, MABS(param.memory())); // add reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_adc_r32_m32(dst, reghi, MABS(param.memory(4))); // adc reghi,[param] } else if (param.is_immediate()) { emit_add_r32_imm(dst, reglo, param.immediate()); // add reglo,param if (saveflags) emit_pushf(dst); // pushf emit_adc_r32_imm(dst, reghi, param.immediate() >> 32); // adc reghi,param >> 32 } else if (param.is_int_register()) { emit_add_r32_r32(dst, reglo, param.ireg()); // add reglo,param if (saveflags) emit_pushf(dst); // pushf emit_adc_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // adc reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_add_m64_p64 - add operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_add_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { emit_add_m32_imm(dst, memref, param.immediate()); // add [dest],param if (saveflags) emit_pushf(dst); // pushf emit_adc_m32_imm(dst, memref + 4, param.immediate() >> 32); // adc [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_add_m32_r32(dst, memref, reglo); // add [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_adc_m32_r32(dst, memref + 4, REG_EDX); // adc [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_adc_r64_p64 - adc operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_adc_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_adc_r32_m32(dst, reglo, MABS(param.memory())); // adc reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_adc_r32_m32(dst, reghi, MABS(param.memory(4))); // adc reghi,[param] } else if (param.is_immediate()) { emit_adc_r32_imm(dst, reglo, param.immediate()); // adc reglo,param if (saveflags) emit_pushf(dst); // pushf emit_adc_r32_imm(dst, reghi, param.immediate() >> 32); // adc reghi,param >> 32 } else if (param.is_int_register()) { emit_adc_r32_r32(dst, reglo, param.ireg()); // adc reglo,param if (saveflags) emit_pushf(dst); // pushf emit_adc_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // adc reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_adc_m64_p64 - adc operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_adc_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { emit_adc_m32_imm(dst, memref, param.immediate()); // adc [dest],param if (saveflags) emit_pushf(dst); // pushf emit_adc_m32_imm(dst, memref + 4, param.immediate() >> 32); // adc [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64_keepflags(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_adc_m32_r32(dst, memref, reglo); // adc [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_adc_m32_r32(dst, memref + 4, REG_EDX); // adc [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_sub_r64_p64 - sub operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_sub_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_sub_r32_m32(dst, reglo, MABS(param.memory())); // sub reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_m32(dst, reghi, MABS(param.memory(4))); // sbb reghi,[param] } else if (param.is_immediate()) { emit_sub_r32_imm(dst, reglo, param.immediate()); // sub reglo,param if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_imm(dst, reghi, param.immediate() >> 32); // sbb reghi,param >> 32 } else if (param.is_int_register()) { emit_sub_r32_r32(dst, reglo, param.ireg()); // sub reglo,param if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // sbb reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_sub_m64_p64 - sub operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_sub_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { emit_sub_m32_imm(dst, memref, param.immediate()); // sub [dest],param if (saveflags) emit_pushf(dst); // pushf emit_sbb_m32_imm(dst, memref + 4, param.immediate() >> 32); // sbb [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_sub_m32_r32(dst, memref, reglo); // sub [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_sbb_m32_r32(dst, memref + 4, REG_EDX); // sbb [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_sbb_r64_p64 - sbb operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_sbb_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_sbb_r32_m32(dst, reglo, MABS(param.memory())); // sbb reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_m32(dst, reghi, MABS(param.memory(4))); // sbb reghi,[param] } else if (param.is_immediate()) { emit_sbb_r32_imm(dst, reglo, param.immediate()); // sbb reglo,param if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_imm(dst, reghi, param.immediate() >> 32); // sbb reghi,param >> 32 } else if (param.is_int_register()) { emit_sbb_r32_r32(dst, reglo, param.ireg()); // sbb reglo,param if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // sbb reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_sbb_m64_p64 - sbb operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_sbb_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { emit_sbb_m32_imm(dst, memref, param.immediate()); // sbb [dest],param if (saveflags) emit_pushf(dst); // pushf emit_sbb_m32_imm(dst, memref + 4, param.immediate() >> 32); // sbb [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64_keepflags(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_sbb_m32_r32(dst, memref, reglo); // sbb [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_sbb_m32_r32(dst, memref + 4, REG_EDX); // sbb [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_cmp_r64_p64 - sub operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_cmp_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = (inst.flags() != FLAG_Z && (inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_sub_r32_m32(dst, reglo, MABS(param.memory())); // sub reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_m32(dst, reghi, MABS(param.memory(4))); // sbb reghi,[param] } else if (param.is_immediate()) { emit_sub_r32_imm(dst, reglo, param.immediate()); // sub reglo,param if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_imm(dst, reghi, param.immediate() >> 32); // sbb reghi,param >> 32 } else if (param.is_int_register()) { emit_sub_r32_r32(dst, reglo, param.ireg()); // sub reglo,param if (saveflags) emit_pushf(dst); // pushf emit_sbb_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // sbb reghi,reghi[param] } if (inst.flags() == FLAG_Z) emit_or_r32_r32(dst, reghi, reglo); // or reghi,reglo else if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_and_r64_p64 - and operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_and_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_and_r32_m32(dst, reglo, MABS(param.memory())); // and reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_and_r32_m32(dst, reghi, MABS(param.memory(4))); // and reghi,[param] } else if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0) emit_xor_r32_r32(dst, reglo, reglo); // xor reglo,reglo else emit_and_r32_imm(dst, reglo, param.immediate()); // and reglo,param if (saveflags) emit_pushf(dst); // pushf if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff) ;// skip else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0) emit_xor_r32_r32(dst, reghi, reghi); // xor reghi,reghi else emit_and_r32_imm(dst, reghi, param.immediate() >> 32); // and reghi,param >> 32 } else if (param.is_int_register()) { emit_and_r32_r32(dst, reglo, param.ireg()); // and reglo,param if (saveflags) emit_pushf(dst); // pushf emit_and_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // and reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_and_m64_p64 - and operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_and_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0) emit_mov_m32_imm(dst, memref, 0); // mov [dest],0 else emit_and_m32_imm(dst, memref, param.immediate()); // and [dest],param if (saveflags) emit_pushf(dst); // pushf if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff) ;// skip else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0) emit_mov_m32_imm(dst, memref + 4, 0); // mov [dest+4],0 else emit_and_m32_imm(dst, memref + 4, param.immediate() >> 32); // and [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_and_m32_r32(dst, memref, reglo); // and [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_and_m32_r32(dst, memref + 4, REG_EDX); // and [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_test_r64_p64 - test operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_test_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_test_m32_r32(dst, MABS(param.memory()), reglo); // test [param],reglo if (saveflags) emit_pushf(dst); // pushf emit_test_m32_r32(dst, MABS(param.memory(4)), reghi); // test [param],reghi } else if (param.is_immediate()) { emit_test_r32_imm(dst, reglo, param.immediate()); // test reglo,param if (saveflags) emit_pushf(dst); // pushf emit_test_r32_imm(dst, reghi, param.immediate() >> 32); // test reghi,param >> 32 } else if (param.is_int_register()) { emit_test_r32_r32(dst, reglo, param.ireg()); // test reglo,param if (saveflags) emit_pushf(dst); // pushf emit_test_m32_r32(dst, MABS(m_reghi[param.ireg()]), reghi); // test reghi[param],reghi } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_test_m64_p64 - test operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_test_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { emit_test_m32_imm(dst, memref, param.immediate()); // test [dest],param if (saveflags) emit_pushf(dst); // pushf emit_test_m32_imm(dst, memref + 4, param.immediate() >> 32); // test [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_test_m32_r32(dst, memref, reglo); // test [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_test_m32_r32(dst, memref + 4, REG_EDX); // test [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_or_r64_p64 - or operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_or_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_or_r32_m32(dst, reglo, MABS(param.memory())); // or reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_or_r32_m32(dst, reghi, MABS(param.memory(4))); // or reghi,[param] } else if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_mov_r32_imm(dst, reglo, ~0); // mov reglo,-1 else emit_or_r32_imm(dst, reglo, param.immediate()); // or reglo,param if (saveflags) emit_pushf(dst); // pushf if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0) ;// skip else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff) emit_mov_r32_imm(dst, reghi, ~0); // mov reghi,-1 else emit_or_r32_imm(dst, reghi, param.immediate() >> 32); // or reghi,param >> 32 } else if (param.is_int_register()) { emit_or_r32_r32(dst, reglo, param.ireg()); // or reglo,param if (saveflags) emit_pushf(dst); // pushf emit_or_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // or reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_or_m64_p64 - or operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_or_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_mov_m32_imm(dst, memref, ~0); // mov [dest],-1 else emit_or_m32_imm(dst, memref, param.immediate()); // or [dest],param if (saveflags) emit_pushf(dst); // pushf if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0) ;// skip else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff) emit_mov_m32_imm(dst, memref + 4, ~0); // mov [dest+4],-1 else emit_or_m32_imm(dst, memref + 4, param.immediate() >> 32); // or [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_or_m32_r32(dst, memref, reglo); // or [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_or_m32_r32(dst, memref + 4, REG_EDX); // or [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_xor_r64_p64 - xor operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_xor_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_memory()) { emit_xor_r32_m32(dst, reglo, MABS(param.memory())); // xor reglo,[param] if (saveflags) emit_pushf(dst); // pushf emit_xor_r32_m32(dst, reghi, MABS(param.memory(4))); // xor reghi,[param] } else if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_not_r32(dst, reglo); // not reglo else emit_xor_r32_imm(dst, reglo, param.immediate()); // xor reglo,param if (saveflags) emit_pushf(dst); // pushf if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0) ;// skip else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff) emit_not_r32(dst, reghi); // not reghi else emit_xor_r32_imm(dst, reghi, param.immediate() >> 32); // xor reghi,param >> 32 } else if (param.is_int_register()) { emit_xor_r32_r32(dst, reglo, param.ireg()); // xor reglo,param if (saveflags) emit_pushf(dst); // pushf emit_xor_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()])); // xor reghi,reghi[param] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_xor_m64_p64 - xor operation to a 64-bit // memory location from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_xor_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { if (inst.flags() == 0 && (UINT32)param.immediate() == 0) ;// skip else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff) emit_not_m32(dst, memref); // not [dest] else emit_xor_m32_imm(dst, memref, param.immediate()); // xor [dest],param if (saveflags) emit_pushf(dst); // pushf if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0) ;// skip else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff) emit_not_m32(dst, memref + 4); // not [dest+4] else emit_xor_m32_imm(dst, memref + 4, param.immediate() >> 32); // xor [dest+4],param >> 32 } else { int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX; emit_mov_r64_p64(dst, reglo, REG_EDX, param); // mov edx:reglo,param emit_xor_m32_r32(dst, memref, reglo); // xor [dest],reglo if (saveflags) emit_pushf(dst); // pushf emit_xor_m32_r32(dst, memref + 4, REG_EDX); // xor [dest+4],edx } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_shl_r64_p64 - shl operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_shl_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = (inst.flags() != 0); if (param.is_immediate()) { int count = param.immediate() & 63; if (inst.flags() == 0 && count == 0) ;// skip else { while (count >= 32) { if (inst.flags() != 0) { emit_shld_r32_r32_imm(dst, reghi, reglo, 31); // shld reghi,reglo,31 emit_shl_r32_imm(dst, reglo, 31); // shl reglo,31 count -= 31; } else { emit_mov_r32_r32(dst, reghi, reglo); // mov reghi,reglo emit_xor_r32_r32(dst, reglo, reglo); // xor reglo,reglo count -= 32; } } if (inst.flags() != 0 || count > 0) { emit_shld_r32_r32_imm(dst, reghi, reglo, count); // shld reghi,reglo,count if (saveflags) emit_pushf(dst); // pushf emit_shl_r32_imm(dst, reglo, count); // shl reglo,count } } } else { emit_link skip1, skip2; emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip1); // jz skip1 if (inst.flags() != 0) { emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_shld_r32_r32_imm(dst, reghi, reglo, 31); // shld reghi,reglo,31 emit_shl_r32_imm(dst, reglo, 31); // shl reglo,31 emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip2); // jz skip2 emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_shld_r32_r32_imm(dst, reghi, reglo, 31); // shld reghi,reglo,31 emit_shl_r32_imm(dst, reglo, 31); // shl reglo,31 track_resolve_link(dst, skip2); // skip2: } else { emit_mov_r32_r32(dst, reghi, reglo); // mov reghi,reglo emit_xor_r32_r32(dst, reglo, reglo); // xor reglo,reglo } track_resolve_link(dst, skip1); // skip1: emit_shld_r32_r32_cl(dst, reghi, reglo); // shld reghi,reglo,cl if (saveflags) emit_pushf(dst); // pushf emit_shl_r32_cl(dst, reglo); // shl reglo,cl } if (saveflags) emit_combine_z_shl_flags(dst); } //------------------------------------------------- // emit_shr_r64_p64 - shr operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_shr_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { int count = param.immediate() & 63; if (inst.flags() == 0 && count == 0) ;// skip else { while (count >= 32) { if (inst.flags() != 0) { emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_shr_r32_imm(dst, reghi, 31); // shr reghi,31 count -= 31; } else { emit_mov_r32_r32(dst, reglo, reghi); // mov reglo,reghi emit_xor_r32_r32(dst, reghi, reghi); // xor reghi,reghi count -= 32; } } if (inst.flags() != 0 || count > 0) { emit_shrd_r32_r32_imm(dst, reglo, reghi, count); // shrd reglo,reghi,count if (saveflags) emit_pushf(dst); // pushf emit_shr_r32_imm(dst, reghi, count); // shr reghi,count } } } else { emit_link skip1, skip2; emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip1); // jz skip1 if (inst.flags() != 0) { emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_shr_r32_imm(dst, reghi, 31); // shr reghi,31 emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip2); // jz skip2 emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_shr_r32_imm(dst, reghi, 31); // shr reghi,31 track_resolve_link(dst, skip2); // skip2: } else { emit_mov_r32_r32(dst, reglo, reghi); // mov reglo,reghi emit_xor_r32_r32(dst, reghi, reghi); // xor reghi,reghi } track_resolve_link(dst, skip1); // skip1: emit_shrd_r32_r32_cl(dst, reglo, reghi); // shrd reglo,reghi,cl if (saveflags) emit_pushf(dst); // pushf emit_shr_r32_cl(dst, reghi); // shr reghi,cl } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_sar_r64_p64 - sar operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_sar_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { int count = param.immediate() & 63; if (inst.flags() == 0 && count == 0) ;// skip else { while (count >= 32) { if (inst.flags() != 0) { emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_sar_r32_imm(dst, reghi, 31); // sar reghi,31 count -= 31; } else { emit_mov_r32_r32(dst, reglo, reghi); // mov reglo,reghi emit_sar_r32_imm(dst, reghi, 31); // sar reghi,31 count -= 32; } } if (inst.flags() != 0 || count > 0) { emit_shrd_r32_r32_imm(dst, reglo, reghi, count); // shrd reglo,reghi,count if (saveflags) emit_pushf(dst); // pushf emit_sar_r32_imm(dst, reghi, count); // sar reghi,count } } } else { emit_link skip1, skip2; emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip1); // jz skip1 if (inst.flags() != 0) { emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_sar_r32_imm(dst, reghi, 31); // sar reghi,31 emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip2); // jz skip emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_sar_r32_imm(dst, reghi, 31); // sar reghi,31 track_resolve_link(dst, skip2); // skip2: } else { emit_mov_r32_r32(dst, reglo, reghi); // mov reglo,reghi emit_sar_r32_imm(dst, reghi, 31); // sar reghi,31 } track_resolve_link(dst, skip1); // skip1: emit_shrd_r32_r32_cl(dst, reglo, reghi); // shrd reglo,reghi,cl if (saveflags) emit_pushf(dst); // pushf emit_sar_r32_cl(dst, reghi); // sar reghi,cl } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_rol_r64_p64 - rol operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_rol_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { int count = param.immediate() & 63; if (inst.flags() == 0 && count == 0) ;// skip else { while (count >= 32) { if (inst.flags() != 0) { emit_mov_r32_r32(dst, REG_ECX, reglo); // mov ecx,reglo emit_shld_r32_r32_imm(dst, reglo, reghi, 31); // shld reglo,reghi,31 emit_shld_r32_r32_imm(dst, reghi, REG_ECX, 31); // shld reghi,ecx,31 count -= 31; } else { emit_xchg_r32_r32(dst, reghi, reglo); // xchg reghi,reglo count -= 32; } } if (inst.flags() != 0 || count > 0) { emit_mov_r32_r32(dst, REG_ECX, reglo); // mov ecx,reglo emit_shld_r32_r32_imm(dst, reglo, reghi, count); // shld reglo,reghi,count if (saveflags) emit_pushf(dst); // pushf emit_shld_r32_r32_imm(dst, reghi, REG_ECX, count); // shld reghi,ecx,count } } } else { emit_link skip1, skip2; int tempreg = REG_EBX; emit_mov_m32_r32(dst, MBD(REG_ESP, -8), tempreg); // mov [esp-8],ebx emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip1); // jz skip1 if (inst.flags() != 0) { emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_mov_r32_r32(dst, tempreg, reglo); // mov ebx,reglo emit_shld_r32_r32_imm(dst, reglo, reghi, 31); // shld reglo,reghi,31 emit_shld_r32_r32_imm(dst, reghi, tempreg, 31); // shld reghi,ebx,31 emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip2); // jz skip2 emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_mov_r32_r32(dst, tempreg, reglo); // mov ebx,reglo emit_shld_r32_r32_imm(dst, reglo, reghi, 31); // shld reglo,reghi,31 emit_shld_r32_r32_imm(dst, reghi, tempreg, 31); // shld reghi,ebx,31 track_resolve_link(dst, skip2); // skip2: } else emit_xchg_r32_r32(dst, reghi, reglo); // xchg reghi,reglo track_resolve_link(dst, skip1); // skip1: emit_mov_r32_r32(dst, tempreg, reglo); // mov ebx,reglo emit_shld_r32_r32_cl(dst, reglo, reghi); // shld reglo,reghi,cl if (saveflags) emit_pushf(dst); // pushf emit_shld_r32_r32_cl(dst, reghi, tempreg); // shld reghi,ebx,cl emit_mov_r32_m32(dst, tempreg, MBD(REG_ESP, saveflags ? -4 : -8)); // mov ebx,[esp-8] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_ror_r64_p64 - ror operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_ror_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); if (param.is_immediate()) { int count = param.immediate() & 63; if (inst.flags() == 0 && count == 0) ;// skip else { while (count >= 32) { if (inst.flags() != 0) { emit_mov_r32_r32(dst, REG_ECX, reglo); // mov ecx,reglo emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_shrd_r32_r32_imm(dst, reghi, REG_ECX, 31); // shrd reghi,ecx,31 count -= 31; } else { emit_xchg_r32_r32(dst, reghi, reglo); // xchg reghi,reglo count -= 32; } } if (inst.flags() != 0 || count > 0) { emit_mov_r32_r32(dst, REG_ECX, reglo); // mov ecx,reglo emit_shrd_r32_r32_imm(dst, reglo, reghi, count); // shrd reglo,reghi,count if (saveflags) emit_pushf(dst); // pushf emit_shrd_r32_r32_imm(dst, reghi, REG_ECX, count); // shrd reghi,ecx,count } } } else { emit_link skip1, skip2; int tempreg = REG_EBX; emit_mov_m32_r32(dst, MBD(REG_ESP, -8), tempreg); // mov [esp-8],ebx emit_mov_r32_p32(dst, REG_ECX, param); // mov ecx,param emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip1); // jz skip1 if (inst.flags() != 0) { emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_mov_r32_r32(dst, tempreg, reglo); // mov ebx,reglo emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_shrd_r32_r32_imm(dst, reghi, tempreg, 31); // shrd reghi,ebx,31 emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 emit_jcc_short_link(dst, x86emit::COND_Z, skip2); // jz skip2 emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 emit_mov_r32_r32(dst, tempreg, reglo); // mov ebx,reglo emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 emit_shrd_r32_r32_imm(dst, reghi, tempreg, 31); // shrd reghi,ebx,31 track_resolve_link(dst, skip2); // skip2: } else emit_xchg_r32_r32(dst, reghi, reglo); // xchg reghi,reglo track_resolve_link(dst, skip1); // skip1: emit_mov_r32_r32(dst, tempreg, reglo); // mov ebx,reglo emit_shrd_r32_r32_cl(dst, reglo, reghi); // shrd reglo,reghi,cl if (saveflags) emit_pushf(dst); // pushf emit_shrd_r32_r32_cl(dst, reghi, tempreg); // shrd reghi,ebx,cl emit_mov_r32_m32(dst, tempreg, MBD(REG_ESP, saveflags ? -4 : -8)); // mov ebx,[esp-8] } if (saveflags) emit_combine_z_flags(dst); } //------------------------------------------------- // emit_rcl_r64_p64 - rcl operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_rcl_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = ((inst.flags() & FLAG_Z) != 0); emit_link skipall, skiploop; x86code *loop; emit_mov_r32_p32_keepflags(dst, REG_ECX, param); // mov ecx,param if (!saveflags) { loop = dst; // loop: emit_jecxz_link(dst, skipall); // jecxz skipall emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1)); // lea ecx,[ecx-1] emit_rcl_r32_imm(dst, reglo, 1); // rcl reglo,1 emit_rcl_r32_imm(dst, reghi, 1); // rcl reghi,1 emit_jmp(dst, loop); // jmp loop track_resolve_link(dst, skipall); // skipall: } else { emit_jecxz_link(dst, skipall); // jecxz skipall emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1)); // lea ecx,[ecx-1] loop = dst; // loop: emit_jecxz_link(dst, skiploop); // jecxz skiploop emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1)); // lea ecx,[ecx-1] emit_rcl_r32_imm(dst, reglo, 1); // rcl reglo,1 emit_rcl_r32_imm(dst, reghi, 1); // rcl reghi,1 emit_jmp(dst, loop); // jmp loop track_resolve_link(dst, skiploop); // skiploop: emit_rcl_r32_imm(dst, reglo, 1); // rcl reglo,1 emit_pushf(dst); // pushf emit_rcl_r32_imm(dst, reghi, 1); // rcl reghi,1 track_resolve_link(dst, skipall); // skipall: emit_combine_z_flags(dst); } } //------------------------------------------------- // emit_rcr_r64_p64 - rcr operation to a 64-bit // pair of registers from a 64-bit parameter //------------------------------------------------- void drcbe_x86::emit_rcr_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter ¶m, const instruction &inst) { int saveflags = (inst.flags() != 0); emit_link skipall, skiploop; x86code *loop; emit_mov_r32_p32_keepflags(dst, REG_ECX, param); // mov ecx,param if (!saveflags) { loop = dst; // loop: emit_jecxz_link(dst, skipall); // jecxz skipall emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1)); // lea ecx,[ecx-1] emit_rcr_r32_imm(dst, reghi, 1); // rcr reghi,1 emit_rcr_r32_imm(dst, reglo, 1); // rcr reglo,1 emit_jmp(dst, loop); // jmp loop track_resolve_link(dst, skipall); // skipall: } else { emit_jecxz_link(dst, skipall); // jecxz skipall emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1)); // lea ecx,[ecx-1] loop = dst; // loop: emit_jecxz_link(dst, skiploop); // jecxz skiploop emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1)); // lea ecx,[ecx-1] emit_rcr_r32_imm(dst, reghi, 1); // rcr reghi,1 emit_rcr_r32_imm(dst, reglo, 1); // rcr reglo,1 emit_jmp(dst, loop); // jmp loop track_resolve_link(dst, skiploop); // skiploop: emit_rcr_r32_imm(dst, reghi, 1); // rcr reghi,1 emit_pushf(dst); // pushf emit_rcr_r32_imm(dst, reglo, 1); // rcr reglo,1 track_resolve_link(dst, skipall); // skipall: emit_combine_z_shl_flags(dst); } } //************************************************************************** // EMITTERS FOR FLOATING POINT //************************************************************************** //------------------------------------------------- // emit_fld_p - load a floating point parameter // onto the stack //------------------------------------------------- void drcbe_x86::emit_fld_p(x86code *&dst, int size, const be_parameter ¶m) { assert(param.is_memory()); assert(size == 4 || size == 8); if (size == 4) emit_fld_m32(dst, MABS(param.memory())); else if (size == 8) emit_fld_m64(dst, MABS(param.memory())); } //------------------------------------------------- // emit_fstp_p - store a floating point parameter // from the stack and pop it //------------------------------------------------- void drcbe_x86::emit_fstp_p(x86code *&dst, int size, const be_parameter ¶m) { assert(param.is_memory()); assert(size == 4 || size == 8); if (size == 4) emit_fstp_m32(dst, MABS(param.memory())); else if (size == 8) emit_fstp_m64(dst, MABS(param.memory())); } //************************************************************************** // OUT-OF-BAND CODE FIXUP CALLBACKS //************************************************************************** //------------------------------------------------- // fixup_label - callback to fixup forward- // referenced labels //------------------------------------------------- void drcbe_x86::fixup_label(void *parameter, drccodeptr labelcodeptr) { drccodeptr src = (drccodeptr)parameter; // find the end of the instruction if (src[0] == 0xe3) { src += 1 + 1; src[-1] = labelcodeptr - src; } else if (src[0] == 0xe9) { src += 1 + 4; ((UINT32 *)src)[-1] = labelcodeptr - src; } else if (src[0] == 0x0f && (src[1] & 0xf0) == 0x80) { src += 2 + 4; ((UINT32 *)src)[-1] = labelcodeptr - src; } else fatalerror("fixup_label called with invalid jmp source!\n"); } //------------------------------------------------- // fixup_exception - callback to perform cleanup // and jump to an exception handler //------------------------------------------------- void drcbe_x86::fixup_exception(drccodeptr *codeptr, void *param1, void *param2) { drccodeptr src = (drccodeptr)param1; const instruction &inst = *(const instruction *)param2; // normalize parameters const parameter &handp = inst.param(0); assert(handp.is_code_handle()); be_parameter exp(*this, inst.param(1), PTYPE_MRI); // look up the handle target drccodeptr *targetptr = handp.handle().codeptr_addr(); // first fixup the jump to get us here drccodeptr dst = *codeptr; ((UINT32 *)src)[-1] = dst - src; // then store the exception parameter emit_mov_m32_p32(dst, MABS(&m_state.exp), exp); // mov [exp],exp // push the original return address on the stack emit_push_imm(dst, (FPTR)src); // push if (*targetptr != NULL) emit_jmp(dst, *targetptr); // jmp *targetptr else emit_jmp_m32(dst, MABS(targetptr)); // jmp [targetptr] *codeptr = dst; } //************************************************************************** // DEBUG HELPERS //************************************************************************** //------------------------------------------------- // debug_log_hashjmp - callback to handle // logging of hashjmps //------------------------------------------------- void drcbe_x86::debug_log_hashjmp(int mode, offs_t pc) { printf("mode=%d PC=%08X\n", mode, pc); } //************************************************************************** // COMPILE-TIME OPCODES //************************************************************************** //------------------------------------------------- // op_handle - process a HANDLE opcode //------------------------------------------------- void drcbe_x86::op_handle(x86code *&dst, const instruction &inst) { assert_no_condition(inst); assert_no_flags(inst); assert(inst.numparams() == 1); assert(inst.param(0).is_code_handle()); reset_last_upper_lower_reg(); // emit a jump around the stack adjust in case code falls through here emit_link skip; emit_jmp_short_link(dst, skip); // jmp skip // register the current pointer for the handle inst.param(0).handle().set_codeptr(dst); // by default, the handle points to prolog code that moves the stack pointer emit_lea_r32_m32(dst, REG_ESP, MBD(REG_ESP, -28)); // lea rsp,[rsp-28] track_resolve_link(dst, skip); // skip: } //------------------------------------------------- // op_hash - process a HASH opcode //------------------------------------------------- void drcbe_x86::op_hash(x86code *&dst, const instruction &inst) { assert_no_condition(inst); assert_no_flags(inst); assert(inst.numparams() == 2); assert(inst.param(0).is_immediate()); assert(inst.param(1).is_immediate()); // register the current pointer for the mode/PC m_hash.set_codeptr(inst.param(0).immediate(), inst.param(1).immediate(), dst); reset_last_upper_lower_reg(); } //------------------------------------------------- // op_label - process a LABEL opcode //------------------------------------------------- void drcbe_x86::op_label(x86code *&dst, const instruction &inst) { assert_no_condition(inst); assert_no_flags(inst); assert(inst.numparams() == 1); assert(inst.param(0).is_code_label()); // register the current pointer for the label m_labels.set_codeptr(inst.param(0).label(), dst); reset_last_upper_lower_reg(); } //------------------------------------------------- // op_comment - process a COMMENT opcode //------------------------------------------------- void drcbe_x86::op_comment(x86code *&dst, const instruction &inst) { assert_no_condition(inst); assert_no_flags(inst); assert(inst.numparams() == 1); assert(inst.param(0).is_string()); // do nothing } //------------------------------------------------- // op_mapvar - process a MAPVAR opcode //------------------------------------------------- void drcbe_x86::op_mapvar(x86code *&dst, const instruction &inst) { assert_no_condition(inst); assert_no_flags(inst); assert(inst.numparams() == 2); assert(inst.param(0).is_mapvar()); assert(inst.param(1).is_immediate()); // set the value of the specified mapvar m_map.set_value(dst, inst.param(0).mapvar(), inst.param(1).immediate()); } //************************************************************************** // CONTROL FLOW OPCODES //************************************************************************** //------------------------------------------------- // op_nop - process a NOP opcode //------------------------------------------------- void drcbe_x86::op_nop(x86code *&dst, const instruction &inst) { // nothing } //------------------------------------------------- // op_debug - process a DEBUG opcode //------------------------------------------------- void drcbe_x86::op_debug(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); if ((m_device.machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) { // normalize parameters be_parameter pcp(*this, inst.param(0), PTYPE_MRI); // test and branch emit_test_m32_imm(dst, MABS(&m_device.machine().debug_flags), DEBUG_FLAG_CALL_HOOK); // test [debug_flags],DEBUG_FLAG_CALL_HOOK emit_link skip = { 0 }; emit_jcc_short_link(dst, x86emit::COND_Z, skip); // jz skip // push the parameter emit_mov_m32_p32(dst, MBD(REG_ESP, 4), pcp); // mov [esp+4],pcp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_device); // mov [esp],device emit_call(dst, (x86code *)debugger_instruction_hook); // call debug_cpu_instruction_hook track_resolve_link(dst, skip); // skip: } } //------------------------------------------------- // op_exit - process an EXIT opcode //------------------------------------------------- void drcbe_x86::op_exit(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter retp(*this, inst.param(0), PTYPE_MRI); // load the parameter into EAX emit_mov_r32_p32(dst, REG_EAX, retp); // mov eax,retp if (inst.condition() == uml::COND_ALWAYS) emit_jmp(dst, m_exit); // jmp exit else emit_jcc(dst, X86_CONDITION(inst.condition()), m_exit); // jcc exit } //------------------------------------------------- // op_hashjmp - process a HASHJMP opcode //------------------------------------------------- void drcbe_x86::op_hashjmp(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter modep(*this, inst.param(0), PTYPE_MRI); be_parameter pcp(*this, inst.param(1), PTYPE_MRI); const parameter &exp = inst.param(2); assert(exp.is_code_handle()); if (LOG_HASHJMPS) { emit_mov_m32_p32(dst, MBD(REG_ESP, 4), pcp); emit_mov_m32_p32(dst, MBD(REG_ESP, 0), modep); emit_call(dst, (x86code *)debug_log_hashjmp); } // load the stack base one word early so we end up at the right spot after our call below emit_mov_r32_m32(dst, REG_ESP, MABS(&m_hashstacksave)); // mov esp,[hashstacksave] // fixed mode cases if (modep.is_immediate() && m_hash.is_mode_populated(modep.immediate())) { // a straight immediate jump is direct, though we need the PC in EAX in case of failure if (pcp.is_immediate()) { UINT32 l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask(); UINT32 l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask(); emit_call_m32(dst, MABS(&m_hash.base()[modep.immediate()][l1val][l2val])); // call hash[modep][l1val][l2val] } // a fixed mode but variable PC else { emit_mov_r32_p32(dst, REG_EAX, pcp); // mov eax,pcp emit_mov_r32_r32(dst, REG_EDX, REG_EAX); // mov edx,eax emit_shr_r32_imm(dst, REG_EDX, m_hash.l1shift()); // shr edx,l1shift emit_and_r32_imm(dst, REG_EAX, m_hash.l2mask() << m_hash.l2shift());// and eax,l2mask << l2shift emit_mov_r32_m32(dst, REG_EDX, MABSI(&m_hash.base()[modep.immediate()][0], REG_EDX, 4)); // mov edx,hash[modep+edx*4] emit_call_m32(dst, MBISD(REG_EDX, REG_EAX, 4 >> m_hash.l2shift(), 0));// call [edx+eax*shift] } } else { // variable mode int modereg = modep.select_register(REG_ECX); emit_mov_r32_p32(dst, modereg, modep); // mov modereg,modep emit_mov_r32_m32(dst, REG_ECX, MABSI(m_hash.base(), modereg, 4)); // mov ecx,hash[modereg*4] // fixed PC if (pcp.is_immediate()) { UINT32 l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask(); UINT32 l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask(); emit_mov_r32_m32(dst, REG_EDX, MBD(REG_ECX, l1val*4)); // mov edx,[ecx+l1val*4] emit_call_m32(dst, MBD(REG_EDX, l2val*4)); // call [l2val*4] } // variable PC else { emit_mov_r32_p32(dst, REG_EAX, pcp); // mov eax,pcp emit_mov_r32_r32(dst, REG_EDX, REG_EAX); // mov edx,eax emit_shr_r32_imm(dst, REG_EDX, m_hash.l1shift()); // shr edx,l1shift emit_mov_r32_m32(dst, REG_EDX, MBISD(REG_ECX, REG_EDX, 4, 0)); // mov edx,[ecx+edx*4] emit_and_r32_imm(dst, REG_EAX, m_hash.l2mask() << m_hash.l2shift());// and eax,l2mask << l2shift emit_call_m32(dst, MBISD(REG_EDX, REG_EAX, 4 >> m_hash.l2shift(), 0));// call [edx+eax*shift] } } // in all cases, if there is no code, we return here to generate the exception emit_mov_m32_p32(dst, MABS(&m_state.exp), pcp); // mov [exp],param emit_sub_r32_imm(dst, REG_ESP, 4); // sub esp,4 emit_call_m32(dst, MABS(exp.handle().codeptr_addr())); // call [exp] } //------------------------------------------------- // op_jmp - process a JMP opcode //------------------------------------------------- void drcbe_x86::op_jmp(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters const parameter &labelp = inst.param(0); assert(labelp.is_code_label()); // look up the jump target and jump there x86code *jmptarget = (x86code *)m_labels.get_codeptr(labelp.label(), m_fixup_label, dst); if (inst.condition() == uml::COND_ALWAYS) emit_jmp(dst, jmptarget); // jmp target else emit_jcc(dst, X86_CONDITION(inst.condition()), jmptarget); // jcc target } //------------------------------------------------- // op_exh - process an EXH opcode //------------------------------------------------- void drcbe_x86::op_exh(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters const parameter &handp = inst.param(0); assert(handp.is_code_handle()); be_parameter exp(*this, inst.param(1), PTYPE_MRI); // look up the handle target drccodeptr *targetptr = handp.handle().codeptr_addr(); // perform the exception processing inline if unconditional if (inst.condition() == uml::COND_ALWAYS) { emit_mov_m32_p32(dst, MABS(&m_state.exp), exp); // mov [exp],exp if (*targetptr != NULL) emit_call(dst, *targetptr); // call *targetptr else emit_call_m32(dst, MABS(targetptr)); // call [targetptr] } // otherwise, jump to an out-of-band handler else { emit_jcc(dst, X86_CONDITION(inst.condition()), 0); // jcc exception m_cache.request_oob_codegen(m_fixup_exception, dst, &const_cast(inst)); } } //------------------------------------------------- // op_callh - process a CALLH opcode //------------------------------------------------- void drcbe_x86::op_callh(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters const parameter &handp = inst.param(0); assert(handp.is_code_handle()); // look up the handle target drccodeptr *targetptr = handp.handle().codeptr_addr(); // skip if conditional emit_link skip = { 0 }; if (inst.condition() != uml::COND_ALWAYS) emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip); // jcc skip // jump through the handle; directly if a normal jump if (*targetptr != NULL) emit_call(dst, *targetptr); // call *targetptr else emit_call_m32(dst, MABS(targetptr)); // call [targetptr] // resolve the conditional link if (inst.condition() != uml::COND_ALWAYS) track_resolve_link(dst, skip); // skip: } //------------------------------------------------- // op_ret - process a RET opcode //------------------------------------------------- void drcbe_x86::op_ret(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_any_condition(inst); assert_no_flags(inst); assert(inst.numparams() == 0); // skip if conditional emit_link skip = { 0 }; if (inst.condition() != uml::COND_ALWAYS) emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip); // jcc skip // return emit_lea_r32_m32(dst, REG_ESP, MBD(REG_ESP, 28)); // lea rsp,[rsp+28] emit_ret(dst); // ret // resolve the conditional link if (inst.condition() != uml::COND_ALWAYS) track_resolve_link(dst, skip); // skip: } //------------------------------------------------- // op_callc - process a CALLC opcode //------------------------------------------------- void drcbe_x86::op_callc(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters const parameter &funcp = inst.param(0); assert(funcp.is_c_function()); be_parameter paramp(*this, inst.param(1), PTYPE_M); // skip if conditional emit_link skip = { 0 }; if (inst.condition() != uml::COND_ALWAYS) emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip); // jcc skip // perform the call emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)paramp.memory()); // mov [esp],paramp emit_call(dst, (x86code *)(FPTR)funcp.cfunc()); // call funcp // resolve the conditional link if (inst.condition() != uml::COND_ALWAYS) track_resolve_link(dst, skip); // skip: } //------------------------------------------------- // op_recover - process a RECOVER opcode //------------------------------------------------- void drcbe_x86::op_recover(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); // call the recovery code emit_mov_r32_m32(dst, REG_EAX, MABS(&m_stacksave)); // mov eax,stacksave emit_mov_r32_m32(dst, REG_EAX, MBD(REG_EAX, -4)); // mov eax,[eax-4] emit_sub_r32_imm(dst, REG_EAX, 1); // sub eax,1 emit_mov_m32_imm(dst, MBD(REG_ESP, 8), inst.param(1).mapvar()); // mov [esp+8],param1 emit_mov_m32_r32(dst, MBD(REG_ESP, 4), REG_EAX); // mov [esp+4],eax emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_map); // mov [esp],m_map emit_call(dst, (x86code *)&drc_map_variables::static_get_value); // call drcmap_get_value emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax } //************************************************************************** // INTERNAL REGISTER OPCODES //************************************************************************** //------------------------------------------------- // op_setfmod - process a SETFMOD opcode //------------------------------------------------- void drcbe_x86::op_setfmod(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter srcp(*this, inst.param(0), PTYPE_MRI); // immediate case if (srcp.is_immediate()) { int value = srcp.immediate() & 3; emit_mov_m8_imm(dst, MABS(&m_state.fmod), value); // mov [fmod],srcp emit_fldcw_m16(dst, MABS(&fp_control[value])); // fldcw fp_control[srcp] } // register/memory case else { emit_mov_r32_p32(dst, REG_EAX, srcp); // mov eax,srcp emit_and_r32_imm(dst, REG_EAX, 3); // and eax,3 emit_mov_m8_r8(dst, MABS(&m_state.fmod), REG_AL); // mov [fmod],al emit_fldcw_m16(dst, MABSI(&fp_control[0], REG_EAX, 2)); // fldcw fp_control[eax] } } //------------------------------------------------- // op_getfmod - process a GETFMOD opcode //------------------------------------------------- void drcbe_x86::op_getfmod(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); // fetch the current mode and store to the destination if (dstp.is_int_register()) emit_movzx_r32_m8(dst, dstp.ireg(), MABS(&m_state.fmod)); // movzx reg,[fmod] else { emit_movzx_r32_m8(dst, REG_EAX, MABS(&m_state.fmod)); // movzx eax,[fmod] emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // mov [dstp],eax } } //------------------------------------------------- // op_getexp - process a GETEXP opcode //------------------------------------------------- void drcbe_x86::op_getexp(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); // fetch the exception parameter and store to the destination if (dstp.is_int_register()) emit_mov_r32_m32(dst, dstp.ireg(), MABS(&m_state.exp)); // mov reg,[exp] else { emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.exp)); // mov eax,[exp] emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // mov [dstp],eax } } //------------------------------------------------- // op_getflgs - process a GETFLGS opcode //------------------------------------------------- void drcbe_x86::op_getflgs(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter maskp(*this, inst.param(1), PTYPE_I); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // compute mask for flags UINT32 flagmask = 0; if (maskp.immediate() & FLAG_C) flagmask |= 0x001; if (maskp.immediate() & FLAG_V) flagmask |= 0x800; if (maskp.immediate() & FLAG_Z) flagmask |= 0x040; if (maskp.immediate() & FLAG_S) flagmask |= 0x080; if (maskp.immediate() & FLAG_U) flagmask |= 0x004; switch (maskp.immediate()) { // single flags only case FLAG_C: emit_setcc_r8(dst, x86emit::COND_C, REG_AL); // setc al emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al break; case FLAG_V: emit_setcc_r8(dst, x86emit::COND_O, REG_AL); // seto al emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al emit_shl_r32_imm(dst, dstreg, 1); // shl dstreg,1 break; case FLAG_Z: emit_setcc_r8(dst, x86emit::COND_Z, REG_AL); // setz al emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al emit_shl_r32_imm(dst, dstreg, 2); // shl dstreg,2 break; case FLAG_S: emit_setcc_r8(dst, x86emit::COND_S, REG_AL); // sets al emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al emit_shl_r32_imm(dst, dstreg, 3); // shl dstreg,3 break; case FLAG_U: emit_setcc_r8(dst, x86emit::COND_P, REG_AL); // setp al emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al emit_shl_r32_imm(dst, dstreg, 4); // shl dstreg,4 break; // carry plus another flag case FLAG_C | FLAG_V: emit_setcc_r8(dst, x86emit::COND_C, REG_AL); // setc al emit_setcc_r8(dst, x86emit::COND_O, REG_CL); // seto cl emit_movzx_r32_r8(dst, REG_EAX, REG_AL); // movzx eax,al emit_movzx_r32_r8(dst, REG_ECX, REG_CL); // movzx ecx,al emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 2, 0)); // lea dstreg,[eax+ecx*2] break; case FLAG_C | FLAG_Z: emit_setcc_r8(dst, x86emit::COND_C, REG_AL); // setc al emit_setcc_r8(dst, x86emit::COND_Z, REG_CL); // setz cl emit_movzx_r32_r8(dst, REG_EAX, REG_AL); // movzx eax,al emit_movzx_r32_r8(dst, REG_ECX, REG_CL); // movzx ecx,al emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 4, 0)); // lea dstreg,[eax+ecx*4] break; case FLAG_C | FLAG_S: emit_setcc_r8(dst, x86emit::COND_C, REG_AL); // setc al emit_setcc_r8(dst, x86emit::COND_S, REG_CL); // sets cl emit_movzx_r32_r8(dst, REG_EAX, REG_AL); // movzx eax,al emit_movzx_r32_r8(dst, REG_ECX, REG_CL); // movzx ecx,al emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 8, 0)); // lea dstreg,[eax+ecx*8] break; // overflow plus another flag case FLAG_V | FLAG_Z: emit_setcc_r8(dst, x86emit::COND_O, REG_AL); // seto al emit_setcc_r8(dst, x86emit::COND_Z, REG_CL); // setz cl emit_movzx_r32_r8(dst, REG_EAX, REG_AL); // movzx eax,al emit_movzx_r32_r8(dst, REG_ECX, REG_CL); // movzx ecx,al emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 2, 0)); // lea dstreg,[eax+ecx*2] emit_shl_r32_imm(dst, dstreg, 1); // shl dstreg,1 break; case FLAG_V | FLAG_S: emit_setcc_r8(dst, x86emit::COND_O, REG_AL); // seto al emit_setcc_r8(dst, x86emit::COND_S, REG_CL); // sets cl emit_movzx_r32_r8(dst, REG_EAX, REG_AL); // movzx eax,al emit_movzx_r32_r8(dst, REG_ECX, REG_CL); // movzx ecx,al emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 4, 0)); // lea dstreg,[eax+ecx*4] emit_shl_r32_imm(dst, dstreg, 1); // shl dstreg,1 break; // zero plus another flag case FLAG_Z | FLAG_S: emit_setcc_r8(dst, x86emit::COND_Z, REG_AL); // setz al emit_setcc_r8(dst, x86emit::COND_S, REG_CL); // sets cl emit_movzx_r32_r8(dst, REG_EAX, REG_AL); // movzx eax,al emit_movzx_r32_r8(dst, REG_ECX, REG_CL); // movzx ecx,al emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 2, 0)); // lea dstreg,[eax+ecx*2] emit_shl_r32_imm(dst, dstreg, 2); // shl dstreg,2 break; // default cases default: emit_pushf(dst); // pushf emit_pop_r32(dst, REG_EAX); // pop eax emit_and_r32_imm(dst, REG_EAX, flagmask); // and eax,flagmask emit_movzx_r32_m8(dst, dstreg, MABSI(flags_map, REG_EAX)); // movzx dstreg,[flags_map] break; } // store low 32 bits emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form stores upper 32 bits if (inst.size() == 8) { // general case if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov [dstp+4],0 else if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov [reghi],0 } } //------------------------------------------------- // op_save - process a SAVE opcode //------------------------------------------------- void drcbe_x86::op_save(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_M); // copy live state to the destination emit_mov_r32_imm(dst, REG_ECX, (FPTR)dstp.memory()); // mov ecx,dstp emit_call(dst, m_save); // call save } //------------------------------------------------- // op_restore - process a RESTORE opcode //------------------------------------------------- void drcbe_x86::op_restore(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4); assert_no_condition(inst); // normalize parameters be_parameter srcp(*this, inst.param(0), PTYPE_M); // copy live state from the destination emit_mov_r32_imm(dst, REG_ECX, (FPTR)srcp.memory()); // mov ecx,dstp emit_call(dst, m_restore); // call restore } //************************************************************************** // INTEGER OPERATIONS //************************************************************************** //------------------------------------------------- // op_load - process a LOAD opcode //------------------------------------------------- void drcbe_x86::op_load(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter basep(*this, inst.param(1), PTYPE_M); be_parameter indp(*this, inst.param(2), PTYPE_MRI); const parameter &scalesizep = inst.param(3); assert(scalesizep.is_size_scale()); int scale = 1 << scalesizep.scale(); int size = scalesizep.size(); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // immediate index if (indp.is_immediate()) { if (size == SIZE_BYTE) emit_movzx_r32_m8(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // movzx dstreg,[basep + scale*indp] else if (size == SIZE_WORD) emit_movzx_r32_m16(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // movzx dstreg,[basep + scale*indp] else if (size == SIZE_DWORD) emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp] else if (size == SIZE_QWORD) { emit_mov_r32_m32(dst, REG_EDX, MABS(basep.memory(scale*indp.immediate() + 4))); // mov edx,[basep + scale*indp + 4] emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp] } } // other index else { int indreg = indp.select_register(REG_ECX); emit_mov_r32_p32(dst, indreg, indp); if (size == SIZE_BYTE) emit_movzx_r32_m8(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // movzx dstreg,[basep + scale*indp] else if (size == SIZE_WORD) emit_movzx_r32_m16(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // movzx dstreg,[basep + scale*indp] else if (size == SIZE_DWORD) emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // mov dstreg,[basep + scale*indp] else if (size == SIZE_QWORD) { emit_mov_r32_m32(dst, REG_EDX, MABSI(basep.memory(4), indreg, scale)); // mov edx,[basep + scale*indp + 4] emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // mov dstreg,[basep + scale*indp] } } // store low 32 bits emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form stores upper 32 bits if (inst.size() == 8) { // 1, 2, or 4-byte case if (size != SIZE_QWORD) { if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov [dstp+4],0 else if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov [reghi],0 } // 8-byte case else { if (dstp.is_memory()) emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // mov [dstp+4],edx else if (dstp.is_int_register()) emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX); // mov [reghi],edx set_last_upper_reg(dst, dstp, REG_EDX); } } set_last_lower_reg(dst, dstp, dstreg); } //------------------------------------------------- // op_loads - process a LOADS opcode //------------------------------------------------- void drcbe_x86::op_loads(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter basep(*this, inst.param(1), PTYPE_M); be_parameter indp(*this, inst.param(2), PTYPE_MRI); const parameter &scalesizep = inst.param(3); assert(scalesizep.is_size_scale()); int scale = 1 << scalesizep.scale(); int size = scalesizep.size(); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // immediate index if (indp.is_immediate()) { if (size == SIZE_BYTE) emit_movsx_r32_m8(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // movsx dstreg,[basep + scale*indp] else if (size == SIZE_WORD) emit_movsx_r32_m16(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // movsx dstreg,[basep + scale*indp] else if (size == SIZE_DWORD) emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp] else if (size == SIZE_QWORD) { emit_mov_r32_m32(dst, REG_EDX, MABS(basep.memory(scale*indp.immediate() + 4))); // mov edx,[basep + scale*indp + 4] emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp] } } // other index else { int indreg = indp.select_register(REG_ECX); emit_mov_r32_p32(dst, indreg, indp); if (size == SIZE_BYTE) emit_movsx_r32_m8(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // movsx dstreg,[basep + scale*indp] else if (size == SIZE_WORD) emit_movsx_r32_m16(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // movsx dstreg,[basep + scale*indp] else if (size == SIZE_DWORD) emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // mov dstreg,[basep + scale*indp] else if (size == SIZE_QWORD) { emit_mov_r32_m32(dst, REG_EDX, MABSI(basep.memory(4), indreg, scale)); // mov edx,[basep + scale*indp + 4] emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale)); // mov dstreg,[basep + scale*indp] } } // store low 32 bits emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form stores upper 32 bits if (inst.size() == 8) { emit_cdq(dst); // cdq if (dstp.is_memory()) emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // mov [dstp+4],edx else if (dstp.is_int_register()) emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX); // mov [reghi],edx set_last_upper_reg(dst, dstp, REG_EDX); } set_last_lower_reg(dst, dstp, dstreg); } //------------------------------------------------- // op_store - process a STORE opcode //------------------------------------------------- void drcbe_x86::op_store(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter basep(*this, inst.param(0), PTYPE_M); be_parameter indp(*this, inst.param(1), PTYPE_MRI); be_parameter srcp(*this, inst.param(2), PTYPE_MRI); const parameter &scalesizep = inst.param(3); int scale = 1 << (scalesizep.scale()); int size = scalesizep.size(); // pick a source register for the general case int srcreg = srcp.select_register(REG_EAX); if (size == SIZE_BYTE && (srcreg & 4)) srcreg = REG_EAX; // degenerate case: constant index if (indp.is_immediate()) { // immediate source if (srcp.is_immediate()) { if (size == SIZE_BYTE) emit_mov_m8_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate()); // mov [basep + scale*indp],srcp else if (size == SIZE_WORD) emit_mov_m16_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate()); // mov [basep + scale*indp],srcp else if (size == SIZE_DWORD) emit_mov_m32_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate()); // mov [basep + scale*indp],srcp else if (size == SIZE_QWORD) { emit_mov_m32_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate()); // mov [basep + scale*indp],srcp emit_mov_m32_imm(dst, MABS(basep.memory(scale*indp.immediate() + 4)), srcp.immediate() >> 32); // mov [basep + scale*indp + 4],srcp >> 32 } } // variable source else { if (size != SIZE_QWORD) emit_mov_r32_p32(dst, srcreg, srcp); // mov srcreg,srcp else emit_mov_r64_p64(dst, srcreg, REG_EDX, srcp); // mov edx:srcreg,srcp if (size == SIZE_BYTE) emit_mov_m8_r8(dst, MABS(basep.memory(scale*indp.immediate())), srcreg); // mov [basep + scale*indp],srcreg else if (size == SIZE_WORD) emit_mov_m16_r16(dst, MABS(basep.memory(scale*indp.immediate())), srcreg); // mov [basep + scale*indp],srcreg else if (size == SIZE_DWORD) emit_mov_m32_r32(dst, MABS(basep.memory(scale*indp.immediate())), srcreg); // mov [basep + scale*indp],srcreg else if (size == SIZE_QWORD) { emit_mov_m32_r32(dst, MABS(basep.memory(scale*indp.immediate())), srcreg); // mov [basep + scale*indp],srcreg emit_mov_m32_r32(dst, MABS(basep.memory(scale*indp.immediate() + 4)), REG_EDX); // mov [basep + scale*indp + 4],edx } } } // normal case: variable index else { int indreg = indp.select_register(REG_ECX); emit_mov_r32_p32(dst, indreg, indp); // mov indreg,indp // immediate source if (srcp.is_immediate()) { if (size == SIZE_BYTE) emit_mov_m8_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate()); // mov [basep + 1*ecx],srcp else if (size == SIZE_WORD) emit_mov_m16_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate()); // mov [basep + 2*ecx],srcp else if (size == SIZE_DWORD) emit_mov_m32_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate()); // mov [basep + 4*ecx],srcp else if (size == SIZE_QWORD) { emit_mov_m32_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate()); // mov [basep + 8*ecx],srcp emit_mov_m32_imm(dst, MABSI(basep.memory(4), indreg, scale), srcp.immediate() >> 32); // mov [basep + 8*ecx + 4],srcp >> 32 } } // variable source else { if (size != SIZE_QWORD) emit_mov_r32_p32(dst, srcreg, srcp); // mov srcreg,srcp else emit_mov_r64_p64(dst, srcreg, REG_EDX, srcp); // mov edx:srcreg,srcp if (size == SIZE_BYTE) emit_mov_m8_r8(dst, MABSI(basep.memory(), indreg, scale), srcreg); // mov [basep + 1*ecx],srcreg else if (size == SIZE_WORD) emit_mov_m16_r16(dst, MABSI(basep.memory(), indreg, scale), srcreg); // mov [basep + 2*ecx],srcreg else if (size == SIZE_DWORD) emit_mov_m32_r32(dst, MABSI(basep.memory(), indreg, scale), srcreg); // mov [basep + 4*ecx],srcreg else if (size == SIZE_QWORD) { emit_mov_m32_r32(dst, MABSI(basep.memory(), indreg, scale), srcreg); // mov [basep + 8*ecx],srcreg emit_mov_m32_r32(dst, MABSI(basep.memory(4), indreg, scale), REG_EDX); // mov [basep + 8*ecx],edx } } } } //------------------------------------------------- // op_read - process a READ opcode //------------------------------------------------- void drcbe_x86::op_read(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter addrp(*this, inst.param(1), PTYPE_MRI); const parameter &spacesizep = inst.param(2); assert(spacesizep.is_size_space()); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // set up a call to the read byte handler emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp); // mov [esp+4],addrp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]); // mov [esp],space if (spacesizep.size() == SIZE_BYTE) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_byte); // call read_byte emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al } else if (spacesizep.size() == SIZE_WORD) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_word); // call read_word emit_movzx_r32_r16(dst, dstreg, REG_AX); // movzx dstreg,ax } else if (spacesizep.size() == SIZE_DWORD) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_dword); // call read_dword emit_mov_r32_r32(dst, dstreg, REG_EAX); // mov dstreg,eax } else if (spacesizep.size() == SIZE_QWORD) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_qword); // call read_qword emit_mov_r32_r32(dst, dstreg, REG_EAX); // mov dstreg,eax } // store low 32 bits emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form stores upper 32 bits if (inst.size() == 8) { // 1, 2, or 4-byte case if (spacesizep.size() != SIZE_QWORD) { if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov [dstp+4],0 else if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov [reghi],0 } // 8-byte case else { if (dstp.is_memory()) emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // mov [dstp+4],edx else if (dstp.is_int_register()) emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX); // mov [reghi],edx } } } //------------------------------------------------- // op_readm - process a READM opcode //------------------------------------------------- void drcbe_x86::op_readm(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter addrp(*this, inst.param(1), PTYPE_MRI); be_parameter maskp(*this, inst.param(2), PTYPE_MRI); const parameter &spacesizep = inst.param(3); assert(spacesizep.is_size_space()); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // set up a call to the read byte handler if (spacesizep.size() != SIZE_QWORD) emit_mov_m32_p32(dst, MBD(REG_ESP, 8), maskp); // mov [esp+8],maskp else emit_mov_m64_p64(dst, MBD(REG_ESP, 8), maskp); // mov [esp+8],maskp emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp); // mov [esp+4],addrp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]); // mov [esp],space if (spacesizep.size() == SIZE_WORD) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_word_masked); // call read_word_masked emit_movzx_r32_r16(dst, dstreg, REG_AX); // movzx dstreg,ax } else if (spacesizep.size() == SIZE_DWORD) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_dword_masked); // call read_dword_masked emit_mov_r32_r32(dst, dstreg, REG_EAX); // mov dstreg,eax } else if (spacesizep.size() == SIZE_QWORD) { emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_qword_masked); // call read_qword_masked emit_mov_r32_r32(dst, dstreg, REG_EAX); // mov dstreg,eax } // store low 32 bits emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form stores upper 32 bits if (inst.size() == 8) { // 1, 2, or 4-byte case if (spacesizep.size() != SIZE_QWORD) { if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov [dstp+4],0 else if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov [reghi],0 } // 8-byte case else { if (dstp.is_memory()) emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // mov [dstp+4],edx else if (dstp.is_int_register()) emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX); // mov [reghi],edx } } } //------------------------------------------------- // op_write - process a WRITE opcode //------------------------------------------------- void drcbe_x86::op_write(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter addrp(*this, inst.param(0), PTYPE_MRI); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); const parameter &spacesizep = inst.param(2); assert(spacesizep.is_size_space()); // set up a call to the write byte handler if (spacesizep.size() != SIZE_QWORD) emit_mov_m32_p32(dst, MBD(REG_ESP, 8), srcp); // mov [esp+8],srcp else emit_mov_m64_p64(dst, MBD(REG_ESP, 8), srcp); // mov [esp+8],srcp emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp); // mov [esp+4],addrp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]); // mov [esp],space if (spacesizep.size() == SIZE_BYTE) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_byte); // call write_byte else if (spacesizep.size() == SIZE_WORD) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_word); // call write_word else if (spacesizep.size() == SIZE_DWORD) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_dword); // call write_dword else if (spacesizep.size() == SIZE_QWORD) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_qword); // call write_qword } //------------------------------------------------- // op_writem - process a WRITEM opcode //------------------------------------------------- void drcbe_x86::op_writem(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter addrp(*this, inst.param(0), PTYPE_MRI); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); be_parameter maskp(*this, inst.param(2), PTYPE_MRI); const parameter &spacesizep = inst.param(3); assert(spacesizep.is_size_space()); // set up a call to the write byte handler if (spacesizep.size() != SIZE_QWORD) { emit_mov_m32_p32(dst, MBD(REG_ESP, 12), maskp); // mov [esp+12],maskp emit_mov_m32_p32(dst, MBD(REG_ESP, 8), srcp); // mov [esp+8],srcp } else { emit_mov_m64_p64(dst, MBD(REG_ESP, 16), maskp); // mov [esp+16],maskp emit_mov_m64_p64(dst, MBD(REG_ESP, 8), srcp); // mov [esp+8],srcp } emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp); // mov [esp+4],addrp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]); // mov [esp],space if (spacesizep.size() == SIZE_WORD) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_word_masked); // call write_word_masked else if (spacesizep.size() == SIZE_DWORD) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_dword_masked); // call write_dword_masked else if (spacesizep.size() == SIZE_QWORD) emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_qword_masked); // call write_qword_masked } //------------------------------------------------- // op_carry - process a CARRY opcode //------------------------------------------------- void drcbe_x86::op_carry(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C); // normalize parameters be_parameter srcp(*this, inst.param(0), PTYPE_MRI); be_parameter bitp(*this, inst.param(1), PTYPE_MRI); // degenerate case: source is immediate if (srcp.is_immediate() && bitp.is_immediate()) { if (srcp.immediate() & ((UINT64)1 << bitp.immediate())) emit_stc(dst); else emit_clc(dst); } // load non-immediate bit numbers into a register if (!bitp.is_immediate()) { emit_mov_r32_p32(dst, REG_ECX, bitp); emit_and_r32_imm(dst, REG_ECX, inst.size() * 8 - 1); } // 32-bit form if (inst.size() == 4) { if (bitp.is_immediate()) { if (srcp.is_memory()) emit_bt_m32_imm(dst, MABS(srcp.memory()), bitp.immediate()); // bt [srcp],bitp else if (srcp.is_int_register()) emit_bt_r32_imm(dst, srcp.ireg(), bitp.immediate()); // bt srcp,bitp } else { if (srcp.is_memory()) emit_bt_m32_r32(dst, MABS(srcp.memory()), REG_ECX); // bt [srcp],ecx else if (srcp.is_int_register()) emit_bt_r32_r32(dst, srcp.ireg(), REG_ECX); // bt [srcp],ecx } } // 64-bit form else { if (bitp.is_immediate()) { if (srcp.is_memory()) emit_bt_m32_imm(dst, MABS(srcp.memory()), bitp.immediate()); // bt [srcp],bitp else if (srcp.is_int_register() && bitp.immediate() < 32) emit_bt_r32_imm(dst, srcp.ireg(), bitp.immediate()); // bt srcp,bitp else if (srcp.is_int_register() && bitp.immediate() >= 32) emit_bt_m32_imm(dst, MABS(m_reghi[srcp.ireg()]), bitp.immediate() - 32); // bt [srcp.hi],bitp } else { if (srcp.is_memory()) emit_bt_m32_r32(dst, MABS(srcp.memory()), REG_ECX); // bt [srcp],ecx else if (srcp.is_int_register()) { emit_mov_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), srcp.ireg()); // mov [srcp.lo],srcp emit_bt_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), REG_ECX); // bt [srcp],ecx } } } } //------------------------------------------------- // op_set - process a SET opcode //------------------------------------------------- void drcbe_x86::op_set(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // set to AL emit_setcc_r8(dst, X86_CONDITION(inst.condition()), REG_AL); // setcc al emit_movzx_r32_r8(dst, dstreg, REG_AL); // movzx dstreg,al // store low 32 bits emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form stores upper 32 bits if (inst.size() == 8) { // general case if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov [dstp+4],0 else if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov [reghi],0 } } //------------------------------------------------- // op_mov - process a MOV opcode //------------------------------------------------- void drcbe_x86::op_mov(x86code *&dst, const instruction &inst) { x86code *savedst = dst; // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // always start with a jmp emit_link skip = { 0 }; if (inst.condition() != uml::COND_ALWAYS) emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip); // jcc skip // 32-bit form if (inst.size() == 4) { // register to memory if (dstp.is_memory() && srcp.is_int_register()) emit_mov_m32_r32(dst, MABS(dstp.memory()), srcp.ireg()); // mov [dstp],srcp // immediate to memory else if (dstp.is_memory() && srcp.is_immediate()) emit_mov_m32_imm(dst, MABS(dstp.memory()), srcp.immediate()); // mov [dstp],srcp // conditional memory to register else if (inst.condition() != uml::COND_ALWAYS && dstp.is_int_register() && srcp.is_memory()) { dst = savedst; skip.target = NULL; emit_cmovcc_r32_m32(dst, X86_CONDITION(inst.condition()), dstp.ireg(), MABS(srcp.memory())); // cmovcc dstp,[srcp] } // conditional register to register else if (inst.condition() != uml::COND_ALWAYS && dstp.is_int_register() && srcp.is_int_register()) { dst = savedst; skip.target = NULL; emit_cmovcc_r32_r32(dst, X86_CONDITION(inst.condition()), dstp.ireg(), srcp.ireg()); // cmovcc dstp,srcp } // general case else { emit_mov_r32_p32_keepflags(dst, dstreg, srcp); // mov dstreg,srcp emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // register to memory if (dstp.is_memory() && srcp.is_int_register()) { emit_mov_r32_m32(dst, REG_EAX, MABS(m_reghi[srcp.ireg()])); // mov eax,reghi[srcp] emit_mov_m32_r32(dst, MABS(dstp.memory()), srcp.ireg()); // mov [dstp],srcp emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EAX); // mov [dstp+4],eax } // immediate to memory else if (dstp.is_memory() && srcp.is_immediate()) { emit_mov_m32_imm(dst, MABS(dstp.memory()), srcp.immediate()); // mov [dstp],srcp emit_mov_m32_imm(dst, MABS(dstp.memory(4)), srcp.immediate() >> 32); // mov [dstp+4],srcp >> 32 } // general case else { emit_mov_r64_p64(dst, dstreg, REG_EDX, srcp); // mov edx:dstreg,srcp emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,edx:dstreg } } // resolve the jump if (skip.target != NULL) track_resolve_link(dst, skip); } //------------------------------------------------- // op_sext - process a SEXT opcode //------------------------------------------------- void drcbe_x86::op_sext(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_S | FLAG_Z); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); const parameter &sizep = inst.param(2); assert(sizep.is_size()); // pick a target register for the general case int dstreg = (inst.size() == 8) ? REG_EAX : dstp.select_register(REG_EAX); // convert 8-bit source registers to EAX if (sizep.size() == SIZE_BYTE && srcp.is_int_register() && (srcp.ireg() & 4)) { emit_mov_r32_r32(dst, REG_EAX, srcp.ireg()); // mov eax,srcp srcp = be_parameter::make_ireg(REG_EAX); } // general case if (srcp.is_memory()) { if (sizep.size() == SIZE_BYTE) emit_movsx_r32_m8(dst, dstreg, MABS(srcp.memory())); // movsx dstreg,[srcp] else if (sizep.size() == SIZE_WORD) emit_movsx_r32_m16(dst, dstreg, MABS(srcp.memory())); // movsx dstreg,[srcp] else if (sizep.size() == SIZE_DWORD) emit_mov_r32_m32(dst, dstreg, MABS(srcp.memory())); // mov dstreg,[srcp] } else if (srcp.is_int_register()) { if (sizep.size() == SIZE_BYTE) emit_movsx_r32_r8(dst, dstreg, srcp.ireg()); // movsx dstreg,srcp else if (sizep.size() == SIZE_WORD) emit_movsx_r32_r16(dst, dstreg, srcp.ireg()); // movsx dstreg,srcp else if (sizep.size() == SIZE_DWORD && dstreg != srcp.ireg()) emit_mov_r32_r32(dst, dstreg, srcp.ireg()); // mov dstreg,srcp } if (inst.flags() != 0) emit_test_r32_r32(dst, dstreg, dstreg); // test dstreg,dstreg // 32-bit form: store the low 32 bits if (inst.size() == 4) emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg // 64-bit form: sign extend to 64 bits and store edx:eax else if (inst.size() == 8) { emit_cdq(dst); // cdq emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax } } //------------------------------------------------- // op_roland - process an ROLAND opcode //------------------------------------------------- void drcbe_x86::op_roland(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_S | FLAG_Z); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); be_parameter shiftp(*this, inst.param(2), PTYPE_MRI); be_parameter maskp(*this, inst.param(3), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, shiftp, maskp); // 32-bit form if (inst.size() == 4) { emit_mov_r32_p32(dst, dstreg, srcp); // mov dstreg,srcp emit_rol_r32_p32(dst, dstreg, shiftp, inst); // rol dstreg,shiftp emit_and_r32_p32(dst, dstreg, maskp, inst); // and dstreg,maskp emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } // 64-bit form else if (inst.size() == 8) { emit_mov_r64_p64(dst, dstreg, REG_EDX, srcp); // mov edx:dstreg,srcp emit_rol_r64_p64(dst, dstreg, REG_EDX, shiftp, inst); // rol edx:dstreg,shiftp emit_and_r64_p64(dst, dstreg, REG_EDX, maskp, inst); // and edx:dstreg,maskp emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,edx:dstreg } } //------------------------------------------------- // op_rolins - process an ROLINS opcode //------------------------------------------------- void drcbe_x86::op_rolins(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_S | FLAG_Z); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); be_parameter shiftp(*this, inst.param(2), PTYPE_MRI); be_parameter maskp(*this, inst.param(3), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_ECX, shiftp, maskp); // 32-bit form if (inst.size() == 4) { emit_mov_r32_p32(dst, REG_EAX, srcp); // mov eax,srcp emit_rol_r32_p32(dst, REG_EAX, shiftp, inst); // rol eax,shiftp emit_mov_r32_p32(dst, dstreg, dstp); // mov dstreg,dstp if (maskp.is_immediate()) { emit_and_r32_imm(dst, REG_EAX, maskp.immediate()); // and eax,maskp emit_and_r32_imm(dst, dstreg, ~maskp.immediate()); // and dstreg,~maskp } else { emit_mov_r32_p32(dst, REG_EDX, maskp); // mov edx,maskp emit_and_r32_r32(dst, REG_EAX, REG_EDX); // and eax,edx emit_not_r32(dst, REG_EDX); // not edx emit_and_r32_r32(dst, dstreg, REG_EDX); // and dstreg,edx } emit_or_r32_r32(dst, dstreg, REG_EAX); // or dstreg,eax emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } // 64-bit form else if (inst.size() == 8) { emit_mov_r64_p64(dst, REG_EAX, REG_EDX, srcp); // mov edx:eax,srcp emit_rol_r64_p64(dst, REG_EAX, REG_EDX, shiftp, inst); // rol edx:eax,shiftp if (maskp.is_immediate()) { emit_and_r32_imm(dst, REG_EAX, maskp.immediate()); // and eax,maskp emit_and_r32_imm(dst, REG_EDX, maskp.immediate() >> 32); // and edx,maskp >> 32 if (dstp.is_int_register()) { emit_and_r32_imm(dst, dstp.ireg(), ~maskp.immediate()); // and dstp.lo,~maskp emit_and_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), ~maskp.immediate() >> 32);// and dstp.hi,~maskp >> 32 emit_or_r32_r32(dst, dstp.ireg(), REG_EAX); // or dstp.lo,eax emit_or_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX); // or dstp.hi,edx } else { emit_and_m32_imm(dst, MABS(dstp.memory()), ~maskp.immediate()); // and dstp.lo,~maskp emit_and_m32_imm(dst, MABS(dstp.memory(4)), ~maskp.immediate() >> 32); // and dstp.hi,~maskp >> 32 emit_or_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // or dstp.lo,eax emit_or_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // or dstp.hi,edx } } else { int tempreg = REG_EBX; emit_mov_m32_r32(dst, MBD(REG_ESP, -8), tempreg); // mov [esp-8],ebx emit_mov_r64_p64(dst, tempreg, REG_ECX, maskp); // mov ecx:ebx,maskp emit_and_r32_r32(dst, REG_EAX, tempreg); // and eax,ebx emit_and_r32_r32(dst, REG_EDX, REG_ECX); // and edx,ecx emit_not_r32(dst, tempreg); // not ebx emit_not_r32(dst, REG_ECX); // not ecx if (dstp.is_int_register()) { emit_and_r32_r32(dst, dstp.ireg(), tempreg); // and dstp.lo,ebx emit_and_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_ECX); // and dstp.hi,ecx emit_or_r32_r32(dst, dstp.ireg(), REG_EAX); // or dstp.lo,eax emit_or_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX); // or dstp.hi,edx } else { emit_and_m32_r32(dst, MABS(dstp.memory()), tempreg); // and dstp.lo,ebx emit_and_m32_r32(dst, MABS(dstp.memory(4)), REG_ECX); // and dstp.hi,ecx emit_or_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // or dstp.lo,eax emit_or_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // or dstp.hi,edx } emit_mov_r32_m32(dst, tempreg, MBD(REG_ESP, -8)); // mov ebx,[esp-8] } if (inst.flags() == FLAG_Z) emit_or_r32_r32(dst, REG_EAX, REG_EDX); // or eax,edx else if (inst.flags() == FLAG_S) ;// do nothing -- final OR will have the right result else if (inst.flags() == (FLAG_Z | FLAG_S)) { emit_movzx_r32_r16(dst, REG_ECX, REG_AX); // movzx ecx,ax emit_shr_r32_imm(dst, REG_EAX, 16); // shr eax,16 emit_or_r32_r32(dst, REG_EDX, REG_ECX); // or edx,ecx emit_or_r32_r32(dst, REG_EDX, REG_EAX); // or edx,eax } } } //------------------------------------------------- // op_add - process a ADD opcode //------------------------------------------------- void drcbe_x86::op_add(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); normalize_commutative(src1p, src2p); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_add_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // add [dstp],src2p // reg = reg + imm else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && inst.flags() == 0) emit_lea_r32_m32(dst, dstp.ireg(), MBD(src1p.ireg(), src2p.immediate())); // lea dstp,[src1p+src2p] // reg = reg + reg else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_int_register() && inst.flags() == 0) emit_lea_r32_m32(dst, dstp.ireg(), MBISD(src1p.ireg(), src2p.ireg(), 1, 0)); // lea dstp,[src1p+src2p] // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_add_r32_p32(dst, dstreg, src2p, inst); // add dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_add_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // add [dstp],src2p // general case else { emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_add_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // add dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_addc - process a ADDC opcode //------------------------------------------------- void drcbe_x86::op_addc(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); normalize_commutative(src1p, src2p); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_adc_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // adc [dstp],src2p // general case else { emit_mov_r32_p32_keepflags(dst, dstreg, src1p); // mov dstreg,src1p emit_adc_r32_p32(dst, dstreg, src2p, inst); // adc dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_adc_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // adc [dstp],src2p // general case else { emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_adc_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // adc dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_sub - process a SUB opcode //------------------------------------------------- void drcbe_x86::op_sub(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_sub_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // sub [dstp],src2p // reg = reg - imm else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && inst.flags() == 0) emit_lea_r32_m32(dst, dstp.ireg(), MBD(src1p.ireg(), -src2p.immediate())); // lea dstp,[src1p-src2p] // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_sub_r32_p32(dst, dstreg, src2p, inst); // sub dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_sub_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // sub [dstp],src2p // general case else { emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_sub_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // sub dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_subc - process a SUBC opcode //------------------------------------------------- void drcbe_x86::op_subc(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_sbb_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // sbb [dstp],src2p // general case else { emit_mov_r32_p32_keepflags(dst, dstreg, src1p); // mov dstreg,src1p emit_sbb_r32_p32(dst, dstreg, src2p, inst); // sbb dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_sbb_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // sbb [dstp],src2p // general case else { emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_sbb_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // sbb dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_cmp - process a CMP opcode //------------------------------------------------- void drcbe_x86::op_cmp(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter src1p(*this, inst.param(0), PTYPE_MRI); be_parameter src2p(*this, inst.param(1), PTYPE_MRI); // pick a target register for the general case int src1reg = src1p.select_register(REG_EAX); // 32-bit form if (inst.size() == 4) { // memory versus anything if (src1p.is_memory()) emit_cmp_m32_p32(dst, MABS(src1p.memory()), src2p, inst); // cmp [dstp],src2p // general case else { if (src1p.is_immediate()) emit_mov_r32_imm(dst, src1reg, src1p.immediate()); // mov src1reg,imm emit_cmp_r32_p32(dst, src1reg, src2p, inst); // cmp src1reg,src2p } } // 64-bit form else { // general case emit_mov_r64_p64(dst, REG_EAX, REG_EDX, src1p); // mov eax:dstp,[src1p] emit_cmp_r64_p64(dst, REG_EAX, REG_EDX, src2p, inst); // cmp eax:dstp,src2p } } //------------------------------------------------- // op_mulu - process a MULU opcode //------------------------------------------------- void drcbe_x86::op_mulu(x86code *&dst, const instruction &inst) { UINT8 zsflags = inst.flags() & (FLAG_Z | FLAG_S); UINT8 vflag = inst.flags() & FLAG_V; // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter edstp(*this, inst.param(1), PTYPE_MR); be_parameter src1p(*this, inst.param(2), PTYPE_MRI); be_parameter src2p(*this, inst.param(3), PTYPE_MRI); normalize_commutative(src1p, src2p); bool compute_hi = (dstp != edstp); // 32-bit form if (inst.size() == 4) { // general case emit_mov_r32_p32(dst, REG_EAX, src1p); // mov eax,src1p if (src2p.is_memory()) emit_mul_m32(dst, MABS(src2p.memory())); // mul [src2p] else if (src2p.is_int_register()) emit_mul_r32(dst, src2p.ireg()); // mul src2p else if (src2p.is_immediate()) { emit_mov_r32_imm(dst, REG_EDX, src2p.immediate()); // mov edx,src2p emit_mul_r32(dst, REG_EDX); // mul edx } emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax if (compute_hi) emit_mov_p32_r32(dst, edstp, REG_EDX); // mov edstp,edx // compute flags if (inst.flags() != 0) { if (zsflags != 0) { if (vflag) emit_pushf(dst); // pushf if (compute_hi) { if (zsflags == FLAG_Z) emit_or_r32_r32(dst, REG_EDX, REG_EAX); // or edx,eax else if (zsflags == FLAG_S) emit_test_r32_r32(dst, REG_EDX, REG_EDX); // test edx,edx else { emit_movzx_r32_r16(dst, REG_ECX, REG_AX); // movzx ecx,ax emit_shr_r32_imm(dst, REG_EAX, 16); // shr eax,16 emit_or_r32_r32(dst, REG_EDX, REG_ECX); // or edx,ecx emit_or_r32_r32(dst, REG_EDX, REG_EAX); // or edx,eax } } else emit_test_r32_r32(dst, REG_EAX, REG_EAX); // test eax,eax // we rely on the fact that OF is cleared by all logical operations above if (vflag) { emit_pushf(dst); // pushf emit_pop_r32(dst, REG_EAX); // pop eax emit_and_m32_imm(dst, MBD(REG_ESP, 0), ~0x84); // and [esp],~0x84 emit_or_m32_r32(dst, MBD(REG_ESP, 0), REG_EAX); // or [esp],eax emit_popf(dst); // popf } } } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_m32_imm(dst, MBD(REG_ESP, 24), inst.flags()); // mov [esp+24],flags emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p); // mov [esp+16],src2p emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p); // mov [esp+8],src1p if (!compute_hi) emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo); // mov [esp+4],&reslo else emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi); // mov [esp+4],&reshi emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo); // mov [esp],&reslo emit_call(dst, (x86code *)dmulu); // call dmulu if (inst.flags() != 0) emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4)); // push flags_unmap[eax*4] emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0)); // mov eax,reslo.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1)); // mov edx,reslo.hi emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax if (compute_hi) { emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0)); // mov eax,reshi.lo emit_mov_r32_m32(dst, REG_ECX, MABS((UINT32 *)&m_reshi + 1)); // mov ecx,reshi.hi emit_mov_p64_r64(dst, edstp, REG_EAX, REG_ECX); // mov edstp,ecx:eax } if (inst.flags() != 0) emit_popf(dst); // popf } } //------------------------------------------------- // op_muls - process a MULS opcode //------------------------------------------------- void drcbe_x86::op_muls(x86code *&dst, const instruction &inst) { UINT8 zsflags = inst.flags() & (FLAG_Z | FLAG_S); UINT8 vflag = inst.flags() & FLAG_V; // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter edstp(*this, inst.param(1), PTYPE_MR); be_parameter src1p(*this, inst.param(2), PTYPE_MRI); be_parameter src2p(*this, inst.param(3), PTYPE_MRI); normalize_commutative(src1p, src2p); bool compute_hi = (dstp != edstp); // 32-bit form if (inst.size() == 4) { // 32-bit destination with memory/immediate or register/immediate if (!compute_hi && !src1p.is_immediate() && src2p.is_immediate()) { if (src1p.is_memory()) emit_imul_r32_m32_imm(dst, REG_EAX, MABS(src1p.memory()), src2p.immediate()); // imul eax,[src1p],src2p else if (src1p.is_int_register()) emit_imul_r32_r32_imm(dst, REG_EAX, src1p.ireg(), src2p.immediate()); // imul eax,src1p,src2p emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax } // 32-bit destination, general case else if (!compute_hi) { emit_mov_r32_p32(dst, REG_EAX, src1p); // mov eax,src1p if (src2p.is_memory()) emit_imul_r32_m32(dst, REG_EAX, MABS(src2p.memory())); // imul eax,[src2p] else if (src2p.is_int_register()) emit_imul_r32_r32(dst, REG_EAX, src2p.ireg()); // imul eax,src2p emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax } // 64-bit destination, general case else { emit_mov_r32_p32(dst, REG_EAX, src1p); // mov eax,src1p if (src2p.is_memory()) emit_imul_m32(dst, MABS(src2p.memory())); // imul [src2p] else if (src2p.is_int_register()) emit_imul_r32(dst, src2p.ireg()); // imul src2p else if (src2p.is_immediate()) { emit_mov_r32_imm(dst, REG_EDX, src2p.immediate()); // mov edx,src2p emit_imul_r32(dst, REG_EDX); // imul edx } emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax emit_mov_p32_r32(dst, edstp, REG_EDX); // mov edstp,edx } // compute flags if (inst.flags() != 0) { if (zsflags != 0) { if (vflag) emit_pushf(dst); // pushf if (compute_hi) { if (inst.flags() == FLAG_Z) emit_or_r32_r32(dst, REG_EDX, REG_EAX); // or edx,eax else if (inst.flags() == FLAG_S) emit_test_r32_r32(dst, REG_EDX, REG_EDX); // test edx,edx else { emit_movzx_r32_r16(dst, REG_ECX, REG_AX); // movzx ecx,ax emit_shr_r32_imm(dst, REG_EAX, 16); // shr eax,16 emit_or_r32_r32(dst, REG_EDX, REG_ECX); // or edx,ecx emit_or_r32_r32(dst, REG_EDX, REG_EAX); // or edx,eax } } else emit_test_r32_r32(dst, REG_EAX, REG_EAX); // test eax,eax // we rely on the fact that OF is cleared by all logical operations above if (vflag) { emit_pushf(dst); // pushf emit_pop_r32(dst, REG_EAX); // pop eax emit_and_m32_imm(dst, MBD(REG_ESP, 0), ~0x84); // and [esp],~0x84 emit_or_m32_r32(dst, MBD(REG_ESP, 0), REG_EAX); // or [esp],eax emit_popf(dst); // popf } } } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_m32_imm(dst, MBD(REG_ESP, 24), inst.flags()); // mov [esp+24],flags emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p); // mov [esp+16],src2p emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p); // mov [esp+8],src1p if (!compute_hi) emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo); // mov [esp+4],&reslo else emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi); // push [esp+4],&reshi emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo); // mov [esp],&reslo emit_call(dst, (x86code *)dmuls); // call dmuls if (inst.flags() != 0) emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4)); // push flags_unmap[eax*4] emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0)); // mov eax,reslo.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1)); // mov edx,reslo.hi emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax if (compute_hi) { emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0)); // mov eax,reshi.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reshi + 1)); // mov edx,reshi.hi emit_mov_p64_r64(dst, edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax } if (inst.flags() != 0) emit_popf(dst); // popf } } //------------------------------------------------- // op_divu - process a DIVU opcode //------------------------------------------------- void drcbe_x86::op_divu(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter edstp(*this, inst.param(1), PTYPE_MR); be_parameter src1p(*this, inst.param(2), PTYPE_MRI); be_parameter src2p(*this, inst.param(3), PTYPE_MRI); bool compute_rem = (dstp != edstp); // 32-bit form if (inst.size() == 4) { // general case emit_mov_r32_p32(dst, REG_ECX, src2p); // mov ecx,src2p if (inst.flags() != 0) { emit_mov_r32_imm(dst, REG_EAX, 0xa0000000); // mov eax,0xa0000000 emit_add_r32_r32(dst, REG_EAX, REG_EAX); // add eax,eax } emit_link skip; emit_jecxz_link(dst, skip); // jecxz skip emit_mov_r32_p32(dst, REG_EAX, src1p); // mov eax,src1p emit_xor_r32_r32(dst, REG_EDX, REG_EDX); // xor edx,edx emit_div_r32(dst, REG_ECX); // div ecx emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax if (compute_rem) emit_mov_p32_r32(dst, edstp, REG_EDX); // mov edstp,edx if (inst.flags() != 0) emit_test_r32_r32(dst, REG_EAX, REG_EAX); // test eax,eax track_resolve_link(dst, skip); // skip: } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p); // mov [esp+16],src2p emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p); // mov [esp+8],src1p if (!compute_rem) emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo); // mov [esp+4],&reslo else emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi); // push [esp+4],&reshi emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo); // mov [esp],&reslo emit_call(dst, (x86code *)ddivu); // call ddivu if (inst.flags() != 0) emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4)); // push flags_unmap[eax*4] emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0)); // mov eax,reslo.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1)); // mov edx,reslo.hi emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax if (compute_rem) { emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0)); // mov eax,reshi.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reshi + 1)); // mov edx,reshi.hi emit_mov_p64_r64(dst, edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax } if (inst.flags() != 0) emit_popf(dst); // popf } } //------------------------------------------------- // op_divs - process a DIVS opcode //------------------------------------------------- void drcbe_x86::op_divs(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter edstp(*this, inst.param(1), PTYPE_MR); be_parameter src1p(*this, inst.param(2), PTYPE_MRI); be_parameter src2p(*this, inst.param(3), PTYPE_MRI); bool compute_rem = (dstp != edstp); // 32-bit form if (inst.size() == 4) { // general case emit_mov_r32_p32(dst, REG_ECX, src2p); // mov ecx,src2p if (inst.flags() != 0) { emit_mov_r32_imm(dst, REG_EAX, 0xa0000000); // mov eax,0xa0000000 emit_add_r32_r32(dst, REG_EAX, REG_EAX); // add eax,eax } emit_link skip; emit_jecxz_link(dst, skip); // jecxz skip emit_mov_r32_p32(dst, REG_EAX, src1p); // mov eax,src1p emit_cdq(dst); // cdq emit_idiv_r32(dst, REG_ECX); // idiv ecx emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax if (compute_rem) emit_mov_p32_r32(dst, edstp, REG_EDX); // mov edstp,edx if (inst.flags() != 0) emit_test_r32_r32(dst, REG_EAX, REG_EAX); // test eax,eax track_resolve_link(dst, skip); // skip: } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p); // mov [esp+16],src2p emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p); // mov [esp+8],src1p if (!compute_rem) emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo); // mov [esp+4],&reslo else emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi); // push [esp+4],&reshi emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo); // mov [esp],&reslo emit_call(dst, (x86code *)ddivs); // call ddivs if (inst.flags() != 0) emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4)); // push flags_unmap[eax*4] emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0)); // mov eax,reslo.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1)); // mov edx,reslo.hi emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax if (compute_rem) { emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0)); // mov eax,reshi.lo emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reshi + 1)); // mov edx,reshi.hi emit_mov_p64_r64(dst, edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax } if (inst.flags() != 0) emit_popf(dst); // popf } } //------------------------------------------------- // op_and - process a AND opcode //------------------------------------------------- void drcbe_x86::op_and(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); normalize_commutative(src1p, src2p); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_and_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // and [dstp],src2p // AND with immediate 0xff else if (src2p.is_immediate_value(0xff) && inst.flags() == 0) { if (src1p.is_int_register()) emit_movzx_r32_r8(dst, dstreg, src1p.ireg()); // movzx dstreg,src1p else if (src1p.is_memory()) emit_movzx_r32_m8(dst, dstreg, MABS(src1p.memory())); // movzx dstreg,[src1p] emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } // AND with immediate 0xffff else if (src2p.is_immediate_value(0xffff) && inst.flags() == 0) { if (src1p.is_int_register()) emit_movzx_r32_r16(dst, dstreg, src1p.ireg()); // movzx dstreg,src1p else if (src1p.is_memory()) emit_movzx_r32_m16(dst, dstreg, MABS(src1p.memory())); // movzx dstreg,[src1p] emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_and_r32_p32(dst, dstreg, src2p, inst); // and dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_and_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // and [dstp],src2p // AND with immediate 0xff else if (src2p.is_immediate_value(0xff) && inst.flags() == 0) { if (src1p.is_int_register()) emit_movzx_r32_r8(dst, dstreg, src1p.ireg()); // movzx dstreg,src1p else if (src1p.is_memory()) emit_movzx_r32_m8(dst, dstreg, MABS(src1p.memory())); // movzx dstreg,[src1p] emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov dsthi,0 else if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov dsthi,0 } // AND with immediate 0xffff else if (src2p.is_immediate_value(0xffff) && inst.flags() == 0) { if (src1p.is_int_register()) emit_movzx_r32_r16(dst, dstreg, src1p.ireg()); // movzx dstreg,src1p else if (src1p.is_memory()) emit_movzx_r32_m16(dst, dstreg, MABS(src1p.memory())); // movzx dstreg,[src1p] emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov dsthi,0 else if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov dsthi,0 } // AND with immediate 0xffffffff else if (src2p.is_immediate_value(0xffffffff) && inst.flags() == 0) { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov dsthi,0 else if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov dsthi,0 } // AND with immediate 0xffffffff00000000 else if (src2p.is_immediate_value(U64(0xffffffff00000000)) && inst.flags() == 0) { if (src1p != dstp) { emit_mov_r64_p64(dst, REG_NONE, REG_EDX, src1p); // mov dstreg,src1p emit_mov_p64_r64(dst, dstp, REG_NONE, REG_EDX); // mov dstp,dstreg } if (dstp.is_int_register()) emit_xor_r32_r32(dst, dstp.ireg(), dstp.ireg()); // xor dstlo,dstlo else if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory()), 0); // mov dstlo,0 } // AND with immediate <= 0xffffffff else if (src2p.is_immediate() && src2p.immediate() <= 0xffffffff && inst.flags() == 0) { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_and_r32_p32(dst, dstreg, src2p, inst); // and dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg if (dstp.is_int_register()) emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0); // mov dsthi,0 else if (dstp.is_memory()) emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0); // mov dsthi,0 } // general case else { emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_and_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // and dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_test - process a TEST opcode //------------------------------------------------- void drcbe_x86::op_test(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_Z | FLAG_S); // normalize parameters be_parameter src1p(*this, inst.param(0), PTYPE_MRI); be_parameter src2p(*this, inst.param(1), PTYPE_MRI); normalize_commutative(src1p, src2p); // pick a target register for the general case int src1reg = src1p.select_register(REG_EAX); // 32-bit form if (inst.size() == 4) { // src1p in memory if (src1p.is_memory()) emit_test_m32_p32(dst, MABS(src1p.memory()), src2p, inst); // test [src1p],src2p // general case else { emit_mov_r32_p32(dst, src1reg, src1p); // mov src1reg,src1p emit_test_r32_p32(dst, src1reg, src2p, inst); // test src1reg,src2p } } // 64-bit form else if (inst.size() == 8) { // src1p in memory if (src1p.is_memory()) emit_test_m64_p64(dst, MABS(src1p.memory()), src2p, inst); // test [dstp],src2p // general case else { emit_mov_r64_p64(dst, src1reg, REG_EDX, src1p); // mov src1reg:dstp,[src1p] emit_test_r64_p64(dst, src1reg, REG_EDX, src2p, inst); // test src1reg:dstp,src2p } } } //------------------------------------------------- // op_or - process a OR opcode //------------------------------------------------- void drcbe_x86::op_or(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); normalize_commutative(src1p, src2p); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_or_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // or [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_or_r32_p32(dst, dstreg, src2p, inst); // or dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_or_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // or [dstp],src2p // general case else { emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_or_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // or dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_xor - process a XOR opcode //------------------------------------------------- void drcbe_x86::op_xor(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); normalize_commutative(src1p, src2p); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_xor_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // xor [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_xor_r32_p32(dst, dstreg, src2p, inst); // xor dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_xor_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // xor [dstp],src2p // general case else { emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_xor_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // xor dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } } //------------------------------------------------- // op_lzcnt - process a LZCNT opcode //------------------------------------------------- void drcbe_x86::op_lzcnt(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // 32-bit form if (inst.size() == 4) { emit_mov_r32_p32(dst, dstreg, srcp); // mov dstreg,src1p emit_mov_r32_imm(dst, REG_ECX, 32 ^ 31); // mov ecx,32 ^ 31 emit_bsr_r32_r32(dst, dstreg, dstreg); // bsr dstreg,dstreg emit_cmovcc_r32_r32(dst, x86emit::COND_Z, dstreg, REG_ECX); // cmovz dstreg,ecx emit_xor_r32_imm(dst, dstreg, 31); // xor dstreg,31 emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } // 64-bit form else if (inst.size() == 8) { emit_mov_r64_p64(dst, REG_EDX, dstreg, srcp); // mov dstreg:edx,srcp emit_bsr_r32_r32(dst, dstreg, dstreg); // bsr dstreg,dstreg emit_link skip; emit_jcc_short_link(dst, x86emit::COND_NZ, skip); // jnz skip emit_mov_r32_imm(dst, REG_ECX, 32 ^ 31); // mov ecx,32 ^ 31 emit_bsr_r32_r32(dst, dstreg, REG_EDX); // bsr dstreg,edx emit_cmovcc_r32_r32(dst, x86emit::COND_Z, dstreg, REG_ECX); // cmovz dstreg,ecx emit_add_r32_imm(dst, REG_ECX, 32); // add ecx,32 track_resolve_link(dst, skip); // skip: emit_xor_r32_r32(dst, REG_EDX, REG_EDX); // xor edx,edx emit_xor_r32_imm(dst, dstreg, 31); // xor dstreg,31 emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,edx:dstreg } } //------------------------------------------------- // op_bswap - process a BSWAP opcode //------------------------------------------------- void drcbe_x86::op_bswap(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX); // 32-bit form if (inst.size() == 4) { emit_mov_r32_p32(dst, dstreg, srcp); // mov dstreg,src1p emit_bswap_r32(dst, dstreg); // bswap dstreg if (inst.flags() != 0) emit_test_r32_r32(dst, dstreg, dstreg); // test dstreg,dstreg emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } // 64-bit form else if (inst.size() == 8) { emit_mov_r64_p64(dst, REG_EDX, dstreg, srcp); // mov dstreg:edx,srcp emit_bswap_r32(dst, dstreg); // bswap dstreg emit_bswap_r32(dst, REG_EDX); // bswap edx emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,edx:dstreg if (inst.flags() == FLAG_Z) emit_or_r32_r32(dst, REG_EDX, dstreg); // or edx,eax else if (inst.flags() == FLAG_S) emit_test_r32_r32(dst, REG_EDX, REG_EDX); // test edx,edx else { emit_movzx_r32_r16(dst, REG_ECX, dstreg); // movzx ecx,dstreg emit_or_r32_r32(dst, REG_EDX, REG_ECX); // or edx,ecx emit_mov_r32_r32(dst, REG_ECX, dstreg); // mov ecx,dstreg emit_shr_r32_imm(dst, REG_ECX, 16); // shr ecx,16 emit_or_r32_r32(dst, REG_EDX, REG_ECX); // or edx,ecx } } } //------------------------------------------------- // op_shl - process a SHL opcode //------------------------------------------------- void drcbe_x86::op_shl(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_shl_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // shl [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_shl_r32_p32(dst, dstreg, src2p, inst); // shl dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_shl_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // shl dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //------------------------------------------------- // op_shr - process a SHR opcode //------------------------------------------------- void drcbe_x86::op_shr(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_shr_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // shr [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_shr_r32_p32(dst, dstreg, src2p, inst); // shr dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_shr_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // shr dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //------------------------------------------------- // op_sar - process a SAR opcode //------------------------------------------------- void drcbe_x86::op_sar(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_sar_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // sar [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_sar_r32_p32(dst, dstreg, src2p, inst); // sar dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_sar_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // sar dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //------------------------------------------------- // op_rol - process a rol opcode //------------------------------------------------- void drcbe_x86::op_rol(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_rol_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // rol [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_rol_r32_p32(dst, dstreg, src2p, inst); // rol dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_rol_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // rol dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //------------------------------------------------- // op_ror - process a ROR opcode //------------------------------------------------- void drcbe_x86::op_ror(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_ror_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // ror [dstp],src2p // general case else { emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p emit_ror_r32_p32(dst, dstreg, src2p, inst); // ror dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_ror_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // ror dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //------------------------------------------------- // op_rolc - process a ROLC opcode //------------------------------------------------- void drcbe_x86::op_rolc(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_rcl_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // rcl [dstp],src2p // general case else { emit_mov_r32_p32_keepflags(dst, dstreg, src1p); // mov dstreg,src1p emit_rcl_r32_p32(dst, dstreg, src2p, inst); // rcl dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_rcl_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // rcl dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //------------------------------------------------- // op_rorc - process a RORC opcode //------------------------------------------------- void drcbe_x86::op_rorc(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); // pick a target register for the general case int dstreg = dstp.select_register(REG_EAX, src2p); // 32-bit form if (inst.size() == 4) { // dstp == src1p in memory if (dstp.is_memory() && dstp == src1p) emit_rcr_m32_p32(dst, MABS(dstp.memory()), src2p, inst); // rcr [dstp],src2p // general case else { emit_mov_r32_p32_keepflags(dst, dstreg, src1p); // mov dstreg,src1p emit_rcr_r32_p32(dst, dstreg, src2p, inst); // rcr dstreg,src2p emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg } } // 64-bit form else if (inst.size() == 8) { // general case emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p); // mov dstreg:dstp,[src1p] emit_rcr_r64_p64(dst, dstreg, REG_EDX, src2p, inst); // rcr dstreg:dstp,src2p emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax } } //************************************************************************** // FLOATING POINT OPERATIONS //************************************************************************** //------------------------------------------------- // op_fload - process a FLOAD opcode //------------------------------------------------- void drcbe_x86::op_fload(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter basep(*this, inst.param(1), PTYPE_M); be_parameter indp(*this, inst.param(2), PTYPE_MRI); // immediate index if (indp.is_immediate()) { emit_mov_r32_m32(dst, REG_EAX, MABS(basep.memory(4*indp.immediate()))); // mov eax,[basep + 4*indp] if (inst.size() == 8) emit_mov_r32_m32(dst, REG_EDX, MABS(basep.memory(4 + 4*indp.immediate()))); // mov edx,[basep + 4*indp + 4] } // other index else { int indreg = indp.select_register(REG_ECX); emit_mov_r32_p32(dst, indreg, indp); emit_mov_r32_m32(dst, REG_EAX, MABSI(basep.memory(), indreg, 4)); // mov eax,[basep + 4*indp] if (inst.size() == 8) emit_mov_r32_m32(dst, REG_EDX, MABSI(basep.memory(4), indreg, 4)); // mov edx,[basep + 4*indp + 4] } // general case emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // mov [dstp],eax if (inst.size() == 8) emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // mov [dstp + 4],edx } //------------------------------------------------- // op_fstore - process a FSTORE opcode //------------------------------------------------- void drcbe_x86::op_fstore(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter basep(*this, inst.param(0), PTYPE_M); be_parameter indp(*this, inst.param(1), PTYPE_MRI); be_parameter srcp(*this, inst.param(2), PTYPE_MF); // general case emit_mov_r32_m32(dst, REG_EAX, MABS(srcp.memory())); // mov eax,[srcp] if (inst.size() == 8) emit_mov_r32_m32(dst, REG_EDX, MABS(srcp.memory(4))); // mov edx,[srcp + 4] // immediate index if (indp.is_immediate()) { emit_mov_m32_r32(dst, MABS(basep.memory(4*indp.immediate())), REG_EAX); // mov [basep + 4*indp],eax if (inst.size() == 8) emit_mov_m32_r32(dst, MABS(basep.memory(4 + 4*indp.immediate())), REG_EDX); // mov [basep + 4*indp + 4],edx } // other index else { int indreg = indp.select_register(REG_ECX); emit_mov_r32_p32(dst, indreg, indp); emit_mov_m32_r32(dst, MABSI(basep.memory(), indreg, 4), REG_EAX); // mov [basep + 4*indp],eax if (inst.size() == 8) emit_mov_m32_r32(dst, MABSI(basep.memory(4), indreg, 4), REG_EDX); // mov [basep + 4*indp + 4],edx } } //------------------------------------------------- // op_fread - process a FREAD opcode //------------------------------------------------- void drcbe_x86::op_fread(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter addrp(*this, inst.param(1), PTYPE_MRI); const parameter &spacep = inst.param(2); assert(spacep.is_size_space()); assert((1 << spacep.size()) == inst.size()); // set up a call to the read dword/qword handler emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp); // mov [esp+4],addrp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacep.space()]); // mov [esp],space if (inst.size() == 4) emit_call(dst, (x86code *)m_accessors[spacep.space()].read_dword); // call read_dword else if (inst.size() == 8) emit_call(dst, (x86code *)m_accessors[spacep.space()].read_qword); // call read_qword // store result if (inst.size() == 4) emit_mov_p32_r32(dst, dstp, REG_EAX); // mov dstp,eax else if (inst.size() == 8) emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax } //------------------------------------------------- // op_fwrite - process a FWRITE opcode //------------------------------------------------- void drcbe_x86::op_fwrite(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter addrp(*this, inst.param(0), PTYPE_MRI); be_parameter srcp(*this, inst.param(1), PTYPE_MF); const parameter &spacep = inst.param(2); assert(spacep.is_size_space()); assert((1 << spacep.size()) == inst.size()); // set up a call to the write dword/qword handler if (inst.size() == 4) emit_mov_m32_p32(dst, MBD(REG_ESP, 8), srcp); // mov [esp+8],srcp else if (inst.size() == 8) emit_mov_m64_p64(dst, MBD(REG_ESP, 8), srcp); // mov [esp+8],srcp emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp); // mov [esp+4],addrp emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacep.space()]); // mov [esp],space if (inst.size() == 4) emit_call(dst, (x86code *)m_accessors[spacep.space()].write_dword); // call write_dword else if (inst.size() == 8) emit_call(dst, (x86code *)m_accessors[spacep.space()].write_qword); // call write_qword } //------------------------------------------------- // op_fmov - process a FMOV opcode //------------------------------------------------- void drcbe_x86::op_fmov(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_any_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // always start with a jmp emit_link skip = { 0 }; if (inst.condition() != uml::COND_ALWAYS) emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip); // jcc skip // general case emit_mov_r32_m32(dst, REG_EAX, MABS(srcp.memory())); // mov eax,[srcp] if (inst.size() == 8) emit_mov_r32_m32(dst, REG_EDX, MABS(srcp.memory(4))); // mov edx,[srcp + 4] emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // mov [dstp],eax if (inst.size() == 8) emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX); // mov [dstp + 4],edx // resolve the jump if (skip.target != NULL) track_resolve_link(dst, skip); // skip: } //------------------------------------------------- // op_ftoint - process a FTOINT opcode //------------------------------------------------- void drcbe_x86::op_ftoint(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MR); be_parameter srcp(*this, inst.param(1), PTYPE_MF); const parameter &sizep = inst.param(2); assert(sizep.is_size()); const parameter &roundp = inst.param(3); assert(roundp.is_rounding()); // set rounding mode if necessary if (roundp.rounding() != ROUND_DEFAULT && (!m_sse3 || roundp.rounding() != ROUND_TRUNC)) { emit_fstcw_m16(dst, MABS(&m_fmodesave)); // fstcw [fmodesave] emit_fldcw_m16(dst, MABS(&fp_control[roundp.rounding()])); // fldcw fpcontrol[roundp] } // general case emit_fld_p(dst, inst.size(), srcp); // fld srcp // 4-byte integer case if (sizep.size() == SIZE_DWORD) { if (dstp.is_memory()) { if (!m_sse3 || roundp.rounding() != ROUND_TRUNC) emit_fistp_m32(dst, MABS(dstp.memory())); // fistp [dstp] else emit_fisttp_m32(dst, MABS(dstp.memory())); // fisttp [dstp] } else if (dstp.is_int_register()) { if (!m_sse3 || roundp.rounding() != ROUND_TRUNC) emit_fistp_m32(dst, MABS(m_reglo[dstp.ireg()])); // fistp reglo[dstp] else emit_fisttp_m32(dst, MABS(m_reglo[dstp.ireg()])); // fisttp reglo[dstp] emit_mov_r32_m32(dst, dstp.ireg(), MABS(m_reglo[dstp.ireg()])); // mov dstp,reglo[dstp] } } // 8-byte integer case else if (sizep.size() == SIZE_QWORD) { if (dstp.is_memory()) { if (!m_sse3 || roundp.rounding() != ROUND_TRUNC) emit_fistp_m64(dst, MABS(dstp.memory())); // fistp [dstp] else emit_fisttp_m64(dst, MABS(dstp.memory())); // fisttp [dstp] } else if (dstp.is_int_register()) { if (!m_sse3 || roundp.rounding() != ROUND_TRUNC) emit_fistp_m64(dst, MABS(m_reglo[dstp.ireg()])); // fistp reglo[dstp] else emit_fisttp_m64(dst, MABS(m_reglo[dstp.ireg()])); // fisttp reglo[dstp] emit_mov_r32_m32(dst, dstp.ireg(), MABS(m_reglo[dstp.ireg()])); // mov dstp,reglo[dstp] } } // restore control word and proceed if (roundp.rounding() != ROUND_DEFAULT && (!m_sse3 || roundp.rounding() != ROUND_TRUNC)) emit_fldcw_m16(dst, MABS(&m_fmodesave)); // fldcw [fmodesave] } //------------------------------------------------- // op_ffrint - process a FFRINT opcode //------------------------------------------------- void drcbe_x86::op_ffrint(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MRI); const parameter &sizep = inst.param(2); assert(sizep.is_size()); // 4-byte integer case if (sizep.size() == SIZE_DWORD) { if (srcp.is_immediate()) { emit_mov_m32_imm(dst, MABS(&m_fptemp), srcp.immediate()); // mov [fptemp],srcp emit_fild_m32(dst, MABS(&m_fptemp)); // fild [fptemp] } else if (srcp.is_memory()) emit_fild_m32(dst, MABS(srcp.memory())); // fild [srcp] else if (srcp.is_int_register()) { emit_mov_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), srcp.ireg()); // mov reglo[srcp],srcp emit_fild_m32(dst, MABS(m_reglo[srcp.ireg()])); // fild reglo[srcp] } } // 8-bit integer case else if (sizep.size() == SIZE_QWORD) { if (srcp.is_immediate()) { emit_mov_m32_imm(dst, MABS(&m_fptemp), srcp.immediate()); // mov [fptemp],srcp emit_mov_m32_imm(dst, MABS((UINT8 *)&m_fptemp + 4), srcp.immediate()); // mov [fptemp+4],srcp emit_fild_m64(dst, MABS(&m_fptemp)); // fild [fptemp] } else if (srcp.is_memory()) emit_fild_m64(dst, MABS(srcp.memory())); // fild [srcp] else if (srcp.is_int_register()) { emit_mov_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), srcp.ireg()); // mov reglo[srcp],srcp emit_fild_m64(dst, MABS(m_reglo[srcp.ireg()])); // fild reglo[srcp] } } // store the result and be done emit_fstp_p(dst, inst.size(), dstp); // fstp [dstp] } //------------------------------------------------- // op_ffrflt - process a FFRFLT opcode //------------------------------------------------- void drcbe_x86::op_ffrflt(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); const parameter &sizep = inst.param(2); assert(sizep.is_size()); // general case if (sizep.size() == SIZE_DWORD) emit_fld_m32(dst, MABS(srcp.memory())); // fld [srcp] else if (sizep.size() == SIZE_QWORD) emit_fld_m64(dst, MABS(srcp.memory())); // fld [srcp] emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_frnds - process a FRNDS opcode //------------------------------------------------- void drcbe_x86::op_frnds(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), srcp); // fld srcp emit_fstp_m32(dst, MABS(&m_fptemp)); // fstp [fptemp] emit_fld_m32(dst, MABS(&m_fptemp)); // fld [fptemp] emit_fstp_p(dst, inst.size(), dstp); // fstp [dstp] } //------------------------------------------------- // op_fadd - process a FADD opcode //------------------------------------------------- void drcbe_x86::op_fadd(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter src1p(*this, inst.param(1), PTYPE_MF); be_parameter src2p(*this, inst.param(2), PTYPE_MF); normalize_commutative(src1p, src2p); // general case emit_fld_p(dst, inst.size(), src1p); // fld src1p emit_fld_p(dst, inst.size(), src2p); // fld src2p emit_faddp(dst); // faddp emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_fsub - process a FSUB opcode //------------------------------------------------- void drcbe_x86::op_fsub(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter src1p(*this, inst.param(1), PTYPE_MF); be_parameter src2p(*this, inst.param(2), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), src1p); // fld src1p emit_fld_p(dst, inst.size(), src2p); // fld src2p emit_fsubp(dst); // fsubp emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_fcmp - process a FCMP opcode //------------------------------------------------- void drcbe_x86::op_fcmp(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_flags(inst, FLAG_C | FLAG_Z | FLAG_U); // normalize parameters be_parameter src1p(*this, inst.param(0), PTYPE_MF); be_parameter src2p(*this, inst.param(1), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), src2p); // fld src2p emit_fld_p(dst, inst.size(), src1p); // fld src1p emit_fcompp(dst); // fcompp emit_fstsw_ax(dst); // fnstsw ax emit_sahf(dst); // sahf } //------------------------------------------------- // op_fmul - process a FMUL opcode //------------------------------------------------- void drcbe_x86::op_fmul(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter src1p(*this, inst.param(1), PTYPE_MF); be_parameter src2p(*this, inst.param(2), PTYPE_MF); normalize_commutative(src1p, src2p); // general case emit_fld_p(dst, inst.size(), src1p); // fld src1p emit_fld_p(dst, inst.size(), src2p); // fld src2p emit_fmulp(dst); // fmulp emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_fdiv - process a FDIV opcode //------------------------------------------------- void drcbe_x86::op_fdiv(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter src1p(*this, inst.param(1), PTYPE_MF); be_parameter src2p(*this, inst.param(2), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), src1p); // fld src1p emit_fld_p(dst, inst.size(), src2p); // fld src2p emit_fdivp(dst); // fdivp emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_fneg - process a FNEG opcode //------------------------------------------------- void drcbe_x86::op_fneg(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), srcp); // fld srcp emit_fchs(dst); // fchs emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_fabs - process a FABS opcode //------------------------------------------------- void drcbe_x86::op_fabs(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), srcp); // fld srcp emit_fabs(dst); // fabs emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_fsqrt - process a FSQRT opcode //------------------------------------------------- void drcbe_x86::op_fsqrt(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // general case emit_fld_p(dst, inst.size(), srcp); // fld srcp emit_fsqrt(dst); // fsqrt emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_frecip - process a FRECIP opcode //------------------------------------------------- void drcbe_x86::op_frecip(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // general case emit_fld1(dst); // fld1 emit_fld_p(dst, inst.size(), srcp); // fld srcp emit_fdivp(dst); // fdivp emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //------------------------------------------------- // op_frsqrt - process a FRSQRT opcode //------------------------------------------------- void drcbe_x86::op_frsqrt(x86code *&dst, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); assert_no_condition(inst); assert_no_flags(inst); // normalize parameters be_parameter dstp(*this, inst.param(0), PTYPE_MF); be_parameter srcp(*this, inst.param(1), PTYPE_MF); // general case emit_fld1(dst); // fld1 emit_fld_p(dst, inst.size(), srcp); // fld srcp emit_fsqrt(dst); // fsqrt emit_fdivp(dst); // fdivp emit_fstp_p(dst, inst.size(), dstp); // fstp dstp } //************************************************************************** // MISCELLAENOUS FUNCTIONS //************************************************************************** //------------------------------------------------- // dmulu - perform a double-wide unsigned multiply //------------------------------------------------- int drcbe_x86::dmulu(UINT64 &dstlo, UINT64 &dsthi, UINT64 src1, UINT64 src2, int flags) { // shortcut if we don't care about the high bits or the flags if (&dstlo == &dsthi && flags == 0) { dstlo = src1 * src2; return 0; } // fetch source values UINT64 a = src1; UINT64 b = src2; if (a == 0 || b == 0) { dsthi = dstlo = 0; return FLAG_Z; } // compute high and low parts first UINT64 lo = (UINT64)(UINT32)(a >> 0) * (UINT64)(UINT32)(b >> 0); UINT64 hi = (UINT64)(UINT32)(a >> 32) * (UINT64)(UINT32)(b >> 32); // compute middle parts UINT64 prevlo = lo; UINT64 temp = (UINT64)(UINT32)(a >> 32) * (UINT64)(UINT32)(b >> 0); lo += temp << 32; hi += (temp >> 32) + (lo < prevlo); prevlo = lo; temp = (UINT64)(UINT32)(a >> 0) * (UINT64)(UINT32)(b >> 32); lo += temp << 32; hi += (temp >> 32) + (lo < prevlo); // store the results dsthi = hi; dstlo = lo; return ((hi >> 60) & FLAG_S) | ((dsthi != 0) << 1); } //------------------------------------------------- // dmuls - perform a double-wide signed multiply //------------------------------------------------- int drcbe_x86::dmuls(UINT64 &dstlo, UINT64 &dsthi, INT64 src1, INT64 src2, int flags) { UINT64 lo, hi, prevlo; UINT64 a, b, temp; // shortcut if we don't care about the high bits or the flags if (&dstlo == &dsthi && flags == 0) { dstlo = src1 * src2; return 0; } // fetch absolute source values a = src1; if ((INT64)a < 0) a = -a; b = src2; if ((INT64)b < 0) b = -b; if (a == 0 || b == 0) { dsthi = dstlo = 0; return FLAG_Z; } // compute high and low parts first lo = (UINT64)(UINT32)(a >> 0) * (UINT64)(UINT32)(b >> 0); hi = (UINT64)(UINT32)(a >> 32) * (UINT64)(UINT32)(b >> 32); // compute middle parts prevlo = lo; temp = (UINT64)(UINT32)(a >> 32) * (UINT64)(UINT32)(b >> 0); lo += temp << 32; hi += (temp >> 32) + (lo < prevlo); prevlo = lo; temp = (UINT64)(UINT32)(a >> 0) * (UINT64)(UINT32)(b >> 32); lo += temp << 32; hi += (temp >> 32) + (lo < prevlo); // adjust for signage if ((INT64)(src1 ^ src2) < 0) { hi = ~hi + (lo == 0); lo = ~lo + 1; } // store the results dsthi = hi; dstlo = lo; return ((hi >> 60) & FLAG_S) | ((dsthi != ((INT64)lo >> 63)) << 1); } //------------------------------------------------- // ddivu - perform a double-wide unsigned divide //------------------------------------------------- int drcbe_x86::ddivu(UINT64 &dstlo, UINT64 &dsthi, UINT64 src1, UINT64 src2) { // do nothing if src2 == 0 if (src2 == 0) return FLAG_V; // shortcut if no remainder dstlo = src1 / src2; if (dstlo != dsthi) dsthi = src1 % src2; return ((dstlo == 0) << 2) | ((dstlo >> 60) & FLAG_S); } //------------------------------------------------- // ddivs - perform a double-wide signed divide //------------------------------------------------- int drcbe_x86::ddivs(UINT64 &dstlo, UINT64 &dsthi, INT64 src1, INT64 src2) { // do nothing if src2 == 0 if (src2 == 0) return FLAG_V; // shortcut if no remainder dstlo = src1 / src2; if (dstlo != dsthi) dsthi = src1 % src2; return ((dstlo == 0) << 2) | ((dstlo >> 60) & FLAG_S); }