eden/src/dynarmic/tests/A32/test_arm_instructions.cpp
crueter 51b170b470
[cmake] refactor: Use CPM over submodules (#143)
Transfers the majority of submodules and large externals to CPM, using source archives rather than full Git clones. Not only does this save massive amounts of clone and configure time, but dependencies are grabbed on-demand rather than being required by default. Additionally, CPM will (generally) automatically search for system dependencies, though certain dependencies have options to control this.

Testing shows gains ranging from 5x to 10x in terms of overall clone/configure time.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/143
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2025-08-04 04:50:14 +02:00

705 lines
20 KiB
C++

/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"
using namespace Dynarmic;
static A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
A32::UserConfig user_config;
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
user_config.callbacks = testenv;
return user_config;
}
TEST_CASE("arm: Opt Failure: Const folding in MostSignificantWord", "[arm][A32]") {
// This was a randomized test-case that was failing.
// This was due to constant folding for MostSignificantWord
// failing to take into account an associated GetCarryFromOp
// pseudoinstruction.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe30ad071, // movw, sp, #41073
0xe75efd3d, // smmulr lr, sp, sp
0xa637af1e, // shadd16ge r10, r7, lr
0xf57ff01f, // clrex
0x86b98879, // sxtahhi r8, r9, r9, ror #16
0xeafffffe, // b +#0
};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 6;
jit.Run();
// If we don't trigger the GetCarryFromOp ASSERT, we're fine.
}
TEST_CASE("arm: Unintended modification in SetCFlag", "[arm][A32]") {
// This was a randomized test-case that was failing.
//
// IR produced for location {12, !T, !E} was:
// %0 = GetRegister r1
// %1 = SubWithCarry %0, #0x3e80000, #1
// %2 = GetCarryFromOp %1
// %3 = GetOverflowFromOp %1
// %4 = MostSignificantBit %1
// SetNFlag %4
// %6 = IsZero %1
// SetZFlag %6
// SetCFlag %2
// SetVFlag %3
// %10 = GetRegister r5
// %11 = AddWithCarry %10, #0x8a00, %2
// SetRegister r4, %11
//
// The reference to %2 in instruction %11 was the issue, because instruction %8
// told the register allocator it was a Use but then modified the value.
// Changing the EmitSet*Flag instruction to declare their arguments as UseScratch
// solved this bug.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe35f0cd9, // cmp pc, #55552
0xe11c0474, // tst r12, r4, ror r4
0xe1a006a7, // mov r0, r7, lsr #13
0xe35107fa, // cmp r1, #0x3E80000
0xe2a54c8a, // adc r4, r5, #35328
0xeafffffe, // b +#0
};
jit.Regs() = {
0x6973b6bb, 0x267ea626, 0x69debf49, 0x8f976895, 0x4ecd2d0d, 0xcf89b8c7, 0xb6713f85, 0x15e2aa5,
0xcd14336a, 0xafca0f3e, 0xace2efd9, 0x68fb82cd, 0x775447c0, 0xc9e1f8cd, 0xebe0e626, 0x0};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 6;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x00000af1);
REQUIRE(jit.Regs()[1] == 0x267ea626);
REQUIRE(jit.Regs()[2] == 0x69debf49);
REQUIRE(jit.Regs()[3] == 0x8f976895);
REQUIRE(jit.Regs()[4] == 0xcf8a42c8);
REQUIRE(jit.Regs()[5] == 0xcf89b8c7);
REQUIRE(jit.Regs()[6] == 0xb6713f85);
REQUIRE(jit.Regs()[7] == 0x015e2aa5);
REQUIRE(jit.Regs()[8] == 0xcd14336a);
REQUIRE(jit.Regs()[9] == 0xafca0f3e);
REQUIRE(jit.Regs()[10] == 0xace2efd9);
REQUIRE(jit.Regs()[11] == 0x68fb82cd);
REQUIRE(jit.Regs()[12] == 0x775447c0);
REQUIRE(jit.Regs()[13] == 0xc9e1f8cd);
REQUIRE(jit.Regs()[14] == 0xebe0e626);
REQUIRE(jit.Regs()[15] == 0x00000014);
REQUIRE(jit.Cpsr() == 0x200001d0);
}
TEST_CASE("arm: shsax (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.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe63dbf59, // shsax r11, sp, r9
0xeafffffe, // b +#0
};
jit.Regs() = {
0x3a3b8b18, 0x96156555, 0xffef039f, 0xafb946f2, 0x2030a69a, 0xafe09b2a, 0x896823c8, 0xabde0ded,
0x9825d6a6, 0x17498000, 0x999d2c95, 0x8b812a59, 0x209bdb58, 0x2f7fb1d4, 0x0f378107, 0x00000000};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x3a3b8b18);
REQUIRE(jit.Regs()[1] == 0x96156555);
REQUIRE(jit.Regs()[2] == 0xffef039f);
REQUIRE(jit.Regs()[3] == 0xafb946f2);
REQUIRE(jit.Regs()[4] == 0x2030a69a);
REQUIRE(jit.Regs()[5] == 0xafe09b2a);
REQUIRE(jit.Regs()[6] == 0x896823c8);
REQUIRE(jit.Regs()[7] == 0xabde0ded);
REQUIRE(jit.Regs()[8] == 0x9825d6a6);
REQUIRE(jit.Regs()[9] == 0x17498000);
REQUIRE(jit.Regs()[10] == 0x999d2c95);
REQUIRE(jit.Regs()[11] == 0x57bfe48e);
REQUIRE(jit.Regs()[12] == 0x209bdb58);
REQUIRE(jit.Regs()[13] == 0x2f7fb1d4);
REQUIRE(jit.Regs()[14] == 0x0f378107);
REQUIRE(jit.Regs()[15] == 0x00000004);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
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.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe6549f35, // uasx r9, r4, r5
0xeafffffe, // b +#0
};
jit.Regs()[4] = 0x8ed38f4c;
jit.Regs()[5] = 0x0000261d;
jit.Regs()[15] = 0x00000000;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[4] == 0x8ed38f4c);
REQUIRE(jit.Regs()[5] == 0x0000261d);
REQUIRE(jit.Regs()[9] == 0xb4f08f4c);
REQUIRE(jit.Regs()[15] == 0x00000004);
REQUIRE(jit.Cpsr() == 0x000301d0);
}
TEST_CASE("arm: smuad (Edge-case)", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xE700F211, // smuad r0, r1, r2
0xeafffffe, // b +#0
};
jit.Regs() = {
0, // Rd
0x80008000, // Rn
0x80008000, // Rm
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x80000000);
REQUIRE(jit.Regs()[1] == 0x80008000);
REQUIRE(jit.Regs()[2] == 0x80008000);
REQUIRE(jit.Cpsr() == 0x080001d0);
}
TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe3a00005, // mov r0, #5
0xe3a0100D, // mov r1, #13
0xe0812000, // add r2, r1, r0
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs() = {};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 4;
jit.Run();
REQUIRE(jit.Regs()[0] == 5);
REQUIRE(jit.Regs()[1] == 13);
REQUIRE(jit.Regs()[2] == 18);
REQUIRE(jit.Regs()[15] == 0x0000000c);
REQUIRE(jit.Cpsr() == 0x000001d0);
// Change the code
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;
test_env.ticks_left = 4;
jit.Run();
jit.Run();
REQUIRE(jit.Regs()[0] == 5);
REQUIRE(jit.Regs()[1] == 7);
REQUIRE(jit.Regs()[2] == 12);
REQUIRE(jit.Regs()[15] == 0x0000000c);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Step blx", "[arm]") {
ArmTestEnv test_env;
A32::UserConfig config = GetUserConfig(&test_env);
config.optimizations |= OptimizationFlag::FastDispatch;
Dynarmic::A32::Jit jit{config};
test_env.code_mem = {
0xe12fff30, // blx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 8;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[0] == 8);
REQUIRE(jit.Regs()[14] == 4);
REQUIRE(jit.Regs()[15] == 8);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Step bx", "[arm]") {
ArmTestEnv test_env;
A32::UserConfig config = GetUserConfig(&test_env);
config.optimizations |= OptimizationFlag::FastDispatch;
Dynarmic::A32::Jit jit{config};
test_env.code_mem = {
0xe12fff10, // bx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 8;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[0] == 8);
REQUIRE(jit.Regs()[15] == 8);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Test stepping", "[arm]") {
ArmTestEnv test_env;
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 8;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
for (size_t i = 0; i < 5; ++i) {
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[15] == (i + 1) * 4);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
test_env.ticks_left = 20;
jit.Run();
REQUIRE(jit.Regs()[15] == 80);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Test stepping 2", "[arm]") {
ArmTestEnv test_env;
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe12fff10, // bx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 4;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
for (size_t i = 0; i < 5; ++i) {
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[15] == (i + 1) * 4);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
test_env.ticks_left = 20;
jit.Run();
REQUIRE(jit.Regs()[15] == 80);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Test stepping 3", "[arm]") {
ArmTestEnv test_env;
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe12fff10, // bx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 4;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[15] == 4);
REQUIRE(jit.Cpsr() == 0x000001d0);
test_env.ticks_left = 20;
jit.Run();
REQUIRE(jit.Regs()[15] == 20);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: PackedAbsDiffSumS8", "[arm][A32]") {
// This was a randomized test-case that was failing.
// In circumstances there were cases when the upper 32 bits of an argument to psadbw were not zero.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0x87414354, // smlsldhi r4, r1, r4, r3
0xe7886412, // usad8a r8, r2, r4, r6
0xeafffffe, // b +#0
};
jit.Regs() = {
0xea85297c,
0x417ad918,
0x64f8b70b,
0xcca0373e,
0xbc722361,
0xc528c69e,
0xca926de8,
0xd665d210,
0xb5650555,
0x4a24b25b,
0xaed44144,
0xe87230b2,
0x98e391de,
0x126efc0c,
0xe591fd11,
0x00000000,
};
jit.SetCpsr(0xb0000010);
test_env.ticks_left = 3;
jit.Run();
REQUIRE(jit.Regs()[0] == 0xea85297c);
REQUIRE(jit.Regs()[1] == 0x417ad918);
REQUIRE(jit.Regs()[2] == 0x64f8b70b);
REQUIRE(jit.Regs()[3] == 0xcca0373e);
REQUIRE(jit.Regs()[4] == 0xb685ec9f);
REQUIRE(jit.Regs()[5] == 0xc528c69e);
REQUIRE(jit.Regs()[6] == 0xca926de8);
REQUIRE(jit.Regs()[7] == 0xd665d210);
REQUIRE(jit.Regs()[8] == 0xca926f76);
REQUIRE(jit.Regs()[9] == 0x4a24b25b);
REQUIRE(jit.Regs()[10] == 0xaed44144);
REQUIRE(jit.Regs()[11] == 0xe87230b2);
REQUIRE(jit.Regs()[12] == 0x98e391de);
REQUIRE(jit.Regs()[13] == 0x126efc0c);
REQUIRE(jit.Regs()[14] == 0xe591fd11);
REQUIRE(jit.Regs()[15] == 0x00000008);
REQUIRE(jit.Cpsr() == 0xb0000010);
}
TEST_CASE("arm: vclt.f32 with zero", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xf3b93628, // vclt.f32 d3, d24, #0
0xeafffffe, // b +#0
};
jit.ExtRegs()[48] = 0x3a87d9f1;
jit.ExtRegs()[49] = 0x80796dc0;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.ExtRegs()[6] == 0x00000000);
REQUIRE(jit.ExtRegs()[7] == 0x00000000);
}
TEST_CASE("arm: vcvt.s16.f64", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xeebe8b45, // vcvt.s16.f64 d8, d8, #6
0xeafffffe, // b +#0
};
jit.ExtRegs()[16] = 0x9a7110b0;
jit.ExtRegs()[17] = 0xcd78f4e7;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
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<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{};
config.callbacks = &env;
config.fastmem_pointer = reinterpret_cast<uintptr_t>(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);
}
TEST_CASE("arm: vmsr, vcmp, vmrs", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xeee10a10, // vmsr fpscr, r0
0xeeb48a4a, // vcmp.f32 s16, s20
0xeef1fa10, // vmrs apsr_nzcv, fpscr
0xe12fff1e, // bx lr
};
jit.ExtRegs()[16] = 0xFF7FFFFF;
jit.ExtRegs()[20] = 0xFF7FFFFF;
jit.Regs()[0] = 0x60000000;
jit.SetFpscr(0x3ee22ac0);
jit.SetCpsr(0x60000000); // User-mode
test_env.ticks_left = 4;
jit.Run();
}
TEST_CASE("arm: sdiv maximally", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe712f011, // sdiv r2, r1, r0
0xeafffffe, // b +#0
};
jit.Regs()[1] = 0x80000000;
jit.Regs()[0] = 0xffffffff;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[2] == 0x80000000);
}
TEST_CASE("arm: tbl", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem.emplace_back(0xf3f408a0); // vtbl.8 d16, {d20 }, d16
test_env.code_mem.emplace_back(0xf3f419a1); // vtbl.8 d17, {d20, d21 }, d17
test_env.code_mem.emplace_back(0xf3f42aa2); // vtbl.8 d18, {d20, d21, d22 }, d18
test_env.code_mem.emplace_back(0xf3f43ba3); // vtbl.8 d19, {d20, d21, d22, d23}, d19
test_env.code_mem.emplace_back(0xeafffffe); // b +#0
// Indices
jit.ExtRegs()[16 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[16 * 2 + 1] = 0x20'1F'10'0F;
jit.ExtRegs()[17 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[17 * 2 + 1] = 0x20'1F'10'0F;
jit.ExtRegs()[18 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[18 * 2 + 1] = 0x20'1F'10'0F;
jit.ExtRegs()[19 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[19 * 2 + 1] = 0x20'1F'10'0F;
// Table
jit.ExtRegs()[20 * 2 + 0] = 0x03'02'01'00;
jit.ExtRegs()[20 * 2 + 1] = 0x07'06'05'04;
jit.ExtRegs()[21 * 2 + 0] = 0x0B'0A'09'08;
jit.ExtRegs()[21 * 2 + 1] = 0x0F'0E'0D'0C;
jit.ExtRegs()[22 * 2 + 0] = 0x13'12'11'10;
jit.ExtRegs()[22 * 2 + 1] = 0x17'16'15'14;
jit.ExtRegs()[23 * 2 + 0] = 0x1B'1A'19'18;
jit.ExtRegs()[23 * 2 + 1] = 0x1F'1E'1D'1C;
test_env.ticks_left = 5;
jit.Run();
REQUIRE(jit.ExtRegs()[16 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[16 * 2 + 1] == 0x00'00'00'00);
REQUIRE(jit.ExtRegs()[17 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[17 * 2 + 1] == 0x00'00'00'0F);
REQUIRE(jit.ExtRegs()[18 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[18 * 2 + 1] == 0x00'00'10'0F);
REQUIRE(jit.ExtRegs()[19 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[19 * 2 + 1] == 0x00'1F'10'0F);
}
TEST_CASE("arm: tbx", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem.emplace_back(0xf3f408e0); // vtbx.8 d16, {d20 }, d16
test_env.code_mem.emplace_back(0xf3f419e1); // vtbx.8 d17, {d20, d21 }, d17
test_env.code_mem.emplace_back(0xf3f42ae2); // vtbx.8 d18, {d20, d21, d22 }, d18
test_env.code_mem.emplace_back(0xf3f43be3); // vtbx.8 d19, {d20, d21, d22, d23}, d19
test_env.code_mem.emplace_back(0xeafffffe); // b +#0
// Indices
jit.ExtRegs()[16 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[16 * 2 + 1] = 0x20'1F'10'0F;
jit.ExtRegs()[17 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[17 * 2 + 1] = 0x20'1F'10'0F;
jit.ExtRegs()[18 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[18 * 2 + 1] = 0x20'1F'10'0F;
jit.ExtRegs()[19 * 2 + 0] = 0x05'02'01'00;
jit.ExtRegs()[19 * 2 + 1] = 0x20'1F'10'0F;
// Table
jit.ExtRegs()[20 * 2 + 0] = 0x03'02'01'00;
jit.ExtRegs()[20 * 2 + 1] = 0x07'06'05'04;
jit.ExtRegs()[21 * 2 + 0] = 0x0B'0A'09'08;
jit.ExtRegs()[21 * 2 + 1] = 0x0F'0E'0D'0C;
jit.ExtRegs()[22 * 2 + 0] = 0x13'12'11'10;
jit.ExtRegs()[22 * 2 + 1] = 0x17'16'15'14;
jit.ExtRegs()[23 * 2 + 0] = 0x1B'1A'19'18;
jit.ExtRegs()[23 * 2 + 1] = 0x1F'1E'1D'1C;
test_env.ticks_left = 5;
jit.Run();
REQUIRE(jit.ExtRegs()[16 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[16 * 2 + 1] == 0x20'1F'10'0F);
REQUIRE(jit.ExtRegs()[17 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[17 * 2 + 1] == 0x20'1F'10'0F);
REQUIRE(jit.ExtRegs()[18 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[18 * 2 + 1] == 0x20'1F'10'0F);
REQUIRE(jit.ExtRegs()[19 * 2 + 0] == 0x05'02'01'00);
REQUIRE(jit.ExtRegs()[19 * 2 + 1] == 0x20'1F'10'0F);
}