mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-03-08 00:16:28 +00:00
A32: Change UserCallbacks to be similar to A64's interface
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include "frontend/ir/location_descriptor.h"
|
||||
#include "ir_opt/passes.h"
|
||||
#include "rand_int.h"
|
||||
#include "testenv.h"
|
||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
||||
|
||||
@@ -37,126 +38,10 @@
|
||||
|
||||
using Dynarmic::Common::Bits;
|
||||
|
||||
struct WriteRecord {
|
||||
size_t size;
|
||||
u32 address;
|
||||
u64 data;
|
||||
};
|
||||
|
||||
static bool operator==(const WriteRecord& a, const WriteRecord& b) {
|
||||
return std::tie(a.size, a.address, a.data) == std::tie(b.size, b.address, b.data);
|
||||
}
|
||||
|
||||
static u64 jit_num_ticks = 0;
|
||||
static std::array<u32, 3000> code_mem{};
|
||||
static std::vector<WriteRecord> write_records;
|
||||
|
||||
static u64 GetTicksRemaining();
|
||||
static void AddTicks(u64 ticks);
|
||||
static bool IsReadOnlyMemory(u32 vaddr);
|
||||
static u8 MemoryRead8(u32 vaddr);
|
||||
static u16 MemoryRead16(u32 vaddr);
|
||||
static u32 MemoryRead32(u32 vaddr);
|
||||
static u64 MemoryRead64(u32 vaddr);
|
||||
static u32 MemoryReadCode(u32 vaddr);
|
||||
static void MemoryWrite8(u32 vaddr, u8 value);
|
||||
static void MemoryWrite16(u32 vaddr, u16 value);
|
||||
static void MemoryWrite32(u32 vaddr, u32 value);
|
||||
static void MemoryWrite64(u32 vaddr, u64 value);
|
||||
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
|
||||
static Dynarmic::A32::UserCallbacks GetUserCallbacks();
|
||||
|
||||
static u64 GetTicksRemaining() {
|
||||
return jit_num_ticks;
|
||||
}
|
||||
static void AddTicks(u64 ticks) {
|
||||
if (ticks > jit_num_ticks) {
|
||||
jit_num_ticks = 0;
|
||||
return;
|
||||
}
|
||||
jit_num_ticks -= ticks;
|
||||
}
|
||||
|
||||
static bool IsReadOnlyMemory(u32 vaddr) {
|
||||
return vaddr < code_mem.size();
|
||||
}
|
||||
static u8 MemoryRead8(u32 vaddr) {
|
||||
return static_cast<u8>(vaddr);
|
||||
}
|
||||
static u16 MemoryRead16(u32 vaddr) {
|
||||
return static_cast<u16>(vaddr);
|
||||
}
|
||||
static u32 MemoryRead32(u32 vaddr) {
|
||||
return vaddr;
|
||||
}
|
||||
static u64 MemoryRead64(u32 vaddr) {
|
||||
return MemoryRead32(vaddr) | (u64(MemoryRead32(vaddr+4)) << 32);
|
||||
}
|
||||
static u32 MemoryReadCode(u32 vaddr) {
|
||||
if (vaddr < code_mem.size() * sizeof(u32)) {
|
||||
size_t index = vaddr / sizeof(u32);
|
||||
return code_mem[index];
|
||||
}
|
||||
return 0xeafffffe; // b +#0
|
||||
}
|
||||
|
||||
static void MemoryWrite8(u32 vaddr, u8 value){
|
||||
write_records.push_back({8, vaddr, value});
|
||||
}
|
||||
static void MemoryWrite16(u32 vaddr, u16 value){
|
||||
write_records.push_back({16, vaddr, value});
|
||||
}
|
||||
static void MemoryWrite32(u32 vaddr, u32 value){
|
||||
write_records.push_back({32, vaddr, value});
|
||||
}
|
||||
static void MemoryWrite64(u32 vaddr, u64 value){
|
||||
write_records.push_back({64, vaddr, value});
|
||||
}
|
||||
|
||||
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
|
||||
ARMul_State interp_state{USER32MODE};
|
||||
interp_state.user_callbacks = GetUserCallbacks();
|
||||
interp_state.NumInstrsToExecute = 1;
|
||||
|
||||
interp_state.Reg = jit->Regs();
|
||||
interp_state.ExtReg = jit->ExtRegs();
|
||||
interp_state.Cpsr = jit->Cpsr();
|
||||
interp_state.VFP[VFP_FPSCR] = jit->Fpscr();
|
||||
interp_state.Reg[15] = pc;
|
||||
|
||||
InterpreterClearCache();
|
||||
InterpreterMainLoop(&interp_state);
|
||||
|
||||
bool T = Dynarmic::Common::Bit<5>(interp_state.Cpsr);
|
||||
interp_state.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
||||
|
||||
jit->Regs() = interp_state.Reg;
|
||||
jit->ExtRegs() = interp_state.ExtReg;
|
||||
jit->SetCpsr(interp_state.Cpsr);
|
||||
jit->SetFpscr(interp_state.VFP[VFP_FPSCR]);
|
||||
}
|
||||
|
||||
static void Fail() {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
|
||||
Dynarmic::A32::UserCallbacks user_callbacks{};
|
||||
user_callbacks.InterpreterFallback = &InterpreterFallback;
|
||||
user_callbacks.CallSVC = (void (*)(u32)) &Fail;
|
||||
user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
|
||||
user_callbacks.memory.Read8 = &MemoryRead8;
|
||||
user_callbacks.memory.Read16 = &MemoryRead16;
|
||||
user_callbacks.memory.Read32 = &MemoryRead32;
|
||||
user_callbacks.memory.Read64 = &MemoryRead64;
|
||||
user_callbacks.memory.ReadCode = &MemoryReadCode;
|
||||
user_callbacks.memory.Write8 = &MemoryWrite8;
|
||||
user_callbacks.memory.Write16 = &MemoryWrite16;
|
||||
user_callbacks.memory.Write32 = &MemoryWrite32;
|
||||
user_callbacks.memory.Write64 = &MemoryWrite64;
|
||||
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
|
||||
user_callbacks.AddTicks = &AddTicks;
|
||||
return user_callbacks;
|
||||
static Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
|
||||
Dynarmic::A32::UserConfig user_config;
|
||||
user_config.callbacks = testenv;
|
||||
return user_config;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -210,7 +95,9 @@ private:
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const std::vector<WriteRecord>& interp_write_records, const std::vector<WriteRecord>& jit_write_records) {
|
||||
using WriteRecords = std::map<u32, u8>;
|
||||
|
||||
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
|
||||
return interp.Reg == jit.Regs()
|
||||
&& interp.ExtReg == jit.ExtRegs()
|
||||
&& interp.Cpsr == jit.Cpsr()
|
||||
@@ -219,13 +106,15 @@ static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Ji
|
||||
}
|
||||
|
||||
void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) {
|
||||
ArmTestEnv test_env;
|
||||
|
||||
// Prepare memory
|
||||
code_mem.fill(0xEAFFFFFE); // b +#0
|
||||
test_env.code_mem.fill(0xEAFFFFFE); // b +#0
|
||||
|
||||
// Prepare test subjects
|
||||
ARMul_State interp{USER32MODE};
|
||||
interp.user_callbacks = GetUserCallbacks();
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
interp.user_callbacks = &test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
|
||||
for (size_t run_number = 0; run_number < run_count; run_number++) {
|
||||
interp.instruction_cache.clear();
|
||||
@@ -256,26 +145,25 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||
jit.ExtRegs() = initial_extregs;
|
||||
jit.SetFpscr(initial_fpscr);
|
||||
|
||||
std::generate_n(code_mem.begin(), instruction_count, instruction_generator);
|
||||
std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
|
||||
|
||||
// Run interpreter
|
||||
write_records.clear();
|
||||
std::vector<WriteRecord> interp_write_records;
|
||||
test_env.modified_memory.clear();
|
||||
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
|
||||
InterpreterMainLoop(&interp);
|
||||
interp_write_records = write_records;
|
||||
WriteRecords interp_write_records = test_env.modified_memory;
|
||||
{
|
||||
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
|
||||
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
||||
}
|
||||
|
||||
// Run jit
|
||||
write_records.clear();
|
||||
std::vector<WriteRecord> jit_write_records;
|
||||
test_env.modified_memory.clear();
|
||||
WriteRecords jit_write_records;
|
||||
try {
|
||||
jit_num_ticks = instructions_to_execute_count;
|
||||
test_env.ticks_left = instructions_to_execute_count;
|
||||
jit.Run();
|
||||
jit_write_records = write_records;
|
||||
jit_write_records = test_env.modified_memory;
|
||||
} catch (...) {
|
||||
printf("Caught something!\n");
|
||||
goto dump_state;
|
||||
@@ -289,7 +177,7 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||
|
||||
printf("\nInstruction Listing: \n");
|
||||
for (size_t i = 0; i < instruction_count; i++) {
|
||||
printf("%x: %s\n", code_mem[i], Dynarmic::A32::DisassembleArm(code_mem[i]).c_str());
|
||||
printf("%x: %s\n", test_env.code_mem[i], Dynarmic::A32::DisassembleArm(test_env.code_mem[i]).c_str());
|
||||
}
|
||||
|
||||
printf("\nInitial Register Listing: \n");
|
||||
@@ -317,21 +205,21 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||
|
||||
printf("\nInterp Write Records:\n");
|
||||
for (auto& record : interp_write_records) {
|
||||
printf("%zu [%x] = %" PRIx64 "\n", record.size, record.address, record.data);
|
||||
printf("[%08x] = %02x\n", record.first, record.second);
|
||||
}
|
||||
|
||||
printf("\nJIT Write Records:\n");
|
||||
for (auto& record : jit_write_records) {
|
||||
printf("%zu [%x] = %" PRIx64 "\n", record.size, record.address, record.data);
|
||||
printf("[%08x] = %02x\n", record.first, record.second);
|
||||
}
|
||||
|
||||
size_t num_insts = 0;
|
||||
while (num_insts < instructions_to_execute_count) {
|
||||
Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), Dynarmic::A32::PSR{}, Dynarmic::A32::FPSCR{}};
|
||||
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, &MemoryReadCode);
|
||||
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); });
|
||||
Dynarmic::Optimization::A32GetSetElimination(ir_block);
|
||||
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
||||
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, GetUserCallbacks().memory);
|
||||
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, &test_env);
|
||||
Dynarmic::Optimization::ConstantPropagation(ir_block);
|
||||
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
||||
Dynarmic::Optimization::VerificationPass(ir_block);
|
||||
@@ -353,7 +241,7 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
|
||||
TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm][A32]" ) {
|
||||
// This was a randomized test-case that was failing.
|
||||
//
|
||||
// IR produced for location {12, !T, !E} was:
|
||||
@@ -376,14 +264,15 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
|
||||
// Changing the EmitSet*Flag instruction to declare their arguments as UseScratch
|
||||
// solved this bug.
|
||||
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xe35f0cd9; // cmp pc, #55552
|
||||
code_mem[1] = 0xe11c0474; // tst r12, r4, ror r4
|
||||
code_mem[2] = 0xe1a006a7; // mov r0, r7, lsr #13
|
||||
code_mem[3] = 0xe35107fa; // cmp r1, #0x3E80000
|
||||
code_mem[4] = 0xe2a54c8a; // adc r4, r5, #35328
|
||||
code_mem[5] = 0xeafffffe; // b +#0
|
||||
ArmTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xe35f0cd9; // cmp pc, #55552
|
||||
test_env.code_mem[1] = 0xe11c0474; // tst r12, r4, ror r4
|
||||
test_env.code_mem[2] = 0xe1a006a7; // mov r0, r7, lsr #13
|
||||
test_env.code_mem[3] = 0xe35107fa; // cmp r1, #0x3E80000
|
||||
test_env.code_mem[4] = 0xe2a54c8a; // adc r4, r5, #35328
|
||||
test_env.code_mem[5] = 0xeafffffe; // b +#0
|
||||
|
||||
jit.Regs() = {
|
||||
0x6973b6bb, 0x267ea626, 0x69debf49, 0x8f976895, 0x4ecd2d0d, 0xcf89b8c7, 0xb6713f85, 0x15e2aa5,
|
||||
@@ -391,7 +280,7 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
|
||||
};
|
||||
jit.SetCpsr(0x000001d0); // User-mode
|
||||
|
||||
jit_num_ticks = 6;
|
||||
test_env.ticks_left = 6;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[0] == 0x00000af1 );
|
||||
@@ -413,16 +302,17 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
|
||||
REQUIRE( jit.Cpsr() == 0x200001d0 );
|
||||
}
|
||||
|
||||
TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
|
||||
TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm][A32]" ) {
|
||||
// This was a randomized test-case that was failing.
|
||||
//
|
||||
// The issue here was one of the words to be subtracted was 0x8000.
|
||||
// When the 2s complement was calculated by (~a + 1), it was 0x8000.
|
||||
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xe63dbf59; // shsax r11, sp, r9
|
||||
code_mem[1] = 0xeafffffe; // b +#0
|
||||
ArmTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xe63dbf59; // shsax r11, sp, r9
|
||||
test_env.code_mem[1] = 0xeafffffe; // b +#0
|
||||
|
||||
jit.Regs() = {
|
||||
0x3a3b8b18, 0x96156555, 0xffef039f, 0xafb946f2, 0x2030a69a, 0xafe09b2a, 0x896823c8, 0xabde0ded,
|
||||
@@ -430,7 +320,7 @@ TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
|
||||
};
|
||||
jit.SetCpsr(0x000001d0); // User-mode
|
||||
|
||||
jit_num_ticks = 2;
|
||||
test_env.ticks_left = 2;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[0] == 0x3a3b8b18 );
|
||||
@@ -452,22 +342,23 @@ TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
|
||||
REQUIRE( jit.Cpsr() == 0x000001d0 );
|
||||
}
|
||||
|
||||
TEST_CASE( "arm: uasx (Edge-case)", "[arm]" ) {
|
||||
TEST_CASE( "arm: uasx (Edge-case)", "[arm][A32]" ) {
|
||||
// UASX's Rm<31:16> == 0x0000.
|
||||
// An implementation that depends on addition overflow to detect
|
||||
// if diff >= 0 will fail this testcase.
|
||||
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xe6549f35; // uasx r9, r4, r5
|
||||
code_mem[1] = 0xeafffffe; // b +#0
|
||||
ArmTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xe6549f35; // uasx r9, r4, r5
|
||||
test_env.code_mem[1] = 0xeafffffe; // b +#0
|
||||
|
||||
jit.Regs()[4] = 0x8ed38f4c;
|
||||
jit.Regs()[5] = 0x0000261d;
|
||||
jit.Regs()[15] = 0x00000000;
|
||||
jit.SetCpsr(0x000001d0); // User-mode
|
||||
|
||||
jit_num_ticks = 2;
|
||||
test_env.ticks_left = 2;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[4] == 0x8ed38f4c );
|
||||
@@ -486,10 +377,11 @@ struct VfpTest {
|
||||
};
|
||||
|
||||
static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = instr;
|
||||
code_mem[1] = 0xeafffffe; // b +#0
|
||||
ArmTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = instr;
|
||||
test_env.code_mem[1] = 0xeafffffe; // b +#0
|
||||
|
||||
printf("vfp test 0x%08x\r", instr);
|
||||
|
||||
@@ -500,7 +392,7 @@ static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
|
||||
jit.ExtRegs()[6] = test.b;
|
||||
jit.SetFpscr(test.initial_fpscr);
|
||||
|
||||
jit_num_ticks = 2;
|
||||
test_env.ticks_left = 2;
|
||||
jit.Run();
|
||||
|
||||
const auto check = [&test, &jit](bool p) {
|
||||
@@ -524,21 +416,21 @@ static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("vfp: vadd", "[vfp]") {
|
||||
TEST_CASE("vfp: vadd", "[vfp][A32]") {
|
||||
// vadd.f32 s2, s4, s6
|
||||
RunVfpTests(0xEE321A03, {
|
||||
#include "vfp_vadd_f32.inc"
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("vfp: vsub", "[vfp]") {
|
||||
TEST_CASE("vfp: vsub", "[vfp][A32]") {
|
||||
// vsub.f32 s2, s4, s6
|
||||
RunVfpTests(0xEE321A43, {
|
||||
#include "vfp_vsub_f32.inc"
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("VFP: VMOV", "[JitX64][vfp]") {
|
||||
TEST_CASE("VFP: VMOV", "[JitX64][vfp][A32]") {
|
||||
const auto is_valid = [](u32 instr) -> bool {
|
||||
return Bits<0, 6>(instr) != 0b111111
|
||||
&& Bits<12, 15>(instr) != 0b1111
|
||||
@@ -562,7 +454,7 @@ TEST_CASE("VFP: VMOV", "[JitX64][vfp]") {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][vfp]") {
|
||||
TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][vfp][A32]") {
|
||||
const std::array<InstructionGenerator, 4> instructions = {{
|
||||
InstructionGenerator("1111000100000001000000e000000000"), // SETEND
|
||||
InstructionGenerator("cccc11101D110000dddd101z01M0mmmm"), // VMOV (reg)
|
||||
@@ -575,7 +467,7 @@ TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][vfp]") {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("VFP: VCMP", "[JitX64][vfp]") {
|
||||
TEST_CASE("VFP: VCMP", "[JitX64][vfp][A32]") {
|
||||
const std::array<InstructionGenerator, 2> instructions = {{
|
||||
InstructionGenerator("cccc11101D110100dddd101zE1M0mmmm"), // VCMP
|
||||
InstructionGenerator("cccc11101D110101dddd101zE1000000"), // VCMP (zero)
|
||||
@@ -586,7 +478,7 @@ TEST_CASE("VFP: VCMP", "[JitX64][vfp]") {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM data processing instructions", "[JitX64][A32]") {
|
||||
const std::array<InstructionGenerator, 16> imm_instructions = {{
|
||||
InstructionGenerator("cccc0010101Snnnnddddrrrrvvvvvvvv"),
|
||||
InstructionGenerator("cccc0010100Snnnnddddrrrrvvvvvvvv"),
|
||||
@@ -709,7 +601,7 @@ TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64][A32]") {
|
||||
auto EXD_valid = [](u32 inst) -> bool {
|
||||
return Bits<0, 3>(inst) % 2 == 0 && Bits<0, 3>(inst) != 14 && Bits<12, 15>(inst) != (Bits<0, 3>(inst) + 1);
|
||||
};
|
||||
@@ -808,7 +700,7 @@ TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64]"
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64][A32]") {
|
||||
const std::array<InstructionGenerator, 2> instructions = {{
|
||||
InstructionGenerator("cccc100pu0w1nnnnxxxxxxxxxxxxxxxx"), // LDM
|
||||
InstructionGenerator("cccc100pu0w0nnnnxxxxxxxxxxxxxxxx"), // STM
|
||||
@@ -849,7 +741,7 @@ TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64]") {
|
||||
FuzzJitArm(1, 1, 10000, instruction_select);
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM branch instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM branch instructions", "[JitX64][A32]") {
|
||||
const std::array<InstructionGenerator, 6> instructions = {{
|
||||
InstructionGenerator("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"),
|
||||
InstructionGenerator("cccc000100101111111111110011mmmm",
|
||||
@@ -864,7 +756,7 @@ TEST_CASE("Fuzz ARM branch instructions", "[JitX64]") {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM reversal instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM reversal instructions", "[JitX64][A32]") {
|
||||
const auto is_valid = [](u32 instr) -> bool {
|
||||
// R15 is UNPREDICTABLE
|
||||
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111;
|
||||
@@ -883,7 +775,7 @@ TEST_CASE("Fuzz ARM reversal instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM extension instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM extension instructions", "[JitX64][A32]") {
|
||||
const auto is_valid = [](u32 instr) -> bool {
|
||||
// R15 as Rd or Rm is UNPREDICTABLE
|
||||
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111;
|
||||
@@ -920,7 +812,7 @@ TEST_CASE("Fuzz ARM extension instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM multiply instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM multiply instructions", "[JitX64][A32]") {
|
||||
auto validate_d_m_n = [](u32 inst) -> bool {
|
||||
return Bits<16, 19>(inst) != 15 &&
|
||||
Bits<8, 11>(inst) != 15 &&
|
||||
@@ -970,7 +862,7 @@ TEST_CASE("Fuzz ARM multiply instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel]") {
|
||||
TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel][A32]") {
|
||||
const auto is_valid = [](u32 instr) -> bool {
|
||||
// R15 as Rd, Rn, or Rm is UNPREDICTABLE
|
||||
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111;
|
||||
@@ -1093,7 +985,7 @@ TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64][A32]") {
|
||||
auto validate_d_m_n = [](u32 inst) -> bool {
|
||||
return Bits<16, 19>(inst) != 15 &&
|
||||
Bits<8, 11>(inst) != 15 &&
|
||||
@@ -1116,10 +1008,11 @@ TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE( "SMUAD", "[JitX64]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xE700F211; // smuad r0, r1, r2
|
||||
TEST_CASE( "SMUAD", "[JitX64][A32]" ) {
|
||||
ArmTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xE700F211; // smuad r0, r1, r2
|
||||
|
||||
jit.Regs() = {
|
||||
0, // Rd
|
||||
@@ -1132,7 +1025,7 @@ TEST_CASE( "SMUAD", "[JitX64]" ) {
|
||||
};
|
||||
jit.SetCpsr(0x000001d0); // User-mode
|
||||
|
||||
jit_num_ticks = 6;
|
||||
test_env.ticks_left = 6;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE(jit.Regs()[0] == 0x80000000);
|
||||
@@ -1141,7 +1034,7 @@ TEST_CASE( "SMUAD", "[JitX64]" ) {
|
||||
REQUIRE(jit.Cpsr() == 0x080001d0);
|
||||
}
|
||||
|
||||
TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") {
|
||||
TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp][A32]") {
|
||||
const auto is_valid = [](u32 instr) -> bool {
|
||||
auto regs = (instr & 0x100) ? (Bits<0, 7>(instr) >> 1) : Bits<0, 7>(instr);
|
||||
auto base = Bits<12, 15>(instr);
|
||||
@@ -1165,7 +1058,7 @@ TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Test ARM misc instructions", "[JitX64]") {
|
||||
TEST_CASE("Test ARM misc instructions", "[JitX64][A32]") {
|
||||
const auto is_clz_valid = [](u32 instr) -> bool {
|
||||
// R15 as Rd, or Rm is UNPREDICTABLE
|
||||
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111;
|
||||
@@ -1180,7 +1073,7 @@ TEST_CASE("Test ARM misc instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test ARM MSR instructions", "[JitX64]") {
|
||||
TEST_CASE("Test ARM MSR instructions", "[JitX64][A32]") {
|
||||
const auto is_msr_valid = [](u32 instr) -> bool {
|
||||
return Bits<18, 19>(instr) != 0;
|
||||
};
|
||||
@@ -1212,7 +1105,7 @@ TEST_CASE("Test ARM MSR instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64][A32]") {
|
||||
auto is_valid = [](u32 inst) -> bool {
|
||||
// R15 as Rd, Rn, or Rm is UNPREDICTABLE
|
||||
return Bits<16, 19>(inst) != 0b1111 &&
|
||||
@@ -1234,7 +1127,7 @@ TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM saturation instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM saturation instructions", "[JitX64][A32]") {
|
||||
auto is_valid = [](u32 inst) -> bool {
|
||||
// R15 as Rd or Rn is UNPREDICTABLE
|
||||
return Bits<12, 15>(inst) != 0b1111 &&
|
||||
@@ -1253,7 +1146,7 @@ TEST_CASE("Fuzz ARM saturation instructions", "[JitX64]") {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") {
|
||||
TEST_CASE("Fuzz ARM packing instructions", "[JitX64][A32]") {
|
||||
auto is_pkh_valid = [](u32 inst) -> bool {
|
||||
// R15 as Rd, Rn, or Rm is UNPREDICTABLE
|
||||
return Bits<16, 19>(inst) != 0b1111 &&
|
||||
@@ -1273,18 +1166,19 @@ TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xe3a00005; // mov r0, #5
|
||||
code_mem[1] = 0xe3a0100D; // mov r1, #13
|
||||
code_mem[2] = 0xe0812000; // add r2, r1, r0
|
||||
code_mem[3] = 0xeafffffe; // b +#0 (infinite loop)
|
||||
TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") {
|
||||
ArmTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xe3a00005; // mov r0, #5
|
||||
test_env.code_mem[1] = 0xe3a0100D; // mov r1, #13
|
||||
test_env.code_mem[2] = 0xe0812000; // add r2, r1, r0
|
||||
test_env.code_mem[3] = 0xeafffffe; // b +#0 (infinite loop)
|
||||
|
||||
jit.Regs() = {};
|
||||
jit.SetCpsr(0x000001d0); // User-mode
|
||||
|
||||
jit_num_ticks = 4;
|
||||
test_env.ticks_left = 4;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE(jit.Regs()[0] == 5);
|
||||
@@ -1294,13 +1188,13 @@ TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") {
|
||||
REQUIRE(jit.Cpsr() == 0x000001d0);
|
||||
|
||||
// Change the code
|
||||
code_mem[1] = 0xe3a01007; // mov r1, #7
|
||||
test_env.code_mem[1] = 0xe3a01007; // mov r1, #7
|
||||
jit.InvalidateCacheRange(/*start_memory_location = */ 4, /* length_in_bytes = */ 4);
|
||||
|
||||
// Reset position of PC
|
||||
jit.Regs()[15] = 0;
|
||||
|
||||
jit_num_ticks = 4;
|
||||
test_env.ticks_left = 4;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE(jit.Regs()[0] == 5);
|
||||
|
||||
@@ -26,133 +26,17 @@
|
||||
#include "frontend/ir/basic_block.h"
|
||||
#include "ir_opt/passes.h"
|
||||
#include "rand_int.h"
|
||||
#include "testenv.h"
|
||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
||||
|
||||
struct WriteRecord {
|
||||
size_t size;
|
||||
u32 address;
|
||||
u64 data;
|
||||
};
|
||||
|
||||
static bool operator==(const WriteRecord& a, const WriteRecord& b) {
|
||||
return std::tie(a.size, a.address, a.data) == std::tie(b.size, b.address, b.data);
|
||||
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
||||
Dynarmic::A32::UserConfig user_config;
|
||||
user_config.callbacks = testenv;
|
||||
return user_config;
|
||||
}
|
||||
|
||||
static u64 jit_num_ticks = 0;
|
||||
static std::array<u16, 3000> code_mem{};
|
||||
static std::vector<WriteRecord> write_records;
|
||||
|
||||
static u64 GetTicksRemaining();
|
||||
static void AddTicks(u64 ticks);
|
||||
static bool IsReadOnlyMemory(u32 vaddr);
|
||||
static u8 MemoryRead8(u32 vaddr);
|
||||
static u16 MemoryRead16(u32 vaddr);
|
||||
static u32 MemoryRead32(u32 vaddr);
|
||||
static u64 MemoryRead64(u32 vaddr);
|
||||
static void MemoryWrite8(u32 vaddr, u8 value);
|
||||
static void MemoryWrite16(u32 vaddr, u16 value);
|
||||
static void MemoryWrite32(u32 vaddr, u32 value);
|
||||
static void MemoryWrite64(u32 vaddr, u64 value);
|
||||
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
|
||||
static Dynarmic::A32::UserCallbacks GetUserCallbacks();
|
||||
|
||||
static u64 GetTicksRemaining() {
|
||||
return jit_num_ticks;
|
||||
}
|
||||
static void AddTicks(u64 ticks) {
|
||||
if (ticks > jit_num_ticks) {
|
||||
jit_num_ticks = 0;
|
||||
return;
|
||||
}
|
||||
jit_num_ticks -= ticks;
|
||||
}
|
||||
|
||||
static bool IsReadOnlyMemory(u32 vaddr) {
|
||||
return vaddr < code_mem.size();
|
||||
}
|
||||
static u8 MemoryRead8(u32 vaddr) {
|
||||
return static_cast<u8>(vaddr);
|
||||
}
|
||||
static u16 MemoryRead16(u32 vaddr) {
|
||||
return static_cast<u16>(vaddr);
|
||||
}
|
||||
static u32 MemoryRead32(u32 vaddr) {
|
||||
if (vaddr < code_mem.size() * sizeof(u16)) {
|
||||
size_t index = vaddr / sizeof(u16);
|
||||
if (index + 1 >= code_mem.size())
|
||||
return code_mem[index];
|
||||
return code_mem[index] | (code_mem[index+1] << 16);
|
||||
}
|
||||
return vaddr;
|
||||
}
|
||||
static u64 MemoryRead64(u32 vaddr) {
|
||||
return vaddr;
|
||||
}
|
||||
static u32 MemoryReadCode(u32 vaddr) {
|
||||
if (vaddr < code_mem.size() * sizeof(u16)) {
|
||||
size_t index = vaddr / sizeof(u16);
|
||||
if (index + 1 >= code_mem.size())
|
||||
return code_mem[index];
|
||||
return code_mem[index] | (code_mem[index + 1] << 16);
|
||||
}
|
||||
return 0xE7FEE7FE; // b +#0, b +#0
|
||||
}
|
||||
|
||||
static void MemoryWrite8(u32 vaddr, u8 value){
|
||||
write_records.push_back({8, vaddr, value});
|
||||
}
|
||||
static void MemoryWrite16(u32 vaddr, u16 value){
|
||||
write_records.push_back({16, vaddr, value});
|
||||
}
|
||||
static void MemoryWrite32(u32 vaddr, u32 value){
|
||||
write_records.push_back({32, vaddr, value});
|
||||
}
|
||||
static void MemoryWrite64(u32 vaddr, u64 value){
|
||||
write_records.push_back({64, vaddr, value});
|
||||
}
|
||||
|
||||
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
|
||||
ARMul_State interp_state{USER32MODE};
|
||||
interp_state.user_callbacks = GetUserCallbacks();
|
||||
interp_state.NumInstrsToExecute = 1;
|
||||
|
||||
interp_state.Reg = jit->Regs();
|
||||
interp_state.Cpsr = jit->Cpsr();
|
||||
interp_state.Reg[15] = pc;
|
||||
|
||||
InterpreterClearCache();
|
||||
InterpreterMainLoop(&interp_state);
|
||||
|
||||
bool T = Dynarmic::Common::Bit<5>(interp_state.Cpsr);
|
||||
interp_state.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
||||
|
||||
jit->Regs() = interp_state.Reg;
|
||||
jit->SetCpsr(interp_state.Cpsr);
|
||||
}
|
||||
|
||||
static void Fail() {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
|
||||
Dynarmic::A32::UserCallbacks user_callbacks{};
|
||||
user_callbacks.InterpreterFallback = &InterpreterFallback;
|
||||
user_callbacks.CallSVC = (void (*)(u32)) &Fail;
|
||||
user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
|
||||
user_callbacks.memory.Read8 = &MemoryRead8;
|
||||
user_callbacks.memory.Read16 = &MemoryRead16;
|
||||
user_callbacks.memory.Read32 = &MemoryRead32;
|
||||
user_callbacks.memory.Read64 = &MemoryRead64;
|
||||
user_callbacks.memory.ReadCode = &MemoryReadCode;
|
||||
user_callbacks.memory.Write8 = &MemoryWrite8;
|
||||
user_callbacks.memory.Write16 = &MemoryWrite16;
|
||||
user_callbacks.memory.Write32 = &MemoryWrite32;
|
||||
user_callbacks.memory.Write64 = &MemoryWrite64;
|
||||
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
|
||||
user_callbacks.AddTicks = &AddTicks;
|
||||
return user_callbacks;
|
||||
}
|
||||
using WriteRecords = std::map<u32, u8>;
|
||||
|
||||
struct ThumbInstGen final {
|
||||
public:
|
||||
@@ -193,7 +77,7 @@ private:
|
||||
std::function<bool(u16)> is_valid;
|
||||
};
|
||||
|
||||
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const std::vector<WriteRecord>& interp_write_records, const std::vector<WriteRecord>& jit_write_records) {
|
||||
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, WriteRecords& interp_write_records, WriteRecords& jit_write_records) {
|
||||
const auto interp_regs = interp.Reg;
|
||||
const auto jit_regs = jit.Regs();
|
||||
|
||||
@@ -202,7 +86,7 @@ static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Ji
|
||||
&& interp_write_records == jit_write_records;
|
||||
}
|
||||
|
||||
static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::Jit& jit, const std::array<u32, 16>& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
|
||||
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State& interp, Dynarmic::A32::Jit& jit, const std::array<u32, 16>& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
|
||||
interp.instruction_cache.clear();
|
||||
InterpreterClearCache();
|
||||
jit.ClearCache();
|
||||
@@ -215,20 +99,20 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
|
||||
jit.Regs() = initial_regs;
|
||||
|
||||
// Run interpreter
|
||||
write_records.clear();
|
||||
test_env.modified_memory.clear();
|
||||
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
|
||||
InterpreterMainLoop(&interp);
|
||||
auto interp_write_records = write_records;
|
||||
auto interp_write_records = test_env.modified_memory;
|
||||
{
|
||||
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
|
||||
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
||||
}
|
||||
|
||||
// Run jit
|
||||
write_records.clear();
|
||||
jit_num_ticks = instructions_to_execute_count;
|
||||
test_env.modified_memory.clear();
|
||||
test_env.ticks_left = instructions_to_execute_count;
|
||||
jit.Run();
|
||||
auto jit_write_records = write_records;
|
||||
auto jit_write_records = test_env.modified_memory;
|
||||
|
||||
// Compare
|
||||
if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) {
|
||||
@@ -236,7 +120,7 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
|
||||
|
||||
printf("\nInstruction Listing: \n");
|
||||
for (size_t i = 0; i < instruction_count; i++) {
|
||||
printf("%04x %s\n", code_mem[i], Dynarmic::A32::DisassembleThumb16(code_mem[i]).c_str());
|
||||
printf("%04x %s\n", test_env.code_mem[i], Dynarmic::A32::DisassembleThumb16(test_env.code_mem[i]).c_str());
|
||||
}
|
||||
|
||||
printf("\nInitial Register Listing: \n");
|
||||
@@ -253,12 +137,12 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
|
||||
|
||||
printf("\nInterp Write Records:\n");
|
||||
for (auto& record : interp_write_records) {
|
||||
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data);
|
||||
printf("[%08x] = %02x\n", record.first, record.second);
|
||||
}
|
||||
|
||||
printf("\nJIT Write Records:\n");
|
||||
for (auto& record : jit_write_records) {
|
||||
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data);
|
||||
printf("[%08x] = %02x\n", record.first, record.second);
|
||||
}
|
||||
|
||||
Dynarmic::A32::PSR cpsr;
|
||||
@@ -267,10 +151,10 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
|
||||
size_t num_insts = 0;
|
||||
while (num_insts < instructions_to_execute_count) {
|
||||
Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, Dynarmic::A32::FPSCR{}};
|
||||
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, &MemoryReadCode);
|
||||
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); });
|
||||
Dynarmic::Optimization::A32GetSetElimination(ir_block);
|
||||
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
||||
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, GetUserCallbacks().memory);
|
||||
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, &test_env);
|
||||
Dynarmic::Optimization::ConstantPropagation(ir_block);
|
||||
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
||||
Dynarmic::Optimization::VerificationPass(ir_block);
|
||||
@@ -287,22 +171,24 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
|
||||
}
|
||||
|
||||
void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
|
||||
ThumbTestEnv test_env;
|
||||
|
||||
// Prepare memory
|
||||
code_mem.fill(0xE7FE); // b +#0
|
||||
test_env.code_mem.fill(0xE7FE); // b +#0
|
||||
|
||||
// Prepare test subjects
|
||||
ARMul_State interp{USER32MODE};
|
||||
interp.user_callbacks = GetUserCallbacks();
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
interp.user_callbacks = &test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
|
||||
for (size_t run_number = 0; run_number < run_count; run_number++) {
|
||||
std::array<u32, 16> initial_regs;
|
||||
std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
||||
initial_regs[15] = 0;
|
||||
|
||||
std::generate_n(code_mem.begin(), instruction_count, instruction_generator);
|
||||
std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
|
||||
|
||||
RunInstance(run_number, interp, jit, initial_regs, instruction_count, instructions_to_execute_count);
|
||||
RunInstance(run_number, test_env, interp, jit, initial_regs, instruction_count, instructions_to_execute_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,13 +274,15 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
||||
}
|
||||
|
||||
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
|
||||
ThumbTestEnv test_env;
|
||||
|
||||
// Prepare memory
|
||||
code_mem.fill(0xE7FE); // b +#0
|
||||
test_env.code_mem.fill(0xE7FE); // b +#0
|
||||
|
||||
// Prepare test subjects
|
||||
ARMul_State interp{USER32MODE};
|
||||
interp.user_callbacks = GetUserCallbacks();
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
interp.user_callbacks = &test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
|
||||
std::array<u32, 16> initial_regs {
|
||||
0xe90ecd70,
|
||||
@@ -415,11 +303,11 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
code_mem[0] = 0x40B8; // lsls r0, r7, #0
|
||||
code_mem[1] = 0x01CA; // lsls r2, r1, #7
|
||||
code_mem[2] = 0x83A1; // strh r1, [r4, #28]
|
||||
code_mem[3] = 0x708A; // strb r2, [r1, #2]
|
||||
code_mem[4] = 0xBCC4; // pop {r2, r6, r7}
|
||||
test_env.code_mem[0] = 0x40B8; // lsls r0, r7, #0
|
||||
test_env.code_mem[1] = 0x01CA; // lsls r2, r1, #7
|
||||
test_env.code_mem[2] = 0x83A1; // strh r1, [r4, #28]
|
||||
test_env.code_mem[3] = 0x708A; // strb r2, [r1, #2]
|
||||
test_env.code_mem[4] = 0xBCC4; // pop {r2, r6, r7}
|
||||
|
||||
RunInstance(1, interp, jit, initial_regs, 5, 5);
|
||||
RunInstance(1, test_env, interp, jit, initial_regs, 5, 5);
|
||||
}
|
||||
|
||||
@@ -803,7 +803,7 @@ enum {
|
||||
|
||||
static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) {
|
||||
unsigned int inst_size = 4;
|
||||
unsigned int inst = (*cpu->user_callbacks.memory.ReadCode)(phys_addr & 0xFFFFFFFC);
|
||||
unsigned int inst = cpu->user_callbacks->MemoryReadCode(phys_addr & 0xFFFFFFFC);
|
||||
|
||||
// If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
|
||||
if (cpu->TFlag) {
|
||||
@@ -3506,7 +3506,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
|
||||
swi_inst* const inst_cream = (swi_inst*)inst_base->component;
|
||||
// SVC::CallSVC(inst_cream->num & 0xFFFF);
|
||||
(*cpu->user_callbacks.CallSVC)(inst_cream->num & 0xFFFF);
|
||||
cpu->user_callbacks->CallSVC(inst_cream->num & 0xFFFF);
|
||||
}
|
||||
|
||||
cpu->Reg[15] += cpu->GetInstructionSize();
|
||||
|
||||
@@ -204,14 +204,14 @@ u8 ARMul_State::ReadMemory8(u32 address) const
|
||||
{
|
||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
||||
|
||||
return (*user_callbacks.memory.Read8)(address);
|
||||
return user_callbacks->MemoryRead8(address);
|
||||
}
|
||||
|
||||
u16 ARMul_State::ReadMemory16(u32 address) const
|
||||
{
|
||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
||||
|
||||
u16 data = (*user_callbacks.memory.Read16)(address);
|
||||
u16 data = user_callbacks->MemoryRead16(address);
|
||||
|
||||
if (InBigEndianMode())
|
||||
data = Common::swap16(data);
|
||||
@@ -223,7 +223,7 @@ u32 ARMul_State::ReadMemory32(u32 address) const
|
||||
{
|
||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
||||
|
||||
u32 data = (*user_callbacks.memory.Read32)(address);
|
||||
u32 data = user_callbacks->MemoryRead32(address);
|
||||
|
||||
if (InBigEndianMode())
|
||||
data = Common::swap32(data);
|
||||
@@ -235,7 +235,7 @@ u64 ARMul_State::ReadMemory64(u32 address) const
|
||||
{
|
||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
||||
|
||||
u64 data = (*user_callbacks.memory.Read64)(address);
|
||||
u64 data = user_callbacks->MemoryRead64(address);
|
||||
|
||||
if (InBigEndianMode())
|
||||
data = Common::swap64(data);
|
||||
@@ -247,7 +247,7 @@ void ARMul_State::WriteMemory8(u32 address, u8 data)
|
||||
{
|
||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
|
||||
|
||||
(*user_callbacks.memory.Write8)(address, data);
|
||||
user_callbacks->MemoryWrite8(address, data);
|
||||
}
|
||||
|
||||
void ARMul_State::WriteMemory16(u32 address, u16 data)
|
||||
@@ -257,7 +257,7 @@ void ARMul_State::WriteMemory16(u32 address, u16 data)
|
||||
if (InBigEndianMode())
|
||||
data = Common::swap16(data);
|
||||
|
||||
(*user_callbacks.memory.Write16)(address, data);
|
||||
user_callbacks->MemoryWrite16(address, data);
|
||||
}
|
||||
|
||||
void ARMul_State::WriteMemory32(u32 address, u32 data)
|
||||
@@ -267,7 +267,7 @@ void ARMul_State::WriteMemory32(u32 address, u32 data)
|
||||
if (InBigEndianMode())
|
||||
data = Common::swap32(data);
|
||||
|
||||
(*user_callbacks.memory.Write32)(address, data);
|
||||
user_callbacks->MemoryWrite32(address, data);
|
||||
}
|
||||
|
||||
void ARMul_State::WriteMemory64(u32 address, u64 data)
|
||||
@@ -277,7 +277,7 @@ void ARMul_State::WriteMemory64(u32 address, u64 data)
|
||||
if (InBigEndianMode())
|
||||
data = Common::swap64(data);
|
||||
|
||||
(*user_callbacks.memory.Write64)(address, data);
|
||||
user_callbacks->MemoryWrite64(address, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <dynarmic/A32/callbacks.h>
|
||||
#include <dynarmic/A32/config.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "A32/skyeye_interpreter/skyeye_common/arm_regformat.h"
|
||||
@@ -252,5 +252,5 @@ public:
|
||||
u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode
|
||||
bool exclusive_state;
|
||||
|
||||
Dynarmic::A32::UserCallbacks user_callbacks;
|
||||
Dynarmic::A32::UserCallbacks* user_callbacks;
|
||||
};
|
||||
|
||||
@@ -11,77 +11,27 @@
|
||||
#include "common/common_types.h"
|
||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
||||
#include "testenv.h"
|
||||
|
||||
static u64 jit_num_ticks = 0;
|
||||
static std::array<u16, 1024> code_mem{};
|
||||
|
||||
static u64 GetTicksRemaining();
|
||||
static void AddTicks(u64 ticks);
|
||||
static u32 MemoryRead32(u32 vaddr);
|
||||
static u32 MemoryReadCode(u32 vaddr);
|
||||
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
|
||||
static Dynarmic::A32::UserCallbacks GetUserCallbacks();
|
||||
|
||||
static u64 GetTicksRemaining() {
|
||||
return jit_num_ticks;
|
||||
}
|
||||
static void AddTicks(u64 ticks) {
|
||||
if (ticks > jit_num_ticks) {
|
||||
jit_num_ticks = 0;
|
||||
return;
|
||||
}
|
||||
jit_num_ticks -= ticks;
|
||||
}
|
||||
|
||||
static u32 MemoryRead32(u32 vaddr) {
|
||||
return vaddr;
|
||||
}
|
||||
static u32 MemoryReadCode(u32 vaddr) {
|
||||
if (vaddr < code_mem.size() * sizeof(u16)) {
|
||||
size_t index = vaddr / sizeof(u16);
|
||||
return code_mem[index] | (code_mem[index+1] << 16);
|
||||
}
|
||||
return 0xE7FEE7FE; //b +#0, b +#0
|
||||
}
|
||||
|
||||
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
|
||||
ARMul_State interp_state{USER32MODE};
|
||||
interp_state.user_callbacks = GetUserCallbacks();
|
||||
interp_state.NumInstrsToExecute = 1;
|
||||
|
||||
interp_state.Reg = jit->Regs();
|
||||
interp_state.Cpsr = jit->Cpsr();
|
||||
interp_state.Reg[15] = pc;
|
||||
|
||||
InterpreterClearCache();
|
||||
InterpreterMainLoop(&interp_state);
|
||||
|
||||
jit->Regs() = interp_state.Reg;
|
||||
jit->SetCpsr(interp_state.Cpsr);
|
||||
}
|
||||
|
||||
static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
|
||||
Dynarmic::A32::UserCallbacks user_callbacks{};
|
||||
user_callbacks.memory.Read32 = &MemoryRead32;
|
||||
user_callbacks.memory.ReadCode = &MemoryReadCode;
|
||||
user_callbacks.InterpreterFallback = &InterpreterFallback;
|
||||
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
|
||||
user_callbacks.AddTicks = &AddTicks;
|
||||
return user_callbacks;
|
||||
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
||||
Dynarmic::A32::UserConfig user_config;
|
||||
user_config.callbacks = testenv;
|
||||
return user_config;
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0x0088; // lsls r0, r1, #2
|
||||
code_mem[1] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0x0088; // lsls r0, r1, #2
|
||||
test_env.code_mem[1] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[0] = 1;
|
||||
jit.Regs()[1] = 2;
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[0] == 8 );
|
||||
@@ -91,17 +41,18 @@ TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0x07C8; // lsls r0, r1, #31
|
||||
code_mem[1] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0x07C8; // lsls r0, r1, #31
|
||||
test_env.code_mem[1] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[0] = 1;
|
||||
jit.Regs()[1] = 0xFFFFFFFF;
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[0] == 0x80000000 );
|
||||
@@ -111,16 +62,17 @@ TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xBADC; // revsh r4, r3
|
||||
code_mem[1] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xBADC; // revsh r4, r3
|
||||
test_env.code_mem[1] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[3] = 0x12345678;
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[3] == 0x12345678 );
|
||||
@@ -130,33 +82,35 @@ TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0x69DB; // ldr r3, [r3, #28]
|
||||
code_mem[1] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0x69DB; // ldr r3, [r3, #28]
|
||||
test_env.code_mem[1] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[3] = 0x12345678;
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[3] == 0x12345694 );
|
||||
REQUIRE( jit.Regs()[3] == 0x97969594 ); // Memory location 0x12345694
|
||||
REQUIRE( jit.Regs()[15] == 2 );
|
||||
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xF010; code_mem[1] = 0xEC3E; // blx +#67712
|
||||
code_mem[2] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xF010; test_env.code_mem[1] = 0xEC3E; // blx +#67712
|
||||
test_env.code_mem[2] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[14] == (0x4 | 1) );
|
||||
@@ -165,15 +119,16 @@ TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xF039; code_mem[1] = 0xFA2A; // bl +#234584
|
||||
code_mem[2] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xF039; test_env.code_mem[1] = 0xFA2A; // bl +#234584
|
||||
test_env.code_mem[2] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[14] == (0x4 | 1) );
|
||||
@@ -182,15 +137,16 @@ TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: bl -#42", "[thumb]" ) {
|
||||
Dynarmic::A32::Jit jit{GetUserCallbacks()};
|
||||
code_mem.fill({});
|
||||
code_mem[0] = 0xF7FF; code_mem[1] = 0xFFE9; // bl -#42
|
||||
code_mem[2] = 0xE7FE; // b +#0
|
||||
ThumbTestEnv test_env;
|
||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||
test_env.code_mem.fill({});
|
||||
test_env.code_mem[0] = 0xF7FF; test_env.code_mem[1] = 0xFFE9; // bl -#42
|
||||
test_env.code_mem[2] = 0xE7FE; // b +#0
|
||||
|
||||
jit.Regs()[15] = 0; // PC = 0
|
||||
jit.SetCpsr(0x00000030); // Thumb, User-mode
|
||||
|
||||
jit_num_ticks = 1;
|
||||
test_env.ticks_left = 1;
|
||||
jit.Run();
|
||||
|
||||
REQUIRE( jit.Regs()[14] == (0x4 | 1) );
|
||||
|
||||
93
tests/A32/testenv.h
Normal file
93
tests/A32/testenv.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
|
||||
#include <dynarmic/A32/a32.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
template <typename InstructionType, u32 infinite_loop>
|
||||
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
u64 ticks_left = 0;
|
||||
bool code_mem_modified_by_guest = false;
|
||||
std::array<InstructionType, 2048> code_mem{};
|
||||
std::map<u32, u8> modified_memory;
|
||||
|
||||
std::uint32_t MemoryReadCode(u32 vaddr) override {
|
||||
const size_t index = vaddr / sizeof(InstructionType);
|
||||
if (index < code_mem.size()) {
|
||||
u32 value;
|
||||
std::memcpy(&value, &code_mem[index], sizeof(u32));
|
||||
return value;
|
||||
}
|
||||
return infinite_loop; // B .
|
||||
}
|
||||
|
||||
std::uint8_t MemoryRead8(u32 vaddr) override {
|
||||
if (vaddr < sizeof(InstructionType) * code_mem.size()) {
|
||||
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
|
||||
}
|
||||
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return static_cast<u8>(vaddr);
|
||||
}
|
||||
std::uint16_t MemoryRead16(u32 vaddr) override {
|
||||
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
|
||||
}
|
||||
std::uint32_t MemoryRead32(u32 vaddr) override {
|
||||
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
|
||||
}
|
||||
std::uint64_t MemoryRead64(u32 vaddr) override {
|
||||
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
|
||||
}
|
||||
|
||||
void MemoryWrite8(u32 vaddr, std::uint8_t value) override {
|
||||
if (vaddr < code_mem.size() * sizeof(u32)) {
|
||||
code_mem_modified_by_guest = true;
|
||||
}
|
||||
modified_memory[vaddr] = value;
|
||||
}
|
||||
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
|
||||
MemoryWrite8(vaddr, static_cast<u8>(value));
|
||||
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
|
||||
}
|
||||
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
|
||||
MemoryWrite16(vaddr, static_cast<u16>(value));
|
||||
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
|
||||
}
|
||||
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
|
||||
MemoryWrite32(vaddr, static_cast<u32>(value));
|
||||
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback(%08x, %zu)", pc, num_instructions); }
|
||||
|
||||
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC(%u)", swi); }
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised(%08x)", pc); }
|
||||
|
||||
void AddTicks(std::uint64_t ticks) override {
|
||||
if (ticks > ticks_left) {
|
||||
ticks_left = 0;
|
||||
return;
|
||||
}
|
||||
ticks_left -= ticks;
|
||||
}
|
||||
std::uint64_t GetTicksRemaining() override {
|
||||
return ticks_left;
|
||||
}
|
||||
};
|
||||
|
||||
using ArmTestEnv = A32TestEnv<u32, 0xEAFFFFFE>;
|
||||
using ThumbTestEnv = A32TestEnv<u16, 0xE7FEE7FE>;
|
||||
@@ -24,6 +24,7 @@ add_executable(dynarmic_tests
|
||||
A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp
|
||||
A32/test_arm_disassembler.cpp
|
||||
A32/test_thumb_instructions.cpp
|
||||
A32/testenv.h
|
||||
A64/a64.cpp
|
||||
A64/inst_gen.cpp
|
||||
A64/inst_gen.h
|
||||
|
||||
Reference in New Issue
Block a user