Emulator/ArmCore.Arm.cs
namespace sGBA;
public partial class ArmCore
{
private void ExecuteArm()
{
uint opcode = _prefetch0;
_prefetch0 = _prefetch1;
_prefetch1 = Memory.Load32( Gprs[15] );
OpenBusPrefetch = _prefetch1;
Cycles += 1 + Memory.WaitstatesSeq32[(Gprs[15] >> 24) & 0xF];
uint cond = opcode >> 28;
if ( cond != 0xE && !CheckCondition( cond ) )
{
Gprs[15] += 4;
return;
}
uint group = (opcode >> 25) & 7;
switch ( group )
{
case 0:
if ( (opcode & 0x0FC000F0) == 0x00000090 )
ArmMultiply( opcode );
else if ( (opcode & 0x0F8000F0) == 0x00800090 )
ArmMultiplyLong( opcode );
else if ( (opcode & 0x0FB00FF0) == 0x01000090 )
ArmSwap( opcode );
else if ( (opcode & 0x0E000090) == 0x00000090 && (opcode & 0x00000060) != 0 )
ArmHalfwordTransfer( opcode );
else if ( (opcode & 0x0FBF0FFF) == 0x010F0000 )
ArmMrs( opcode );
else if ( (opcode & 0x0FFFFFF0) == 0x012FFF10 )
ArmBx( opcode );
else if ( (opcode & 0x0DB0F000) == 0x0120F000 )
ArmMsr( opcode );
else
ArmDataProcessing( opcode );
break;
case 1:
if ( (opcode & 0x0FB0F000) == 0x0320F000 )
ArmMsr( opcode );
else
ArmDataProcessing( opcode );
break;
case 2:
ArmSingleTransfer( opcode );
break;
case 3:
if ( (opcode & 0x10) != 0 )
{
break;
}
ArmSingleTransfer( opcode );
break;
case 4:
ArmBlockTransfer( opcode );
break;
case 5:
ArmBranch( opcode );
break;
case 6:
break;
case 7:
if ( (opcode & 0x0F000000) == 0x0F000000 )
ArmSwi( opcode );
break;
}
if ( !_prefetchFlushed )
Gprs[15] += 4;
}
private void ArmDataProcessing( uint opcode )
{
uint op = (opcode >> 21) & 0xF;
bool setFlags = ((opcode >> 20) & 1) != 0;
uint rn = (opcode >> 16) & 0xF;
uint rd = (opcode >> 12) & 0xF;
uint operand1 = Gprs[rn];
bool isRegShift = ((opcode >> 25) & 1) == 0 && ((opcode >> 4) & 1) != 0;
if ( rn == 15 && isRegShift ) operand1 += 4;
uint operand2;
bool shiftCarry = FlagC;
if ( ((opcode >> 25) & 1) != 0 )
{
uint imm = opcode & 0xFF;
uint rotate = ((opcode >> 8) & 0xF) * 2;
if ( rotate != 0 )
{
operand2 = Ror( imm, (int)rotate );
shiftCarry = (operand2 & 0x80000000) != 0;
}
else
{
operand2 = imm;
}
}
else
{
operand2 = GetShifterOperand( opcode, out shiftCarry );
}
uint result;
bool writeDest = true;
switch ( op )
{
case 0x0:
result = operand1 & operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
break;
case 0x1:
result = operand1 ^ operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
break;
case 0x2:
result = operand1 - operand2;
if ( setFlags ) SetSubFlags( operand1, operand2, result );
break;
case 0x3:
result = operand2 - operand1;
if ( setFlags ) SetSubFlags( operand2, operand1, result );
break;
case 0x4:
result = operand1 + operand2;
if ( setFlags ) SetAddFlags( operand1, operand2, result );
break;
case 0x5:
result = operand1 + operand2 + (FlagC ? 1u : 0u);
if ( setFlags ) SetAdcFlags( operand1, operand2, FlagC );
break;
case 0x6:
result = operand1 - operand2 - (FlagC ? 0u : 1u);
if ( setFlags ) SetSbcFlags( operand1, operand2, FlagC );
break;
case 0x7:
result = operand2 - operand1 - (FlagC ? 0u : 1u);
if ( setFlags ) SetSbcFlags( operand2, operand1, FlagC );
break;
case 0x8:
result = operand1 & operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
writeDest = false;
break;
case 0x9:
result = operand1 ^ operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
writeDest = false;
break;
case 0xA:
result = operand1 - operand2;
if ( setFlags ) SetSubFlags( operand1, operand2, result );
writeDest = false;
break;
case 0xB:
result = operand1 + operand2;
if ( setFlags ) SetAddFlags( operand1, operand2, result );
writeDest = false;
break;
case 0xC:
result = operand1 | operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
break;
case 0xD:
result = operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
break;
case 0xE:
result = operand1 & ~operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
break;
case 0xF:
result = ~operand2;
if ( setFlags ) SetLogicFlags( result, shiftCarry );
break;
default:
result = 0;
break;
}
if ( writeDest )
{
Gprs[rd] = result;
if ( rd == 15 )
{
if ( setFlags && PrivilegeMode != PrivilegeMode.User && PrivilegeMode != PrivilegeMode.System )
{
uint spsr = GetSpsr();
SetCpsr( spsr );
}
_prefetchFlushed = true;
}
}
}
private uint GetShifterOperand( uint opcode, out bool carryOut )
{
uint rm = opcode & 0xF;
uint val = Gprs[rm];
uint shiftType = (opcode >> 5) & 3;
uint shiftAmount;
bool regShift = ((opcode >> 4) & 1) != 0;
if ( rm == 15 && regShift ) val += 4;
if ( regShift )
{
uint rs = (opcode >> 8) & 0xF;
shiftAmount = Gprs[rs] & 0xFF;
Cycles += 1;
if ( shiftAmount == 0 )
{
carryOut = FlagC;
return val;
}
switch ( shiftType )
{
case 0:
if ( shiftAmount < 32 ) { carryOut = ((val >> (int)(32 - shiftAmount)) & 1) != 0; return val << (int)shiftAmount; }
if ( shiftAmount == 32 ) { carryOut = (val & 1) != 0; return 0; }
carryOut = false; return 0;
case 1:
if ( shiftAmount < 32 ) { carryOut = ((val >> (int)(shiftAmount - 1)) & 1) != 0; return val >> (int)shiftAmount; }
if ( shiftAmount == 32 ) { carryOut = (val & 0x80000000) != 0; return 0; }
carryOut = false; return 0;
case 2:
if ( shiftAmount >= 32 ) { carryOut = (val & 0x80000000) != 0; return carryOut ? 0xFFFFFFFF : 0; }
carryOut = ((val >> (int)(shiftAmount - 1)) & 1) != 0;
return (uint)((int)val >> (int)shiftAmount);
case 3:
shiftAmount &= 31;
if ( shiftAmount == 0 ) { carryOut = (val & 0x80000000) != 0; return val; }
uint ror = Ror( val, (int)shiftAmount );
carryOut = (ror & 0x80000000) != 0;
return ror;
}
}
else
{
shiftAmount = (opcode >> 7) & 0x1F;
switch ( shiftType )
{
case 0:
if ( shiftAmount == 0 ) { carryOut = FlagC; return val; }
carryOut = ((val >> (int)(32 - shiftAmount)) & 1) != 0;
return val << (int)shiftAmount;
case 1:
if ( shiftAmount == 0 ) { carryOut = (val & 0x80000000) != 0; return 0; }
carryOut = ((val >> (int)(shiftAmount - 1)) & 1) != 0;
return val >> (int)shiftAmount;
case 2:
if ( shiftAmount == 0 ) { carryOut = (val & 0x80000000) != 0; return carryOut ? 0xFFFFFFFF : 0; }
carryOut = ((val >> (int)(shiftAmount - 1)) & 1) != 0;
return (uint)((int)val >> (int)shiftAmount);
case 3:
if ( shiftAmount == 0 )
{
carryOut = (val & 1) != 0;
return (FlagC ? 0x80000000u : 0) | (val >> 1);
}
uint ror = Ror( val, (int)shiftAmount );
carryOut = (ror & 0x80000000) != 0;
return ror;
}
}
carryOut = FlagC;
return val;
}
private void ArmMultiply( uint opcode )
{
uint rd = (opcode >> 16) & 0xF;
uint rn = (opcode >> 12) & 0xF;
uint rs = (opcode >> 8) & 0xF;
uint rm = opcode & 0xF;
bool accumulate = ((opcode >> 21) & 1) != 0;
bool setFlags = ((opcode >> 20) & 1) != 0;
uint rsVal = Gprs[rs];
uint result = Gprs[rm] * rsVal;
if ( accumulate ) result += Gprs[rn];
Gprs[rd] = result;
if ( setFlags )
{
FlagN = (result & 0x80000000) != 0;
FlagZ = result == 0;
}
int mulWait = MultiplyExtraCycles( rsVal ) + (accumulate ? 1 : 0);
Cycles += Memory.MemoryStall( Gprs[15], mulWait );
int mulCr = (int)((Gprs[15] >> 24) & 0xF);
Cycles += Memory.WaitstatesNonseq32[mulCr] - Memory.WaitstatesSeq32[mulCr];
}
private void ArmMultiplyLong( uint opcode )
{
uint rdHi = (opcode >> 16) & 0xF;
uint rdLo = (opcode >> 12) & 0xF;
uint rs = (opcode >> 8) & 0xF;
uint rm = opcode & 0xF;
bool isSigned = ((opcode >> 22) & 1) != 0;
bool accumulate = ((opcode >> 21) & 1) != 0;
bool setFlags = ((opcode >> 20) & 1) != 0;
long result;
if ( isSigned )
result = (long)(int)Gprs[rm] * (int)Gprs[rs];
else
result = (long)((ulong)Gprs[rm] * Gprs[rs]);
if ( accumulate )
result += (long)(((ulong)Gprs[rdHi] << 32) | Gprs[rdLo]);
Gprs[rdLo] = (uint)result;
Gprs[rdHi] = (uint)(result >> 32);
if ( setFlags )
{
FlagN = (Gprs[rdHi] & 0x80000000) != 0;
FlagZ = result == 0;
}
int longWait = accumulate ? 2 : 1;
if ( isSigned )
longWait += MultiplyExtraCycles( Gprs[rs] );
else
longWait += MultiplyExtraCyclesUnsigned( Gprs[rs] );
Cycles += Memory.MemoryStall( Gprs[15], longWait );
int longMulCr = (int)((Gprs[15] >> 24) & 0xF);
Cycles += Memory.WaitstatesNonseq32[longMulCr] - Memory.WaitstatesSeq32[longMulCr];
}
private void ArmSwap( uint opcode )
{
uint rn = (opcode >> 16) & 0xF;
uint rd = (opcode >> 12) & 0xF;
uint rm = opcode & 0xF;
bool byteSwap = ((opcode >> 22) & 1) != 0;
uint addr = Gprs[rn];
if ( byteSwap )
{
byte tmp = Memory.Load8( addr );
Memory.Store8( addr, (byte)Gprs[rm] );
Gprs[rd] = tmp;
}
else
{
uint tmp = ReadWordRotated( addr );
Memory.Store32( addr, Gprs[rm] );
Gprs[rd] = tmp;
}
int dr = (int)((addr >> 24) & 0xF);
int swpWait = byteSwap ? Memory.WaitstatesNonseq16[dr] : Memory.WaitstatesNonseq32[dr];
swpWait = swpWait * 2 + 3;
if ( dr < 8 )
swpWait = Memory.MemoryStall( Gprs[15], swpWait );
Cycles += swpWait;
int cr = (int)((Gprs[15] >> 24) & 0xF);
Cycles += Memory.WaitstatesNonseq32[cr] - Memory.WaitstatesSeq32[cr];
}
private void ArmHalfwordTransfer( uint opcode )
{
uint rn = (opcode >> 16) & 0xF;
uint rd = (opcode >> 12) & 0xF;
bool preIndex = ((opcode >> 24) & 1) != 0;
bool addOffset = ((opcode >> 23) & 1) != 0;
bool immediate = ((opcode >> 22) & 1) != 0;
bool writeBack = ((opcode >> 21) & 1) != 0;
bool isLoad = ((opcode >> 20) & 1) != 0;
uint sh = (opcode >> 5) & 3;
uint offset;
if ( immediate )
offset = ((opcode >> 4) & 0xF0) | (opcode & 0xF);
else
offset = Gprs[opcode & 0xF];
uint addr = Gprs[rn];
if ( preIndex )
addr = addOffset ? addr + offset : addr - offset;
switch ( sh )
{
case 1:
if ( isLoad )
{
Gprs[rd] = Memory.Load16( addr );
if ( (addr & 1) != 0 ) Gprs[rd] = Ror( Gprs[rd], 8 );
}
else
{
uint val = Gprs[rd];
if ( rd == 15 ) val += 4;
Memory.Store16( addr, (ushort)val );
}
break;
case 2:
if ( isLoad )
Gprs[rd] = (uint)(sbyte)Memory.Load8( addr );
break;
case 3:
if ( isLoad )
{
if ( (addr & 1) != 0 )
Gprs[rd] = (uint)(sbyte)Memory.Load8( addr );
else
Gprs[rd] = (uint)(short)Memory.Load16( addr );
}
break;
}
if ( !preIndex )
{
uint newAddr = addOffset ? Gprs[rn] + offset : Gprs[rn] - offset;
Gprs[rn] = newAddr;
}
else if ( writeBack )
{
Gprs[rn] = addr;
}
if ( isLoad && rd == 15 ) _prefetchFlushed = true;
int dataRegion = (int)((addr >> 24) & 0xF);
{
int wait = Memory.WaitstatesNonseq16[dataRegion] + (isLoad ? 2 : 1);
if ( dataRegion < 8 )
wait = Memory.MemoryStall( Gprs[15], wait );
Cycles += wait;
}
int cr = (int)((Gprs[15] >> 24) & 0xF);
Cycles += Memory.WaitstatesNonseq32[cr] - Memory.WaitstatesSeq32[cr];
}
private void ArmSingleTransfer( uint opcode )
{
uint rn = (opcode >> 16) & 0xF;
uint rd = (opcode >> 12) & 0xF;
bool immediate = ((opcode >> 25) & 1) == 0;
bool preIndex = ((opcode >> 24) & 1) != 0;
bool addOffset = ((opcode >> 23) & 1) != 0;
bool byteTransfer = ((opcode >> 22) & 1) != 0;
bool writeBack = ((opcode >> 21) & 1) != 0;
bool isLoad = ((opcode >> 20) & 1) != 0;
uint offset;
if ( immediate )
{
offset = opcode & 0xFFF;
}
else
{
uint rm = opcode & 0xF;
uint shiftType = (opcode >> 5) & 3;
uint shiftAmount = (opcode >> 7) & 0x1F;
offset = ApplyShift( Gprs[rm], shiftType, shiftAmount );
}
uint addr = Gprs[rn];
if ( rn == 15 ) addr = Gprs[15];
if ( preIndex )
addr = addOffset ? addr + offset : addr - offset;
if ( isLoad )
{
if ( byteTransfer )
Gprs[rd] = Memory.Load8( addr );
else
Gprs[rd] = ReadWordRotated( addr );
if ( rd == 15 ) _prefetchFlushed = true;
}
else
{
uint val = Gprs[rd];
if ( rd == 15 ) val += 4;
if ( byteTransfer )
Memory.Store8( addr, (byte)val );
else
Memory.Store32( addr, val );
}
if ( !preIndex )
{
Gprs[rn] = addOffset ? Gprs[rn] + offset : Gprs[rn] - offset;
}
else if ( writeBack )
{
Gprs[rn] = addr;
}
int dataRegion = (int)((addr >> 24) & 0xF);
{
int wait = byteTransfer ? Memory.WaitstatesNonseq16[dataRegion] : Memory.WaitstatesNonseq32[dataRegion];
wait += isLoad ? 2 : 1;
if ( dataRegion < 8 )
wait = Memory.MemoryStall( Gprs[15], wait );
Cycles += wait;
}
int cr = (int)((Gprs[15] >> 24) & 0xF);
Cycles += Memory.WaitstatesNonseq32[cr] - Memory.WaitstatesSeq32[cr];
}
private void ArmBlockTransfer( uint opcode )
{
uint rn = (opcode >> 16) & 0xF;
bool preIndex = ((opcode >> 24) & 1) != 0;
bool addOffset = ((opcode >> 23) & 1) != 0;
bool psr = ((opcode >> 22) & 1) != 0;
bool writeBack = ((opcode >> 21) & 1) != 0;
bool isLoad = ((opcode >> 20) & 1) != 0;
ushort regList = (ushort)(opcode & 0xFFFF);
int instructionRegion = (int)((Gprs[15] >> 24) & 0xF);
if ( regList == 0 )
{
uint origAddr = Gprs[rn];
if ( isLoad )
{
Gprs[15] = Memory.Load32( Gprs[rn] );
_prefetchFlushed = true;
}
else
{
Memory.Store32( Gprs[rn], Gprs[15] + 4 );
}
if ( addOffset )
Gprs[rn] += 0x40;
else
Gprs[rn] -= 0x40;
int dr0 = (int)((origAddr >> 24) & 0xF);
int seqWait = Memory.WaitstatesSeq32[dr0];
int emptyWait = 2 * seqWait - Memory.WaitstatesNonseq32[dr0] + 16 + (isLoad ? 1 : 0);
if ( dr0 < 8 )
emptyWait = Memory.MemoryStall( Gprs[15], emptyWait );
Cycles += emptyWait;
Cycles += Memory.WaitstatesNonseq32[instructionRegion] - Memory.WaitstatesSeq32[instructionRegion];
return;
}
int count = BitCount( regList );
uint baseAddr = Gprs[rn];
uint startAddr;
if ( addOffset )
startAddr = preIndex ? baseAddr + 4 : baseAddr;
else
startAddr = preIndex ? baseAddr - (uint)(count * 4) : baseAddr - (uint)(count * 4) + 4;
uint addr = startAddr;
bool useUserBank = psr && !(isLoad && (regList & (1 << 15)) != 0);
for ( int i = 0; i < 16; i++ )
{
if ( (regList & (1 << i)) == 0 ) continue;
if ( isLoad )
{
uint value = Memory.Load32( addr );
if ( useUserBank && i >= 8 && i <= 14 )
SetUserReg( i, value );
else
Gprs[i] = value;
if ( i == 15 )
{
_prefetchFlushed = true;
if ( psr && PrivilegeMode != PrivilegeMode.User && PrivilegeMode != PrivilegeMode.System )
{
uint spsr = GetSpsr();
SetCpsr( spsr );
}
}
}
else
{
uint val;
if ( useUserBank && i >= 8 && i <= 14 )
val = GetUserReg( i );
else
val = Gprs[i];
if ( i == 15 ) val += 4;
Memory.Store32( addr, val );
}
addr += 4;
}
if ( writeBack && !(isLoad && (regList & (1 << (int)rn)) != 0) )
{
if ( addOffset )
Gprs[rn] = baseAddr + (uint)(count * 4);
else
Gprs[rn] = baseAddr - (uint)(count * 4);
}
{
int blockRegion = (int)((startAddr >> 24) & 0xF);
int firstWait = Memory.WaitstatesNonseq32[blockRegion];
int seqWait = Memory.WaitstatesSeq32[blockRegion];
int blockWait = seqWait - firstWait + count * (seqWait + 1) + (isLoad ? 1 : 0);
uint endAddr = startAddr + (uint)(count * 4);
int endRegion = (int)((endAddr >> 24) & 0xF);
if ( endRegion < 8 )
blockWait = Memory.MemoryStall( Gprs[15], blockWait );
Cycles += blockWait;
}
Cycles += Memory.WaitstatesNonseq32[instructionRegion] - Memory.WaitstatesSeq32[instructionRegion];
}
private void ArmBranch( uint opcode )
{
bool link = ((opcode >> 24) & 1) != 0;
int offset = (int)(opcode & 0x00FFFFFF);
if ( (offset & 0x800000) != 0 )
offset |= unchecked((int)0xFF000000);
offset <<= 2;
if ( link )
Gprs[14] = Gprs[15] - 4;
Gprs[15] = (uint)(Gprs[15] + offset);
_prefetchFlushed = true;
}
private void ArmBx( uint opcode )
{
uint rm = opcode & 0xF;
uint addr = Gprs[rm];
ThumbMode = (addr & 1) != 0;
Gprs[15] = addr & ~1u;
_prefetchFlushed = true;
}
private void ArmSwi( uint opcode )
{
uint comment = (opcode >> 16) & 0xFF;
if ( Gba.Bios.HandleSwi( comment ) )
return;
uint savedCpsr = GetCpsrRaw();
SetPrivilegeMode( PrivilegeMode.Supervisor );
SetSpsr( savedCpsr );
Gprs[14] = Gprs[15] - 4;
IrqDisable = true;
ThumbMode = false;
Gprs[15] = GbaConstants.BaseSwi;
_prefetchFlushed = true;
}
private void ArmMrs( uint opcode )
{
uint rd = (opcode >> 12) & 0xF;
bool useSPSR = ((opcode >> 22) & 1) != 0;
Gprs[rd] = useSPSR ? GetSpsr() : GetCpsrRaw();
}
private void ArmMsr( uint opcode )
{
bool useSPSR = ((opcode >> 22) & 1) != 0;
uint mask = 0;
if ( (opcode & 0x00080000u) != 0 ) mask |= 0xFF000000u;
if ( (opcode & 0x00010000u) != 0 ) mask |= 0x000000FFu;
uint operand;
if ( ((opcode >> 25) & 1) != 0 )
{
uint imm = opcode & 0xFF;
uint rotate = ((opcode >> 8) & 0xF) * 2;
operand = Ror( imm, (int)rotate );
}
else
{
operand = Gprs[opcode & 0xF];
}
if ( useSPSR )
{
mask &= PsrUserMask | PsrPrivMask | PsrStateMask;
uint spsr = GetSpsr();
SetSpsr( (spsr & ~mask) | (operand & mask) | 0x00000010u );
}
else
{
if ( (mask & PsrUserMask) != 0 )
{
FlagN = (operand & 0x80000000u) != 0;
FlagZ = (operand & 0x40000000u) != 0;
FlagC = (operand & 0x20000000u) != 0;
FlagV = (operand & 0x10000000u) != 0;
}
if ( (mask & PsrStateMask) != 0 )
{
ThumbMode = (operand & PsrStateMask) != 0;
}
if ( PrivilegeMode != PrivilegeMode.User && (mask & PsrPrivMask) != 0 )
{
SetPrivilegeMode( (PrivilegeMode)((operand & 0x0Fu) | 0x10u) );
IrqDisable = (operand & 0x80u) != 0;
FiqDisable = (operand & 0x40u) != 0;
}
Gba.Io.TestIrq();
if ( ThumbMode )
_prefetchFlushed = true;
else
{
_prefetch0 = Memory.Load32( Gprs[15] - 4 );
_prefetch1 = Memory.Load32( Gprs[15] );
OpenBusPrefetch = _prefetch1;
}
}
}
}