Cores/Nds/ARMInterpreter_ALU.cs
namespace Emulotl.Nds;
public static partial class ARMInterpreter
{
private static bool CarryAdd( uint a, uint b ) => (0xFFFFFFFF - a) < b;
private static bool CarrySub( uint a, uint b ) => a >= b;
private static bool OverflowAdd( uint a, uint b )
{
uint res = a + b;
return ((a ^ b) & 0x80000000) == 0 && ((a ^ res) & 0x80000000) != 0;
}
private static bool OverflowSub( uint a, uint b )
{
uint res = a - b;
return ((a ^ b) & 0x80000000) != 0 && ((a ^ res) & 0x80000000) != 0;
}
private static bool OverflowAdc( uint a, uint b, uint carry )
{
long fullResult = (long)(int)a + (int)b + carry;
uint res = a + b + carry;
return (int)res != fullResult;
}
private static bool OverflowSbc( uint a, uint b, uint carry )
{
long fullResult = (long)(int)a - (int)b - carry;
uint res = a - b - carry;
return (int)res != fullResult;
}
private static void Shift( ARM cpu, ref uint x, uint s, int mode, bool isReg, bool setC )
{
if ( !isReg )
{
switch ( mode )
{
case 0:
if ( setC )
{
if ( s > 0 ) { cpu.SetC( (x & (1u << (int)(32 - s))) != 0 ); x <<= (int)s; }
}
else x <<= (int)s;
break;
case 1:
if ( setC )
{
if ( s == 0 ) { cpu.SetC( (x & 0x80000000) != 0 ); x = 0; }
else { cpu.SetC( (x & (1u << (int)(s - 1))) != 0 ); x >>= (int)s; }
}
else { if ( s == 0 ) x = 0; else x >>= (int)s; }
break;
case 2:
if ( setC )
{
if ( s == 0 ) { cpu.SetC( (x & 0x80000000) != 0 ); x = (uint)((int)x >> 31); }
else { cpu.SetC( (x & (1u << (int)(s - 1))) != 0 ); x = (uint)((int)x >> (int)s); }
}
else { if ( s == 0 ) x = (uint)((int)x >> 31); else x = (uint)((int)x >> (int)s); }
break;
case 3:
if ( setC )
{
if ( s == 0 ) { uint newc = x & 1; x = (x >> 1) | ((cpu.CPSR & 0x20000000) << 2); cpu.SetC( newc != 0 ); }
else { cpu.SetC( (x & (1u << (int)(s - 1))) != 0 ); x = ARM.ROR( x, s ); }
}
else
{
if ( s == 0 ) x = (x >> 1) | ((cpu.CPSR & 0x20000000) << 2);
else x = ARM.ROR( x, s );
}
break;
}
}
else
{
switch ( mode )
{
case 0:
if ( setC )
{
if ( s > 31 ) { cpu.SetC( s > 32 ? false : (x & 1) != 0 ); x = 0; }
else if ( s > 0 ) { cpu.SetC( (x & (1u << (int)(32 - s))) != 0 ); x <<= (int)s; }
}
else { if ( s > 31 ) x = 0; else x <<= (int)s; }
break;
case 1:
if ( setC )
{
if ( s > 31 ) { cpu.SetC( s > 32 ? false : (x & 0x80000000) != 0 ); x = 0; }
else if ( s > 0 ) { cpu.SetC( (x & (1u << (int)(s - 1))) != 0 ); x >>= (int)s; }
}
else { if ( s > 31 ) x = 0; else x >>= (int)s; }
break;
case 2:
if ( setC )
{
if ( s > 31 ) { cpu.SetC( (x & 0x80000000) != 0 ); x = (uint)((int)x >> 31); }
else if ( s > 0 ) { cpu.SetC( (x & (1u << (int)(s - 1))) != 0 ); x = (uint)((int)x >> (int)s); }
}
else { if ( s > 31 ) x = (uint)((int)x >> 31); else x = (uint)((int)x >> (int)s); }
break;
case 3:
if ( setC )
{
if ( s > 0 ) cpu.SetC( (x & (1u << (int)(s - 1))) != 0 );
x = ARM.ROR( x, s & 0x1F );
}
else x = ARM.ROR( x, s & 0x1F );
break;
}
}
}
private static uint Op2ImmRot( ARM cpu ) => ARM.ROR( cpu.CurInstr & 0xFF, (cpu.CurInstr >> 7) & 0x1E );
private static uint Op2ImmRotS( ARM cpu )
{
uint b = ARM.ROR( cpu.CurInstr & 0xFF, (cpu.CurInstr >> 7) & 0x1E );
if ( ((cpu.CurInstr >> 7) & 0x1E) != 0 )
cpu.SetC( (b & 0x80000000) != 0 );
return b;
}
private static uint Op2RegImm( ARM cpu, int mode, bool setC )
{
uint b = cpu.R[cpu.CurInstr & 0xF];
uint s = (cpu.CurInstr >> 7) & 0x1F;
Shift( cpu, ref b, s, mode, false, setC );
return b;
}
private static uint Op2RegReg( ARM cpu, int mode, bool setC )
{
uint b = cpu.R[cpu.CurInstr & 0xF];
if ( (cpu.CurInstr & 0xF) == 15 ) b += 4;
uint s = cpu.R[(cpu.CurInstr >> 8) & 0xF] & 0xFF;
Shift( cpu, ref b, s, mode, true, setC );
return b;
}
private static void Cycles( ARM cpu, int c )
{
if ( c != 0 ) cpu.AddCycles_CI( c );
else cpu.AddCycles_C();
}
private static void WriteAlu( ARM cpu, uint res )
{
if ( ((cpu.CurInstr >> 12) & 0xF) == 15 ) cpu.JumpTo( res & ~1u );
else cpu.R[(cpu.CurInstr >> 12) & 0xF] = res;
}
private static void WriteAluS( ARM cpu, uint res )
{
if ( ((cpu.CurInstr >> 12) & 0xF) == 15 ) cpu.JumpTo( res, true );
else cpu.R[(cpu.CurInstr >> 12) & 0xF] = res;
}
private static uint Rn( ARM cpu ) => cpu.R[(cpu.CurInstr >> 16) & 0xF];
private static void Op_AND( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) & b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_AND_S( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) & b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_EOR( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) ^ b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_EOR_S( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) ^ b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_SUB( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) - b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_SUB_S( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint res = a - b; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarrySub( a, b ), OverflowSub( a, b ) ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_RSB( ARM cpu, uint b, int c ) { uint res = b - Rn( cpu ); Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_RSB_S( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint res = b - a; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarrySub( b, a ), OverflowSub( b, a ) ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_ADD( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) + b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_ADD_S( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint res = a + b; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarryAdd( a, b ), OverflowAdd( a, b ) ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_ADC( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) + b + ((cpu.CPSR & 0x20000000) != 0 ? 1u : 0u); Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_ADC_S( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint resTmp = a + b; uint carry = (cpu.CPSR & 0x20000000) != 0 ? 1u : 0u; uint res = resTmp + carry; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarryAdd( a, b ) | CarryAdd( resTmp, carry ), OverflowAdc( a, b, carry ) ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_SBC( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) - b - ((cpu.CPSR & 0x20000000) != 0 ? 0u : 1u); Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_SBC_S( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint resTmp = a - b; uint carry = (cpu.CPSR & 0x20000000) != 0 ? 0u : 1u; uint res = resTmp - carry; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarrySub( a, b ) && CarrySub( resTmp, carry ), OverflowSbc( a, b, carry ) ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_RSC( ARM cpu, uint b, int c ) { uint res = b - Rn( cpu ) - ((cpu.CPSR & 0x20000000) != 0 ? 0u : 1u); Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_RSC_S( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint resTmp = b - a; uint carry = (cpu.CPSR & 0x20000000) != 0 ? 0u : 1u; uint res = resTmp - carry; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarrySub( b, a ) && CarrySub( resTmp, carry ), OverflowSbc( b, a, carry ) ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_TST( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) & b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); }
private static void Op_TEQ( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) ^ b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); }
private static void Op_CMP( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint res = a - b; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarrySub( a, b ), OverflowSub( a, b ) ); Cycles( cpu, c ); }
private static void Op_CMN( ARM cpu, uint b, int c ) { uint a = Rn( cpu ); uint res = a + b; cpu.SetNZCV( (res & 0x80000000) != 0, res == 0, CarryAdd( a, b ), OverflowAdd( a, b ) ); Cycles( cpu, c ); }
private static void Op_ORR( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) | b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_ORR_S( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) | b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_MOV( ARM cpu, uint b, int c ) { Cycles( cpu, c ); WriteAlu( cpu, b ); }
private static void Op_MOV_S( ARM cpu, uint b, int c ) { cpu.SetNZ( (b & 0x80000000) != 0, b == 0 ); Cycles( cpu, c ); WriteAluS( cpu, b ); }
private static void Op_BIC( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) & ~b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_BIC_S( ARM cpu, uint b, int c ) { uint res = Rn( cpu ) & ~b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static void Op_MVN( ARM cpu, uint b, int c ) { uint res = ~b; Cycles( cpu, c ); WriteAlu( cpu, res ); }
private static void Op_MVN_S( ARM cpu, uint b, int c ) { uint res = ~b; cpu.SetNZ( (res & 0x80000000) != 0, res == 0 ); Cycles( cpu, c ); WriteAluS( cpu, res ); }
private static bool IsLogical( int op ) => op is 0 or 1 or 8 or 9 or 12 or 13 or 14 or 15;
private static Action<ARM, uint, int> AluBody( int op, int s ) => op switch
{
0 => s == 0 ? Op_AND : Op_AND_S,
1 => s == 0 ? Op_EOR : Op_EOR_S,
2 => s == 0 ? Op_SUB : Op_SUB_S,
3 => s == 0 ? Op_RSB : Op_RSB_S,
4 => s == 0 ? Op_ADD : Op_ADD_S,
5 => s == 0 ? Op_ADC : Op_ADC_S,
6 => s == 0 ? Op_SBC : Op_SBC_S,
7 => s == 0 ? Op_RSC : Op_RSC_S,
8 => Op_TST,
9 => Op_TEQ,
10 => Op_CMP,
11 => Op_CMN,
12 => s == 0 ? Op_ORR : Op_ORR_S,
13 => s == 0 ? Op_MOV : Op_MOV_S,
14 => s == 0 ? Op_BIC : Op_BIC_S,
_ => s == 0 ? Op_MVN : Op_MVN_S,
};
private static void BuildAluTable()
{
for ( int op = 0; op < 16; op++ )
{
bool isTest = op is >= 8 and <= 11;
bool logical = IsLogical( op );
for ( int s = 0; s <= 1; s++ )
{
if ( isTest && s == 0 )
continue;
Action<ARM, uint, int> body = AluBody( op, s );
bool carry = s == 1 && logical;
int hiReg = (op << 1) | s;
for ( int low = 0; low < 16; low++ )
{
int icode = (hiReg << 4) | low;
int mode = (low >> 1) & 3;
if ( (low & 1) == 0 )
ARMInstrTable[icode] = MakeRegImm( body, mode, carry );
else if ( low is 1 or 3 or 5 or 7 )
ARMInstrTable[icode] = MakeRegReg( body, mode, carry );
}
int hiImm = 0x20 | (op << 1) | s;
for ( int low = 0; low < 16; low++ )
ARMInstrTable[(hiImm << 4) | low] = MakeImm( body, carry );
}
}
}
private static InstrFunc MakeImm( Action<ARM, uint, int> body, bool carry )
=> cpu => body( cpu, carry ? Op2ImmRotS( cpu ) : Op2ImmRot( cpu ), 0 );
private static InstrFunc MakeRegImm( Action<ARM, uint, int> body, int mode, bool carry )
=> cpu => body( cpu, Op2RegImm( cpu, mode, carry ), 0 );
private static InstrFunc MakeRegReg( Action<ARM, uint, int> body, int mode, bool carry )
=> cpu => body( cpu, Op2RegReg( cpu, mode, carry ), 1 );
}