IOS LLE: execute ARMv5TE enhanced DSP instructions (StarletOS phase 11 I1)

SMLAxy, SMLAWy, SMULWy, SMLALxy, SMULxy, QADD, QSUB, QDADD and QDSUB were
decoded as compare data processing that writes no register; the ARM926EJ-S
implements them. The interpreter now executes them with the Q flag and the
JIT leaves them to it. Adds ARMv5TEEnhancedDSPInstructions.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Yaya48andClaude Opus 5.5 committed 2026-10-06 22:16:53 +02:00
1 parent ba5b6de0d0
commit 3d12571127
6 files changed
+209 -7

No files matched your search

@@ -1755,6 +1755,83 @@ TEST(StarletARMCore, ARMJitCompilesLongMultiplyFamily)
#endif
}
TEST(StarletARMCore, ARMv5TEEnhancedDSPInstructions)
{
// StarletOS phase 11 (I1): encodings from GNU as (-march=armv5te); results from the ARM
// Architecture Reference Manual. r0 = Rm, r1 = Rs or Rn, r7 = accumulator, r3/r4 results.
struct Case
{
u32 instruction;
u32 r0;
u32 r1;
u32 r3;
u32 r4;
u32 r7;
u32 expected_r3;
u32 expected_r4;
bool expected_q;
};
constexpr std::array cases = {
// smlabb r4, r0, r1, r7: a 88-byte entry 9 of a table (Revolution's RAM layer).
Case{0xe1047180, 9, 88, 0, 0, 0x13780000, 0, 0x13780318, false},
Case{0xe1640180, 0xffff0003, 0x0002fffe, 0, 0, 0, 0, 0xfffffffa, false}, // smulbb
Case{0xe16401a0, 0xffff0003, 0x0002fffe, 0, 0, 0, 0, 2, false}, // smultb
// smlatt r4, r0, r1, r7: 0x3fff0001 + 0x7fffffff overflows, wraps and sets Q.
Case{0xe10471e0, 0x7fff0000, 0x7fff0000, 0, 0, 0x7fffffff, 0, 0xbfff0000, true},
Case{0xe1247180, 0x00020000, 0x00000003, 0, 0, 10, 0, 16, false}, // smlawb
Case{0xe12401e0, 0xfffe0000, 0x00030000, 0, 0, 0, 0, 0xfffffffa, false}, // smulwt
// smlalbb r3, r4, r0, r1: 0x00000000_ffffffff + 1 carries into RdHi.
Case{0xe1443180, 1, 1, 0xffffffff, 0, 0, 0, 1, false},
Case{0xe1014050, 0x7fffffff, 1, 0, 0, 0, 0, 0x7fffffff, true}, // qadd
Case{0xe1214050, 0x80000000, 1, 0, 0, 0, 0, 0x80000000, true}, // qsub
Case{0xe1414050, 1, 0x40000000, 0, 0, 0, 0, 0x7fffffff, true}, // qdadd
Case{0xe1614050, 0, 3, 0, 0, 0, 0, 0xfffffffa, false}, // qdsub
};
for (const Case& test : cases)
{
TestBus interpreter_bus;
ARMCore interpreter(interpreter_bus);
#if defined(_M_X86_64)
TestBus jit_bus;
ARMCore jit(jit_bus);
std::array<ARMCore*, 2> cores = {&interpreter, &jit};
jit_bus.WriteARM(0, test.instruction);
jit_bus.WriteARM(4, 0xeafffffe); // b .
#else
std::array<ARMCore*, 1> cores = {&interpreter};
#endif
interpreter_bus.WriteARM(0, test.instruction);
interpreter_bus.WriteARM(4, 0xeafffffe); // b .
for (ARMCore* core : cores)
{
core->SetCPSR(static_cast<u32>(ARMCore::Mode::System));
core->SetRegister(0, test.r0);
core->SetRegister(1, test.r1);
core->SetRegister(3, test.r3);
core->SetRegister(4, test.r4);
core->SetRegister(7, test.r7);
core->SetRegister(15, 0);
}
#if defined(_M_X86_64)
jit.SetJitEnabled(true);
#endif
for (ARMCore* core : cores)
{
ASSERT_EQ(core->RunCycles(2), 2u);
EXPECT_EQ(core->GetRegister(4), test.expected_r4) << std::hex << test.instruction;
if (test.instruction == 0xe1443180)
EXPECT_EQ(core->GetRegister(3), test.expected_r3);
EXPECT_EQ((core->GetCPSR() & ARMCore::CPSR_Q) != 0, test.expected_q)
<< std::hex << test.instruction;
// The DSP instruction must not touch the condition flags (it is not a compare).
EXPECT_EQ(core->GetCPSR() & (ARMCore::CPSR_N | ARMCore::CPSR_Z | ARMCore::CPSR_C |
ARMCore::CPSR_V),
0u);
}
}
}
TEST(StarletARMCore, FCSESwitchPreservesTaggedTLBTranslations)
{
TestBus bus(0x10000);