diff options
author | 2025-02-22 06:07:07 +1100 | |
---|---|---|
committer | 2025-02-22 06:07:07 +1100 | |
commit | e0b33abadbe6ff1e1ec973f62704c34609d0b4d9 (patch) | |
tree | 7505fba659892fb307a45aa98117d2d9117212a2 | |
parent | 9a58e39d6e8a4aba4e1dbad28d4e4179c9a896d3 (diff) |
cpu: Moved DRC backend declarations out of headers and into anonymous namespaces.
-rw-r--r-- | src/devices/cpu/drcbearm64.cpp | 278 | ||||
-rw-r--r-- | src/devices/cpu/drcbearm64.h | 247 | ||||
-rw-r--r-- | src/devices/cpu/drcbec.cpp | 317 | ||||
-rw-r--r-- | src/devices/cpu/drcbec.h | 51 | ||||
-rw-r--r-- | src/devices/cpu/drcbex64.cpp | 296 | ||||
-rw-r--r-- | src/devices/cpu/drcbex64.h | 284 | ||||
-rw-r--r-- | src/devices/cpu/drcbex86.cpp | 676 | ||||
-rw-r--r-- | src/devices/cpu/drcbex86.h | 298 | ||||
-rw-r--r-- | src/devices/cpu/drcuml.cpp | 13 |
9 files changed, 1262 insertions, 1198 deletions
diff --git a/src/devices/cpu/drcbearm64.cpp b/src/devices/cpu/drcbearm64.cpp index a49745e9533..5477343ce57 100644 --- a/src/devices/cpu/drcbearm64.cpp +++ b/src/devices/cpu/drcbearm64.cpp @@ -75,21 +75,27 @@ the location FP points to. #include "emu.h" #include "drcbearm64.h" +#include "drcbeut.h" +#include "uml.h" + #include "debug/debugcpu.h" #include "emuopts.h" -#include "uml.h" + +#include "asmjit/src/asmjit/asmjit.h" +#include "asmjit/src/asmjit/a64.h" #include <cstddef> +#include <vector> namespace drc { +namespace { + using namespace uml; using namespace asmjit; -namespace { - 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; @@ -343,7 +349,237 @@ void get_imm_absolute(a64::Assembler &a, const a64::Gp ®, const uint64_t val) a.mov(reg, val); } -} // anonymous namespace + +class drcbe_arm64 : public drcbe_interface +{ + using arm64_entry_point_func = uint32_t (*)(void *entry); + +public: + drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits); + virtual ~drcbe_arm64(); + + 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 false; } + +private: + class be_parameter + { + static inline constexpr int REG_MAX = 30; + + 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 + }; + + typedef uint64_t be_parameter_value; + + be_parameter() : m_type(PTYPE_NONE), m_value(0) { } + be_parameter(uint64_t val) : m_type(PTYPE_IMMEDIATE), m_value(val) { } + be_parameter(drcbe_arm64 &drcbe, const uml::parameter ¶m, uint32_t allowed); + be_parameter(const be_parameter ¶m) = 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))); } + + bool operator==(const be_parameter &rhs) const { return (m_type == rhs.m_type && m_value == rhs.m_value); } + bool operator!=(const be_parameter &rhs) const { return (m_type != rhs.m_type || m_value != rhs.m_value); } + + 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); } + + 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); } + + bool is_immediate_value(uint64_t value) const { return (m_type == PTYPE_IMMEDIATE && m_value == value); } + + asmjit::a64::Vec get_register_float(uint32_t regsize) const; + asmjit::a64::Gp get_register_int(uint32_t regsize) const; + asmjit::a64::Vec select_register(asmjit::a64::Vec const ®, uint32_t regsize) const; + asmjit::a64::Gp select_register(asmjit::a64::Gp const ®, uint32_t regsize) const; + + private: + be_parameter(be_parameter_type type, be_parameter_value value) : m_type(type), m_value(value) { } + + be_parameter_type m_type; + be_parameter_value m_value; + }; + + void op_handle(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_hash(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_label(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_comment(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_mapvar(asmjit::a64::Assembler &a, const uml::instruction &inst); + + void op_nop(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_break(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_debug(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_exit(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_hashjmp(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_jmp(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_exh(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_callh(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_ret(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_callc(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_recover(asmjit::a64::Assembler &a, const uml::instruction &inst); + + void op_setfmod(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_getfmod(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_getexp(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_getflgs(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_setflgs(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_save(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_restore(asmjit::a64::Assembler &a, const uml::instruction &inst); + + void op_load(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_loads(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_store(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_read(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_readm(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_write(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_writem(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_carry(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_set(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_mov(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_sext(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_roland(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_rolins(asmjit::a64::Assembler &a, const uml::instruction &inst); + template <asmjit::a64::Inst::Id Opcode> void op_add(asmjit::a64::Assembler &a, const uml::instruction &inst); + template <asmjit::a64::Inst::Id Opcode> void op_sub(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_cmp(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_mulu(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_mululw(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_muls(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_mulslw(asmjit::a64::Assembler &a, const uml::instruction &inst); + template <asmjit::a64::Inst::Id Opcode> void op_div(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_and(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_test(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_or(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_xor(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_lzcnt(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_tzcnt(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_bswap(asmjit::a64::Assembler &a, const uml::instruction &inst); + template <asmjit::a64::Inst::Id Opcode> void op_shift(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_rol(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_rolc(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_rorc(asmjit::a64::Assembler &a, const uml::instruction &inst); + + void op_fload(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_fstore(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_fread(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_fwrite(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_fmov(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_ftoint(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_ffrint(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_ffrflt(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_frnds(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_fcmp(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_fcopyi(asmjit::a64::Assembler &a, const uml::instruction &inst); + void op_icopyf(asmjit::a64::Assembler &a, const uml::instruction &inst); + + template <asmjit::a64::Inst::Id Opcode> void op_float_alu(asmjit::a64::Assembler &a, const uml::instruction &inst); + template <asmjit::a64::Inst::Id Opcode> void op_float_alu2(asmjit::a64::Assembler &a, const uml::instruction &inst); + + size_t emit(asmjit::CodeHolder &ch); + + + // helper functions + void get_imm_relative(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const uint64_t ptr) const; + + void emit_ldr_str_base_mem(asmjit::a64::Assembler &a, asmjit::a64::Inst::Id opcode, const asmjit::a64::Reg ®, int max_shift, const void *ptr) const; + void emit_ldr_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_ldrb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_ldrh_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_ldrsb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_ldrsh_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_ldrsw_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_str_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_strb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + void emit_strh_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; + + void emit_float_ldr_mem(asmjit::a64::Assembler &a, const asmjit::a64::Vec ®, const void *ptr) const; + void emit_float_str_mem(asmjit::a64::Assembler &a, const asmjit::a64::Vec ®, const void *ptr) const; + + void get_carry(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, bool inverted = false) const; + void load_carry(asmjit::a64::Assembler &a, bool inverted = false) const; + void store_carry(asmjit::a64::Assembler &a, bool inverted = false) const; + void store_carry_reg(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®) const; + + void store_unordered(asmjit::a64::Assembler &a) const; + void get_unordered(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®) const; + void check_unordered_condition(asmjit::a64::Assembler &a, uml::condition_t cond, asmjit::Label condition_met, bool not_equal) const; + + void calculate_carry_shift_left(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const asmjit::a64::Gp &shift, int maxBits) const; + void calculate_carry_shift_left_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const int shift, int maxBits) const; + + void calculate_carry_shift_right(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const asmjit::a64::Gp &shift) const; + void calculate_carry_shift_right_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const int shift) const; + + void mov_float_reg_param(asmjit::a64::Assembler &a, uint32_t regsize, asmjit::a64::Vec const &dst, const be_parameter &src) const; + void mov_float_param_param(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const be_parameter &src) const; + void mov_float_param_reg(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, asmjit::a64::Vec const &src) const; + void mov_float_param_int_reg(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, asmjit::a64::Gp const &src) const; + + void mov_reg_param(asmjit::a64::Assembler &a, uint32_t regsize, const asmjit::a64::Gp &dst, const be_parameter &src) const; + void mov_param_reg(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const asmjit::a64::Gp &src) const; + void mov_param_imm(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, uint64_t src) const; + void mov_param_param(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const be_parameter &src) const; + void mov_mem_param(asmjit::a64::Assembler &a, uint32_t regsize, void *dst, const be_parameter &src) const; + void mov_r64_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &dst, uint64_t const src) const; + + void call_arm_addr(asmjit::a64::Assembler &a, const void *offs) const; + + drc_hash_table m_hash; + drc_map_variables m_map; + FILE * m_log_asmjit; + + arm64_entry_point_func m_entry; + drccodeptr m_exit; + drccodeptr m_nocode; + + uint8_t *m_baseptr; + + struct near_state + { + uint32_t emulated_flags; + }; + near_state &m_near; + + using opcode_generate_func = void (drcbe_arm64::*)(asmjit::a64::Assembler &, const uml::instruction &); + struct opcode_table_entry + { + uml::opcode_t opcode; + opcode_generate_func func; + }; + static const opcode_table_entry s_opcode_table_source[]; + static opcode_generate_func s_opcode_table[uml::OP_MAX]; + + struct memory_accessors + { + resolved_memory_accessors resolved; + address_space::specific_access_info specific; + }; + resolved_member_function m_debug_cpu_instruction_hook; + resolved_member_function m_drcmap_get_value; + std::vector<memory_accessors> m_memory_accessors; +}; drcbe_arm64::opcode_generate_func drcbe_arm64::s_opcode_table[OP_MAX]; @@ -763,25 +999,6 @@ void drcbe_arm64::mov_param_imm(a64::Assembler &a, uint32_t regsize, const be_pa } } -void drcbe_arm64::mov_signed_reg64_param32(a64::Assembler &a, const a64::Gp &dst, const be_parameter &src) const -{ - if (src.is_memory()) - { - emit_ldrsw_mem(a, dst.x(), src.memory()); - } - else if (src.is_immediate()) - { - get_imm_relative(a, dst.x(), src.immediate()); - - if (!src.is_immediate_value(0)) - a.sxtw(dst.x(), dst.w()); - } - else if (src.is_int_register()) - { - a.sxtw(dst.x(), src.get_register_int(4)); - } -} - void drcbe_arm64::mov_float_reg_param(a64::Assembler &a, uint32_t regsize, a64::Vec const &dst, const be_parameter &src) const { assert(!src.is_immediate()); @@ -4126,4 +4343,19 @@ void drcbe_arm64::op_icopyf(a64::Assembler &a, const uml::instruction &inst) mov_param_reg(a, inst.size(), dstp, dstreg); } +} // anonymous namespace + + +std::unique_ptr<drcbe_interface> make_drcbe_arm64( + 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_arm64(drcuml, device, cache, flags, modes, addrbits, ignorebits)); } + +} // namespace drc diff --git a/src/devices/cpu/drcbearm64.h b/src/devices/cpu/drcbearm64.h index 0e54234bc42..2006b4a14ab 100644 --- a/src/devices/cpu/drcbearm64.h +++ b/src/devices/cpu/drcbearm64.h @@ -6,250 +6,21 @@ #pragma once #include "drcuml.h" -#include "drcbeut.h" -#include "asmjit/src/asmjit/asmjit.h" -#include "asmjit/src/asmjit/a64.h" - -#include <vector> +#include <memory> namespace drc { -class drcbe_arm64 : public drcbe_interface -{ - using arm64_entry_point_func = uint32_t (*)(void *entry); - -public: - drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits); - virtual ~drcbe_arm64(); - - 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 false; } - -private: - class be_parameter - { - static inline constexpr int REG_MAX = 30; - - 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 - }; - - typedef uint64_t be_parameter_value; - - be_parameter() : m_type(PTYPE_NONE), m_value(0) { } - be_parameter(uint64_t val) : m_type(PTYPE_IMMEDIATE), m_value(val) { } - be_parameter(drcbe_arm64 &drcbe, const uml::parameter ¶m, uint32_t allowed); - be_parameter(const be_parameter ¶m) = 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))); } - - bool operator==(const be_parameter &rhs) const { return (m_type == rhs.m_type && m_value == rhs.m_value); } - bool operator!=(const be_parameter &rhs) const { return (m_type != rhs.m_type || m_value != rhs.m_value); } - - 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); } - - 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); } - - bool is_immediate_value(uint64_t value) const { return (m_type == PTYPE_IMMEDIATE && m_value == value); } - - asmjit::a64::Vec get_register_float(uint32_t regsize) const; - asmjit::a64::Gp get_register_int(uint32_t regsize) const; - asmjit::a64::Vec select_register(asmjit::a64::Vec const ®, uint32_t regsize) const; - asmjit::a64::Gp select_register(asmjit::a64::Gp const ®, uint32_t regsize) const; - - private: - be_parameter(be_parameter_type type, be_parameter_value value) : m_type(type), m_value(value) { } - - be_parameter_type m_type; - be_parameter_value m_value; - }; - - void op_handle(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_hash(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_label(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_comment(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_mapvar(asmjit::a64::Assembler &a, const uml::instruction &inst); - - void op_nop(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_break(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_debug(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_exit(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_hashjmp(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_jmp(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_exh(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_callh(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_ret(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_callc(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_recover(asmjit::a64::Assembler &a, const uml::instruction &inst); - - void op_setfmod(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_getfmod(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_getexp(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_getflgs(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_setflgs(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_save(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_restore(asmjit::a64::Assembler &a, const uml::instruction &inst); - - void op_load(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_loads(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_store(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_read(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_readm(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_write(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_writem(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_carry(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_set(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_mov(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_sext(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_roland(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_rolins(asmjit::a64::Assembler &a, const uml::instruction &inst); - template <asmjit::a64::Inst::Id Opcode> void op_add(asmjit::a64::Assembler &a, const uml::instruction &inst); - template <asmjit::a64::Inst::Id Opcode> void op_sub(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_cmp(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_mulu(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_mululw(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_muls(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_mulslw(asmjit::a64::Assembler &a, const uml::instruction &inst); - template <asmjit::a64::Inst::Id Opcode> void op_div(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_and(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_test(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_or(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_xor(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_lzcnt(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_tzcnt(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_bswap(asmjit::a64::Assembler &a, const uml::instruction &inst); - template <asmjit::a64::Inst::Id Opcode> void op_shift(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_rol(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_rolc(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_rorc(asmjit::a64::Assembler &a, const uml::instruction &inst); - - void op_fload(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_fstore(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_fread(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_fwrite(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_fmov(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_ftoint(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_ffrint(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_ffrflt(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_frnds(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_fcmp(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_fcopyi(asmjit::a64::Assembler &a, const uml::instruction &inst); - void op_icopyf(asmjit::a64::Assembler &a, const uml::instruction &inst); - - template <asmjit::a64::Inst::Id Opcode> void op_float_alu(asmjit::a64::Assembler &a, const uml::instruction &inst); - template <asmjit::a64::Inst::Id Opcode> void op_float_alu2(asmjit::a64::Assembler &a, const uml::instruction &inst); - - size_t emit(asmjit::CodeHolder &ch); - - - // helper functions - void get_imm_relative(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const uint64_t ptr) const; - - void emit_ldr_str_base_mem(asmjit::a64::Assembler &a, asmjit::a64::Inst::Id opcode, const asmjit::a64::Reg ®, int max_shift, const void *ptr) const; - void emit_ldr_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_ldrb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_ldrh_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_ldrsb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_ldrsh_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_ldrsw_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_str_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_strb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - void emit_strh_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const void *ptr) const; - - void emit_float_ldr_mem(asmjit::a64::Assembler &a, const asmjit::a64::Vec ®, const void *ptr) const; - void emit_float_str_mem(asmjit::a64::Assembler &a, const asmjit::a64::Vec ®, const void *ptr) const; - - void get_carry(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, bool inverted = false) const; - void load_carry(asmjit::a64::Assembler &a, bool inverted = false) const; - void store_carry(asmjit::a64::Assembler &a, bool inverted = false) const; - void store_carry_reg(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®) const; - - void store_unordered(asmjit::a64::Assembler &a) const; - void get_unordered(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®) const; - void check_unordered_condition(asmjit::a64::Assembler &a, uml::condition_t cond, asmjit::Label condition_met, bool not_equal) const; - - void calculate_carry_shift_left(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const asmjit::a64::Gp &shift, int maxBits) const; - void calculate_carry_shift_left_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const int shift, int maxBits) const; - - void calculate_carry_shift_right(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const asmjit::a64::Gp &shift) const; - void calculate_carry_shift_right_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp ®, const int shift) const; - - void mov_float_reg_param(asmjit::a64::Assembler &a, uint32_t regsize, asmjit::a64::Vec const &dst, const be_parameter &src) const; - void mov_float_param_param(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const be_parameter &src) const; - void mov_float_param_reg(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, asmjit::a64::Vec const &src) const; - void mov_float_param_int_reg(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, asmjit::a64::Gp const &src) const; - - void mov_reg_param(asmjit::a64::Assembler &a, uint32_t regsize, const asmjit::a64::Gp &dst, const be_parameter &src) const; - void mov_param_reg(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const asmjit::a64::Gp &src) const; - void mov_param_imm(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, uint64_t src) const; - void mov_param_param(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const be_parameter &src) const; - void mov_mem_param(asmjit::a64::Assembler &a, uint32_t regsize, void *dst, const be_parameter &src) const; - void mov_signed_reg64_param32(asmjit::a64::Assembler &a, const asmjit::a64::Gp &dst, const be_parameter &src) const; - void mov_r64_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &dst, uint64_t const src) const; - - void call_arm_addr(asmjit::a64::Assembler &a, const void *offs) const; - - drc_hash_table m_hash; - drc_map_variables m_map; - FILE * m_log_asmjit; - - arm64_entry_point_func m_entry; - drccodeptr m_exit; - drccodeptr m_nocode; - - uint8_t *m_baseptr; - - struct near_state - { - uint32_t emulated_flags; - }; - near_state &m_near; - - using opcode_generate_func = void (drcbe_arm64::*)(asmjit::a64::Assembler &, const uml::instruction &); - struct opcode_table_entry - { - uml::opcode_t opcode; - opcode_generate_func func; - }; - static const opcode_table_entry s_opcode_table_source[]; - static opcode_generate_func s_opcode_table[uml::OP_MAX]; - - struct memory_accessors - { - resolved_memory_accessors resolved; - address_space::specific_access_info specific; - }; - resolved_member_function m_debug_cpu_instruction_hook; - resolved_member_function m_drcmap_get_value; - std::vector<memory_accessors> m_memory_accessors; -}; +std::unique_ptr<drcbe_interface> make_drcbe_arm64( + drcuml_state &drcuml, + device_t &device, + drc_cache &cache, + uint32_t flags, + int modes, + int addrbits, + int ignorebits); } // namespace drc -using drc::drcbe_arm64; - #endif // MAME_CPU_DRCBEARM64_H diff --git a/src/devices/cpu/drcbec.cpp b/src/devices/cpu/drcbec.cpp index d2fa0e744ab..f6f1e1a9242 100644 --- a/src/devices/cpu/drcbec.cpp +++ b/src/devices/cpu/drcbec.cpp @@ -9,13 +9,19 @@ ***************************************************************************/ #include "emu.h" -#include "debug/debugcpu.h" #include "drcbec.h" +#include "drcbeut.h" + +#include "debug/debugcpu.h" + #include <cmath> + namespace drc { +namespace { + using namespace uml; @@ -24,22 +30,22 @@ using namespace uml; //************************************************************************** // define a bit to match each possible condition, starting at bit 12 -#define ZBIT (0x1000 << (COND_Z & 15)) -#define NZBIT (0x1000 << (COND_NZ & 15)) -#define SBIT (0x1000 << (COND_S & 15)) -#define NSBIT (0x1000 << (COND_NS & 15)) -#define CBIT (0x1000 << (COND_C & 15)) -#define NCBIT (0x1000 << (COND_NC & 15)) -#define VBIT (0x1000 << (COND_V & 15)) -#define NVBIT (0x1000 << (COND_NV & 15)) -#define UBIT (0x1000 << (COND_U & 15)) -#define NUBIT (0x1000 << (COND_NU & 15)) -#define ABIT (0x1000 << (COND_A & 15)) -#define BEBIT (0x1000 << (COND_BE & 15)) -#define GBIT (0x1000 << (COND_G & 15)) -#define GEBIT (0x1000 << (COND_GE & 15)) -#define LBIT (0x1000 << (COND_L & 15)) -#define LEBIT (0x1000 << (COND_LE & 15)) +constexpr uint32_t ZBIT = 0x1000 << (COND_Z & 15); +constexpr uint32_t NZBIT = 0x1000 << (COND_NZ & 15); +constexpr uint32_t SBIT = 0x1000 << (COND_S & 15); +constexpr uint32_t NSBIT = 0x1000 << (COND_NS & 15); +constexpr uint32_t CBIT = 0x1000 << (COND_C & 15); +constexpr uint32_t NCBIT = 0x1000 << (COND_NC & 15); +constexpr uint32_t VBIT = 0x1000 << (COND_V & 15); +constexpr uint32_t NVBIT = 0x1000 << (COND_NV & 15); +constexpr uint32_t UBIT = 0x1000 << (COND_U & 15); +constexpr uint32_t NUBIT = 0x1000 << (COND_NU & 15); +constexpr uint32_t ABIT = 0x1000 << (COND_A & 15); +constexpr uint32_t BEBIT = 0x1000 << (COND_BE & 15); +constexpr uint32_t GBIT = 0x1000 << (COND_G & 15); +constexpr uint32_t GEBIT = 0x1000 << (COND_GE & 15); +constexpr uint32_t LBIT = 0x1000 << (COND_L & 15); +constexpr uint32_t LEBIT = 0x1000 << (COND_LE & 15); // internal opcodes @@ -207,6 +213,121 @@ enum #define FLAGS64_NZCV_SUBC(r,a,b,c) (FLAGS64_NZ(r) | FLAGS64_C_SUBC(a,b,c) | FLAGS64_V_SUB(r,a,b)) +//------------------------------------------------- +// dmulu - perform a double-wide unsigned multiply +//------------------------------------------------- + +inline 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; + } + + // fetch source values + uint64_t a = src1; + uint64_t b = src2; + if (a == 0 || b == 0) + { + dsthi = dstlo = 0; + return FLAG_Z; + } + + // compute high and low parts first + uint64_t lo = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 0)); + uint64_t hi = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 32)); + + // compute middle parts + uint64_t prevlo = lo; + uint64_t temp = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 0)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + prevlo = lo; + temp = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 32)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + // store the results + dsthi = hi; + dstlo = lo; + return ((hi >> 60) & FLAG_S) | ((hi != 0) << 1); +} + + +//------------------------------------------------- +// dmuls - perform a double-wide signed multiply +//------------------------------------------------- + +inline int dmuls(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_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; + } + + // fetch absolute source values + uint64_t a = src1; if (int64_t(a) < 0) a = -a; + uint64_t b = src2; if (int64_t(b) < 0) b = -b; + if (a == 0 || b == 0) + { + dsthi = dstlo = 0; + return FLAG_Z; + } + + // compute high and low parts first + uint64_t lo = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 0)); + uint64_t hi = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 32)); + + // compute middle parts + uint64_t prevlo = lo; + uint64_t temp = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 0)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + prevlo = lo; + temp = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 32)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + // adjust for signage + if (int64_t(src1 ^ src2) < 0) + { + hi = ~hi + ((lo == 0) ? 1 : 0); + lo = ~lo + 1; + } + + // store the results + dsthi = hi; + dstlo = lo; + return ((hi >> 60) & FLAG_S) | ((hi != (int64_t(lo) >> 63)) << 1); +} + +inline uint32_t tzcount32(uint32_t value) +{ + for (int i = 0; i < 32; i++) + { + if (value & (uint32_t(1) << i)) + return i; + } + return 32; +} + +inline uint64_t tzcount64(uint64_t value) +{ + for (int i = 0; i < 64; i++) + { + if (value & (uint64_t(1) << i)) + return i; + } + return 64; +} + + //************************************************************************** // TYPE DEFINITIONS @@ -236,6 +357,36 @@ union drcbec_instruction }; +class drcbe_c : public drcbe_interface +{ +public: + // construction/destruction + drcbe_c(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits); + virtual ~drcbe_c(); + + // 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; + +private: + // helpers + void output_parameter(drcbec_instruction **dstptr, void **immedptr, int size, const uml::parameter ¶m); + void fixup_label(void *parameter, drccodeptr labelcodeptr); + + // internal state + drc_hash_table m_hash; // hash table state + drc_map_variables m_map; // code map + drc_label_list m_labels; // label list + drc_label_fixup_delegate m_fixup_delegate; // precomputed delegate + + static const uint32_t s_condition_map[32]; + static uint64_t s_immediate_zero; +}; + + //************************************************************************** // GLOBAL VARIABLES @@ -289,12 +440,12 @@ const uint32_t drcbe_c::s_condition_map[] = // drcbe_c - constructor //------------------------------------------------- -drcbe_c::drcbe_c(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, 0xaaaaaaaa55555555), - m_labels(cache), - m_fixup_delegate(&drcbe_c::fixup_label, this) +drcbe_c::drcbe_c(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, 0xaaaaaaaa55555555), + m_labels(cache), + m_fixup_delegate(&drcbe_c::fixup_label, this) { } @@ -2324,119 +2475,19 @@ void drcbe_c::fixup_label(void *parameter, drccodeptr labelcodeptr) dst->inst = (drcbec_instruction *)labelcodeptr; } +} // anonymous namespace -//------------------------------------------------- -// dmulu - perform a double-wide unsigned multiply -//------------------------------------------------- - -int drcbe_c::dmulu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2, bool flags) -{ - // shortcut if we don't care about the high bits or the flags - if (&dstlo == &dsthi && flags == false) - { - dstlo = src1 * src2; - return 0; - } - - // fetch source values - uint64_t a = src1; - uint64_t b = src2; - if (a == 0 || b == 0) - { - dsthi = dstlo = 0; - return FLAG_Z; - } - - // compute high and low parts first - uint64_t lo = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 0); - uint64_t hi = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 32); - - // compute middle parts - uint64_t prevlo = lo; - uint64_t temp = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 0); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - prevlo = lo; - temp = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 32); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - // store the results - dsthi = hi; - dstlo = lo; - return ((hi >> 60) & FLAG_S) | ((hi != 0) << 1); -} - - -//------------------------------------------------- -// dmuls - perform a double-wide signed multiply -//------------------------------------------------- - -int drcbe_c::dmuls(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2, bool flags) -{ - // shortcut if we don't care about the high bits or the flags - if (&dstlo == &dsthi && flags == false) - { - dstlo = src1 * src2; - return 0; - } - - // fetch absolute source values - uint64_t a = src1; if ((int64_t)a < 0) a = -a; - uint64_t b = src2; if ((int64_t)b < 0) b = -b; - if (a == 0 || b == 0) - { - dsthi = dstlo = 0; - return FLAG_Z; - } - - // compute high and low parts first - uint64_t lo = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 0); - uint64_t hi = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 32); - - // compute middle parts - uint64_t prevlo = lo; - uint64_t temp = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 0); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - prevlo = lo; - temp = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 32); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - // adjust for signage - if ((int64_t)(src1 ^ src2) < 0) - { - hi = ~hi + (lo == 0); - lo = ~lo + 1; - } - - // store the results - dsthi = hi; - dstlo = lo; - return ((hi >> 60) & FLAG_S) | ((hi != ((int64_t)lo >> 63)) << 1); -} - -uint32_t drcbe_c::tzcount32(uint32_t value) -{ - for (int i = 0; i < 32; i++) - { - if (value & (1 << i)) - return i; - } - return 32; -} -uint64_t drcbe_c::tzcount64(uint64_t value) +std::unique_ptr<drcbe_interface> make_drcbe_c( + drcuml_state &drcuml, + device_t &device, + drc_cache &cache, + uint32_t flags, + int modes, + int addrbits, + int ignorebits) { - for (int i = 0; i < 64; i++) - { - if (value & ((uint64_t)(1) << i)) - return i; - } - return 64; + return std::unique_ptr<drcbe_interface>(new drcbe_c(drcuml, device, cache, flags, modes, addrbits, ignorebits)); } } // namespace drc diff --git a/src/devices/cpu/drcbec.h b/src/devices/cpu/drcbec.h index 6c572fb65b0..4bfea5a46f4 100644 --- a/src/devices/cpu/drcbec.h +++ b/src/devices/cpu/drcbec.h @@ -14,52 +14,21 @@ #pragma once #include "drcuml.h" -#include "drcbeut.h" +#include <memory> -namespace drc { - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -union drcbec_instruction; - -class drcbe_c : public drcbe_interface -{ -public: - // construction/destruction - drcbe_c(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits); - virtual ~drcbe_c(); - // 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; - -private: - // helpers - void output_parameter(drcbec_instruction **dstptr, void **immedptr, int size, const uml::parameter ¶m); - void fixup_label(void *parameter, drccodeptr labelcodeptr); - int dmulu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2, bool flags); - int dmuls(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2, bool flags); - uint32_t tzcount32(uint32_t value); - uint64_t tzcount64(uint64_t value); - - // internal state - drc_hash_table m_hash; // hash table state - drc_map_variables m_map; // code map - drc_label_list m_labels; // label list - drc_label_fixup_delegate m_fixup_delegate; // precomputed delegate +namespace drc { - static const uint32_t s_condition_map[32]; - static uint64_t s_immediate_zero; -}; +std::unique_ptr<drcbe_interface> make_drcbe_c( + drcuml_state &drcuml, + device_t &device, + drc_cache &cache, + uint32_t flags, + int modes, + int addrbits, + int ignorebits); } // namespace drc -using drc::drcbe_c; - #endif // MAME_CPU_DRCBEC_H 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 ¶m, uint32_t allowed); + be_parameter(const be_parameter ¶m) = 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 ®, int32_t &offset); + void smart_call_r64(asmjit::x86::Assembler &a, x86code *target, asmjit::x86::Gp const ®); + 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 ¶m, 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 ¶m, bool update_flags); + + // parameter helpers + void mov_reg_param(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m, bool const keepflags = false); + void mov_param_reg(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®); + void mov_mem_param(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref, be_parameter const ¶m); + + // special-case move helpers + void movsx_r64_p32(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m); + void mov_r64_imm(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, uint64_t const imm); + + // floating-point helpers + void movss_r128_p32(asmjit::x86::Assembler &a, asmjit::x86::Xmm const ®, be_parameter const ¶m); + void movss_p32_r128(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Xmm const ®); + void movsd_r128_p64(asmjit::x86::Assembler &a, asmjit::x86::Xmm const ®, be_parameter const ¶m); + void movsd_p64_r128(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Xmm const ®); + + 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 diff --git a/src/devices/cpu/drcbex64.h b/src/devices/cpu/drcbex64.h index e3d8ee3f4b3..116b4939730 100644 --- a/src/devices/cpu/drcbex64.h +++ b/src/devices/cpu/drcbex64.h @@ -13,287 +13,21 @@ #pragma once #include "drcuml.h" -#include "drcbeut.h" -#include "x86log.h" -#include "asmjit/src/asmjit/asmjit.h" - -#include <vector> +#include <memory> namespace drc { -//************************************************************************** -// 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 ¶m, uint32_t allowed); - be_parameter(const be_parameter ¶m) = 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 ®, int32_t &offset); - void smart_call_r64(asmjit::x86::Assembler &a, x86code *target, asmjit::x86::Gp const ®); - 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 ¶m, 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 ¶m, bool update_flags); - - // parameter helpers - void mov_reg_param(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m, bool const keepflags = false); - void mov_param_reg(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®); - void mov_mem_param(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref, be_parameter const ¶m); - - // special-case move helpers - void movsx_r64_p32(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m); - void mov_r64_imm(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, uint64_t const imm); - - // floating-point helpers - void movss_r128_p32(asmjit::x86::Assembler &a, asmjit::x86::Xmm const ®, be_parameter const ¶m); - void movss_p32_r128(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Xmm const ®); - void movsd_r128_p64(asmjit::x86::Assembler &a, asmjit::x86::Xmm const ®, be_parameter const ¶m); - void movsd_p64_r128(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Xmm const ®); - - 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]; -}; +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); } // namespace drc -using drc::drcbe_x64; - #endif // MAME_CPU_DRCBEX64_H diff --git a/src/devices/cpu/drcbex86.cpp b/src/devices/cpu/drcbex86.cpp index 0bb540260cb..af3cf56a839 100644 --- a/src/devices/cpu/drcbex86.cpp +++ b/src/devices/cpu/drcbex86.cpp @@ -85,23 +85,27 @@ #include "emu.h" #include "drcbex86.h" +#include "drcbeut.h" +#include "x86log.h" + #include "debug/debugcpu.h" #include "emuopts.h" -#include "drcuml.h" + +#include "asmjit/src/asmjit/asmjit.h" #include <cstddef> namespace drc { +namespace { + using namespace uml; using namespace asmjit; using namespace asmjit::x86; -namespace { - //************************************************************************** // DEBUGGING //************************************************************************** @@ -190,6 +194,202 @@ const uint16_t fp_control[4] = +//************************************************************************** +// 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.isMem() ? eax : dst.as<Gpd>().id() == ebx.id() ? eax : ebx; + Gp tempreg2 = dst.isMem() ? edx : dst.as<Gpd>().id() == ecx.id() ? edx : ecx; + + if (dst.isMem()) + { + a.push(tempreg2); + a.mov(tempreg2, dst.as<Mem>()); + } + + a.push(tempreg); + + a.pushfd(); + a.pop(tempreg); + a.and_(tempreg, ~flagmask); + + a.add(dst.isMem() ? tempreg2.as<Gpd>() : dst.as<Gpd>(), 0); + + a.pushfd(); + a.and_(dword_ptr(esp), flagmask); + a.or_(dword_ptr(esp), tempreg); + a.popfd(); + + a.pop(tempreg); + + if (dst.isMem()) + a.pop(tempreg2); + } + else + { + fatalerror("drcbe_x86::calculate_status_flags: unknown flag combination requested: %02x\n", flags); + } +} + + +//------------------------------------------------- +// dmulu - perform a double-wide unsigned multiply +//------------------------------------------------- + +int dmulu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2, bool flags, bool halfmul_flags) +{ + // shortcut if we don't care about the high bits or the flags + if (&dstlo == &dsthi && !flags) + { + dstlo = src1 * src2; + return 0; + } + + // fetch source values + uint64_t a = src1; + uint64_t b = src2; + if (a == 0 || b == 0) + { + dsthi = dstlo = 0; + return FLAG_Z; + } + + // compute high and low parts first + uint64_t lo = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 0)); + uint64_t hi = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 32)); + + // compute middle parts + uint64_t prevlo = lo; + uint64_t temp = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 0)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + prevlo = lo; + temp = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 32)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + // store the results + dsthi = hi; + dstlo = lo; + + if (halfmul_flags) + return ((lo >> 60) & FLAG_S) | ((hi != 0) << 1) | ((dstlo == 0) << 2); + + return ((hi >> 60) & FLAG_S) | ((hi != 0) << 1) | ((dsthi == 0 && dstlo == 0) << 2); +} + + +//------------------------------------------------- +// dmuls - perform a double-wide signed multiply +//------------------------------------------------- + +int dmuls(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2, bool flags, bool halfmul_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; + } + + // fetch absolute source values + a = src1; if (int64_t(a) < 0) a = -a; + b = src2; if (int64_t(b) < 0) b = -b; + if (a == 0 || b == 0) + { + dsthi = dstlo = 0; + return FLAG_Z; + } + + // compute high and low parts first + lo = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 0)); + hi = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 32)); + + // compute middle parts + prevlo = lo; + temp = uint64_t(uint32_t(a >> 32)) * uint64_t(uint32_t(b >> 0)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + prevlo = lo; + temp = uint64_t(uint32_t(a >> 0)) * uint64_t(uint32_t(b >> 32)); + lo += temp << 32; + hi += (temp >> 32) + (lo < prevlo); + + // adjust for signage + if (int64_t(src1 ^ src2) < 0) + { + hi = ~hi + (lo == 0); + lo = ~lo + 1; + } + + // store the results + dsthi = hi; + dstlo = lo; + + if (halfmul_flags) + return ((lo >> 60) & FLAG_S) | ((hi != (int64_t(lo) >> 63)) << 1) | ((dstlo == 0) << 2); + + return ((hi >> 60) & FLAG_S) | ((hi != (int64_t(lo) >> 63)) << 1) | ((dsthi == 0 && dstlo == 0) << 2); +} + + +//------------------------------------------------- +// 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: @@ -199,7 +399,279 @@ public: } }; -} // anonymous namespace + +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) override; + virtual void get_info(drcbe_info &info) override; + virtual bool logging() const override { return m_log != nullptr; } + +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 ¶m, uint32_t allowed); + be_parameter(const be_parameter ¶m) = 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 + asmjit::x86::Gpd select_register(asmjit::x86::Gpd const &defreg) const; + asmjit::x86::Xmm select_register(asmjit::x86::Xmm 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 + asmjit::x86::Mem MABS(void const *base, u32 const size = 0) const { return asmjit::x86::Mem(u64(base), size); } + void normalize_commutative(be_parameter &inner, be_parameter &outer); + void emit_combine_z_flags(asmjit::x86::Assembler &a); + void emit_combine_zs_flags(asmjit::x86::Assembler &a); + void emit_combine_z_shl_flags(asmjit::x86::Assembler &a); + void reset_last_upper_lower_reg(); + void set_last_lower_reg(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®lo); + void set_last_upper_reg(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®hi); + bool can_skip_lower_load(asmjit::x86::Assembler &a, uint32_t *memref, asmjit::x86::Gp const ®lo); + bool can_skip_upper_load(asmjit::x86::Assembler &a, uint32_t *memref, asmjit::x86::Gp const ®hi); + + static void debug_log_hashjmp(int mode, offs_t pc); + + // 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); + void op_shl(asmjit::x86::Assembler &a, const uml::instruction &inst); + void op_shr(asmjit::x86::Assembler &a, const uml::instruction &inst); + void op_sar(asmjit::x86::Assembler &a, const uml::instruction &inst); + void op_ror(asmjit::x86::Assembler &a, const uml::instruction &inst); + void op_rol(asmjit::x86::Assembler &a, const uml::instruction &inst); + void op_rorc(asmjit::x86::Assembler &a, const uml::instruction &inst); + void op_rolc(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); + + // 32-bit code emission helpers + void emit_mov_r32_p32(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m); + void emit_mov_r32_p32_keepflags(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m); + void emit_mov_m32_p32(asmjit::x86::Assembler &a, asmjit::x86::Mem memref, be_parameter const ¶m); + void emit_mov_p32_r32(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®); + + void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode, asmjit::Operand const &dst, be_parameter const ¶m, 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 ¶m, std::function<bool(asmjit::x86::Assembler &a, asmjit::Operand const &dst, be_parameter const &src)> optimize, bool update_flags); + + // 64-bit code emission helpers + void emit_mov_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m); + void emit_mov_r64_p64_keepflags(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m); + void emit_mov_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref, be_parameter const ¶m); + void emit_mov_p64_r64(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi); + void emit_and_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_and_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref_lo, asmjit::x86::Mem const &memref_hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_or_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_or_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref_lo, asmjit::x86::Mem const &memref_hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_xor_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_xor_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref_lo, asmjit::x86::Mem const &memref_hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_shl_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_shr_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_sar_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_rol_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_ror_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_rcl_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + void emit_rcr_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); + + void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode_lo, asmjit::x86::Inst::Id const opcode_hi, asmjit::x86::Gp const &lo, asmjit::x86::Gp const &hi, be_parameter const ¶m, bool const saveflags); + void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode_lo, asmjit::x86::Inst::Id const opcode_hi, asmjit::x86::Mem const &lo, asmjit::x86::Mem const &hi, be_parameter const ¶m, bool const saveflags); + + // floating-point code emission helpers + void emit_fld_p(asmjit::x86::Assembler &a, int size, be_parameter const ¶m); + void emit_fstp_p(asmjit::x86::Assembler &a, int size, be_parameter const ¶m); + + 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; + bool m_logged_common; // logged common code already? + 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_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 + asmjit::x86::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 + asmjit::x86::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 + + 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; + + // globals + typedef void (drcbe_x86::*opcode_generate_func)(asmjit::x86::Assembler &a, const uml::instruction &inst); + 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]; +}; @@ -1097,52 +1569,6 @@ void drcbe_x86::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const a.emit(opcode, dst, Gpd(param.ireg())); // op dst,param } -void drcbe_x86::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.isMem() ? eax : dst.as<Gpd>().id() == ebx.id() ? eax : ebx; - Gp tempreg2 = dst.isMem() ? edx : dst.as<Gpd>().id() == ecx.id() ? edx : ecx; - - if (dst.isMem()) - { - a.push(tempreg2); - a.mov(tempreg2, dst.as<Mem>()); - } - - a.push(tempreg); - - a.pushfd(); - a.pop(tempreg); - a.and_(tempreg, ~flagmask); - - a.add(dst.isMem() ? tempreg2.as<Gpd>() : dst.as<Gpd>(), 0); - - a.pushfd(); - a.and_(dword_ptr(esp), flagmask); - a.or_(dword_ptr(esp), tempreg); - a.popfd(); - - a.pop(tempreg); - - if (dst.isMem()) - a.pop(tempreg2); - } - else - { - fatalerror("drcbe_x86::calculate_status_flags: unknown flag combination requested: %02x\n", flags); - } -} - void drcbe_x86::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsize, Operand const &dst, be_parameter const ¶m, std::function<bool(Assembler &a, Operand const &dst, be_parameter const &src)> optimize, bool update_flags) { if (param.is_immediate()) @@ -6817,149 +7243,19 @@ void drcbe_x86::op_icopyf(Assembler &a, const instruction &inst) } } +} // anonymous namespace -//************************************************************************** -// MISCELLAENOUS FUNCTIONS -//************************************************************************** - -//------------------------------------------------- -// dmulu - perform a double-wide unsigned multiply -//------------------------------------------------- - -int drcbe_x86::dmulu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2, bool flags, bool halfmul_flags) -{ - // shortcut if we don't care about the high bits or the flags - if (&dstlo == &dsthi && flags == false) - { - dstlo = src1 * src2; - return 0; - } - - // fetch source values - uint64_t a = src1; - uint64_t b = src2; - if (a == 0 || b == 0) - { - dsthi = dstlo = 0; - return FLAG_Z; - } - - // compute high and low parts first - uint64_t lo = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 0); - uint64_t hi = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 32); - - // compute middle parts - uint64_t prevlo = lo; - uint64_t temp = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 0); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - prevlo = lo; - temp = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 32); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - // store the results - dsthi = hi; - dstlo = lo; - - if (halfmul_flags) - return ((lo >> 60) & FLAG_S) | ((hi != 0) << 1) | ((dstlo == 0) << 2); - - return ((hi >> 60) & FLAG_S) | ((hi != 0) << 1) | ((dsthi == 0 && dstlo == 0) << 2); -} - - -//------------------------------------------------- -// dmuls - perform a double-wide signed multiply -//------------------------------------------------- - -int drcbe_x86::dmuls(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2, bool flags, bool halfmul_flags) -{ - uint64_t lo, hi, prevlo; - uint64_t a, b, temp; - - // shortcut if we don't care about the high bits or the flags - if (&dstlo == &dsthi && flags == false) - { - dstlo = src1 * src2; - return 0; - } - - // fetch absolute source values - a = src1; if ((int64_t)a < 0) a = -a; - b = src2; if ((int64_t)b < 0) b = -b; - if (a == 0 || b == 0) - { - dsthi = dstlo = 0; - return FLAG_Z; - } - - // compute high and low parts first - lo = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 0); - hi = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 32); - - // compute middle parts - prevlo = lo; - temp = (uint64_t)(uint32_t)(a >> 32) * (uint64_t)(uint32_t)(b >> 0); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - prevlo = lo; - temp = (uint64_t)(uint32_t)(a >> 0) * (uint64_t)(uint32_t)(b >> 32); - lo += temp << 32; - hi += (temp >> 32) + (lo < prevlo); - - // adjust for signage - if ((int64_t)(src1 ^ src2) < 0) - { - hi = ~hi + (lo == 0); - lo = ~lo + 1; - } - - // store the results - dsthi = hi; - dstlo = lo; - - if (halfmul_flags) - return ((lo >> 60) & FLAG_S) | ((hi != ((int64_t)lo >> 63)) << 1) | ((dstlo == 0) << 2); - - return ((hi >> 60) & FLAG_S) | ((hi != ((int64_t)lo >> 63)) << 1) | ((dsthi == 0 && dstlo == 0) << 2); -} - - -//------------------------------------------------- -// ddivu - perform a double-wide unsigned divide -//------------------------------------------------- - -int drcbe_x86::ddivu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2) -{ - // do nothing if src2 == 0 - if (src2 == 0) - return FLAG_V; - - dstlo = src1 / src2; - if (&dstlo != &dsthi) - dsthi = src1 % src2; - return ((dstlo == 0) << 2) | ((dstlo >> 60) & FLAG_S); -} - - -//------------------------------------------------- -// ddivs - perform a double-wide signed divide -//------------------------------------------------- - -int drcbe_x86::ddivs(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2) +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) { - // 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); + 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 index 25e926eef3c..3d873677fed 100644 --- a/src/devices/cpu/drcbex86.h +++ b/src/devices/cpu/drcbex86.h @@ -14,301 +14,21 @@ #pragma once #include "drcuml.h" -#include "drcbeut.h" -#include "x86log.h" -#include "asmjit/src/asmjit/asmjit.h" +#include <memory> namespace drc { -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -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) override; - virtual void get_info(drcbe_info &info) override; - virtual bool logging() const override { return m_log != nullptr; } - -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 ¶m, uint32_t allowed); - be_parameter(const be_parameter ¶m) = 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 - asmjit::x86::Gpd select_register(asmjit::x86::Gpd const &defreg) const; - asmjit::x86::Xmm select_register(asmjit::x86::Xmm 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 - asmjit::x86::Mem MABS(void const *base, u32 const size = 0) const { return asmjit::x86::Mem(u64(base), size); } - void normalize_commutative(be_parameter &inner, be_parameter &outer); - void emit_combine_z_flags(asmjit::x86::Assembler &a); - void emit_combine_zs_flags(asmjit::x86::Assembler &a); - void emit_combine_z_shl_flags(asmjit::x86::Assembler &a); - void reset_last_upper_lower_reg(); - void set_last_lower_reg(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®lo); - void set_last_upper_reg(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®hi); - bool can_skip_lower_load(asmjit::x86::Assembler &a, uint32_t *memref, asmjit::x86::Gp const ®lo); - bool can_skip_upper_load(asmjit::x86::Assembler &a, uint32_t *memref, asmjit::x86::Gp const ®hi); - - static void debug_log_hashjmp(int mode, offs_t pc); - - // 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); - void op_shl(asmjit::x86::Assembler &a, const uml::instruction &inst); - void op_shr(asmjit::x86::Assembler &a, const uml::instruction &inst); - void op_sar(asmjit::x86::Assembler &a, const uml::instruction &inst); - void op_ror(asmjit::x86::Assembler &a, const uml::instruction &inst); - void op_rol(asmjit::x86::Assembler &a, const uml::instruction &inst); - void op_rorc(asmjit::x86::Assembler &a, const uml::instruction &inst); - void op_rolc(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); - - // 32-bit code emission helpers - void emit_mov_r32_p32(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m); - void emit_mov_r32_p32_keepflags(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®, be_parameter const ¶m); - void emit_mov_m32_p32(asmjit::x86::Assembler &a, asmjit::x86::Mem memref, be_parameter const ¶m); - void emit_mov_p32_r32(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®); - - void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode, asmjit::Operand const &dst, be_parameter const ¶m, 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 ¶m, std::function<bool(asmjit::x86::Assembler &a, asmjit::Operand const &dst, be_parameter const &src)> optimize, bool update_flags); - - // 64-bit code emission helpers - void emit_mov_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m); - void emit_mov_r64_p64_keepflags(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m); - void emit_mov_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref, be_parameter const ¶m); - void emit_mov_p64_r64(asmjit::x86::Assembler &a, be_parameter const ¶m, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi); - void emit_and_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_and_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref_lo, asmjit::x86::Mem const &memref_hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_or_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_or_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref_lo, asmjit::x86::Mem const &memref_hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_xor_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_xor_m64_p64(asmjit::x86::Assembler &a, asmjit::x86::Mem const &memref_lo, asmjit::x86::Mem const &memref_hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_shl_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_shr_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_sar_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_rol_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_ror_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_rcl_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - void emit_rcr_r64_p64(asmjit::x86::Assembler &a, asmjit::x86::Gp const ®lo, asmjit::x86::Gp const ®hi, be_parameter const ¶m, const uml::instruction &inst); - - void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode_lo, asmjit::x86::Inst::Id const opcode_hi, asmjit::x86::Gp const &lo, asmjit::x86::Gp const &hi, be_parameter const ¶m, bool const saveflags); - void alu_op_param(asmjit::x86::Assembler &a, asmjit::x86::Inst::Id const opcode_lo, asmjit::x86::Inst::Id const opcode_hi, asmjit::x86::Mem const &lo, asmjit::x86::Mem const &hi, be_parameter const ¶m, bool const saveflags); - - // floating-point code emission helpers - void emit_fld_p(asmjit::x86::Assembler &a, int size, be_parameter const ¶m); - void emit_fstp_p(asmjit::x86::Assembler &a, int size, be_parameter const ¶m); - - // callback helpers - static int dmulu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2, bool flags, bool halfmul_flags); - static int dmuls(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2, bool flags, bool halfmul_flags); - static int ddivu(uint64_t &dstlo, uint64_t &dsthi, uint64_t src1, uint64_t src2); - static int ddivs(uint64_t &dstlo, uint64_t &dsthi, int64_t src1, int64_t src2); - - void calculate_status_flags(asmjit::x86::Assembler &a, asmjit::Operand const &dst, u8 flags); - - 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; - bool m_logged_common; // logged common code already? - 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_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 - asmjit::x86::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 - asmjit::x86::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 - - 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; - - // globals - typedef void (drcbe_x86::*opcode_generate_func)(asmjit::x86::Assembler &a, const uml::instruction &inst); - 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]; -}; +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 -using drc::drcbe_x86; - #endif // MAME_CPU_DRCBEX86_H diff --git a/src/devices/cpu/drcuml.cpp b/src/devices/cpu/drcuml.cpp index 6d21b262a4a..47de628574e 100644 --- a/src/devices/cpu/drcuml.cpp +++ b/src/devices/cpu/drcuml.cpp @@ -58,15 +58,16 @@ //************************************************************************** -// TYPE DEFINITIONS +// MACROS //************************************************************************** // determine the type of the native DRC, falling back to C #ifndef NATIVE_DRC -typedef drcbe_c drcbe_native; -#else -typedef NATIVE_DRC drcbe_native; +#define NATIVE_DRC drcbe_c #endif +#define MAKE_DRCBE_IMPL(name) make_##name +#define MAKE_DRCBE(name) MAKE_DRCBE_IMPL(name) +#define make_drcbe_native MAKE_DRCBE(NATIVE_DRC) // structure describing back-end validation test @@ -137,8 +138,8 @@ drcuml_state::drcuml_state(device_t &device, drc_cache &cache, u32 flags, int mo : m_device(device) , m_cache(cache) , m_beintf(device.machine().options().drc_use_c() - ? std::unique_ptr<drcbe_interface>{ new drcbe_c(*this, device, cache, flags, modes, addrbits, ignorebits) } - : std::unique_ptr<drcbe_interface>{ new drcbe_native(*this, device, cache, flags, modes, addrbits, ignorebits) }) + ? drc::make_drcbe_c(*this, device, cache, flags, modes, addrbits, ignorebits) + : drc::make_drcbe_native(*this, device, cache, flags, modes, addrbits, ignorebits)) , m_umllog(device.machine().options().drc_log_uml() ? new std::ofstream(util::string_format("drcuml_%s.asm", device.shortname())) : nullptr) |