Cores/Nds/NDS.cs
namespace Emulotl.Nds;
public sealed partial class NDS : IEmulatorCore
{
public ARMv5 ARM9 { get; }
public ARMv4 ARM7 { get; }
public GPU GPU { get; }
public GPU3D GPU3D { get; }
public SPU SPU { get; }
public Wifi Wifi { get; }
public byte[] MainRAM = new byte[NdsConstants.MainRamSize];
public byte[] SharedWRAM = new byte[NdsConstants.SharedWramSize];
public byte[] ARM7WRAM = new byte[NdsConstants.Arm7WramSize];
public ushort KeyInput = 0x03FF;
public ushort ExtKeyIn = 0x007F;
private readonly NdsScreen _topScreen;
private readonly NdsScreen _bottomScreen;
private readonly ComputeRenderer3D _renderer3D;
private readonly ComputeRenderer2D _render2D_A;
private readonly ComputeRenderer2D _render2D_B;
private readonly NdsAudio _audio = new();
private readonly NdsSaveData _save = new();
private readonly IVideoOutput[] _screens;
public ComputeRenderer2D Render2DA => _render2D_A;
public ComputeRenderer2D Render2DB => _render2D_B;
private byte[] _rom;
private bool _running;
public NDS()
{
ARM9 = new ARMv5( this );
ARM7 = new ARMv4( this );
GPU = new GPU( this );
GPU3D = new GPU3D( this );
SPU = new SPU( this, _audio );
Wifi = new Wifi( this );
_renderer3D = new ComputeRenderer3D( GPU3D, GPU );
_render2D_A = new ComputeRenderer2D( 0, GPU.GPU2D_A, GPU, GPU3D );
_render2D_B = new ComputeRenderer2D( 1, GPU.GPU2D_B, GPU, GPU3D );
_render2D_A.SetRenderer3D( _renderer3D );
_render2D_B.SetRenderer3D( _renderer3D );
_render2D_B.SetCaptureOwner( _render2D_A );
_renderer3D.SetCaptureSource( _render2D_A );
_render2D_A.SetSwapPeer( _render2D_B );
_render2D_B.SetSwapPeer( _render2D_A );
_topScreen = new NdsScreen( _render2D_B, _renderer3D );
_bottomScreen = new NdsScreen( _render2D_A, _renderer3D );
_screens = [_topScreen, _bottomScreen];
InitDmas();
}
public SystemProfile Profile => NdsSystem.Profile;
IReadOnlyList<IVideoOutput> IEmulatorCore.Screens => _screens;
IAudioOutput IEmulatorCore.Audio => _audio;
ISaveData IEmulatorCore.SaveData => _save;
public void LoadRom( byte[] romData )
{
_rom = romData;
_secureAreaProcessed = false;
}
public void Reset()
{
Array.Clear( MainRAM );
Array.Clear( SharedWRAM );
Array.Clear( ARM7WRAM );
ResetMemory();
ResetScheduler();
ResetInterrupts();
ResetTimings();
ResetTimers();
ResetDmas();
ResetIpc();
ResetMath();
ResetCart();
ResetCartSave();
ResetSpi();
Wifi.Reset();
ResetRtc();
GPU.Reset();
GPU3D.Reset();
ARM9.CP15Reset();
ARM9.Reset();
ARM7.Reset();
SPU.Reset();
SPU.SetBias( 0x200 );
KeyInput = 0x03FF;
ExtKeyIn = 0x007F;
_running = true;
DirectBoot();
}
public void RunFrame()
{
if ( !_running )
return;
_audio.BeginFrame();
GPU.StartFrame();
long frameEnd = SysTimestamp + NdsConstants.CyclesPerFrame;
while ( SysTimestamp < frameEnd )
{
long minEvent = NextEventTimestamp();
long max = SysTimestamp + 64;
long target = minEvent < max + 8 ? minEvent : max;
if ( target > frameEnd )
target = frameEnd;
ARM9Target = target << 1;
CurCPU = 0;
if ( (CPUStop & CPUStop_GXStall) != 0 )
{
ARM9Timestamp = Math.Min( ARM9Target, ARM9Timestamp + (GPU3D.CyclesToRunFor() << 1) );
}
else if ( (CPUStop & CPUStop_DMA9) != 0 )
{
DMAs[0].Run();
if ( (CPUStop & CPUStop_GXStall) == 0 ) DMAs[1].Run();
if ( (CPUStop & CPUStop_GXStall) == 0 ) DMAs[2].Run();
if ( (CPUStop & CPUStop_GXStall) == 0 ) DMAs[3].Run();
}
else
{
ARM9.Execute();
}
RunTimers( 0 );
GPU3D.Run();
target = ARM9Timestamp >> 1;
CurCPU = 1;
while ( ARM7Timestamp < target )
{
ARM7Target = target;
if ( (CPUStop & CPUStop_DMA7) != 0 )
RunDMAs( 1 );
else
ARM7.Execute();
RunTimers( 1 );
}
RunSystem( target );
}
_audio.EndFrame();
PresentFrame();
_renderer3D.Snapshot();
}
private void PresentFrame()
{
_render2D_A.SetOutput3D( _renderer3D.OutputTexture );
_render2D_B.SetOutput3D( _renderer3D.OutputTexture );
}
public bool StepFrame()
{
RunFrame();
return true;
}
public void SetButtons( int player, ulong pressedMask )
{
KeyInput = (ushort)(~pressedMask & 0x03FF);
int btns = 0x003F & ~(int)((pressedMask >> 10) & 0x3);
ExtKeyIn = (ushort)((ExtKeyIn & 0x40) | btns);
}
public void SetTouch( bool down, int x, int y )
{
if ( down )
{
if ( x < 0 ) x = 0; else if ( x > 255 ) x = 255;
if ( y < 0 ) y = 0; else if ( y > 191 ) y = 191;
TouchScreen( x, y );
}
else
{
ReleaseScreen();
}
}
public byte[] SaveState( byte[] screenshot ) => [];
public void LoadState( byte[] data ) { }
}