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