Cores/Nds/GPU.Lcd.cs
namespace Emulotl.Nds;
public sealed partial class GPU
{
private const int LineCycles = 355 * 6;
private const int HBlankCycles = 48 + (256 * 6);
public ushort VCount;
public ushort[] DispStat = new ushort[2];
public ushort[] VMatch = new ushort[2];
public ushort TotalScanlines;
public bool ScreensEnabled;
public bool ScreenSwap;
private ushort NextVCount;
private bool VCountOverride;
public uint CaptureCnt;
public bool CaptureEnable;
private void ResetLcd()
{
VCount = 0;
DispStat[0] = DispStat[1] = 0;
VMatch[0] = VMatch[1] = 0;
TotalScanlines = 0;
ScreensEnabled = false;
ScreenSwap = false;
CaptureCnt = 0;
CaptureEnable = false;
NextVCount = 0;
VCountOverride = false;
}
private void SetDispStatIRQ( int cpu, int num )
{
ushort irqmask = (ushort)(1 << num);
ushort enablemask = (ushort)(1 << (num + 3));
if ( (DispStat[cpu] & irqmask) != 0 )
return;
DispStat[cpu] |= irqmask;
if ( (DispStat[cpu] & enablemask) != 0 )
NDS.SetIRQ( (uint)cpu, num );
}
public void SetDispStat( int cpu, ushort val, ushort mask )
{
const ushort ro_mask = 0x0047;
val &= (ushort)(mask & ~ro_mask);
DispStat[cpu] &= (ushort)(~mask | ro_mask);
DispStat[cpu] |= val;
VMatch[cpu] = (ushort)((DispStat[cpu] >> 8) | ((DispStat[cpu] & 0x80) << 1));
}
public void SetVCount( ushort val, ushort mask )
{
val &= (ushort)(mask & 0x1FF);
NextVCount = (ushort)((NextVCount & ~mask) | (val & mask));
VCountOverride = true;
}
public void StartFrame()
{
ScreensEnabled = (NDS.PowerControl9 & (1 << 0)) != 0;
RebuildVRAMFlat();
TotalScanlines = 0;
StartScanline( 0 );
}
private void StartScanline( uint line )
{
DispStat[0] &= unchecked((ushort)~(1 << 1));
DispStat[1] &= unchecked((ushort)~(1 << 1));
if ( line == 0 )
VCount = 0;
else
{
VCount++;
VCount &= 0x1FF;
}
GPU2D_A.UpdateWindows( VCount );
GPU2D_B.UpdateWindows( VCount );
if ( VCount == 0 )
{
if ( (CaptureCnt & (1u << 31)) != 0 )
{
CaptureEnable = true;
CheckCaptureStart();
}
}
if ( VCount >= 2 && VCount < 194 )
NDS.CheckDMAs( 0, 0x03 );
else if ( VCount == 194 )
NDS.StopDMAs( 0, 0x03 );
if ( VCount == 192 )
{
if ( CaptureEnable )
CheckCaptureEnd();
NDS.GPU3D.VBlank();
NDS.Render2DA.VBlank();
NDS.Render2DB.VBlank();
if ( CaptureEnable )
{
CaptureCnt &= ~(1u << 31);
CaptureEnable = false;
}
SetDispStatIRQ( 0, 0 );
SetDispStatIRQ( 1, 0 );
DispStat[0] |= 1 << 0;
DispStat[1] |= 1 << 0;
NDS.StopDMAs( 0, 0x04 );
NDS.CheckDMAs( 0, 0x01 );
NDS.CheckDMAs( 1, 0x11 );
}
else if ( VCount == 262 )
{
DispStat[0] &= unchecked((ushort)~(1 << 0));
DispStat[1] &= unchecked((ushort)~(1 << 0));
}
if ( VCountOverride )
{
VCount = NextVCount;
VCountOverride = false;
}
if ( VCount == VMatch[0] ) SetDispStatIRQ( 0, 2 );
else DispStat[0] &= unchecked((ushort)~(1 << 2));
if ( VCount == VMatch[1] ) SetDispStatIRQ( 1, 2 );
else DispStat[1] &= unchecked((ushort)~(1 << 2));
NDS.ScheduleEvent( SysEvent.Lcd, true, HBlankCycles, OnLcdStartHBlank, line );
}
private void StartHBlank( uint line )
{
DispStat[0] |= 1 << 1;
DispStat[1] |= 1 << 1;
bool resetregs = VCount == 262;
GPU2D_A.UpdateRegistersPreDraw( resetregs );
GPU2D_B.UpdateRegistersPreDraw( resetregs );
if ( VCount < 192 )
{
NDS.Render2DA.DrawScanline( VCount );
NDS.Render2DB.DrawScanline( VCount );
NDS.CheckDMAs( 0, 0x02 );
}
GPU2D_A.UpdateRegistersPostDraw( resetregs );
GPU2D_B.UpdateRegistersPostDraw( resetregs );
if ( (DispStat[0] & (1 << 4)) != 0 ) NDS.SetIRQ( 0, IRQ.HBlank );
if ( (DispStat[1] & (1 << 4)) != 0 ) NDS.SetIRQ( 1, IRQ.HBlank );
if ( VCount == 262 || VCount == 511 )
NDS.ScheduleEvent( SysEvent.Lcd, true, LineCycles - HBlankCycles, OnLcdFinishFrame, line + 1 );
else
NDS.ScheduleEvent( SysEvent.Lcd, true, LineCycles - HBlankCycles, OnLcdStartScanline, line + 1 );
}
private void FinishFrame( uint lines )
{
TotalScanlines = (ushort)lines;
}
private void OnLcdStartScanline( uint line ) => StartScanline( line );
private void OnLcdStartHBlank( uint line ) => StartHBlank( line );
private void OnLcdFinishFrame( uint line ) => FinishFrame( line );
}