Cores/Nds/ARMv5.cs
namespace Emulotl.Nds;
public sealed partial class ARMv5 : ARM
{
public uint CP15Control;
public uint RNGSeed;
public uint TraceProcessID;
public uint DTCMSetting, ITCMSetting;
public uint ITCMSize;
public uint DTCMBase, DTCMMask;
public int RegionCodeCycles;
public byte[] ITCM = new byte[NdsConstants.Arm9ItcmSize];
public byte[] DTCM = new byte[NdsConstants.Arm9DtcmSize];
public byte[] ICache = new byte[0x2000];
public uint[] ICacheTags = new uint[64 * 4];
public byte[] ICacheCount = new byte[64];
public uint PU_CodeCacheable;
public uint PU_DataCacheable;
public uint PU_DataCacheWrite;
public uint PU_CodeRW;
public uint PU_DataRW;
public uint[] PU_Region = new uint[8];
public byte[] PU_PrivMap = new byte[0x100000];
public byte[] PU_UserMap = new byte[0x100000];
public byte[] PU_Map;
public byte[,] MemTimings = new byte[0x100000, 4];
public ARMv5( NDS nds ) : base( 0, nds )
{
PU_Map = PU_PrivMap;
}
public override void Reset()
{
PU_Map = PU_PrivMap;
base.Reset();
}
public override void FillPipeline()
{
SetupCodeMem( R[15] );
if ( (CPSR & 0x20) != 0 )
{
if ( ((R[15] - 2) & 0x2) != 0 )
{
NextInstr[0] = CodeRead32( R[15] - 4, false ) >> 16;
NextInstr[1] = CodeRead32( R[15], false );
}
else
{
NextInstr[0] = CodeRead32( R[15] - 2, false );
NextInstr[1] = NextInstr[0] >> 16;
}
}
else
{
NextInstr[0] = CodeRead32( R[15] - 4, false );
NextInstr[1] = CodeRead32( R[15], false );
}
}
public override void JumpTo( uint addr, bool restorecpsr = false )
{
if ( restorecpsr )
{
RestoreCPSR();
if ( (CPSR & 0x20) != 0 ) addr |= 0x1;
else addr &= ~0x1u;
}
uint oldregion = R[15] >> 24;
uint newregion = addr >> 24;
RegionCodeCycles = MemTimings[addr >> 12, 0];
if ( (addr & 0x1) != 0 )
{
addr &= ~0x1u;
R[15] = addr + 2;
if ( newregion != oldregion ) SetupCodeMem( addr );
if ( (addr & 0x2) != 0 )
{
NextInstr[0] = CodeRead32( addr - 2, true ) >> 16;
Cycles += CodeCycles;
NextInstr[1] = CodeRead32( addr + 2, false );
Cycles += CodeCycles;
}
else
{
NextInstr[0] = CodeRead32( addr, true );
NextInstr[1] = NextInstr[0] >> 16;
Cycles += CodeCycles;
}
CPSR |= 0x20;
}
else
{
addr &= ~0x3u;
R[15] = addr + 4;
if ( newregion != oldregion ) SetupCodeMem( addr );
NextInstr[0] = CodeRead32( addr, true );
Cycles += CodeCycles;
NextInstr[1] = CodeRead32( addr + 4, false );
Cycles += CodeCycles;
CPSR &= ~0x20u;
}
if ( (PU_Map[addr >> 12] & 0x04) == 0 )
{
PrefetchAbort();
return;
}
NDS.MonitorARM9Jump( addr );
}
public void PrefetchAbort()
{
Platform.Log( LogLevel.Warn, $"ARM9: prefetch abort ({R[15]:X8})" );
uint oldcpsr = CPSR;
CPSR &= ~0xBFu;
CPSR |= 0x97;
UpdateMode( oldcpsr, CPSR );
if ( (PU_Map[ExceptionBase >> 12] & 0x04) == 0 )
{
Platform.Log( LogLevel.Error, "!!!!! EXCEPTION REGION NOT EXECUTABLE. THIS IS VERY BAD!!" );
Halt( 1 );
return;
}
R_ABT[2] = oldcpsr;
R[14] = R[15] + ((oldcpsr & 0x20) != 0 ? 4u : 0u);
JumpTo( ExceptionBase + 0x0C );
}
public void DataAbort()
{
Platform.Log( LogLevel.Warn, $"ARM9: data abort ({R[15]:X8})" );
uint oldcpsr = CPSR;
CPSR &= ~0xBFu;
CPSR |= 0x97;
UpdateMode( oldcpsr, CPSR );
R_ABT[2] = oldcpsr;
R[14] = R[15] + ((oldcpsr & 0x20) != 0 ? 4u : 0u);
JumpTo( ExceptionBase + 0x10 );
}
public void Execute()
{
if ( Halted != 0 )
{
if ( Halted == 2 )
{
Halted = 0;
}
else if ( NDS.HaltInterrupted( 0 ) )
{
Halted = 0;
if ( (NDS.IME[0] & 0x1) != 0 )
TriggerIRQ();
}
else
{
NDS.ARM9Timestamp = NDS.ARM9Target;
return;
}
}
while ( NDS.ARM9Timestamp < NDS.ARM9Target )
{
if ( (CPSR & 0x20) != 0 )
{
R[15] += 2;
CurInstr = NextInstr[0];
NextInstr[0] = NextInstr[1];
if ( (R[15] & 0x2) != 0 ) { NextInstr[1] >>= 16; CodeCycles = 0; }
else NextInstr[1] = CodeRead32( R[15], false );
uint icode = (CurInstr >> 6) & 0x3FF;
ARMInterpreter.THUMBInstrTable[(int)icode]( this );
}
else
{
R[15] += 4;
CurInstr = NextInstr[0];
NextInstr[0] = NextInstr[1];
NextInstr[1] = CodeRead32( R[15], false );
if ( CheckCondition( CurInstr >> 28 ) )
{
uint icode = ((CurInstr >> 4) & 0xF) | ((CurInstr >> 16) & 0xFF0);
ARMInterpreter.ARMInstrTable[(int)icode]( this );
}
else if ( (CurInstr & 0xFE000000) == 0xFA000000 )
{
ARMInterpreter.A_BLX_IMM( this );
}
else
{
AddCycles_C();
}
}
if ( Halted != 0 )
{
if ( Halted == 1 && NDS.ARM9Timestamp < NDS.ARM9Target )
NDS.ARM9Timestamp = NDS.ARM9Target;
break;
}
if ( IRQ != 0 )
TriggerIRQ();
NDS.ARM9Timestamp += Cycles;
Cycles = 0;
}
if ( Halted == 2 )
Halted = 0;
}
public override void AddCycles_C()
{
int numC = (R[15] & 0x2) != 0 ? 0 : CodeCycles;
Cycles += numC;
}
public override void AddCycles_CI( int numI )
{
int numC = (R[15] & 0x2) != 0 ? 0 : CodeCycles;
Cycles += numC + numI;
}
public override void AddCycles_CDI()
{
int numC = (R[15] & 0x2) != 0 ? 0 : CodeCycles;
int numD = DataCycles;
Cycles += Math.Max( numC + numD - 6, Math.Max( numC, numD ) );
}
public override void AddCycles_CD()
{
int numC = (R[15] & 0x2) != 0 ? 0 : CodeCycles;
int numD = DataCycles;
Cycles += Math.Max( numC + numD - 6, Math.Max( numC, numD ) );
}
protected override byte BusRead8( uint addr ) => NDS.ARM9Read8( addr );
protected override ushort BusRead16( uint addr ) => NDS.ARM9Read16( addr );
protected override uint BusRead32( uint addr ) => NDS.ARM9Read32( addr );
protected override void BusWrite8( uint addr, byte val ) => NDS.ARM9Write8( addr, val );
protected override void BusWrite16( uint addr, ushort val ) => NDS.ARM9Write16( addr, val );
protected override void BusWrite32( uint addr, uint val ) => NDS.ARM9Write32( addr, val );
}