From b4ee976a6f7464b568f8595d41ef255a02a7f246 Mon Sep 17 00:00:00 2001 From: Jeremy Van de woestyne Date: Sun, 21 Jun 2020 19:29:55 +0200 Subject: [PATCH] unit tests & various fixes --- tests/A32/test_arm_instructions.cpp | 34 ++++++++++ tests/A32/testenv.h | 83 +++++++++++++++++++++++++ tests/A64/a64.cpp | 43 +++++++++++++ tests/A64/testenv.h | 96 +++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+) diff --git a/tests/A32/test_arm_instructions.cpp b/tests/A32/test_arm_instructions.cpp index 450a6813..fcfdf07b 100644 --- a/tests/A32/test_arm_instructions.cpp +++ b/tests/A32/test_arm_instructions.cpp @@ -525,3 +525,37 @@ TEST_CASE("arm: vcvt.s16.f64", "[arm][A32]") { REQUIRE(jit.ExtRegs()[16] == 0xffff8000); REQUIRE(jit.ExtRegs()[17] == 0xffffffff); } + +TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") { + constexpr size_t address_width = 12; + constexpr size_t memory_size = 1ull << address_width; // 4K + constexpr size_t page_size = 4 * 1024; + constexpr size_t buffer_size = 2 * page_size; + char buffer[buffer_size]; + + void* buffer_ptr = reinterpret_cast(buffer); + size_t buffer_size_nconst = buffer_size; + char* backing_memory = reinterpret_cast(std::align(page_size, memory_size, buffer_ptr, buffer_size_nconst)); + + A32FastmemTestEnv env{backing_memory}; + Dynarmic::A32::UserConfig config{&env}; + config.fastmem_pointer = backing_memory; + config.recompile_on_fastmem_failure = false; + config.processor_id = 0; + + Dynarmic::A32::Jit jit{config}; + memset(backing_memory, 0, memory_size); + memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57); + + env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0] + env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1] + env.MemoryWrite32(8, 0xEAFFFFFE); // B . + jit.Regs()[0] = 0x100; + jit.Regs()[1] = 0x1F0; + jit.Regs()[15] = 0; // PC = 0 + jit.SetCpsr(0x000001d0); // User-mode + env.ticks_left = 3; + + jit.Run(); + REQUIRE(strncmp(backing_memory + 0x100, backing_memory + 0x1F0, 4) == 0); +} diff --git a/tests/A32/testenv.h b/tests/A32/testenv.h index 1a1c6520..9a35b885 100644 --- a/tests/A32/testenv.h +++ b/tests/A32/testenv.h @@ -114,3 +114,86 @@ public: using ArmTestEnv = A32TestEnv; using ThumbTestEnv = A32TestEnv; + +class A32FastmemTestEnv final : public Dynarmic::A32::UserCallbacks { +public: + u64 ticks_left = 0; + char* backing_memory = nullptr; + + explicit A32FastmemTestEnv(char* addr) : backing_memory(addr) {} + + template + T read(std::uint32_t vaddr) { + T value; + memcpy(&value, backing_memory + vaddr, sizeof(T)); + return value; + } + template + void write(std::uint32_t vaddr, const T& value) { + memcpy(backing_memory + vaddr, &value, sizeof(T)); + } + + std::uint32_t MemoryReadCode(std::uint32_t vaddr) override { + return read(vaddr); + } + + std::uint8_t MemoryRead8(std::uint32_t vaddr) override { + return read(vaddr); + } + std::uint16_t MemoryRead16(std::uint32_t vaddr) override { + return read(vaddr); + } + std::uint32_t MemoryRead32(std::uint32_t vaddr) override { + return read(vaddr); + } + std::uint64_t MemoryRead64(std::uint32_t vaddr) override { + return read(vaddr); + } + + void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override { + write(vaddr, value); + } + void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override { + write(vaddr, value); + } + void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override { + write(vaddr, value); + } + void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override { + write(vaddr, value); + } + + bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { + MemoryWrite8(vaddr, value); + return true; + } + bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { + MemoryWrite16(vaddr, value); + return true; + } + bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { + MemoryWrite32(vaddr, value); + return true; + } + bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { + MemoryWrite64(vaddr, value); + return true; + } + + void InterpreterFallback(std::uint32_t pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:016x}, {})", pc, num_instructions); } + + void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); } + + void ExceptionRaised(std::uint32_t pc, Dynarmic::A32::Exception) override { ASSERT_MSG(false, "ExceptionRaised({:016x})", 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; + } +}; diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index efbf8233..9f82975f 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -837,3 +837,46 @@ TEST_CASE("A64: Cache Maintenance Instructions", "[a64]") { env.ticks_left = 3; jit.Run(); } + +TEST_CASE("A64: Memory access (fastmem)", "[a64]") { + constexpr size_t address_width = 12; + constexpr size_t memory_size = 1ull << address_width; // 4K + constexpr size_t page_size = 4 * 1024; + constexpr size_t buffer_size = 2 * page_size; + char buffer[buffer_size]; + + void* buffer_ptr = reinterpret_cast(buffer); + size_t buffer_size_nconst = buffer_size; + char* backing_memory = reinterpret_cast(std::align(page_size, memory_size, buffer_ptr, buffer_size_nconst)); + + A64FastmemTestEnv env{backing_memory}; + Dynarmic::A64::UserConfig config{&env}; + config.fastmem_pointer = backing_memory; + config.page_table_address_space_bits = address_width; + config.recompile_on_fastmem_failure = false; + config.silently_mirror_page_table = true; + config.processor_id = 0; + + Dynarmic::A64::Jit jit{config}; + memset(backing_memory, 0, memory_size); + memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57); + + env.MemoryWrite32(0, 0xA9401404); // LDP X4, X5, [X0] + env.MemoryWrite32(4, 0xF9400046); // LDR X6, [X2] + env.MemoryWrite32(8, 0xA9001424); // STP X4, X5, [X1] + env.MemoryWrite32(12, 0xF9000066); // STR X6, [X3] + env.MemoryWrite32(16, 0x14000000); // B . + jit.SetRegister(0, 0x100); + jit.SetRegister(1, 0x1F0); + jit.SetRegister(2, 0x10F); + jit.SetRegister(3, 0x1FF); + + jit.SetPC(0); + jit.SetSP(memory_size - 1); + jit.SetFpsr(0x03480000); + jit.SetPstate(0x30000000); + env.ticks_left = 5; + + jit.Run(); + REQUIRE(strncmp(backing_memory + 0x100, backing_memory + 0x1F0, 23) == 0); +} diff --git a/tests/A64/testenv.h b/tests/A64/testenv.h index 88743e10..79a2bff0 100644 --- a/tests/A64/testenv.h +++ b/tests/A64/testenv.h @@ -124,3 +124,99 @@ public: return 0x10000000000 - ticks_left; } }; + +class A64FastmemTestEnv final : public Dynarmic::A64::UserCallbacks { +public: + u64 ticks_left = 0; + char* backing_memory = nullptr; + + explicit A64FastmemTestEnv(char* addr) : backing_memory(addr) {} + + template + T read(u64 vaddr) { + T value; + memcpy(&value, backing_memory + vaddr, sizeof(T)); + return value; + } + template + void write(u64 vaddr, const T& value) { + memcpy(backing_memory + vaddr, &value, sizeof(T)); + } + + std::uint32_t MemoryReadCode(u64 vaddr) override { + return read(vaddr); + } + + std::uint8_t MemoryRead8(u64 vaddr) override { + return read(vaddr); + } + std::uint16_t MemoryRead16(u64 vaddr) override { + return read(vaddr); + } + std::uint32_t MemoryRead32(u64 vaddr) override { + return read(vaddr); + } + std::uint64_t MemoryRead64(u64 vaddr) override { + return read(vaddr); + } + Vector MemoryRead128(u64 vaddr) override { + return read(vaddr); + } + + void MemoryWrite8(u64 vaddr, std::uint8_t value) override { + write(vaddr, value); + } + void MemoryWrite16(u64 vaddr, std::uint16_t value) override { + write(vaddr, value); + } + void MemoryWrite32(u64 vaddr, std::uint32_t value) override { + write(vaddr, value); + } + void MemoryWrite64(u64 vaddr, std::uint64_t value) override { + write(vaddr, value); + } + void MemoryWrite128(u64 vaddr, Vector value) override { + write(vaddr, value); + } + + bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { + MemoryWrite8(vaddr, value); + return true; + } + bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { + MemoryWrite16(vaddr, value); + return true; + } + bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { + MemoryWrite32(vaddr, value); + return true; + } + bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { + MemoryWrite64(vaddr, value); + return true; + } + bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { + MemoryWrite128(vaddr, value); + return true; + } + + void InterpreterFallback(u64 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:016x}, {})", pc, num_instructions); } + + void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); } + + void ExceptionRaised(u64 pc, Dynarmic::A64::Exception) override { ASSERT_MSG(false, "ExceptionRaised({:016x})", 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; + } + std::uint64_t GetCNTPCT() override { + return 0x10000000000 - ticks_left; + } +};