rework data abort handling for ldm/stm; implement thumb stmia+push

This commit is contained in:
Jaklyy 2024-06-06 18:58:43 -04:00
parent 13ae96b4e3
commit d6cd189455
1 changed files with 65 additions and 41 deletions

View File

@ -424,7 +424,6 @@ void A_LDM(ARM* cpu)
u32 oldbase = base; u32 oldbase = base;
u32 preinc = (cpu->CurInstr & (1<<24)); u32 preinc = (cpu->CurInstr & (1<<24));
bool first = true; bool first = true;
bool dataabort = false;
if (!(cpu->CurInstr & (1<<23))) // decrement if (!(cpu->CurInstr & (1<<23))) // decrement
{ {
@ -456,8 +455,7 @@ void A_LDM(ARM* cpu)
if (!(first ? cpu->DataRead32 (base, &cpu->R[i]) if (!(first ? cpu->DataRead32 (base, &cpu->R[i])
: cpu->DataRead32S(base, &cpu->R[i]))) : cpu->DataRead32S(base, &cpu->R[i])))
{ {
dataabort = true; goto dataabort;
goto abortjump;
} }
first = false; first = false;
@ -472,7 +470,7 @@ void A_LDM(ARM* cpu)
if (!(first ? cpu->DataRead32 (base, &pc) if (!(first ? cpu->DataRead32 (base, &pc)
: cpu->DataRead32S(base, &pc))) : cpu->DataRead32S(base, &pc)))
{ {
dataabort = true; goto dataabort;
} }
if (!preinc) base += 4; if (!preinc) base += 4;
@ -481,14 +479,10 @@ void A_LDM(ARM* cpu)
pc &= ~0x1; pc &= ~0x1;
} }
abortjump:
// switch back to previous regs // switch back to previous regs
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15))) if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true); cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if (!dataabort)
{
// writeback to base // writeback to base
if (cpu->CurInstr & (1<<21)) if (cpu->CurInstr & (1<<21))
{ {
@ -512,8 +506,19 @@ void A_LDM(ARM* cpu)
// jump if pc got written // jump if pc got written
if (cpu->CurInstr & (1<<15)) if (cpu->CurInstr & (1<<15))
cpu->JumpTo(pc, cpu->CurInstr & (1<<22)); cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
// jump here if a data abort occurred; writeback is ignored, and any jumps were aborted
if (false)
{
dataabort:
// switch back to original set of regs
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
// restore original value of base in case the reg got written to
cpu->R[baseid] = oldbase;
} }
else cpu->R[baseid] = oldbase; // restore original value of base in case the reg got written to
cpu->AddCycles_CDI(); cpu->AddCycles_CDI();
} }
@ -525,7 +530,6 @@ void A_STM(ARM* cpu)
u32 oldbase = base; u32 oldbase = base;
u32 preinc = (cpu->CurInstr & (1<<24)); u32 preinc = (cpu->CurInstr & (1<<24));
bool first = true; bool first = true;
bool dataabort = false;
if (!(cpu->CurInstr & (1<<23))) if (!(cpu->CurInstr & (1<<23)))
{ {
@ -571,8 +575,7 @@ void A_STM(ARM* cpu)
if (!(first ? cpu->DataWrite32 (base, val) if (!(first ? cpu->DataWrite32 (base, val)
: cpu->DataWrite32S(base, val))) : cpu->DataWrite32S(base, val)))
{ {
dataabort = true; goto dataabort;
break;
} }
first = false; first = false;
@ -584,12 +587,20 @@ void A_STM(ARM* cpu)
if (cpu->CurInstr & (1<<22)) if (cpu->CurInstr & (1<<22))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true); cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if (!dataabort)
{
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21))) if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
cpu->R[baseid] = base; cpu->R[baseid] = base;
// jump here if a data abort occurred
if (false)
{
dataabort:
if (cpu->CurInstr & (1<<22))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
// restore original value of base
cpu->R[baseid] = oldbase;
} }
else cpu->R[baseid] = oldbase;
cpu->AddCycles_CD(); cpu->AddCycles_CD();
} }
@ -774,14 +785,17 @@ void T_PUSH(ARM* cpu)
u32 base = cpu->R[13]; u32 base = cpu->R[13];
base -= (nregs<<2); base -= (nregs<<2);
cpu->R[13] = base; u32 wbbase = base;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
if (cpu->CurInstr & (1<<i)) if (cpu->CurInstr & (1<<i))
{ {
if (first) cpu->DataWrite32 (base, cpu->R[i]); if (!(first ? cpu->DataWrite32 (base, cpu->R[i])
else cpu->DataWrite32S(base, cpu->R[i]); : cpu->DataWrite32S(base, cpu->R[i])))
{
goto dataabort;
}
first = false; first = false;
base += 4; base += 4;
} }
@ -789,10 +803,16 @@ void T_PUSH(ARM* cpu)
if (cpu->CurInstr & (1<<8)) if (cpu->CurInstr & (1<<8))
{ {
if (first) cpu->DataWrite32 (base, cpu->R[14]); if (!(first ? cpu->DataWrite32 (base, cpu->R[14])
else cpu->DataWrite32S(base, cpu->R[14]); : cpu->DataWrite32S(base, cpu->R[14])))
{
goto dataabort;
}
} }
cpu->R[13] = wbbase;
dataabort:
cpu->AddCycles_CD(); cpu->AddCycles_CD();
} }
@ -835,8 +855,11 @@ void T_STMIA(ARM* cpu)
{ {
if (cpu->CurInstr & (1<<i)) if (cpu->CurInstr & (1<<i))
{ {
if (first) cpu->DataWrite32 (base, cpu->R[i]); if (!(first ? cpu->DataWrite32 (base, cpu->R[i])
else cpu->DataWrite32S(base, cpu->R[i]); : cpu->DataWrite32S(base, cpu->R[i])))
{
goto dataabort;
}
first = false; first = false;
base += 4; base += 4;
} }
@ -844,6 +867,7 @@ void T_STMIA(ARM* cpu)
// TODO: check "Rb included in Rlist" case // TODO: check "Rb included in Rlist" case
cpu->R[(cpu->CurInstr >> 8) & 0x7] = base; cpu->R[(cpu->CurInstr >> 8) & 0x7] = base;
dataabort:
cpu->AddCycles_CD(); cpu->AddCycles_CD();
} }