Cores/Nds/NDS.Timers.cs
namespace Emulotl.Nds;
public sealed partial class NDS
{
public struct Timer
{
public ushort Reload;
public ushort Cnt;
public uint Counter;
public int CycleShift;
}
private const int Arm9ClockShift = 1;
public Timer[] Timers = new Timer[8];
public byte[] TimerCheckMask = new byte[2];
public long[] TimerTimestamp = new long[2];
private static readonly int[] TimerPrescaler = [0, 6, 8, 10];
private void ResetTimers()
{
Array.Clear( Timers );
TimerCheckMask[0] = 0;
TimerCheckMask[1] = 0;
TimerTimestamp[0] = 0;
TimerTimestamp[1] = 0;
}
private void HandleTimerOverflow( uint tid )
{
ref Timer timer = ref Timers[tid];
timer.Counter += (uint)(timer.Reload << 10);
if ( (timer.Cnt & (1 << 6)) != 0 )
SetIRQ( tid >> 2, IRQ.Timer0 + (int)(tid & 0x3) );
if ( (tid & 0x3) == 3 )
return;
for (; ; )
{
tid++;
timer = ref Timers[tid];
if ( (timer.Cnt & 0x84) != 0x84 )
break;
timer.Counter += 1 << 10;
if ( (timer.Counter >> 26) == 0 )
break;
timer.Counter = (uint)(timer.Reload << 10);
if ( (timer.Cnt & (1 << 6)) != 0 )
SetIRQ( tid >> 2, IRQ.Timer0 + (int)(tid & 0x3) );
if ( (tid & 0x3) == 3 )
break;
}
}
private void RunTimer( uint tid, int cycles )
{
ref Timer timer = ref Timers[tid];
timer.Counter += (uint)(cycles << timer.CycleShift);
while ( (timer.Counter >> 26) != 0 )
{
timer.Counter -= 1 << 26;
HandleTimerOverflow( tid );
}
}
public void RunTimers( uint cpu )
{
uint timermask = TimerCheckMask[cpu];
int cycles;
if ( cpu == 0 )
cycles = (int)((ARM9Timestamp >> Arm9ClockShift) - TimerTimestamp[0]);
else
cycles = (int)(ARM7Timestamp - TimerTimestamp[1]);
if ( (timermask & 0x1) != 0 ) RunTimer( (cpu << 2) + 0, cycles );
if ( (timermask & 0x2) != 0 ) RunTimer( (cpu << 2) + 1, cycles );
if ( (timermask & 0x4) != 0 ) RunTimer( (cpu << 2) + 2, cycles );
if ( (timermask & 0x8) != 0 ) RunTimer( (cpu << 2) + 3, cycles );
TimerTimestamp[cpu] += cycles;
}
public ushort TimerGetCounter( uint timer )
{
RunTimers( timer >> 2 );
uint ret = Timers[timer].Counter;
return (ushort)(ret >> 10);
}
public void TimerStart( uint id, ushort cnt )
{
ref Timer timer = ref Timers[id];
ushort curstart = (ushort)(timer.Cnt & (1 << 7));
ushort newstart = (ushort)(cnt & (1 << 7));
RunTimers( id >> 2 );
timer.Cnt = cnt;
timer.CycleShift = 10 - TimerPrescaler[cnt & 0x03];
if ( curstart == 0 && newstart != 0 )
timer.Counter = (uint)(timer.Reload << 10);
if ( (cnt & 0x84) == 0x80 )
TimerCheckMask[id >> 2] |= (byte)(0x01 << (int)(id & 0x3));
else
TimerCheckMask[id >> 2] &= (byte)~(0x01 << (int)(id & 0x3));
}
}