DRC cleanup and minor optimisation: [Windy Fairy, Vas Crabb]

* Build all native back-ends if any native back-end is enabled so errors
  caused by changing interfaces can be found faster.
* cpu/drcbeut.cpp: Moved resolved member function stuff to a place where
  it can be shared by back-ends.
* cpu/drcbearm64.cpp: Use ubfx instruction to extract unordered flag.
* cpu/drcbearm64.cpp, cpu/drcbex64.cpp: Bypass trampolines when calling
  get map variable value and debugger instruction hook functions.
* cpu/drcbearm64.cpp: Moved some internal helpers that don't need to be
  members to anonymous namespace.
* cpu/drcbearm64.cpp: Added a comment with some info to help when
  debugging generated code.
* cpu/drcbec.cpp: Put code in the drc namespace.
This commit is contained in:
Vas Crabb 2025-01-16 06:24:11 +11:00
parent 8cfc62f24d
commit 26d8e47c00
10 changed files with 470 additions and 405 deletions

View File

@ -1959,13 +1959,6 @@ project "asmjit"
end
end
if (_OPTIONS["PLATFORM"]=="arm64") then
configuration { }
defines {
"ASMJIT_NO_X86",
}
end
files {
MAME_DIR .. "3rdparty/asmjit/src/asmjit/a64.h",
MAME_DIR .. "3rdparty/asmjit/src/asmjit/arm.h",

View File

@ -42,19 +42,14 @@ if (CPU_INCLUDE_DRC) then
MAME_DIR .. "src/devices/cpu/drcumlsh.h",
}
if not _OPTIONS["FORCE_DRC_C_BACKEND"] then
if (_OPTIONS["PLATFORM"]=="arm64") then
files {
MAME_DIR .. "src/devices/cpu/drcbearm64.cpp",
MAME_DIR .. "src/devices/cpu/drcbearm64.h",
}
else
files {
MAME_DIR .. "src/devices/cpu/drcbex64.cpp",
MAME_DIR .. "src/devices/cpu/drcbex64.h",
MAME_DIR .. "src/devices/cpu/drcbex86.cpp",
MAME_DIR .. "src/devices/cpu/drcbex86.h",
}
end
files {
MAME_DIR .. "src/devices/cpu/drcbearm64.cpp",
MAME_DIR .. "src/devices/cpu/drcbearm64.h",
MAME_DIR .. "src/devices/cpu/drcbex64.cpp",
MAME_DIR .. "src/devices/cpu/drcbex64.h",
MAME_DIR .. "src/devices/cpu/drcbex86.cpp",
MAME_DIR .. "src/devices/cpu/drcbex86.h",
}
end
if _OPTIONS["targetos"]=="macosx" and _OPTIONS["gcc"]~=nil then

View File

@ -1,5 +1,76 @@
// license:BSD-3-Clause
// copyright-holders:windyfairy
/***************************************************************************
Register use:
r0 first function parameter/return value
r1 second function parameter
r2 third function parameter
r3 fourth function parameter
r4
r5
r6
r7
r8
r9 temporary for intermediate values
r10 temporary for intermediate values
r11 temporary for intermediate values
r12 scratch register used by helper functions
r13 scratch register used by helper functions
r14 scratch register used for address calculation
r15 temporary used in opcode functions
r16
r17
r18
r19 UML register I0
r20 UML register I1
r21 UML register I2
r22 UML register I3
r23 UML register I4
r24 UML register I5
r35 UML register I6
r26 UML register I7
r27 near cache pointer
r28 emulated flags
r29 base generated code frame pointer
r30 link register
sp stack pointer
Stack layout in top-level generated code frame:
FP -> SP + 0x00 previous FP
SP + 0x08 top-level return address
SP + 0x10 saved non-volatile registers
SP + 0x18 ...
Stack layout in nested generated code subroutine call frame:
SP -> SP + 0x00 previous FP
SP + 0x08 return address
...
FP - 0x10 previous FP
FP - 0x08 return address
FP -> FP + 0x00 previous FP
FP + 0x08 top-level return address
The frame pointer (FP or x29) is only updated by the top-level generated
code entry point. Generated code subroutines (called using CALLH, EXH or
on a failed HASHJMP) push FP and LR onto the stack but do not update FP.
All the saved FP values will be identical.
A native debugger following the FP chain will see any number of nested
generated code subroutine call frames as a single stack frame. The return
addresses and duplicate saved FP values for the generated code subroutine
calls will appear as the local variable area of the frame.
You can calculate the generated code subroutine call depth as
(FP - SP) / 0x10. You can see the return addresses for the generated code
subroutine calls at SP + 0x08, SP + 0x18, SP + 0x28, etc. until reaching
the location FP points to.
***************************************************************************/
#include "emu.h"
#include "drcbearm64.h"
@ -8,8 +79,6 @@
#include "emuopts.h"
#include "uml.h"
#include "mfpresolve.h"
#include <cstddef>
@ -44,12 +113,12 @@ const a64::Gp TEMP_REG3 = a64::x11;
const a64::Gp SCRATCH_REG1 = a64::x12;
const a64::Gp SCRATCH_REG2 = a64::x13;
// Only to be used in an opcode level function. Should not be used in helper functions
const a64::Gp FUNC_SCRATCH_REG = a64::x15;
// Temporary memory calculation register, should not be used outside of functions that calculate memory addresses
const a64::Gp MEM_SCRATCH_REG = a64::x14;
// Only to be used in an opcode level function. Should not be used in helper functions
const a64::Gp FUNC_SCRATCH_REG = a64::x15;
const a64::Vec TEMPF_REG1 = a64::d16;
const a64::Vec TEMPF_REG2 = a64::d17;
const a64::Vec TEMPF_REG3 = a64::d18;
@ -124,6 +193,111 @@ public:
}
};
// helper functions
a64::Vec select_register(a64::Vec const &reg, uint32_t regsize)
{
if (regsize == 4)
return reg.s();
return reg.d();
}
a64::Gp select_register(a64::Gp const &reg, uint32_t regsize)
{
if (regsize == 4)
return reg.w();
return reg.x();
}
bool is_valid_immediate_mask(uint64_t val, size_t bytes)
{
// all zeros and all ones aren't allowed, and disallow any value with bits outside of the max bit range
if (val == 0 || val == make_bitmask<uint64_t>(bytes * 8))
return false;
uint32_t head = 64 - count_leading_zeros_64(val);
if (head >= (bytes * 8))
return false;
uint32_t tail = 0;
while (tail < head)
{
if (BIT(val, tail))
break;
tail++;
}
return population_count_64(val) == head - tail;
}
bool is_valid_immediate(uint64_t val, size_t bits)
{
assert(bits < 64);
return val < (uint64_t(1) << bits);
}
bool is_valid_immediate_signed(int64_t val, size_t bits)
{
return util::sext(val, bits) == val;
}
bool emit_add_optimized(a64::Assembler &a, const a64::Gp &dst, const a64::Gp &src, int64_t val)
{
// If the bottom 12 bits are 0s then an optimized form can be used if the remaining bits are <= 12
if (is_valid_immediate(val, 12) || ((val & 0xfff) == 0 && is_valid_immediate(val >> 12, 12)))
{
a.add(dst, src, val);
return true;
}
return false;
}
bool emit_sub_optimized(a64::Assembler &a, const a64::Gp &dst, const a64::Gp &src, int64_t val)
{
if (val < 0)
val = -val;
// If the bottom 12 bits are 0s then an optimized form can be used if the remaining bits are <= 12
if (is_valid_immediate(val, 12) || ((val & 0xfff) == 0 && is_valid_immediate(val >> 12, 12)))
{
a.sub(dst, src, val);
return true;
}
return false;
}
arm::Mem get_mem_absolute(a64::Assembler &a, const void *ptr)
{
const uint64_t codeoffs = a.code()->baseAddress() + a.offset();
const int64_t reloffs = codeoffs - (int64_t)ptr;
if (is_valid_immediate_signed(reloffs, 21))
{
a.adr(MEM_SCRATCH_REG, ptr);
return arm::Mem(MEM_SCRATCH_REG);
}
const uint64_t pagebase = codeoffs & ~make_bitmask<uint64_t>(12);
const int64_t pagerel = (int64_t)ptr - pagebase;
if (is_valid_immediate_signed(pagerel, 33))
{
const uint64_t targetpage = (uint64_t)ptr & ~make_bitmask<uint64_t>(12);
const uint64_t pageoffs = (uint64_t)ptr & util::make_bitmask<uint64_t>(12);
a.adrp(MEM_SCRATCH_REG, targetpage);
if (is_valid_immediate_signed(pageoffs, 9))
return arm::Mem(MEM_SCRATCH_REG, pageoffs);
else if (emit_add_optimized(a, MEM_SCRATCH_REG, MEM_SCRATCH_REG, pageoffs))
return arm::Mem(MEM_SCRATCH_REG);
}
a.mov(MEM_SCRATCH_REG, ptr);
return arm::Mem(MEM_SCRATCH_REG);
}
} // anonymous namespace
@ -161,67 +335,67 @@ const drcbe_arm64::opcode_table_entry drcbe_arm64::s_opcode_table_source[] =
{ uml::OP_RESTORE, &drcbe_arm64::op_restore }, // RESTORE dst
// Integer Operations
{ uml::OP_LOAD, &drcbe_arm64::op_load }, // LOAD dst,base,index,size
{ uml::OP_LOADS, &drcbe_arm64::op_loads }, // LOADS dst,base,index,size
{ uml::OP_STORE, &drcbe_arm64::op_store }, // STORE base,index,src,size
{ uml::OP_READ, &drcbe_arm64::op_read }, // READ dst,src1,spacesize
{ uml::OP_READM, &drcbe_arm64::op_readm }, // READM dst,src1,mask,spacesize
{ uml::OP_WRITE, &drcbe_arm64::op_write }, // WRITE dst,src1,spacesize
{ uml::OP_WRITEM, &drcbe_arm64::op_writem }, // WRITEM dst,src1,spacesize
{ uml::OP_CARRY, &drcbe_arm64::op_carry }, // CARRY src,bitnum
{ uml::OP_SET, &drcbe_arm64::op_set }, // SET dst,c
{ uml::OP_MOV, &drcbe_arm64::op_mov }, // MOV dst,src[,c]
{ uml::OP_SEXT, &drcbe_arm64::op_sext }, // SEXT dst,src
{ uml::OP_ROLAND, &drcbe_arm64::op_roland }, // ROLAND dst,src1,src2,src3
{ uml::OP_ROLINS, &drcbe_arm64::op_rolins }, // ROLINS dst,src1,src2,src3
{ uml::OP_ADD, &drcbe_arm64::op_add<a64::Inst::kIdAdds> }, // ADD dst,src1,src2[,f]
{ uml::OP_ADDC, &drcbe_arm64::op_add<a64::Inst::kIdAdcs> }, // ADDC dst,src1,src2[,f]
{ uml::OP_SUB, &drcbe_arm64::op_sub<a64::Inst::kIdSubs> }, // SUB dst,src1,src2[,f]
{ uml::OP_SUBB, &drcbe_arm64::op_sub<a64::Inst::kIdSbcs> }, // SUBB dst,src1,src2[,f]
{ uml::OP_CMP, &drcbe_arm64::op_cmp }, // CMP src1,src2[,f]
{ uml::OP_MULU, &drcbe_arm64::op_mulu }, // MULU dst,edst,src1,src2[,f]
{ uml::OP_MULULW, &drcbe_arm64::op_mululw }, // MULULW dst,src1,src2[,f]
{ uml::OP_MULS, &drcbe_arm64::op_muls }, // MULS dst,edst,src1,src2[,f]
{ uml::OP_MULSLW, &drcbe_arm64::op_mulslw }, // MULSLW dst,src1,src2[,f]
{ uml::OP_DIVU, &drcbe_arm64::op_div<a64::Inst::kIdUdiv> }, // DIVU dst,edst,src1,src2[,f]
{ uml::OP_DIVS, &drcbe_arm64::op_div<a64::Inst::kIdSdiv> }, // DIVS dst,edst,src1,src2[,f]
{ uml::OP_AND, &drcbe_arm64::op_and }, // AND dst,src1,src2[,f]
{ uml::OP_TEST, &drcbe_arm64::op_test }, // TEST src1,src2[,f]
{ uml::OP_OR, &drcbe_arm64::op_or }, // OR dst,src1,src2[,f]
{ uml::OP_XOR, &drcbe_arm64::op_xor }, // XOR dst,src1,src2[,f]
{ uml::OP_LZCNT, &drcbe_arm64::op_lzcnt }, // LZCNT dst,src[,f]
{ uml::OP_TZCNT, &drcbe_arm64::op_tzcnt }, // TZCNT dst,src[,f]
{ uml::OP_BSWAP, &drcbe_arm64::op_bswap }, // BSWAP dst,src
{ uml::OP_SHL, &drcbe_arm64::op_shift<a64::Inst::kIdLsl> }, // SHL dst,src,count[,f]
{ uml::OP_SHR, &drcbe_arm64::op_shift<a64::Inst::kIdLsr> }, // SHR dst,src,count[,f]
{ uml::OP_SAR, &drcbe_arm64::op_shift<a64::Inst::kIdAsr> }, // SAR dst,src,count[,f]
{ uml::OP_ROL, &drcbe_arm64::op_rol }, // ROL dst,src,count[,f]
{ uml::OP_ROLC, &drcbe_arm64::op_rolc }, // ROLC dst,src,count[,f]
{ uml::OP_LOAD, &drcbe_arm64::op_load }, // LOAD dst,base,index,size
{ uml::OP_LOADS, &drcbe_arm64::op_loads }, // LOADS dst,base,index,size
{ uml::OP_STORE, &drcbe_arm64::op_store }, // STORE base,index,src,size
{ uml::OP_READ, &drcbe_arm64::op_read }, // READ dst,src1,spacesize
{ uml::OP_READM, &drcbe_arm64::op_readm }, // READM dst,src1,mask,spacesize
{ uml::OP_WRITE, &drcbe_arm64::op_write }, // WRITE dst,src1,spacesize
{ uml::OP_WRITEM, &drcbe_arm64::op_writem }, // WRITEM dst,src1,spacesize
{ uml::OP_CARRY, &drcbe_arm64::op_carry }, // CARRY src,bitnum
{ uml::OP_SET, &drcbe_arm64::op_set }, // SET dst,c
{ uml::OP_MOV, &drcbe_arm64::op_mov }, // MOV dst,src[,c]
{ uml::OP_SEXT, &drcbe_arm64::op_sext }, // SEXT dst,src
{ uml::OP_ROLAND, &drcbe_arm64::op_roland }, // ROLAND dst,src1,src2,src3
{ uml::OP_ROLINS, &drcbe_arm64::op_rolins }, // ROLINS dst,src1,src2,src3
{ uml::OP_ADD, &drcbe_arm64::op_add<a64::Inst::kIdAdds> }, // ADD dst,src1,src2[,f]
{ uml::OP_ADDC, &drcbe_arm64::op_add<a64::Inst::kIdAdcs> }, // ADDC dst,src1,src2[,f]
{ uml::OP_SUB, &drcbe_arm64::op_sub<a64::Inst::kIdSubs> }, // SUB dst,src1,src2[,f]
{ uml::OP_SUBB, &drcbe_arm64::op_sub<a64::Inst::kIdSbcs> }, // SUBB dst,src1,src2[,f]
{ uml::OP_CMP, &drcbe_arm64::op_cmp }, // CMP src1,src2[,f]
{ uml::OP_MULU, &drcbe_arm64::op_mulu }, // MULU dst,edst,src1,src2[,f]
{ uml::OP_MULULW, &drcbe_arm64::op_mululw }, // MULULW dst,src1,src2[,f]
{ uml::OP_MULS, &drcbe_arm64::op_muls }, // MULS dst,edst,src1,src2[,f]
{ uml::OP_MULSLW, &drcbe_arm64::op_mulslw }, // MULSLW dst,src1,src2[,f]
{ uml::OP_DIVU, &drcbe_arm64::op_div<a64::Inst::kIdUdiv> }, // DIVU dst,edst,src1,src2[,f]
{ uml::OP_DIVS, &drcbe_arm64::op_div<a64::Inst::kIdSdiv> }, // DIVS dst,edst,src1,src2[,f]
{ uml::OP_AND, &drcbe_arm64::op_and }, // AND dst,src1,src2[,f]
{ uml::OP_TEST, &drcbe_arm64::op_test }, // TEST src1,src2[,f]
{ uml::OP_OR, &drcbe_arm64::op_or }, // OR dst,src1,src2[,f]
{ uml::OP_XOR, &drcbe_arm64::op_xor }, // XOR dst,src1,src2[,f]
{ uml::OP_LZCNT, &drcbe_arm64::op_lzcnt }, // LZCNT dst,src[,f]
{ uml::OP_TZCNT, &drcbe_arm64::op_tzcnt }, // TZCNT dst,src[,f]
{ uml::OP_BSWAP, &drcbe_arm64::op_bswap }, // BSWAP dst,src
{ uml::OP_SHL, &drcbe_arm64::op_shift<a64::Inst::kIdLsl> }, // SHL dst,src,count[,f]
{ uml::OP_SHR, &drcbe_arm64::op_shift<a64::Inst::kIdLsr> }, // SHR dst,src,count[,f]
{ uml::OP_SAR, &drcbe_arm64::op_shift<a64::Inst::kIdAsr> }, // SAR dst,src,count[,f]
{ uml::OP_ROL, &drcbe_arm64::op_rol }, // ROL dst,src,count[,f]
{ uml::OP_ROLC, &drcbe_arm64::op_rolc }, // ROLC dst,src,count[,f]
{ uml::OP_ROR, &drcbe_arm64::op_shift<a64::Inst::kIdRor> }, // ROR dst,src,count[,f]
{ uml::OP_RORC, &drcbe_arm64::op_rorc }, // RORC dst,src,count[,f]
{ uml::OP_RORC, &drcbe_arm64::op_rorc }, // RORC dst,src,count[,f]
// Floating Point Operations
{ uml::OP_FLOAD, &drcbe_arm64::op_fload }, // FLOAD dst,base,index
{ uml::OP_FSTORE, &drcbe_arm64::op_fstore }, // FSTORE base,index,src
{ uml::OP_FREAD, &drcbe_arm64::op_fread }, // FREAD dst,space,src1
{ uml::OP_FWRITE, &drcbe_arm64::op_fwrite }, // FWRITE space,dst,src1
{ uml::OP_FMOV, &drcbe_arm64::op_fmov }, // FMOV dst,src1[,c]
{ uml::OP_FTOINT, &drcbe_arm64::op_ftoint }, // FTOINT dst,src1,size,round
{ uml::OP_FFRINT, &drcbe_arm64::op_ffrint }, // FFRINT dst,src1,size
{ uml::OP_FFRFLT, &drcbe_arm64::op_ffrflt }, // FFRFLT dst,src1,size
{ uml::OP_FRNDS, &drcbe_arm64::op_frnds }, // FRNDS dst,src1
{ uml::OP_FADD, &drcbe_arm64::op_float_alu<a64::Inst::kIdFadd_v> }, // FADD dst,src1,src2
{ uml::OP_FSUB, &drcbe_arm64::op_float_alu<a64::Inst::kIdFsub_v> }, // FSUB dst,src1,src2
{ uml::OP_FCMP, &drcbe_arm64::op_fcmp }, // FCMP src1,src2
{ uml::OP_FMUL, &drcbe_arm64::op_float_alu<a64::Inst::kIdFmul_v> }, // FMUL dst,src1,src2
{ uml::OP_FDIV, &drcbe_arm64::op_float_alu<a64::Inst::kIdFdiv_v> }, // FDIV dst,src1,src2
{ uml::OP_FNEG, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFneg_v> }, // FNEG dst,src1
{ uml::OP_FABS, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFabs_v> }, // FABS dst,src1
{ uml::OP_FSQRT, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFsqrt_v> }, // FSQRT dst,src1
{ uml::OP_FRECIP, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFrecpe_v> }, // FRECIP dst,src1
{ uml::OP_FRSQRT, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFrsqrte_v> }, // FRSQRT dst,src1
{ uml::OP_FCOPYI, &drcbe_arm64::op_fcopyi }, // FCOPYI dst,src
{ uml::OP_ICOPYF, &drcbe_arm64::op_icopyf } // ICOPYF dst,src
{ uml::OP_FLOAD, &drcbe_arm64::op_fload }, // FLOAD dst,base,index
{ uml::OP_FSTORE, &drcbe_arm64::op_fstore }, // FSTORE base,index,src
{ uml::OP_FREAD, &drcbe_arm64::op_fread }, // FREAD dst,space,src1
{ uml::OP_FWRITE, &drcbe_arm64::op_fwrite }, // FWRITE space,dst,src1
{ uml::OP_FMOV, &drcbe_arm64::op_fmov }, // FMOV dst,src1[,c]
{ uml::OP_FTOINT, &drcbe_arm64::op_ftoint }, // FTOINT dst,src1,size,round
{ uml::OP_FFRINT, &drcbe_arm64::op_ffrint }, // FFRINT dst,src1,size
{ uml::OP_FFRFLT, &drcbe_arm64::op_ffrflt }, // FFRFLT dst,src1,size
{ uml::OP_FRNDS, &drcbe_arm64::op_frnds }, // FRNDS dst,src1
{ uml::OP_FADD, &drcbe_arm64::op_float_alu<a64::Inst::kIdFadd_v> }, // FADD dst,src1,src2
{ uml::OP_FSUB, &drcbe_arm64::op_float_alu<a64::Inst::kIdFsub_v> }, // FSUB dst,src1,src2
{ uml::OP_FCMP, &drcbe_arm64::op_fcmp }, // FCMP src1,src2
{ uml::OP_FMUL, &drcbe_arm64::op_float_alu<a64::Inst::kIdFmul_v> }, // FMUL dst,src1,src2
{ uml::OP_FDIV, &drcbe_arm64::op_float_alu<a64::Inst::kIdFdiv_v> }, // FDIV dst,src1,src2
{ uml::OP_FNEG, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFneg_v> }, // FNEG dst,src1
{ uml::OP_FABS, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFabs_v> }, // FABS dst,src1
{ uml::OP_FSQRT, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFsqrt_v> }, // FSQRT dst,src1
{ uml::OP_FRECIP, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFrecpe_v> }, // FRECIP dst,src1
{ uml::OP_FRSQRT, &drcbe_arm64::op_float_alu2<a64::Inst::kIdFrsqrte_v> }, // FRSQRT dst,src1
{ uml::OP_FCOPYI, &drcbe_arm64::op_fcopyi }, // FCOPYI dst,src
{ uml::OP_ICOPYF, &drcbe_arm64::op_icopyf } // ICOPYF dst,src
};
drcbe_arm64::be_parameter::be_parameter(drcbe_arm64 &drcbe, const parameter &param, uint32_t allowed)
@ -300,108 +474,6 @@ a64::Gp drcbe_arm64::be_parameter::select_register(a64::Gp const &reg, uint32_t
return reg.x();
}
a64::Vec drcbe_arm64::select_register(a64::Vec const &reg, uint32_t regsize) const
{
if (regsize == 4)
return reg.s();
return reg.d();
}
a64::Gp drcbe_arm64::select_register(a64::Gp const &reg, uint32_t regsize) const
{
if (regsize == 4)
return reg.w();
return reg.x();
}
bool drcbe_arm64::is_valid_immediate_mask(uint64_t val, size_t bytes)
{
// all zeros and all ones aren't allowed, and disallow any value with bits outside of the max bit range
if (val == 0 || val == make_bitmask<uint64_t>(bytes * 8))
return false;
uint32_t head = 64 - count_leading_zeros_64(val);
if (head >= (bytes * 8))
return false;
uint32_t tail = 0;
while (tail < head)
{
if (BIT(val, tail))
break;
tail++;
}
return population_count_64(val) == head - tail;
}
bool drcbe_arm64::is_valid_immediate(uint64_t val, size_t bits)
{
assert(bits < 64);
return val < (uint64_t(1) << bits);
}
bool drcbe_arm64::is_valid_immediate_signed(int64_t val, size_t bits)
{
return util::sext(val, bits) == val;
}
arm::Mem drcbe_arm64::get_mem_absolute(a64::Assembler &a, const void *ptr) const
{
const uint64_t codeoffs = a.code()->baseAddress() + a.offset();
const int64_t reloffs = codeoffs - (int64_t)ptr;
if (is_valid_immediate_signed(reloffs, 21))
{
a.adr(MEM_SCRATCH_REG, ptr);
return arm::Mem(MEM_SCRATCH_REG);
}
const uint64_t pagebase = codeoffs & ~make_bitmask<uint64_t>(12);
const int64_t pagerel = (int64_t)ptr - pagebase;
if (is_valid_immediate_signed(pagerel, 33))
{
const uint64_t targetpage = (uint64_t)ptr & ~make_bitmask<uint64_t>(12);
const uint64_t pageoffs = (uint64_t)ptr & util::make_bitmask<uint64_t>(12);
a.adrp(MEM_SCRATCH_REG, targetpage);
if (is_valid_immediate_signed(pageoffs, 9))
return arm::Mem(MEM_SCRATCH_REG, pageoffs);
else if (emit_add_optimized(a, MEM_SCRATCH_REG, MEM_SCRATCH_REG, pageoffs))
return arm::Mem(MEM_SCRATCH_REG);
}
a.mov(MEM_SCRATCH_REG, ptr);
return arm::Mem(MEM_SCRATCH_REG);
}
bool drcbe_arm64::emit_add_optimized(a64::Assembler &a, const a64::Gp &dst, const a64::Gp &src, int64_t val) const
{
// If the bottom 12 bits are 0s then an optimized form can be used if the remaining bits are <= 12
if (is_valid_immediate(val, 12) || ((val & 0xfff) == 0 && is_valid_immediate(val >> 12, 12)))
{
a.add(dst, src, val);
return true;
}
return false;
}
bool drcbe_arm64::emit_sub_optimized(a64::Assembler &a, const a64::Gp &dst, const a64::Gp &src, int64_t val) const
{
if (val < 0)
val = -val;
// If the bottom 12 bits are 0s then an optimized form can be used if the remaining bits are <= 12
if (is_valid_immediate(val, 12) || ((val & 0xfff) == 0 && is_valid_immediate(val >> 12, 12)))
{
a.sub(dst, src, val);
return true;
}
return false;
}
void drcbe_arm64::get_imm_relative(a64::Assembler &a, const a64::Gp &reg, const uint64_t ptr) const
{
// If a value can be expressed relative to the base register then it's worth using it instead of a mov
@ -765,7 +837,7 @@ void drcbe_arm64::store_unordered(a64::Assembler &a) const
void drcbe_arm64::get_unordered(a64::Assembler &a, const a64::Gp &reg) const
{
get_shifted_bit(a, reg.x(), FLAGS_REG, 1, 4);
a.ubfx(reg.x(), FLAGS_REG, 4, 1);
}
void drcbe_arm64::store_carry_reg(a64::Assembler &a, const a64::Gp &reg) const
@ -802,12 +874,6 @@ void drcbe_arm64::load_carry(a64::Assembler &a, bool inverted) const
a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
}
void drcbe_arm64::get_shifted_bit(a64::Assembler &a, const a64::Gp &dst, const a64::Gp &src, uint32_t bits, uint32_t shift) const
{
a.lsr(dst.x(), src.x(), shift);
a.and_(dst.x(), dst.x(), bits);
}
void drcbe_arm64::calculate_carry_shift_left(a64::Assembler &a, const a64::Gp &reg, const a64::Gp &shift, int maxBits) const
{
Label calc = a.newLabel();
@ -890,11 +956,6 @@ drcbe_arm64::drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cach
, m_baseptr(cache.near() + 0x80)
, m_near(*(near_state *)cache.alloc_near(sizeof(m_near)))
{
// get pointers to C functions we need to call
using debugger_hook_func = void (*)(device_debug *, offs_t);
static const debugger_hook_func debugger_inst_hook = [] (device_debug *dbg, offs_t pc) { dbg->instruction_hook(pc); };
m_near.debug_cpu_instruction_hook = (uint8_t *)debugger_inst_hook;
m_near.drcmap_get_value = (uint8_t *)&drc_map_variables::static_get_value;
m_near.emulated_flags = 0;
// build the opcode table (static but it doesn't hurt to regenerate it)
@ -907,38 +968,18 @@ drcbe_arm64::drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cach
m_log_asmjit = fopen(std::string("drcbearm64_asmjit_").append(device.shortname()).append(".asm").c_str(), "w");
}
// resolve the actual addresses of the address space handlers
auto const resolve_accessor =
[] (resolved_handler &handler, address_space &space, auto accessor)
{
auto const [entrypoint, adjusted] = util::resolve_member_function(accessor, space);
handler.func = reinterpret_cast<uint8_t *>(entrypoint);
handler.obj = adjusted;
};
// resolve the actual addresses of member functions we need to call
m_drcmap_get_value.set(m_map, &drc_map_variables::get_value);
if (!m_drcmap_get_value)
{
m_drcmap_get_value.obj = uintptr_t(&m_map);
m_drcmap_get_value.func = reinterpret_cast<uint8_t *>(uintptr_t(&drc_map_variables::static_get_value));
}
m_resolved_accessors.resize(m_space.size());
for (int space = 0; m_space.size() > space; ++space)
{
if (m_space[space])
{
resolve_accessor(m_resolved_accessors[space].read_byte, *m_space[space], static_cast<u8 (address_space::*)(offs_t) >(&address_space::read_byte));
resolve_accessor(m_resolved_accessors[space].read_byte_masked, *m_space[space], static_cast<u8 (address_space::*)(offs_t, u8) >(&address_space::read_byte));
resolve_accessor(m_resolved_accessors[space].read_word, *m_space[space], static_cast<u16 (address_space::*)(offs_t) >(&address_space::read_word));
resolve_accessor(m_resolved_accessors[space].read_word_masked, *m_space[space], static_cast<u16 (address_space::*)(offs_t, u16)>(&address_space::read_word));
resolve_accessor(m_resolved_accessors[space].read_dword, *m_space[space], static_cast<u32 (address_space::*)(offs_t) >(&address_space::read_dword));
resolve_accessor(m_resolved_accessors[space].read_dword_masked, *m_space[space], static_cast<u32 (address_space::*)(offs_t, u32)>(&address_space::read_dword));
resolve_accessor(m_resolved_accessors[space].read_qword, *m_space[space], static_cast<u64 (address_space::*)(offs_t) >(&address_space::read_qword));
resolve_accessor(m_resolved_accessors[space].read_qword_masked, *m_space[space], static_cast<u64 (address_space::*)(offs_t, u64)>(&address_space::read_qword));
resolve_accessor(m_resolved_accessors[space].write_byte, *m_space[space], static_cast<void (address_space::*)(offs_t, u8) >(&address_space::write_byte));
resolve_accessor(m_resolved_accessors[space].write_byte_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u8, u8) >(&address_space::write_byte));
resolve_accessor(m_resolved_accessors[space].write_word, *m_space[space], static_cast<void (address_space::*)(offs_t, u16) >(&address_space::write_word));
resolve_accessor(m_resolved_accessors[space].write_word_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u16, u16)>(&address_space::write_word));
resolve_accessor(m_resolved_accessors[space].write_dword, *m_space[space], static_cast<void (address_space::*)(offs_t, u32) >(&address_space::write_dword));
resolve_accessor(m_resolved_accessors[space].write_dword_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u32, u32)>(&address_space::write_dword));
resolve_accessor(m_resolved_accessors[space].write_qword, *m_space[space], static_cast<void (address_space::*)(offs_t, u64) >(&address_space::write_qword));
resolve_accessor(m_resolved_accessors[space].write_qword_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u64, u64)>(&address_space::write_qword));
}
m_resolved_accessors[space].set(*m_space[space]);
}
}
@ -1048,6 +1089,19 @@ int drcbe_arm64::execute(code_handle &entry)
void drcbe_arm64::generate(drcuml_block &block, const instruction *instlist, uint32_t numinst)
{
// do this here because device.debug() isn't initialised at construction time
if (!m_debug_cpu_instruction_hook && (m_device.machine().debug_flags & DEBUG_FLAG_ENABLED))
{
m_debug_cpu_instruction_hook.set(*m_device.debug(), &device_debug::instruction_hook);
if (!m_debug_cpu_instruction_hook)
{
m_debug_cpu_instruction_hook.obj = uintptr_t(m_device.debug());
using debugger_hook_func = void (*)(device_debug *, offs_t);
static const auto debugger_inst_hook = [] (device_debug *dbg, offs_t pc) { dbg->instruction_hook(pc); };
m_debug_cpu_instruction_hook.func = reinterpret_cast<uint8_t *>(uintptr_t(debugger_hook_func(debugger_inst_hook)));
}
}
// tell all of our utility objects that a block is beginning
m_hash.block_begin(block, instlist, numinst);
m_map.block_begin(block);
@ -1233,11 +1287,10 @@ void drcbe_arm64::op_debug(a64::Assembler &a, const uml::instruction &inst)
emit_ldr_mem(a, temp, &m_device.machine().debug_flags);
a.tbz(temp, 1, skip); // DEBUG_FLAG_CALL_HOOK
get_imm_relative(a, REG_PARAM1, (uintptr_t)m_device.debug());
get_imm_relative(a, REG_PARAM1, m_debug_cpu_instruction_hook.obj);
mov_reg_param(a, 4, REG_PARAM2, pcp);
emit_ldr_mem(a, TEMP_REG2, &m_near.debug_cpu_instruction_hook);
a.blr(TEMP_REG2);
call_arm_addr(a, m_debug_cpu_instruction_hook.func);
a.bind(skip);
}
@ -1556,13 +1609,11 @@ void drcbe_arm64::op_recover(a64::Assembler &a, const uml::instruction &inst)
be_parameter dstp(*this, inst.param(0), PTYPE_MR);
get_imm_relative(a, REG_PARAM1, (uintptr_t)&m_map);
get_imm_relative(a, REG_PARAM1, m_drcmap_get_value.obj);
a.ldr(REG_PARAM2, arm::Mem(a64::x29, -8)); // saved LR (x30) from first level CALLH/EXH or failed hash jump
a.mov(REG_PARAM3, inst.param(1).mapvar());
emit_ldr_mem(a, TEMP_REG1, &m_near.drcmap_get_value);
a.blr(TEMP_REG1);
call_arm_addr(a, m_drcmap_get_value.func);
mov_param_reg(a, inst.size(), dstp, REG_PARAM1);
}
@ -2007,7 +2058,7 @@ void drcbe_arm64::op_read(a64::Assembler &a, const uml::instruction &inst)
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.read_byte.func)
if (resolved.read_byte)
{
get_imm_relative(a, REG_PARAM1, resolved.read_byte.obj);
call_arm_addr(a, resolved.read_byte.func);
@ -2021,7 +2072,7 @@ void drcbe_arm64::op_read(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.read_word.func)
if (resolved.read_word)
{
get_imm_relative(a, REG_PARAM1, resolved.read_word.obj);
call_arm_addr(a, resolved.read_word.func);
@ -2035,7 +2086,7 @@ void drcbe_arm64::op_read(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.read_dword.func)
if (resolved.read_dword)
{
get_imm_relative(a, REG_PARAM1, resolved.read_dword.obj);
call_arm_addr(a, resolved.read_dword.func);
@ -2049,7 +2100,7 @@ void drcbe_arm64::op_read(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.read_qword.func)
if (resolved.read_qword)
{
get_imm_relative(a, REG_PARAM1, resolved.read_qword.obj);
call_arm_addr(a, resolved.read_qword.func);
@ -2085,7 +2136,7 @@ void drcbe_arm64::op_readm(a64::Assembler &a, const uml::instruction &inst)
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.read_byte_masked.func)
if (resolved.read_byte_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.read_byte_masked.obj);
call_arm_addr(a, resolved.read_byte_masked.func);
@ -2099,7 +2150,7 @@ void drcbe_arm64::op_readm(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.read_word_masked.func)
if (resolved.read_word_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.read_word_masked.obj);
call_arm_addr(a, resolved.read_word_masked.func);
@ -2113,7 +2164,7 @@ void drcbe_arm64::op_readm(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.read_dword_masked.func)
if (resolved.read_dword_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.read_dword_masked.obj);
call_arm_addr(a, resolved.read_dword_masked.func);
@ -2127,7 +2178,7 @@ void drcbe_arm64::op_readm(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.read_qword_masked.func)
if (resolved.read_qword_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.read_qword_masked.obj);
call_arm_addr(a, resolved.read_qword_masked.func);
@ -2162,7 +2213,7 @@ void drcbe_arm64::op_write(a64::Assembler &a, const uml::instruction &inst)
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.write_byte.func)
if (resolved.write_byte)
{
get_imm_relative(a, REG_PARAM1, resolved.write_byte.obj);
call_arm_addr(a, resolved.write_byte.func);
@ -2176,7 +2227,7 @@ void drcbe_arm64::op_write(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.write_word.func)
if (resolved.write_word)
{
get_imm_relative(a, REG_PARAM1, resolved.write_word.obj);
call_arm_addr(a, resolved.write_word.func);
@ -2190,7 +2241,7 @@ void drcbe_arm64::op_write(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.write_dword.func)
if (resolved.write_dword)
{
get_imm_relative(a, REG_PARAM1, resolved.write_dword.obj);
call_arm_addr(a, resolved.write_dword.func);
@ -2204,7 +2255,7 @@ void drcbe_arm64::op_write(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.write_qword.func)
if (resolved.write_qword)
{
get_imm_relative(a, REG_PARAM1, resolved.write_qword.obj);
call_arm_addr(a, resolved.write_qword.func);
@ -2240,7 +2291,7 @@ void drcbe_arm64::op_writem(a64::Assembler &a, const uml::instruction &inst)
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.write_byte_masked.func)
if (resolved.write_byte_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.write_byte_masked.obj);
call_arm_addr(a, resolved.write_byte_masked.func);
@ -2254,7 +2305,7 @@ void drcbe_arm64::op_writem(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.write_word_masked.func)
if (resolved.write_word_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.write_word_masked.obj);
call_arm_addr(a, resolved.write_word_masked.func);
@ -2268,7 +2319,7 @@ void drcbe_arm64::op_writem(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.write_dword_masked.func)
if (resolved.write_dword_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.write_dword_masked.obj);
call_arm_addr(a, resolved.write_dword_masked.func);
@ -2282,7 +2333,7 @@ void drcbe_arm64::op_writem(a64::Assembler &a, const uml::instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.write_qword_masked.func)
if (resolved.write_qword_masked)
{
get_imm_relative(a, REG_PARAM1, resolved.write_qword_masked.obj);
call_arm_addr(a, resolved.write_qword_masked.func);
@ -2676,12 +2727,12 @@ void drcbe_arm64::op_rolins(a64::Assembler &a, const uml::instruction &inst)
if (is_right_aligned || is_contiguous)
{
uint32_t rot = 0;
uint32_t lsb = 0;
dst = can_use_dst_reg ? dstp.select_register(SCRATCH_REG1, inst.size()) : select_register(SCRATCH_REG1, inst.size());
mov_reg_param(a, inst.size(), dst, dstp);
uint32_t rot = 0;
uint32_t lsb = 0;
if (is_right_aligned)
{
// Optimize a contiguous right-aligned mask
@ -2707,10 +2758,6 @@ void drcbe_arm64::op_rolins(a64::Assembler &a, const uml::instruction &inst)
result = rotr_64(srcp.immediate(), rot);
a.mov(src, result);
a.bfi(dst, src, lsb, pop);
optimized = true;
}
else
{
@ -2718,11 +2765,11 @@ void drcbe_arm64::op_rolins(a64::Assembler &a, const uml::instruction &inst)
if (rot > 0)
a.ror(src, src, rot);
a.bfi(dst, src, lsb, pop);
optimized = true;
}
a.bfi(dst, src, lsb, pop);
optimized = true;
}
else if (srcp.is_immediate())
{
@ -3881,7 +3928,7 @@ void drcbe_arm64::op_fread(a64::Assembler &a, const uml::instruction &inst)
if (inst.size() == 4)
{
if (resolved.read_dword.func)
if (resolved.read_dword)
{
get_imm_relative(a, REG_PARAM1, resolved.read_dword.obj);
call_arm_addr(a, resolved.read_dword.func);
@ -3897,7 +3944,7 @@ void drcbe_arm64::op_fread(a64::Assembler &a, const uml::instruction &inst)
}
else if (inst.size() == 8)
{
if (resolved.read_qword.func)
if (resolved.read_qword)
{
get_imm_relative(a, REG_PARAM1, resolved.read_qword.obj);
call_arm_addr(a, resolved.read_qword.func);
@ -3935,7 +3982,7 @@ void drcbe_arm64::op_fwrite(a64::Assembler &a, const uml::instruction &inst)
if (inst.size() == 4)
{
if (resolved.write_dword.func)
if (resolved.write_dword)
{
get_imm_relative(a, REG_PARAM1, resolved.write_dword.obj);
call_arm_addr(a, resolved.write_dword.func);
@ -3949,7 +3996,7 @@ void drcbe_arm64::op_fwrite(a64::Assembler &a, const uml::instruction &inst)
}
else if (inst.size() == 8)
{
if (resolved.write_qword.func)
if (resolved.write_qword)
{
get_imm_relative(a, REG_PARAM1, resolved.write_qword.obj);
call_arm_addr(a, resolved.write_qword.func);

View File

@ -167,19 +167,8 @@ private:
// helper functions
asmjit::a64::Vec select_register(asmjit::a64::Vec const &reg, uint32_t regsize) const;
asmjit::a64::Gp select_register(asmjit::a64::Gp const &reg, uint32_t regsize) const;
static bool is_valid_immediate(uint64_t val, size_t bits);
static bool is_valid_immediate_signed(int64_t val, size_t bits);
static bool is_valid_immediate_mask(uint64_t val, size_t bytes);
asmjit::arm::Mem get_mem_absolute(asmjit::a64::Assembler &a, const void *ptr) const;
void get_imm_relative(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const uint64_t ptr) const;
bool emit_add_optimized(asmjit::a64::Assembler &a, const asmjit::a64::Gp &dst, const asmjit::a64::Gp &src, int64_t val) const;
bool emit_sub_optimized(asmjit::a64::Assembler &a, const asmjit::a64::Gp &dst, const asmjit::a64::Gp &src, int64_t val) const;
void emit_ldr_str_base_mem(asmjit::a64::Assembler &a, asmjit::a64::Inst::Id opcode, const asmjit::a64::Reg &reg, const void *ptr) const;
void emit_ldr_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const void *ptr) const;
void emit_ldrb_mem(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const void *ptr) const;
@ -203,8 +192,6 @@ private:
void get_unordered(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg) const;
void check_unordered_condition(asmjit::a64::Assembler &a, uml::condition_t cond, asmjit::Label condition_met, bool not_equal) const;
void get_shifted_bit(asmjit::a64::Assembler &a, const asmjit::a64::Gp &dst, const asmjit::a64::Gp &src, uint32_t bits, uint32_t shift) const;
void calculate_carry_shift_left(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const asmjit::a64::Gp &shift, int maxBits) const;
void calculate_carry_shift_left_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const int shift, int maxBits) const;
@ -238,9 +225,6 @@ private:
struct near_state
{
void *debug_cpu_instruction_hook;
void *drcmap_get_value;
uint32_t emulated_flags;
};
near_state &m_near;
@ -254,33 +238,12 @@ private:
static const opcode_table_entry s_opcode_table_source[];
static opcode_generate_func s_opcode_table[uml::OP_MAX];
struct resolved_handler { uintptr_t obj = 0; void *func = nullptr; };
struct resolved_accessors
{
resolved_handler read_byte;
resolved_handler read_byte_masked;
resolved_handler read_word;
resolved_handler read_word_masked;
resolved_handler read_dword;
resolved_handler read_dword_masked;
resolved_handler read_qword;
resolved_handler read_qword_masked;
resolved_handler write_byte;
resolved_handler write_byte_masked;
resolved_handler write_word;
resolved_handler write_word_masked;
resolved_handler write_dword;
resolved_handler write_dword_masked;
resolved_handler write_qword;
resolved_handler write_qword_masked;
};
using resolved_accessors_vector = std::vector<resolved_accessors>;
resolved_accessors_vector m_resolved_accessors;
resolved_member_function m_debug_cpu_instruction_hook;
resolved_member_function m_drcmap_get_value;
resolved_memory_accessors_vector m_resolved_accessors;
};
}
} // namespace drc
using drc::drcbe_arm64;

View File

@ -14,6 +14,8 @@
#include <cmath>
namespace drc {
using namespace uml;
@ -2436,3 +2438,5 @@ uint64_t drcbe_c::tzcount64(uint64_t value)
}
return 64;
}
} // namespace drc

View File

@ -17,6 +17,7 @@
#include "drcbeut.h"
namespace drc {
//**************************************************************************
// TYPE DEFINITIONS
@ -57,5 +58,8 @@ private:
static uint64_t s_immediate_zero;
};
} // namespace drc
using drc::drcbe_c;
#endif // MAME_CPU_DRCBEC_H

View File

@ -11,6 +11,8 @@
#include "emu.h"
#include "drcbeut.h"
namespace drc {
using namespace uml;
@ -30,19 +32,19 @@ using namespace uml;
// drc_hash_table - constructor
//-------------------------------------------------
drc_hash_table::drc_hash_table(drc_cache &cache, uint32_t modes, uint8_t addrbits, uint8_t ignorebits)
: m_cache(cache),
m_modes(modes),
m_nocodeptr(nullptr),
m_l1bits((addrbits - ignorebits) / 2),
m_l2bits((addrbits - ignorebits) - m_l1bits),
m_l1shift(ignorebits + m_l2bits),
m_l2shift(ignorebits),
m_l1mask((1 << m_l1bits) - 1),
m_l2mask((1 << m_l2bits) - 1),
m_base(reinterpret_cast<drccodeptr ***>(cache.alloc(modes * sizeof(**m_base)))),
m_emptyl1(nullptr),
m_emptyl2(nullptr)
drc_hash_table::drc_hash_table(drc_cache &cache, uint32_t modes, uint8_t addrbits, uint8_t ignorebits) :
m_cache(cache),
m_modes(modes),
m_nocodeptr(nullptr),
m_l1bits((addrbits - ignorebits) / 2),
m_l2bits((addrbits - ignorebits) - m_l1bits),
m_l1shift(ignorebits + m_l2bits),
m_l2shift(ignorebits),
m_l1mask((1 << m_l1bits) - 1),
m_l2mask((1 << m_l2bits) - 1),
m_base(reinterpret_cast<drccodeptr ***>(cache.alloc(modes * sizeof(**m_base)))),
m_emptyl1(nullptr),
m_emptyl2(nullptr)
{
reset();
}
@ -577,3 +579,36 @@ void drc_label_list::oob_callback(drccodeptr *codeptr, void *param1, void *param
label_fixup *fixup = reinterpret_cast<label_fixup *>(param1);
fixup->m_callback(param2, fixup->m_label->m_codeptr);
}
//**************************************************************************
// RESOLVED MEMORY ACCESSORS
//**************************************************************************
//-------------------------------------------------
// set - bind to address space
//-------------------------------------------------
void resolved_memory_accessors::set(address_space &space) noexcept
{
read_byte .set(space, static_cast<u8 (address_space::*)(offs_t) >(&address_space::read_byte));
read_byte_masked .set(space, static_cast<u8 (address_space::*)(offs_t, u8) >(&address_space::read_byte));
read_word .set(space, static_cast<u16 (address_space::*)(offs_t) >(&address_space::read_word));
read_word_masked .set(space, static_cast<u16 (address_space::*)(offs_t, u16)>(&address_space::read_word));
read_dword .set(space, static_cast<u32 (address_space::*)(offs_t) >(&address_space::read_dword));
read_dword_masked .set(space, static_cast<u32 (address_space::*)(offs_t, u32)>(&address_space::read_dword));
read_qword .set(space, static_cast<u64 (address_space::*)(offs_t) >(&address_space::read_qword));
read_qword_masked .set(space, static_cast<u64 (address_space::*)(offs_t, u64)>(&address_space::read_qword));
write_byte .set(space, static_cast<void (address_space::*)(offs_t, u8) >(&address_space::write_byte));
write_byte_masked .set(space, static_cast<void (address_space::*)(offs_t, u8, u8) >(&address_space::write_byte));
write_word .set(space, static_cast<void (address_space::*)(offs_t, u16) >(&address_space::write_word));
write_word_masked .set(space, static_cast<void (address_space::*)(offs_t, u16, u16)>(&address_space::write_word));
write_dword .set(space, static_cast<void (address_space::*)(offs_t, u32) >(&address_space::write_dword));
write_dword_masked .set(space, static_cast<void (address_space::*)(offs_t, u32, u32)>(&address_space::write_dword));
write_qword .set(space, static_cast<void (address_space::*)(offs_t, u64) >(&address_space::write_qword));
write_qword_masked .set(space, static_cast<void (address_space::*)(offs_t, u64, u64)>(&address_space::write_qword));
}
} // namespace drc

View File

@ -15,6 +15,13 @@
#include "drcuml.h"
#include "mfpresolve.h"
#include <utility>
#include <vector>
namespace drc {
//**************************************************************************
// TYPE DEFINITIONS
@ -55,14 +62,14 @@ public:
private:
// internal state
drc_cache & m_cache; // cache where allocations come from
uint32_t m_modes; // number of modes supported
uint32_t m_modes; // number of modes supported
drccodeptr m_nocodeptr; // pointer to code which will handle missing entries
uint8_t m_l1bits; // bits worth of entries in l1 hash tables
uint8_t m_l2bits; // bits worth of entries in l2 hash tables
uint8_t m_l1shift; // shift to apply to the PC to get the l1 hash entry
uint8_t m_l2shift; // shift to apply to the PC to get the l2 hash entry
uint8_t m_l1bits; // bits worth of entries in l1 hash tables
uint8_t m_l2bits; // bits worth of entries in l2 hash tables
uint8_t m_l1shift; // shift to apply to the PC to get the l1 hash entry
uint8_t m_l2shift; // shift to apply to the PC to get the l2 hash entry
offs_t m_l1mask; // mask to apply after shifting
offs_t m_l2mask; // mask to apply after shifting
@ -97,8 +104,8 @@ public:
private:
// internal state
drc_cache & m_cache; // pointer to the cache
uint64_t m_uniquevalue; // unique value used to find the table
uint32_t m_mapvalue[uml::MAPVAR_END - uml::MAPVAR_M0]; // array of current values
uint64_t m_uniquevalue; // unique value used to find the table
uint32_t m_mapvalue[uml::MAPVAR_END - uml::MAPVAR_M0]; // array of current values
// list of entries
struct map_entry
@ -106,8 +113,8 @@ private:
map_entry *next() const { return m_next; }
map_entry * m_next; // pointer to next map entry
drccodeptr m_codeptr; // pointer to the relevant code
uint32_t m_mapvar; // map variable id
uint32_t m_newval; // value of the variable starting at codeptr
uint32_t m_mapvar; // map variable id
uint32_t m_newval; // value of the variable starting at codeptr
};
simple_list<map_entry> m_entry_list; // list of entries
};
@ -163,4 +170,56 @@ private:
};
// ======================> resolved_member_function
struct resolved_member_function
{
uintptr_t obj = uintptr_t(nullptr);
uint8_t *func = nullptr;
explicit operator bool() const noexcept
{
return bool(func);
}
template <typename C, typename F>
void set(C &&instance, F &&mfp) noexcept
{
auto const [entrypoint, adjusted] = util::resolve_member_function(std::forward<F>(mfp), std::forward<C>(instance));
obj = adjusted;
func = reinterpret_cast<uint8_t *>(entrypoint);
}
};
// ======================> resolved_memory_accessors
struct resolved_memory_accessors
{
resolved_member_function read_byte;
resolved_member_function read_byte_masked;
resolved_member_function read_word;
resolved_member_function read_word_masked;
resolved_member_function read_dword;
resolved_member_function read_dword_masked;
resolved_member_function read_qword;
resolved_member_function read_qword_masked;
resolved_member_function write_byte;
resolved_member_function write_byte_masked;
resolved_member_function write_word;
resolved_member_function write_word_masked;
resolved_member_function write_dword;
resolved_member_function write_dword_masked;
resolved_member_function write_qword;
resolved_member_function write_qword_masked;
void set(address_space &space) noexcept;
};
using resolved_memory_accessors_vector = std::vector<resolved_memory_accessors>;
} // namespace drc
#endif // MAME_CPU_DRCBEUT_H

View File

@ -172,8 +172,6 @@
#include "debug/debugcpu.h"
#include "emuopts.h"
#include "mfpresolve.h"
#include <cstddef>
@ -660,15 +658,11 @@ drcbe_x64::drcbe_x64(drcuml_state &drcuml, device_t &device, drc_cache &cache, u
m_absmask64[0] = m_absmask64[1] = 0x7fffffffffffffffU;
// get pointers to C functions we need to call
using debugger_hook_func = void (*)(device_debug *, offs_t);
static const debugger_hook_func debugger_inst_hook = [] (device_debug *dbg, offs_t pc) { dbg->instruction_hook(pc); }; // TODO: kill trampoline if possible
m_near.debug_cpu_instruction_hook = (x86code *)debugger_inst_hook;
if (LOG_HASHJMPS)
{
m_near.debug_log_hashjmp = (x86code *)debug_log_hashjmp;
m_near.debug_log_hashjmp_fail = (x86code *)debug_log_hashjmp_fail;
}
m_near.drcmap_get_value = (x86code *)&drc_map_variables::static_get_value;
// build the flags map
for (int entry = 0; entry < std::size(m_near.flagsmap); entry++)
@ -692,37 +686,18 @@ drcbe_x64::drcbe_x64(drcuml_state &drcuml, device_t &device, drc_cache &cache, u
m_near.flagsunmap[entry] = flags;
}
// resolve the actual addresses of the address space handlers
auto const resolve_accessor =
[] (resolved_handler &handler, address_space &space, auto accessor)
{
auto const [entrypoint, adjusted] = util::resolve_member_function(accessor, space);
handler.func = reinterpret_cast<x86code *>(entrypoint);
handler.obj = adjusted;
};
// resolve the actual addresses of member functions we need to call
m_drcmap_get_value.set(m_map, &drc_map_variables::get_value);
if (!m_drcmap_get_value)
{
m_drcmap_get_value.obj = uintptr_t(&m_map);
m_drcmap_get_value.func = reinterpret_cast<uint8_t *>(uintptr_t(&drc_map_variables::static_get_value));
}
m_resolved_accessors.resize(m_space.size());
for (int space = 0; m_space.size() > space; ++space)
{
if (m_space[space])
{
resolve_accessor(m_resolved_accessors[space].read_byte, *m_space[space], static_cast<u8 (address_space::*)(offs_t) >(&address_space::read_byte));
resolve_accessor(m_resolved_accessors[space].read_byte_masked, *m_space[space], static_cast<u8 (address_space::*)(offs_t, u8) >(&address_space::read_byte));
resolve_accessor(m_resolved_accessors[space].read_word, *m_space[space], static_cast<u16 (address_space::*)(offs_t) >(&address_space::read_word));
resolve_accessor(m_resolved_accessors[space].read_word_masked, *m_space[space], static_cast<u16 (address_space::*)(offs_t, u16)>(&address_space::read_word));
resolve_accessor(m_resolved_accessors[space].read_dword, *m_space[space], static_cast<u32 (address_space::*)(offs_t) >(&address_space::read_dword));
resolve_accessor(m_resolved_accessors[space].read_dword_masked, *m_space[space], static_cast<u32 (address_space::*)(offs_t, u32)>(&address_space::read_dword));
resolve_accessor(m_resolved_accessors[space].read_qword, *m_space[space], static_cast<u64 (address_space::*)(offs_t) >(&address_space::read_qword));
resolve_accessor(m_resolved_accessors[space].read_qword_masked, *m_space[space], static_cast<u64 (address_space::*)(offs_t, u64)>(&address_space::read_qword));
resolve_accessor(m_resolved_accessors[space].write_byte, *m_space[space], static_cast<void (address_space::*)(offs_t, u8) >(&address_space::write_byte));
resolve_accessor(m_resolved_accessors[space].write_byte_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u8, u8) >(&address_space::write_byte));
resolve_accessor(m_resolved_accessors[space].write_word, *m_space[space], static_cast<void (address_space::*)(offs_t, u16) >(&address_space::write_word));
resolve_accessor(m_resolved_accessors[space].write_word_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u16, u16)>(&address_space::write_word));
resolve_accessor(m_resolved_accessors[space].write_dword, *m_space[space], static_cast<void (address_space::*)(offs_t, u32) >(&address_space::write_dword));
resolve_accessor(m_resolved_accessors[space].write_dword_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u32, u32)>(&address_space::write_dword));
resolve_accessor(m_resolved_accessors[space].write_qword, *m_space[space], static_cast<void (address_space::*)(offs_t, u64) >(&address_space::write_qword));
resolve_accessor(m_resolved_accessors[space].write_qword_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u64, u64)>(&address_space::write_qword));
}
m_resolved_accessors[space].set(*m_space[space]);
}
// build the opcode table (static but it doesn't hurt to regenerate it)
@ -892,6 +867,19 @@ int drcbe_x64::execute(code_handle &entry)
void drcbe_x64::generate(drcuml_block &block, const instruction *instlist, uint32_t numinst)
{
// do this here because device.debug() isn't initialised at construction time
if (!m_debug_cpu_instruction_hook && (m_device.machine().debug_flags & DEBUG_FLAG_ENABLED))
{
m_debug_cpu_instruction_hook.set(*m_device.debug(), &device_debug::instruction_hook);
if (!m_debug_cpu_instruction_hook)
{
m_debug_cpu_instruction_hook.obj = uintptr_t(m_device.debug());
using debugger_hook_func = void (*)(device_debug *, offs_t);
static const auto debugger_inst_hook = [] (device_debug *dbg, offs_t pc) { dbg->instruction_hook(pc); };
m_debug_cpu_instruction_hook.func = reinterpret_cast<uint8_t *>(uintptr_t(debugger_hook_func(debugger_inst_hook)));
}
}
// tell all of our utility objects that a block is beginning
m_hash.block_begin(block, instlist, numinst);
m_map.block_begin(block);
@ -1556,9 +1544,9 @@ void drcbe_x64::op_debug(Assembler &a, const instruction &inst)
a.short_().jz(skip);
// push the parameter
mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_device.debug()); // mov param1,device.debug
mov_r64_imm(a, Gpq(REG_PARAM1), m_debug_cpu_instruction_hook.obj); // mov param1,device.debug
mov_reg_param(a, Gpd(REG_PARAM2), pcp); // mov param2,pcp
smart_call_m64(a, &m_near.debug_cpu_instruction_hook); // call debug_cpu_instruction_hook
smart_call_r64(a, m_debug_cpu_instruction_hook.func, rax); // call debug_cpu_instruction_hook
a.bind(skip);
}
@ -1861,10 +1849,10 @@ void drcbe_x64::op_recover(Assembler &a, const instruction &inst)
// call the recovery code
a.mov(rax, MABS(&m_near.stacksave)); // mov rax,stacksave
a.mov(rax, ptr(rax, -8)); // mov rax,[rax-8]
mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)&m_map); // mov param1,m_map
mov_r64_imm(a, Gpq(REG_PARAM1), m_drcmap_get_value.obj); // mov param1,m_map
a.lea(Gpq(REG_PARAM2), ptr(rax, -1)); // lea param2,[rax-1]
mov_r64_imm(a, Gpq(REG_PARAM3), inst.param(1).mapvar()); // mov param3,param[1].value
smart_call_m64(a, &m_near.drcmap_get_value); // call drcmap_get_value
smart_call_r64(a, m_drcmap_get_value.func, rax); // call drcmap_get_value
mov_param_reg(a, dstp, eax); // mov dstp,eax
}
@ -2523,7 +2511,7 @@ void drcbe_x64::op_read(Assembler &a, const instruction &inst)
mov_reg_param(a, Gpd(REG_PARAM2), addrp); // mov param2,addrp
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.read_byte.func)
if (resolved.read_byte)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_byte.obj); // mov param1,space
smart_call_r64(a, resolved.read_byte.func, rax); // call read_byte
@ -2537,7 +2525,7 @@ void drcbe_x64::op_read(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.read_word.func)
if (resolved.read_word)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_word.obj); // mov param1,space
smart_call_r64(a, resolved.read_word.func, rax); // call read_word
@ -2551,7 +2539,7 @@ void drcbe_x64::op_read(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.read_dword.func)
if (resolved.read_dword)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_dword.obj); // mov param1,space
smart_call_r64(a, resolved.read_dword.func, rax); // call read_dword
@ -2566,7 +2554,7 @@ void drcbe_x64::op_read(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.read_qword.func)
if (resolved.read_qword)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_qword.obj); // mov param1,space
smart_call_r64(a, resolved.read_qword.func, rax); // call read_qword
@ -2619,7 +2607,7 @@ void drcbe_x64::op_readm(Assembler &a, const instruction &inst)
mov_reg_param(a, Gpq(REG_PARAM3), maskp); // mov param3,maskp
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.read_byte_masked.func)
if (resolved.read_byte_masked)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_byte_masked.obj); // mov param1,space
smart_call_r64(a, resolved.read_byte_masked.func, rax); // call read_byte_masked
@ -2633,7 +2621,7 @@ void drcbe_x64::op_readm(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.read_word_masked.func)
if (resolved.read_word_masked)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_word_masked.obj); // mov param1,space
smart_call_r64(a, resolved.read_word_masked.func, rax); // call read_word_masked
@ -2647,7 +2635,7 @@ void drcbe_x64::op_readm(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.read_dword_masked.func)
if (resolved.read_dword_masked)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_dword_masked.obj); // mov param1,space
smart_call_r64(a, resolved.read_dword_masked.func, rax); // call read_dword_masked
@ -2662,7 +2650,7 @@ void drcbe_x64::op_readm(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.read_qword_masked.func)
if (resolved.read_qword_masked)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_qword_masked.obj); // mov param1,space
smart_call_r64(a, resolved.read_qword_masked.func, rax); // call read_qword_masked
@ -2711,7 +2699,7 @@ void drcbe_x64::op_write(Assembler &a, const instruction &inst)
mov_reg_param(a, Gpq(REG_PARAM3), srcp); // mov param3,srcp
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.write_byte.func)
if (resolved.write_byte)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_byte.obj); // mov param1,space
smart_call_r64(a, resolved.write_byte.func, rax); // call write_byte
@ -2724,7 +2712,7 @@ void drcbe_x64::op_write(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.write_word.func)
if (resolved.write_word)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_word.obj); // mov param1,space
smart_call_r64(a, resolved.write_word.func, rax); // call write_word
@ -2737,7 +2725,7 @@ void drcbe_x64::op_write(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.write_dword.func)
if (resolved.write_dword)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_dword.obj); // mov param1,space
smart_call_r64(a, resolved.write_dword.func, rax); // call write_dword
@ -2750,7 +2738,7 @@ void drcbe_x64::op_write(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.write_qword.func)
if (resolved.write_qword)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_qword.obj); // mov param1,space
smart_call_r64(a, resolved.write_qword.func, rax); // call write_qword
@ -2798,7 +2786,7 @@ void drcbe_x64::op_writem(Assembler &a, const instruction &inst)
}
if (spacesizep.size() == SIZE_BYTE)
{
if (resolved.write_byte.func)
if (resolved.write_byte)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_byte_masked.obj); // mov param1,space
smart_call_r64(a, resolved.write_byte_masked.func, rax); // call write_byte_masked
@ -2811,7 +2799,7 @@ void drcbe_x64::op_writem(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_WORD)
{
if (resolved.write_word.func)
if (resolved.write_word)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_word_masked.obj); // mov param1,space
smart_call_r64(a, resolved.write_word_masked.func, rax); // call write_word_masked
@ -2824,7 +2812,7 @@ void drcbe_x64::op_writem(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_DWORD)
{
if (resolved.write_word.func)
if (resolved.write_word)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_dword_masked.obj); // mov param1,space
smart_call_r64(a, resolved.write_dword_masked.func, rax); // call write_dword_masked
@ -2837,7 +2825,7 @@ void drcbe_x64::op_writem(Assembler &a, const instruction &inst)
}
else if (spacesizep.size() == SIZE_QWORD)
{
if (resolved.write_word.func)
if (resolved.write_word)
{
mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_qword_masked.obj); // mov param1,space
smart_call_r64(a, resolved.write_qword_masked.func, rax); // call write_qword_masked

View File

@ -251,10 +251,8 @@ private:
// 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
@ -270,30 +268,9 @@ private:
near_state & m_near;
// resolved memory handler functions
struct resolved_handler { uintptr_t obj = 0; x86code *func = nullptr; };
struct resolved_accessors
{
resolved_handler read_byte;
resolved_handler read_byte_masked;
resolved_handler read_word;
resolved_handler read_word_masked;
resolved_handler read_dword;
resolved_handler read_dword_masked;
resolved_handler read_qword;
resolved_handler read_qword_masked;
resolved_handler write_byte;
resolved_handler write_byte_masked;
resolved_handler write_word;
resolved_handler write_word_masked;
resolved_handler write_dword;
resolved_handler write_dword_masked;
resolved_handler write_qword;
resolved_handler write_qword_masked;
};
using resolved_accessors_vector = std::vector<resolved_accessors>;
resolved_accessors_vector m_resolved_accessors;
resolved_member_function m_debug_cpu_instruction_hook;
resolved_member_function m_drcmap_get_value;
resolved_memory_accessors_vector m_resolved_accessors;
// globals
using opcode_generate_func = void (drcbe_x64::*)(asmjit::x86::Assembler &, const uml::instruction &);