1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
drcbex64.h
64-bit x64 back-end for the universal machine language.
***************************************************************************/
#pragma once
#ifndef MAME_DEVICES_CPU_DRCBEX64_H
#define MAME_DEVICES_CPU_DRCBEX64_H
#include "drcuml.h"
#include "drcbeut.h"
#include "x86log.h"
#include "asmjit/src/asmjit/asmjit.h"
using namespace asmjit;
using namespace asmjit::x86;
namespace drc {
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class drcbe_x64 : public drcbe_interface
{
typedef uint32_t (*x86_entry_point_func)(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 int const 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_VECTOR_REGISTER, // vector 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(be_parameter const ¶m) : m_type(param.m_type), m_value(param.m_value) { }
be_parameter(uint64_t val) : m_type(PTYPE_IMMEDIATE), m_value(val) { }
be_parameter(drcbe_x64 &drcbe, const uml::parameter ¶m, uint32_t allowed);
// creators for types that don't safely default
static inline be_parameter make_ireg(int regnum) { assert(regnum >= 0 && regnum < REG_MAX); return be_parameter(PTYPE_INT_REGISTER, regnum); }
static inline be_parameter make_freg(int regnum) { assert(regnum >= 0 && regnum < REG_MAX); return be_parameter(PTYPE_FLOAT_REGISTER, regnum); }
static inline be_parameter make_memory(void *base) { return be_parameter(PTYPE_MEMORY, reinterpret_cast<be_parameter_value>(base)); }
static inline 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
Gp select_register(Gp defreg) const;
Xmm select_register(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
Mem MABS(const void *ptr, const uint32_t size = 0) const { return Mem(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;
Gp get_base_register_and_offset(Assembler &a, void *target, Gp const ®, int32_t &offset);
void smart_call_r64(Assembler &a, x86code *target, Gp const ®);
void smart_call_m64(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(Assembler &a, const uml::instruction &inst);
void op_hash(Assembler &a, const uml::instruction &inst);
void op_label(Assembler &a, const uml::instruction &inst);
void op_comment(Assembler &a, const uml::instruction &inst);
void op_mapvar(Assembler &a, const uml::instruction &inst);
void op_nop(Assembler &a, const uml::instruction &inst);
void op_debug(Assembler &a, const uml::instruction &inst);
void op_exit(Assembler &a, const uml::instruction &inst);
void op_hashjmp(Assembler &a, const uml::instruction &inst);
void op_jmp(Assembler &a, const uml::instruction &inst);
void op_exh(Assembler &a, const uml::instruction &inst);
void op_callh(Assembler &a, const uml::instruction &inst);
void op_ret(Assembler &a, const uml::instruction &inst);
void op_callc(Assembler &a, const uml::instruction &inst);
void op_recover(Assembler &a, const uml::instruction &inst);
void op_setfmod(Assembler &a, const uml::instruction &inst);
void op_getfmod(Assembler &a, const uml::instruction &inst);
void op_getexp(Assembler &a, const uml::instruction &inst);
void op_getflgs(Assembler &a, const uml::instruction &inst);
void op_save(Assembler &a, const uml::instruction &inst);
void op_restore(Assembler &a, const uml::instruction &inst);
void op_load(Assembler &a, const uml::instruction &inst);
void op_loads(Assembler &a, const uml::instruction &inst);
void op_store(Assembler &a, const uml::instruction &inst);
void op_read(Assembler &a, const uml::instruction &inst);
void op_readm(Assembler &a, const uml::instruction &inst);
void op_write(Assembler &a, const uml::instruction &inst);
void op_writem(Assembler &a, const uml::instruction &inst);
void op_carry(Assembler &a, const uml::instruction &inst);
void op_set(Assembler &a, const uml::instruction &inst);
void op_mov(Assembler &a, const uml::instruction &inst);
void op_sext(Assembler &a, const uml::instruction &inst);
void op_roland(Assembler &a, const uml::instruction &inst);
void op_rolins(Assembler &a, const uml::instruction &inst);
void op_add(Assembler &a, const uml::instruction &inst);
void op_addc(Assembler &a, const uml::instruction &inst);
void op_sub(Assembler &a, const uml::instruction &inst);
void op_subc(Assembler &a, const uml::instruction &inst);
void op_cmp(Assembler &a, const uml::instruction &inst);
void op_mulu(Assembler &a, const uml::instruction &inst);
void op_muls(Assembler &a, const uml::instruction &inst);
void op_divu(Assembler &a, const uml::instruction &inst);
void op_divs(Assembler &a, const uml::instruction &inst);
void op_and(Assembler &a, const uml::instruction &inst);
void op_test(Assembler &a, const uml::instruction &inst);
void op_or(Assembler &a, const uml::instruction &inst);
void op_xor(Assembler &a, const uml::instruction &inst);
void op_lzcnt(Assembler &a, const uml::instruction &inst);
void op_tzcnt(Assembler &a, const uml::instruction &inst);
void op_bswap(Assembler &a, const uml::instruction &inst);
template <Inst::Id Opcode> void op_shift(Assembler &a, const uml::instruction &inst);
void op_fload(Assembler &a, const uml::instruction &inst);
void op_fstore(Assembler &a, const uml::instruction &inst);
void op_fread(Assembler &a, const uml::instruction &inst);
void op_fwrite(Assembler &a, const uml::instruction &inst);
void op_fmov(Assembler &a, const uml::instruction &inst);
void op_ftoint(Assembler &a, const uml::instruction &inst);
void op_ffrint(Assembler &a, const uml::instruction &inst);
void op_ffrflt(Assembler &a, const uml::instruction &inst);
void op_frnds(Assembler &a, const uml::instruction &inst);
void op_fadd(Assembler &a, const uml::instruction &inst);
void op_fsub(Assembler &a, const uml::instruction &inst);
void op_fcmp(Assembler &a, const uml::instruction &inst);
void op_fmul(Assembler &a, const uml::instruction &inst);
void op_fdiv(Assembler &a, const uml::instruction &inst);
void op_fneg(Assembler &a, const uml::instruction &inst);
void op_fabs(Assembler &a, const uml::instruction &inst);
void op_fsqrt(Assembler &a, const uml::instruction &inst);
void op_frecip(Assembler &a, const uml::instruction &inst);
void op_frsqrt(Assembler &a, const uml::instruction &inst);
void op_fcopyi(Assembler &a, const uml::instruction &inst);
void op_icopyf(Assembler &a, const uml::instruction &inst);
// 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(Assembler &a, Inst::Id const opcode, Operand const &dst, be_parameter const ¶m, std::function<bool(Assembler &a, Operand const &dst, be_parameter const &src)> optimize = [](Assembler &a, Operand dst, be_parameter const &src) { return false; });
void shift_op_param(Assembler &a, Inst::Id const opcode, Operand const &dst, be_parameter const ¶m);
// parameter helpers
void mov_reg_param(Assembler &a, Gp const ®, be_parameter const ¶m, bool const keepflags = false);
void mov_param_reg(Assembler &a, be_parameter const ¶m, Gp const ®);
void mov_mem_param(Assembler &a, Mem const &memref, be_parameter const ¶m);
// special-case move helpers
void movsx_r64_p32(Assembler &a, Gp const ®, be_parameter const ¶m);
void mov_r64_imm(Assembler &a, Gp const ®, uint64_t const imm);
// floating-point helpers
void movss_r128_p32(Assembler &a, Xmm const ®, be_parameter const ¶m);
void movss_p32_r128(Assembler &a, be_parameter const ¶m, Xmm const ®);
void movsd_r128_p64(Assembler &a, Xmm const ®, be_parameter const ¶m);
void movsd_p64_r128(Assembler &a, be_parameter const ¶m, Xmm const ®);
size_t emit(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_cpu_instruction_hook;// debugger callback
x86code * debug_log_hashjmp; // hashjmp debugging
x86code * debug_log_hashjmp_fail; // hashjmp debugging
x86code * drcmap_get_value; // map lookup helper
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 is single-precision
double double1; // 1.0 in double-precision
void * stacksave; // saved stack pointer
void * hashstacksave; // saved stack pointer for hashjmp
uint8_t flagsmap[0x1000]; // flags map
uint64_t flagsunmap[0x20]; // flags unmapper
};
near_state & m_near;
// globals
typedef void (drcbe_x64::*opcode_generate_func)(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];
};
} // namespace drc
using drc::drcbe_x64;
#endif /* MAME_DEVICES_CPU_DRCBEX64_H */
|