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

+91
View File
@@ -1423,6 +1423,16 @@ void ARMCore::ExecuteARM(u32 instruction)
return;
}
// ARMv5TE enhanced DSP instructions (ARM Architecture Reference Manual, ARMv5TE): SMLAxy,
// SMLAWy, SMULWy, SMLALxy, SMULxy, then QADD, QSUB, QDADD, QDSUB. They sit in the
// data-processing space with a compare opcode and S clear, which ARMv5 reserves for such
// miscellaneous instructions; decoded as data processing they would write no register.
if ((instruction & 0x0f900090) == 0x01000080 || (instruction & 0x0f900ff0) == 0x01000050)
{
ExecuteEnhancedDSP(instruction);
return;
}
if ((instruction & 0x0f8000f0) == 0x00800090)
{
ExecuteMultiplyLong(instruction);
@@ -2327,6 +2337,87 @@ void ARMCore::ExecuteMultiply(u32 instruction)
SetNZ(result);
}
void ARMCore::ExecuteEnhancedDSP(u32 instruction)
{
// StarletOS phase 11 (I1). The ARM926EJ-S implements these; the Q flag (CPSR bit 27) is sticky
// and set on signed overflow of an accumulation or on saturation.
const auto half = [](u32 value, bool top) {
return static_cast<s64>(static_cast<s16>(top ? value >> 16 : value));
};
const auto saturate = [this](s64 value) {
if (value > std::numeric_limits<s32>::max())
{
m_cpsr |= CPSR_Q;
return static_cast<u32>(std::numeric_limits<s32>::max());
}
if (value < std::numeric_limits<s32>::min())
{
m_cpsr |= CPSR_Q;
return static_cast<u32>(std::numeric_limits<s32>::min());
}
return static_cast<u32>(static_cast<s32>(value));
};
const auto accumulate = [this](s64 sum) {
if (sum != static_cast<s32>(sum))
m_cpsr |= CPSR_Q;
return static_cast<u32>(static_cast<s32>(sum));
};
const u32 op = (instruction >> 21) & 3;
const u32 rm = instruction & 0xf;
if ((instruction & 0xf0) == 0x50)
{
// QADD (0), QSUB (1), QDADD (2), QDSUB (3): Rd = sat(Rm +/- [sat(2 *)] Rn).
const u32 rn = (instruction >> 16) & 0xf;
const u32 rd = (instruction >> 12) & 0xf;
const s64 m = static_cast<s32>(ReadRegisterOperand(rm));
s64 n = static_cast<s32>(ReadRegisterOperand(rn));
if ((op & 2) != 0)
n = static_cast<s32>(saturate(2 * n));
m_registers[rd] = saturate((op & 1) != 0 ? m - n : m + n);
return;
}
// SMLAxy (0), SMLAWy/SMULWy (1), SMLALxy (2), SMULxy (3). Rd (RdHi) is bits 19-16, Rn (RdLo)
// bits 15-12, Rs bits 11-8; x (bit 5) picks Rm's half, y (bit 6) Rs's half.
const u32 rd = (instruction >> 16) & 0xf;
const u32 rn = (instruction >> 12) & 0xf;
const u32 rs = (instruction >> 8) & 0xf;
const bool x = (instruction & (1U << 5)) != 0;
const bool y = (instruction & (1U << 6)) != 0;
const u32 m = ReadRegisterOperand(rm);
const u32 s = ReadRegisterOperand(rs);
switch (op)
{
case 0:
m_registers[rd] =
accumulate(half(m, x) * half(s, y) + static_cast<s32>(ReadRegisterOperand(rn)));
break;
case 1:
{
// The top 32 bits of the 48-bit product of Rm and a halfword of Rs.
const s64 product = (static_cast<s64>(static_cast<s32>(m)) * half(s, y)) >> 16;
if (x) // SMULWy
m_registers[rd] = static_cast<u32>(static_cast<s32>(product));
else // SMLAWy
m_registers[rd] = accumulate(product + static_cast<s32>(ReadRegisterOperand(rn)));
break;
}
case 2:
{
const u64 accumulator =
(static_cast<u64>(ReadRegisterOperand(rd)) << 32) | ReadRegisterOperand(rn);
const u64 result = accumulator + static_cast<u64>(half(m, x) * half(s, y));
m_registers[rn] = static_cast<u32>(result);
m_registers[rd] = static_cast<u32>(result >> 32);
break;
}
default:
m_registers[rd] = static_cast<u32>(static_cast<s32>(half(m, x) * half(s, y)));
break;
}
}
void ARMCore::ExecuteMultiplyLong(u32 instruction)
{
const bool signed_multiply = (instruction & (1U << 22)) != 0;
+2
View File
@@ -315,6 +315,8 @@ private:
void TraceWatchedBlockTransfer(u32 address, u32 size, bool load) const;
void ExecuteMultiply(u32 instruction);
void ExecuteMultiplyLong(u32 instruction);
// ARMv5TE signed halfword multiplies and saturating arithmetic.
void ExecuteEnhancedDSP(u32 instruction);
void ExecuteBranch(u32 instruction);
void ExecuteCoprocessor(u32 instruction);
void ExecutePSRTransfer(u32 instruction);
+3 -1
View File
@@ -1164,7 +1164,9 @@ bool ARMJitX64::CanEmitARMDataProcessing(u32 instruction) const
if ((instruction & 0x0fff0ff0) == 0x016f0f10 || (instruction & 0x0f8000f0) == 0x00800090 ||
(instruction & 0x0fc000f0) == 0x00000090 || (instruction & 0x0fb00ff0) == 0x01000090 ||
(instruction & 0x0fbf0fff) == 0x010f0000 || (instruction & 0x0fb0fff0) == 0x0120f000 ||
(instruction & 0x0fb0f000) == 0x0320f000 || (instruction & 0x0e000090) == 0x00000090)
(instruction & 0x0fb0f000) == 0x0320f000 || (instruction & 0x0e000090) == 0x00000090 ||
// ARMv5TE enhanced DSP multiplies and saturating arithmetic: interpreter (StarletOS I1).
(instruction & 0x0f900090) == 0x01000080 || (instruction & 0x0f900ff0) == 0x01000050)
{
return false;
}