Cores/Nds/ARMInterpreter.cs
namespace Emulotl.Nds;
public delegate void InstrFunc( ARM cpu );
public static partial class ARMInterpreter
{
public static readonly InstrFunc[] ARMInstrTable = new InstrFunc[4096];
public static readonly InstrFunc[] THUMBInstrTable = new InstrFunc[1024];
static ARMInterpreter()
{
for ( int i = 0; i < ARMInstrTable.Length; i++ )
ARMInstrTable[i] = A_UNK;
for ( int i = 0; i < THUMBInstrTable.Length; i++ )
THUMBInstrTable[i] = T_UNK;
BuildAluTable();
BuildMultiplyTable();
BuildBranchTable();
BuildLoadStoreTable();
BuildSpecialsTable();
BuildThumbTable();
}
private static void BuildSpecialsTable()
{
for ( int low = 0; low < 16; low++ )
{
ARMInstrTable[(0x32 << 4) | low] = A_MSR_IMM;
ARMInstrTable[(0x36 << 4) | low] = A_MSR_IMM;
}
for ( int h = 0xE0; h <= 0xEF; h++ )
{
InstrFunc fn = (h & 1) == 0 ? A_MCR : A_MRC;
for ( int low = 1; low < 16; low += 2 )
ARMInstrTable[(h << 4) | low] = fn;
}
for ( int h = 0xF0; h <= 0xFF; h++ )
{
for ( int low = 0; low < 16; low++ )
ARMInstrTable[(h << 4) | low] = A_SVC;
}
}
public static void A_UNK( ARM cpu )
{
Platform.Log( LogLevel.Warn, $"undefined ARM{(cpu.Num != 0 ? 7 : 9)} instruction {cpu.CurInstr:X8} @ {cpu.R[15] - 8:X8}" );
uint oldcpsr = cpu.CPSR;
cpu.CPSR &= ~0xBFu;
cpu.CPSR |= 0x9B;
cpu.UpdateMode( oldcpsr, cpu.CPSR );
cpu.R_UND[2] = oldcpsr;
cpu.R[14] = cpu.R[15] - 4;
cpu.JumpTo( cpu.ExceptionBase + 0x04 );
}
public static void T_UNK( ARM cpu )
{
Platform.Log( LogLevel.Warn, $"undefined THUMB{(cpu.Num != 0 ? 7 : 9)} instruction {cpu.CurInstr:X4} @ {cpu.R[15] - 4:X8}" );
uint oldcpsr = cpu.CPSR;
cpu.CPSR &= ~0xBFu;
cpu.CPSR |= 0x9B;
cpu.UpdateMode( oldcpsr, cpu.CPSR );
cpu.R_UND[2] = oldcpsr;
cpu.R[14] = cpu.R[15] - 2;
cpu.JumpTo( cpu.ExceptionBase + 0x04 );
}
private static uint MsrMask( ARM cpu, bool spsr )
{
uint mask = 0;
if ( (cpu.CurInstr & (1 << 16)) != 0 ) mask |= 0x000000FF;
if ( (cpu.CurInstr & (1 << 17)) != 0 ) mask |= 0x0000FF00;
if ( (cpu.CurInstr & (1 << 18)) != 0 ) mask |= 0x00FF0000;
if ( (cpu.CurInstr & (1 << 19)) != 0 ) mask |= 0xFF000000;
if ( !spsr )
mask &= 0xFFFFFFDF;
if ( (cpu.CPSR & 0x1F) == 0x10 )
mask &= 0xFFFFFF00;
return mask;
}
private static void WritePsr( ARM cpu, ref uint psr, uint mask, uint val, bool isCpsr )
{
uint oldpsr = psr;
psr = (psr & ~mask) | (val & mask);
if ( isCpsr )
cpu.UpdateMode( oldpsr, cpu.CPSR );
}
private static void DoMsr( ARM cpu, uint val )
{
bool spsr = (cpu.CurInstr & (1 << 22)) != 0;
uint mask = MsrMask( cpu, spsr );
val |= 0x00000010;
if ( !spsr )
{
WritePsr( cpu, ref cpu.CPSR, mask, val, true );
}
else
{
switch ( cpu.CPSR & 0x1F )
{
case 0x11: WritePsr( cpu, ref cpu.R_FIQ[7], mask, val, false ); break;
case 0x12: WritePsr( cpu, ref cpu.R_IRQ[2], mask, val, false ); break;
case 0x13: WritePsr( cpu, ref cpu.R_SVC[2], mask, val, false ); break;
case 0x14:
case 0x15:
case 0x16:
case 0x17: WritePsr( cpu, ref cpu.R_ABT[2], mask, val, false ); break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: WritePsr( cpu, ref cpu.R_UND[2], mask, val, false ); break;
default: cpu.AddCycles_C(); return;
}
}
cpu.AddCycles_C();
}
public static void A_MSR_IMM( ARM cpu )
{
uint val = ARM.ROR( cpu.CurInstr & 0xFF, (cpu.CurInstr >> 7) & 0x1E );
DoMsr( cpu, val );
}
public static void A_MSR_REG( ARM cpu )
{
uint val = cpu.R[cpu.CurInstr & 0xF];
DoMsr( cpu, val );
}
public static void A_MRS( ARM cpu )
{
uint psr;
if ( (cpu.CurInstr & (1 << 22)) != 0 )
{
switch ( cpu.CPSR & 0x1F )
{
case 0x11: psr = cpu.R_FIQ[7]; break;
case 0x12: psr = cpu.R_IRQ[2]; break;
case 0x13: psr = cpu.R_SVC[2]; break;
case 0x14:
case 0x15:
case 0x16:
case 0x17: psr = cpu.R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = cpu.R_UND[2]; break;
default: psr = cpu.CPSR; break;
}
}
else
{
psr = cpu.CPSR;
}
cpu.R[(cpu.CurInstr >> 12) & 0xF] = psr;
cpu.AddCycles_C();
}
public static void A_MCR( ARM cpu )
{
if ( (cpu.CPSR & 0x1F) == 0x10 )
{
A_UNK( cpu );
return;
}
uint cp = (cpu.CurInstr >> 8) & 0xF;
uint cn = (cpu.CurInstr >> 16) & 0xF;
uint cm = cpu.CurInstr & 0xF;
uint cpinfo = (cpu.CurInstr >> 5) & 0x7;
if ( cpu.Num == 0 && cp == 15 )
{
((ARMv5)cpu).CP15Write( (cn << 8) | (cm << 4) | cpinfo, cpu.R[(cpu.CurInstr >> 12) & 0xF] );
}
else if ( cpu.Num == 1 && cp == 14 )
{
Platform.Log( LogLevel.Debug, $"MCR p14,{cn},{cm},{cpinfo} on ARM7" );
}
else
{
Platform.Log( LogLevel.Warn, $"bad MCR opcode p{cp},{cn},{cm},{cpinfo} on ARM{(cpu.Num != 0 ? 7 : 9)}" );
A_UNK( cpu );
return;
}
cpu.AddCycles_CI( 1 + 1 );
}
public static void A_MRC( ARM cpu )
{
if ( (cpu.CPSR & 0x1F) == 0x10 )
{
A_UNK( cpu );
return;
}
uint cp = (cpu.CurInstr >> 8) & 0xF;
uint cn = (cpu.CurInstr >> 16) & 0xF;
uint cm = cpu.CurInstr & 0xF;
uint cpinfo = (cpu.CurInstr >> 5) & 0x7;
if ( cpu.Num == 0 && cp == 15 )
{
cpu.R[(cpu.CurInstr >> 12) & 0xF] = ((ARMv5)cpu).CP15Read( (cn << 8) | (cm << 4) | cpinfo );
}
else if ( cpu.Num == 1 && cp == 14 )
{
Platform.Log( LogLevel.Debug, $"MRC p14,{cn},{cm},{cpinfo} on ARM7" );
}
else
{
Platform.Log( LogLevel.Warn, $"bad MRC opcode p{cp},{cn},{cm},{cpinfo} on ARM{(cpu.Num != 0 ? 7 : 9)}" );
A_UNK( cpu );
return;
}
cpu.AddCycles_CI( 2 + 1 );
}
public static void A_SVC( ARM cpu )
{
uint oldcpsr = cpu.CPSR;
cpu.CPSR &= ~0xBFu;
cpu.CPSR |= 0x93;
cpu.UpdateMode( oldcpsr, cpu.CPSR );
cpu.R_SVC[2] = oldcpsr;
cpu.R[14] = cpu.R[15] - 4;
cpu.JumpTo( cpu.ExceptionBase + 0x08 );
}
public static void T_SVC( ARM cpu )
{
uint oldcpsr = cpu.CPSR;
cpu.CPSR &= ~0xBFu;
cpu.CPSR |= 0x93;
cpu.UpdateMode( oldcpsr, cpu.CPSR );
cpu.R_SVC[2] = oldcpsr;
cpu.R[14] = cpu.R[15] - 2;
cpu.JumpTo( cpu.ExceptionBase + 0x08 );
}
}