/***************************************************************************
drcbex64.c
64-bit x64 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:
* Add support for FP registers
* Optimize to avoid unnecessary reloads
* Identify common pairs and optimize output
* Convert SUB a,0,b to NEG
* Optimize, e.g., and [r5],i0,$FF to use rbx as temporary register
(avoid initial move) if i0 is not needed going forward
****************************************************************************
-------------------------
ABI/conventions (Windows)
-------------------------
Registers:
RAX - volatile, function return value
RBX - non-volatile
RCX - volatile, integer function parameter 1
RDX - volatile, integer function parameter 2
RSI - non-volatile
RDI - non-volatile
RBP - non-volatile
R8 - volatile, integer function parameter 3
R9 - volatile, integer function parameter 4
R10 - volatile
R11 - volatile, scratch immediate storage
R12 - non-volatile
R13 - non-volatile
R14 - non-volatile
R15 - non-volatile
XMM0 - volatile, FP function parameter 1
XMM1 - volatile, FP function parameter 2
XMM2 - volatile, FP function parameter 3
XMM3 - volatile, FP function parameter 4
XMM4 - volatile
XMM5 - volatile
XMM6 - non-volatile
XMM7 - non-volatile
XMM8 - non-volatile
XMM9 - non-volatile
XMM10 - non-volatile
XMM11 - non-volatile
XMM12 - non-volatile
XMM13 - non-volatile
XMM14 - non-volatile
XMM15 - non-volatile
-----------------------------
ABI/conventions (Linux/MacOS)
-----------------------------
Registers:
RAX - volatile, function return value
RBX - non-volatile
RCX - volatile, integer function parameter 4
RDX - volatile, integer function parameter 3
RSI - volatile, integer function parameter 2
RDI - volatile, integer function parameter 1
RBP - non-volatile
R8 - volatile, integer function parameter 5
R9 - volatile, integer function parameter 6
R10 - volatile
R11 - volatile, scratch immediate storage
R12 - non-volatile
R13 - non-volatile
R14 - non-volatile
R15 - non-volatile
XMM0 - volatile, FP function parameter 1
XMM1 - volatile, FP function parameter 2
XMM2 - volatile, FP function parameter 3
XMM3 - volatile, FP function parameter 4
XMM4 - volatile
XMM5 - volatile
XMM6 - volatile
XMM7 - volatile
XMM8 - volatile
XMM9 - volatile
XMM10 - volatile
XMM11 - volatile
XMM12 - volatile
XMM13 - volatile
XMM14 - volatile
XMM15 - volatile
---------------
Execution model
---------------
Registers (Windows):
RAX - scratch register
RBX - maps to I0
RCX - scratch register
RDX - scratch register
RSI - maps to I1
RDI - maps to I2
RBP - pointer to code cache
R8 - scratch register
R9 - scratch register
R10 - scratch register
R11 - scratch register
R12 - maps to I3
R13 - maps to I4
R14 - maps to I5
R15 - maps to I6
Registers (Linux/MacOS):
RAX - scratch register
RBX - maps to I0
RCX - scratch register
RDX - scratch register
RSI - unused
RDI - unused
RBP - pointer to code cache
R8 - scratch register
R9 - scratch register
R10 - scratch register
R11 - scratch register
R12 - maps to I1
R13 - maps to I2
R14 - maps to I3
R15 - maps to I4
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 RAX.
Entry stack:
[rsp] - return
Runtime stack:
[rsp] - r9 home
[rsp+8] - r8 home
[rsp+16] - rdx home
[rsp+24] - rcx home
[rsp+40] - saved r15
[rsp+48] - saved r14
[rsp+56] - saved r13
[rsp+64] - saved r12
[rsp+72] - saved ebp
[rsp+80] - saved edi
[rsp+88] - saved esi
[rsp+96] - saved ebx
[rsp+104] - ret
***************************************************************************/
#include "emu.h"
#include "debugger.h"
#include "drcuml.h"
#include "drcbex64.h"
using namespace uml;
using namespace x64emit;
//**************************************************************************
// DEBUGGING
//**************************************************************************
#define LOG_HASHJMPS (0)
#define USE_RCPSS_FOR_SINGLES (0)
#define USE_RSQRTSS_FOR_SINGLES (0)
#define USE_RCPSS_FOR_DOUBLES (0)
#define USE_RSQRTSS_FOR_DOUBLES (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;
#ifdef X64_WINDOWS_ABI
const int REG_PARAM1 = REG_RCX;
const int REG_PARAM2 = REG_RDX;
const int REG_PARAM3 = REG_R8;
const int REG_PARAM4 = REG_R9;
#else
const int REG_PARAM1 = REG_RDI;
const int REG_PARAM2 = REG_RSI;
const int REG_PARAM3 = REG_RDX;
const int REG_PARAM4 = REG_RCX;
#endif
//**************************************************************************
// MACROS
//**************************************************************************
#define X86_CONDITION(condition) (condition_map[condition - uml::COND_Z])
#define X86_NOT_CONDITION(condition) (condition_map[condition - uml::COND_Z] ^ 1)
inline x86_memref drcbe_x64::MABS(const void *ptr)
{
return MBD(REG_BP, offset_from_rbp(ptr));
}
#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_x64::opcode_generate_func drcbe_x64::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] =
{
#ifdef X64_WINDOWS_ABI
REG_RBX, REG_RSI, REG_RDI, REG_R12, REG_R13, REG_R14, REG_R15
#else
REG_RBX, REG_R12, REG_R13, REG_R14, REG_R15
#endif
};
static UINT8 float_register_map[REG_F_COUNT] =
{
0
};
// condition mapping table
static const UINT8 condition_map[uml::COND_MAX - uml::COND_Z] =
{
x64emit::COND_Z, // COND_Z = 0x80, requires Z
x64emit::COND_NZ, // COND_NZ, requires Z
x64emit::COND_S, // COND_S, requires S
x64emit::COND_NS, // COND_NS, requires S
x64emit::COND_C, // COND_C, requires C
x64emit::COND_NC, // COND_NC, requires C
x64emit::COND_O, // COND_V, requires V
x64emit::COND_NO, // COND_NV, requires V
x64emit::COND_P, // COND_U, requires U
x64emit::COND_NP, // COND_NU, requires U
x64emit::COND_A, // COND_A, requires CZ
x64emit::COND_BE, // COND_BE, requires CZ
x64emit::COND_G, // COND_G, requires SVZ
x64emit::COND_LE, // COND_LE, requires SVZ
x64emit::COND_L, // COND_L, requires SV
x64emit::COND_GE, // COND_GE, requires SV
};
// rounding mode mapping table
static const UINT8 fprnd_map[4] =
{
FPRND_CHOP, // ROUND_TRUNC, truncate
FPRND_NEAR, // ROUND_ROUND, round
FPRND_UP, // ROUND_CEIL, round up
FPRND_DOWN // ROUND_FLOOR round down
};
//**************************************************************************
// TABLES
//**************************************************************************
const drcbe_x64::opcode_table_entry drcbe_x64::s_opcode_table_source[] =
{
// Compile-time opcodes
{ uml::OP_HANDLE, &drcbe_x64::op_handle }, // HANDLE handle
{ uml::OP_HASH, &drcbe_x64::op_hash }, // HASH mode,pc
{ uml::OP_LABEL, &drcbe_x64::op_label }, // LABEL imm
{ uml::OP_COMMENT, &drcbe_x64::op_comment }, // COMMENT string
{ uml::OP_MAPVAR, &drcbe_x64::op_mapvar }, // MAPVAR mapvar,value
// Control Flow Operations
{ uml::OP_NOP, &drcbe_x64::op_nop }, // NOP
{ uml::OP_DEBUG, &drcbe_x64::op_debug }, // DEBUG pc
{ uml::OP_EXIT, &drcbe_x64::op_exit }, // EXIT src1[,c]
{ uml::OP_HASHJMP, &drcbe_x64::op_hashjmp }, // HASHJMP mode,pc,handle
{ uml::OP_JMP, &drcbe_x64::op_jmp }, // JMP imm[,c]
{ uml::OP_EXH, &drcbe_x64::op_exh }, // EXH handle,param[,c]
{ uml::OP_CALLH, &drcbe_x64::op_callh }, // CALLH handle[,c]
{ uml::OP_RET, &drcbe_x64::op_ret }, // RET [c]
{ uml::OP_CALLC, &drcbe_x64::op_callc }, // CALLC func,ptr[,c]
{ uml::OP_RECOVER, &drcbe_x64::op_recover }, // RECOVER dst,mapvar
// Internal Register Operations
{ uml::OP_SETFMOD, &drcbe_x64::op_setfmod }, // SETFMOD src
{ uml::OP_GETFMOD, &drcbe_x64::op_getfmod }, // GETFMOD dst
{ uml::OP_GETEXP, &drcbe_x64::op_getexp }, // GETEXP dst
{ uml::OP_GETFLGS, &drcbe_x64::op_getflgs }, // GETFLGS dst[,f]
{ uml::OP_SAVE, &drcbe_x64::op_save }, // SAVE dst
{ uml::OP_RESTORE, &drcbe_x64::op_restore }, // RESTORE dst
// Integer Operations
{ uml::OP_LOAD, &drcbe_x64::op_load }, // LOAD dst,base,index,size
{ uml::OP_LOADS, &drcbe_x64::op_loads }, // LOADS dst,base,index,size
{ uml::OP_STORE, &drcbe_x64::op_store }, // STORE base,index,src,size
{ uml::OP_READ, &drcbe_x64::op_read }, // READ dst,src1,spacesize
{ uml::OP_READM, &drcbe_x64::op_readm }, // READM dst,src1,mask,spacesize
{ uml::OP_WRITE, &drcbe_x64::op_write }, // WRITE dst,src1,spacesize
{ uml::OP_WRITEM, &drcbe_x64::op_writem }, // WRITEM dst,src1,spacesize
{ uml::OP_CARRY, &drcbe_x64::op_carry }, // CARRY src,bitnum
{ uml::OP_SET, &drcbe_x64::op_set }, // SET dst,c
{ uml::OP_MOV, &drcbe_x64::op_mov }, // MOV dst,src[,c]
{ uml::OP_SEXT, &drcbe_x64::op_sext }, // SEXT dst,src
{ uml::OP_ROLAND, &drcbe_x64::op_roland }, // ROLAND dst,src1,src2,src3
{ uml::OP_ROLINS, &drcbe_x64::op_rolins }, // ROLINS dst,src1,src2,src3
{ uml::OP_ADD, &drcbe_x64::op_add }, // ADD dst,src1,src2[,f]
{ uml::OP_ADDC, &drcbe_x64::op_addc }, // ADDC dst,src1,src2[,f]
{ uml::OP_SUB, &drcbe_x64::op_sub }, // SUB dst,src1,src2[,f]
{ uml::OP_SUBB, &drcbe_x64::op_subc }, // SUBB dst,src1,src2[,f]
{ uml::OP_CMP, &drcbe_x64::op_cmp }, // CMP src1,src2[,f]
{ uml::OP_MULU, &drcbe_x64::op_mulu }, // MULU dst,edst,src1,src2[,f]
{ uml::OP_MULS, &drcbe_x64::op_muls }, // MULS dst,edst,src1,src2[,f]
{ uml::OP_DIVU, &drcbe_x64::op_divu }, // DIVU dst,edst,src1,src2[,f]
{ uml::OP_DIVS, &drcbe_x64::op_divs }, // DIVS dst,edst,src1,src2[,f]
{ uml::OP_AND, &drcbe_x64::op_and }, // AND dst,src1,src2[,f]
{ uml::OP_TEST, &drcbe_x64::op_test }, // TEST src1,src2[,f]
{ uml::OP_OR, &drcbe_x64::op_or }, // OR dst,src1,src2[,f]
{ uml::OP_XOR, &drcbe_x64::op_xor }, // XOR dst,src1,src2[,f]
{ uml::OP_LZCNT, &drcbe_x64::op_lzcnt }, // LZCNT dst,src[,f]
{ uml::OP_BSWAP, &drcbe_x64::op_bswap }, // BSWAP dst,src
{ uml::OP_SHL, &drcbe_x64::op_shl }, // SHL dst,src,count[,f]
{ uml::OP_SHR, &drcbe_x64::op_shr }, // SHR dst,src,count[,f]
{ uml::OP_SAR, &drcbe_x64::op_sar }, // SAR dst,src,count[,f]
{ uml::OP_ROL, &drcbe_x64::op_rol }, // ROL dst,src,count[,f]
{ uml::OP_ROLC, &drcbe_x64::op_rolc }, // ROLC dst,src,count[,f]
{ uml::OP_ROR, &drcbe_x64::op_ror }, // ROR dst,src,count[,f]
{ uml::OP_RORC, &drcbe_x64::op_rorc }, // RORC dst,src,count[,f]
// Floating Point Operations
{ uml::OP_FLOAD, &drcbe_x64::op_fload }, // FLOAD dst,base,index
{ uml::OP_FSTORE, &drcbe_x64::op_fstore }, // FSTORE base,index,src
{ uml::OP_FREAD, &drcbe_x64::op_fread }, // FREAD dst,space,src1
{ uml::OP_FWRITE, &drcbe_x64::op_fwrite }, // FWRITE space,dst,src1
{ uml::OP_FMOV, &drcbe_x64::op_fmov }, // FMOV dst,src1[,c]
{ uml::OP_FTOINT, &drcbe_x64::op_ftoint }, // FTOINT dst,src1,size,round
{ uml::OP_FFRINT, &drcbe_x64::op_ffrint }, // FFRINT dst,src1,size
{ uml::OP_FFRFLT, &drcbe_x64::op_ffrflt }, // FFRFLT dst,src1,size
{ uml::OP_FRNDS, &drcbe_x64::op_frnds }, // FRNDS dst,src1
{ uml::OP_FADD, &drcbe_x64::op_fadd }, // FADD dst,src1,src2
{ uml::OP_FSUB, &drcbe_x64::op_fsub }, // FSUB dst,src1,src2
{ uml::OP_FCMP, &drcbe_x64::op_fcmp }, // FCMP src1,src2
{ uml::OP_FMUL, &drcbe_x64::op_fmul }, // FMUL dst,src1,src2
{ uml::OP_FDIV, &drcbe_x64::op_fdiv }, // FDIV dst,src1,src2
{ uml::OP_FNEG, &drcbe_x64::op_fneg }, // FNEG dst,src1
{ uml::OP_FABS, &drcbe_x64::op_fabs }, // FABS dst,src1
{ uml::OP_FSQRT, &drcbe_x64::op_fsqrt }, // FSQRT dst,src1
{ uml::OP_FRECIP, &drcbe_x64::op_frecip }, // FRECIP dst,src1
{ uml::OP_FRSQRT, &drcbe_x64::op_frsqrt } // FRSQRT dst,src1
};
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
//-------------------------------------------------
// param_normalize - convert a full parameter
// into a reduced set
//-------------------------------------------------
drcbe_x64::be_parameter::be_parameter(drcbe_x64 &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);
regnum = float_register_map[param.freg() - REG_F0];
if (regnum != 0)
*this = make_freg(regnum);
else
*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_x64::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_x64::be_parameter::select_register(int defreg, const be_parameter &checkparam) const
{
if (*this == checkparam)
return defreg;
return select_register(defreg);
}
inline int drcbe_x64::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_x64::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;
}
}
//-------------------------------------------------
// offset_from_rbp - return the verified offset
// from rbp
//-------------------------------------------------
inline INT32 drcbe_x64::offset_from_rbp(const void *ptr)
{
INT64 delta = reinterpret_cast<UINT8 *>(const_cast<void *>(ptr)) - m_rbpvalue;
assert_always((INT32)delta == delta, "offset_from_rbp: delta out of range");
return (INT32)delta;
}
//-------------------------------------------------
// get_base_register_and_offset - determine right
// base register and offset to access the given
// target address
//-------------------------------------------------
inline int drcbe_x64::get_base_register_and_offset(x86code *&dst, void *target, UINT8 reg, INT32 &offset)
{
INT64 delta = (UINT8 *)target - m_rbpvalue;
if (short_immediate(delta))
{
offset = delta;
return REG_RBP;
}
else
{
offset = 0;
emit_mov_r64_imm(dst, reg, (FPTR)target); // mov reg,target
return reg;
}
}
//-------------------------------------------------
// emit_smart_call_r64 - generate a call either
// directly or via a call through pointer
//-------------------------------------------------
inline void drcbe_x64::emit_smart_call_r64(x86code *&dst, x86code *target, UINT8 reg)
{
INT64 delta = target - (dst + 5);
if (short_immediate(delta))
emit_call(dst, target); // call target
else
{
emit_mov_r64_imm(dst, reg, (FPTR)target); // mov reg,target
emit_call_r64(dst, reg); // call reg
}
}
//-------------------------------------------------
// emit_smart_call_m64 - generate a call either
// directly or via a call through pointer
//-------------------------------------------------
inline void drcbe_x64::emit_smart_call_m64(x86code *&dst, x86code **target)
{
INT64 delta = *target - (dst + 5);
if (short_immediate(delta))
emit_call(dst, *target); // call *target
else
emit_call_m64(dst, MABS(target)); // call [target]
}
//**************************************************************************
// BACKEND CALLBACKS
//**************************************************************************
//-------------------------------------------------
// drcbe_x64 - constructor
//-------------------------------------------------
drcbe_x64::drcbe_x64(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_sse41(false),
m_absmask32((UINT32 *)cache.alloc_near(16*2 + 15)),
m_absmask64(NULL),
m_rbpvalue(cache.near() + 0x80),
m_entry(NULL),
m_exit(NULL),
m_nocode(NULL),
m_fixup_label(FUNC(drcbe_x64::fixup_label), this),
m_fixup_exception(FUNC(drcbe_x64::fixup_exception), this),
m_near(*(near_state *)cache.alloc_near(sizeof(m_near)))
{
// build up necessary arrays
static const UINT32 sse_control[4] =
{
0xffc0, // ROUND_TRUNC
0x9fc0, // ROUND_ROUND
0xdfc0, // ROUND_CEIL
0xbfc0 // ROUND_FLOOR
};
memcpy(m_near.ssecontrol, sse_control, sizeof(m_near.ssecontrol));
m_near.single1 = 1.0f;
m_near.double1 = 1.0;
// create absolute value masks that are aligned to SSE boundaries
m_absmask32 = (UINT32 *)(((FPTR)m_absmask32 + 15) & ~15);
m_absmask32[0] = m_absmask32[1] = m_absmask32[2] = m_absmask32[3] = 0x7fffffff;
m_absmask64 = (UINT64 *)&m_absmask32[4];
m_absmask64[0] = m_absmask64[1] = U64(0x7fffffffffffffff);
// get pointers to C functions we need to call
m_near.debug_cpu_instruction_hook = (x86code *)debugger_instruction_hook;
if (LOG_HASHJMPS)
{
m_near.debug_log_hashjmp = (x86code *)debug_log_hashjmp;
m_near.debug_log_hashjmp_fail = (x86code *)debug_log_hashjmp_fail;
}
m_near.drcmap_get_value = (x86code *)&drc_map_variables::static_get_value;
// build the flags map
for (int entry = 0; entry < ARRAY_LENGTH(m_near.flagsmap); 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;
m_near.flagsmap[entry] = flags;
}
for (int entry = 0; entry < ARRAY_LENGTH(m_near.flagsunmap); entry++)
{
UINT64 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;
m_near.flagsunmap[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("drcbex64.asm");
}
//-------------------------------------------------
// ~drcbe_x64 - destructor
//-------------------------------------------------
drcbe_x64::~drcbe_x64()
{
// free the log context
if (m_log != NULL)
x86log_free_context(m_log);
}
//-------------------------------------------------
// reset - reset back-end specific state
//-------------------------------------------------
void drcbe_x64::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_r64(dst, REG_RBX); // push rbx
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_r64(dst, REG_RBX); // pop rbx
emit_ret(dst); // ret
// call it to determine if we have SSE4.1 support
m_sse41 = (((*cpuid_ecx_stub)() & 0x80000) != 0);
// generate an entry point
m_entry = (x86_entry_point_func)dst;
emit_push_r64(dst, REG_RBX); // push rbx
emit_push_r64(dst, REG_RSI); // push rsi
emit_push_r64(dst, REG_RDI); // push rdi
emit_push_r64(dst, REG_RBP); // push rbp
emit_push_r64(dst, REG_R12); // push r12
emit_push_r64(dst, REG_R13); // push r13
emit_push_r64(dst, REG_R14); // push r14
emit_push_r64(dst, REG_R15); // push r15
emit_mov_r64_r64(dst, REG_RBP, REG_PARAM1); // mov rbp,param1
emit_sub_r64_imm(dst, REG_RSP, 32); // sub rsp,32
emit_mov_m64_r64(dst, MABS(&m_near.hashstacksave), REG_RSP); // mov [hashstacksave],rsp
emit_sub_r64_imm(dst, REG_RSP, 8); // sub rsp,8
emit_mov_m64_r64(dst, MABS(&m_near.stacksave), REG_RSP); // mov [stacksave],rsp
emit_stmxcsr_m32(dst, MABS(&m_near.ssemode)); // stmxcsr [ssemode]
emit_jmp_r64(dst, REG_PARAM2); // jmp param2
if (m_log != NULL)
x86log_disasm_code_range(m_log, "entry_point", (x86code *)m_entry, dst);
// generate an exit point
m_exit = dst;
emit_ldmxcsr_m32(dst, MABS(&m_near.ssemode)); // ldmxcsr [ssemode]
emit_mov_r64_m64(dst, REG_RSP, MABS(&m_near.hashstacksave)); // mov rsp,[hashstacksave]
emit_add_r64_imm(dst, REG_RSP, 32); // add rsp,32
emit_pop_r64(dst, REG_R15); // pop r15
emit_pop_r64(dst, REG_R14); // pop r14
emit_pop_r64(dst, REG_R13); // pop r13
emit_pop_r64(dst, REG_R12); // pop r12
emit_pop_r64(dst, REG_RBP); // pop rbp
emit_pop_r64(dst, REG_RDI); // pop rdi
emit_pop_r64(dst, REG_RSI); // pop rsi
emit_pop_r64(dst, REG_RBX); // pop rbx
emit_ret(dst); // ret
if (m_log != NULL)
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)
x86log_disasm_code_range(m_log, "nocode", m_nocode, dst);
// finish up codegen
*cachetop = (drccodeptr)dst;
m_cache.end_codegen();
// reset our hash tables
m_hash.reset();
m_hash.set_default_codeptr(m_nocode);
}
//-------------------------------------------------
// execute - execute a block of code referenced
// by the given handle
//-------------------------------------------------
int drcbe_x64::execute(code_handle &entry)
{
// call our entry point which will jump to the destination
return (*m_entry)(m_rbpvalue, (x86code *)entry.codeptr());
}
//-------------------------------------------------
// drcbex64_generate - generate code
//-------------------------------------------------
void drcbe_x64::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);
}
//-------------------------------------------------
// hash_exists - return true if the given mode/pc
// exists in the hash table
//-------------------------------------------------
bool drcbe_x64::hash_exists(UINT32 mode, UINT32 pc)
{
return m_hash.code_exists(mode, pc);
}
//-------------------------------------------------
// get_info - return information about the
// back-end implementation
//-------------------------------------------------
void drcbe_x64::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;
for (info.direct_fregs = 0; info.direct_fregs < REG_F_COUNT; info.direct_fregs++)
if (float_register_map[info.direct_fregs] == 0)
break;
}
/***************************************************************************
EMITTERS FOR 32-BIT OPERATIONS WITH PARAMETERS
***************************************************************************/
//-------------------------------------------------
// emit_mov_r32_p32 - move a 32-bit parameter
// into a register
//-------------------------------------------------
void drcbe_x64::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_movsx_r64_p32 - move a 32-bit parameter
// sign-extended into a register
//-------------------------------------------------
void drcbe_x64::emit_movsx_r64_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 if ((INT32)param.immediate() >= 0)
emit_mov_r32_imm(dst, reg, param.immediate()); // mov reg,param
else
emit_mov_r64_imm(dst, reg, (INT32)param.immediate()); // mov reg,param
}
else if (param.is_memory())
emit_movsxd_r64_m32(dst, reg, MABS(param.memory())); // movsxd reg,[param]
else if (param.is_int_register())
emit_movsxd_r64_r32(dst, reg, param.ireg()); // movsdx reg,param
}
//-------------------------------------------------
// emit_mov_r32_p32_keepflags - move a 32-bit
// parameter into a register without affecting
// any flags
//-------------------------------------------------
void drcbe_x64::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())
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_x64::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())
{
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_x64::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
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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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, 0xffffffff); // 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_x64::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, 0xffffffff); // 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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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_x64::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 register
//-------------------------------------------------
void drcbe_x64::emit_mov_r64_p64(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_r64_imm(dst, reg, param.immediate()); // mov reg,param
}
else if (param.is_memory())
emit_mov_r64_m64(dst, reg, MABS(param.memory())); // mov reg,[param]
else if (param.is_int_register())
{
if (reg != param.ireg())
emit_mov_r64_r64(dst, reg, param.ireg()); // mov reg,param
}
}
//-------------------------------------------------
// emit_mov_r64_p64_keepflags - move a 64-bit
// parameter into a register without affecting
// any flags
//-------------------------------------------------
void drcbe_x64::emit_mov_r64_p64_keepflags(x86code *&dst, UINT8 reg, const be_parameter ¶m)
{
if (param.is_immediate())
emit_mov_r64_imm(dst, reg, param.immediate()); // mov reg,param
else if (param.is_memory())
emit_mov_r64_m64(dst, reg, MABS(param.memory())); // mov reg,[param]
else if (param.is_int_register())
{
if (reg != param.ireg())
emit_mov_r64_r64(dst, reg, param.ireg()); // mov reg,param
}
}
//-------------------------------------------------
// emit_mov_p64_r64 - move a registers into a
// 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_mov_p64_r64(x86code *&dst, const be_parameter ¶m, UINT8 reg)
{
assert(!param.is_immediate());
if (param.is_memory())
emit_mov_m64_r64(dst, MABS(param.memory()), reg); // mov [param],reg
else if (param.is_int_register())
{
if (reg != param.ireg())
emit_mov_r64_r64(dst, param.ireg(), reg); // mov param,reg
}
}
//-------------------------------------------------
// emit_add_r64_p64 - add operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_add_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (short_immediate(param.immediate()))
emit_add_r64_imm(dst, reg, param.immediate()); // add reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_add_r64_r64(dst, reg, REG_R11); // add reg,r11
}
}
}
else if (param.is_memory())
emit_add_r64_m64(dst, reg, MABS(param.memory())); // add reg,[param]
else if (param.is_int_register())
emit_add_r64_r64(dst, reg, param.ireg()); // add reg,param
}
//-------------------------------------------------
// emit_add_m64_p64 - add operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_add_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (short_immediate(param.immediate()))
emit_add_m64_imm(dst, memref, param.immediate()); // add [mem],param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_add_m64_r64(dst, memref, REG_R11); // add [mem],r11
}
}
}
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64(dst, reg, param); // mov reg,param
emit_add_m64_r64(dst, memref, reg); // add [dest],reg
}
}
//-------------------------------------------------
// emit_adc_r64_p64 - adc operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_adc_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (short_immediate(param.immediate()))
emit_adc_r64_imm(dst, reg, param.immediate()); // adc reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_adc_r64_r64(dst, reg, REG_R11); // adc reg,r11
}
}
else if (param.is_memory())
emit_adc_r64_m64(dst, reg, MABS(param.memory())); // adc reg,[param]
else if (param.is_int_register())
emit_adc_r64_r64(dst, reg, param.ireg()); // adc reg,param
}
//-------------------------------------------------
// emit_adc_m64_p64 - adc operation to a 64-bit
// memory locaiton from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_adc_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate() && short_immediate(param.immediate()))
emit_adc_m64_imm(dst, memref, param.immediate()); // adc [mem],param
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64_keepflags(dst, reg, param); // mov reg,param
emit_adc_m64_r64(dst, memref, reg); // adc [dest],reg
}
}
//-------------------------------------------------
// emit_sub_r64_p64 - sub operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_sub_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (short_immediate(param.immediate()))
emit_sub_r64_imm(dst, reg, param.immediate()); // sub reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_sub_r64_r64(dst, reg, REG_R11); // sub reg,r11
}
}
}
else if (param.is_memory())
emit_sub_r64_m64(dst, reg, MABS(param.memory())); // sub reg,[param]
else if (param.is_int_register())
emit_sub_r64_r64(dst, reg, param.ireg()); // sub reg,param
}
//-------------------------------------------------
// emit_sub_m64_p64 - sub operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_sub_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (short_immediate(param.immediate()))
emit_sub_m64_imm(dst, memref, param.immediate()); // sub [mem],param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_sub_m64_r64(dst, memref, REG_R11); // sub [mem],r11
}
}
}
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64(dst, reg, param); // mov reg,param
emit_sub_m64_r64(dst, memref, reg); // sub [dest],reg
}
}
//-------------------------------------------------
// emit_sbb_r64_p64 - sbb operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_sbb_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (short_immediate(param.immediate()))
emit_sbb_r64_imm(dst, reg, param.immediate()); // sbb reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_sbb_r64_r64(dst, reg, REG_R11); // sbb reg,r11
}
}
else if (param.is_memory())
emit_sbb_r64_m64(dst, reg, MABS(param.memory())); // sbb reg,[param]
else if (param.is_int_register())
emit_sbb_r64_r64(dst, reg, param.ireg()); // sbb reg,param
}
//-------------------------------------------------
// emit_sbb_m64_p64 - sbb operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_sbb_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate() && short_immediate(param.immediate()))
emit_sbb_m64_imm(dst, memref, param.immediate()); // sbb [mem],param
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64_keepflags(dst, reg, param); // mov reg,param
emit_sbb_m64_r64(dst, memref, reg); // sbb [dest],reg
}
}
//-------------------------------------------------
// emit_cmp_r64_p64 - cmp operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_cmp_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (short_immediate(param.immediate()))
emit_cmp_r64_imm(dst, reg, param.immediate()); // cmp reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_cmp_r64_r64(dst, reg, REG_R11); // cmp reg,r11
}
}
else if (param.is_memory())
emit_cmp_r64_m64(dst, reg, MABS(param.memory())); // cmp reg,[param]
else if (param.is_int_register())
emit_cmp_r64_r64(dst, reg, param.ireg()); // cmp reg,param
}
//-------------------------------------------------
// emit_cmp_m64_p64 - cmp operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_cmp_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate() && short_immediate(param.immediate()))
emit_cmp_m64_imm(dst, memref, param.immediate()); // cmp [dest],param
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64(dst, reg, param); // mov reg,param
emit_cmp_m64_r64(dst, memref, reg); // cmp [dest],reg
}
}
//-------------------------------------------------
// emit_and_r64_p64 - and operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_and_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != U64(0xffffffffffffffff))
{
if (short_immediate(param.immediate()))
emit_and_r64_imm(dst, reg, param.immediate()); // and reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_and_r64_r64(dst, reg, REG_R11); // and reg,r11
}
}
}
else if (param.is_memory())
emit_and_r64_m64(dst, reg, MABS(param.memory())); // and reg,[param]
else if (param.is_int_register())
emit_and_r64_r64(dst, reg, param.ireg()); // and reg,param
}
//-------------------------------------------------
// emit_and_m64_p64 - and operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_and_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != U64(0xffffffffffffffff))
{
if (short_immediate(param.immediate()))
emit_and_m64_imm(dst, memref, param.immediate()); // and [mem],param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_and_m64_r64(dst, memref, REG_R11); // and [mem],r11
}
}
}
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64(dst, reg, param); // mov reg,param
emit_and_m64_r64(dst, memref, reg); // and [dest],reg
}
}
//-------------------------------------------------
// emit_test_r64_p64 - test operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_test_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (short_immediate(param.immediate()))
emit_test_r64_imm(dst, reg, param.immediate()); // test reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_test_r64_r64(dst, reg, REG_R11); // test reg,r11
}
}
else if (param.is_memory())
emit_test_m64_r64(dst, MABS(param.memory()), reg); // test [param],reg
else if (param.is_int_register())
emit_test_r64_r64(dst, reg, param.ireg()); // test reg,param
}
//-------------------------------------------------
// emit_test_m64_p64 - test operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_test_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate() && short_immediate(param.immediate()))
emit_test_m64_imm(dst, memref, param.immediate()); // test [dest],param
else if (param.is_memory())
{
emit_mov_r64_p64(dst, REG_EAX, param); // mov reg,param
emit_test_m64_r64(dst, memref, REG_EAX); // test [dest],reg
}
else if (param.is_int_register())
emit_test_m64_r64(dst, memref, param.ireg()); // test [dest],param
}
//-------------------------------------------------
// emit_or_r64_p64 - or operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_or_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (short_immediate(param.immediate()))
emit_or_r64_imm(dst, reg, param.immediate()); // or reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_or_r64_r64(dst, reg, REG_R11); // or reg,r11
}
}
}
else if (param.is_memory())
emit_or_r64_m64(dst, reg, MABS(param.memory())); // or reg,[param]
else if (param.is_int_register())
emit_or_r64_r64(dst, reg, param.ireg()); // or reg,param
}
//-------------------------------------------------
// emit_or_m64_p64 - or operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_or_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (short_immediate(param.immediate()))
emit_or_m64_imm(dst, memref, param.immediate()); // or [mem],param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_or_m64_r64(dst, memref, REG_R11); // or [mem],r11
}
}
}
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64(dst, reg, param); // mov reg,param
emit_or_m64_r64(dst, memref, reg); // or [dest],reg
}
}
//-------------------------------------------------
// emit_xor_r64_p64 - xor operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_xor_r64_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (param.immediate() == U64(0xffffffffffffffff))
emit_not_r64(dst, reg); // not reg
else if (short_immediate(param.immediate()))
emit_xor_r64_imm(dst, reg, param.immediate()); // xor reg,param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_xor_r64_r64(dst, reg, REG_R11); // xor reg,r11
}
}
}
else if (param.is_memory())
emit_xor_r64_m64(dst, reg, MABS(param.memory())); // xor reg,[param]
else if (param.is_int_register())
emit_xor_r64_r64(dst, reg, param.ireg()); // xor reg,param
}
//-------------------------------------------------
// emit_xor_m64_p64 - xor operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_xor_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() != 0 || param.immediate() != 0)
{
if (param.immediate() == U64(0xffffffffffffffff))
emit_not_m64(dst, memref); // not [mem]
else if (short_immediate(param.immediate()))
emit_xor_m64_imm(dst, memref, param.immediate()); // xor [mem],param
else
{
emit_mov_r64_imm(dst, REG_R11, param.immediate()); // mov r11,param
emit_xor_m64_r64(dst, memref, REG_R11); // xor [mem],r11
}
}
}
else
{
int reg = param.select_register(REG_EAX);
emit_mov_r64_p64(dst, reg, param); // mov reg,param
emit_xor_m64_r64(dst, memref, reg); // xor [dest],reg
}
}
//-------------------------------------------------
// emit_shl_r64_p64 - shl operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_shl_r64_p64(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_r64_imm(dst, reg, param.immediate()); // shl reg,param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_shl_r64_cl(dst, reg); // shl reg,cl
}
}
//-------------------------------------------------
// emit_shl_m64_p64 - shl operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_shl_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_shl_m64_imm(dst, memref, param.immediate()); // shl [dest],param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_shl_m64_cl(dst, memref); // shl [dest],cl
}
}
//-------------------------------------------------
// emit_shr_r64_p64 - shr operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_shr_r64_p64(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_r64_imm(dst, reg, param.immediate()); // shr reg,param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_shr_r64_cl(dst, reg); // shr reg,cl
}
}
//-------------------------------------------------
// emit_shr_m64_p64 - shr operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_shr_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_shr_m64_imm(dst, memref, param.immediate()); // shr [dest],param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_shr_m64_cl(dst, memref); // shr [dest],cl
}
}
//-------------------------------------------------
// emit_sar_r64_p64 - sar operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_sar_r64_p64(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_r64_imm(dst, reg, param.immediate()); // sar reg,param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_sar_r64_cl(dst, reg); // sar reg,cl
}
}
//-------------------------------------------------
// emit_sar_m64_p64 - sar operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_sar_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_sar_m64_imm(dst, memref, param.immediate()); // sar [dest],param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_sar_m64_cl(dst, memref); // sar [dest],cl
}
}
//-------------------------------------------------
// emit_rol_r64_p64 - rol operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_rol_r64_p64(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_r64_imm(dst, reg, param.immediate()); // rol reg,param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_rol_r64_cl(dst, reg); // rol reg,cl
}
}
//-------------------------------------------------
// emit_rol_m64_p64 - rol operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_rol_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_rol_m64_imm(dst, memref, param.immediate()); // rol [dest],param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_rol_m64_cl(dst, memref); // rol [dest],cl
}
}
//-------------------------------------------------
// emit_ror_r64_p64 - ror operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_ror_r64_p64(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_r64_imm(dst, reg, param.immediate()); // ror reg,param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_ror_r64_cl(dst, reg); // ror reg,cl
}
}
//-------------------------------------------------
// emit_ror_m64_p64 - ror operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_ror_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_ror_m64_imm(dst, memref, param.immediate()); // ror [dest],param
}
else
{
emit_mov_r64_p64(dst, REG_RCX, param); // mov rcx,param
emit_ror_m64_cl(dst, memref); // ror [dest],cl
}
}
//-------------------------------------------------
// emit_rcl_r64_p64 - rcl operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_rcl_r64_p64(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_r64_imm(dst, reg, param.immediate()); // rcl reg,param
}
else
{
emit_mov_r64_p64_keepflags(dst, REG_RCX, param); // mov rcx,param
emit_rcl_r64_cl(dst, reg); // rcl reg,cl
}
}
//-------------------------------------------------
// emit_rcl_m64_p64 - rcl operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_rcl_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_rcl_m64_imm(dst, memref, param.immediate()); // rcl [dest],param
}
else
{
emit_mov_r64_p64_keepflags(dst, REG_RCX, param); // mov rcx,param
emit_rcl_m64_cl(dst, memref); // rcl [dest],cl
}
}
//-------------------------------------------------
// emit_rcr_r64_p64 - rcr operation to a 64-bit
// register from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_rcr_r64_p64(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_r64_imm(dst, reg, param.immediate()); // rcr reg,param
}
else
{
emit_mov_r64_p64_keepflags(dst, REG_RCX, param); // mov rcx,param
emit_rcr_r64_cl(dst, reg); // rcr reg,cl
}
}
//-------------------------------------------------
// emit_rcr_m64_p64 - rcr operation to a 64-bit
// memory location from a 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_rcr_m64_p64(x86code *&dst, x86_memref memref, const be_parameter ¶m, const instruction &inst)
{
if (param.is_immediate())
{
if (inst.flags() == 0 && (UINT64)param.immediate() == 0)
;// skip
else
emit_rcr_m64_imm(dst, memref, param.immediate()); // rcr [dest],param
}
else
{
emit_mov_r64_p64_keepflags(dst, REG_RCX, param); // mov rcx,param
emit_rcr_m64_cl(dst, memref); // rcr [dest],cl
}
}
/***************************************************************************
EMITTERS FOR FLOATING POINT OPERATIONS WITH PARAMETERS
***************************************************************************/
//-------------------------------------------------
// emit_movss_r128_p32 - move a 32-bit parameter
// into a register
//-------------------------------------------------
void drcbe_x64::emit_movss_r128_p32(x86code *&dst, UINT8 reg, const be_parameter ¶m)
{
assert(!param.is_immediate());
if (param.is_memory())
emit_movss_r128_m32(dst, reg, MABS(param.memory())); // movss reg,[param]
else if (param.is_float_register())
{
if (reg != param.freg())
emit_movss_r128_r128(dst, reg, param.freg()); // movss reg,param
}
}
//-------------------------------------------------
// emit_movss_p32_r128 - move a register into a
// 32-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_movss_p32_r128(x86code *&dst, const be_parameter ¶m, UINT8 reg)
{
assert(!param.is_immediate());
if (param.is_memory())
emit_movss_m32_r128(dst, MABS(param.memory()), reg); // movss [param],reg
else if (param.is_float_register())
{
if (reg != param.freg())
emit_movss_r128_r128(dst, param.freg(), reg); // movss param,reg
}
}
//-------------------------------------------------
// emit_movsd_r128_p64 - move a 64-bit parameter
// into a register
//-------------------------------------------------
void drcbe_x64::emit_movsd_r128_p64(x86code *&dst, UINT8 reg, const be_parameter ¶m)
{
assert(!param.is_immediate());
if (param.is_memory())
emit_movsd_r128_m64(dst, reg, MABS(param.memory())); // movsd reg,[param]
else if (param.is_float_register())
{
if (reg != param.freg())
emit_movsd_r128_r128(dst, reg, param.freg()); // movsd reg,param
}
}
//-------------------------------------------------
// emit_movsd_p64_r128 - move a register into a
// 64-bit parameter
//-------------------------------------------------
void drcbe_x64::emit_movsd_p64_r128(x86code *&dst, const be_parameter ¶m, UINT8 reg)
{
assert(!param.is_immediate());
if (param.is_memory())
emit_movsd_m64_r128(dst, MABS(param.memory()), reg); // movsd [param],reg
else if (param.is_float_register())
{
if (reg != param.freg())
emit_movsd_r128_r128(dst, param.freg(), reg); // movsd param,reg
}
}
/***************************************************************************
OUT-OF-BAND CODE FIXUP CALLBACKS
***************************************************************************/
//-------------------------------------------------
// fixup_label - callback to fixup forward-
// referenced labels
//-------------------------------------------------
void drcbe_x64::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_x64::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_lea_r64_m64(dst, REG_RAX, MABS(src)); // lea rax,[return]
emit_push_r64(dst, REG_RAX); // push rax
if (*targetptr != NULL)
emit_jmp(dst, *targetptr); // jmp *targetptr
else
emit_jmp_m64(dst, MABS(targetptr)); // jmp [targetptr]
*codeptr = dst;
}
//**************************************************************************
// DEBUG HELPERS
//**************************************************************************
//-------------------------------------------------
// debug_log_hashjmp - callback to handle
// logging of hashjmps
//-------------------------------------------------
void drcbe_x64::debug_log_hashjmp(offs_t pc, int mode)
{
printf("mode=%d PC=%08X\n", mode, pc);
}
//-------------------------------------------------
// debug_log_hashjmp - callback to handle
// logging of hashjmps
//-------------------------------------------------
void drcbe_x64::debug_log_hashjmp_fail()
{
printf(" (FAIL)\n");
}
/***************************************************************************
COMPILE-TIME OPCODES
***************************************************************************/
//-------------------------------------------------
// op_handle - process a HANDLE opcode
//-------------------------------------------------
void drcbe_x64::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());
// 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_r64_m64(dst, REG_RSP, MBD(REG_RSP, -40)); // lea rsp,[rsp-40]
resolve_link(dst, skip); // skip:
}
//-------------------------------------------------
// op_hash - process a HASH opcode
//-------------------------------------------------
void drcbe_x64::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);
}
//-------------------------------------------------
// op_label - process a LABEL opcode
//-------------------------------------------------
void drcbe_x64::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);
}
//-------------------------------------------------
// op_comment - process a COMMENT opcode
//-------------------------------------------------
void drcbe_x64::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_x64::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_x64::op_nop(x86code *&dst, const instruction &inst)
{
// nothing
}
//-------------------------------------------------
// op_debug - process a DEBUG opcode
//-------------------------------------------------
void drcbe_x64::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_mov_r64_imm(dst, REG_RAX, (FPTR)&m_device.machine().debug_flags); // mov rax,&debug_flags
emit_test_m32_imm(dst, MBD(REG_RAX, 0), DEBUG_FLAG_CALL_HOOK); // test [debug_flags],DEBUG_FLAG_CALL_HOOK
emit_link skip = { 0 };
emit_jcc_short_link(dst, x64emit::COND_Z, skip); // jz skip
// push the parameter
emit_mov_r64_imm(dst, REG_PARAM1, (FPTR)&m_device); // mov param1,device
emit_mov_r32_p32(dst, REG_PARAM2, pcp); // mov param2,pcp
emit_smart_call_m64(dst, &m_near.debug_cpu_instruction_hook); // call debug_cpu_instruction_hook
resolve_link(dst, skip); // skip:
}
}
//-------------------------------------------------
// op_exit - process an EXIT opcode
//-------------------------------------------------
void drcbe_x64::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_x64::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_r32_p32(dst, REG_PARAM1, pcp);
emit_mov_r32_p32(dst, REG_PARAM2, modep);
emit_smart_call_m64(dst, &m_near.debug_log_hashjmp);
}
// load the stack base one word early so we end up at the right spot after our call below
emit_mov_r64_m64(dst, REG_RSP, MABS(&m_near.hashstacksave)); // mov rsp,[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_m64(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_r64_m64(dst, REG_RDX, MBISD(REG_RBP, REG_RDX, 8, offset_from_rbp(&m_hash.base()[modep.immediate()][0])));
// mov rdx,hash[modep+edx*8]
emit_call_m64(dst, MBISD(REG_RDX, REG_RAX, 8 >> m_hash.l2shift(), 0)); // call [rdx+rax*shift]
}
}
else
{
// variable mode
int modereg = modep.select_register(REG_ECX);
emit_mov_r32_p32(dst, modereg, modep); // mov modereg,modep
emit_mov_r64_m64(dst, REG_RCX, MBISD(REG_RBP, modereg, 8, offset_from_rbp(m_hash.base())));
// mov rcx,hash[modereg*8]
// 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_r64_m64(dst, REG_RDX, MBD(REG_RCX, l1val*8)); // mov rdx,[rcx+l1val*8]
emit_call_m64(dst, MBD(REG_RDX, l2val*8)); // call [l2val*8]
}
// 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_r64_m64(dst, REG_RDX, MBISD(REG_RCX, REG_RDX, 8, 0)); // mov rdx,[rcx+rdx*8]
emit_and_r32_imm(dst, REG_EAX, m_hash.l2mask() << m_hash.l2shift()); // and eax,l2mask << l2shift
emit_call_m64(dst, MBISD(REG_RDX, REG_RAX, 8 >> m_hash.l2shift(), 0)); // call [rdx+rax*shift]
}
}
// in all cases, if there is no code, we return here to generate the exception
if (LOG_HASHJMPS)
emit_smart_call_m64(dst, &m_near.debug_log_hashjmp_fail);
emit_mov_m32_p32(dst, MABS(&m_state.exp), pcp); // mov [exp],param
emit_sub_r64_imm(dst, REG_RSP, 8); // sub rsp,8
emit_call_m64(dst, MABS(exp.handle().codeptr_addr())); // call [exp]
}
//-------------------------------------------------
// op_jmp - process a JMP opcode
//-------------------------------------------------
void drcbe_x64::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 (jmptarget == NULL)
jmptarget = dst + 0x7ffffff0;
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_x64::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_m64(dst, MABS(targetptr)); // call [targetptr]
}
// otherwise, jump to an out-of-band handler
else
{
emit_jcc(dst, X86_CONDITION(inst.condition()), dst + 0x7ffffff0); // jcc exception
m_cache.request_oob_codegen(m_fixup_exception, dst, &const_cast<instruction &>(inst));
}
}
//-------------------------------------------------
// op_callh - process a CALLH opcode
//-------------------------------------------------
void drcbe_x64::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_m64(dst, MABS(targetptr)); // call [targetptr]
// resolve the conditional link
if (inst.condition() != uml::COND_ALWAYS)
resolve_link(dst, skip); // skip:
}
//-------------------------------------------------
// op_ret - process a RET opcode
//-------------------------------------------------
void drcbe_x64::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_r64_m64(dst, REG_RSP, MBD(REG_RSP, 40)); // lea rsp,[rsp+40]
emit_ret(dst); // ret
// resolve the conditional link
if (inst.condition() != uml::COND_ALWAYS)
resolve_link(dst, skip); // skip:
}
//-------------------------------------------------
// op_callc - process a CALLC opcode
//-------------------------------------------------
void drcbe_x64::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_r64_imm(dst, REG_PARAM1, (FPTR)paramp.memory()); // mov param1,paramp
emit_smart_call_r64(dst, (x86code *)(FPTR)funcp.cfunc(), REG_RAX); // call funcp
// resolve the conditional link
if (inst.condition() != uml::COND_ALWAYS)
resolve_link(dst, skip); // skip:
}
//-------------------------------------------------
// op_recover - process a RECOVER opcode
//-------------------------------------------------
void drcbe_x64::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_r64_m64(dst, REG_RAX, MABS(&m_near.stacksave)); // mov rax,stacksave
emit_mov_r64_m64(dst, REG_RAX, MBD(REG_RAX, -8)); // mov rax,[rax-4]
emit_mov_r64_imm(dst, REG_PARAM1, (FPTR)&m_map); // mov param1,m_map
emit_lea_r64_m64(dst, REG_PARAM2, MBD(REG_RAX, -1)); // lea param2,[rax-1]
emit_mov_r64_imm(dst, REG_PARAM3, inst.param(1).mapvar()); // mov param3,param[1].value
emit_smart_call_m64(dst, &m_near.drcmap_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_x64::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_ldmxcsr_m32(dst, MABS(&m_near.ssecontrol[value])); // ldmxcsr 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_ldmxcsr_m32(dst, MBISD(REG_RBP, REG_RAX, 4, offset_from_rbp(&m_near.ssecontrol[0])));
// ldmxcsr fp_control[eax]
}
}
//-------------------------------------------------
// op_getfmod - process a GETFMOD opcode
//-------------------------------------------------
void drcbe_x64::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_x64::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_x64::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, x64emit::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, x64emit::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, x64emit::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, x64emit::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, x64emit::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, x64emit::COND_C, REG_AL); // setc al
emit_setcc_r8(dst, x64emit::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,cl
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, x64emit::COND_C, REG_AL); // setc al
emit_setcc_r8(dst, x64emit::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,cl
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, x64emit::COND_C, REG_AL); // setc al
emit_setcc_r8(dst, x64emit::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,cl
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, x64emit::COND_O, REG_AL); // seto al
emit_setcc_r8(dst, x64emit::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,cl
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, x64emit::COND_O, REG_AL); // seto al
emit_setcc_r8(dst, x64emit::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, x64emit::COND_Z, REG_AL); // setz al
emit_setcc_r8(dst, x64emit::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_r64(dst, REG_EAX); // pop eax
emit_and_r32_imm(dst, REG_EAX, flagmask); // and eax,flagmask
emit_movzx_r32_m8(dst, dstreg, MBISD(REG_RBP, REG_RAX, 1, offset_from_rbp(&m_near.flagsmap[0])));
// movzx dstreg,[flags_map]
break;
}
// 32-bit form
if (inst.size() == 4)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
// 64-bit form
else if (inst.size() == 8)
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
//-------------------------------------------------
// op_save - process a SAVE opcode
//-------------------------------------------------
void drcbe_x64::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_r64_imm(dst, REG_RCX, (FPTR)dstp.memory()); // mov rcx,dstp
// copy flags
emit_pushf(dst); // pushf
emit_pop_r64(dst, REG_RAX); // pop rax
emit_and_r32_imm(dst, REG_EAX, 0x8c5); // and eax,0x8c5
emit_mov_r8_m8(dst, REG_AL, MBISD(REG_RBP, REG_RAX, 1, offset_from_rbp(&m_near.flagsmap[0])));
// mov al,[flags_map]
emit_mov_m8_r8(dst, MBD(REG_RCX, offsetof(drcuml_machine_state, flags)), REG_AL); // mov state->flags,al
// copy fmod and exp
emit_mov_r8_m8(dst, REG_AL, MABS(&m_state.fmod)); // mov al,[fmod]
emit_mov_m8_r8(dst, MBD(REG_RCX, 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_RCX, offsetof(drcuml_machine_state, exp)), REG_EAX); // mov state->exp,eax
// copy integer registers
int regoffs = offsetof(drcuml_machine_state, r);
for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.r); regnum++)
{
if (int_register_map[regnum] != 0)
emit_mov_m64_r64(dst, MBD(REG_RCX, regoffs + 8 * regnum), int_register_map[regnum]);
else
{
emit_mov_r64_m64(dst, REG_RAX, MABS(&m_state.r[regnum].d));
emit_mov_m64_r64(dst, MBD(REG_RCX, regoffs + 8 * regnum), REG_RAX);
}
}
// copy FP registers
regoffs = offsetof(drcuml_machine_state, f);
for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.f); regnum++)
{
if (float_register_map[regnum] != 0)
emit_movsd_m64_r128(dst, MBD(REG_RCX, regoffs + 8 * regnum), float_register_map[regnum]);
else
{
emit_mov_r64_m64(dst, REG_RAX, MABS(&m_state.f[regnum].d));
emit_mov_m64_r64(dst, MBD(REG_RCX, regoffs + 8 * regnum), REG_RAX);
}
}
}
//-------------------------------------------------
// op_restore - process a RESTORE opcode
//-------------------------------------------------
void drcbe_x64::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_r64_imm(dst, REG_ECX, (FPTR)srcp.memory()); // mov rcx,dstp
// copy integer registers
int regoffs = offsetof(drcuml_machine_state, r);
for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.r); regnum++)
{
if (int_register_map[regnum] != 0)
emit_mov_r64_m64(dst, int_register_map[regnum], MBD(REG_RCX, regoffs + 8 * regnum));
else
{
emit_mov_r64_m64(dst, REG_RAX, MBD(REG_RCX, regoffs + 8 * regnum));
emit_mov_m64_r64(dst, MABS(&m_state.r[regnum].d), REG_RAX);
}
}
// copy FP registers
regoffs = offsetof(drcuml_machine_state, f);
for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.f); regnum++)
{
if (float_register_map[regnum] != 0)
emit_movsd_r128_m64(dst, float_register_map[regnum], MBD(REG_RCX, regoffs + 8 * regnum));
else
{
emit_mov_r64_m64(dst, REG_RAX, MBD(REG_RCX, regoffs + 8 * regnum));
emit_mov_m64_r64(dst, MABS(&m_state.f[regnum].d), REG_RAX);
}
}
// copy fmod and exp
emit_movzx_r32_m8(dst, REG_EAX, MBD(REG_RCX, 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_ldmxcsr_m32(dst, MBISD(REG_RBP, REG_RAX, 4, offset_from_rbp(&m_near.ssecontrol[0])));
emit_mov_r32_m32(dst, REG_EAX, MBD(REG_RCX, offsetof(drcuml_machine_state, exp))); // mov eax,state->exp
emit_mov_m32_r32(dst, MABS(&m_state.exp), REG_EAX); // mov [exp],eax
// copy flags
emit_movzx_r32_m8(dst, REG_EAX, MBD(REG_RCX, offsetof(drcuml_machine_state, flags)));// movzx eax,state->flags
emit_push_m64(dst, MBISD(REG_RBP, REG_RAX, 8, offset_from_rbp(&m_near.flagsunmap[0])));
// push flags_unmap[eax*8]
emit_popf(dst); // popf
}
/***************************************************************************
INTEGER OPERATIONS
***************************************************************************/
//-------------------------------------------------
// op_load - process a LOAD opcode
//-------------------------------------------------
void drcbe_x64::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();
// determine the pointer base
INT32 baseoffs;
int basereg = get_base_register_and_offset(dst, basep.memory(), REG_RDX, baseoffs);
// 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, MBD(basereg, baseoffs + scale*indp.immediate()));// movzx dstreg,[basep + scale*indp]
else if (size == SIZE_WORD)
emit_movzx_r32_m16(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate()));// movzx dstreg,[basep + scale*indp]
else if (size == SIZE_DWORD)
emit_mov_r32_m32(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate())); // mov dstreg,[basep + scale*indp]
else if (size == SIZE_QWORD)
emit_mov_r64_m64(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate())); // mov dstreg,[basep + scale*indp]
}
// other index
else
{
int indreg = indp.select_register(REG_ECX);
emit_movsx_r64_p32(dst, indreg, indp);
if (size == SIZE_BYTE)
emit_movzx_r32_m8(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs)); // movzx dstreg,[basep + scale*indp]
else if (size == SIZE_WORD)
emit_movzx_r32_m16(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs)); // movzx dstreg,[basep + scale*indp]
else if (size == SIZE_DWORD)
emit_mov_r32_m32(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs)); // mov dstreg,[basep + scale*indp]
else if (size == SIZE_QWORD)
emit_mov_r64_m64(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs)); // mov dstreg,[basep + scale*indp]
}
// store result
if (inst.size() == 4)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
else
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
//-------------------------------------------------
// op_loads - process a LOADS opcode
//-------------------------------------------------
void drcbe_x64::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();
// determine the pointer base
INT32 baseoffs;
int basereg = get_base_register_and_offset(dst, basep.memory(), REG_RDX, baseoffs);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_EAX);
// immediate index
if (indp.is_immediate())
{
if (inst.size() == 4)
{
if (size == SIZE_BYTE)
emit_movsx_r32_m8(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate()));// movsx dstreg,[basep + scale*indp]
else if (size == SIZE_WORD)
emit_movsx_r32_m16(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate()));// movsx dstreg,[basep + scale*indp]
else if (size == SIZE_DWORD)
emit_mov_r32_m32(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate())); // mov dstreg,[basep + scale*indp]
}
else if (inst.size() == 8)
{
if (size == SIZE_BYTE)
emit_movsx_r64_m8(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate()));// movzx dstreg,[basep + scale*indp]
else if (size == SIZE_WORD)
emit_movsx_r64_m16(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate()));// movzx dstreg,[basep + scale*indp]
else if (size == SIZE_DWORD)
emit_movsxd_r64_m32(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate()));// movsxd dstreg,[basep + scale*indp]
else if (size == SIZE_QWORD)
emit_mov_r64_m64(dst, dstreg, MBD(basereg, baseoffs + scale*indp.immediate())); // mov dstreg,[basep + scale*indp]
}
}
// other index
else
{
int indreg = indp.select_register(REG_ECX);
emit_movsx_r64_p32(dst, indreg, indp);
if (inst.size() == 4)
{
if (size == SIZE_BYTE)
emit_movsx_r32_m8(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs));// movsx dstreg,[basep + scale*indp]
else if (size == SIZE_WORD)
emit_movsx_r32_m16(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs));// movsx dstreg,[basep + scale*indp]
else if (size == SIZE_DWORD)
emit_mov_r32_m32(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs)); // mov dstreg,[basep + scale*indp]
}
else if (inst.size() == 8)
{
if (size == SIZE_BYTE)
emit_movsx_r64_m8(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs));// movsx dstreg,[basep + scale*indp]
else if (size == SIZE_WORD)
emit_movsx_r64_m16(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs));// movsx dstreg,[basep + scale*indp]
else if (size == SIZE_DWORD)
emit_movsxd_r64_m32(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs));// movsxd dstreg,[basep + scale*indp]
else if (size == SIZE_QWORD)
emit_mov_r64_m64(dst, dstreg, MBISD(basereg, indreg, scale, baseoffs)); // mov dstreg,[basep + scale*indp]
}
}
// store result
if (inst.size() == 4)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
else
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
//-------------------------------------------------
// op_store - process a STORE opcode
//-------------------------------------------------
void drcbe_x64::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();
// determine the pointer base
INT32 baseoffs;
int basereg = get_base_register_and_offset(dst, basep.memory(), REG_RDX, baseoffs);
// pick a source register for the general case
int srcreg = srcp.select_register(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, MBD(basereg, baseoffs + scale*indp.immediate()), srcp.immediate());// mov [basep + scale*indp],srcp
else if (size == SIZE_WORD)
emit_mov_m16_imm(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcp.immediate());// mov [basep + scale*indp],srcp
else if (size == SIZE_DWORD)
emit_mov_m32_imm(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcp.immediate());// mov [basep + scale*indp],srcp
else if (size == SIZE_QWORD)
{
if (short_immediate(srcp.immediate()))
emit_mov_m64_imm(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcp.immediate());// mov [basep + scale*indp],srcp
else
{
emit_mov_m32_imm(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcp.immediate());// mov [basep + scale*indp],srcp
emit_mov_m32_imm(dst, MBD(basereg, baseoffs + 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, srcp); // mov srcreg,srcp
if (size == SIZE_BYTE)
emit_mov_m8_r8(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcreg); // mov [basep + scale*indp],srcreg
else if (size == SIZE_WORD)
emit_mov_m16_r16(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcreg); // mov [basep + scale*indp],srcreg
else if (size == SIZE_DWORD)
emit_mov_m32_r32(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcreg); // mov [basep + scale*indp],srcreg
else if (size == SIZE_QWORD)
emit_mov_m64_r64(dst, MBD(basereg, baseoffs + scale*indp.immediate()), srcreg); // mov [basep + scale*indp],srcreg
}
}
// normal case: variable index
else
{
int indreg = indp.select_register(REG_ECX);
emit_movsx_r64_p32(dst, indreg, indp); // mov indreg,indp
// immediate source
if (srcp.is_immediate())
{
if (size == SIZE_BYTE)
emit_mov_m8_imm(dst, MBISD(basereg, indreg, scale, baseoffs), srcp.immediate());// mov [basep + scale*ecx],srcp
else if (size == SIZE_WORD)
emit_mov_m16_imm(dst, MBISD(basereg, indreg, scale, baseoffs), srcp.immediate());// mov [basep + scale*ecx],srcp
else if (size == SIZE_DWORD)
emit_mov_m32_imm(dst, MBISD(basereg, indreg, scale, baseoffs), srcp.immediate());// mov [basep + scale*ecx],srcp
else if (size == SIZE_QWORD)
{
if (short_immediate(srcp.immediate()))
emit_mov_m64_imm(dst, MBISD(basereg, indreg, scale, baseoffs), srcp.immediate());// mov [basep + scale*indp],srcp
else
{
emit_mov_m32_imm(dst, MBISD(basereg, indreg, scale, baseoffs), srcp.immediate());// mov [basep + scale*ecx],srcp
emit_mov_m32_imm(dst, MBISD(basereg, indreg, scale, baseoffs + 4), srcp.immediate() >> 32);
// mov [basep + scale*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, srcp); // mov edx:srcreg,srcp
if (size == SIZE_BYTE)
emit_mov_m8_r8(dst, MBISD(basereg, indreg, scale, baseoffs), srcreg); // mov [basep + scale*ecx],srcreg
else if (size == SIZE_WORD)
emit_mov_m16_r16(dst, MBISD(basereg, indreg, scale, baseoffs), srcreg); // mov [basep + scale*ecx],srcreg
else if (size == SIZE_DWORD)
emit_mov_m32_r32(dst, MBISD(basereg, indreg, scale, baseoffs), srcreg); // mov [basep + scale*ecx],srcreg
else if (size == SIZE_QWORD)
emit_mov_m64_r64(dst, MBISD(basereg, indreg, scale, baseoffs), srcreg); // mov [basep + scale*ecx],srcreg
}
}
}
//-------------------------------------------------
// op_read - process a READ opcode
//-------------------------------------------------
void drcbe_x64::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_r64_imm(dst, REG_PARAM1, (FPTR)(m_space[spacesizep.space()])); // mov param1,space
emit_mov_r32_p32(dst, REG_PARAM2, addrp); // mov param2,addrp
if (spacesizep.size() == SIZE_BYTE)
{
emit_smart_call_m64(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_smart_call_m64(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_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].read_dword);
// call read_dword
if (dstreg != REG_EAX || inst.size() == 8)
emit_mov_r32_r32(dst, dstreg, REG_EAX); // mov dstreg,eax
}
else if (spacesizep.size() == SIZE_QWORD)
{
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].read_qword);
// call read_qword
if (dstreg != REG_RAX)
emit_mov_r64_r64(dst, dstreg, REG_RAX); // mov dstreg,rax
}
// store result
if (inst.size() == 4)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
else
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
//-------------------------------------------------
// op_readm - process a READM opcode
//-------------------------------------------------
void drcbe_x64::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
emit_mov_r64_imm(dst, REG_PARAM1, (FPTR)(m_space[spacesizep.space()])); // mov param1,space
emit_mov_r32_p32(dst, REG_PARAM2, addrp); // mov param2,addrp
if (spacesizep.size() != SIZE_QWORD)
emit_mov_r32_p32(dst, REG_PARAM3, maskp); // mov param3,maskp
else
emit_mov_r64_p64(dst, REG_PARAM3, maskp); // mov param3,maskp
if (spacesizep.size() == SIZE_WORD)
{
emit_smart_call_m64(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_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].read_dword_masked);
// call read_dword_masked
if (dstreg != REG_EAX || inst.size() == 8)
emit_mov_r32_r32(dst, dstreg, REG_EAX); // mov dstreg,eax
}
else if (spacesizep.size() == SIZE_QWORD)
{
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].read_qword_masked);
// call read_qword_masked
if (dstreg != REG_RAX)
emit_mov_r64_r64(dst, dstreg, REG_RAX); // mov dstreg,rax
}
// store result
if (inst.size() == 4)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
else
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
//-------------------------------------------------
// op_write - process a WRITE opcode
//-------------------------------------------------
void drcbe_x64::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
emit_mov_r64_imm(dst, REG_PARAM1, (FPTR)(m_space[spacesizep.space()])); // mov param1,space
emit_mov_r32_p32(dst, REG_PARAM2, addrp); // mov param2,addrp
if (spacesizep.size() != SIZE_QWORD)
emit_mov_r32_p32(dst, REG_PARAM3, srcp); // mov param3,srcp
else
emit_mov_r64_p64(dst, REG_PARAM3, srcp); // mov param3,srcp
if (spacesizep.size() == SIZE_BYTE)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_byte);
// call write_byte
else if (spacesizep.size() == SIZE_WORD)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_word);
// call write_word
else if (spacesizep.size() == SIZE_DWORD)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_dword);
// call write_dword
else if (spacesizep.size() == SIZE_QWORD)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_qword);
// call write_qword
}
//-------------------------------------------------
// op_writem - process a WRITEM opcode
//-------------------------------------------------
void drcbe_x64::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
emit_mov_r64_imm(dst, REG_PARAM1, (FPTR)(m_space[spacesizep.space()])); // mov param1,space
emit_mov_r32_p32(dst, REG_PARAM2, addrp); // mov param2,addrp
if (spacesizep.size() != SIZE_QWORD)
{
emit_mov_r32_p32(dst, REG_PARAM3, srcp); // mov param3,srcp
emit_mov_r32_p32(dst, REG_PARAM4, maskp); // mov param4,maskp
}
else
{
emit_mov_r64_p64(dst, REG_PARAM3, srcp); // mov param3,srcp
emit_mov_r64_p64(dst, REG_PARAM4, maskp); // mov param4,maskp
}
if (spacesizep.size() == SIZE_WORD)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_word_masked);
// call write_word_masked
else if (spacesizep.size() == SIZE_DWORD)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_dword_masked);
// call write_dword_masked
else if (spacesizep.size() == SIZE_QWORD)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacesizep.space()].write_qword_masked);
// call write_qword_masked
}
//-------------------------------------------------
// op_carry - process a CARRY opcode
//-------------------------------------------------
void drcbe_x64::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_m64_imm(dst, MABS(srcp.memory()), bitp.immediate()); // bt [srcp],bitp
else if (srcp.is_int_register())
emit_bt_r64_imm(dst, srcp.ireg(), bitp.immediate()); // bt srcp,bitp
}
else
{
if (srcp.is_memory())
emit_bt_m64_r64(dst, MABS(srcp.memory()), REG_ECX); // bt [srcp],ecx
else if (srcp.is_int_register())
emit_bt_r64_r64(dst, srcp.ireg(), REG_ECX); // bt srcp,ecx
}
}
}
//-------------------------------------------------
// op_set - process a SET opcode
//-------------------------------------------------
void drcbe_x64::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
// 32-bit form
if (inst.size() == 4)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
// 64-bit form
else if (inst.size() == 8)
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
//-------------------------------------------------
// op_mov - process a MOV opcode
//-------------------------------------------------
void drcbe_x64::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() != 0 && 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() != 0 && 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_m64_r64(dst, MABS(dstp.memory()), srcp.ireg()); // mov [dstp],srcp
// immediate to memory
else if (dstp.is_memory() && srcp.is_immediate() && short_immediate(srcp.immediate()))
emit_mov_m64_imm(dst, MABS(dstp.memory()), srcp.immediate()); // mov [dstp],srcp
// conditional memory to register
else if (inst.condition() != 0 && dstp.is_int_register() && srcp.is_memory())
{
dst = savedst;
skip.target = NULL;
emit_cmovcc_r64_m64(dst, X86_CONDITION(inst.condition()), dstp.ireg(), MABS(srcp.memory()));
// cmovcc dstp,[srcp]
}
// conditional register to register
else if (inst.condition() != 0 && dstp.is_int_register() && srcp.is_int_register())
{
dst = savedst;
skip.target = NULL;
emit_cmovcc_r64_r64(dst, X86_CONDITION(inst.condition()), dstp.ireg(), srcp.ireg());
// cmovcc dstp,srcp
}
// general case
else
{
emit_mov_r64_p64_keepflags(dst, dstreg, srcp); // mov dstreg,srcp
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
// resolve the jump
if (skip.target != NULL)
resolve_link(dst, skip);
}
//-------------------------------------------------
// op_sext - process a SEXT opcode
//-------------------------------------------------
void drcbe_x64::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 = dstp.select_register(REG_EAX);
// 32-bit form
if (inst.size() == 4)
{
// 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)
emit_mov_r32_r32(dst, dstreg, srcp.ireg()); // mov dstreg,srcp
}
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
if (inst.flags() != 0)
emit_test_r32_r32(dst, dstreg, dstreg); // test dstreg,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
// general case
if (srcp.is_memory())
{
if (sizep.size() == SIZE_BYTE)
emit_movsx_r64_m8(dst, dstreg, MABS(srcp.memory())); // movsx dstreg,[srcp]
else if (sizep.size() == SIZE_WORD)
emit_movsx_r64_m16(dst, dstreg, MABS(srcp.memory())); // movsx dstreg,[srcp]
else if (sizep.size() == SIZE_DWORD)
emit_movsxd_r64_m32(dst, dstreg, MABS(srcp.memory())); // movsxd dstreg,[srcp]
else if (sizep.size() == SIZE_QWORD)
emit_mov_r64_m64(dst, dstreg, MABS(srcp.memory())); // mov dstreg,[srcp]
}
else if (srcp.is_int_register())
{
if (sizep.size() == SIZE_BYTE)
emit_movsx_r64_r8(dst, dstreg, srcp.ireg()); // movsx dstreg,srcp
else if (sizep.size() == SIZE_WORD)
emit_movsx_r64_r16(dst, dstreg, srcp.ireg()); // movsx dstreg,srcp
else if (sizep.size() == SIZE_DWORD)
emit_movsxd_r64_r32(dst, dstreg, srcp.ireg()); // movsxd dstreg,srcp
else if (sizep.size() == SIZE_QWORD)
emit_mov_r64_r64(dst, dstreg, srcp.ireg()); // mov dstreg,srcp
}
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
if (inst.flags() != 0)
emit_test_r64_r64(dst, dstreg, dstreg); // test dstreg,dstreg
}
}
//-------------------------------------------------
// op_roland - process an ROLAND opcode
//-------------------------------------------------
void drcbe_x64::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, srcp); // mov dstreg,srcp
emit_rol_r64_p64(dst, dstreg, shiftp, inst); // rol dstreg,shiftp
emit_and_r64_p64(dst, dstreg, maskp, inst); // and dstreg,maskp
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
//-------------------------------------------------
// op_rolins - process an ROLINS opcode
//-------------------------------------------------
void drcbe_x64::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_RAX, srcp); // mov rax,srcp
emit_mov_r64_p64(dst, REG_RDX, maskp); // mov rdx,maskp
emit_rol_r64_p64(dst, REG_RAX, shiftp, inst); // rol rax,shiftp
emit_mov_r64_p64(dst, dstreg, dstp); // mov dstreg,dstp
emit_and_r64_r64(dst, REG_RAX, REG_RDX); // and eax,rdx
emit_not_r64(dst, REG_RDX); // not rdx
emit_and_r64_r64(dst, dstreg, REG_RDX); // and dstreg,rdx
emit_or_r64_r64(dst, dstreg, REG_RAX); // or dstreg,rax
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
//-------------------------------------------------
// op_add - process a ADD opcode
//-------------------------------------------------
void drcbe_x64::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
// reg = reg + imm
else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && short_immediate(src2p.immediate()) && inst.flags() == 0)
emit_lea_r64_m64(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_r64_m64(dst, dstp.ireg(), MBISD(src1p.ireg(), src2p.ireg(), 1, 0));// lea dstp,[src1p+src2p]
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_add_r64_p64(dst, dstreg, src2p, inst); // add dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_addc - process a ADDC opcode
//-------------------------------------------------
void drcbe_x64::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, src1p); // mov dstreg,src1p
emit_adc_r64_p64(dst, dstreg, src2p, inst); // adc dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_sub - process a SUB opcode
//-------------------------------------------------
void drcbe_x64::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
// reg = reg - imm
else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && short_immediate(src2p.immediate()) && inst.flags() == 0)
emit_lea_r64_m64(dst, dstp.ireg(), MBD(src1p.ireg(), -src2p.immediate())); // lea dstp,[src1p-src2p]
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_sub_r64_p64(dst, dstreg, src2p, inst); // sub dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_subc - process a SUBC opcode
//-------------------------------------------------
void drcbe_x64::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, src1p); // mov dstreg,src1p
emit_sbb_r64_p64(dst, dstreg, src2p, inst); // sbb dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_cmp - process a CMP opcode
//-------------------------------------------------
void drcbe_x64::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
{
// memory versus anything
if (src1p.is_memory())
emit_cmp_m64_p64(dst, MABS(src1p.memory()), src2p, inst); // cmp [dstp],src2p
// general case
else
{
if (src1p.is_immediate())
emit_mov_r64_imm(dst, src1reg, src1p.immediate()); // mov src1reg,imm
emit_cmp_r64_p64(dst, src1reg, src2p, inst); // cmp src1reg,src2p
}
}
}
//-------------------------------------------------
// op_mulu - process a MULU opcode
//-------------------------------------------------
void drcbe_x64::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_r64(dst, REG_RAX); // pop rax
emit_and_m64_imm(dst, MBD(REG_RSP, 0), ~0x84); // and [rsp],~0x84
emit_or_m64_r64(dst, MBD(REG_RSP, 0), REG_RAX); // or [rsp],rax
emit_popf(dst); // popf
}
}
}
}
// 64-bit form
else if (inst.size() == 8)
{
// general case
emit_mov_r64_p64(dst, REG_RAX, src1p); // mov rax,src1p
if (src2p.is_memory())
emit_mul_m64(dst, MABS(src2p.memory())); // mul [src2p]
else if (src2p.is_int_register())
emit_mul_r64(dst, src2p.ireg()); // mul src2p
else if (src2p.is_immediate())
{
emit_mov_r64_imm(dst, REG_RDX, src2p.immediate()); // mov rdx,src2p
emit_mul_r64(dst, REG_RDX); // mul rdx
}
emit_mov_p64_r64(dst, dstp, REG_RAX); // mov dstp,rax
if (compute_hi)
emit_mov_p64_r64(dst, edstp, REG_RDX); // mov edstp,rdx
// compute flags
if (inst.flags() != 0)
{
if (zsflags != 0)
{
if (vflag)
emit_pushf(dst); // pushf
if (compute_hi)
{
if (zsflags == FLAG_Z)
emit_or_r64_r64(dst, REG_RDX, REG_RAX); // or rdx,rax
else if (zsflags == FLAG_S)
emit_test_r64_r64(dst, REG_RDX, REG_RDX); // test rdx,rdx
else
{
emit_mov_r32_r32(dst, REG_ECX, REG_EAX); // mov ecx,eax
emit_shr_r64_imm(dst, REG_RAX, 32); // shr rax,32
emit_or_r64_r64(dst, REG_RDX, REG_RCX); // or rdx,rcx
emit_or_r64_r64(dst, REG_RDX, REG_RAX); // or rdx,rax
}
}
else
emit_test_r64_r64(dst, REG_RAX, REG_RAX); // test rax,rax
// we rely on the fact that OF is cleared by all logical operations above
if (vflag)
{
emit_pushf(dst); // pushf
emit_pop_r64(dst, REG_RAX); // pop rax
emit_and_m64_imm(dst, MBD(REG_RSP, 0), ~0x84); // and [rsp],~0x84
emit_or_m64_r64(dst, MBD(REG_RSP, 0), REG_RAX); // or [rsp],rax
emit_popf(dst); // popf
}
}
}
}
}
//-------------------------------------------------
// op_muls - process a MULS opcode
//-------------------------------------------------
void drcbe_x64::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 (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_r64(dst, REG_RAX); // pop rax
emit_and_m64_imm(dst, MBD(REG_RSP, 0), ~0x84); // and [rsp],~0x84
emit_or_m64_r64(dst, MBD(REG_RSP, 0), REG_RAX); // or [rsp],rax
emit_popf(dst); // popf
}
}
}
}
// 64-bit form
else if (inst.size() == 8)
{
// 64-bit destination with memory/immediate or register/immediate
if (!compute_hi && !src1p.is_immediate() && src2p.is_immediate() && short_immediate(src2p.immediate()))
{
if (src1p.is_memory())
emit_imul_r64_m64_imm(dst, REG_RAX, MABS(src1p.memory()), src2p.immediate());// imul rax,[src1p],src2p
else if (src1p.is_int_register())
emit_imul_r64_r64_imm(dst, REG_RAX, src1p.ireg(), src2p.immediate()); // imul rax,src1p,src2p
emit_mov_p64_r64(dst, dstp, REG_RAX); // mov dstp,rax
}
// 64-bit destination, general case
else if (!compute_hi)
{
emit_mov_r64_p64(dst, REG_RAX, src1p); // mov rax,src1p
if (src2p.is_memory())
emit_imul_r64_m64(dst, REG_RAX, MABS(src2p.memory())); // imul rax,[src2p]
else if (src2p.is_int_register())
emit_imul_r64_r64(dst, REG_RAX, src2p.ireg()); // imul rax,src2p
emit_mov_p64_r64(dst, dstp, REG_RAX); // mov dstp,rax
}
// 128-bit destination, general case
else
{
emit_mov_r64_p64(dst, REG_RAX, src1p); // mov rax,src1p
if (src2p.is_memory())
emit_imul_m64(dst, MABS(src2p.memory())); // imul [src2p]
else if (src2p.is_int_register())
emit_imul_r64(dst, src2p.ireg()); // imul src2p
else if (src2p.is_immediate())
{
emit_mov_r64_imm(dst, REG_RDX, src2p.immediate()); // mov rdx,src2p
emit_imul_r64(dst, REG_RDX); // imul rdx
}
emit_mov_p64_r64(dst, dstp, REG_RAX); // mov dstp,rax
emit_mov_p64_r64(dst, edstp, REG_RDX); // mov edstp,rdx
}
// compute flags
if (inst.flags() != 0)
{
if (zsflags != 0)
{
if (vflag)
emit_pushf(dst); // pushf
if (compute_hi)
{
if (zsflags == FLAG_Z)
emit_or_r64_r64(dst, REG_RDX, REG_RAX); // or rdx,rax
else if (zsflags == FLAG_S)
emit_test_r64_r64(dst, REG_RDX, REG_RDX); // test rdx,rdx
else
{
emit_mov_r32_r32(dst, REG_ECX, REG_EAX); // mov ecx,eax
emit_shr_r64_imm(dst, REG_RAX, 32); // shr rax,32
emit_or_r64_r64(dst, REG_RDX, REG_RCX); // or rdx,rcx
emit_or_r64_r64(dst, REG_RDX, REG_RAX); // or rdx,rax
}
}
else
emit_test_r64_r64(dst, REG_RAX, REG_RAX); // test rax,rax
// we rely on the fact that OF is cleared by all logical operations above
if (vflag)
{
emit_pushf(dst); // pushf
emit_pop_r64(dst, REG_RAX); // pop rax
emit_and_m64_imm(dst, MBD(REG_RSP, 0), ~0x84); // and [rsp],~0x84
emit_or_m64_r64(dst, MBD(REG_RSP, 0), REG_RAX); // or [rsp],rax
emit_popf(dst); // popf
}
}
}
}
}
//-------------------------------------------------
// op_divu - process a DIVU opcode
//-------------------------------------------------
void drcbe_x64::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
resolve_link(dst, skip); // skip:
}
// 64-bit form
else if (inst.size() == 8)
{
// general case
emit_mov_r64_p64(dst, REG_RCX, src2p); // mov rcx,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_jrcxz_link(dst, skip); // jrcxz skip
emit_mov_r64_p64(dst, REG_RAX, src1p); // mov rax,src1p
emit_xor_r32_r32(dst, REG_EDX, REG_EDX); // xor edx,edx
emit_div_r64(dst, REG_RCX); // div rcx
emit_mov_p64_r64(dst, dstp, REG_RAX); // mov dstp,rax
if (compute_rem)
emit_mov_p64_r64(dst, edstp, REG_RDX); // mov edstp,rdx
if (inst.flags() != 0)
emit_test_r64_r64(dst, REG_RAX, REG_RAX); // test eax,eax
resolve_link(dst, skip); // skip:
}
}
//-------------------------------------------------
// op_divs - process a DIVS opcode
//-------------------------------------------------
void drcbe_x64::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
resolve_link(dst, skip); // skip:
}
// 64-bit form
else if (inst.size() == 8)
{
// general case
emit_mov_r64_p64(dst, REG_RCX, src2p); // mov rcx,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_jrcxz_link(dst, skip); // jrcxz skip
emit_mov_r64_p64(dst, REG_RAX, src1p); // mov rax,src1p
emit_cqo(dst); // cqo
emit_idiv_r64(dst, REG_RCX); // idiv rcx
emit_mov_p64_r64(dst, dstp, REG_RAX); // mov dstp,rax
if (compute_rem)
emit_mov_p64_r64(dst, edstp, REG_RDX); // mov edstp,rdx
if (inst.flags() != 0)
emit_test_r64_r64(dst, REG_RAX, REG_RAX); // test eax,eax
resolve_link(dst, skip); // skip:
}
}
//-------------------------------------------------
// op_and - process a AND opcode
//-------------------------------------------------
void drcbe_x64::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_p64_r64(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_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
// AND with immediate 0xffffffff
else if (src2p.is_immediate_value(0xffffffff) && inst.flags() == 0)
{
if (dstp.is_int_register() && src1p == dstp)
emit_mov_r32_r32(dst, dstreg, dstreg); // mov dstreg,dstreg
else
{
emit_mov_r32_p32(dst, dstreg, src1p); // mov dstreg,src1p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_and_r64_p64(dst, dstreg, src2p, inst); // and dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_test - process a TEST opcode
//-------------------------------------------------
void drcbe_x64::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 [src1p],src2p
// general case
else
{
emit_mov_r64_p64(dst, src1reg, src1p); // mov src1reg,src1p
emit_test_r64_p64(dst, src1reg, src2p, inst); // test src1reg,src2p
}
}
}
//-------------------------------------------------
// op_or - process a OR opcode
//-------------------------------------------------
void drcbe_x64::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, src1p); // mov dstreg,src1p
emit_or_r64_p64(dst, dstreg, src2p, inst); // or dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_xor - process a XOR opcode
//-------------------------------------------------
void drcbe_x64::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, src1p); // mov dstreg,src1p
emit_xor_r64_p64(dst, dstreg, src2p, inst); // xor dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_lzcnt - process a LZCNT opcode
//-------------------------------------------------
void drcbe_x64::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, x64emit::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, dstreg, srcp); // mov dstreg,src1p
emit_mov_r64_imm(dst, REG_RCX, 64 ^ 63); // mov rcx,64 ^ 63
emit_bsr_r64_r64(dst, dstreg, dstreg); // bsr dstreg,dstreg
emit_cmovcc_r64_r64(dst, x64emit::COND_Z, dstreg, REG_RCX); // cmovz dstreg,rcx
emit_xor_r32_imm(dst, dstreg, 63); // xor dstreg,63
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
//-------------------------------------------------
// op_bswap - process a BSWAP opcode
//-------------------------------------------------
void drcbe_x64::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_RAX);
// 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, dstreg, srcp); // mov dstreg,src1p
emit_bswap_r64(dst, dstreg); // bswap dstreg
if (inst.flags() != 0)
emit_test_r64_r64(dst, dstreg, dstreg); // test dstreg,dstreg
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
//-------------------------------------------------
// op_shl - process a SHL opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_shl_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // shl [dstp],src2p
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_shl_r64_p64(dst, dstreg, src2p, inst); // shl dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_shr - process a SHR opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_shr_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // shr [dstp],src2p
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_shr_r64_p64(dst, dstreg, src2p, inst); // shr dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_sar - process a SAR opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_sar_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // sar [dstp],src2p
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_sar_r64_p64(dst, dstreg, src2p, inst); // sar dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_rol - process a rol opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_rol_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // rol [dstp],src2p
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_rol_r64_p64(dst, dstreg, src2p, inst); // rol dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_ror - process a ROR opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_ror_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // ror [dstp],src2p
// general case
else
{
emit_mov_r64_p64(dst, dstreg, src1p); // mov dstreg,src1p
emit_ror_r64_p64(dst, dstreg, src2p, inst); // ror dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_rolc - process a ROLC opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_rcl_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // rcl [dstp],src2p
// general case
else
{
emit_mov_r64_p64_keepflags(dst, dstreg, src1p); // mov dstreg,src1p
emit_rcl_r64_p64(dst, dstreg, src2p, inst); // rcl dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
//-------------------------------------------------
// op_rorc - process a RORC opcode
//-------------------------------------------------
void drcbe_x64::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)
{
// dstp == src1p in memory
if (dstp.is_memory() && dstp == src1p)
emit_rcr_m64_p64(dst, MABS(dstp.memory()), src2p, inst); // rcr [dstp],src2p
// general case
else
{
emit_mov_r64_p64_keepflags(dst, dstreg, src1p); // mov dstreg,src1p
emit_rcr_r64_p64(dst, dstreg, src2p, inst); // rcr dstreg,src2p
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
}
}
}
/***************************************************************************
FLOATING POINT OPERATIONS
***************************************************************************/
//-------------------------------------------------
// op_fload - process a FLOAD opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// determine the pointer base
INT32 baseoffs;
int basereg = get_base_register_and_offset(dst, basep.memory(), REG_RDX, baseoffs);
// 32-bit form
if (inst.size() == 4)
{
if (indp.is_immediate())
emit_movss_r128_m32(dst, dstreg, MBD(basereg, baseoffs + 4*indp.immediate())); // movss dstreg,[basep + 4*indp]
else
{
int indreg = indp.select_register(REG_ECX);
emit_mov_r32_p32(dst, indreg, indp); // mov indreg,indp
emit_movss_r128_m32(dst, dstreg, MBISD(basereg, indreg, 4, baseoffs)); // movss dstreg,[basep + 4*indp]
}
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
if (indp.is_immediate())
emit_movsd_r128_m64(dst, dstreg, MBD(basereg, baseoffs + 8*indp.immediate())); // movsd dstreg,[basep + 8*indp]
else
{
int indreg = indp.select_register(REG_ECX);
emit_mov_r32_p32(dst, indreg, indp); // mov indreg,indp
emit_movsd_r128_m64(dst, dstreg, MBISD(basereg, indreg, 8, baseoffs)); // movsd dstreg,[basep + 8*indp]
}
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fstore - process a FSTORE opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int srcreg = srcp.select_register(REG_XMM0);
// determine the pointer base
INT32 baseoffs;
int basereg = get_base_register_and_offset(dst, basep.memory(), REG_RDX, baseoffs);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, srcreg, srcp); // movss srcreg,srcp
if (indp.is_immediate())
emit_movss_m32_r128(dst, MBD(basereg, baseoffs + 4*indp.immediate()), srcreg); // movss [basep + 4*indp],srcreg
else
{
int indreg = indp.select_register(REG_ECX);
emit_mov_r32_p32(dst, indreg, indp); // mov indreg,indp
emit_movss_m32_r128(dst, MBISD(basereg, indreg, 4, baseoffs), srcreg); // movss [basep + 4*indp],srcreg
}
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, srcreg, srcp); // movsd srcreg,srcp
if (indp.is_immediate())
emit_movsd_m64_r128(dst, MBD(basereg, baseoffs + 8*indp.immediate()), srcreg); // movsd [basep + 8*indp],srcreg
else
{
int indreg = indp.select_register(REG_ECX);
emit_mov_r32_p32(dst, indreg, indp); // mov indreg,indp
emit_movsd_m64_r128(dst, MBISD(basereg, indreg, 8, baseoffs), srcreg); // movsd [basep + 8*indp],srcreg
}
}
}
//-------------------------------------------------
// op_fread - process a FREAD opcode
//-------------------------------------------------
void drcbe_x64::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_r64_imm(dst, REG_PARAM1, (FPTR)(m_space[spacep.space()])); // mov param1,space
emit_mov_r32_p32(dst, REG_PARAM2, addrp); // mov param2,addrp
if (inst.size() == 4)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacep.space()].read_dword); // call read_dword
else if (inst.size() == 8)
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacep.space()].read_qword); // call read_qword
// store result
if (inst.size() == 4)
{
if (dstp.is_memory())
emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX); // mov [dstp],eax
else if (dstp.is_float_register())
emit_movd_r128_r32(dst, dstp.freg(), REG_EAX); // movd dstp,eax
}
else if (inst.size() == 8)
{
if (dstp.is_memory())
emit_mov_m64_r64(dst, MABS(dstp.memory()), REG_RAX); // mov [dstp],rax
else if (dstp.is_float_register())
emit_movq_r128_r64(dst, dstp.freg(), REG_RAX); // movq dstp,rax
}
}
//-------------------------------------------------
// op_fwrite - process a FWRITE opcode
//-------------------------------------------------
void drcbe_x64::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());
// general case
emit_mov_r64_imm(dst, REG_PARAM1, (FPTR)(m_space[spacep.space()])); // mov param1,space
emit_mov_r32_p32(dst, REG_PARAM2, addrp); // mov param21,addrp
// 32-bit form
if (inst.size() == 4)
{
if (srcp.is_memory())
emit_mov_r32_m32(dst, REG_PARAM3, MABS(srcp.memory())); // mov param3,[srcp]
else if (srcp.is_float_register())
emit_movd_r32_r128(dst, REG_PARAM3, srcp.freg()); // movd param3,srcp
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacep.space()].write_dword); // call write_dword
}
// 64-bit form
else if (inst.size() == 8)
{
if (srcp.is_memory())
emit_mov_r64_m64(dst, REG_PARAM3, MABS(srcp.memory())); // mov param3,[srcp]
else if (srcp.is_float_register())
emit_movq_r64_r128(dst, REG_PARAM3, srcp.freg()); // movq param3,srcp
emit_smart_call_m64(dst, (x86code **)&m_accessors[spacep.space()].write_qword); // call write_qword
}
}
//-------------------------------------------------
// op_fmov - process a FMOV opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// 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)
{
emit_movss_r128_p32(dst, dstreg, srcp); // movss dstreg,srcp
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, dstreg, srcp); // movsd dstreg,srcp
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
// resolve the jump
if (inst.condition() != uml::COND_ALWAYS)
resolve_link(dst, skip); // skip:
}
//-------------------------------------------------
// op_ftoint - process a FTOINT opcode
//-------------------------------------------------
void drcbe_x64::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());
// pick a target register for the general case
int dstreg = dstp.select_register(REG_EAX);
// set rounding mode if necessary
if (roundp.rounding() != ROUND_DEFAULT && roundp.rounding() != ROUND_TRUNC)
{
emit_stmxcsr_m32(dst, MABS(&m_near.ssemodesave)); // stmxcsr [ssemodesave]
emit_ldmxcsr_m32(dst, MABS(&m_near.ssecontrol[roundp.rounding()])); // ldmxcsr fpcontrol[mode]
}
// 32-bit form
if (inst.size() == 4)
{
// 32-bit integer source
if (sizep.size() == SIZE_DWORD)
{
if (srcp.is_memory())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtss2si_r32_m32(dst, dstreg, MABS(srcp.memory())); // cvtss2si dstreg,[srcp]
else
emit_cvttss2si_r32_m32(dst, dstreg, MABS(srcp.memory())); // cvttss2si dstreg,[srcp]
}
else if (srcp.is_float_register())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtss2si_r32_r128(dst, dstreg, srcp.freg()); // cvtss2si dstreg,srcp
else
emit_cvttss2si_r32_r128(dst, dstreg, srcp.freg()); // cvttss2si dstreg,srcp
}
}
// 64-bit integer source
else if (sizep.size() == SIZE_QWORD)
{
if (srcp.is_memory())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtss2si_r64_m32(dst, dstreg, MABS(srcp.memory())); // cvtss2si dstreg,[srcp]
else
emit_cvttss2si_r64_m32(dst, dstreg, MABS(srcp.memory())); // cvttss2si dstreg,[srcp]
}
else if (srcp.is_float_register())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtss2si_r64_r128(dst, dstreg, srcp.freg()); // cvtss2si dstreg,srcp
else
emit_cvttss2si_r64_r128(dst, dstreg, srcp.freg()); // cvttss2si dstreg,srcp
}
}
}
// 64-bit form
else if (inst.size() == 8)
{
// 32-bit integer source
if (sizep.size() == SIZE_DWORD)
{
if (srcp.is_memory())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtsd2si_r32_m64(dst, dstreg, MABS(srcp.memory())); // cvtsd2si dstreg,[srcp]
else
emit_cvttsd2si_r32_m64(dst, dstreg, MABS(srcp.memory())); // cvttsd2si dstreg,[srcp]
}
else if (srcp.is_float_register())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtsd2si_r32_r128(dst, dstreg, srcp.freg()); // cvtsd2si dstreg,srcp
else
emit_cvttsd2si_r32_r128(dst, dstreg, srcp.freg()); // cvttsd2si dstreg,srcp
}
}
// 64-bit integer source
else if (sizep.size() == SIZE_QWORD)
{
if (srcp.is_memory())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtsd2si_r64_m64(dst, dstreg, MABS(srcp.memory())); // cvtsd2si dstreg,[srcp]
else
emit_cvttsd2si_r64_m64(dst, dstreg, MABS(srcp.memory())); // cvttsd2si dstreg,[srcp]
}
else if (srcp.is_float_register())
{
if (roundp.rounding() != ROUND_TRUNC)
emit_cvtsd2si_r64_r128(dst, dstreg, srcp.freg()); // cvtsd2si dstreg,srcp
else
emit_cvttsd2si_r64_r128(dst, dstreg, srcp.freg()); // cvttsd2si dstreg,srcp
}
}
}
// general case
if (sizep.size() == SIZE_DWORD)
emit_mov_p32_r32(dst, dstp, dstreg); // mov dstp,dstreg
else
emit_mov_p64_r64(dst, dstp, dstreg); // mov dstp,dstreg
// restore rounding mode
if (roundp.rounding() != ROUND_DEFAULT && roundp.rounding() != ROUND_TRUNC)
emit_ldmxcsr_m32(dst, MABS(&m_near.ssemodesave)); // ldmxcsr [ssemodesave]
}
//-------------------------------------------------
// op_ffrint - process a FFRINT opcode
//-------------------------------------------------
void drcbe_x64::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());
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// 32-bit form
if (inst.size() == 4)
{
// 32-bit integer source
if (sizep.size() == SIZE_DWORD)
{
if (srcp.is_memory())
emit_cvtsi2ss_r128_m32(dst, dstreg, MABS(srcp.memory())); // cvtsi2ss dstreg,[srcp]
else
{
int srcreg = srcp.select_register(REG_EAX);
emit_mov_r32_p32(dst, srcreg, srcp); // mov srcreg,srcp
emit_cvtsi2ss_r128_r32(dst, dstreg, srcreg); // cvtsi2ss dstreg,srcreg
}
}
// 64-bit integer source
else
{
if (srcp.is_memory())
emit_cvtsi2ss_r128_m64(dst, dstreg, MABS(srcp.memory())); // cvtsi2ss dstreg,[srcp]
else
{
int srcreg = srcp.select_register(REG_RAX);
emit_mov_r64_p64(dst, srcreg, srcp); // mov srcreg,srcp
emit_cvtsi2ss_r128_r64(dst, dstreg, srcreg); // cvtsi2ss dstreg,srcreg
}
}
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
// 32-bit integer source
if (sizep.size() == SIZE_DWORD)
{
if (srcp.is_memory())
emit_cvtsi2sd_r128_m32(dst, dstreg, MABS(srcp.memory())); // cvtsi2sd dstreg,[srcp]
else
{
int srcreg = srcp.select_register(REG_EAX);
emit_mov_r32_p32(dst, srcreg, srcp); // mov srcreg,srcp
emit_cvtsi2sd_r128_r32(dst, dstreg, srcreg); // cvtsi2sd dstreg,srcreg
}
}
// 64-bit integer source
else
{
if (srcp.is_memory())
emit_cvtsi2sd_r128_m64(dst, dstreg, MABS(srcp.memory())); // cvtsi2sd dstreg,[srcp]
else
{
int srcreg = srcp.select_register(REG_EAX);
emit_mov_r64_p64(dst, srcreg, srcp); // mov srcreg,srcp
emit_cvtsi2sd_r128_r64(dst, dstreg, srcreg); // cvtsi2sd dstreg,srcreg
}
}
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_ffrflt - process a FFRFLT opcode
//-------------------------------------------------
void drcbe_x64::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());
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// single-to-double
if (inst.size() == 8 && sizep.size() == SIZE_DWORD)
{
if (srcp.is_memory())
emit_cvtss2sd_r128_m32(dst, dstreg, MABS(srcp.memory())); // cvtss2sd dstreg,[srcp]
else if (srcp.is_float_register())
emit_cvtss2sd_r128_r128(dst, dstreg, srcp.freg()); // cvtss2sd dstreg,srcp
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
// double-to-single
else if (inst.size() == 4 && sizep.size() == SIZE_QWORD)
{
if (srcp.is_memory())
emit_cvtsd2ss_r128_m64(dst, dstreg, MABS(srcp.memory())); // cvtsd2ss dstreg,[srcp]
else if (srcp.is_float_register())
emit_cvtsd2ss_r128_r128(dst, dstreg, srcp.freg()); // cvtsd2ss dstreg,srcp
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
}
//-------------------------------------------------
// op_frnds - process a FRNDS opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// 64-bit form
if (srcp.is_memory())
emit_cvtsd2ss_r128_m64(dst, dstreg, MABS(srcp.memory())); // cvtsd2ss dstreg,[srcp]
else if (srcp.is_float_register())
emit_cvtsd2ss_r128_r128(dst, dstreg, srcp.freg()); // cvtsd2ss dstreg,srcp
emit_cvtss2sd_r128_r128(dst, dstreg, dstreg); // cvtss2sd dstreg,dstreg
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
//-------------------------------------------------
// op_fadd - process a FADD opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0, src2p);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, dstreg, src1p); // movss dstreg,src1p
if (src2p.is_memory())
emit_addss_r128_m32(dst, dstreg, MABS(src2p.memory())); // addss dstreg,[src2p]
else if (src2p.is_float_register())
emit_addss_r128_r128(dst, dstreg, src2p.freg()); // addss dstreg,src2p
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, dstreg, src1p); // movsd dstreg,src1p
if (src2p.is_memory())
emit_addsd_r128_m64(dst, dstreg, MABS(src2p.memory())); // addsd dstreg,[src2p]
else if (src2p.is_float_register())
emit_addsd_r128_r128(dst, dstreg, src2p.freg()); // addsd dstreg,src2p
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fsub - process a FSUB opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0, src2p);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, dstreg, src1p); // movss dstreg,src1p
if (src2p.is_memory())
emit_subss_r128_m32(dst, dstreg, MABS(src2p.memory())); // subss dstreg,[src2p]
else if (src2p.is_float_register())
emit_subss_r128_r128(dst, dstreg, src2p.freg()); // subss dstreg,src2p
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, dstreg, src1p); // movsd dstreg,src1p
if (src2p.is_memory())
emit_subsd_r128_m64(dst, dstreg, MABS(src2p.memory())); // subsd dstreg,[src2p]
else if (src2p.is_float_register())
emit_subsd_r128_r128(dst, dstreg, src2p.freg()); // subsd dstreg,src2p
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fcmp - process a FCMP opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int src1reg = src1p.select_register(REG_XMM0);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, src1reg, src1p); // movss src1reg,src1p
if (src2p.is_memory())
emit_comiss_r128_m32(dst, src1reg, MABS(src2p.memory())); // comiss src1reg,[src2p]
else if (src2p.is_float_register())
emit_comiss_r128_r128(dst, src1reg, src2p.freg()); // comiss src1reg,src2p
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, src1reg, src1p); // movsd src1reg,src1p
if (src2p.is_memory())
emit_comisd_r128_m64(dst, src1reg, MABS(src2p.memory())); // comisd src1reg,[src2p]
else if (src2p.is_float_register())
emit_comisd_r128_r128(dst, src1reg, src2p.freg()); // comisd src1reg,src2p
}
}
//-------------------------------------------------
// op_fmul - process a FMUL opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0, src2p);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, dstreg, src1p); // movss dstreg,src1p
if (src2p.is_memory())
emit_mulss_r128_m32(dst, dstreg, MABS(src2p.memory())); // mulss dstreg,[src2p]
else if (src2p.is_float_register())
emit_mulss_r128_r128(dst, dstreg, src2p.freg()); // mulss dstreg,src2p
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, dstreg, src1p); // movsd dstreg,src1p
if (src2p.is_memory())
emit_mulsd_r128_m64(dst, dstreg, MABS(src2p.memory())); // mulsd dstreg,[src2p]
else if (src2p.is_float_register())
emit_mulsd_r128_r128(dst, dstreg, src2p.freg()); // mulsd dstreg,src2p
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fdiv - process a FDIV opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0, src2p);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, dstreg, src1p); // movss dstreg,src1p
if (src2p.is_memory())
emit_divss_r128_m32(dst, dstreg, MABS(src2p.memory())); // divss dstreg,[src2p]
else if (src2p.is_float_register())
emit_divss_r128_r128(dst, dstreg, src2p.freg()); // divss dstreg,src2p
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, dstreg, src1p); // movsd dstreg,src1p
if (src2p.is_memory())
emit_divsd_r128_m64(dst, dstreg, MABS(src2p.memory())); // divsd dstreg,[src2p]
else if (src2p.is_float_register())
emit_divsd_r128_r128(dst, dstreg, src2p.freg()); // divsd dstreg,src2p
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fneg - process a FNEG opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0, srcp);
// 32-bit form
if (inst.size() == 4)
{
emit_xorps_r128_r128(dst, dstreg, dstreg); // xorps dstreg,dstreg
if (srcp.is_memory())
emit_subss_r128_m32(dst, dstreg, MABS(srcp.memory())); // subss dstreg,[srcp]
else if (srcp.is_float_register())
emit_subss_r128_r128(dst, dstreg, srcp.freg()); // subss dstreg,srcp
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_xorpd_r128_r128(dst, dstreg, dstreg); // xorpd dstreg,dstreg
if (srcp.is_memory())
emit_subsd_r128_m64(dst, dstreg, MABS(srcp.memory())); // subsd dstreg,[srcp]
else if (srcp.is_float_register())
emit_subsd_r128_r128(dst, dstreg, srcp.freg()); // subsd dstreg,srcp
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fabs - process a FABS opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0, srcp);
// 32-bit form
if (inst.size() == 4)
{
emit_movss_r128_p32(dst, dstreg, srcp); // movss dstreg,srcp
emit_andps_r128_m128(dst, dstreg, MABS(m_absmask32)); // andps dstreg,[absmask32]
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
emit_movsd_r128_p64(dst, dstreg, srcp); // movsd dstreg,srcp
emit_andpd_r128_m128(dst, dstreg, MABS(m_absmask64)); // andpd dstreg,[absmask64]
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_fsqrt - process a FSQRT opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// 32-bit form
if (inst.size() == 4)
{
if (srcp.is_memory())
emit_sqrtss_r128_m32(dst, dstreg, MABS(srcp.memory())); // sqrtss dstreg,[srcp]
else if (srcp.is_float_register())
emit_sqrtss_r128_r128(dst, dstreg, srcp.freg()); // sqrtss dstreg,srcp
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
if (srcp.is_memory())
emit_sqrtsd_r128_m64(dst, dstreg, MABS(srcp.memory())); // sqrtsd dstreg,[srcp]
else if (srcp.is_float_register())
emit_sqrtsd_r128_r128(dst, dstreg, srcp.freg()); // sqrtsd dstreg,srcp
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}
//-------------------------------------------------
// op_frecip - process a FRECIP opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// 32-bit form
if (inst.size() == 4)
{
if (USE_RCPSS_FOR_SINGLES)
{
if (srcp.is_memory())
emit_rcpss_r128_m32(dst, dstreg, MABS(srcp.memory())); // rcpss dstreg,[srcp]
else if (srcp.is_float_register())
emit_rcpss_r128_r128(dst, dstreg, srcp.freg()); // rcpss dstreg,srcp
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
else
{
emit_movss_r128_m32(dst, REG_XMM1, MABS(&m_near.single1)); // movss xmm1,1.0
if (srcp.is_memory())
emit_divss_r128_m32(dst, REG_XMM1, MABS(srcp.memory())); // divss xmm1,[srcp]
else if (srcp.is_float_register())
emit_divss_r128_r128(dst, REG_XMM1, srcp.freg()); // divss xmm1,srcp
emit_movss_p32_r128(dst, dstp, REG_XMM1); // movss dstp,xmm1
}
}
// 64-bit form
else if (inst.size() == 8)
{
if (USE_RCPSS_FOR_DOUBLES)
{
if (srcp.is_memory())
emit_cvtsd2ss_r128_m64(dst, dstreg, MABS(srcp.memory())); // cvtsd2ss dstreg,[srcp]
else if (srcp.is_float_register())
emit_cvtsd2ss_r128_r128(dst, dstreg, srcp.freg()); // cvtsd2ss dstreg,srcp
emit_rcpss_r128_r128(dst, dstreg, dstreg); // rcpss dstreg,dstreg
emit_cvtss2sd_r128_r128(dst, dstreg, dstreg); // cvtss2sd dstreg,dstreg
emit_movsd_p64_r128(dst, dstp, REG_XMM1); // movsd dstp,dstreg
}
else
{
emit_movsd_r128_m64(dst, REG_XMM1, MABS(&m_near.double1)); // movsd xmm1,1.0
if (srcp.is_memory())
emit_divsd_r128_m64(dst, REG_XMM1, MABS(srcp.memory())); // divsd xmm1,[srcp]
else if (srcp.is_float_register())
emit_divsd_r128_r128(dst, REG_XMM1, srcp.freg()); // divsd xmm1,srcp
emit_movsd_p64_r128(dst, dstp, REG_XMM1); // movsd dstp,xmm1
}
}
}
//-------------------------------------------------
// op_frsqrt - process a FRSQRT opcode
//-------------------------------------------------
void drcbe_x64::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);
// pick a target register for the general case
int dstreg = dstp.select_register(REG_XMM0);
// 32-bit form
if (inst.size() == 4)
{
if (USE_RSQRTSS_FOR_SINGLES)
{
if (srcp.is_memory())
emit_rsqrtss_r128_m32(dst, dstreg, MABS(srcp.memory())); // rsqrtss dstreg,[srcp]
else if (srcp.is_float_register())
emit_rsqrtss_r128_r128(dst, dstreg, srcp.freg()); // rsqrtss dstreg,srcp
}
else
{
if (srcp.is_memory())
emit_sqrtss_r128_m32(dst, REG_XMM1, MABS(srcp.memory())); // sqrtss xmm1,[srcp]
else if (srcp.is_float_register())
emit_sqrtss_r128_r128(dst, REG_XMM1, srcp.freg()); // sqrtss xmm1,srcp
emit_movss_r128_m32(dst, dstreg, MABS(&m_near.single1)); // movss dstreg,1.0
emit_divss_r128_r128(dst, dstreg, REG_XMM1); // divss dstreg,xmm1
}
emit_movss_p32_r128(dst, dstp, dstreg); // movss dstp,dstreg
}
// 64-bit form
else if (inst.size() == 8)
{
if (USE_RSQRTSS_FOR_DOUBLES)
{
if (srcp.is_memory())
emit_cvtsd2ss_r128_m64(dst, dstreg, MABS(srcp.memory())); // cvtsd2ss dstreg,[srcp]
else if (srcp.is_float_register())
emit_cvtsd2ss_r128_r128(dst, dstreg, srcp.freg()); // cvtsd2ss dstreg,srcp
emit_rsqrtss_r128_r128(dst, dstreg, dstreg); // rsqrtss dstreg,dstreg
emit_cvtss2sd_r128_r128(dst, dstreg, dstreg); // cvtss2sd dstreg,dstreg
}
else
{
if (srcp.is_memory())
emit_sqrtsd_r128_m64(dst, REG_XMM1, MABS(srcp.memory())); // sqrtsd xmm1,[srcp]
else if (srcp.is_float_register())
emit_sqrtsd_r128_r128(dst, REG_XMM1, srcp.freg()); // sqrtsd xmm1,srcp
emit_movsd_r128_m64(dst, dstreg, MABS(&m_near.double1)); // movsd dstreg,1.0
emit_divsd_r128_r128(dst, dstreg, REG_XMM1); // divsd dstreg,xmm1
}
emit_movsd_p64_r128(dst, dstp, dstreg); // movsd dstp,dstreg
}
}