2024-07-25 14:03:52 +08:00
|
|
|
|
using Codice.CM.Client.Differences;
|
|
|
|
|
using System;
|
2024-07-25 11:03:58 +08:00
|
|
|
|
|
|
|
|
|
namespace VirtualNes.Core
|
2024-07-23 18:31:59 +08:00
|
|
|
|
{
|
|
|
|
|
public class PPU
|
|
|
|
|
{
|
2024-07-25 14:03:52 +08:00
|
|
|
|
// PPU Control Register #1 PPU #0
|
|
|
|
|
public const byte PPU_VBLANK_BIT = 0x80;
|
|
|
|
|
public const byte PPU_SPHIT_BIT = 0x40; // 堘偆丠
|
|
|
|
|
public const byte PPU_SP16_BIT = 0x20;
|
|
|
|
|
public const byte PPU_BGTBL_BIT = 0x10;
|
|
|
|
|
public const byte PPU_SPTBL_BIT = 0x08;
|
|
|
|
|
public const byte PPU_INC32_BIT = 0x04;
|
|
|
|
|
public const byte PPU_NAMETBL_BIT = 0x03;
|
|
|
|
|
|
|
|
|
|
// PPU Control Register #2 PPU #1
|
|
|
|
|
public const byte PPU_BGCOLOR_BIT = 0xE0;
|
|
|
|
|
public const byte PPU_SPDISP_BIT = 0x10;
|
|
|
|
|
public const byte PPU_BGDISP_BIT = 0x08;
|
|
|
|
|
public const byte PPU_SPCLIP_BIT = 0x04;
|
|
|
|
|
public const byte PPU_BGCLIP_BIT = 0x02;
|
|
|
|
|
public const byte PPU_COLORMODE_BIT = 0x01;
|
|
|
|
|
|
|
|
|
|
// PPU Status Register PPU #2
|
|
|
|
|
public const byte PPU_VBLANK_FLAG = 0x80;
|
|
|
|
|
public const byte PPU_SPHIT_FLAG = 0x40;
|
|
|
|
|
public const byte PPU_SPMAX_FLAG = 0x20;
|
|
|
|
|
public const byte PPU_WENABLE_FLAG = 0x10;
|
|
|
|
|
|
|
|
|
|
// SPRITE Attribute
|
|
|
|
|
public const byte SP_VMIRROR_BIT = 0x80;
|
|
|
|
|
public const byte SP_HMIRROR_BIT = 0x40;
|
|
|
|
|
public const byte SP_PRIORITY_BIT = 0x20;
|
|
|
|
|
public const byte SP_COLOR_BIT = 0x03;
|
|
|
|
|
|
2024-07-23 18:31:59 +08:00
|
|
|
|
private NES m_nes;
|
|
|
|
|
private byte[] lpScreen;
|
|
|
|
|
private byte[] lpColormode;
|
|
|
|
|
private bool bVSMode;
|
|
|
|
|
private int nVSColorMap;
|
|
|
|
|
private byte VSSecurityData;
|
|
|
|
|
private byte[] Bit2Rev = new byte[256];
|
2024-07-25 11:03:58 +08:00
|
|
|
|
private int ScanlineNo;
|
|
|
|
|
/// <summary> 作为lpScreen数组的索引 </summary>
|
|
|
|
|
private int lpScanline;
|
|
|
|
|
|
2024-07-23 18:31:59 +08:00
|
|
|
|
public PPU(NES nes)
|
|
|
|
|
{
|
|
|
|
|
m_nes = nes;
|
|
|
|
|
lpScreen = null;
|
|
|
|
|
lpColormode = null;
|
|
|
|
|
|
|
|
|
|
bVSMode = false;
|
|
|
|
|
nVSColorMap = -1;
|
|
|
|
|
VSSecurityData = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
byte m = 0x80;
|
|
|
|
|
byte c = 0;
|
|
|
|
|
for (int j = 0; j < 8; j++)
|
|
|
|
|
{
|
|
|
|
|
if ((i & (1 << j)) > 0) c |= m;
|
|
|
|
|
m >>= 1;
|
|
|
|
|
}
|
|
|
|
|
Bit2Rev[i] = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-25 11:03:58 +08:00
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 14:03:52 +08:00
|
|
|
|
internal byte Read(ushort addr)
|
|
|
|
|
{
|
|
|
|
|
byte data = 0x00;
|
|
|
|
|
|
|
|
|
|
switch (addr)
|
|
|
|
|
{
|
|
|
|
|
// Write only Register
|
|
|
|
|
case 0x2000: // PPU Control Register #1(W)
|
|
|
|
|
case 0x2001: // PPU Control Register #2(W)
|
|
|
|
|
case 0x2003: // SPR-RAM Address Register(W)
|
|
|
|
|
case 0x2005: // PPU Scroll Register(W2)
|
|
|
|
|
case 0x2006: // VRAM Address Register(W2)
|
|
|
|
|
data = MMU.PPU7_Temp; // 懡暘
|
|
|
|
|
break;
|
|
|
|
|
// Read/Write Register
|
|
|
|
|
case 0x2002: // PPU Status Register(R)
|
|
|
|
|
//DEBUGOUT( "2002 RD L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() );
|
|
|
|
|
data = (byte)(MMU.PPUREG[2] | VSSecurityData);
|
|
|
|
|
MMU.PPU56Toggle = 0;
|
|
|
|
|
byte temp = unchecked((byte)~PPU_VBLANK_FLAG);
|
|
|
|
|
MMU.PPUREG[2] &= temp;
|
|
|
|
|
break;
|
|
|
|
|
case 0x2004: // SPR_RAM I/O Register(RW)
|
|
|
|
|
data = MMU.SPRAM[MMU.PPUREG[3]++];
|
|
|
|
|
break;
|
|
|
|
|
case 0x2007: // VRAM I/O Register(RW)
|
|
|
|
|
addr = (ushort)(MMU.loopy_v & 0x3FFF);
|
|
|
|
|
data = MMU.PPU7_Temp;
|
|
|
|
|
if ((MMU.PPUREG[0] & PPU_INC32_BIT) != 0) MMU.loopy_v += 32;
|
|
|
|
|
else MMU.loopy_v++;
|
|
|
|
|
if (addr >= 0x3000)
|
|
|
|
|
{
|
|
|
|
|
if (addr >= 0x3F00)
|
|
|
|
|
{
|
|
|
|
|
// data &= 0x3F;
|
|
|
|
|
if ((addr & 0x0010) == 0)
|
|
|
|
|
{
|
|
|
|
|
return MMU.BGPAL[addr & 0x000F];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return MMU.SPPAL[addr & 0x000F];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
addr &= 0xEFFF;
|
|
|
|
|
}
|
|
|
|
|
MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10][addr & 0x03FF];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 11:03:58 +08:00
|
|
|
|
internal void SetRenderScanline(int scanline)
|
|
|
|
|
{
|
|
|
|
|
ScanlineNo = scanline;
|
|
|
|
|
if (scanline < 240)
|
|
|
|
|
{
|
|
|
|
|
lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private enum Screen
|
|
|
|
|
{
|
|
|
|
|
SCREEN_WIDTH = 256 + 16,
|
|
|
|
|
SCREEN_HEIGHT = 240
|
|
|
|
|
}
|
2024-07-23 18:31:59 +08:00
|
|
|
|
}
|
|
|
|
|
}
|