gba: set cpsr=spsr when switching to FIQ mode

The CPSR register needs to be restored from SPSR when switching modes.
This is currently being done for all mode switches /except/ for FIQ,
which is very likely just an oversight rather than intended behaviour.

This fixes the random crashing in OpenLara, as well as fixing random
glitching of my own project that uses FIQ mode switches. The issue
happens "at random" because it requires an interrupt to occur while in
FIQ mode, and it must also fire inside a section of code that relies on
the status flags (or the CPSR register in general): When exiting the
interrupt exception, the CPSR register is supposed to be restored from
SPSR, but this isn't being done when switching from IRQ mode back to FIQ
mode, which results in CPSR (and thus the status flags) being corrupted.
For example, SUBS r0, #1; [interrupt]; BNE 1b would trigger the bug, but
[interrupt]; SUBS r0, #1; BNE 1b wouldn't, and neither would
SUBS r0,#1; BNE 1b; [interrupt].
This commit is contained in:
Ruben 2024-04-13 01:49:45 +10:00 committed by GitHub
parent 8abe3e79da
commit 647be137f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 1 additions and 0 deletions

View File

@ -1936,6 +1936,7 @@ void CPUSwitchMode(int mode, bool saveState, bool breakLoop)
CPUSwap(&reg[12].I, &reg[R12_FIQ].I); CPUSwap(&reg[12].I, &reg[R12_FIQ].I);
reg[13].I = reg[R13_FIQ].I; reg[13].I = reg[R13_FIQ].I;
reg[14].I = reg[R14_FIQ].I; reg[14].I = reg[R14_FIQ].I;
reg[16].I = SPSR;
if (saveState) if (saveState)
reg[17].I = CPSR; reg[17].I = CPSR;
else else