Cores/Nds/NDS.Scheduler.cs
namespace Emulotl.Nds;

public enum SysEvent
{
	Lcd,
	Spu,
	Wifi,
	DisplayFifo,
	RomTransfer,
	RomSpiTransfer,
	SpiTransfer,
	Div,
	Sqrt,
	Rtc,
	Timer9,
	Timer7,
	Dma9,
	Dma7,

	Count
}

public sealed partial class NDS
{
	private struct SchedEvent
	{
		public bool Active;
		public long Timestamp;
		public Action<uint> Func;
		public uint Param;
	}

	private readonly SchedEvent[] _schedList = new SchedEvent[(int)SysEvent.Count];
	private uint _schedMask;

	public int CurCPU;

	public long SysTimestamp { get; private set; }

	private void ResetScheduler()
	{
		SysTimestamp = 0;
		CurCPU = 0;
		Array.Clear( _schedList );
		_schedMask = 0;
	}

	public void ScheduleEvent( SysEvent id, bool periodic, long delay, Action<uint> func, uint param = 0 )
	{
		ref SchedEvent ev = ref _schedList[(int)id];

		if ( periodic )
			ev.Timestamp += delay;
		else
			ev.Timestamp = (CurCPU == 0 ? (ARM9Timestamp >> 1) : ARM7Timestamp) + delay;

		ev.Active = true;
		ev.Func = func;
		ev.Param = param;
		_schedMask |= 1u << (int)id;

		Reschedule( ev.Timestamp );
	}

	public void Reschedule( long target )
	{
		if ( CurCPU == 0 )
		{
			if ( target < (ARM9Target >> 1) )
				ARM9Target = target << 1;
		}
		else
		{
			if ( target < ARM7Target )
				ARM7Target = target;
		}
	}

	public void CancelEvent( SysEvent id )
	{
		_schedList[(int)id].Active = false;
		_schedMask &= ~(1u << (int)id);
	}

	public bool EventScheduled( SysEvent id ) => _schedList[(int)id].Active;

	public long NextEventTimestamp()
	{
		long min = long.MaxValue;
		uint mask = _schedMask;
		for ( int i = 0; mask != 0; i++, mask >>= 1 )
		{
			if ( (mask & 1) != 0 && _schedList[i].Timestamp < min )
				min = _schedList[i].Timestamp;
		}
		return min;
	}

	public void RunSystem( long target )
	{
		SysTimestamp = target;

		uint mask = _schedMask;
		for ( int i = 0; mask != 0; i++, mask >>= 1 )
		{
			if ( (mask & 1) != 0 )
			{
				ref SchedEvent ev = ref _schedList[i];
				if ( ev.Timestamp <= SysTimestamp )
				{
					ev.Active = false;
					_schedMask &= ~(1u << i);
					ev.Func?.Invoke( ev.Param );
				}
			}
		}
	}
}