unit tests & various fixes
This commit is contained in:
parent
709773dcf1
commit
b4ee976a6f
4 changed files with 256 additions and 0 deletions
|
@ -525,3 +525,37 @@ TEST_CASE("arm: vcvt.s16.f64", "[arm][A32]") {
|
||||||
REQUIRE(jit.ExtRegs()[16] == 0xffff8000);
|
REQUIRE(jit.ExtRegs()[16] == 0xffff8000);
|
||||||
REQUIRE(jit.ExtRegs()[17] == 0xffffffff);
|
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<void*>(buffer);
|
||||||
|
size_t buffer_size_nconst = buffer_size;
|
||||||
|
char* backing_memory = reinterpret_cast<char*>(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);
|
||||||
|
}
|
||||||
|
|
|
@ -114,3 +114,86 @@ public:
|
||||||
|
|
||||||
using ArmTestEnv = A32TestEnv<u32, 0xEAFFFFFE>;
|
using ArmTestEnv = A32TestEnv<u32, 0xEAFFFFFE>;
|
||||||
using ThumbTestEnv = A32TestEnv<u16, 0xE7FEE7FE>;
|
using ThumbTestEnv = A32TestEnv<u16, 0xE7FEE7FE>;
|
||||||
|
|
||||||
|
class A32FastmemTestEnv final : public Dynarmic::A32::UserCallbacks {
|
||||||
|
public:
|
||||||
|
u64 ticks_left = 0;
|
||||||
|
char* backing_memory = nullptr;
|
||||||
|
|
||||||
|
explicit A32FastmemTestEnv(char* addr) : backing_memory(addr) {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T read(std::uint32_t vaddr) {
|
||||||
|
T value;
|
||||||
|
memcpy(&value, backing_memory + vaddr, sizeof(T));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
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<std::uint32_t>(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t MemoryRead8(std::uint32_t vaddr) override {
|
||||||
|
return read<std::uint8_t>(vaddr);
|
||||||
|
}
|
||||||
|
std::uint16_t MemoryRead16(std::uint32_t vaddr) override {
|
||||||
|
return read<std::uint16_t>(vaddr);
|
||||||
|
}
|
||||||
|
std::uint32_t MemoryRead32(std::uint32_t vaddr) override {
|
||||||
|
return read<std::uint32_t>(vaddr);
|
||||||
|
}
|
||||||
|
std::uint64_t MemoryRead64(std::uint32_t vaddr) override {
|
||||||
|
return read<std::uint64_t>(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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -837,3 +837,46 @@ TEST_CASE("A64: Cache Maintenance Instructions", "[a64]") {
|
||||||
env.ticks_left = 3;
|
env.ticks_left = 3;
|
||||||
jit.Run();
|
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<void*>(buffer);
|
||||||
|
size_t buffer_size_nconst = buffer_size;
|
||||||
|
char* backing_memory = reinterpret_cast<char*>(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);
|
||||||
|
}
|
||||||
|
|
|
@ -124,3 +124,99 @@ public:
|
||||||
return 0x10000000000 - ticks_left;
|
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<typename T>
|
||||||
|
T read(u64 vaddr) {
|
||||||
|
T value;
|
||||||
|
memcpy(&value, backing_memory + vaddr, sizeof(T));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
void write(u64 vaddr, const T& value) {
|
||||||
|
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t MemoryReadCode(u64 vaddr) override {
|
||||||
|
return read<std::uint32_t>(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t MemoryRead8(u64 vaddr) override {
|
||||||
|
return read<std::uint8_t>(vaddr);
|
||||||
|
}
|
||||||
|
std::uint16_t MemoryRead16(u64 vaddr) override {
|
||||||
|
return read<std::uint16_t>(vaddr);
|
||||||
|
}
|
||||||
|
std::uint32_t MemoryRead32(u64 vaddr) override {
|
||||||
|
return read<std::uint32_t>(vaddr);
|
||||||
|
}
|
||||||
|
std::uint64_t MemoryRead64(u64 vaddr) override {
|
||||||
|
return read<std::uint64_t>(vaddr);
|
||||||
|
}
|
||||||
|
Vector MemoryRead128(u64 vaddr) override {
|
||||||
|
return read<Vector>(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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue