summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2026-04-06 05:16:43 +1000
committer Vas Crabb <vas@vastheman.com>2026-04-06 05:16:43 +1000
commit7ff17615ba95d57befd8cf050b9a88bcb9aaf83c (patch)
tree2fcdb28a05180d9c2cd2632caa403960852faef3 /src/devices/cpu
parentd2e952322e057e42ab059035157c7136e0565727 (diff)
Cleaned up unsupportable legacy stuff:
cpu/drcbex86.cpp: Removed i686 recompiler back-end. Without a practical way to make Windows i686 builds, it's just going to rot. Also, x86-64 is now older than Pac-Man was when MAME was initially created. It's dead on the desktop tools: Removed aueffectutil - it's no longer useful. emu/video/rgbutil.cpp: Removed Altivec/VMX bilinear filtering implementation. PowerPC on desktop is dead. file/posixptty.cpp, sdl, sdl3, scripts: Removed support for SysV operating systems. They’re dead on the desktop. scripts: Removed outdated Lua compatibility macros. They weren't doing anything sice we updated to Lua 5.4 (the compatibility options changed, but we just left the old macros in place, which no longer had any effect).
Diffstat (limited to 'src/devices/cpu')
-rw-r--r--src/devices/cpu/drcbex86.cpp7716
-rw-r--r--src/devices/cpu/drcbex86.h34
-rw-r--r--src/devices/cpu/drcuml.cpp7
3 files changed, 2 insertions, 7755 deletions
diff --git a/src/devices/cpu/drcbex86.cpp b/src/devices/cpu/drcbex86.cpp
deleted file mode 100644
index c4fa610384b..00000000000
--- a/src/devices/cpu/drcbex86.cpp
+++ /dev/null
@@ -1,7716 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
-/***************************************************************************
-
- drcbex86.c
-
- 32-bit x86 back-end for the universal machine language.
-
-****************************************************************************
-
- Future improvements/changes:
-
- * Restore floating point environment when calling out to C code
-
- * Optimize to avoid unnecessary reloads
- - especially EDX for 64-bit operations
- - also FCMP/FLAGS has unnecessary PUSHF/POP EAX
-
- * Identify common pairs and optimize output
-
- * Convert SUB a,0,b to NEG
-
- * Optimize, e.g., and [r5],i0,$FF to use ebx as temporary register
- (avoid initial move) if i0 is not needed going forward
-
-****************************************************************************
-
- ---------------
- ABI/conventions
- ---------------
-
- Registers:
- EAX - volatile, function return value
- EBX - non-volatile
- ECX - volatile
- EDX - volatile, function return value (upper 32 bits)
- ESI - non-volatile
- EDI - non-volatile
- EBP - non-volatile
-
- FP stack - volatile
-
-
- ---------------
- Execution model
- ---------------
-
- Registers:
- EAX - scratch register
- EBX - maps to I0 (low 32 bits)
- ECX - scratch register
- EDX - scratch register
- ESI - maps to I1 (low 32 bits)
- EDI - maps to I2 (low 32 bits)
- EBP - maps to I3 (low 32 bits)
-
- FP stack - scratch registers
-
- Entry point:
- Assumes 1 parameter passed, which is the codeptr of the code
- to execute once the environment is set up.
-
- Exit point:
- Assumes exit value is in EAX.
-
- Entry stack:
- [esp] - return
- [esp+4] - input parameter (entry handle)
-
- Runtime stack:
- [esp] - param 0
- [esp+4] - param 1
- [esp+8] - param 2
- [esp+12] - param 3
- [esp+16] - param 4
- [esp+20] - alignment
- [esp+24] - alignment
- [esp+28] - saved ebp
- [esp+32] - saved edi
- [esp+36] - saved esi
- [esp+40] - saved ebx
- [esp+44] - ret
- [esp+48] - input parameter (entry handle)
-
-**************************************************************************/
-
-#include "emu.h"
-#include "drcbex86.h"
-
-#include "drcbeut.h"
-#include "x86log.h"
-
-#include "debug/debugcpu.h"
-#include "emuopts.h"
-
-#include "mfpresolve.h"
-
-#include "asmjit/src/asmjit/x86.h"
-
-#include <cstddef>
-#include <cstdio>
-#include <cstdlib>
-#include <locale>
-#include <string>
-#include <sstream>
-#include <type_traits>
-#include <utility>
-
-
-namespace drc {
-
-namespace {
-
-using namespace uml;
-
-using namespace asmjit;
-using namespace asmjit::x86;
-
-
-//**************************************************************************
-// DEBUGGING
-//**************************************************************************
-
-#define LOG_HASHJMPS (0)
-
-
-
-//**************************************************************************
-// CONSTANTS
-//**************************************************************************
-
-#ifdef _WIN32
-constexpr bool USE_THISCALL = true;
-#else
-constexpr bool USE_THISCALL = false;
-#endif
-
-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;
-
-
-// size-to-mask table
-//const uint64_t size_to_mask[] = { 0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0, 0xffffffffffffffffU };
-
-// register mapping tables
-const Gp::Id int_register_map[REG_I_COUNT] =
-{
- Gp::kIdBx, Gp::kIdSi, Gp::kIdDi, Gp::kIdBp
-};
-
-// flags mapping tables
-uint8_t flags_map[0x1000];
-uint32_t flags_unmap[0x20];
-
-// condition mapping table
-const CondCode condition_map[uml::COND_MAX - uml::COND_Z] =
-{
- CondCode::kZ, // COND_Z = 0x80, requires Z
- CondCode::kNZ, // COND_NZ, requires Z
- CondCode::kS, // COND_S, requires S
- CondCode::kNS, // COND_NS, requires S
- CondCode::kC, // COND_C, requires C
- CondCode::kNC, // COND_NC, requires C
- CondCode::kO, // COND_V, requires V
- CondCode::kNO, // COND_NV, requires V
- CondCode::kP, // COND_U, requires U
- CondCode::kNP, // COND_NU, requires U
- CondCode::kA, // COND_A, requires CZ
- CondCode::kBE, // COND_BE, requires CZ
- CondCode::kG, // COND_G, requires SVZ
- CondCode::kLE, // COND_LE, requires SVZ
- CondCode::kL, // COND_L, requires SV
- CondCode::kGE, // COND_GE, requires SV
-};
-
-// FPU control register mapping
-const uint16_t fp_control[4] =
-{
- 0x0e3f, // ROUND_TRUNC
- 0x023f, // ROUND_ROUND
- 0x0a3f, // ROUND_CEIL
- 0x063f // ROUND_FLOOR
-};
-
-
-
-//**************************************************************************
-// MACROS
-//**************************************************************************
-
-#define X86_CONDITION(condition) (condition_map[condition - uml::COND_Z])
-#define X86_NOT_CONDITION(condition) negate_cond(condition_map[condition - uml::COND_Z])
-
-#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)
-
-
-
-//**************************************************************************
-// MISCELLAENOUS FUNCTIONS
-//**************************************************************************
-
-void calculate_status_flags(Assembler &a, Operand const &dst, u8 flags)
-{
- // calculate status flags in a way that does not modify any other status flags
- uint32_t flagmask = 0;
-
- if (flags & FLAG_C) flagmask |= 0x001;
- if (flags & FLAG_V) flagmask |= 0x800;
- if (flags & FLAG_Z) flagmask |= 0x040;
- if (flags & FLAG_S) flagmask |= 0x080;
- if (flags & FLAG_U) flagmask |= 0x004;
-
- if ((flags & (FLAG_Z | FLAG_S)) == flags)
- {
- Gp tempreg = dst.is_mem() ? eax : dst.id() == ebx.id() ? eax : ebx;
- Gp tempreg2 = dst.is_mem() ? edx : dst.id() == ecx.id() ? edx : ecx;
-
- if (dst.is_mem())
- {
- a.push(tempreg2);
- a.mov(tempreg2, dst.as<Mem>());
- }
-
- a.push(tempreg);
-
- a.pushfd();
- a.pop(tempreg);
- a.and_(tempreg, ~flagmask);
-
- a.add(dst.is_mem() ? tempreg2 : dst.as<Gp>(), 0);
-
- a.pushfd();
- a.and_(dword_ptr(esp), flagmask);
- a.or_(dword_ptr(esp), tempreg);
- a.popfd();
-
- a.pop(tempreg);
-
- if (dst.is_mem())
- a.pop(tempreg2);
- }
- else
- {
- fatalerror("drcbe_x86::calculate_status_flags: unknown flag combination requested: %02x\n", flags);
- }
-}
-
-
-//-------------------------------------------------
-// dmulu - perform a double-wide unsigned multiply
-//-------------------------------------------------
-
-template <bool HalfmulFlags>
-int 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)
- {
- dstlo = src1 * src2;
- return 0;
- }
-
- if (!src1 || !src2)
- {
- dsthi = dstlo = 0;
- return FLAG_Z;
- }
-
- // compute high and low parts first
- uint64_t lo = uint64_t(uint32_t(src1 >> 0)) * uint64_t(uint32_t(src2 >> 0));
- uint64_t hi = uint64_t(uint32_t(src1 >> 32)) * uint64_t(uint32_t(src2 >> 32));
-
- // compute middle parts
- uint64_t prevlo = lo;
- uint64_t temp = uint64_t(uint32_t(src1 >> 32)) * uint64_t(uint32_t(src2 >> 0));
- lo += temp << 32;
- hi += (temp >> 32) + (lo < prevlo);
-
- prevlo = lo;
- temp = uint64_t(uint32_t(src1 >> 0)) * uint64_t(uint32_t(src2 >> 32));
- lo += temp << 32;
- hi += (temp >> 32) + (lo < prevlo);
-
- // store the results
- dsthi = hi;
- dstlo = lo;
-
- if (HalfmulFlags)
- return ((lo >> 60) & FLAG_S) | (hi ? FLAG_V : 0) | (!lo ? FLAG_Z : 0);
- else
- return ((hi >> 60) & FLAG_S) | (hi ? FLAG_V : 0) | ((!hi && !lo) ? FLAG_Z : 0);
-}
-
-
-//-------------------------------------------------
-// dmuls - perform a double-wide signed multiply
-//-------------------------------------------------
-
-template <bool HalfmulFlags>
-int 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)
- {
- dstlo = src1 * src2;
- return 0;
- }
-
- if (!src1 || !src2)
- {
- dsthi = dstlo = 0;
- return FLAG_Z;
- }
-
- // fetch absolute source values
- a = src1; if (int64_t(a) < 0) a = -a;
- b = src2; if (int64_t(b) < 0) b = -b;
-
- // 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;
-
- if (HalfmulFlags)
- return ((lo >> 60) & FLAG_S) | ((hi != (int64_t(lo) >> 63)) ? FLAG_V : 0) | (!lo ? FLAG_Z : 0);
- else
- return ((hi >> 60) & FLAG_S) | ((hi != (int64_t(lo) >> 63)) ? FLAG_V : 0) | ((!hi && !lo) ? FLAG_Z : 0);
-}
-
-
-//-------------------------------------------------
-// ddivu - perform a double-wide unsigned divide
-//-------------------------------------------------
-
-int 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 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);
-}
-
-
-
-//**************************************************************************
-// TYPE DEFINITIONS
-//**************************************************************************
-
-class ThrowableErrorHandler : public ErrorHandler
-{
-public:
- virtual void handle_error(Error err, const char *message, BaseEmitter *origin) override
- {
- throw emu_fatalerror("asmjit error %u: %s", std::underlying_type_t<Error>(err), message);
- }
-};
-
-
-class drcbe_x86 : public drcbe_interface
-{
- using x86_entry_point_func = uint32_t (*)(x86code *entry);
-
-public:
- // construction/destruction
- drcbe_x86(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits);
- virtual ~drcbe_x86();
-
- // required overrides
- virtual void reset() override;
- virtual int execute(uml::code_handle &entry) override;
- virtual void generate(drcuml_block &block, const uml::instruction *instlist, uint32_t numinst) override;
- virtual bool hash_exists(uint32_t mode, uint32_t pc) const noexcept override;
- virtual void get_info(drcbe_info &info) const noexcept override;
- virtual bool logging() const noexcept override { return bool(m_log); }
-
-private:
- // HACK: leftover from x86emit
- static inline constexpr int REG_MAX = 16;
-
- // a be_parameter is similar to a uml::parameter but maps to native registers/memory
- class be_parameter
- {
- public:
- // parameter types
- enum be_parameter_type
- {
- PTYPE_NONE = 0, // invalid
- PTYPE_IMMEDIATE, // immediate; value = sign-extended to 64 bits
- PTYPE_INT_REGISTER, // integer register; value = 0-REG_MAX
- PTYPE_FLOAT_REGISTER, // floating point register; value = 0-REG_MAX
- PTYPE_MEMORY, // memory; value = pointer to memory
- PTYPE_MAX
- };
-
- // represents the value of a parameter
- typedef uint64_t be_parameter_value;
-
- // construction
- be_parameter() : m_type(PTYPE_NONE), m_value(0) { }
- be_parameter(uint64_t val) : m_type(PTYPE_IMMEDIATE), m_value(val) { }
- be_parameter(drcbe_x86 &drcbe, const uml::parameter &param, uint32_t allowed);
- be_parameter(const be_parameter &param) = default;
-
- // creators for types that don't safely default
- static be_parameter make_ireg(int regnum) { assert(regnum >= 0 && regnum < REG_MAX); return be_parameter(PTYPE_INT_REGISTER, regnum); }
- static be_parameter make_freg(int regnum) { assert(regnum >= 0 && regnum < REG_MAX); return be_parameter(PTYPE_FLOAT_REGISTER, regnum); }
- static be_parameter make_memory(void *base) { return be_parameter(PTYPE_MEMORY, reinterpret_cast<be_parameter_value>(base)); }
- static be_parameter make_memory(const void *base) { return be_parameter(PTYPE_MEMORY, reinterpret_cast<be_parameter_value>(const_cast<void *>(base))); }
-
- // operators
- bool operator==(be_parameter const &rhs) const { return (m_type == rhs.m_type && m_value == rhs.m_value); }
- bool operator!=(be_parameter const &rhs) const { return (m_type != rhs.m_type || m_value != rhs.m_value); }
-
- // getters
- be_parameter_type type() const { return m_type; }
- uint64_t immediate() const { assert(m_type == PTYPE_IMMEDIATE); return m_value; }
- uint32_t ireg() const { assert(m_type == PTYPE_INT_REGISTER); assert(m_value < REG_MAX); return m_value; }
- uint32_t freg() const { assert(m_type == PTYPE_FLOAT_REGISTER); assert(m_value < REG_MAX); return m_value; }
- void *memory(uint32_t offset = 0) const { assert(m_type == PTYPE_MEMORY); return reinterpret_cast<void *>(m_value + offset); }
-
- // type queries
- bool is_immediate() const { return (m_type == PTYPE_IMMEDIATE); }
- bool is_int_register() const { return (m_type == PTYPE_INT_REGISTER); }
- bool is_float_register() const { return (m_type == PTYPE_FLOAT_REGISTER); }
- bool is_memory() const { return (m_type == PTYPE_MEMORY); }
-
- // other queries
- bool is_immediate_value(uint64_t value) const { return (m_type == PTYPE_IMMEDIATE && m_value == value); }
-
- // helpers
- Gp select_register(Gp const &defreg) const;
- Vec select_register(Vec defreg) const;
- template <typename T> T select_register(T defreg, be_parameter const &checkparam) const;
- template <typename T> T select_register(T defreg, be_parameter const &checkparam, be_parameter const &checkparam2) const;
-
- private:
- // private constructor
- be_parameter(be_parameter_type type, be_parameter_value value) : m_type(type), m_value(value) { }
-
- // internals
- be_parameter_type m_type; // parameter type
- be_parameter_value m_value; // parameter value
- };
-
- // helpers
- Mem MABS(void const *base, u32 const size = 0) const { return Mem(uintptr_t(base), size); }
- void normalize_commutative(be_parameter &inner, be_parameter &outer);
- void emit_combine_z_flags(Assembler &a);
- void emit_combine_zs_flags(Assembler &a);
- void emit_combine_z_shl_flags(Assembler &a);
- void reset_last_upper_lower_reg();
- void set_last_lower_reg(Assembler &a, be_parameter const &param, Gp const &reglo);
- void set_last_upper_reg(Assembler &a, be_parameter const &param, Gp const &reghi);
- bool can_skip_lower_load(Assembler &a, uint32_t *memref, Gp const &reglo);
- bool can_skip_upper_load(Assembler &a, uint32_t *memref, Gp const &reghi);
-
- [[noreturn]] void end_of_block() const;
- static void debug_log_hashjmp(char const *tag, int mode, offs_t pc);
-
- void generate_one(Assembler &a, const uml::instruction &inst);
-
- // code generators
- void op_handle(Assembler &a, const uml::instruction &inst);
- void op_hash(Assembler &a, const uml::instruction &inst);
- void op_label(Assembler &a, const uml::instruction &inst);
- void op_comment(Assembler &a, const uml::instruction &inst);
- void op_mapvar(Assembler &a, const uml::instruction &inst);
-
- void op_nop(Assembler &a, const uml::instruction &inst);
- void op_break(Assembler &a, const uml::instruction &inst);
- void op_debug(Assembler &a, const uml::instruction &inst);
- void op_exit(Assembler &a, const uml::instruction &inst);
- void op_hashjmp(Assembler &a, const uml::instruction &inst);
- void op_jmp(Assembler &a, const uml::instruction &inst);
- void op_exh(Assembler &a, const uml::instruction &inst);
- void op_callh(Assembler &a, const uml::instruction &inst);
- void op_ret(Assembler &a, const uml::instruction &inst);
- void op_callc(Assembler &a, const uml::instruction &inst);
- void op_recover(Assembler &a, const uml::instruction &inst);
-
- void op_setfmod(Assembler &a, const uml::instruction &inst);
- void op_getfmod(Assembler &a, const uml::instruction &inst);
- void op_getexp(Assembler &a, const uml::instruction &inst);
- void op_getflgs(Assembler &a, const uml::instruction &inst);
- void op_setflgs(Assembler &a, const uml::instruction &inst);
- void op_save(Assembler &a, const uml::instruction &inst);
- void op_restore(Assembler &a, const uml::instruction &inst);
-
- void op_load(Assembler &a, const uml::instruction &inst);
- void op_loads(Assembler &a, const uml::instruction &inst);
- void op_store(Assembler &a, const uml::instruction &inst);
- void op_read(Assembler &a, const uml::instruction &inst);
- void op_readm(Assembler &a, const uml::instruction &inst);
- void op_write(Assembler &a, const uml::instruction &inst);
- void op_writem(Assembler &a, const uml::instruction &inst);
- void op_carry(Assembler &a, const uml::instruction &inst);
- void op_set(Assembler &a, const uml::instruction &inst);
- void op_mov(Assembler &a, const uml::instruction &inst);
- void op_sext(Assembler &a, const uml::instruction &inst);
- void op_bfxu(Assembler &a, const uml::instruction &inst);
- void op_bfxs(Assembler &a, const uml::instruction &inst);
- void op_roland(Assembler &a, const uml::instruction &inst);
- void op_rolins(Assembler &a, const uml::instruction &inst);
- void op_add(Assembler &a, const uml::instruction &inst);
- void op_addc(Assembler &a, const uml::instruction &inst);
- void op_sub(Assembler &a, const uml::instruction &inst);
- void op_subc(Assembler &a, const uml::instruction &inst);
- void op_cmp(Assembler &a, const uml::instruction &inst);
- void op_mulu(Assembler &a, const uml::instruction &inst);
- void op_mululw(Assembler &a, const uml::instruction &inst);
- void op_muls(Assembler &a, const uml::instruction &inst);
- void op_mulslw(Assembler &a, const uml::instruction &inst);
- void op_divu(Assembler &a, const uml::instruction &inst);
- void op_divs(Assembler &a, const uml::instruction &inst);
- void op_and(Assembler &a, const uml::instruction &inst);
- void op_test(Assembler &a, const uml::instruction &inst);
- void op_or(Assembler &a, const uml::instruction &inst);
- void op_xor(Assembler &a, const uml::instruction &inst);
- void op_lzcnt(Assembler &a, const uml::instruction &inst);
- void op_tzcnt(Assembler &a, const uml::instruction &inst);
- void op_bswap(Assembler &a, const uml::instruction &inst);
- void op_shl(Assembler &a, const uml::instruction &inst);
- void op_shr(Assembler &a, const uml::instruction &inst);
- void op_sar(Assembler &a, const uml::instruction &inst);
- void op_ror(Assembler &a, const uml::instruction &inst);
- void op_rol(Assembler &a, const uml::instruction &inst);
- void op_rorc(Assembler &a, const uml::instruction &inst);
- void op_rolc(Assembler &a, const uml::instruction &inst);
-
- void op_fload(Assembler &a, const uml::instruction &inst);
- void op_fstore(Assembler &a, const uml::instruction &inst);
- void op_fread(Assembler &a, const uml::instruction &inst);
- void op_fwrite(Assembler &a, const uml::instruction &inst);
- void op_fmov(Assembler &a, const uml::instruction &inst);
- void op_ftoint(Assembler &a, const uml::instruction &inst);
- void op_ffrint(Assembler &a, const uml::instruction &inst);
- void op_ffrflt(Assembler &a, const uml::instruction &inst);
- void op_frnds(Assembler &a, const uml::instruction &inst);
- void op_fadd(Assembler &a, const uml::instruction &inst);
- void op_fsub(Assembler &a, const uml::instruction &inst);
- void op_fcmp(Assembler &a, const uml::instruction &inst);
- void op_fmul(Assembler &a, const uml::instruction &inst);
- void op_fdiv(Assembler &a, const uml::instruction &inst);
- void op_fneg(Assembler &a, const uml::instruction &inst);
- void op_fabs(Assembler &a, const uml::instruction &inst);
- void op_fsqrt(Assembler &a, const uml::instruction &inst);
- void op_frecip(Assembler &a, const uml::instruction &inst);
- void op_frsqrt(Assembler &a, const uml::instruction &inst);
- void op_fcopyi(Assembler &a, const uml::instruction &inst);
- void op_icopyf(Assembler &a, const uml::instruction &inst);
-
- // 32-bit code emission helpers
- void emit_mov_r32_p32(Assembler &a, Gp const &reg, be_parameter const &param);
- void emit_mov_r32_p32_keepflags(Assembler &a, Gp const &reg, be_parameter const &param);
- void emit_mov_m32_p32(Assembler &a, Mem memref, be_parameter const &param);
- void emit_mov_p32_r32(Assembler &a, be_parameter const &param, Gp const &reg);
-
- template <typename T> void alu_op_param(Assembler &a, Inst::Id const opcode, asmjit::Operand const &dst, be_parameter const &param, T &&optimize);
- void alu_op_param(Assembler &a, Inst::Id const opcode, asmjit::Operand const &dst, be_parameter const &param) { alu_op_param(a, opcode, dst, param, [](Assembler &a, asmjit::Operand dst, be_parameter const &src) { return false; }); }
- template <typename T> void shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsize, asmjit::Operand const &dst, be_parameter const &param, T &&optimize, bool update_flags);
-
- // 64-bit code emission helpers
- void emit_mov_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param);
- void emit_mov_r64_p64_keepflags(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param);
- void emit_mov_m64_p64(Assembler &a, Mem const &memref, be_parameter const &param);
- void emit_mov_p64_r64(Assembler &a, be_parameter const &param, Gp const &reglo, Gp const &reghi);
- void emit_and_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, const uml::instruction &inst);
- void emit_and_m64_p64(Assembler &a, Mem const &memref_lo, Mem const &memref_hi, be_parameter const &param, const uml::instruction &inst);
- void emit_or_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, const uml::instruction &inst);
- void emit_or_m64_p64(Assembler &a, Mem const &memref_lo, Mem const &memref_hi, be_parameter const &param, const uml::instruction &inst);
- void emit_xor_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, const uml::instruction &inst);
- void emit_xor_m64_p64(Assembler &a, Mem const &memref_lo, Mem const &memref_hi, be_parameter const &param, const uml::instruction &inst);
- void emit_shl_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
- void emit_shr_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
- void emit_sar_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
- void emit_rol_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
- void emit_ror_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
- void emit_rcl_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
- void emit_rcr_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags);
-
- void alu_op_param(Assembler &a, Inst::Id const opcode_lo, Inst::Id const opcode_hi, Gp const &lo, Gp const &hi, be_parameter const &param, bool const saveflags);
- void alu_op_param(Assembler &a, Inst::Id const opcode_lo, Inst::Id const opcode_hi, Mem const &lo, Mem const &hi, be_parameter const &param, bool const saveflags);
-
- // floating-point code emission helpers
- void emit_fld_p(Assembler &a, int size, be_parameter const &param);
- void emit_fstp_p(Assembler &a, int size, be_parameter const &param);
-
- size_t emit(asmjit::CodeHolder &ch, bool invariant);
-
- // internal state
- drc_hash_table m_hash; // hash table state
- drc_map_variables m_map; // code map
- x86log_context::ptr m_log; // logging
- FILE * m_log_asmjit;
- bool const m_sse3; // do we have SSE3 support?
-
- x86_entry_point_func m_entry; // entry point
- x86code * m_exit; // exit point
- x86code * m_nocode; // nocode handler
- x86code * m_endofblock; // end of block handler
- x86code * m_save; // save handler
- x86code * m_restore; // restore handler
-
- uint32_t * m_reglo[REG_MAX]; // pointer to low part of data for each register
- uint32_t * m_reghi[REG_MAX]; // pointer to high part of data for each register
- Gp m_last_lower_reg; // last register we stored a lower from
- x86code * m_last_lower_pc; // PC after instruction where we last stored a lower register
- uint32_t * m_last_lower_addr; // address where we last stored an lower register
- Gp m_last_upper_reg; // last register we stored an upper from
- x86code * m_last_upper_pc; // PC after instruction where we last stored an upper register
- uint32_t * m_last_upper_addr; // address where we last stored an upper register
- double m_fptemp; // temporary storage for floating point
- bool m_invariant_block; // are we generating an invariant block?
-
- uint16_t m_fpumode; // saved FPU mode
- uint16_t m_fmodesave; // temporary location for saving
-
- void * m_stacksave; // saved stack pointer
- void * m_hashstacksave; // saved stack pointer for hashjmp
- uint64_t m_reslo; // extended low result
- uint64_t m_reshi; // extended high result
-
- // resolved memory handler functions
- resolved_member_function m_debug_cpu_instruction_hook;
- resolved_member_function m_drcmap_get_value;
- resolved_memory_accessors_vector m_memory_accessors;
-};
-
-
-
-//**************************************************************************
-// TABLES
-//**************************************************************************
-
-inline void drcbe_x86::generate_one(Assembler &a, const uml::instruction &inst)
-{
- switch (inst.opcode())
- {
- // Compile-time opcodes
- case uml::OP_HANDLE: op_handle(a, inst); break; // HANDLE handle
- case uml::OP_HASH: op_hash(a, inst); break; // HASH mode,pc
- case uml::OP_LABEL: op_label(a, inst); break; // LABEL imm
- case uml::OP_COMMENT: op_comment(a, inst); break; // COMMENT string
- case uml::OP_MAPVAR: op_mapvar(a, inst); break; // MAPVAR mapvar,value
-
- // Control Flow Operations
- case uml::OP_NOP: op_nop(a, inst); break; // NOP
- case uml::OP_BREAK: op_break(a, inst); break; // BREAK
- case uml::OP_DEBUG: op_debug(a, inst); break; // DEBUG pc
- case uml::OP_EXIT: op_exit(a, inst); break; // EXIT src1[,c]
- case uml::OP_HASHJMP: op_hashjmp(a, inst); break; // HASHJMP mode,pc,handle
- case uml::OP_JMP: op_jmp(a, inst); break; // JMP imm[,c]
- case uml::OP_EXH: op_exh(a, inst); break; // EXH handle,param[,c]
- case uml::OP_CALLH: op_callh(a, inst); break; // CALLH handle[,c]
- case uml::OP_RET: op_ret(a, inst); break; // RET [c]
- case uml::OP_CALLC: op_callc(a, inst); break; // CALLC func,ptr[,c]
- case uml::OP_RECOVER: op_recover(a, inst); break; // RECOVER dst,mapvar
-
- // Internal Register Operations
- case uml::OP_SETFMOD: op_setfmod(a, inst); break; // SETFMOD src
- case uml::OP_GETFMOD: op_getfmod(a, inst); break; // GETFMOD dst
- case uml::OP_GETEXP: op_getexp(a, inst); break; // GETEXP dst
- case uml::OP_GETFLGS: op_getflgs(a, inst); break; // GETFLGS dst[,f]
- case uml::OP_SETFLGS: op_setflgs(a, inst); break; // GETFLGS src
- case uml::OP_SAVE: op_save(a, inst); break; // SAVE dst
- case uml::OP_RESTORE: op_restore(a, inst); break; // RESTORE dst
-
- // Integer Operations
- case uml::OP_LOAD: op_load(a, inst); break; // LOAD dst,base,index,size
- case uml::OP_LOADS: op_loads(a, inst); break; // LOADS dst,base,index,size
- case uml::OP_STORE: op_store(a, inst); break; // STORE base,index,src,size
- case uml::OP_READ: op_read(a, inst); break; // READ dst,src1,spacesize
- case uml::OP_READM: op_readm(a, inst); break; // READM dst,src1,mask,spacesize
- case uml::OP_WRITE: op_write(a, inst); break; // WRITE dst,src1,spacesize
- case uml::OP_WRITEM: op_writem(a, inst); break; // WRITEM dst,src1,spacesize
- case uml::OP_CARRY: op_carry(a, inst); break; // CARRY src,bitnum
- case uml::OP_SET: op_set(a, inst); break; // SET dst,c
- case uml::OP_MOV: op_mov(a, inst); break; // MOV dst,src[,c]
- case uml::OP_SEXT: op_sext(a, inst); break; // SEXT dst,src
- case uml::OP_BFXU: op_bfxu(a, inst); break; // BFXU dst,src1,src2,src3
- case uml::OP_BFXS: op_bfxs(a, inst); break; // BFXS dst,src1,src2,src3
- case uml::OP_ROLAND: op_roland(a, inst); break; // ROLAND dst,src1,src2,src3
- case uml::OP_ROLINS: op_rolins(a, inst); break; // ROLINS dst,src1,src2,src3
- case uml::OP_ADD: op_add(a, inst); break; // ADD dst,src1,src2[,f]
- case uml::OP_ADDC: op_addc(a, inst); break; // ADDC dst,src1,src2[,f]
- case uml::OP_SUB: op_sub(a, inst); break; // SUB dst,src1,src2[,f]
- case uml::OP_SUBB: op_subc(a, inst); break; // SUBB dst,src1,src2[,f]
- case uml::OP_CMP: op_cmp(a, inst); break; // CMP src1,src2[,f]
- case uml::OP_MULU: op_mulu(a, inst); break; // MULU dst,edst,src1,src2[,f]
- case uml::OP_MULULW: op_mululw(a, inst); break; // MULULW dst,src1,src2[,f]
- case uml::OP_MULS: op_muls(a, inst); break; // MULS dst,edst,src1,src2[,f]
- case uml::OP_MULSLW: op_mulslw(a, inst); break; // MULSLW dst,src1,src2[,f]
- case uml::OP_DIVU: op_divu(a, inst); break; // DIVU dst,edst,src1,src2[,f]
- case uml::OP_DIVS: op_divs(a, inst); break; // DIVS dst,edst,src1,src2[,f]
- case uml::OP_AND: op_and(a, inst); break; // AND dst,src1,src2[,f]
- case uml::OP_TEST: op_test(a, inst); break; // TEST src1,src2[,f]
- case uml::OP_OR: op_or(a, inst); break; // OR dst,src1,src2[,f]
- case uml::OP_XOR: op_xor(a, inst); break; // XOR dst,src1,src2[,f]
- case uml::OP_LZCNT: op_lzcnt(a, inst); break; // LZCNT dst,src[,f]
- case uml::OP_TZCNT: op_tzcnt(a, inst); break; // TZCNT dst,src[,f]
- case uml::OP_BSWAP: op_bswap(a, inst); break; // BSWAP dst,src
- case uml::OP_SHL: op_shl(a, inst); break; // SHL dst,src,count[,f]
- case uml::OP_SHR: op_shr(a, inst); break; // SHR dst,src,count[,f]
- case uml::OP_SAR: op_sar(a, inst); break; // SAR dst,src,count[,f]
- case uml::OP_ROL: op_rol(a, inst); break; // ROL dst,src,count[,f]
- case uml::OP_ROLC: op_rolc(a, inst); break; // ROLC dst,src,count[,f]
- case uml::OP_ROR: op_ror(a, inst); break; // ROR dst,src,count[,f]
- case uml::OP_RORC: op_rorc(a, inst); break; // RORC dst,src,count[,f]
-
- // Floating Point Operations
- case uml::OP_FLOAD: op_fload(a, inst); break; // FLOAD dst,base,index
- case uml::OP_FSTORE: op_fstore(a, inst); break; // FSTORE base,index,src
- case uml::OP_FREAD: op_fread(a, inst); break; // FREAD dst,space,src1
- case uml::OP_FWRITE: op_fwrite(a, inst); break; // FWRITE space,dst,src1
- case uml::OP_FMOV: op_fmov(a, inst); break; // FMOV dst,src1[,c]
- case uml::OP_FTOINT: op_ftoint(a, inst); break; // FTOINT dst,src1,size,round
- case uml::OP_FFRINT: op_ffrint(a, inst); break; // FFRINT dst,src1,size
- case uml::OP_FFRFLT: op_ffrflt(a, inst); break; // FFRFLT dst,src1,size
- case uml::OP_FRNDS: op_frnds(a, inst); break; // FRNDS dst,src1
- case uml::OP_FADD: op_fadd(a, inst); break; // FADD dst,src1,src2
- case uml::OP_FSUB: op_fsub(a, inst); break; // FSUB dst,src1,src2
- case uml::OP_FCMP: op_fcmp(a, inst); break; // FCMP src1,src2
- case uml::OP_FMUL: op_fmul(a, inst); break; // FMUL dst,src1,src2
- case uml::OP_FDIV: op_fdiv(a, inst); break; // FDIV dst,src1,src2
- case uml::OP_FNEG: op_fneg(a, inst); break; // FNEG dst,src1
- case uml::OP_FABS: op_fabs(a, inst); break; // FABS dst,src1
- case uml::OP_FSQRT: op_fsqrt(a, inst); break; // FSQRT dst,src1
- case uml::OP_FRECIP: op_frecip(a, inst); break; // FRECIP dst,src1
- case uml::OP_FRSQRT: op_frsqrt(a, inst); break; // FRSQRT dst,src1
- case uml::OP_FCOPYI: op_fcopyi(a, inst); break; // FCOPYI dst,src
- case uml::OP_ICOPYF: op_icopyf(a, inst); break; // ICOPYF dst,src
-
- default: throw emu_fatalerror("drcbe_x86(%s): unhandled opcode %u\n", m_device.tag(), inst.opcode());
- }
-};
-
-
-//**************************************************************************
-// INLINE FUNCTIONS
-//**************************************************************************
-
-//-------------------------------------------------
-// param_normalize - convert a full parameter
-// into a reduced set
-//-------------------------------------------------
-
-drcbe_x86::be_parameter::be_parameter(drcbe_x86 &drcbe, parameter const &param, 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 Gp drcbe_x86::be_parameter::select_register(Gp const &defreg) const
-{
- if (m_type == PTYPE_INT_REGISTER)
- return gpd(m_value);
- return defreg;
-}
-
-inline Vec drcbe_x86::be_parameter::select_register(Vec defreg) const
-{
- if (m_type == PTYPE_FLOAT_REGISTER)
- return xmm(m_value);
- return defreg;
-}
-
-template <typename T> T drcbe_x86::be_parameter::select_register(T defreg, be_parameter const &checkparam) const
-{
- if (*this == checkparam)
- return defreg;
- return select_register(defreg);
-}
-
-template <typename T> T drcbe_x86::be_parameter::select_register(T defreg, be_parameter const &checkparam, be_parameter const &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(Assembler &a)
-{
- // 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
- a.pushfd();
-
- a.mov(ecx, dword_ptr(esp, 4)); // zero flag
- a.or_(ecx, ~0x40);
- a.and_(dword_ptr(esp, 0), ecx);
-
- a.popfd();
- a.lea(esp, ptr(esp, 4));
-}
-
-inline void drcbe_x86::emit_combine_zs_flags(Assembler &a)
-{
- // 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
- a.pushfd();
-
- a.mov(ecx, dword_ptr(esp, 4)); // zero flag
- a.or_(ecx, ~(0x40 | 0x80));
- a.and_(dword_ptr(esp, 0), ecx);
- a.and_(ecx, 0x80); // sign flag
- a.or_(dword_ptr(esp, 0), ecx);
-
- a.popfd();
- a.lea(esp, ptr(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(Assembler &a)
-{
- // 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
- a.pushfd();
- a.pop(ecx);
- a.or_(ecx, ~0x40);
- a.and_(ptr(esp, 0), ecx);
- a.popfd();
-}
-
-
-//-------------------------------------------------
-// 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 = Gp();
- m_last_upper_reg = Gp();
-}
-
-
-//-------------------------------------------------
-// set_last_lower_reg - note that we have just
-// loaded a lower register
-//-------------------------------------------------
-
-inline void drcbe_x86::set_last_lower_reg(Assembler &a, be_parameter const &param, Gp const &reglo)
-{
- if (param.is_memory())
- {
- m_last_lower_reg = reglo;
- m_last_lower_addr = (uint32_t *)((uintptr_t)param.memory());
- m_last_lower_pc = (x86code *)(a.code()->base_address() + a.offset());
- }
-}
-
-
-//-------------------------------------------------
-// set_last_upper_reg - note that we have just
-// loaded an upper register
-//-------------------------------------------------
-
-inline void drcbe_x86::set_last_upper_reg(Assembler &a, be_parameter const &param, Gp const &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 = (x86code *)(a.code()->base_address() + a.offset());
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, uint32_t *memref, Gp const &reglo)
-{
- return ((x86code *)(a.code()->base_address() + a.offset()) == 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(Assembler &a, uint32_t *memref, Gp const &reghi)
-{
- return ((x86code *)(a.code()->base_address() + a.offset()) == m_last_upper_pc && memref == m_last_upper_addr && reghi == m_last_upper_reg);
-}
-
-
-//-------------------------------------------------
-// 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_log_asmjit(nullptr)
- , m_sse3(CpuInfo::host().features().x86().has_sse3())
- , m_entry(nullptr)
- , m_exit(nullptr)
- , m_nocode(nullptr)
- , m_endofblock(nullptr)
- , m_save(nullptr)
- , m_restore(nullptr)
- , m_last_lower_reg(Gp())
- , m_last_lower_pc(nullptr)
- , m_last_lower_addr(nullptr)
- , m_last_upper_reg(Gp())
- , m_last_upper_pc(nullptr)
- , m_last_upper_addr(nullptr)
- , m_fptemp(0)
- , m_invariant_block(false)
- , m_fpumode(0)
- , m_fmodesave(0)
- , m_stacksave(nullptr)
- , m_hashstacksave(nullptr)
- , m_reslo(0)
- , m_reshi(0)
-{
- // compute hi pointers for each register
- for (int regnum = 0; regnum < std::size(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 < std::size(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 < std::size(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;
- }
-
- // resolve the actual addresses of member functions we need to call
- m_drcmap_get_value.set(m_map, &drc_map_variables::get_value);
- if (!m_drcmap_get_value)
- throw emu_fatalerror("Error resolving map variable get value function!\n");
- m_memory_accessors.resize(m_space.size());
- for (int space = 0; m_space.size() > space; ++space)
- {
- if (m_space[space])
- m_memory_accessors[space].set(*m_space[space]);
- }
-
- // create the log
- if (device.machine().options().drc_log_native())
- {
- std::string filename = device.tag();
- for (auto &ch : filename)
- {
- if (':' == ch)
- ch = '_';
- }
- std::ostringstream str;
- str.imbue(std::locale::classic());
- str << device.shortname();
- if ('_' != filename[0])
- str << '_';
- str << filename << ".asm";
- filename = std::move(str).str();
- m_log = x86log_context::create("drcbex86_" + filename);
- m_log_asmjit = fopen(("drcbex86_asmjit_" + filename).c_str(), "w");
- }
-
- // generate a little bit of glue code to set up the environment
- x86code *dst = (x86code *)m_cache.top();
-
- CodeHolder ch;
- ch.init(Environment::host(), uint64_t(dst));
-
- FileLogger logger(m_log_asmjit);
- if (logger.file())
- {
- logger.set_flags(FormatFlags::kHexOffsets | FormatFlags::kHexImms | FormatFlags::kMachineCode);
- logger.set_indentation(FormatIndentationGroup::kCode, 4);
- ch.set_logger(&logger);
- }
-
- Assembler a(&ch);
- if (logger.file())
- a.add_diagnostic_options(DiagnosticOptions::kValidateIntermediate);
-
- // generate an entry point
- m_entry = (x86_entry_point_func)dst;
- a.bind(a.new_named_label("entry_point"));
-
- FuncDetail entry_point;
- entry_point.init(FuncSignature::build<uint32_t, x86code *>(CallConvId::kCDecl), Environment::host());
-
- FuncFrame frame;
- frame.init(entry_point);
- frame.add_dirty_regs(ebx, esi, edi, ebp);
- FuncArgsAssignment args(&entry_point);
- args.assign_all(eax);
- args.update_func_frame(frame);
- frame.finalize();
-
- a.emit_prolog(frame);
- a.emit_args_assignment(frame, args);
- a.sub(esp, 24);
- a.mov(MABS(&m_hashstacksave), esp);
- a.sub(esp, 4);
- a.mov(MABS(&m_stacksave), esp);
- a.fnstcw(MABS(&m_fpumode));
- a.call(eax);
-
- // generate an exit point
- m_exit = dst + a.offset();
- a.bind(a.new_named_label("exit_point"));
- a.fldcw(MABS(&m_fpumode));
- a.mov(esp, MABS(&m_hashstacksave));
- a.add(esp, 24);
- a.emit_epilog(frame);
-
- // generate a no code point
- m_nocode = dst + a.offset();
- a.bind(a.new_named_label("nocode_point"));
- a.ret(); // ret
-
- // generate an end-of-block handler point
- m_endofblock = dst + a.offset();
- a.bind(a.new_named_label("end_of_block_point"));
- auto const [entrypoint, adjusted] = util::resolve_member_function(&drcbe_x86::end_of_block, *this);
- if (USE_THISCALL)
- a.mov(ecx, imm(adjusted));
- else
- a.mov(dword_ptr(esp, 0), imm(adjusted));
- a.call(imm(entrypoint));
- if (USE_THISCALL)
- a.sub(esp, 4);
-
- // generate a save subroutine
- m_save = dst + a.offset();
- a.bind(a.new_named_label("save"));
- a.pushfd(); // pushf
- a.pop(eax); // pop eax
- a.and_(eax, 0x8c5); // and eax,0x8c5
- a.mov(al, ptr(uintptr_t(flags_map), eax)); // mov al,[flags_map]
- a.mov(ptr(ecx, offsetof(drcuml_machine_state, flags)), al); // mov state->flags,al
- a.mov(al, MABS(&m_state.fmod)); // mov al,[fmod]
- a.mov(ptr(ecx, offsetof(drcuml_machine_state, fmod)), al); // mov state->fmod,al
- a.mov(eax, MABS(&m_state.exp)); // mov eax,[exp]
- a.mov(ptr(ecx, offsetof(drcuml_machine_state, exp)), eax); // mov state->exp,eax
- for (int regnum = 0; regnum < std::size(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)
- a.mov(ptr(ecx, regoffsl), gpd(int_register_map[regnum]));
- else
- {
- a.mov(eax, MABS(&m_state.r[regnum].w.l));
- a.mov(ptr(ecx, regoffsl), eax);
- }
- a.mov(eax, MABS(&m_state.r[regnum].w.h));
- a.mov(ptr(ecx, regoffsh), eax);
- }
- for (int regnum = 0; regnum < std::size(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;
- a.mov(eax, MABS(&m_state.f[regnum].s.l));
- a.mov(ptr(ecx, regoffsl), eax);
- a.mov(eax, MABS(&m_state.f[regnum].s.h));
- a.mov(ptr(ecx, regoffsh), eax);
- }
- a.ret(); // ret
-
- // generate a restore subroutine
- m_restore = dst + a.offset();
- a.bind(a.new_named_label("restore"));
- for (int regnum = 0; regnum < std::size(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)
- a.mov(gpd(int_register_map[regnum]), ptr(ecx, regoffsl));
- else
- {
- a.mov(eax, ptr(ecx, regoffsl));
- a.mov(MABS(&m_state.r[regnum].w.l), eax);
- }
- a.mov(eax, ptr(ecx, regoffsh));
- a.mov(MABS(&m_state.r[regnum].w.h), eax);
- }
- for (int regnum = 0; regnum < std::size(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;
- a.mov(eax, ptr(ecx, regoffsl));
- a.mov(MABS(&m_state.f[regnum].s.l), eax);
- a.mov(eax, ptr(ecx, regoffsh));
- a.mov(MABS(&m_state.f[regnum].s.h), eax);
- }
- a.movzx(eax, byte_ptr(ecx, offsetof(drcuml_machine_state, fmod)));
- a.and_(eax, 3);
- a.mov(MABS(&m_state.fmod), al);
- a.fldcw(word_ptr(uintptr_t(&fp_control[0]), eax, 1));
- a.mov(eax, ptr(ecx, offsetof(drcuml_machine_state, exp)));
- a.mov(MABS(&m_state.exp), eax);
-
- a.pushfd();
- a.and_(dword_ptr(esp), ~0x8c5);
- a.movzx(eax, byte_ptr(ecx, offsetof(drcuml_machine_state, flags)));
- a.and_(eax, FLAGS_ALL);
- a.mov(eax, ptr(uintptr_t(flags_unmap), eax, 2));
- a.or_(dword_ptr(esp), eax);
- a.popfd();
- a.ret();
-
-
- // emit the generated code
- size_t const bytes = emit(ch, true);
-
- if (m_log)
- {
- m_log->disasm_code_range("entry_point", dst, m_exit);
- m_log->disasm_code_range("exit_point", m_exit, m_nocode);
- m_log->disasm_code_range("nocode_point", m_nocode, m_endofblock);
- m_log->disasm_code_range("end_of_block", m_endofblock, m_save);
- m_log->disasm_code_range("save", m_save, m_restore);
- m_log->disasm_code_range("restore", m_restore, dst + bytes);
- }
-
- // set the "no code" pointer
- m_hash.set_default_codeptr(m_nocode);
-}
-
-
-//-------------------------------------------------
-// ~drcbe_x86 - destructor
-//-------------------------------------------------
-
-drcbe_x86::~drcbe_x86()
-{
- // free the log context
- m_log.reset();
-
- if (m_log_asmjit)
- fclose(m_log_asmjit);
-}
-
-size_t drcbe_x86::emit(CodeHolder &ch, bool invariant)
-{
- Error err;
-
- // the following three calls aren't currently required, but may be if
- // other asmjit features are used in future
- if (false)
- {
- err = ch.flatten();
- if (err != kErrorOk)
- throw emu_fatalerror("asmjit::CodeHolder::flatten() error %u", std::underlying_type_t<Error>(err));
-
- err = ch.resolve_cross_section_fixups();
- if (err != kErrorOk)
- throw emu_fatalerror("asmjit::CodeHolder::resolve_cross_section_fixups() error %u", std::underlying_type_t<Error>(err));
-
- err = ch.relocate_to_base(ch.base_address());
- if (err != kErrorOk)
- throw emu_fatalerror("asmjit::CodeHolder::relocate_to_base() error %u", std::underlying_type_t<Error>(err));
- }
-
- size_t const alignment = ch.base_address() - uint64_t(m_cache.top());
- size_t const code_size = ch.code_size();
-
- // try to allocate space from the DRC cache
- auto space = invariant
- ? m_cache.alloc_invariant(alignment + code_size, std::align_val_t(1))
- : m_cache.alloc_transient(alignment + code_size, std::align_val_t(1));
- if (!space)
- return 0;
-
- assert(uintptr_t(space) <= ch.base_address());
- err = ch.copy_flattened_data(drccodeptr(ch.base_address()), code_size, CopySectionFlags::kPadTargetBuffer);
- if (err != kErrorOk)
- throw emu_fatalerror("asmjit::CodeHolder::copy_flattened_data() error %u", std::underlying_type_t<Error>(err));
-
- osd::invalidate_instruction_cache(drccodeptr(ch.base_address()), code_size);
-
- return code_size;
-}
-
-//-------------------------------------------------
-// reset - reset back-end specific state
-//-------------------------------------------------
-
-void drcbe_x86::reset()
-{
- // output a note to the log
- if (m_log)
- m_log->printf("%s", "\n\n===========\nCACHE RESET\n===========\n\n");
-
- // reset our hash tables
- m_hash.reset();
-}
-
-
-//-------------------------------------------------
-// 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
- m_cache.codegen_complete();
- return (*m_entry)((x86code *)entry.codeptr());
-}
-
-
-//-------------------------------------------------
-// drcbex86_generate - generate code
-//-------------------------------------------------
-
-void drcbe_x86::generate(drcuml_block &block, const instruction *instlist, uint32_t numinst)
-{
- // do this here because device.debug() isn't initialised at construction time
- if (!m_debug_cpu_instruction_hook && (m_device.machine().debug_flags & DEBUG_FLAG_ENABLED) && m_device.debug())
- {
- m_debug_cpu_instruction_hook.set(*m_device.debug(), &device_debug::instruction_hook);
- if (!m_debug_cpu_instruction_hook)
- throw emu_fatalerror("Error resolving debugger instruction hook member function!\n");
- }
-
- // tell all of our utility objects that a block is beginning
- m_hash.block_begin(block, instlist, numinst);
- m_map.block_begin(block);
- m_invariant_block = block.invariant();
-
- // compute the base by aligning the cache top to a cache line
- auto [err, linesize] = osd_get_cache_line_size();
- uintptr_t linemask = 63;
- if (err)
- {
- osd_printf_verbose("drcbe_x86(%s): Error getting cache line size (%s:%d %s), assuming 64 bytes\n", m_device.tag(), err.category().name(), err.value(), err.message());
- }
- else
- {
- assert(linesize);
- linemask = linesize - 1;
- for (unsigned shift = 1; linemask & (linemask + 1); ++shift)
- linemask |= linemask >> shift;
- }
- x86code *dst = (x86code *)(uintptr_t(m_cache.top() + linemask) & ~linemask);
-
- CodeHolder ch;
- ch.init(Environment::host(), uint64_t(dst));
- ThrowableErrorHandler e;
- ch.set_error_handler(&e);
-
- FileLogger logger(m_log_asmjit);
- if (logger.file())
- {
- logger.set_flags(FormatFlags::kHexOffsets | FormatFlags::kHexImms | FormatFlags::kMachineCode);
- logger.set_indentation(FormatIndentationGroup::kCode, 4);
- ch.set_logger(&logger);
- }
-
- Assembler a(&ch);
- if (logger.file())
- a.add_diagnostic_options(DiagnosticOptions::kValidateIntermediate);
-
- // generate code
- std::string blockname;
- for (int inum = 0; inum < numinst; inum++)
- {
- const instruction &inst = instlist[inum];
-
- // must remain in scope until output
- std::string dasm;
-
- // add a comment
- if (m_log)
- {
- dasm = inst.disasm(&m_drcuml);
- m_log->add_comment(dst + a.offset(), "%s", dasm.c_str());
- a.set_inline_comment(dasm.c_str());
- }
-
- // extract a blockname
- if (blockname.empty())
- {
- 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());
- }
-
- // generate code
- generate_one(a, inst);
- }
-
- // catch falling off the end of a block
- if (m_log)
- {
- m_log->add_comment(dst + a.offset(), "%s", "end of block");
- a.set_inline_comment("end of block");
- }
- a.jmp(imm(m_endofblock));
-
- // emit the generated code
- size_t const bytes = emit(ch, block.invariant());
- if (!bytes)
- block.abort();
-
- // log it
- if (m_log)
- m_log->disasm_code_range(blockname.empty() ? "Unknown block" : blockname.c_str(), dst, dst + bytes);
-
- // tell all of our utility objects that the block is finished
- m_hash.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) const noexcept
-{
- 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) const noexcept
-{
- 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(Assembler &a, Gp const &reg, be_parameter const &param)
-{
- if (param.is_immediate())
- {
- if (param.immediate() == 0)
- a.xor_(reg, reg); // xor reg,reg
- else
- a.mov(reg, param.immediate()); // mov reg,param
- }
- else if (param.is_memory())
- {
- a.mov(reg, MABS(param.memory())); // mov reg,[param]
- }
- else if (param.is_int_register())
- {
- if (reg.id() != param.ireg())
- a.mov(reg, gpd(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(Assembler &a, Gp const &reg, be_parameter const &param)
-{
- if (param.is_immediate())
- a.mov(reg, param.immediate()); // mov reg,param
- else if (param.is_memory())
- {
- if (!can_skip_lower_load(a, (uint32_t *)((uintptr_t)param.memory()), reg))
- a.mov(reg, MABS(param.memory())); // mov reg,[param]
- }
- else if (param.is_int_register())
- {
- if (reg.id() != param.ireg())
- a.mov(reg, gpd(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(Assembler &a, Mem memref, be_parameter const &param)
-{
- if (param.is_immediate())
- a.mov(memref, param.immediate()); // mov [mem],param
- else if (param.is_memory())
- {
- if (!can_skip_lower_load(a, (uint32_t *)((uintptr_t)param.memory()), eax))
- a.mov(eax, MABS(param.memory())); // mov eax,[param]
- a.mov(memref, eax); // mov [mem],eax
- }
- else if (param.is_int_register())
- a.mov(memref, gpd(param.ireg())); // mov [mem],param
-}
-
-
-//-------------------------------------------------
-// emit_mov_p32_r32 - move a register into a
-// 32-bit parameter
-//-------------------------------------------------
-
-void drcbe_x86::emit_mov_p32_r32(Assembler &a, be_parameter const &param, Gp const &reg)
-{
- assert(!param.is_immediate());
- if (param.is_memory())
- {
- a.mov(MABS(param.memory()), reg); // mov [param],reg
- set_last_lower_reg(a, param, reg);
- }
- else if (param.is_int_register())
- {
- if (reg.id() != param.ireg())
- a.mov(gpd(param.ireg()), reg); // mov param,reg
- a.mov(MABS(m_reghi[param.ireg()], 4), 0);
- }
-}
-
-
-template <typename T>
-void drcbe_x86::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const &dst, be_parameter const &param, T &&optimize)
-{
- if (param.is_immediate())
- {
- if (!optimize(a, dst, param))
- a.emit(opcode, dst, param.immediate()); // op dst,param
- }
- else if (param.is_memory())
- {
- if (dst.is_mem())
- {
- // use temporary register for memory,memory
- Gp const reg = param.select_register(eax);
-
- a.mov(reg, MABS(param.memory())); // mov reg,param
- a.emit(opcode, dst, reg); // op [dst],reg
- }
- else if (opcode != Inst::kIdTest)
- // most instructions are register,memory
- a.emit(opcode, dst, MABS(param.memory())); // op dst,[param]
- else
- // test instruction requires memory,register
- a.emit(opcode, MABS(param.memory()), dst); // op [param],dst
- }
- else if (param.is_int_register())
- a.emit(opcode, dst, gpd(param.ireg())); // op dst,param
-}
-
-template <typename T>
-void drcbe_x86::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsize, Operand const &dst, be_parameter const &param, T &&optimize, bool update_flags)
-{
- if (param.is_immediate())
- {
- const uint32_t bitshift = param.immediate() & (opsize * 8 - 1);
-
- if (!optimize(a, dst, param) && bitshift != 0)
- a.emit(opcode, dst, imm(bitshift));
-
- if (update_flags)
- {
- if ((bitshift == 0) && (opcode != Inst::kIdRcl) && (opcode != Inst::kIdRcr))
- a.clc(); // throw away carry since it'll never be used
-
- calculate_status_flags(a, dst, FLAG_S | FLAG_Z); // calculate status flags but preserve carry
- }
- }
- else
- {
- Label calc = a.new_label();
- Label end = a.new_label();
-
- Gp shift = dst.id() == ecx.id() ? ebx : ecx;
-
- a.pushfd(); // preserve flags for carry
-
- emit_mov_r32_p32(a, shift, param);
-
- a.and_(shift, opsize * 8 - 1);
- a.test(shift, shift);
-
- a.short_().jnz(calc);
-
- a.popfd(); // preserved flags not needed so throw it away
-
- if (update_flags && (opcode != Inst::kIdRcl) && (opcode != Inst::kIdRcr))
- a.clc(); // throw away carry since it'll never be used
-
- a.short_().jmp(end);
-
- a.bind(calc);
-
- a.popfd(); // restore flags to keep carry for rolc/rorc
-
- a.emit(opcode, dst, shift);
-
- a.bind(end);
-
- if (update_flags)
- calculate_status_flags(a, dst, FLAG_S | FLAG_Z); // calculate status flags but preserve carry
- }
-}
-
-
-
-//**************************************************************************
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param)
-{
- if (param.is_immediate())
- {
- if (!reglo.is_valid())
- ;
- else if (u32(param.immediate()) == 0)
- a.xor_(reglo, reglo); // xor reglo,reglo
- else
- a.mov(reglo, param.immediate()); // mov reglo,param
- if (!reghi.is_valid())
- ;
- else if (u32(param.immediate() >> 32) == 0)
- a.xor_(reghi, reghi); // xor reghi,reghi
- else
- a.mov(reghi, param.immediate() >> 32); // mov reghi,param >> 32
- }
- else if (param.is_memory())
- {
- int skip_lower = can_skip_lower_load(a, (uint32_t *)((uintptr_t)param.memory(0)), reglo);
- int skip_upper = can_skip_upper_load(a, (uint32_t *)((uintptr_t)param.memory(4)), reghi);
- if (reglo.is_valid() && !skip_lower)
- a.mov(reglo, MABS(param.memory(0))); // mov reglo,[param]
- if (reghi.is_valid() && !skip_upper)
- a.mov(reghi, MABS(param.memory(4))); // mov reghi,[param+4]
- }
- else if (param.is_int_register())
- {
- int skip_upper = can_skip_upper_load(a, m_reghi[param.ireg()], reghi);
- if (reglo.is_valid() && reglo.id() != param.ireg())
- a.mov(reglo, gpd(param.ireg())); // mov reglo,param
- if (reghi.is_valid() && !skip_upper)
- a.mov(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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param)
-{
- if (param.is_immediate())
- {
- if (reglo.is_valid())
- a.mov(reglo, param.immediate()); // mov reglo,param
- if (reghi.is_valid())
- a.mov(reghi, param.immediate() >> 32); // mov reghi,param >> 32
- }
- else if (param.is_memory())
- {
- int skip_lower = can_skip_lower_load(a, (uint32_t *)((uintptr_t)param.memory(0)), reglo);
- int skip_upper = can_skip_upper_load(a, (uint32_t *)((uintptr_t)param.memory(4)), reghi);
- if (reglo.is_valid() && !skip_lower)
- a.mov(reglo, MABS(param.memory(0))); // mov reglo,[param]
- if (reghi.is_valid() && !skip_upper)
- a.mov(reghi, MABS(param.memory(4))); // mov reghi,[param+4]
- }
- else if (param.is_int_register())
- {
- int skip_upper = can_skip_upper_load(a, m_reghi[param.ireg()], reghi);
- if (reglo.is_valid() && reglo.id() != param.ireg())
- a.mov(reglo, gpd(param.ireg())); // mov reglo,param
- if (reghi.is_valid() && !skip_upper)
- a.mov(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(Assembler &a, Mem const &memref, be_parameter const &param)
-{
- Mem memref_lo = memref.clone_adjusted(0); memref_lo.set_size(4);
- Mem memref_hi = memref.clone_adjusted(4); memref_hi.set_size(4);
-
- if (param.is_immediate())
- {
- a.mov(memref_lo, param.immediate()); // mov [mem],param
- a.mov(memref_hi, param.immediate() >> 32); // mov [mem],param >> 32
- }
- else if (param.is_memory())
- {
- int skip_lower = can_skip_lower_load(a, (uint32_t *)((uintptr_t)param.memory()), eax);
- if (!skip_lower)
- a.mov(eax, MABS(param.memory(0))); // mov eax,[param]
- a.mov(memref_lo, eax); // mov [mem],eax
- a.mov(eax, MABS(param.memory(4))); // mov eax,[param+4]
- a.mov(memref_hi, eax); // mov [mem+4],eax
- }
- else if (param.is_int_register())
- {
- a.mov(memref_lo, gpd(param.ireg())); // mov [mem],param
- a.mov(eax, MABS(m_reghi[param.ireg()])); // mov eax,[param.hi]
- a.mov(memref_hi, 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(Assembler &a, be_parameter const &param, Gp const &reglo, Gp const &reghi)
-{
- assert(!param.is_immediate());
- if (param.is_memory())
- {
- a.mov(MABS(param.memory(0)), reglo); // mov [param],reglo
- a.mov(MABS(param.memory(4)), reghi); // mov [param+4],reghi
- }
- else if (param.is_int_register())
- {
- if (reglo.id() != param.ireg())
- a.mov(gpd(param.ireg()), reglo); // mov param,reglo
- a.mov(MABS(m_reghi[param.ireg()]), reghi); // mov reghi[param],reghi
- }
- set_last_lower_reg(a, param, reglo);
- set_last_upper_reg(a, param, reghi);
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, const instruction &inst)
-{
- int saveflags = ((inst.flags() & FLAG_Z) != 0);
- if (param.is_memory())
- {
- a.and_(reglo, MABS(param.memory(0))); // and reglo,[param]
- if (saveflags) a.pushfd(); // pushf
- a.and_(reghi, MABS(param.memory(4))); // and reghi,[param]
- }
- else if (param.is_immediate())
- {
- if (!inst.flags() && u32(param.immediate()) == 0xffffffffU)
- ;// skip
- else if (!inst.flags() && u32(param.immediate()) == 0)
- a.xor_(reglo, reglo); // xor reglo,reglo
- else
- a.and_(reglo, param.immediate()); // and reglo,param
- if (saveflags) a.pushfd(); // pushf
- if (!inst.flags() && u32(param.immediate() >> 32) == 0xffffffffU)
- ;// skip
- else if (!inst.flags() && u32(param.immediate() >> 32) == 0)
- a.xor_(reghi, reghi); // xor reghi,reghi
- else
- a.and_(reghi, param.immediate() >> 32); // and reghi,param >> 32
- }
- else if (param.is_int_register())
- {
- a.and_(reglo, gpd(param.ireg())); // and reglo,param
- if (saveflags) a.pushfd(); // pushf
- a.and_(reghi, MABS(m_reghi[param.ireg()])); // and reghi,reghi[param]
- }
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//-------------------------------------------------
-// emit_and_m64_p64 - and operation to a 64-bit
-// memory location from a 64-bit parameter
-//-------------------------------------------------
-
-void drcbe_x86::emit_and_m64_p64(Assembler &a, Mem const &memref_lo, Mem const &memref_hi, be_parameter const &param, const instruction &inst)
-{
- int saveflags = ((inst.flags() & FLAG_Z) != 0);
- if (param.is_immediate())
- {
- if (!inst.flags() && u32(param.immediate()) == 0xffffffffU)
- ;// skip
- else if (!inst.flags() && u32(param.immediate()) == 0)
- a.mov(memref_lo, 0); // mov [dest],0
- else
- a.and_(memref_lo, param.immediate()); // and [dest],param
- if (saveflags) a.pushfd(); // pushf
- if (!inst.flags() && u32(param.immediate() >> 32) == 0xffffffffU)
- ;// skip
- else if (!inst.flags() && u32(param.immediate() >> 32) == 0)
- a.mov(memref_hi, 0); // mov [dest+4],0
- else
- a.and_(memref_hi, param.immediate() >> 32); // and [dest+4],param >> 32
- }
- else
- {
- Gp const reglo = (param.is_int_register()) ? gpd(param.ireg()) : eax;
- emit_mov_r64_p64(a, reglo, edx, param); // mov edx:reglo,param
- a.and_(memref_lo, reglo); // and [dest],reglo
- if (saveflags) a.pushfd(); // pushf
- a.and_(memref_hi, edx); // and [dest+4],edx
- }
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, const instruction &inst)
-{
- int saveflags = ((inst.flags() & FLAG_Z) != 0);
- if (param.is_memory())
- {
- a.or_(reglo, MABS(param.memory(0))); // or reglo,[param]
- if (saveflags) a.pushfd(); // pushf
- a.or_(reghi, MABS(param.memory(4))); // or reghi,[param]
- }
- else if (param.is_immediate())
- {
- if (!inst.flags() && u32(param.immediate()) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate()) == 0xffffffffU)
- a.mov(reglo, ~0); // mov reglo,-1
- else
- a.or_(reglo, param.immediate()); // or reglo,param
- if (saveflags) a.pushfd(); // pushf
- if (!inst.flags() && u32(param.immediate() >> 32) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate() >> 32) == 0xffffffffU)
- a.mov(reghi, ~0); // mov reghi,-1
- else
- a.or_(reghi, param.immediate() >> 32); // or reghi,param >> 32
- }
- else if (param.is_int_register())
- {
- a.or_(reglo, gpd(param.ireg())); // or reglo,param
- if (saveflags) a.pushfd(); // pushf
- a.or_(reghi, MABS(m_reghi[param.ireg()])); // or reghi,reghi[param]
- }
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//-------------------------------------------------
-// emit_or_m64_p64 - or operation to a 64-bit
-// memory location from a 64-bit parameter
-//-------------------------------------------------
-
-void drcbe_x86::emit_or_m64_p64(Assembler &a, Mem const &memref_lo, Mem const &memref_hi, be_parameter const &param, const instruction &inst)
-{
- int saveflags = ((inst.flags() & FLAG_Z) != 0);
- if (param.is_immediate())
- {
- if (!inst.flags() && u32(param.immediate()) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate()) == 0xffffffffU)
- a.mov(memref_lo, ~0); // mov [dest],-1
- else
- a.or_(memref_lo, param.immediate()); // or [dest],param
- if (saveflags) a.pushfd(); // pushf
- if (!inst.flags() && u32(param.immediate() >> 32) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate() >> 32) == 0xffffffffU)
- a.mov(memref_hi, ~0); // mov [dest+4],-1
- else
- a.or_(memref_hi, param.immediate() >> 32); // or [dest+4],param >> 32
- }
- else
- {
- Gp const reglo = (param.is_int_register()) ? gpd(param.ireg()) : eax;
- emit_mov_r64_p64(a, reglo, edx, param); // mov edx:reglo,param
- a.or_(memref_lo, reglo); // or [dest],reglo
- if (saveflags) a.pushfd(); // pushf
- a.or_(memref_hi, edx); // or [dest+4],edx
- }
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, const instruction &inst)
-{
- int saveflags = ((inst.flags() & FLAG_Z) != 0);
- if (param.is_memory())
- {
- a.xor_(reglo, MABS(param.memory(0))); // xor reglo,[param]
- if (saveflags) a.pushfd(); // pushf
- a.xor_(reghi, MABS(param.memory(4))); // xor reghi,[param]
- }
- else if (param.is_immediate())
- {
- if (!inst.flags() && u32(param.immediate()) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate()) == 0xffffffffU)
- a.not_(reglo); // not reglo
- else
- a.xor_(reglo, param.immediate()); // xor reglo,param
- if (saveflags) a.pushfd(); // pushf
- if (!inst.flags() && u32(param.immediate() >> 32) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate() >> 32) == 0xffffffffU)
- a.not_(reghi); // not reghi
- else
- a.xor_(reghi, param.immediate() >> 32); // xor reghi,param >> 32
- }
- else if (param.is_int_register())
- {
- a.xor_(reglo, gpd(param.ireg())); // xor reglo,param
- if (saveflags) a.pushfd(); // pushf
- a.xor_(reghi, MABS(m_reghi[param.ireg()])); // xor reghi,reghi[param]
- }
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//-------------------------------------------------
-// emit_xor_m64_p64 - xor operation to a 64-bit
-// memory location from a 64-bit parameter
-//-------------------------------------------------
-
-void drcbe_x86::emit_xor_m64_p64(Assembler &a, Mem const &memref_lo, Mem const &memref_hi, be_parameter const &param, const instruction &inst)
-{
- int saveflags = ((inst.flags() & FLAG_Z) != 0);
- if (param.is_immediate())
- {
- if (!inst.flags() && u32(param.immediate()) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate()) == 0xffffffffU)
- a.not_(memref_lo); // not [dest]
- else
- a.xor_(memref_lo, param.immediate()); // xor [dest],param
- if (saveflags) a.pushfd(); // pushf
- if (!inst.flags() && u32(param.immediate() >> 32) == 0)
- ;// skip
- else if (!inst.flags() && u32(param.immediate() >> 32) == 0xffffffffU)
- a.not_(memref_hi); // not [dest+4]
- else
- a.xor_(memref_hi, param.immediate() >> 32); // xor [dest+4],param >> 32
- }
- else
- {
- Gp const reglo = (param.is_int_register()) ? gpd(param.ireg()) : eax;
- emit_mov_r64_p64(a, reglo, edx, param); // mov edx:reglo,param
- a.xor_(memref_lo, reglo); // xor [dest],reglo
- if (saveflags) a.pushfd(); // pushf
- a.xor_(memref_hi, edx); // xor [dest+4],edx
- }
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- bool const saveflags = flags != 0;
- if (param.is_immediate())
- {
- int count = param.immediate() & 63;
- if (!flags && count == 0)
- ;// skip
- else
- {
- while (count >= 32)
- {
- if (flags != 0)
- {
- a.shld(reghi, reglo, 31); // shld reghi,reglo,31
- a.shl(reglo, 31); // shl reglo,31
- count -= 31;
- }
- else
- {
- a.mov(reghi, reglo); // mov reghi,reglo
- a.xor_(reglo, reglo); // xor reglo,reglo
- count -= 32;
- }
- }
- if (flags != 0 || count > 0)
- {
- a.shld(reghi, reglo, count); // shld reghi,reglo,count
- if (saveflags && count != 0) a.pushfd(); // pushf
- a.shl(reglo, count); // shl reglo,count
- }
- }
-
- if (saveflags)
- {
- if (count == 0)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
- else
- {
- emit_combine_z_shl_flags(a);
- }
- }
- }
- else
- {
- Label skipall = a.new_label();
- Label end = a.new_label();
- Label skip1 = a.new_label();
- Label skip2 = a.new_label();
-
- emit_mov_r32_p32(a, ecx, param); // mov ecx,param
-
- a.and_(ecx, 63);
- a.test(ecx, ecx);
- a.short_().jz(skipall);
-
- a.test(ecx, 0x20); // test ecx,0x20
- a.short_().jz(skip1); // jz skip1
- if (flags != 0)
- {
- a.sub(ecx, 31); // sub ecx,31
- a.shld(reghi, reglo, 31); // shld reghi,reglo,31
- a.shl(reglo, 31); // shl reglo,31
- a.test(ecx, 0x20); // test ecx,0x20
- a.short_().jz(skip2); // jz skip2
- a.sub(ecx, 31); // sub ecx,31
- a.shld(reghi, reglo, 31); // shld reghi,reglo,31
- a.shl(reglo, 31); // shl reglo,31
- a.bind(skip2); // skip2:
- reset_last_upper_lower_reg();
- }
- else
- {
- a.mov(reghi, reglo); // mov reghi,reglo
- a.xor_(reglo, reglo); // xor reglo,reglo
- }
- a.bind(skip1); // skip1:
- reset_last_upper_lower_reg();
- a.shld(reghi, reglo, cl); // shld reghi,reglo,cl
- if (saveflags) a.pushfd(); // pushf
- a.shl(reglo, cl); // shl reglo,cl
-
- if (saveflags)
- {
- emit_combine_z_shl_flags(a);
-
- a.short_().jmp(end);
- }
-
- a.bind(skipall);
-
- if (saveflags)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- a.bind(end);
- }
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- bool const saveflags = flags != 0;
- if (param.is_immediate())
- {
- int count = param.immediate() & 63;
- if (!flags && count == 0)
- ;// skip
- else
- {
- while (count >= 32)
- {
- if (flags != 0)
- {
- a.shrd(reglo, reghi, 31); // shrd reglo,reghi,31
- a.shr(reghi, 31); // shr reghi,31
- count -= 31;
- }
- else
- {
- a.mov(reglo, reghi); // mov reglo,reghi
- a.xor_(reghi, reghi); // xor reghi,reghi
- count -= 32;
- }
- }
- if (flags != 0 || count > 0)
- {
- a.shrd(reglo, reghi, count); // shrd reglo,reghi,count
- if (saveflags && count != 0) a.pushfd(); // pushf
- a.shr(reghi, count); // shr reghi,count
- }
- }
-
- if (saveflags)
- {
- if (count == 0)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
- else
- {
- // take carry from lower register's flags
- a.pushfd();
- a.mov(ecx, dword_ptr(esp, 4));
- a.and_(ecx, 0x01); // carry flag
- a.and_(dword_ptr(esp, 0), ~0x01);
- a.or_(dword_ptr(esp, 0), ecx);
- a.popfd();
-
- emit_combine_z_flags(a);
- }
- }
- }
- else
- {
- Label skipall = a.new_label();
- Label end = a.new_label();
- Label skip1 = a.new_label();
- Label skip2 = a.new_label();
-
- emit_mov_r32_p32(a, ecx, param); // mov ecx,param
-
- a.and_(ecx, 63);
- a.test(ecx, ecx);
- a.short_().jz(skipall);
-
- a.test(ecx, 0x20); // test ecx,0x20
- a.short_().jz(skip1); // jz skip1
- if (flags != 0)
- {
- a.sub(ecx, 31); // sub ecx,31
- a.shrd(reglo, reghi, 31); // shrd reglo,reghi,31
- a.shr(reghi, 31); // shr reghi,31
- a.test(ecx, 0x20); // test ecx,0x20
- a.short_().jz(skip2); // jz skip2
- a.sub(ecx, 31); // sub ecx,31
- a.shrd(reglo, reghi, 31); // shrd reglo,reghi,31
- a.shr(reghi, 31); // shr reghi,31
- a.bind(skip2); // skip2:
- reset_last_upper_lower_reg();
- }
- else
- {
- a.mov(reglo, reghi); // mov reglo,reghi
- a.xor_(reghi, reghi); // xor reghi,reghi
- }
- a.bind(skip1); // skip1:
- reset_last_upper_lower_reg();
- a.shrd(reglo, reghi, cl); // shrd reglo,reghi,cl
- if (saveflags) a.pushfd(); // pushf
- a.shr(reghi, cl); // shr reghi,cl
-
- if (saveflags)
- {
- // take carry from lower register's flags
- a.pushfd();
- a.mov(ecx, dword_ptr(esp, 4));
- a.and_(ecx, 0x01); // carry flag
- a.and_(dword_ptr(esp, 0), ~0x01);
- a.or_(dword_ptr(esp, 0), ecx);
- a.popfd();
-
- emit_combine_z_flags(a);
-
- a.short_().jmp(end);
- }
-
- a.bind(skipall);
-
- if (saveflags)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- a.bind(end);
- }
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- bool const saveflags = flags != 0;
- if (param.is_immediate())
- {
- int count = param.immediate() & 63;
- if (!flags && count == 0)
- ;// skip
- else
- {
- while (count >= 32)
- {
- if (flags != 0)
- {
- a.shrd(reglo, reghi, 31); // shrd reglo,reghi,31
- a.sar(reghi, 31); // sar reghi,31
- count -= 31;
- }
- else
- {
- a.mov(reglo, reghi); // mov reglo,reghi
- a.sar(reghi, 31); // sar reghi,31
- count -= 32;
- }
- }
- if (flags != 0 || count > 0)
- {
- a.shrd(reglo, reghi, count); // shrd reglo,reghi,count
- if (saveflags && count != 0) a.pushfd(); // pushf
- a.sar(reghi, count); // sar reghi,count
- }
- }
-
- if (saveflags)
- {
- if (count == 0)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
- else
- {
- // take carry from lower register's flags
- a.pushfd();
- a.mov(ecx, dword_ptr(esp, 4));
- a.and_(ecx, 0x01); // carry flag
- a.and_(dword_ptr(esp, 0), ~0x01);
- a.or_(dword_ptr(esp, 0), ecx);
- a.popfd();
-
- emit_combine_z_flags(a);
- }
- }
- }
- else
- {
- Label skip1 = a.new_label();
- Label skip2 = a.new_label();
- Label skipall = a.new_label();
- Label end = a.new_label();
-
- emit_mov_r32_p32(a, ecx, param); // mov ecx,param
-
- a.and_(ecx, 63);
- a.test(ecx, ecx);
- a.short_().jz(skipall);
-
- a.test(ecx, 0x20); // test ecx,0x20
- a.short_().jz(skip1); // jz skip1
- if (flags != 0)
- {
- a.sub(ecx, 31); // sub ecx,31
- a.shrd(reglo, reghi, 31); // shrd reglo,reghi,31
- a.sar(reghi, 31); // sar reghi,31
- a.test(ecx, 0x20); // test ecx,0x20
- a.short_().jz(skip2); // jz skip
- a.sub(ecx, 31); // sub ecx,31
- a.shrd(reglo, reghi, 31); // shrd reglo,reghi,31
- a.sar(reghi, 31); // sar reghi,31
- a.bind(skip2); // skip2:
- reset_last_upper_lower_reg();
- }
- else
- {
- a.mov(reglo, reghi); // mov reglo,reghi
- a.sar(reghi, 31); // sar reghi,31
- }
- a.bind(skip1); // skip1:
- reset_last_upper_lower_reg();
- a.shrd(reglo, reghi, cl); // shrd reglo,reghi,cl
- if (saveflags) a.pushfd(); // pushf
- a.sar(reghi, cl); // sar reghi,cl
-
- if (saveflags)
- {
- // take carry from lower register's flags
- a.pushfd();
- a.mov(ecx, dword_ptr(esp, 4));
- a.and_(ecx, 0x01); // carry flag
- a.and_(dword_ptr(esp, 0), ~0x01);
- a.or_(dword_ptr(esp, 0), ecx);
- a.popfd();
-
- emit_combine_z_flags(a);
-
- a.short_().jmp(end);
- }
-
- a.bind(skipall);
-
- if (saveflags)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- a.bind(end);
- }
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- bool const saveflags = flags != 0;
-
- Gp tempreg = esi;
- if ((reglo == tempreg) || (reghi == tempreg))
- tempreg = edi;
- if ((reglo == tempreg) || (reghi == tempreg))
- tempreg = ebp;
- assert(reglo != tempreg);
- assert(reghi != tempreg);
- a.push(tempreg);
-
- if (param.is_immediate())
- {
- int count = param.immediate() & 63;
- if (!flags && count == 0)
- {
- // skip
- }
- else
- {
- while (count >= 32)
- {
- if (flags != 0)
- {
- a.mov(ecx, reglo);
- a.shld(reglo, reghi, 31);
- a.shld(reghi, ecx, 31);
- count -= 31;
- }
- else
- {
- a.xchg(reghi, reglo);
- count -= 32;
- }
- }
-
- a.mov(ecx, reglo);
- a.shld(reglo, reghi, count);
- if (saveflags && count) a.pushfd();
- a.shld(reghi, ecx, count);
- }
-
- if (saveflags)
- {
- if (!count)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
- else
- {
- emit_combine_zs_flags(a);
- }
- }
- }
- else
- {
- Label const skipall = a.new_label();
- Label const end = a.new_label();
- Label const skip1 = a.new_label();
-
- emit_mov_r32_p32(a, ecx, param);
-
- a.and_(ecx, 63);
- a.test(ecx, ecx);
- a.short_().jz(skipall);
-
- a.cmp(ecx, 32);
- a.short_().jl(skip1);
-
- if (flags)
- {
- Label const shift_loop = a.new_label();
-
- a.bind(shift_loop);
- a.sub(ecx, 31);
- a.mov(tempreg, reglo);
- a.shld(reglo, reghi, 31);
- a.shld(reghi, tempreg, 31);
- a.cmp(ecx, 32);
- a.short_().jge(shift_loop);
- }
- else
- {
- a.xchg(reghi, reglo);
- a.sub(ecx, 32);
- }
-
- a.bind(skip1);
- reset_last_upper_lower_reg();
- a.mov(tempreg, reglo);
- a.shld(reglo, reghi, cl);
- if (saveflags) a.pushfd();
- a.shld(reghi, tempreg, cl);
-
- if (saveflags)
- {
- emit_combine_zs_flags(a);
- a.short_().jmp(end);
- }
-
- a.bind(skipall);
-
- if (saveflags)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- a.bind(end);
- }
-
- a.pop(tempreg);
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- bool const saveflags = flags != 0;
-
- Gp tempreg = esi;
- if ((reglo == tempreg) || (reghi == tempreg))
- tempreg = edi;
- if ((reglo == tempreg) || (reghi == tempreg))
- tempreg = ebp;
- assert(reglo != tempreg);
- assert(reghi != tempreg);
- a.push(tempreg);
-
- if (param.is_immediate())
- {
- int count = param.immediate() & 63;
- if (!flags && count == 0)
- {
- // skip
- }
- else
- {
- while (count >= 32)
- {
- if (flags != 0)
- {
- a.mov(tempreg, reglo);
- a.shrd(reglo, reghi, 31);
- a.shrd(reghi, tempreg, 31);
- count -= 31;
- }
- else
- {
- a.xchg(reghi, reglo);
- count -= 32;
- }
- }
-
- a.mov(tempreg, reghi);
- a.shrd(reghi, reglo, count);
- if (saveflags && count != 0) a.pushfd();
- a.shrd(reglo, tempreg, count);
-
- if (saveflags)
- {
- if (count == 0)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
- else
- {
- emit_combine_zs_flags(a);
- }
- }
- }
- }
- else
- {
- Label skipall = a.new_label();
- Label end = a.new_label();
- Label skip1 = a.new_label();
- Label shift_loop = a.new_label();
-
- emit_mov_r32_p32(a, ecx, param);
-
- a.and_(ecx, 63);
- a.test(ecx, ecx);
- a.short_().jz(skipall);
-
- a.cmp(ecx, 32);
- a.short_().jl(skip1);
-
- a.bind(shift_loop);
- if (flags != 0)
- {
- a.sub(ecx, 31);
- a.mov(tempreg, reglo);
- a.shrd(reglo, reghi, 31);
- a.shrd(reghi, tempreg, 31);
- }
- else
- {
- a.xchg(reghi, reglo);
- a.sub(ecx, 32);
- }
- a.cmp(ecx, 32);
- a.short_().jge(shift_loop);
-
- a.bind(skip1);
- reset_last_upper_lower_reg();
- a.mov(tempreg, reghi);
- a.shrd(reghi, reglo, cl);
- if (saveflags) a.pushfd();
- a.shrd(reglo, tempreg, cl);
-
- if (saveflags)
- {
- emit_combine_zs_flags(a);
- a.short_().jmp(end);
- }
-
- a.bind(skipall);
-
- if (saveflags)
- {
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- a.bind(end);
- }
-
- a.pop(tempreg);
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- Label loop = a.new_label();
- Label skipall = a.new_label();
- Label skiploop = a.new_label();
-
- a.pushfd(); // keep carry flag after and
- emit_mov_r32_p32(a, ecx, param);
-
- a.and_(ecx, 63);
- a.popfd();
-
- a.short_().jecxz(skipall);
- a.lea(ecx, ptr(ecx, -1));
-
- a.bind(loop);
- a.short_().jecxz(skiploop);
- a.lea(ecx, ptr(ecx, -1));
- a.rcl(reglo, 1);
- a.rcl(reghi, 1);
- a.short_().jmp(loop);
-
- a.bind(skiploop);
- reset_last_upper_lower_reg();
- a.rcl(reglo, 1);
- a.rcl(reghi, 1);
-
- a.bind(skipall);
- if (flags)
- {
- if (flags & FLAG_C)
- calculate_status_flags(a, reglo, FLAG_Z);
- else
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- reset_last_upper_lower_reg();
-}
-
-
-//-------------------------------------------------
-// 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(Assembler &a, Gp const &reglo, Gp const &reghi, be_parameter const &param, u8 flags)
-{
- Label loop = a.new_label();
- Label skipall = a.new_label();
- Label skiploop = a.new_label();
-
- a.pushfd(); // keep carry flag after and
- emit_mov_r32_p32(a, ecx, param);
-
- a.and_(ecx, 63);
- a.popfd();
-
- a.short_().jecxz(skipall);
- a.lea(ecx, ptr(ecx, -1));
-
- a.bind(loop);
- a.short_().jecxz(skiploop);
- a.lea(ecx, ptr(ecx, -1));
- a.rcr(reghi, 1);
- a.rcr(reglo, 1);
- a.short_().jmp(loop);
-
- a.bind(skiploop);
- reset_last_upper_lower_reg();
- a.rcr(reghi, 1);
- a.rcr(reglo, 1);
-
- a.bind(skipall);
- if (flags)
- {
- if (flags & FLAG_C)
- calculate_status_flags(a, reglo, FLAG_Z);
- else
- a.test(reglo, reglo);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
- }
-
- reset_last_upper_lower_reg();
-}
-
-
-void drcbe_x86::alu_op_param(Assembler &a, Inst::Id const opcode_lo, Inst::Id const opcode_hi, Gp const &lo, Gp const &hi, be_parameter const &param, bool saveflags)
-{
- if (param.is_memory())
- {
- if (opcode_lo == Inst::kIdTest) // can't use memory on right of test
- a.emit(opcode_lo, MABS(param.memory(0)), lo); // opl [param],reglo
- else
- a.emit(opcode_lo, lo, MABS(param.memory(0))); // opl reglo,[param]
- if (saveflags) a.pushfd(); // pushf
- if (opcode_hi == Inst::kIdTest) // can't use memory on right of test
- a.emit(opcode_hi, MABS(param.memory(4)), hi); // oph [param],reghi
- else
- a.emit(opcode_hi, hi, MABS(param.memory(4))); // oph reghi,[param]
- }
- else if (param.is_immediate())
- {
- a.emit(opcode_lo, lo, param.immediate()); // opl reglo,param
- if (saveflags) a.pushfd(); // pushf
- a.emit(opcode_hi, hi, param.immediate() >> 32); // oph reghi,param >> 32
- }
- else if (param.is_int_register())
- {
- a.emit(opcode_lo, lo, gpd(param.ireg())); // opl reglo,param
- if (saveflags) a.pushfd(); // pushf
-
- if (opcode_hi == Inst::kIdTest) // can't use memory on right of test
- a.emit(opcode_hi, MABS(m_reghi[param.ireg()]), hi); // oph reghi[param],reghi
- else
- a.emit(opcode_hi, hi, MABS(m_reghi[param.ireg()])); // oph reghi,reghi[param]
- }
-
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-void drcbe_x86::alu_op_param(Assembler &a, Inst::Id const opcode_lo, Inst::Id const opcode_hi, Mem const &lo, Mem const &hi, be_parameter const &param, bool saveflags)
-{
- if (param.is_immediate())
- {
- a.emit(opcode_lo, lo, param.immediate()); // opl [dest],param
- if (saveflags) a.pushfd(); // pushf
- a.emit(opcode_hi, hi, param.immediate() >> 32); // oph [dest+4],param >> 32
- }
- else
- {
- Gp const reg = (param.is_int_register()) ? gpd(param.ireg()) : eax;
-
- emit_mov_r64_p64(a, reg, edx, param); // mov edx:reglo,param
- a.emit(opcode_lo, lo, reg); // opl [dest],reglo
- if (saveflags) a.pushfd(); // pushf
- a.emit(opcode_hi, hi, edx); // oph [dest+4],edx
- }
-
- if (saveflags)
- emit_combine_z_flags(a);
-}
-
-
-//**************************************************************************
-// EMITTERS FOR FLOATING POINT
-//**************************************************************************
-
-//-------------------------------------------------
-// emit_fld_p - load a floating point parameter
-// onto the stack
-//-------------------------------------------------
-
-void drcbe_x86::emit_fld_p(Assembler &a, int size, be_parameter const &param)
-{
- assert(param.is_memory());
- assert(size == 4 || size == 8);
- a.fld(ptr(uintptr_t(param.memory()), size));
-}
-
-
-//-------------------------------------------------
-// emit_fstp_p - store a floating point parameter
-// from the stack and pop it
-//-------------------------------------------------
-
-void drcbe_x86::emit_fstp_p(Assembler &a, int size, be_parameter const &param)
-{
- assert(param.is_memory());
- assert(size == 4 || size == 8);
-
- a.fstp(ptr(uintptr_t(param.memory()), size));
-}
-
-
-
-//**************************************************************************
-// DEBUG HELPERS
-//**************************************************************************
-
-//-------------------------------------------------
-// end_of_block - function to catch falling off
-// the end of a generated code block
-//-------------------------------------------------
-
-[[noreturn]] void drcbe_x86::end_of_block() const
-{
- osd_printf_error("drcbe_x86(%s): Fell off the end of a generated code block!\n", m_device.tag());
- std::fflush(stdout);
- std::fflush(stderr);
- std::abort();
-}
-
-
-//-------------------------------------------------
-// debug_log_hashjmp - callback to handle
-// logging of hashjmps
-//-------------------------------------------------
-
-void drcbe_x86::debug_log_hashjmp(char const *tag, int mode, offs_t pc)
-{
- osd_printf_info("drcbe_x86(%s): HASHJMP mode=%d PC=%08X\n", tag, mode, pc);
-}
-
-
-
-//**************************************************************************
-// COMPILE-TIME OPCODES
-//**************************************************************************
-
-//-------------------------------------------------
-// op_handle - process a HANDLE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_handle(Assembler &a, 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();
-
- // make a label for documentation
- Label handle = a.new_named_label(inst.param(0).handle().string());
- a.bind(handle);
-
- // emit a jump around the stack adjust in case code falls through here
- Label skip = a.new_label();
- a.short_().jmp(skip);
-
- // register the current pointer for the handle
- inst.param(0).handle().set_codeptr(drccodeptr(a.code()->base_address() + a.offset()));
-
- // by default, the handle points to prolog code that moves the stack pointer
- a.lea(esp, ptr(esp, -28));
- a.bind(skip);
- reset_last_upper_lower_reg();
-}
-
-
-//-------------------------------------------------
-// op_hash - process a HASH opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_hash(Assembler &a, 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(), drccodeptr(a.code()->base_address() + a.offset()));
- reset_last_upper_lower_reg();
-}
-
-
-//-------------------------------------------------
-// op_label - process a LABEL opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_label(Assembler &a, const instruction &inst)
-{
- assert_no_condition(inst);
- assert_no_flags(inst);
- assert(inst.numparams() == 1);
- assert(inst.param(0).is_code_label());
-
- std::string labelName = util::string_format("PC$%x", inst.param(0).label());
- Label label = a.label_by_name(labelName.c_str());
- if (!label.is_valid())
- label = a.new_named_label(labelName.c_str());
-
- // register the current pointer for the label
- a.bind(label);
-
- reset_last_upper_lower_reg();
-}
-
-
-//-------------------------------------------------
-// op_comment - process a COMMENT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_comment(Assembler &a, 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(Assembler &a, 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(drccodeptr(a.code()->base_address() + a.offset()), inst.param(0).mapvar(), inst.param(1).immediate());
-}
-
-
-
-//**************************************************************************
-// CONTROL FLOW OPCODES
-//**************************************************************************
-
-//-------------------------------------------------
-// op_nop - process a NOP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_nop(Assembler &a, const instruction &inst)
-{
- // nothing
-}
-
-//-------------------------------------------------
-// op_break - process a BREAK opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_break(Assembler &a, const instruction &inst)
-{
- static const char *const message = "break from drc";
- a.mov(dword_ptr(esp, 0), imm(message));
- a.call(imm(&osd_break_into_debugger));
-}
-
-//-------------------------------------------------
-// op_debug - process a DEBUG opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_debug(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4);
- assert_no_condition(inst);
- assert_no_flags(inst);
-
- if ((m_device.machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
- {
- // normalize parameters
- be_parameter const pcp(*this, inst.param(0), PTYPE_MRI);
-
- // test and branch
- a.test(MABS(&m_device.machine().debug_flags, 4), DEBUG_FLAG_CALL_HOOK);
- Label skip = a.new_label();
- a.short_().jz(skip);
-
- // push the parameter
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), pcp);
- if (USE_THISCALL)
- a.mov(ecx, imm(m_debug_cpu_instruction_hook.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(m_debug_cpu_instruction_hook.obj));
- a.call(imm(m_debug_cpu_instruction_hook.func));
- if (USE_THISCALL)
- a.sub(esp, 4);
-
- a.bind(skip);
- reset_last_upper_lower_reg();
- }
-}
-
-
-//-------------------------------------------------
-// op_exit - process an EXIT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_exit(Assembler &a, 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(a, eax, retp); // mov eax,retp
- if (inst.condition() == uml::COND_ALWAYS)
- a.jmp(imm(m_exit)); // jmp exit
- else
- a.j(X86_CONDITION(inst.condition()), imm(m_exit)); // jcc exit
-}
-
-
-//-------------------------------------------------
-// op_hashjmp - process a HASHJMP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_hashjmp(Assembler &a, 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);
- parameter const &exp = inst.param(2);
- assert(exp.is_code_handle());
-
- if (LOG_HASHJMPS)
- {
- a.mov(dword_ptr(esp, 8), imm(uintptr_t(m_device.tag())));
- emit_mov_m32_p32(a, dword_ptr(esp, 4), pcp);
- emit_mov_m32_p32(a, dword_ptr(esp, 0), modep);
- a.call(imm(debug_log_hashjmp));
- }
-
- // load the stack base one word early so we end up at the right spot after our call below
- a.mov(esp, MABS(&m_hashstacksave));
-
- if (modep.is_immediate() && m_hash.populate_mode(modep.immediate()))
- {
- // fixed mode cases
- if (pcp.is_immediate() && !m_invariant_block)
- {
- // a straight immediate jump is direct, though we need the PC in EAX in case of failure
- uint32_t const l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
- uint32_t const l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
- a.call(MABS(&m_hash.base()[modep.immediate()][l1val][l2val]));
- }
- else
- {
- // a fixed mode but variable PC
- emit_mov_r32_p32(a, eax, pcp);
- a.mov(edx, eax);
- a.shr(edx, m_hash.l1shift());
- a.and_(eax, m_hash.l2mask() << m_hash.l2shift());
- a.mov(edx, ptr(uintptr_t(&m_hash.base()[modep.immediate()][0]), edx, 2));
- a.call(ptr(edx, eax, 2 - m_hash.l2shift()));
- }
- }
- else
- {
- // variable mode
- Gp const modereg = modep.select_register(ecx);
- emit_mov_r32_p32(a, modereg, modep);
- a.mov(ecx, ptr(uintptr_t(m_hash.base()), modereg, 2));
-
- if (pcp.is_immediate())
- {
- // fixed PC
- uint32_t const l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
- uint32_t const l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
- a.mov(edx, ptr(ecx, l1val * 4));
- a.call(ptr(edx, l2val * 4));
- }
- else
- {
- // variable PC
- emit_mov_r32_p32(a, eax, pcp);
- a.mov(edx, eax);
- a.shr(edx, m_hash.l1shift());
- a.mov(edx, ptr(ecx, edx, 2));
- a.and_(eax, m_hash.l2mask() << m_hash.l2shift());
- a.call(ptr(edx, eax, 2 - m_hash.l2shift()));
- }
- }
-
- // in all cases, if there is no code, we return here to generate the exception
- emit_mov_m32_p32(a, MABS(&m_state.exp, 4), pcp);
- a.sub(esp, 4);
- a.call(MABS(exp.handle().codeptr_addr()));
-}
-
-
-//-------------------------------------------------
-// op_jmp - process a JMP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_jmp(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4);
- assert_any_condition(inst);
- assert_no_flags(inst);
-
- // normalize parameters
- parameter const &labelp = inst.param(0);
- assert(labelp.is_code_label());
-
- std::string labelName = util::string_format("PC$%x", labelp.label());
- Label jmptarget = a.label_by_name(labelName.c_str());
- if (!jmptarget.is_valid())
- jmptarget = a.new_named_label(labelName.c_str());
-
- if (inst.condition() == uml::COND_ALWAYS)
- a.jmp(jmptarget);
- else
- a.j(X86_CONDITION(inst.condition()), jmptarget);
-}
-
-
-//-------------------------------------------------
-// op_exh - process an EXH opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_exh(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4);
- assert_any_condition(inst);
- assert_no_flags(inst);
-
- // normalize parameters
- parameter const &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
- Label no_exception;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- no_exception = a.new_label();
- a.short_().j(X86_NOT_CONDITION(inst.condition()), no_exception); // jcc no_exception
- }
- emit_mov_m32_p32(a, MABS(&m_state.exp, 4), exp); // mov [exp],exp
- if (*targetptr != nullptr)
- a.call(imm(*targetptr)); // call *targetptr
- else
- a.call(MABS(targetptr)); // call [targetptr]
- if (inst.condition() != uml::COND_ALWAYS)
- a.bind(no_exception);
-}
-
-
-//-------------------------------------------------
-// op_callh - process a CALLH opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_callh(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4);
- assert_any_condition(inst);
- assert_no_flags(inst);
-
- // normalize parameters
- parameter const &handp = inst.param(0);
- assert(handp.is_code_handle());
-
- // look up the handle target
- drccodeptr *targetptr = handp.handle().codeptr_addr();
-
- // skip if conditional
- Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.new_label();
- a.short_().j(X86_NOT_CONDITION(inst.condition()), skip); // jcc skip
- }
-
- // jump through the handle; directly if a normal jump
- if (*targetptr != nullptr)
- a.call(imm(*targetptr)); // call *targetptr
- else
- a.call(MABS(targetptr)); // call [targetptr]
-
- // resolve the conditional link
- if (inst.condition() != uml::COND_ALWAYS)
- {
- a.bind(skip); // skip:
- reset_last_upper_lower_reg();
- }
-}
-
-
-//-------------------------------------------------
-// op_ret - process a RET opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_ret(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4);
- assert_any_condition(inst);
- assert_no_flags(inst);
- assert(inst.numparams() == 0);
-
- // skip if conditional
- Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.new_label();
- a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);
- }
-
- // return
- a.lea(esp, ptr(esp, 28)); // lea rsp,[rsp+28]
- a.ret(); // ret
-
- // resolve the conditional link
- if (inst.condition() != uml::COND_ALWAYS)
- {
- a.bind(skip); // skip:
- reset_last_upper_lower_reg();
- }
-}
-
-
-//-------------------------------------------------
-// op_callc - process a CALLC opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_callc(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4);
- assert_any_condition(inst);
- assert_no_flags(inst);
-
- // normalize parameters
- parameter const &funcp = inst.param(0);
- assert(funcp.is_c_function());
- be_parameter paramp(*this, inst.param(1), PTYPE_M);
-
- // skip if conditional
- Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.new_label();
- a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);
- }
-
- // perform the call
- a.mov(dword_ptr(esp, 0), imm(paramp.memory()));
- a.call(imm(funcp.cfunc()));
-
- // resolve the conditional link
- if (inst.condition() != uml::COND_ALWAYS)
- {
- a.bind(skip);
- reset_last_upper_lower_reg();
- }
-}
-
-
-//-------------------------------------------------
-// op_recover - process a RECOVER opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_recover(Assembler &a, 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
- a.mov(eax, MABS(&m_stacksave));
- a.mov(eax, ptr(eax, -4));
- a.sub(eax, 1);
- a.mov(dword_ptr(esp, USE_THISCALL ? 4 : 8), inst.param(1).mapvar());
- a.mov(ptr(esp, USE_THISCALL ? 0 : 4), eax);
- if (USE_THISCALL)
- a.mov(ecx, imm(m_drcmap_get_value.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(m_drcmap_get_value.obj));
- a.call(imm(m_drcmap_get_value.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- emit_mov_p32_r32(a, dstp, eax);
-}
-
-
-
-//**************************************************************************
-// INTERNAL REGISTER OPCODES
-//**************************************************************************
-
-//-------------------------------------------------
-// op_setfmod - process a SETFMOD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_setfmod(Assembler &a, 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;
- a.mov(MABS(&m_state.fmod, 1), value); // mov [fmod],srcp
- a.fldcw(MABS(&fp_control[value], 2)); // fldcw fp_control[srcp]
- }
-
- // register/memory case
- else
- {
- emit_mov_r32_p32(a, eax, srcp); // mov eax,srcp
- a.and_(eax, 3); // and eax,3
- a.mov(MABS(&m_state.fmod), al); // mov [fmod],al
- a.fldcw(ptr(uintptr_t(&fp_control[0]), eax, 1, 2)); // fldcw fp_control[eax]
- }
-}
-
-
-//-------------------------------------------------
-// op_getfmod - process a GETFMOD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_getfmod(Assembler &a, 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())
- a.movzx(gpd(dstp.ireg()), MABS(&m_state.fmod, 1)); // movzx reg,[fmod]
- else
- {
- a.movzx(eax, MABS(&m_state.fmod, 1)); // movzx eax,[fmod]
- a.mov(MABS(dstp.memory()), eax); // mov [dstp],eax
- }
-}
-
-
-//-------------------------------------------------
-// op_getexp - process a GETEXP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_getexp(Assembler &a, 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())
- a.mov(gpd(dstp.ireg()), MABS(&m_state.exp)); // mov reg,[exp]
- else
- {
- a.mov(eax, MABS(&m_state.exp)); // mov eax,[exp]
- a.mov(MABS(dstp.memory()), eax); // mov [dstp],eax
- }
-}
-
-
-//-------------------------------------------------
-// op_getflgs - process a GETFLGS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_getflgs(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax);
-
- a.pushfd();
-
- // 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:
- a.setc(al); // setc al
- a.movzx(dstreg, al); // movzx dstreg,al
- break;
-
- case FLAG_V:
- a.seto(al); // seto al
- a.movzx(dstreg, al); // movzx dstreg,al
- a.shl(dstreg, 1); // shl dstreg,1
- break;
-
- case FLAG_Z:
- a.setz(al); // setz al
- a.movzx(dstreg, al); // movzx dstreg,al
- a.shl(dstreg, 2); // shl dstreg,2
- break;
-
- case FLAG_S:
- a.sets(al); // sets al
- a.movzx(dstreg, al); // movzx dstreg,al
- a.shl(dstreg, 3); // shl dstreg,3
- break;
-
- case FLAG_U:
- a.setp(al); // setp al
- a.movzx(dstreg, al); // movzx dstreg,al
- a.shl(dstreg, 4); // shl dstreg,4
- break;
-
- // carry plus another flag
- case FLAG_C | FLAG_V:
- a.setc(al); // setc al
- a.seto(cl); // seto cl
- a.movzx(eax, al); // movzx eax,al
- a.movzx(ecx, cl); // movzx ecx,al
- a.lea(dstreg, ptr(eax, ecx, 1)); // lea dstreg,[eax+ecx*2]
- break;
-
- case FLAG_C | FLAG_Z:
- a.setc(al); // setc al
- a.setz(cl); // setz cl
- a.movzx(eax, al); // movzx eax,al
- a.movzx(ecx, cl); // movzx ecx,al
- a.lea(dstreg, ptr(eax, ecx, 2)); // lea dstreg,[eax+ecx*4]
- break;
-
- case FLAG_C | FLAG_S:
- a.setc(al); // setc al
- a.sets(cl); // sets cl
- a.movzx(eax, al); // movzx eax,al
- a.movzx(ecx, cl); // movzx ecx,al
- a.lea(dstreg, ptr(eax, ecx, 3)); // lea dstreg,[eax+ecx*8]
- break;
-
- // overflow plus another flag
- case FLAG_V | FLAG_Z:
- a.seto(al); // seto al
- a.setz(cl); // setz cl
- a.movzx(eax, al); // movzx eax,al
- a.movzx(ecx, cl); // movzx ecx,al
- a.lea(dstreg, ptr(eax, ecx, 1)); // lea dstreg,[eax+ecx*2]
- a.shl(dstreg, 1); // shl dstreg,1
- break;
-
- case FLAG_V | FLAG_S:
- a.seto(al); // seto al
- a.sets(cl); // sets cl
- a.movzx(eax, al); // movzx eax,al
- a.movzx(ecx, cl); // movzx ecx,al
- a.lea(dstreg, ptr(eax, ecx, 2)); // lea dstreg,[eax+ecx*4]
- a.shl(dstreg, 1); // shl dstreg,1
- break;
-
- // zero plus another flag
- case FLAG_Z | FLAG_S:
- a.setz(al); // setz al
- a.sets(cl); // sets cl
- a.movzx(eax, al); // movzx eax,al
- a.movzx(ecx, cl); // movzx ecx,al
- a.lea(dstreg, ptr(eax, ecx, 1)); // lea dstreg,[eax+ecx*2]
- a.shl(dstreg, 2); // shl dstreg,2
- break;
-
- // default cases
- default:
- a.pushfd(); // pushf
- a.pop(eax); // pop eax
- a.and_(eax, flagmask); // and eax,flagmask
- a.movzx(dstreg, byte_ptr(uintptr_t(flags_map), eax)); // movzx dstreg,[flags_map]
- break;
- }
-
- // store low 32 bits
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- // 64-bit form stores upper 32 bits
- if (inst.size() == 8)
- {
- // general case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov [dstp+4],0
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov [reghi],0
- }
-
- a.popfd();
-}
-
-
-//-------------------------------------------------
-// op_setflgs - process a SETFLGS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_setflgs(Assembler &a, const instruction &inst)
-{
- assert(inst.size() == 4);
- assert_no_condition(inst);
-
- be_parameter srcp(*this, inst.param(0), PTYPE_MRI);
-
- a.pushfd();
- a.and_(dword_ptr(esp), ~0x8c5);
-
- if (srcp.is_immediate())
- {
- uint32_t const flags = flags_unmap[srcp.immediate() & FLAGS_ALL];
- if (!flags)
- a.xor_(eax, eax);
- else
- a.mov(eax, flags);
- }
- else
- {
- emit_mov_r32_p32(a, eax, srcp);
- a.and_(eax, FLAGS_ALL);
- a.mov(eax, ptr(uintptr_t(flags_unmap), eax, 2));
- }
- a.or_(dword_ptr(esp), eax);
-
- a.popfd();
-}
-
-
-//-------------------------------------------------
-// op_save - process a SAVE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_save(Assembler &a, 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
- a.mov(ecx, imm(dstp.memory())); // mov ecx,dstp
- a.call(imm(m_save)); // call save
-}
-
-
-//-------------------------------------------------
-// op_restore - process a RESTORE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_restore(Assembler &a, 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
- a.mov(ecx, imm(srcp.memory())); // mov ecx,dstp
- a.call(imm(m_restore)); // call restore
-}
-
-
-
-//**************************************************************************
-// INTEGER OPERATIONS
-//**************************************************************************
-
-//-------------------------------------------------
-// op_load - process a LOAD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_load(Assembler &a, 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);
- parameter const &scalesizep = inst.param(3);
- assert(scalesizep.is_size_scale());
- int const size = scalesizep.size();
-
- // pick a target register for the general case
- Gp const dstreg = dstp.select_register(eax);
-
- if (indp.is_immediate())
- {
- // immediate index
- int const scale = 1 << scalesizep.scale();
-
- if (size == SIZE_BYTE)
- a.movzx(dstreg, MABS(basep.memory(scale*indp.immediate()), 1)); // movzx dstreg,[basep + scale*indp]
- else if (size == SIZE_WORD)
- a.movzx(dstreg, MABS(basep.memory(scale*indp.immediate()), 2)); // movzx dstreg,[basep + scale*indp]
- else if (size == SIZE_DWORD)
- a.mov(dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp]
- else if (size == SIZE_QWORD)
- {
- a.mov(edx, MABS(basep.memory(scale*indp.immediate() + 4))); // mov edx,[basep + scale*indp + 4]
- a.mov(dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp]
- }
- }
- else
- {
- // other index
- Gp const indreg = indp.select_register(ecx);
- emit_mov_r32_p32_keepflags(a, indreg, indp);
- if (size == SIZE_BYTE)
- a.movzx(dstreg, ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 1)); // movzx dstreg,[basep + scale*indp]
- else if (size == SIZE_WORD)
- a.movzx(dstreg, ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 2)); // movzx dstreg,[basep + scale*indp]
- else if (size == SIZE_DWORD)
- a.mov(dstreg, ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale())); // mov dstreg,[basep + scale*indp]
- else if (size == SIZE_QWORD)
- {
- a.mov(edx, ptr(uintptr_t(basep.memory(4)), indreg, scalesizep.scale())); // mov edx,[basep + scale*indp + 4]
- a.mov(dstreg, ptr(uintptr_t(basep.memory(0)), indreg, scalesizep.scale())); // mov dstreg,[basep + scale*indp]
- }
- }
-
- // store low 32 bits
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- // 64-bit form stores upper 32 bits
- if (inst.size() == 8)
- {
- if (size != SIZE_QWORD)
- {
- // 1, 2, or 4-byte case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov [dstp+4],0
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov [reghi],0
- }
- else
- {
- // 8-byte case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4)), edx); // mov [dstp+4],edx
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()]), edx); // mov [reghi],edx
- set_last_upper_reg(a, dstp, edx);
- }
- }
- set_last_lower_reg(a, dstp, dstreg);
-}
-
-
-//-------------------------------------------------
-// op_loads - process a LOADS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_loads(Assembler &a, 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);
- parameter const &scalesizep = inst.param(3);
- assert(scalesizep.is_size_scale());
- int const size = scalesizep.size();
-
- // pick a target register for the general case
- Gp const dstreg = dstp.select_register(eax);
-
- if (indp.is_immediate())
- {
- // immediate index
- int const scale = 1 << scalesizep.scale();
-
- if (size == SIZE_BYTE)
- a.movsx(dstreg, MABS(basep.memory(scale*indp.immediate()), 1)); // movsx dstreg,[basep + scale*indp]
- else if (size == SIZE_WORD)
- a.movsx(dstreg, MABS(basep.memory(scale*indp.immediate()), 2)); // movsx dstreg,[basep + scale*indp]
- else if (size == SIZE_DWORD)
- a.mov(dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp]
- else if (size == SIZE_QWORD)
- {
- a.mov(edx, MABS(basep.memory(scale*indp.immediate() + 4))); // mov edx,[basep + scale*indp + 4]
- a.mov(dstreg, MABS(basep.memory(scale*indp.immediate()))); // mov dstreg,[basep + scale*indp]
- }
- }
- else
- {
- // other index
- Gp const indreg = indp.select_register(ecx);
- emit_mov_r32_p32_keepflags(a, indreg, indp);
- if (size == SIZE_BYTE)
- a.movsx(dstreg, ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 1)); // movsx dstreg,[basep + scale*indp]
- else if (size == SIZE_WORD)
- a.movsx(dstreg, ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 2)); // movsx dstreg,[basep + scale*indp]
- else if (size == SIZE_DWORD)
- a.mov(dstreg, ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale())); // mov dstreg,[basep + scale*indp]
- else if (size == SIZE_QWORD)
- {
- a.mov(edx, ptr(uintptr_t(basep.memory(4)), indreg, scalesizep.scale())); // mov edx,[basep + scale*indp + 4]
- a.mov(dstreg, ptr(uintptr_t(basep.memory(0)), indreg, scalesizep.scale())); // mov dstreg,[basep + scale*indp]
- }
- }
-
- // store low 32 bits
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- // 64-bit form stores upper 32 bits
- if (inst.size() == 8)
- {
- if (size != SIZE_QWORD)
- {
- if (dstreg.id() != eax.id())
- a.mov(eax, dstreg);
- a.cdq(); // cdq
- }
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4)), edx); // mov [dstp+4],edx
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()]), edx); // mov [reghi],edx
- set_last_upper_reg(a, dstp, edx);
- }
- set_last_lower_reg(a, dstp, dstreg);
-}
-
-
-//-------------------------------------------------
-// op_store - process a STORE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_store(Assembler &a, 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);
- parameter const &scalesizep = inst.param(3);
- int const size = scalesizep.size();
-
- // pick a source register for the general case
- Gp srcreg = srcp.select_register(eax);
- if (size == SIZE_BYTE && (srcreg.id() & 4)) // FIXME: &4?
- srcreg = eax;
-
- if (indp.is_immediate())
- {
- // degenerate case: constant index
- int const scale = 1 << (scalesizep.scale());
-
- if (srcp.is_immediate())
- {
- // immediate source
- if (size == SIZE_BYTE)
- a.mov(MABS(basep.memory(scale*indp.immediate()), 1), srcp.immediate()); // mov [basep + scale*indp],srcp
- else if (size == SIZE_WORD)
- a.mov(MABS(basep.memory(scale*indp.immediate()), 2), srcp.immediate()); // mov [basep + scale*indp],srcp
- else if (size == SIZE_DWORD)
- a.mov(MABS(basep.memory(scale*indp.immediate()), 4), srcp.immediate()); // mov [basep + scale*indp],srcp
- else if (size == SIZE_QWORD)
- {
- a.mov(MABS(basep.memory(scale*indp.immediate()), 4), srcp.immediate()); // mov [basep + scale*indp],srcp
- a.mov(MABS(basep.memory(scale*indp.immediate() + 4), 4), srcp.immediate() >> 32);
- // mov [basep + scale*indp + 4],srcp >> 32
- }
- }
- else
- {
- // variable source
- if (size != SIZE_QWORD)
- emit_mov_r32_p32_keepflags(a, srcreg, srcp); // mov srcreg,srcp
- else
- emit_mov_r64_p64_keepflags(a, srcreg, edx, srcp); // mov edx:srcreg,srcp
-
- if (size == SIZE_BYTE)
- a.mov(MABS(basep.memory(scale*indp.immediate())), srcreg.r8()); // mov [basep + scale*indp],srcreg
- else if (size == SIZE_WORD)
- a.mov(MABS(basep.memory(scale*indp.immediate())), srcreg.r16()); // mov [basep + scale*indp],srcreg
- else if (size == SIZE_DWORD)
- a.mov(MABS(basep.memory(scale*indp.immediate())), srcreg); // mov [basep + scale*indp],srcreg
- else if (size == SIZE_QWORD)
- {
- a.mov(MABS(basep.memory(scale*indp.immediate())), srcreg); // mov [basep + scale*indp],srcreg
- a.mov(MABS(basep.memory(scale*indp.immediate() + 4)), edx); // mov [basep + scale*indp + 4],edx
- }
- }
- }
- else
- {
- // normal case: variable index
- Gp const indreg = indp.select_register(ecx);
- emit_mov_r32_p32_keepflags(a, indreg, indp); // mov indreg,indp
-
- if (srcp.is_immediate())
- {
- // immediate source
- if (size == SIZE_BYTE)
- a.mov(ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 1), srcp.immediate()); // mov [basep + 1*ecx],srcp
- else if (size == SIZE_WORD)
- a.mov(ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 2), srcp.immediate()); // mov [basep + 2*ecx],srcp
- else if (size == SIZE_DWORD)
- a.mov(ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale(), 4), srcp.immediate()); // mov [basep + 4*ecx],srcp
- else if (size == SIZE_QWORD)
- {
- a.mov(ptr(uintptr_t(basep.memory(0)), indreg, scalesizep.scale(), 4), srcp.immediate()); // mov [basep + 8*ecx],srcp
- a.mov(ptr(uintptr_t(basep.memory(4)), indreg, scalesizep.scale(), 4), srcp.immediate() >> 32);
- // mov [basep + 8*ecx + 4],srcp >> 32
- }
- }
- else
- {
- // variable source
- if (size != SIZE_QWORD)
- emit_mov_r32_p32_keepflags(a, srcreg, srcp); // mov srcreg,srcp
- else
- emit_mov_r64_p64_keepflags(a, srcreg, edx, srcp); // mov edx:srcreg,srcp
- if (size == SIZE_BYTE)
- a.mov(ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale()), srcreg.r8()); // mov [basep + 1*ecx],srcreg
- else if (size == SIZE_WORD)
- a.mov(ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale()), srcreg.r16()); // mov [basep + 2*ecx],srcreg
- else if (size == SIZE_DWORD)
- a.mov(ptr(uintptr_t(basep.memory()), indreg, scalesizep.scale()), srcreg); // mov [basep + 4*ecx],srcreg
- else if (size == SIZE_QWORD)
- {
- a.mov(ptr(uintptr_t(basep.memory(0)), indreg, scalesizep.scale()), srcreg); // mov [basep + 8*ecx],srcreg
- a.mov(ptr(uintptr_t(basep.memory(4)), indreg, scalesizep.scale()), edx); // mov [basep + 8*ecx],edx
- }
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_read - process a READ opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_read(Assembler &a, 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);
- parameter const &spacesizep = inst.param(2);
- assert(spacesizep.is_size_space());
-
- // pick a target register for the general case
- Gp const dstreg = dstp.select_register(eax);
-
- // set up a call to the read handler
- auto const &accessors = m_memory_accessors[spacesizep.space()];
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), addrp);
- if (spacesizep.size() == SIZE_BYTE)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_byte.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_byte.obj));
- a.call(imm(accessors.read_byte.func));
- if (USE_THISCALL)
- a.sub(esp, 4);
- a.movzx(dstreg, al);
- }
- else if (spacesizep.size() == SIZE_WORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_word.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_word.obj));
- a.call(imm(accessors.read_word.func));
- if (USE_THISCALL)
- a.sub(esp, 4);
- a.movzx(dstreg, ax);
- }
- else if (spacesizep.size() == SIZE_DWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_dword.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_dword.obj));
- a.call(imm(accessors.read_dword.func));
- if (USE_THISCALL)
- a.sub(esp, 4);
- a.mov(dstreg, eax);
- }
- else if (spacesizep.size() == SIZE_QWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_qword.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_qword.obj));
- a.call(imm(accessors.read_qword.func));
- if (USE_THISCALL)
- a.sub(esp, 4);
- a.mov(dstreg, eax);
- }
-
- // store low 32 bits
- emit_mov_p32_r32(a, dstp, dstreg);
-
- // 64-bit form stores upper 32 bits
- if (inst.size() == 8)
- {
- if (spacesizep.size() != SIZE_QWORD)
- {
- // 1, 2, or 4-byte case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0);
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0);
- }
- else
- {
- // 8-byte case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4)), edx);
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()]), edx);
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_readm - process a READM opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_readm(Assembler &a, 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);
- parameter const &spacesizep = inst.param(3);
- assert(spacesizep.is_size_space());
-
- // pick a target register for the general case
- Gp const dstreg = dstp.select_register(eax);
-
- // set up a call to the read byte handler
- auto const &accessors = m_memory_accessors[spacesizep.space()];
- if (spacesizep.size() != SIZE_QWORD)
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 4 : 8), maskp);
- else
- emit_mov_m64_p64(a, qword_ptr(esp, USE_THISCALL ? 4 : 8), maskp);
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), addrp);
- if (spacesizep.size() == SIZE_BYTE)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_byte_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_byte_masked.obj));
- a.call(imm(accessors.read_byte_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- a.movzx(dstreg, al);
- }
- else if (spacesizep.size() == SIZE_WORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_word_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_word_masked.obj));
- a.call(imm(accessors.read_word_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- a.movzx(dstreg, ax);
- }
- else if (spacesizep.size() == SIZE_DWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_dword_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_dword_masked.obj));
- a.call(imm(accessors.read_dword_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- a.mov(dstreg, eax);
- }
- else if (spacesizep.size() == SIZE_QWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.read_qword_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.read_qword_masked.obj));
- a.call(imm(accessors.read_qword_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 12);
- a.mov(dstreg, eax);
- }
-
- // store low 32 bits
- emit_mov_p32_r32(a, dstp, dstreg);
-
- // 64-bit form stores upper 32 bits
- if (inst.size() == 8)
- {
- if (spacesizep.size() != SIZE_QWORD)
- {
- // 1, 2, or 4-byte case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0);
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0);
- }
- else
- {
- // 8-byte case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4)), edx);
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()]), edx);
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_write - process a WRITE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_write(Assembler &a, 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);
- parameter const &spacesizep = inst.param(2);
- assert(spacesizep.is_size_space());
-
- // set up a call to the write byte handler
- auto const &accessors = m_memory_accessors[spacesizep.space()];
- if (spacesizep.size() != SIZE_QWORD)
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 4 : 8), srcp);
- else
- emit_mov_m64_p64(a, qword_ptr(esp, USE_THISCALL ? 4 : 8), srcp);
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), addrp);
- if (spacesizep.size() == SIZE_BYTE)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_byte.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_byte.obj));
- a.call(imm(accessors.write_byte.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- }
- else if (spacesizep.size() == SIZE_WORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_word.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_word.obj));
- a.call(imm(accessors.write_word.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- }
- else if (spacesizep.size() == SIZE_DWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_dword.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_dword.obj));
- a.call(imm(accessors.write_dword.func));
- if (USE_THISCALL)
- a.sub(esp, 8);
- }
- else if (spacesizep.size() == SIZE_QWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_qword.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_qword.obj));
- a.call(imm(accessors.write_qword.func));
- if (USE_THISCALL)
- a.sub(esp, 12);
- }
-}
-
-
-//-------------------------------------------------
-// op_writem - process a WRITEM opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_writem(Assembler &a, 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);
- parameter const &spacesizep = inst.param(3);
- assert(spacesizep.is_size_space());
-
- // set up a call to the write byte handler
- auto const &accessors = m_memory_accessors[spacesizep.space()];
- if (spacesizep.size() != SIZE_QWORD)
- {
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 8 : 12), maskp);
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 4 : 8), srcp);
- }
- else
- {
- emit_mov_m64_p64(a, qword_ptr(esp, USE_THISCALL ? 12 : 16), maskp);
- emit_mov_m64_p64(a, qword_ptr(esp, USE_THISCALL ? 4 : 8), srcp);
- }
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), addrp);
- if (spacesizep.size() == SIZE_BYTE)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_byte_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_byte_masked.obj));
- a.call(imm(accessors.write_byte_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 12);
- }
- else if (spacesizep.size() == SIZE_WORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_word_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_word_masked.obj));
- a.call(imm(accessors.write_word_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 12);
- }
- else if (spacesizep.size() == SIZE_DWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_dword_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_dword_masked.obj));
- a.call(imm(accessors.write_dword_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 12);
- }
- else if (spacesizep.size() == SIZE_QWORD)
- {
- if (USE_THISCALL)
- a.mov(ecx, imm(accessors.write_qword_masked.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessors.write_qword_masked.obj));
- a.call(imm(accessors.write_qword_masked.func));
- if (USE_THISCALL)
- a.sub(esp, 20);
- }
-}
-
-
-//-------------------------------------------------
-// op_carry - process a CARRY opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_carry(Assembler &a, 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() & (inst.size() * 8 - 1))))
- a.stc();
- else
- a.clc();
-
- return;
- }
-
- // load non-immediate bit numbers into a register
- if (!bitp.is_immediate())
- {
- emit_mov_r32_p32(a, ecx, bitp);
- a.and_(ecx, inst.size() * 8 - 1);
- }
-
- // 32-bit form
- if (inst.size() == 4)
- {
- if (srcp.is_immediate())
- emit_mov_r32_p32(a, edx, srcp);
-
- if (bitp.is_immediate())
- {
- if (srcp.is_memory())
- a.bt(MABS(srcp.memory(), 4), (bitp.immediate() & (inst.size() * 8 - 1)));
- else if (srcp.is_int_register())
- a.bt(gpd(srcp.ireg()), (bitp.immediate() & (inst.size() * 8 - 1)));
- else if (srcp.is_immediate())
- a.bt(edx, (bitp.immediate() & (inst.size() * 8 - 1)));
- }
- else
- {
- if (srcp.is_memory())
- a.bt(MABS(srcp.memory()), ecx);
- else if (srcp.is_int_register())
- a.bt(gpd(srcp.ireg()), ecx);
- else if (srcp.is_immediate())
- a.bt(edx, ecx);
- }
- }
-
- // 64-bit form
- else
- {
- if (srcp.is_immediate())
- emit_mov_r64_p64(a, ebx, edx, srcp);
-
- if (bitp.is_immediate())
- {
- const uint32_t bitshift = bitp.immediate() & (inst.size() * 8 - 1);
- if (bitshift < 32)
- {
- if (srcp.is_memory())
- a.bt(MABS(srcp.memory(), 4), bitshift);
- else if (srcp.is_int_register())
- a.bt(gpd(srcp.ireg()), bitshift);
- else if (srcp.is_immediate())
- a.bt(ebx, bitshift);
- }
- else if (bitshift >= 32)
- {
- if (srcp.is_memory())
- a.bt(MABS((uint8_t*)srcp.memory() + 4, 4), bitshift - 32);
- else if (srcp.is_int_register())
- a.bt(MABS(m_reghi[srcp.ireg()], 4), bitshift - 32);
- else if (srcp.is_immediate())
- a.bt(edx, bitshift);
- }
- }
- else
- {
- Label end = a.new_label();
- Label higher = a.new_label();
-
- a.cmp(ecx, 32);
- a.short_().jge(higher);
-
- if (srcp.is_memory())
- {
- a.bt(MABS(srcp.memory(), 4), ecx);
- }
- else if (srcp.is_int_register())
- {
- a.mov(MABS(m_reglo[srcp.ireg()], 4), gpd(srcp.ireg())); // mov [srcp.lo],srcp
- a.bt(MABS(m_reglo[srcp.ireg()], 4), ecx); // bt [srcp],ecx
- }
- else if (srcp.is_immediate())
- {
- a.bt(ebx, ecx);
- }
-
- a.short_().jmp(end);
-
- a.bind(higher);
- a.sub(ecx, 32);
-
- if (srcp.is_memory())
- {
- a.bt(MABS((uint8_t*)srcp.memory() + 4, 4), ecx);
- }
- else if (srcp.is_int_register())
- {
- a.bt(MABS(m_reghi[srcp.ireg()], 4), ecx);
- }
- else if (srcp.is_immediate())
- {
- a.bt(edx, ecx);
- }
-
- a.bind(end);
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_set - process a SET opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_set(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax);
-
- // set to AL
- a.set(X86_CONDITION(inst.condition()), al); // setcc al
- a.movzx(dstreg, al); // movzx dstreg,al
-
- // store low 32 bits
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- // 64-bit form stores upper 32 bits
- if (inst.size() == 8)
- {
- // general case
- if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov [dstp+4],0
- else if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov [reghi],0
- }
-}
-
-
-//-------------------------------------------------
-// op_mov - process a MOV opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_mov(Assembler &a, 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);
- be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
-
- // pick a target register for the general case
- Gp const dstreg = dstp.select_register(eax);
-
- // add a conditional branch unless a conditional move is possible
- Label skip;
- if (inst.condition() != uml::COND_ALWAYS && ((inst.size() == 8) || !(dstp.is_int_register() && !srcp.is_immediate())))
- {
- skip = a.new_label();
- a.short_().j(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())
- a.mov(MABS(dstp.memory()), gpd(srcp.ireg())); // mov [dstp],srcp
-
- // immediate to memory
- else if (dstp.is_memory() && srcp.is_immediate())
- a.mov(MABS(dstp.memory(), 4), srcp.immediate()); // mov [dstp],srcp
-
- // conditional memory to register
- else if (inst.condition() != uml::COND_ALWAYS && dstp.is_int_register() && srcp.is_memory())
- a.cmov(X86_CONDITION(inst.condition()), gpd(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())
- a.cmov(X86_CONDITION(inst.condition()), gpd(dstp.ireg()), gpd(srcp.ireg()));
- // cmovcc dstp,srcp
-
- // general case
- else
- {
- emit_mov_r32_p32_keepflags(a, dstreg, srcp); // mov dstreg,srcp
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // register to memory
- if (dstp.is_memory() && srcp.is_int_register())
- {
- a.mov(eax, MABS(m_reghi[srcp.ireg()])); // mov eax,reghi[srcp]
- a.mov(MABS(dstp.memory(0)), gpd(srcp.ireg())); // mov [dstp],srcp
- a.mov(MABS(dstp.memory(4)), eax); // mov [dstp+4],eax
- }
-
- // immediate to memory
- else if (dstp.is_memory() && srcp.is_immediate())
- {
- a.mov(MABS(dstp.memory(0), 4), srcp.immediate()); // mov [dstp],srcp
- a.mov(MABS(dstp.memory(4), 4), srcp.immediate() >> 32); // mov [dstp+4],srcp >> 32
- }
-
- // general case
- else
- {
- emit_mov_r64_p64_keepflags(a, dstreg, edx, srcp); // mov edx:dstreg,srcp
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-
- // bind the label
- if (inst.condition() != uml::COND_ALWAYS && ((inst.size() == 8) || !(dstp.is_int_register() && !srcp.is_immediate())))
- {
- a.bind(skip);
- reset_last_upper_lower_reg();
- }
-}
-
-
-//-------------------------------------------------
-// op_sext - process a SEXT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_sext(Assembler &a, 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);
- parameter const &sizep = inst.param(2);
- assert(sizep.is_size());
-
- // pick a target register for the general case
- Gp const dstreg = eax;
-
- // convert 8-bit source registers to EAX
- if (sizep.size() == SIZE_BYTE && srcp.is_int_register() && (srcp.ireg() & 4))
- {
- a.mov(eax, gpd(srcp.ireg())); // mov eax,srcp
- srcp = be_parameter::make_ireg(eax.id());
- }
-
- // general case
- if (srcp.is_memory())
- {
- if (sizep.size() == SIZE_BYTE)
- a.movsx(dstreg, MABS(srcp.memory(), 1)); // movsx dstreg,[srcp]
- else if (sizep.size() == SIZE_WORD)
- a.movsx(dstreg, MABS(srcp.memory(), 2)); // movsx dstreg,[srcp]
- else if (sizep.size() == SIZE_DWORD)
- a.mov(dstreg, MABS(srcp.memory())); // mov dstreg,[srcp]
- }
- else if (srcp.is_int_register())
- {
- if (sizep.size() == SIZE_BYTE)
- a.movsx(dstreg, gpb_lo(srcp.ireg())); // movsx dstreg,srcp
- else if (sizep.size() == SIZE_WORD)
- a.movsx(dstreg, gpw(srcp.ireg())); // movsx dstreg,srcp
- else if (sizep.size() == SIZE_DWORD && dstreg.id() != srcp.ireg())
- a.mov(dstreg, gpd(srcp.ireg())); // mov dstreg,srcp
- }
- else if (srcp.is_immediate())
- {
- if (sizep.size() == SIZE_BYTE)
- a.mov(dstreg, (int8_t)srcp.immediate());
- else if (sizep.size() == SIZE_WORD)
- a.mov(dstreg, (int16_t)srcp.immediate());
- else if (sizep.size() == SIZE_DWORD)
- a.mov(dstreg, (int32_t)srcp.immediate());
- }
-
- if (inst.flags() != 0)
- a.test(dstreg, dstreg); // test dstreg,dstreg
-
- // 32-bit form: store the low 32 bits
- if (inst.size() == 4)
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- // 64-bit form: sign extend to 64 bits and store edx:eax
- else if (inst.size() == 8)
- {
- a.cdq(); // cdq
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:eax
- }
-}
-
-
-//-------------------------------------------------
-// op_bfxu - process a BFXU opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_bfxu(Assembler &a, 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 widthp(*this, inst.param(3), PTYPE_MRI);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- if (widthp.is_immediate_value(0))
- {
- // undefined behaviour - do something
- if (inst.flags() || dstp.is_int_register())
- {
- Gp const dstreg = dstp.select_register(eax);
-
- a.xor_(dstreg, dstreg);
-
- emit_mov_p32_r32(a, dstp, dstreg);
- }
- else if (dstp.is_memory())
- {
- a.mov(MABS(dstp.memory(), 4), 0);
- }
- }
- else
- {
- Gp dstreg;
-
- if (widthp.is_immediate())
- {
- const unsigned width = widthp.immediate() & 31;
-
- dstreg = dstp.select_register(eax);
-
- if (!shiftp.is_immediate())
- emit_mov_r32_p32(a, ecx, shiftp);
- emit_mov_r32_p32(a, dstreg, srcp);
-
- if (shiftp.is_immediate())
- {
- const unsigned shift = shiftp.immediate() & 31;
-
- a.ror(dstreg, shift);
- }
- else
- {
- a.ror(dstreg, cl);
- }
- a.and_(dstreg, util::make_bitmask<uint32_t>(width));
- }
- else
- {
- Gp const widthreg = widthp.select_register(edx);
- dstreg = dstp.select_register(eax, widthp);
-
- if (!shiftp.is_immediate())
- emit_mov_r32_p32(a, ecx, shiftp);
- emit_mov_r32_p32(a, widthreg, widthp);
- emit_mov_r32_p32(a, dstreg, srcp);
-
- if (shiftp.is_immediate())
- a.mov(ecx, shiftp.immediate() & 31);
- a.add(ecx, widthreg);
- a.ror(dstreg, cl);
- a.mov(ecx, widthreg);
- a.neg(ecx);
- a.and_(ecx, 31);
- a.shr(dstreg, cl);
- }
-
- emit_mov_p32_r32(a, dstp, dstreg);
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- if (widthp.is_immediate())
- {
- be_parameter maskp(*this, uml::parameter(util::make_bitmask<u64>(widthp.immediate() & 63)), PTYPE_I);
-
- Gp const dstreg = dstp.select_register(eax, shiftp);
-
- emit_mov_r64_p64(a, dstreg, edx, srcp);
- emit_ror_r64_p64(a, dstreg, edx, shiftp, FLAGS_NONE);
- emit_and_r64_p64(a, dstreg, edx, maskp, inst);
- emit_mov_p64_r64(a, dstp, dstreg, edx);
- }
- else
- {
- Gp const dstreg = dstp.select_register(eax, shiftp);
-
- // first make the mask
- Label large = a.new_label();
- Label shift = a.new_label();
-
- emit_mov_r32_p32(a, ecx, widthp);
-
- a.mov(eax, ~u32(0));
- a.test(ecx, 0x20);
- a.short_().jnz(large);
- a.mov(dword_ptr(esp, 4), 0);
- a.and_(ecx, 31);
- a.shl(eax, cl);
- a.not_(eax);
- a.mov(dword_ptr(esp), eax);
- a.short_().jmp(shift);
-
- a.bind(large);
-
- a.mov(dword_ptr(esp), eax);
- a.and_(ecx, 31);
- a.shl(eax, cl);
- a.not_(eax);
- a.mov(dword_ptr(esp, 4), eax);
-
- a.bind(shift);
-
- // shift the field into position
- emit_mov_r64_p64(a, dstreg, edx, srcp);
- emit_ror_r64_p64(a, dstreg, edx, shiftp, FLAGS_NONE);
-
- // apply the mask
- a.and_(dstreg, dword_ptr(esp));
- if (inst.flags() & FLAG_Z)
- {
- a.pushfd();
- a.and_(edx, dword_ptr(esp, 8));
- emit_combine_z_flags(a);
- }
- else
- {
- a.and_(edx, dword_ptr(esp, 4));
- }
-
- emit_mov_p64_r64(a, dstp, dstreg, edx);
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_bfxs - process a BFXS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_bfxs(Assembler &a, 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 widthp(*this, inst.param(3), PTYPE_MRI);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- if (widthp.is_immediate_value(0))
- {
- // undefined behaviour - do something
- if (inst.flags() || dstp.is_int_register())
- {
- Gp const dstreg = dstp.select_register(eax);
-
- a.xor_(dstreg, dstreg);
-
- emit_mov_p32_r32(a, dstp, dstreg);
- }
- else if (dstp.is_memory())
- {
- a.mov(MABS(dstp.memory(), 4), 0);
- }
- }
- else
- {
- Gp dstreg;
-
- if (widthp.is_immediate())
- {
- dstreg = dstp.select_register(eax, shiftp);
-
- const unsigned width = widthp.immediate() & 31;
-
- emit_mov_r32_p32(a, dstreg, srcp);
-
- if (shiftp.is_immediate())
- {
- const unsigned shift = shiftp.immediate() & 31;
-
- a.ror(dstreg, (width + shift) & 31);
- }
- else
- {
- a.mov(ecx, width);
- if (shiftp.is_int_register())
- a.add(ecx, shiftp.select_register(edx));
- else
- a.add(ecx, MABS(shiftp.memory()));
- a.ror(dstreg, cl);
- }
- a.sar(dstreg, -int(width) & 31);
- }
- else if (shiftp.is_immediate_value(0))
- {
- dstreg = dstp.select_register(eax);
-
- emit_mov_r32_p32(a, ecx, widthp);
- emit_mov_r32_p32(a, dstreg, srcp);
-
- a.ror(dstreg, cl);
- a.neg(ecx);
- a.and_(ecx, 31);
- a.sar(dstreg, cl);
- }
- else
- {
- Gp const widthreg = widthp.select_register(edx);
- dstreg = dstp.select_register(eax, widthp);
-
- if (!shiftp.is_immediate())
- emit_mov_r32_p32(a, ecx, shiftp);
- emit_mov_r32_p32(a, widthreg, widthp);
- emit_mov_r32_p32(a, dstreg, srcp);
-
- if (shiftp.is_immediate())
- a.mov(ecx, shiftp.immediate() & 31);
- a.add(ecx, widthreg);
- a.ror(dstreg, cl);
- a.mov(ecx, widthreg);
- a.neg(ecx);
- a.and_(ecx, 31);
- a.sar(dstreg, cl);
- }
-
- emit_mov_p32_r32(a, dstp, dstreg);
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- Gp dstreg;
-
- if (widthp.is_immediate())
- {
- be_parameter rshiftp(*this, uml::parameter(-int64_t(widthp.immediate()) & 63), PTYPE_I);
-
- dstreg = dstp.select_register(eax);
-
- if (shiftp.is_immediate())
- {
- be_parameter rotp(*this, uml::parameter((shiftp.immediate() + widthp.immediate()) & 63), PTYPE_I);
-
- emit_mov_r64_p64(a, dstreg, edx, srcp);
- emit_ror_r64_p64(a, dstreg, edx, rotp, FLAGS_NONE);
- }
- else
- {
- Gp tempreg = esi;
- if (dstreg == tempreg)
- tempreg = edi;
- if (dstreg == tempreg)
- tempreg = ebp;
- assert(dstreg != tempreg);
- a.mov(dword_ptr(esp), tempreg);
-
- emit_mov_r32_p32(a, ecx, shiftp);
- emit_mov_r64_p64(a, dstreg, edx, srcp);
-
- // if the count is at least 32, swap the halves
- Label small = a.new_label();
-
- a.add(ecx, widthp.immediate() & 63);
- a.test(ecx, 0x20);
- a.short_().jz(small);
- a.xchg(dstreg, edx);
- a.bind(small);
- a.and_(ecx, 31);
-
- // do the extended rotate
- reset_last_upper_lower_reg();
- a.mov(tempreg, edx);
- a.shrd(edx, dstreg, cl);
- a.shrd(dstreg, tempreg, cl);
-
- a.mov(tempreg, dword_ptr(esp));
- }
-
- emit_sar_r64_p64(a, dstreg, edx, rshiftp, inst.flags());
- }
- else
- {
- dstreg = dstp.select_register(eax, widthp);
-
- Gp tempreg = esi;
- if (dstreg == tempreg)
- tempreg = edi;
- if (dstreg == tempreg)
- tempreg = ebp;
- assert(dstreg != tempreg);
- a.mov(dword_ptr(esp), tempreg);
-
- // calculate the required rotation
- emit_mov_r32_p32(a, ecx, widthp);
- if (shiftp.is_immediate_value(0))
- ;
- else if (shiftp.is_immediate())
- a.add(ecx, shiftp.immediate());
- else if (shiftp.is_int_register())
- a.add(ecx, shiftp.select_register(edx));
- else if (shiftp.is_memory())
- a.add(ecx, MABS(shiftp.memory()));
-
- emit_mov_r64_p64(a, dstreg, edx, srcp);
-
- // if the count is at least 32, swap the halves
- Label small_ror = a.new_label();
-
- a.test(ecx, 0x20);
- a.short_().jz(small_ror);
- a.xchg(dstreg, edx);
- a.bind(small_ror);
- a.and_(ecx, 31);
-
- // do the extended rotate
- reset_last_upper_lower_reg();
- a.mov(tempreg, edx);
- a.shrd(edx, dstreg, cl);
- a.shrd(dstreg, tempreg, cl);
-
- a.mov(tempreg, dword_ptr(esp));
-
- // now do the shift
- Label small_sar = a.new_label();
- Label no_shift, done;
- if (inst.flags())
- {
- no_shift = a.new_label();
- done = a.new_label();
- }
-
- emit_mov_r32_p32(a, ecx, widthp);
- a.neg(ecx);
- a.test(ecx, 0x20);
- a.short_().jz(small_sar);
- a.mov(dstreg, edx);
- a.sar(edx, 31);
- a.bind(small_sar);
- a.and_(ecx, 31);
- if (inst.flags())
- a.short_().jz(no_shift);
-
- a.shrd(dstreg, edx, cl);
- if (inst.flags() & FLAG_Z)
- a.pushfd();
- a.sar(edx, cl);
-
- // zero-bit shifts don't update the flags
- if (inst.flags())
- {
- a.short_().jmp(done);
-
- a.bind(no_shift);
- if (inst.flags() & FLAG_Z)
- {
- a.test(eax, eax);
- a.pushfd();
- }
- a.test(edx, edx);
- a.bind(done);
- if (inst.flags() & FLAG_Z)
- emit_combine_z_flags(a);
- }
- }
-
- emit_mov_p64_r64(a, dstp, dstreg, edx);
- }
-}
-
-
-//-------------------------------------------------
-// op_roland - process an ROLAND opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_roland(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, shiftp, maskp);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, dstreg, srcp); // mov dstreg,srcp
- shift_op_param(a, Inst::kIdRol, inst.size(), dstreg, shiftp, // rol dstreg,shiftp
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, false);
- alu_op_param(a, Inst::kIdAnd, dstreg, maskp, // and dstreg,maskp
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && !src.immediate())
- {
- a.xor_(dst.as<Gp>(), dst.as<Gp>());
- return true;
- }
- else if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- return true;
-
- return false;
- });
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- emit_mov_r64_p64(a, dstreg, edx, srcp); // mov edx:dstreg,srcp
- emit_rol_r64_p64(a, dstreg, edx, shiftp, FLAGS_NONE); // rol edx:dstreg,shiftp
- emit_and_r64_p64(a, dstreg, edx, maskp, inst); // and edx:dstreg,maskp
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-//-------------------------------------------------
-// op_rolins - process an ROLINS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_rolins(Assembler &a, 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
- Gp const dstreg = dstp.select_register(ecx, shiftp, maskp);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, eax, srcp); // mov eax,srcp
- shift_op_param(a, Inst::kIdRol, inst.size(), eax, shiftp, // rol eax,shiftp
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, false);
- emit_mov_r32_p32(a, dstreg, dstp); // mov dstreg,dstp
- if (maskp.is_immediate())
- {
- a.and_(eax, maskp.immediate()); // and eax,maskp
- a.and_(dstreg, ~maskp.immediate()); // and dstreg,~maskp
- }
- else
- {
- emit_mov_r32_p32(a, edx, maskp); // mov edx,maskp
- a.and_(eax, edx); // and eax,edx
- a.not_(edx); // not edx
- a.and_(dstreg, edx); // and dstreg,edx
- }
- a.or_(dstreg, eax); // or dstreg,eax
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- if (inst.flags())
- a.test(dstreg, dstreg);
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- emit_mov_r64_p64(a, eax, edx, srcp); // mov edx:eax,srcp
- emit_rol_r64_p64(a, eax, edx, shiftp, FLAGS_NONE); // rol edx:eax,shiftp
- if (maskp.is_immediate())
- {
- a.and_(eax, maskp.immediate()); // and eax,maskp
- a.and_(edx, maskp.immediate() >> 32); // and edx,maskp >> 32
- if (dstp.is_int_register())
- {
- a.and_(gpd(dstp.ireg()), ~maskp.immediate()); // and dstp.lo,~maskp
- a.and_(MABS(m_reghi[dstp.ireg()], 4), ~maskp.immediate() >> 32); // and dstp.hi,~maskp >> 32
- a.or_(gpd(dstp.ireg()), eax); // or dstp.lo,eax
- a.or_(MABS(m_reghi[dstp.ireg()]), edx); // or dstp.hi,edx
- }
- else
- {
- a.and_(MABS(dstp.memory(0), 4), ~maskp.immediate()); // and dstp.lo,~maskp
- a.and_(MABS(dstp.memory(4), 4), ~maskp.immediate() >> 32); // and dstp.hi,~maskp >> 32
- a.or_(MABS(dstp.memory(0)), eax); // or dstp.lo,eax
- a.or_(MABS(dstp.memory(4)), edx); // or dstp.hi,edx
- }
- }
- else
- {
- a.mov(ptr(esp, 0), ebx); // mov [esp],ebx
- emit_mov_r64_p64(a, ebx, ecx, maskp); // mov ecx:ebx,maskp
- a.and_(eax, ebx); // and eax,ebx
- a.and_(edx, ecx); // and edx,ecx
- a.not_(ebx); // not ebx
- a.not_(ecx); // not ecx
- if (dstp.is_int_register())
- {
- if (dstp.ireg() == Gp::kIdBx)
- a.and_(ptr(esp, 0), ebx); // and dstp.lo,ebx
- else
- a.and_(gpd(dstp.ireg()), ebx); // and dstp.lo,ebx
- a.and_(MABS(m_reghi[dstp.ireg()]), ecx); // and dstp.hi,ecx
- if (dstp.ireg() == Gp::kIdBx)
- a.or_(ptr(esp, 0), eax); // or dstp.lo,eax
- else
- a.or_(gpd(dstp.ireg()), eax); // or dstp.lo,eax
- a.or_(MABS(m_reghi[dstp.ireg()]), edx); // or dstp.hi,edx
- }
- else
- {
- a.and_(MABS(dstp.memory(0)), ebx); // and dstp.lo,ebx
- a.and_(MABS(dstp.memory(4)), ecx); // and dstp.hi,ecx
- a.or_(MABS(dstp.memory(0)), eax); // or dstp.lo,eax
- a.or_(MABS(dstp.memory(4)), edx); // or dstp.hi,edx
- }
-
- a.mov(ebx, ptr(esp, 0)); // mov ebx,[esp]
-
- if (inst.flags())
- {
- if (dstp.is_int_register())
- calculate_status_flags(a, gpd(dstp.ireg()), FLAG_Z);
- else
- calculate_status_flags(a, MABS(dstp.memory(0)), FLAG_Z);
-
- a.pushfd();
-
- if (dstp.is_int_register())
- calculate_status_flags(a, MABS(m_reghi[dstp.ireg()]), FLAG_S | FLAG_Z);
- else
- calculate_status_flags(a, MABS(dstp.memory(4)), FLAG_S | FLAG_Z);
-
- emit_combine_z_flags(a);
- }
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_add - process a ADD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_add(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdAdd, MABS(dstp.memory(), 4), src2p, // add [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- });
-
- // reg = reg + imm
- else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && !inst.flags())
- a.lea(gpd(dstp.ireg()), ptr(gpd(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())
- a.lea(gpd(dstp.ireg()), ptr(gpd(src1p.ireg()), gpd(src2p.ireg()))); // lea dstp,[src1p+src2p]
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdAdd, dstreg, src2p, // add dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- });
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdAdd, Inst::kIdAdc, // add [dstp],src2p
- MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), src2p, inst.flags() & FLAG_Z);
-
- // general case
- else
- {
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- alu_op_param(a, Inst::kIdAdd, Inst::kIdAdc, // add edx:dstreg,src2p
- dstreg, edx, src2p, inst.flags() & FLAG_Z);
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_addc - process a ADDC opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_addc(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdAdc, MABS(dstp.memory(), 4), src2p); // adc [dstp],src2p
-
- // general case
- else
- {
- emit_mov_r32_p32_keepflags(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdAdc, dstreg, src2p); // adc dstreg,src2p
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdAdc, Inst::kIdAdc, // adc [dstp],src2p
- MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), src2p, inst.flags() & FLAG_Z);
-
- // general case
- else
- {
- emit_mov_r64_p64_keepflags(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- alu_op_param(a, Inst::kIdAdc, Inst::kIdAdc, // adc edx:dstreg,src2p
- dstreg, edx, src2p, inst.flags() & FLAG_Z);
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_sub - process a SUB opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_sub(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdSub, MABS(dstp.memory(), 4), src2p, // sub [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- });
-
- // reg = reg - imm
- else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && !inst.flags())
- a.lea(gpd(dstp.ireg()), ptr(gpd(src1p.ireg()), -src2p.immediate())); // lea dstp,[src1p-src2p]
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdSub, dstreg, src2p, // sub dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- });
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdSub, Inst::kIdSbb, // sub [dstp],src2p
- MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), src2p, inst.flags() & FLAG_Z);
-
- // general case
- else
- {
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- alu_op_param(a, Inst::kIdSub, Inst::kIdSbb, // sub edx:dstreg,src2p
- dstreg, edx, src2p, inst.flags() & FLAG_Z);
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_subc - process a SUBC opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_subc(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdSbb, MABS(dstp.memory(), 4), src2p); // sbb [dstp],src2p
-
- // general case
- else
- {
- emit_mov_r32_p32_keepflags(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdSbb, dstreg, src2p); // sbb dstreg,src2p
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdSbb, Inst::kIdSbb, // sbb [dstp],src2p
- MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), src2p, inst.flags() & FLAG_Z);
-
- // general case
- else
- {
- emit_mov_r64_p64_keepflags(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- alu_op_param(a, Inst::kIdSbb, Inst::kIdSbb, // sbb edx:dstreg,src2p
- dstreg, edx, src2p, inst.flags() & FLAG_Z);
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_cmp - process a CMP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_cmp(Assembler &a, 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
- Gp const src1reg = src1p.select_register(eax);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // memory versus anything
- if (src1p.is_memory())
- alu_op_param(a, Inst::kIdCmp, MABS(src1p.memory(), 4), src2p); // cmp [src1p],src2p
-
- // general case
- else
- {
- if (src1p.is_immediate())
- a.mov(src1reg, src1p.immediate()); // mov src1reg,imm
- alu_op_param(a, Inst::kIdCmp, src1reg, src2p); // cmp src1reg,src2p
- }
- }
-
- // 64-bit form
- else
- {
- // general case
- emit_mov_r64_p64(a, eax, edx, src1p); // mov edx:eax,[src1p]
- alu_op_param(a, Inst::kIdSub, Inst::kIdSbb, // cmp edx:eax,src2p
- eax, edx, src2p, (inst.flags() & FLAG_Z) && (inst.flags() != FLAG_Z));
- if (inst.flags() == FLAG_Z)
- a.or_(edx, eax);
- }
-}
-
-
-//-------------------------------------------------
-// op_mulu - process a MULU opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_mulu(Assembler &a, 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);
- normalize_commutative(src1p, src2p);
- const bool compute_hi = (dstp != edstp);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
- emit_mov_r32_p32(a, edx, src2p); // mov edx,src2p
- a.mul(edx); // mul edx
-
- emit_mov_p32_r32(a, dstp, eax); // mov dstp,eax
- if (compute_hi)
- emit_mov_p32_r32(a, edstp, edx); // mov edstp,edx
-
- if (inst.flags())
- {
- a.pushfd();
-
- a.test(edx, edx);
- a.pushfd(); // will have the sign flag + upper half zero
- a.pop(edx);
-
- a.test(eax, eax);
- a.pushfd(); // lower half zero
- a.pop(eax);
-
- a.and_(dword_ptr(esp, 0), ~(0x40 | 0x80));
- a.mov(ecx, edx);
- a.and_(ecx, 0x80); // sign
-
- a.and_(eax, edx);
- a.and_(eax, 0x40); // zero
-
- a.or_(eax, ecx);
- a.or_(dword_ptr(esp, 0), eax);
-
- a.popfd();
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- a.mov(dword_ptr(esp, 24), inst.flags() ? 1 : 0); // mov [esp+24],flags
- emit_mov_m64_p64(a, qword_ptr(esp, 16), src2p); // mov [esp+16],src2p
- emit_mov_m64_p64(a, qword_ptr(esp, 8), src1p); // mov [esp+8],src1p
- if (!compute_hi)
- a.mov(dword_ptr(esp, 4), imm(&m_reslo)); // mov [esp+4],&reslo
- else
- a.mov(dword_ptr(esp, 4), imm(&m_reshi)); // mov [esp+4],&reshi
- a.mov(dword_ptr(esp, 0), imm(&m_reslo)); // mov [esp],&reslo
- a.call(imm(dmulu<false>)); // call dmulu (calculate ZS flags as 64*64->128)
- if (inst.flags() != 0)
- a.push(dword_ptr(uintptr_t(flags_unmap), eax, 2)); // push flags_unmap[eax*4]
- a.mov(eax, MABS((uint32_t *)&m_reslo + 0)); // mov eax,reslo.lo
- a.mov(edx, MABS((uint32_t *)&m_reslo + 1)); // mov edx,reslo.hi
- emit_mov_p64_r64(a, dstp, eax, edx); // mov dstp,edx:eax
- if (compute_hi)
- {
- a.mov(eax, MABS((uint32_t *)&m_reshi + 0)); // mov eax,reshi.lo
- a.mov(ecx, MABS((uint32_t *)&m_reshi + 1)); // mov ecx,reshi.hi
- emit_mov_p64_r64(a, edstp, eax, ecx); // mov edstp,ecx:eax
- }
- if (inst.flags() != 0)
- a.popfd(); // popf
- }
-}
-
-
-//-------------------------------------------------
-// op_mululw - process a MULULW (32x32=32) opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_mululw(Assembler &a, 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 src1p(*this, inst.param(1), PTYPE_MRI);
- be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- normalize_commutative(src1p, src2p);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
- emit_mov_r32_p32(a, edx, src2p); // mov edx,src2p
- a.mul(edx); // mul edx
-
- emit_mov_p32_r32(a, dstp, eax); // mov dstp,eax
-
- if (inst.flags())
- {
- a.test(eax, eax);
- a.pushfd(); // sign + zero
-
- // if edx is not zero then it overflowed
- a.test(edx, edx);
- a.pushfd();
- a.pop(edx);
- a.and_(edx, 0x40); // zero
- a.xor_(edx, 0x40);
- a.shl(edx, 5); // turn into overflow flag
- a.or_(dword_ptr(esp, 0), edx);
-
- a.popfd();
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- a.mov(dword_ptr(esp, 24), inst.flags() ? 1 : 0); // mov [esp+24],flags
- emit_mov_m64_p64(a, qword_ptr(esp, 16), src2p); // mov [esp+16],src2p
- emit_mov_m64_p64(a, qword_ptr(esp, 8), src1p); // mov [esp+8],src1p
- a.mov(dword_ptr(esp, 4), imm(&m_reslo)); // mov [esp+4],&reslo
- a.mov(dword_ptr(esp, 0), imm(&m_reslo)); // mov [esp],&reslo
- a.call(imm(dmulu<true>)); // call dmulu (calculate ZS flags as 64*64->64)
- if (inst.flags() != 0)
- a.push(dword_ptr(uintptr_t(flags_unmap), eax, 2)); // push flags_unmap[eax*4]
- a.mov(eax, MABS((uint32_t *)&m_reslo + 0)); // mov eax,reslo.lo
- a.mov(edx, MABS((uint32_t *)&m_reslo + 1)); // mov edx,reslo.hi
- emit_mov_p64_r64(a, dstp, eax, edx); // mov dstp,edx:eax
- if (inst.flags() != 0)
- a.popfd(); // popf
- }
-
-}
-
-
-//-------------------------------------------------
-// op_muls - process a MULS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_muls(Assembler &a, 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);
- normalize_commutative(src1p, src2p);
- const bool compute_hi = (dstp != edstp);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
- emit_mov_r32_p32(a, edx, src2p); // mov edx,src2p
- a.imul(edx); // imul edx
-
- emit_mov_p32_r32(a, dstp, eax); // mov dstp,eax
- if (compute_hi)
- emit_mov_p32_r32(a, edstp, edx); // mov edstp,edx
-
- if (inst.flags())
- {
- a.pushfd();
-
- a.test(edx, edx);
- a.pushfd(); // will have the sign flag + upper half zero
- a.pop(edx);
-
- a.test(eax, eax);
- a.pushfd(); // lower half zero
- a.pop(eax);
-
- a.and_(dword_ptr(esp, 0), ~(0x40 | 0x80));
- a.mov(ecx, edx);
- a.and_(ecx, 0x80); // sign
-
- a.and_(eax, edx);
- a.and_(eax, 0x40); // zero
-
- a.or_(eax, ecx);
- a.or_(dword_ptr(esp, 0), eax);
-
- a.popfd();
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- a.mov(dword_ptr(esp, 24), inst.flags() ? 1 : 0); // mov [esp+24],flags
- emit_mov_m64_p64(a, qword_ptr(esp, 16), src2p); // mov [esp+16],src2p
- emit_mov_m64_p64(a, qword_ptr(esp, 8), src1p); // mov [esp+8],src1p
- if (!compute_hi)
- a.mov(dword_ptr(esp, 4), imm(&m_reslo)); // mov [esp+4],&reslo
- else
- a.mov(dword_ptr(esp, 4), imm(&m_reshi)); // push [esp+4],&reshi
- a.mov(dword_ptr(esp, 0), imm(&m_reslo)); // mov [esp],&reslo
- a.call(imm(dmuls<false>)); // call dmuls (calculate ZS flags as 64*64->128)
- if (inst.flags() != 0)
- a.push(dword_ptr(uintptr_t(flags_unmap), eax, 2)); // push flags_unmap[eax*4]
- a.mov(eax, MABS((uint32_t *)&m_reslo + 0)); // mov eax,reslo.lo
- a.mov(edx, MABS((uint32_t *)&m_reslo + 1)); // mov edx,reslo.hi
- emit_mov_p64_r64(a, dstp, eax, edx); // mov dstp,edx:eax
- if (compute_hi)
- {
- a.mov(eax, MABS((uint32_t *)&m_reshi + 0)); // mov eax,reshi.lo
- a.mov(edx, MABS((uint32_t *)&m_reshi + 1)); // mov edx,reshi.hi
- emit_mov_p64_r64(a, edstp, eax, edx); // mov edstp,edx:eax
- }
- if (inst.flags() != 0)
- a.popfd(); // popf
- }
-}
-
-
-//-------------------------------------------------
-// op_mulslw - process a MULSLW (32x32=32) opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_mulslw(Assembler &a, 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 src1p(*this, inst.param(1), PTYPE_MRI);
- be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- normalize_commutative(src1p, src2p);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
- emit_mov_r32_p32(a, edx, src2p); // mov edx,src2p
- a.imul(edx); // imul edx
-
- emit_mov_p32_r32(a, dstp, eax); // mov dstp,eax
-
- if (inst.flags())
- {
- a.test(eax, eax);
- a.pushfd(); // sign + zero
-
- a.mov(ecx, edx);
- a.cdq();
-
- a.cmp(ecx, edx);
- a.pushfd();
- a.pop(edx);
- a.and_(edx, 0x40); // zero
- a.xor_(edx, 0x40);
- a.shl(edx, 5); // turn into overflow flag
- a.or_(dword_ptr(esp, 0), edx);
-
- a.popfd();
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- a.mov(dword_ptr(esp, 24), inst.flags() ? 1 : 0); // mov [esp+24],flags
- emit_mov_m64_p64(a, qword_ptr(esp, 16), src2p); // mov [esp+16],src2p
- emit_mov_m64_p64(a, qword_ptr(esp, 8), src1p); // mov [esp+8],src1p
- a.mov(dword_ptr(esp, 4), imm(&m_reslo)); // mov [esp+4],&reslo
- a.mov(dword_ptr(esp, 0), imm(&m_reslo)); // mov [esp],&reslo
- a.call(imm(dmuls<true>)); // call dmuls (calculate ZS flags as 64*64->64)
- if (inst.flags() != 0)
- a.push(dword_ptr(uintptr_t(flags_unmap), eax, 2)); // push flags_unmap[eax*4]
- a.mov(eax, MABS((uint32_t *)&m_reslo + 0)); // mov eax,reslo.lo
- a.mov(edx, MABS((uint32_t *)&m_reslo + 1)); // mov edx,reslo.hi
- emit_mov_p64_r64(a, dstp, eax, edx); // mov dstp,edx:eax
- if (inst.flags() != 0)
- a.popfd(); // popf
- }
-}
-
-
-//-------------------------------------------------
-// op_divu - process a DIVU opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_divu(Assembler &a, 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);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, ecx, src2p); // mov ecx,src2p
- if (inst.flags() != 0)
- {
- a.mov(eax, 0xa0000000); // mov eax,0xa0000000
- a.add(eax, eax); // add eax,eax
- }
- Label skip = a.new_label();
- a.short_().jecxz(skip); // jecxz skip
- emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
- a.xor_(edx, edx); // xor edx,edx
- a.div(ecx); // div ecx
- emit_mov_p32_r32(a, dstp, eax); // mov dstp,eax
- if (compute_rem)
- emit_mov_p32_r32(a, edstp, edx); // mov edstp,edx
- if (inst.flags() != 0)
- a.test(eax, eax); // test eax,eax
- a.bind(skip); // skip:
- reset_last_upper_lower_reg();
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- emit_mov_m64_p64(a, qword_ptr(esp, 16), src2p); // mov [esp+16],src2p
- emit_mov_m64_p64(a, qword_ptr(esp, 8), src1p); // mov [esp+8],src1p
- if (!compute_rem)
- a.mov(dword_ptr(esp, 4), imm(&m_reslo)); // mov [esp+4],&reslo
- else
- a.mov(dword_ptr(esp, 4), imm(&m_reshi)); // push [esp+4],&reshi
- a.mov(dword_ptr(esp, 0), imm(&m_reslo)); // mov [esp],&reslo
- a.call(imm(ddivu)); // call ddivu
- if (inst.flags() != 0)
- a.push(dword_ptr(uintptr_t(flags_unmap), eax, 2)); // push flags_unmap[eax*4]
- a.mov(eax, MABS((uint32_t *)&m_reslo + 0)); // mov eax,reslo.lo
- a.mov(edx, MABS((uint32_t *)&m_reslo + 1)); // mov edx,reslo.hi
- emit_mov_p64_r64(a, dstp, eax, edx); // mov dstp,edx:eax
- if (compute_rem)
- {
- a.mov(eax, MABS((uint32_t *)&m_reshi + 0)); // mov eax,reshi.lo
- a.mov(edx, MABS((uint32_t *)&m_reshi + 1)); // mov edx,reshi.hi
- emit_mov_p64_r64(a, edstp, eax, edx); // mov edstp,edx:eax
- }
- if (inst.flags() != 0)
- a.popfd(); // popf
- }
-}
-
-
-//-------------------------------------------------
-// op_divs - process a DIVS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_divs(Assembler &a, 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);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- emit_mov_r32_p32(a, ecx, src2p); // mov ecx,src2p
- if (inst.flags() != 0)
- {
- a.mov(eax, 0xa0000000); // mov eax,0xa0000000
- a.add(eax, eax); // add eax,eax
- }
- Label skip = a.new_label();
- a.short_().jecxz(skip); // jecxz skip
- emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
- a.cdq(); // cdq
- a.idiv(ecx); // idiv ecx
- emit_mov_p32_r32(a, dstp, eax); // mov dstp,eax
- if (compute_rem)
- emit_mov_p32_r32(a, edstp, edx); // mov edstp,edx
- if (inst.flags() != 0)
- a.test(eax, eax); // test eax,eax
- a.bind(skip); // skip:
- reset_last_upper_lower_reg();
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- emit_mov_m64_p64(a, qword_ptr(esp, 16), src2p); // mov [esp+16],src2p
- emit_mov_m64_p64(a, qword_ptr(esp, 8), src1p); // mov [esp+8],src1p
- if (!compute_rem)
- a.mov(dword_ptr(esp, 4), imm(&m_reslo)); // mov [esp+4],&reslo
- else
- a.mov(dword_ptr(esp, 4), imm(&m_reshi)); // push [esp+4],&reshi
- a.mov(dword_ptr(esp, 0), imm(&m_reslo)); // mov [esp],&reslo
- a.call(imm(ddivs)); // call ddivs
- if (inst.flags() != 0)
- a.push(dword_ptr(uintptr_t(flags_unmap), eax, 2)); // push flags_unmap[eax*4]
- a.mov(eax, MABS((uint32_t *)&m_reslo + 0)); // mov eax,reslo.lo
- a.mov(edx, MABS((uint32_t *)&m_reslo + 1)); // mov edx,reslo.hi
- emit_mov_p64_r64(a, dstp, eax, edx); // mov dstp,edx:eax
- if (compute_rem)
- {
- a.mov(eax, MABS((uint32_t *)&m_reshi + 0)); // mov eax,reshi.lo
- a.mov(edx, MABS((uint32_t *)&m_reshi + 1)); // mov edx,reshi.hi
- emit_mov_p64_r64(a, edstp, eax, edx); // mov edstp,edx:eax
- }
- if (inst.flags() != 0)
- a.popfd(); // popf
- }
-}
-
-
-//-------------------------------------------------
-// op_and - process a AND opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_and(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdAnd, MABS(dstp.memory(), 4), src2p, // and [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && !src.immediate())
- {
- a.mov(dst.as<Mem>(), imm(0));
- return true;
- }
- else if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- return true;
-
- return false;
- });
-
- // dstp == src2p in memory
- else if (dstp.is_memory() && dstp == src2p)
- alu_op_param(a, Inst::kIdAnd, MABS(dstp.memory(), 4), src1p, // and [dstp],src1p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && !src.immediate())
- {
- a.mov(dst.as<Mem>(), imm(0));
- return true;
- }
- else if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- return true;
-
- return false;
- });
-
- // AND with immediate 0xff
- else if (src2p.is_immediate_value(0xff) && !inst.flags())
- {
- if (src1p.is_int_register())
- {
- if (src1p.ireg() & 4)
- {
- if (dstreg.id() != src1p.ireg())
- a.mov(dstreg, gpd(src1p.ireg())); // mov dstreg,src1p
- a.and_(dstreg, 0xff); // and dstreg,0xff
- }
- else
- a.movzx(dstreg, gpb_lo(src1p.ireg())); // movzx dstreg,src1p
- }
- else if (src1p.is_memory())
- a.movzx(dstreg, MABS(src1p.memory(), 1)); // movzx dstreg,[src1p]
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
-
- // AND with immediate 0xffff
- else if (src2p.is_immediate_value(0xffff) && !inst.flags())
- {
- if (src1p.is_int_register())
- a.movzx(dstreg, gpw(src1p.ireg())); // movzx dstreg,src1p
- else if (src1p.is_memory())
- a.movzx(dstreg, MABS(src1p.memory(), 2)); // movzx dstreg,[src1p]
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdAnd, dstreg, src2p, // and dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && !src.immediate())
- {
- a.xor_(dst.as<Gp>(), dst.as<Gp>());
- return true;
- }
- else if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- return true;
-
- return false;
- });
- emit_mov_p32_r32(a, 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(a, MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), // and [dstp],src2p
- src2p, inst);
-
- // dstp == src2p in memory
- else if (dstp.is_memory() && dstp == src2p)
- emit_and_m64_p64(a, MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), // and [dstp],src1p
- src1p, inst);
-
- // AND with immediate 0xff
- else if (src2p.is_immediate_value(0xff) && !inst.flags())
- {
- if (src1p.is_int_register())
- {
- if (src1p.ireg() & 4)
- {
- if (dstreg.id() != src1p.ireg())
- a.mov(dstreg, gpd(src1p.ireg())); // mov dstreg,src1p
- a.and_(dstreg, 0xff); // and dstreg,0xff
- }
- else
- a.movzx(dstreg, gpb_lo(src1p.ireg())); // movzx dstreg,src1p
- }
- else if (src1p.is_memory())
- a.movzx(dstreg, MABS(src1p.memory(), 1)); // movzx dstreg,[src1p]
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov dsthi,0
- else if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov dsthi,0
- }
-
- // AND with immediate 0xffff
- else if (src2p.is_immediate_value(0xffff) && !inst.flags())
- {
- if (src1p.is_int_register())
- a.movzx(dstreg, gpw(src1p.ireg())); // movzx dstreg,src1p
- else if (src1p.is_memory())
- a.movzx(dstreg, MABS(src1p.memory(), 2)); // movzx dstreg,[src1p]
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov dsthi,0
- else if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov dsthi,0
- }
-
- // AND with immediate 0xffffffff
- else if (src2p.is_immediate_value(0xffffffffU) && !inst.flags())
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov dsthi,0
- else if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov dsthi,0
- }
-
- // AND with immediate 0xffffffff00000000
- else if (src2p.is_immediate_value(0xffffffff00000000ULL) && !inst.flags())
- {
- if (src1p != dstp)
- {
- emit_mov_r64_p64(a, Gp(), edx, src1p); // mov dstreg,src1p
- emit_mov_p64_r64(a, dstp, Gp(), edx); // mov dstp,dstreg
- }
- if (dstp.is_int_register())
- a.xor_(gpd(dstp.ireg()), gpd(dstp.ireg())); // xor dstlo,dstlo
- else if (dstp.is_memory())
- a.mov(MABS(dstp.memory(0), 4), 0); // mov dstlo,0
- }
-
- // AND with immediate <= 0xffffffff
- else if (src2p.is_immediate() && src2p.immediate() <= 0xffffffffU && !inst.flags())
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdAnd, dstreg, src2p, // and dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && !src.immediate())
- {
- a.xor_(dst.as<Gp>(), dst.as<Gp>());
- return true;
- }
- else if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- return true;
-
- return false;
- });
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- if (dstp.is_int_register())
- a.mov(MABS(m_reghi[dstp.ireg()], 4), 0); // mov dsthi,0
- else if (dstp.is_memory())
- a.mov(MABS(dstp.memory(4), 4), 0); // mov dsthi,0
- }
-
- // general case
- else
- {
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_and_r64_p64(a, dstreg, edx, src2p, inst); // and edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_test - process a TEST opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_test(Assembler &a, 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
- Gp const src1reg = src1p.select_register(eax);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // src1p in memory
- if (src1p.is_memory())
- alu_op_param(a, Inst::kIdTest, MABS(src1p.memory(), 4), src2p); // test [src1p],src2p
-
- // general case
- else
- {
- emit_mov_r32_p32(a, src1reg, src1p); // mov src1reg,src1p
- alu_op_param(a, Inst::kIdTest, src1reg, src2p); // test src1reg,src2p
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // src1p in memory
- if (src1p.is_memory())
- alu_op_param(a, Inst::kIdTest, Inst::kIdTest, // test [dstp],src2p
- MABS(src1p.memory(0), 4), MABS(src1p.memory(4), 4), src2p, inst.flags() & FLAG_Z);
-
- // general case
- else
- {
- emit_mov_r64_p64(a, src1reg, edx, src1p); // mov src1reg:dstp,[src1p]
- alu_op_param(a, Inst::kIdTest, Inst::kIdTest, // test src1reg:dstp,src2p
- src1reg, edx, src2p, inst.flags() & FLAG_Z);
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_or - process a OR opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_or(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdOr, MABS(dstp.memory(), 4), src2p, // or [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- {
- a.mov(dst.as<Mem>(), imm(-1));
- return true;
- }
- else if (!inst.flags() && !src.immediate())
- return true;
-
- return false;
- });
- // dstp == src2p in memory
- else if (dstp.is_memory() && dstp == src2p)
- alu_op_param(a, Inst::kIdOr, MABS(dstp.memory(), 4), src1p, // or [dstp],src1p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- {
- a.mov(dst.as<Mem>(), imm(-1));
- return true;
- }
- else if (!inst.flags() && !src.immediate())
- return true;
-
- return false;
- });
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdOr, dstreg, src2p, // or dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- {
- a.mov(dst.as<Gp>(), imm(-1));
- return true;
- }
- else if (!inst.flags() && !src.immediate())
- return true;
-
- return false;
- });
- emit_mov_p32_r32(a, 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(a, MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), // or [dstp],src2p
- src2p, inst);
-
- // dstp == src2p in memory
- else if (dstp.is_memory() && dstp == src2p)
- emit_or_m64_p64(a, MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), // or [dstp],src1p
- src1p, inst);
-
- // general case
- else
- {
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_or_r64_p64(a, dstreg, edx, src2p, inst); // or edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_xor - process a XOR opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_xor(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- alu_op_param(a, Inst::kIdXor, MABS(dstp.memory(), 4), src2p, // xor [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- {
- a.not_(dst.as<Mem>());
- return true;
- }
- else if (!inst.flags() && !src.immediate())
- return true;
-
- return false;
- });
-
- // dstp == src2p in memory
- else if (dstp.is_memory() && dstp == src2p)
- alu_op_param(a, Inst::kIdXor, MABS(dstp.memory(), 4), src1p, // xor [dstp],src1p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- {
- a.not_(dst.as<Mem>());
- return true;
- }
- else if (!inst.flags() && !src.immediate())
- return true;
-
- return false;
- });
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- alu_op_param(a, Inst::kIdXor, dstreg, src2p, // xor dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize all-zero and all-one cases
- if (!inst.flags() && u32(src.immediate()) == 0xffffffffU)
- {
- a.not_(dst.as<Gp>());
- return true;
- }
- else if (!inst.flags() && !src.immediate())
- return true;
-
- return false;
- });
- emit_mov_p32_r32(a, 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(a, MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), // xor [dstp],src2p
- src2p, inst);
-
- // dstp == src1p in memory
- else if (dstp.is_memory() && dstp == src2p)
- emit_xor_m64_p64(a, MABS(dstp.memory(0), 4), MABS(dstp.memory(4), 4), // xor [dstp],src1p
- src1p, inst);
-
- // general case
- else
- {
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_xor_r64_p64(a, dstreg, edx, src2p, inst); // xor edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_lzcnt - process a LZCNT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_lzcnt(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax);
-
- if (inst.flags())
- {
- a.xor_(eax, eax); // reset status flags
- a.test(eax, eax);
- }
-
- // 32-bit form
- if (inst.size() == 4)
- {
- emit_mov_r32_p32(a, dstreg, srcp); // mov dstreg,src1p
- a.mov(ecx, 32 ^ 31); // mov ecx,32 ^ 31
- a.bsr(dstreg, dstreg); // bsr dstreg,dstreg
- a.cmovz(dstreg, ecx); // cmovz dstreg,ecx
- a.xor_(dstreg, 31); // xor dstreg,31
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- a.test(dstreg, dstreg);
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- emit_mov_r64_p64(a, dstreg, edx, srcp); // mov dstreg:edx,srcp
-
- Label skip = a.new_label();
- Label end = a.new_label();
-
- a.bsr(edx, edx);
- a.short_().jz(skip);
- a.xor_(edx, 31 ^ 63);
- a.mov(dstreg, edx);
- a.short_().jmp(end);
-
- a.bind(skip);
- a.mov(edx, 64 ^ 63);
- a.bsr(dstreg, dstreg);
- a.cmovz(dstreg, edx);
-
- a.bind(end);
-
- a.xor_(dstreg, 63);
- a.mov(edx, 0);
-
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
-
- a.test(dstreg, dstreg);
- }
-}
-
-
-//-------------------------------------------------
-// op_tzcnt - process a TZCNT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_tzcnt(Assembler &a, 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);
-
- Gp const dstreg = dstp.select_register(eax);
-
- if (inst.flags())
- {
- a.xor_(eax, eax); // reset status flags
- a.test(eax, eax);
- }
-
- // 32-bit form
- if (inst.size() == 4)
- {
- emit_mov_r32_p32(a, dstreg, srcp); // mov dstreg,src1p
- a.mov(ecx, 32); // mov ecx,32
- a.bsf(dstreg, dstreg); // bsf dstreg,dstreg
- a.cmovz(dstreg, ecx); // cmovz dstreg,ecx
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
-
- a.mov(ecx, dstreg);
- a.xor_(ecx, 32);
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- Label skip = a.new_label();
- emit_mov_r64_p64(a, dstreg, edx, srcp); // mov dstreg:edx,srcp
- a.bsf(dstreg, dstreg); // bsf dstreg,dstreg
- a.short_().jnz(skip); // jnz skip
- a.mov(ecx, 32); // mov ecx,32
- a.bsf(dstreg, edx); // bsf dstreg,edx
- a.cmovz(dstreg, ecx); // cmovz dstreg,ecx
- a.add(dstreg, 32); // add dstreg,32
- a.bind(skip); // skip:
- reset_last_upper_lower_reg();
- a.xor_(edx, edx); // xor edx,edx
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
-
- a.mov(ecx, dstreg);
- a.xor_(ecx, 64);
- }
-}
-
-
-//-------------------------------------------------
-// op_bswap - process a BSWAP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_bswap(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- emit_mov_r32_p32(a, dstreg, srcp); // mov dstreg,src1p
- a.bswap(dstreg); // bswap dstreg
- if (inst.flags() != 0)
- a.test(dstreg, dstreg); // test dstreg,dstreg
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- emit_mov_r64_p64(a, edx, dstreg, srcp); // mov dstreg:edx,srcp
- a.bswap(dstreg); // bswap dstreg
- a.bswap(edx); // bswap edx
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- if (inst.flags() == FLAG_Z)
- a.or_(edx, dstreg); // or edx,eax
- else if (inst.flags() == FLAG_S)
- a.test(edx, edx); // test edx,edx
- else
- {
- a.movzx(ecx, dstreg.r16()); // movzx ecx,dstreg
- a.or_(edx, ecx); // or edx,ecx
- a.mov(ecx, dstreg); // mov ecx,dstreg
- a.shr(ecx, 16); // shr ecx,16
- a.or_(edx, ecx); // or edx,ecx
- }
- }
-}
-
-
-//-------------------------------------------------
-// op_shl - process a SHL opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_shl(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- if (inst.size() == 4)
- {
- // 32-bit form
- if (dstp.is_memory() && dstp == src1p)
- {
- // dstp == src1p in memory
- shift_op_param(a, Inst::kIdShl, inst.size(), MABS(dstp.memory(), 4), src2p,
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, inst.flags() != 0);
- }
- else
- {
- // general case
- emit_mov_r32_p32(a, dstreg, src1p);
- shift_op_param(a, Inst::kIdShl, inst.size(), dstreg, src2p,
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, inst.flags() != 0);
- emit_mov_p32_r32(a, dstp, dstreg);
- }
- }
- else if (inst.size() == 8)
- {
- // 64-bit form
- if (src2p.is_immediate_value(32) && !inst.flags())
- {
- // shift by 32 bits
- emit_mov_r32_p32(a, edx, src1p);
- a.xor_(dstreg, dstreg);
- }
- else
- {
- // general case
- emit_mov_r64_p64(a, dstreg, edx, src1p);
- emit_shl_r64_p64(a, dstreg, edx, src2p, inst.flags());
- }
- emit_mov_p64_r64(a, dstp, dstreg, edx);
- }
-}
-
-
-//-------------------------------------------------
-// op_shr - process a SHR opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_shr(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- shift_op_param(a, Inst::kIdShr, inst.size(), MABS(dstp.memory(), 4), src2p, // shr [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, inst.flags() != 0);
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- shift_op_param(a, Inst::kIdShr, inst.size(), dstreg, src2p, // shr dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, inst.flags() != 0);
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // general case
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_shr_r64_p64(a, dstreg, edx, src2p, inst.flags()); // shr edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-//-------------------------------------------------
-// op_sar - process a SAR opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_sar(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- shift_op_param(a, Inst::kIdSar, inst.size(), MABS(dstp.memory(), 4), src2p, // sar [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- shift_op_param(a, Inst::kIdSar, inst.size(), dstreg, src2p, // sar dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // general case
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_sar_r64_p64(a, dstreg, edx, src2p, inst.flags()); // sar edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-//-------------------------------------------------
-// op_rol - process a rol opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_rol(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- shift_op_param(a, Inst::kIdRol, inst.size(), MABS(dstp.memory(), 4), src2p, // rol [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- shift_op_param(a, Inst::kIdRol, inst.size(), dstreg, src2p, // rol dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // general case
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_rol_r64_p64(a, dstreg, edx, src2p, inst.flags()); // rol edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-//-------------------------------------------------
-// op_ror - process a ROR opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_ror(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- shift_op_param(a, Inst::kIdRor, inst.size(), MABS(dstp.memory(), 4), src2p, // ror [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
-
- // general case
- else
- {
- emit_mov_r32_p32(a, dstreg, src1p); // mov dstreg,src1p
- shift_op_param(a, Inst::kIdRor, inst.size(), dstreg, src2p, // rol dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // general case
- emit_mov_r64_p64(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_ror_r64_p64(a, dstreg, edx, src2p, inst.flags()); // ror edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-//-------------------------------------------------
-// op_rolc - process a ROLC opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_rolc(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- shift_op_param(a, Inst::kIdRcl, inst.size(), MABS(dstp.memory(), 4), src2p, // rcl [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
-
- // general case
- else
- {
- emit_mov_r32_p32_keepflags(a, dstreg, src1p); // mov dstreg,src1p
- shift_op_param(a, Inst::kIdRcl, inst.size(), dstreg, src2p, // rcl dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // general case
- emit_mov_r64_p64_keepflags(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_rcl_r64_p64(a, dstreg, edx, src2p, inst.flags()); // rcl edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-//-------------------------------------------------
-// op_rorc - process a RORC opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_rorc(Assembler &a, 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
- Gp const dstreg = dstp.select_register(eax, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- // dstp == src1p in memory
- if (dstp.is_memory() && dstp == src1p)
- shift_op_param(a, Inst::kIdRcr, inst.size(), MABS(dstp.memory(), 4), src2p, // rcr [dstp],src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
-
- // general case
- else
- {
- emit_mov_r32_p32_keepflags(a, dstreg, src1p); // mov dstreg,src1p
- shift_op_param(a, Inst::kIdRcr, inst.size(), dstreg, src2p, // rcr dstreg,src2p
- [inst](Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // optimize zero case
- return (!inst.flags() && !src.immediate());
- }, true);
- emit_mov_p32_r32(a, dstp, dstreg); // mov dstp,dstreg
- }
- }
-
- // 64-bit form
- else if (inst.size() == 8)
- {
- // general case
- emit_mov_r64_p64_keepflags(a, dstreg, edx, src1p); // mov edx:dstreg,[src1p]
- emit_rcr_r64_p64(a, dstreg, edx, src2p, inst.flags()); // rcr edx:dstreg,src2p
- emit_mov_p64_r64(a, dstp, dstreg, edx); // mov dstp,edx:dstreg
- }
-}
-
-
-
-//**************************************************************************
-// FLOATING POINT OPERATIONS
-//**************************************************************************
-
-//-------------------------------------------------
-// op_fload - process a FLOAD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fload(Assembler &a, 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);
-
- if (indp.is_immediate())
- {
- // immediate index
- a.mov(eax, MABS(basep.memory(inst.size()*indp.immediate())));
- if (inst.size() == 8)
- a.mov(edx, MABS(basep.memory(4 + inst.size()*indp.immediate())));
- }
- else
- {
- // other index
- Gp const indreg = indp.select_register(ecx);
- emit_mov_r32_p32_keepflags(a, indreg, indp);
- a.mov(eax, ptr(uintptr_t(basep.memory(0)), indreg, (inst.size() == 8) ? 3 : 2));
- if (inst.size() == 8)
- a.mov(edx, ptr(uintptr_t(basep.memory(4)), indreg, (inst.size() == 8) ? 3 : 2));
- }
-
- // general case
- a.mov(MABS(dstp.memory(0)), eax);
- if (inst.size() == 8)
- a.mov(MABS(dstp.memory(4)), edx);
-}
-
-
-//-------------------------------------------------
-// op_fstore - process a FSTORE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fstore(Assembler &a, 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);
-
- a.mov(eax, MABS(srcp.memory(0)));
- if (inst.size() == 8)
- a.mov(edx, MABS(srcp.memory(4)));
-
- if (indp.is_immediate())
- {
- // immediate index
- a.mov(MABS(basep.memory(inst.size()*indp.immediate())), eax);
- if (inst.size() == 8)
- a.mov(MABS(basep.memory(4 + inst.size()*indp.immediate())), edx);
- }
- else
- {
- // other index
- Gp const indreg = indp.select_register(ecx);
- emit_mov_r32_p32_keepflags(a, indreg, indp);
- a.mov(ptr(uintptr_t(basep.memory(0)), indreg, (inst.size() == 8) ? 3 : 2), eax);
- if (inst.size() == 8)
- a.mov(ptr(uintptr_t(basep.memory(4)), indreg, (inst.size() == 8) ? 3 : 2), edx);
- }
-}
-
-
-//-------------------------------------------------
-// op_fread - process a FREAD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fread(Assembler &a, 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);
- parameter const &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
- auto const &accessors = m_memory_accessors[spacep.space()];
- auto const &accessor = (inst.size() == 4) ? accessors.read_dword : accessors.read_qword;
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), addrp);
- if (USE_THISCALL)
- a.mov(ecx, imm(accessor.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessor.obj));
- a.call(imm(accessor.func));
- if (USE_THISCALL)
- a.sub(esp, 4);
-
- // store result
- if (inst.size() == 4)
- emit_mov_p32_r32(a, dstp, eax);
- else if (inst.size() == 8)
- emit_mov_p64_r64(a, dstp, eax, edx);
-}
-
-
-//-------------------------------------------------
-// op_fwrite - process a FWRITE opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fwrite(Assembler &a, 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);
- parameter const &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
- auto const &accessors = m_memory_accessors[spacep.space()];
- auto const &accessor = (inst.size() == 4) ? accessors.write_dword : accessors.write_qword;
- if (inst.size() == 4)
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 4 : 8), srcp);
- else if (inst.size() == 8)
- emit_mov_m64_p64(a, qword_ptr(esp, USE_THISCALL ? 4 : 8), srcp);
- emit_mov_m32_p32(a, dword_ptr(esp, USE_THISCALL ? 0 : 4), addrp);
- if (USE_THISCALL)
- a.mov(ecx, imm(accessor.obj));
- else
- a.mov(dword_ptr(esp, 0), imm(accessor.obj));
- a.call(imm(accessor.func));
- if (USE_THISCALL)
- a.sub(esp, (inst.size() == 4) ? 8 : 12);
-}
-
-
-//-------------------------------------------------
-// op_fmov - process a FMOV opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fmov(Assembler &a, 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
- Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.new_label();
- a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);
- }
-
- // general case
- a.mov(eax, MABS(srcp.memory(0)));
- if (inst.size() == 8)
- a.mov(edx, MABS(srcp.memory(4)));
- a.mov(MABS(dstp.memory(0)), eax);
- if (inst.size() == 8)
- a.mov(MABS(dstp.memory(4)), edx);
-
- // resolve the jump
- if (inst.condition() != uml::COND_ALWAYS)
- {
- a.bind(skip);
- reset_last_upper_lower_reg();
- }
-}
-
-
-//-------------------------------------------------
-// op_ftoint - process a FTOINT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_ftoint(Assembler &a, 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);
- parameter const &sizep = inst.param(2);
- assert(sizep.is_size());
- parameter const &roundp = inst.param(3);
- assert(roundp.is_rounding());
-
- // set rounding mode if necessary
- if (roundp.rounding() != ROUND_DEFAULT && (!m_sse3 || roundp.rounding() != ROUND_TRUNC))
- {
- a.fstcw(MABS(&m_fmodesave)); // fstcw [fmodesave]
- a.fldcw(MABS(&fp_control[roundp.rounding()])); // fldcw fpcontrol[roundp]
- }
-
- // general case
- emit_fld_p(a, 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)
- a.fistp(MABS(dstp.memory(), 4)); // fistp [dstp]
- else
- a.fisttp(MABS(dstp.memory(), 4)); // fisttp [dstp]
- }
- else if (dstp.is_int_register())
- {
- if (!m_sse3 || roundp.rounding() != ROUND_TRUNC)
- a.fistp(MABS(m_reglo[dstp.ireg()], 4)); // fistp reglo[dstp]
- else
- a.fisttp(MABS(m_reglo[dstp.ireg()], 4)); // fisttp reglo[dstp]
- a.mov(gpd(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)
- a.fistp(MABS(dstp.memory(), 8)); // fistp [dstp]
- else
- a.fisttp(MABS(dstp.memory(), 8)); // fisttp [dstp]
- }
- else if (dstp.is_int_register())
- {
- if (!m_sse3 || roundp.rounding() != ROUND_TRUNC)
- a.fistp(MABS(m_reglo[dstp.ireg()], 8)); // fistp reglo[dstp]
- else
- a.fisttp(MABS(m_reglo[dstp.ireg()], 8)); // fisttp reglo[dstp]
- a.mov(gpd(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))
- a.fldcw(MABS(&m_fmodesave)); // fldcw [fmodesave]
-}
-
-
-//-------------------------------------------------
-// op_ffrint - process a FFRINT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_ffrint(Assembler &a, 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);
- parameter const &sizep = inst.param(2);
- assert(sizep.is_size());
-
- if (sizep.size() == SIZE_DWORD)
- {
- // 4-byte integer case
- if (srcp.is_immediate())
- {
- a.mov(MABS(&m_fptemp, 4), srcp.immediate()); // mov [fptemp],srcp
- a.fild(MABS(&m_fptemp, 4)); // fild [fptemp]
- }
- else if (srcp.is_memory())
- a.fild(MABS(srcp.memory(), 4)); // fild [srcp]
- else if (srcp.is_int_register())
- {
- a.mov(MABS(m_reglo[srcp.ireg()]), gpd(srcp.ireg())); // mov reglo[srcp],srcp
- a.fild(MABS(m_reglo[srcp.ireg()], 4)); // fild reglo[srcp]
- }
- }
- else if (sizep.size() == SIZE_QWORD)
- {
- // 8-bit integer case
- if (srcp.is_immediate())
- {
- a.mov(MABS(&m_fptemp, 4), srcp.immediate()); // mov [fptemp],srcp
- a.mov(MABS((uint8_t *)&m_fptemp + 4, 4), srcp.immediate()); // mov [fptemp+4],srcp
- a.fild(MABS(&m_fptemp, 8)); // fild [fptemp]
- }
- else if (srcp.is_memory())
- a.fild(MABS(srcp.memory(), 8)); // fild [srcp]
- else if (srcp.is_int_register())
- {
- a.mov(MABS(m_reglo[srcp.ireg()]), gpd(srcp.ireg())); // mov reglo[srcp],srcp
- a.fild(MABS(m_reglo[srcp.ireg()], 8)); // fild reglo[srcp]
- }
- }
-
- // store the result and be done
- emit_fstp_p(a, inst.size(), dstp); // fstp [dstp]
-}
-
-
-//-------------------------------------------------
-// op_ffrflt - process a FFRFLT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_ffrflt(Assembler &a, 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);
- parameter const &sizep = inst.param(2);
- assert(sizep.is_size());
-
- // general case
- if (sizep.size() == SIZE_DWORD)
- a.fld(MABS(srcp.memory(), 4)); // fld [srcp]
- else if (sizep.size() == SIZE_QWORD)
- a.fld(MABS(srcp.memory(), 8)); // fld [srcp]
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_frnds - process a FRNDS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_frnds(Assembler &a, 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(a, inst.size(), srcp); // fld srcp
- a.fstp(MABS(&m_fptemp, 4)); // fstp [fptemp]
- a.fld(MABS(&m_fptemp, 4)); // fld [fptemp]
- emit_fstp_p(a, inst.size(), dstp); // fstp [dstp]
-}
-
-
-//-------------------------------------------------
-// op_fadd - process a FADD opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fadd(Assembler &a, 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(a, inst.size(), src1p); // fld src1p
- emit_fld_p(a, inst.size(), src2p); // fld src2p
- a.faddp(); // faddp
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fsub - process a FSUB opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fsub(Assembler &a, 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(a, inst.size(), src1p); // fld src1p
- emit_fld_p(a, inst.size(), src2p); // fld src2p
- a.fsubp(); // fsubp
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fcmp - process a FCMP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fcmp(Assembler &a, 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(a, inst.size(), src2p);
- emit_fld_p(a, inst.size(), src1p);
- a.fcomip(st1);
- a.fstp(st0);
-
- if (inst.flags() & (FLAG_Z | FLAG_C))
- {
- // clear Z and C if unordered
- Label ordered = a.new_label();
-
- a.short_().jnp(ordered);
- a.lahf();
- a.and_(eax, 0x00003e00);
- a.sahf();
-
- a.bind(ordered);
- }
-}
-
-
-//-------------------------------------------------
-// op_fmul - process a FMUL opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fmul(Assembler &a, 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(a, inst.size(), src1p); // fld src1p
- emit_fld_p(a, inst.size(), src2p); // fld src2p
- a.fmulp(); // fmulp
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fdiv - process a FDIV opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fdiv(Assembler &a, 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(a, inst.size(), src1p); // fld src1p
- emit_fld_p(a, inst.size(), src2p); // fld src2p
- a.fdivp(); // fdivp
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fneg - process a FNEG opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fneg(Assembler &a, 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(a, inst.size(), srcp); // fld srcp
- a.fchs(); // fchs
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fabs - process a FABS opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fabs(Assembler &a, 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(a, inst.size(), srcp); // fld srcp
- a.fabs(); // fabs
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fsqrt - process a FSQRT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fsqrt(Assembler &a, 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(a, inst.size(), srcp); // fld srcp
- a.fsqrt(); // fsqrt
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_frecip - process a FRECIP opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_frecip(Assembler &a, 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
- a.fld1(); // fld1
- emit_fld_p(a, inst.size(), srcp); // fld srcp
- a.fdivp(); // fdivp
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_frsqrt - process a FRSQRT opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_frsqrt(Assembler &a, 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
- a.fld1(); // fld1
- emit_fld_p(a, inst.size(), srcp); // fld srcp
- a.fsqrt(); // fsqrt
- a.fdivp(); // fdivp
- emit_fstp_p(a, inst.size(), dstp); // fstp dstp
-}
-
-
-//-------------------------------------------------
-// op_fcopyi - process a FCOPYI opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_fcopyi(Assembler &a, 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())
- {
- a.mov(eax, MABS(srcp.memory())); // mov eax,[srcp]
- a.mov(MABS(dstp.memory()), eax); // mov [dstp],eax
- }
- else if (srcp.is_int_register())
- {
- a.mov(MABS(dstp.memory()), gpd(srcp.ireg())); // mov [dstp],srcp
- }
- }
-
- // 64-bit case
- else if (inst.size() == 8)
- {
- if (srcp.is_memory())
- {
- a.mov(eax, MABS(srcp.memory(0))); // mov eax,[srcp]
- a.mov(edx, MABS(srcp.memory(4))); // mov edx,[srcp+4]
- }
- else if (srcp.is_int_register())
- {
- a.mov(edx, MABS(m_reghi[srcp.ireg()])); // mov edx,[reghi[srcp]]
- a.mov(eax, gpd(srcp.ireg())); // mov eax,srcp
- }
-
- a.mov(MABS(dstp.memory(0)), eax); // mov [dstp],eax
- a.mov(MABS(dstp.memory(4)), edx); // mov [dstp+4],edx
- }
-}
-
-
-//-------------------------------------------------
-// op_icopyf - process a ICOPYF opcode
-//-------------------------------------------------
-
-void drcbe_x86::op_icopyf(Assembler &a, 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)
- {
- a.mov(eax, MABS(srcp.memory())); // mov eax,[srcp]
-
- if (dstp.is_memory())
- {
- a.mov(MABS(dstp.memory()), eax); // mov [dstp],eax
- }
- else if (dstp.is_int_register())
- {
- a.mov(gpd(dstp.ireg()), eax); // mov dstp,eax
- }
- }
-
- // 64-bit case
- else if (inst.size() == 8)
- {
- a.mov(eax, MABS(srcp.memory(0))); // mov eax,[srcp]
- a.mov(edx, MABS(srcp.memory(4))); // mov edx,[srcp+4]
-
- if (dstp.is_memory())
- {
- a.mov(MABS(dstp.memory(0)), eax); // mov [dstp],eax
- a.mov(MABS(dstp.memory(4)), edx); // mov [dstp+4],edx
- }
- else
- {
- a.mov(MABS(m_reghi[dstp.ireg()]), edx); // mov [reghi[dstp]],edx
- a.mov(gpd(dstp.ireg()), eax); // mov dstp,eax
- }
- }
-}
-
-} // anonymous namespace
-
-
-std::unique_ptr<drcbe_interface> make_drcbe_x86(
- drcuml_state &drcuml,
- device_t &device,
- drc_cache &cache,
- uint32_t flags,
- int modes,
- int addrbits,
- int ignorebits)
-{
- return std::unique_ptr<drcbe_interface>(new drcbe_x86(drcuml, device, cache, flags, modes, addrbits, ignorebits));
-}
-
-} // namespace drc
diff --git a/src/devices/cpu/drcbex86.h b/src/devices/cpu/drcbex86.h
deleted file mode 100644
index 3d873677fed..00000000000
--- a/src/devices/cpu/drcbex86.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
-/***************************************************************************
-
- drcbex86.h
-
- 32-bit x86 back-end for the universal machine language.
-
-***************************************************************************/
-
-#ifndef MAME_CPU_DRCBEX86_H
-#define MAME_CPU_DRCBEX86_H
-
-#pragma once
-
-#include "drcuml.h"
-
-#include <memory>
-
-
-namespace drc {
-
-std::unique_ptr<drcbe_interface> make_drcbe_x86(
- drcuml_state &drcuml,
- device_t &device,
- drc_cache &cache,
- uint32_t flags,
- int modes,
- int addrbits,
- int ignorebits);
-
-} // namespace drc
-
-#endif // MAME_CPU_DRCBEX86_H
diff --git a/src/devices/cpu/drcuml.cpp b/src/devices/cpu/drcuml.cpp
index 41b9c6466a9..0ae6f2c2868 100644
--- a/src/devices/cpu/drcuml.cpp
+++ b/src/devices/cpu/drcuml.cpp
@@ -37,7 +37,6 @@
#include "drcbearm64.h"
#include "drcbec.h"
#include "drcbex64.h"
-#include "drcbex86.h"
#include "emuopts.h"
@@ -61,17 +60,15 @@
//**************************************************************************
// determine the type of the native DRC, falling back to C
-#ifndef NATIVE_DRC
+#if !defined(NATIVE_DRC)
#if !defined(MAME_NOASM) && (defined(__x86_64__) || defined(_M_X64))
#define NATIVE_DRC drcbe_x64
-#elif !defined(MAME_NOASM) && (defined(__i386__) || defined(_M_IX86))
-#define NATIVE_DRC drcbe_x86
#elif !defined(MAME_NOASM) && (defined(__aarch64__) || defined(_M_ARM64))
#define NATIVE_DRC drcbe_arm64
#else
#define NATIVE_DRC drcbe_c
#endif
-#endif
+#endif // !defined(NATIVE_DRC)
#define MAKE_DRCBE_IMPL(name) make_##name
#define MAKE_DRCBE(name) MAKE_DRCBE_IMPL(name)