summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/drcbex64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/drcbex64.cpp')
-rw-r--r--src/devices/cpu/drcbex64.cpp296
1 files changed, 293 insertions, 3 deletions
diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp
index 1d2033f2977..d6dab04f58d 100644
--- a/src/devices/cpu/drcbex64.cpp
+++ b/src/devices/cpu/drcbex64.cpp
@@ -190,24 +190,30 @@
#include "emu.h"
#include "drcbex64.h"
+#include "drcbeut.h"
+#include "x86log.h"
+
#include "debug/debugcpu.h"
#include "emuopts.h"
+#include "asmjit/src/asmjit/asmjit.h"
+
#include <cstddef>
+#include <vector>
// This is a trick to make it build on Android where the ARM SDK declares ::REG_Rn
// and the x64 SDK declares ::REG_Exx and ::REG_Rxx
namespace drc {
+namespace {
+
using namespace uml;
using namespace asmjit;
using namespace asmjit::x86;
-namespace {
-
//**************************************************************************
// DEBUGGING
//**************************************************************************
@@ -343,7 +349,276 @@ inline bool is_nonvolatile_register(Gp reg)
#endif
}
-} // anonymous namespace
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class drcbe_x64 : public drcbe_interface
+{
+ using x86_entry_point_func = uint32_t (*)(uint8_t *rbpvalue, x86code *entry);
+
+public:
+ // construction/destruction
+ drcbe_x64(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits);
+ virtual ~drcbe_x64();
+
+ // 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) override;
+ virtual void get_info(drcbe_info &info) override;
+ virtual bool logging() const override { return m_log != nullptr; }
+
+private:
+ // a be_parameter is similar to a uml::parameter but maps to native registers/memory
+ class be_parameter
+ {
+ public:
+ // HACK: leftover from x86emit
+ static inline constexpr int REG_MAX = 16;
+
+ // 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_x64 &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() const { assert(m_type == PTYPE_MEMORY); return reinterpret_cast<void *>(m_value); }
+
+ // 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
+ asmjit::x86::Gp select_register(asmjit::x86::Gp defreg) const;
+ asmjit::x86::Xmm select_register(asmjit::x86::Xmm defreg) const;
+ asmjit::x86::Gp select_register(asmjit::x86::Gp defreg, be_parameter const &checkparam) const;
+ asmjit::x86::Gp select_register(asmjit::x86::Gp defreg, be_parameter const &checkparam, be_parameter const &checkparam2) const;
+ asmjit::x86::Xmm select_register(asmjit::x86::Xmm defreg, be_parameter const &checkparam) 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
+ asmjit::x86::Mem MABS(const void *ptr, const uint32_t size = 0) const { return asmjit::x86::Mem(asmjit::x86::rbp, offset_from_rbp(ptr), size); }
+ bool short_immediate(int64_t immediate) const { return (int32_t)immediate == immediate; }
+ void normalize_commutative(be_parameter &inner, be_parameter &outer);
+ int32_t offset_from_rbp(const void *ptr) const;
+ asmjit::x86::Gp get_base_register_and_offset(asmjit::x86::Assembler &a, void *target, asmjit::x86::Gp const &reg, int32_t &offset);
+ void smart_call_r64(asmjit::x86::Assembler &a, x86code *target, asmjit::x86::Gp const &reg);
+ void smart_call_m64(asmjit::x86::Assembler &a, x86code **target);
+
+ static void debug_log_hashjmp(offs_t pc, int mode);
+ static void debug_log_hashjmp_fail();
+
+ // code generators
+ void op_handle(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_hash(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_label(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_comment(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_mapvar(asmjit::x86::Assembler &a, const uml::instruction &inst);
+
+ void op_nop(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_break(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_debug(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_exit(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_hashjmp(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_jmp(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_exh(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_callh(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_ret(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_callc(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_recover(asmjit::x86::Assembler &a, const uml::instruction &inst);
+
+ void op_setfmod(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_getfmod(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_getexp(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_getflgs(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_setflgs(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_save(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_restore(asmjit::x86::Assembler &a, const uml::instruction &inst);
+
+ void op_load(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_loads(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_store(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_read(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_readm(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_write(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_writem(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_carry(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_set(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_mov(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_sext(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_roland(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_rolins(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_add(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_addc(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_sub(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_subc(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_cmp(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_mulu(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_mululw(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_muls(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_mulslw(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_divu(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_divs(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_and(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_test(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_or(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_xor(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_lzcnt(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_tzcnt(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_bswap(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ template <asmjit::x86::Inst::Id Opcode> void op_shift(asmjit::x86::Assembler &a, const uml::instruction &inst);
+
+ void op_fload(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fstore(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fread(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fwrite(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fmov(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_ftoint(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_ffrint(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_ffrflt(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_frnds(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fadd(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fsub(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fcmp(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fmul(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fdiv(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fneg(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fabs(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fsqrt(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_frecip(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_frsqrt(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_fcopyi(asmjit::x86::Assembler &a, const uml::instruction &inst);
+ void op_icopyf(asmjit::x86::Assembler &a, const uml::instruction &inst);
+
+ // alu and shift operation helpers
+ static bool ones(u64 const value, unsigned const size) noexcept { return (size == 4) ? u32(value) == 0xffffffffU : value == 0xffffffff'ffffffffULL; }
+ void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode, asmjit::Operand const &dst, be_parameter const &param, std::function<bool(asmjit::x86::Assembler &a, asmjit::Operand const &dst, be_parameter const &src)> optimize = [](asmjit::x86::Assembler &a, asmjit::Operand dst, be_parameter const &src) { return false; });
+ void shift_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode, size_t opsize, asmjit::Operand const &dst, be_parameter const &param, bool update_flags);
+
+ // parameter helpers
+ void mov_reg_param(asmjit::x86::Assembler &a, asmjit::x86::Gp const &reg, be_parameter const &param, bool const keepflags = false);
+ void mov_param_reg(asmjit::x86::Assembler &a, be_parameter const &param, asmjit::x86::Gp const &reg);
+ void mov_mem_param(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref, be_parameter const &param);
+
+ // special-case move helpers
+ void movsx_r64_p32(asmjit::x86::Assembler &a, asmjit::x86::Gp const &reg, be_parameter const &param);
+ void mov_r64_imm(asmjit::x86::Assembler &a, asmjit::x86::Gp const &reg, uint64_t const imm);
+
+ // floating-point helpers
+ void movss_r128_p32(asmjit::x86::Assembler &a, asmjit::x86::Xmm const &reg, be_parameter const &param);
+ void movss_p32_r128(asmjit::x86::Assembler &a, be_parameter const &param, asmjit::x86::Xmm const &reg);
+ void movsd_r128_p64(asmjit::x86::Assembler &a, asmjit::x86::Xmm const &reg, be_parameter const &param);
+ void movsd_p64_r128(asmjit::x86::Assembler &a, be_parameter const &param, asmjit::x86::Xmm const &reg);
+
+ void calculate_status_flags(asmjit::x86::Assembler &a, uint32_t instsize, asmjit::Operand const &dst, u8 flags);
+ void calculate_status_flags_mul(asmjit::x86::Assembler &a, uint32_t instsize, asmjit::x86::Gp const &lo, asmjit::x86::Gp const &hi);
+ void calculate_status_flags_mul_low(asmjit::x86::Assembler &a, uint32_t instsize, asmjit::x86::Gp const &lo);
+
+ size_t emit(asmjit::CodeHolder &ch);
+
+ // internal state
+ drc_hash_table m_hash; // hash table state
+ drc_map_variables m_map; // code map
+ x86log_context * m_log; // logging
+ FILE * m_log_asmjit;
+
+ uint32_t * m_absmask32; // absolute value mask (32-bit)
+ uint64_t * m_absmask64; // absolute value mask (32-bit)
+ uint8_t * m_rbpvalue; // value of RBP
+
+ x86_entry_point_func m_entry; // entry point
+ x86code * m_exit; // exit point
+ x86code * m_nocode; // nocode handler
+
+ // state to live in the near cache
+ struct near_state
+ {
+ x86code * debug_log_hashjmp; // hashjmp debugging
+ x86code * debug_log_hashjmp_fail; // hashjmp debugging
+
+ uint32_t ssemode; // saved SSE mode
+ uint32_t ssemodesave; // temporary location for saving
+ uint32_t ssecontrol[4]; // copy of the sse_control array
+ float single1; // 1.0 in single-precision
+ double double1; // 1.0 in double-precision
+
+ void * stacksave; // saved stack pointer
+
+ uint8_t flagsmap[0x1000]; // flags map
+ uint64_t flagsunmap[0x20]; // flags unmapper
+ };
+ near_state & m_near;
+
+ // resolved memory handler functions
+ struct memory_accessors
+ {
+ resolved_memory_accessors resolved;
+ address_space::specific_access_info specific;
+ offs_t address_mask;
+ bool no_mask;
+ bool has_high_bits;
+ bool mask_high_bits;
+ };
+ resolved_member_function m_debug_cpu_instruction_hook;
+ resolved_member_function m_drcmap_get_value;
+ std::vector<memory_accessors> m_memory_accessors;
+
+ // globals
+ using opcode_generate_func = void (drcbe_x64::*)(asmjit::x86::Assembler &, const uml::instruction &);
+ struct opcode_table_entry
+ {
+ uml::opcode_t opcode; // opcode in question
+ opcode_generate_func func; // function pointer to the work
+ };
+ static const opcode_table_entry s_opcode_table_source[];
+ static opcode_generate_func s_opcode_table[uml::OP_MAX];
+};
@@ -5800,4 +6075,19 @@ void drcbe_x64::op_icopyf(Assembler &a, const instruction &inst)
}
}
+} // anonymous namespace
+
+
+std::unique_ptr<drcbe_interface> make_drcbe_x64(
+ 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_x64(drcuml, device, cache, flags, modes, addrbits, ignorebits));
+}
+
} // namespace drc