Cores/Nds/GPU.cs
namespace Emulotl.Nds;
public sealed partial class GPU
{
private readonly NDS NDS;
public byte[] VRAM_A = new byte[128 * 1024];
public byte[] VRAM_B = new byte[128 * 1024];
public byte[] VRAM_C = new byte[128 * 1024];
public byte[] VRAM_D = new byte[128 * 1024];
public byte[] VRAM_E = new byte[64 * 1024];
public byte[] VRAM_F = new byte[16 * 1024];
public byte[] VRAM_G = new byte[16 * 1024];
public byte[] VRAM_H = new byte[32 * 1024];
public byte[] VRAM_I = new byte[16 * 1024];
public readonly byte[][] VRAM;
public static readonly uint[] VRAMMask = [0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF];
public byte[] VRAMCNT = new byte[9];
public byte VRAMSTAT;
public uint VRAMMap_LCDC;
public uint[] VRAMMap_ABG = new uint[0x20];
public uint[] VRAMMap_AOBJ = new uint[0x10];
public uint[] VRAMMap_BBG = new uint[0x8];
public uint[] VRAMMap_BOBJ = new uint[0x8];
public uint[] VRAMMap_ABGExtPal = new uint[4];
public uint VRAMMap_AOBJExtPal;
public uint[] VRAMMap_BBGExtPal = new uint[4];
public uint VRAMMap_BOBJExtPal;
public uint[] VRAMMap_Texture = new uint[4];
public uint[] VRAMMap_TexPal = new uint[8];
public uint[] VRAMMap_ARM7 = new uint[2];
public ulong TexVRAMVersion;
public uint TexBankMask;
private ulong _lastTexMapSig;
public byte[] Palette = new byte[2 * 1024];
public byte[] OAM = new byte[2 * 1024];
public ushort MasterBrightnessA;
public ushort MasterBrightnessB;
public GPU2D GPU2D_A;
public GPU2D GPU2D_B;
public GPU( NDS nds )
{
NDS = nds;
VRAM = [VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I];
GPU2D_A = new GPU2D( 0, this );
GPU2D_B = new GPU2D( 1, this );
}
public void Reset()
{
foreach ( byte[] bank in VRAM )
Array.Clear( bank );
Array.Clear( Palette );
Array.Clear( OAM );
Array.Clear( VRAMCNT );
VRAMSTAT = 0;
VRAMMap_LCDC = 0;
Array.Clear( VRAMMap_ABG );
Array.Clear( VRAMMap_AOBJ );
Array.Clear( VRAMMap_BBG );
Array.Clear( VRAMMap_BOBJ );
Array.Clear( VRAMMap_ABGExtPal );
VRAMMap_AOBJExtPal = 0;
Array.Clear( VRAMMap_BBGExtPal );
VRAMMap_BOBJExtPal = 0;
Array.Clear( VRAMMap_Texture );
Array.Clear( VRAMMap_TexPal );
Array.Clear( VRAMMap_ARM7 );
ResetCapture();
MasterBrightnessA = 0;
MasterBrightnessB = 0;
GPU2D_A.Reset();
GPU2D_B.Reset();
ResetLcd();
}
public void SetPowerCnt( uint val )
{
GPU2D_A.SetEnabled( (val & (1 << 1)) != 0 );
GPU2D_B.SetEnabled( (val & (1 << 9)) != 0 );
NDS.GPU3D.SetEnabled( (val & (1 << 3)) != 0, (val & (1 << 2)) != 0 );
ScreenSwap = (val & (1 << 15)) != 0;
}
private static void MapRange( uint[] map, int based, int n, uint bankmask )
{
for ( int i = 0; i < n; i++ )
map[based + i] |= bankmask;
}
private static void UnmapRange( uint[] map, int based, int n, uint bankmask )
{
for ( int i = 0; i < n; i++ )
map[based + i] &= ~bankmask;
}
public void MapVRAM_AB( uint bank, byte cnt )
{
cnt &= 0x9B;
byte oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if ( oldcnt == cnt ) return;
int oldofs = (oldcnt >> 3) & 0x3;
int ofs = (cnt >> 3) & 0x3;
uint bankmask = 1u << (int)bank;
if ( (oldcnt & (1 << 7)) != 0 )
{
switch ( oldcnt & 0x3 )
{
case 0: VRAMMap_LCDC &= ~bankmask; break;
case 1: UnmapRange( VRAMMap_ABG, oldofs << 3, 8, bankmask ); SetRangeCBF( VRAMCBF_ABG, VRAMMap_ABG, oldofs << 3, 8 ); break;
case 2: UnmapRange( VRAMMap_AOBJ, (oldofs & 0x1) << 3, 8, bankmask ); SetRangeCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, (oldofs & 0x1) << 3, 8 ); break;
case 3: VRAMMap_Texture[oldofs] &= ~bankmask; break;
}
}
if ( (cnt & (1 << 7)) != 0 )
{
switch ( cnt & 0x3 )
{
case 0: VRAMMap_LCDC |= bankmask; break;
case 1: MapRange( VRAMMap_ABG, ofs << 3, 8, bankmask ); SetRangeCBF( VRAMCBF_ABG, VRAMMap_ABG, ofs << 3, 8 ); break;
case 2: MapRange( VRAMMap_AOBJ, (ofs & 0x1) << 3, 8, bankmask ); SetRangeCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, (ofs & 0x1) << 3, 8 ); break;
case 3: VRAMMap_Texture[ofs] |= bankmask; break;
}
}
}
public void MapVRAM_CD( uint bank, byte cnt )
{
cnt &= 0x9F;
byte oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if ( oldcnt == cnt ) return;
VRAMSTAT &= (byte)~(1 << (int)(bank - 2));
int oldofs = (oldcnt >> 3) & 0x7;
int ofs = (cnt >> 3) & 0x7;
uint bankmask = 1u << (int)bank;
if ( (oldcnt & (1 << 7)) != 0 )
{
switch ( oldcnt & 0x7 )
{
case 0: VRAMMap_LCDC &= ~bankmask; break;
case 1: UnmapRange( VRAMMap_ABG, oldofs << 3, 8, bankmask ); SetRangeCBF( VRAMCBF_ABG, VRAMMap_ABG, oldofs << 3, 8 ); break;
case 2: VRAMMap_ARM7[oldofs & 0x1] &= ~bankmask; break;
case 3: VRAMMap_Texture[oldofs] &= ~bankmask; break;
case 4:
if ( bank == 2 ) { UnmapRange( VRAMMap_BBG, 0, 8, bankmask ); SetRangeCBF( VRAMCBF_BBG, VRAMMap_BBG, 0, 8 ); }
else { UnmapRange( VRAMMap_BOBJ, 0, 8, bankmask ); SetRangeCBF( VRAMCBF_BOBJ, VRAMMap_BOBJ, 0, 8 ); }
break;
}
}
if ( (cnt & (1 << 7)) != 0 )
{
switch ( cnt & 0x7 )
{
case 0: VRAMMap_LCDC |= bankmask; break;
case 1: MapRange( VRAMMap_ABG, ofs << 3, 8, bankmask ); SetRangeCBF( VRAMCBF_ABG, VRAMMap_ABG, ofs << 3, 8 ); break;
case 2:
VRAMMap_ARM7[ofs & 0x1] |= bankmask;
VRAMSTAT |= (byte)(1 << (int)(bank - 2));
break;
case 3: VRAMMap_Texture[ofs] |= bankmask; break;
case 4:
if ( bank == 2 ) { MapRange( VRAMMap_BBG, 0, 8, bankmask ); SetRangeCBF( VRAMCBF_BBG, VRAMMap_BBG, 0, 8 ); }
else { MapRange( VRAMMap_BOBJ, 0, 8, bankmask ); SetRangeCBF( VRAMCBF_BOBJ, VRAMMap_BOBJ, 0, 8 ); }
break;
}
}
}
public void MapVRAM_E( uint bank, byte cnt )
{
cnt &= 0x87;
byte oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if ( oldcnt == cnt ) return;
uint bankmask = 1u << (int)bank;
if ( (oldcnt & (1 << 7)) != 0 )
{
switch ( oldcnt & 0x7 )
{
case 0: VRAMMap_LCDC &= ~bankmask; break;
case 1: UnmapRange( VRAMMap_ABG, 0, 4, bankmask ); SetRangeCBF( VRAMCBF_ABG, VRAMMap_ABG, 0, 8 ); break;
case 2: UnmapRange( VRAMMap_AOBJ, 0, 4, bankmask ); SetRangeCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, 0, 8 ); break;
case 3: UnmapRange( VRAMMap_TexPal, 0, 4, bankmask ); break;
case 4: UnmapRange( VRAMMap_ABGExtPal, 0, 4, bankmask ); break;
}
}
if ( (cnt & (1 << 7)) != 0 )
{
switch ( cnt & 0x7 )
{
case 0: VRAMMap_LCDC |= bankmask; break;
case 1: MapRange( VRAMMap_ABG, 0, 4, bankmask ); SetRangeCBF( VRAMCBF_ABG, VRAMMap_ABG, 0, 8 ); break;
case 2: MapRange( VRAMMap_AOBJ, 0, 4, bankmask ); SetRangeCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, 0, 8 ); break;
case 3: MapRange( VRAMMap_TexPal, 0, 4, bankmask ); break;
case 4: MapRange( VRAMMap_ABGExtPal, 0, 4, bankmask ); break;
}
}
}
public void MapVRAM_FG( uint bank, byte cnt )
{
cnt &= 0x9F;
byte oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if ( oldcnt == cnt ) return;
int oldofs = (oldcnt >> 3) & 0x7;
int ofs = (cnt >> 3) & 0x7;
uint bankmask = 1u << (int)bank;
if ( (oldcnt & (1 << 7)) != 0 )
{
switch ( oldcnt & 0x7 )
{
case 0: VRAMMap_LCDC &= ~bankmask; break;
case 1:
{
int based = (oldofs & 0x1) + ((oldofs & 0x2) << 1);
VRAMMap_ABG[based] &= ~bankmask;
VRAMMap_ABG[based + 2] &= ~bankmask; SetCBF( VRAMCBF_ABG, VRAMMap_ABG, based ); SetCBF( VRAMCBF_ABG, VRAMMap_ABG, based + 2 );
break;
}
case 2:
{
int based = (oldofs & 0x1) + ((oldofs & 0x2) << 1);
VRAMMap_AOBJ[based] &= ~bankmask;
VRAMMap_AOBJ[based + 2] &= ~bankmask; SetCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, based ); SetCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, based + 2 );
break;
}
case 3: VRAMMap_TexPal[(oldofs & 0x1) + ((oldofs & 0x2) << 1)] &= ~bankmask; break;
case 4:
VRAMMap_ABGExtPal[(oldofs & 0x1) << 1] &= ~bankmask;
VRAMMap_ABGExtPal[((oldofs & 0x1) << 1) + 1] &= ~bankmask;
break;
case 5: VRAMMap_AOBJExtPal &= ~bankmask; break;
}
}
if ( (cnt & (1 << 7)) != 0 )
{
switch ( cnt & 0x7 )
{
case 0: VRAMMap_LCDC |= bankmask; break;
case 1:
{
int based = (ofs & 0x1) + ((ofs & 0x2) << 1);
VRAMMap_ABG[based] |= bankmask;
VRAMMap_ABG[based + 2] |= bankmask; SetCBF( VRAMCBF_ABG, VRAMMap_ABG, based ); SetCBF( VRAMCBF_ABG, VRAMMap_ABG, based + 2 );
break;
}
case 2:
{
int based = (ofs & 0x1) + ((ofs & 0x2) << 1);
VRAMMap_AOBJ[based] |= bankmask;
VRAMMap_AOBJ[based + 2] |= bankmask; SetCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, based ); SetCBF( VRAMCBF_AOBJ, VRAMMap_AOBJ, based + 2 );
break;
}
case 3: VRAMMap_TexPal[(ofs & 0x1) + ((ofs & 0x2) << 1)] |= bankmask; break;
case 4:
VRAMMap_ABGExtPal[(ofs & 0x1) << 1] |= bankmask;
VRAMMap_ABGExtPal[((ofs & 0x1) << 1) + 1] |= bankmask;
break;
case 5: VRAMMap_AOBJExtPal |= bankmask; break;
}
}
}
public void MapVRAM_H( uint bank, byte cnt )
{
cnt &= 0x83;
byte oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if ( oldcnt == cnt ) return;
uint bankmask = 1u << (int)bank;
if ( (oldcnt & (1 << 7)) != 0 )
{
switch ( oldcnt & 0x3 )
{
case 0: VRAMMap_LCDC &= ~bankmask; break;
case 1:
foreach ( int i in new[] { 0, 1, 4, 5 } )
{ VRAMMap_BBG[i] &= ~bankmask; SetCBF( VRAMCBF_BBG, VRAMMap_BBG, i ); }
break;
case 2: UnmapRange( VRAMMap_BBGExtPal, 0, 4, bankmask ); break;
}
}
if ( (cnt & (1 << 7)) != 0 )
{
switch ( cnt & 0x3 )
{
case 0: VRAMMap_LCDC |= bankmask; break;
case 1:
foreach ( int i in new[] { 0, 1, 4, 5 } )
{ VRAMMap_BBG[i] |= bankmask; SetCBF( VRAMCBF_BBG, VRAMMap_BBG, i ); }
break;
case 2: MapRange( VRAMMap_BBGExtPal, 0, 4, bankmask ); break;
}
}
}
public void MapVRAM_I( uint bank, byte cnt )
{
cnt &= 0x83;
byte oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if ( oldcnt == cnt ) return;
uint bankmask = 1u << (int)bank;
if ( (oldcnt & (1 << 7)) != 0 )
{
switch ( oldcnt & 0x3 )
{
case 0: VRAMMap_LCDC &= ~bankmask; break;
case 1:
foreach ( int i in new[] { 2, 3, 6, 7 } )
{ VRAMMap_BBG[i] &= ~bankmask; SetCBF( VRAMCBF_BBG, VRAMMap_BBG, i ); }
break;
case 2: UnmapRange( VRAMMap_BOBJ, 0, 8, bankmask ); SetRangeCBF( VRAMCBF_BOBJ, VRAMMap_BOBJ, 0, 8 ); break;
case 3: VRAMMap_BOBJExtPal &= ~bankmask; break;
}
}
if ( (cnt & (1 << 7)) != 0 )
{
switch ( cnt & 0x3 )
{
case 0: VRAMMap_LCDC |= bankmask; break;
case 1:
foreach ( int i in new[] { 2, 3, 6, 7 } )
{ VRAMMap_BBG[i] |= bankmask; SetCBF( VRAMCBF_BBG, VRAMMap_BBG, i ); }
break;
case 2: MapRange( VRAMMap_BOBJ, 0, 8, bankmask ); SetRangeCBF( VRAMCBF_BOBJ, VRAMMap_BOBJ, 0, 8 ); break;
case 3: VRAMMap_BOBJExtPal |= bankmask; break;
}
}
}
}