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:
1 parent
ba5b6de0d0
commit
3d12571127
6 files changed
+209
-7
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user