📄 ppu.cpp
字号:
//////////////////////////////////////////////////////////////////////////
// //
// NES PPU core //
// Norix //
// written 2001/02/22 //
// last modify ----/--/-- //
//////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "VirtuaNESres.h"
#include "typedef.h"
#include "macro.h"
#include "DebugOut.h"
#include "App.h"
#include "nes.h"
#include "mmu.h"
#include "cpu.h"
#include "ppu.h"
#include "rom.h"
#include "mapper.h"
BYTE PPU::VSColorMap[5][64] = {
{ 0x35, 0xFF, 0x16, 0x22, 0x1C, 0xFF, 0xFF, 0x15,
0xFF, 0x00, 0x27, 0x05, 0x04, 0x27, 0x08, 0x30,
0x21, 0xFF, 0xFF, 0x29, 0x3C, 0xFF, 0x36, 0x12,
0xFF, 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
0xFF, 0x31, 0xFF, 0x2A, 0x2C, 0x0C, 0xFF, 0xFF,
0xFF, 0x07, 0x34, 0x06, 0x13, 0xFF, 0x26, 0x0F,
0xFF, 0x19, 0x10, 0x0A, 0xFF, 0xFF, 0xFF, 0x17,
0xFF, 0x11, 0x09, 0xFF, 0xFF, 0x25, 0x18, 0xFF
},
{ 0xFF, 0x27, 0x18, 0xFF, 0x3A, 0x25, 0xFF, 0x31,
// 0x16, 0x13, 0x38, 0x34, 0x20, 0x23, 0xFF, 0x0B,
0x16, 0x13, 0x38, 0x34, 0x20, 0x23, 0xFF, 0x1A,
0xFF, 0x21, 0x06, 0xFF, 0x1B, 0x29, 0xFF, 0x22,
0xFF, 0x24, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0x03,
0xFF, 0x36, 0x26, 0x33, 0x11, 0xFF, 0x10, 0x02,
0x14, 0xFF, 0x00, 0x09, 0x12, 0x0F, 0xFF, 0x30,
0xFF, 0xFF, 0x2A, 0x17, 0x0C, 0x01, 0x15, 0x19,
0xFF, 0x2C, 0x07, 0x37, 0xFF, 0x05, 0xFF, 0xFF
},
#if 1
{ 0xFF, 0xFF, 0xFF, 0x10, 0x1A, 0x30, 0x31, 0x09,
0x01, 0x0F, 0x36, 0x08, 0x15, 0xFF, 0xFF, 0xF0,
0x22, 0x1C, 0xFF, 0x12, 0x19, 0x18, 0x17, 0xFF,
0x00, 0xFF, 0xFF, 0x02, 0x16, 0x06, 0xFF, 0x35,
0x23, 0xFF, 0x8B, 0xF7, 0xFF, 0x27, 0x26, 0x20,
0x29, 0xFF, 0x21, 0x24, 0x11, 0xFF, 0xEF, 0xFF,
0x2C, 0xFF, 0xFF, 0xFF, 0x07, 0xF9, 0x28, 0xFF,
0x0A, 0xFF, 0x32, 0x37, 0x13, 0xFF, 0xFF, 0x0C
},
#else
{ 0xFF, 0xFF, 0xFF, 0x10, 0x0B, 0x30, 0x31, 0x09, // 00-07
0x01, 0x0F, 0x36, 0x08, 0x15, 0xFF, 0xFF, 0x3C, // 08-0F
0x22, 0x1C, 0xFF, 0x12, 0x19, 0x18, 0x17, 0x1B, // 10-17
0x00, 0xFF, 0xFF, 0x02, 0x16, 0x06, 0xFF, 0x35, // 18-1F
0x23, 0xFF, 0x8B, 0x3C, 0xFF, 0x27, 0x26, 0x20, // 20-27
0x29, 0x04, 0x21, 0x24, 0x11, 0xFF, 0xEF, 0xFF, // 28-2F
0x2C, 0xFF, 0xFF, 0xFF, 0x07, 0x39, 0x28, 0xFF, // 30-37
0x0A, 0xFF, 0x32, 0x38, 0x13, 0x3B, 0xFF, 0x0C // 38-3F
},
#endif
#if 0
{ 0x18, 0xFF, 0x1C, 0x89, 0x0F, 0xFF, 0x01, 0x17,
0x10, 0x0F, 0x2A, 0xFF, 0x36, 0x37, 0x1A, 0xFF,
0x25, 0xFF, 0x12, 0xFF, 0x0F, 0xFF, 0xFF, 0x26,
0xFF, 0xFF, 0x22, 0xFF, 0xFF, 0x0F, 0x3A, 0x21,
0x05, 0x0A, 0x07, 0xC2, 0x13, 0xFF, 0x00, 0x15,
0x0C, 0xFF, 0x11, 0xFF, 0xFF, 0x38, 0xFF, 0xFF,
0xFF, 0xFF, 0x08, 0x45, 0xFF, 0xFF, 0x30, 0x3C,
0x0F, 0x27, 0xFF, 0x60, 0x29, 0xFF, 0x30, 0x09
},
#else
{ 0x18, 0xFF, 0x1C, 0x89, 0x0F, 0xFF, 0x01, 0x17, // 00-07
0x10, 0x0F, 0x2A, 0xFF, 0x36, 0x37, 0x1A, 0xFF, // 08-0F
0x25, 0xFF, 0x12, 0xFF, 0x0F, 0xFF, 0xFF, 0x26, // 10-17
0xFF, 0xFF, 0x22, 0xFF, 0xFF, 0x0F, 0x3A, 0x21, // 18-1F
0x05, 0x0A, 0x07, 0xC2, 0x13, 0xFF, 0x00, 0x15, // 20-27
0x0C, 0xFF, 0x11, 0xFF, 0xFF, 0x38, 0xFF, 0xFF, // 28-2F
0xFF, 0xFF, 0x08, 0x16, 0xFF, 0xFF, 0x30, 0x3C, // 30-37
0x0F, 0x27, 0xFF, 0x60, 0x29, 0xFF, 0x30, 0x09 // 38-3F
},
#endif
{
// Super Xevious/Gradius
0x35, 0xFF, 0x16, 0x22, 0x1C, 0x09, 0xFF, 0x15, // 00-07
0x20, 0x00, 0x27, 0x05, 0x04, 0x28, 0x08, 0x30, // 08-0F
0x21, 0xFF, 0xFF, 0x29, 0x3C, 0xFF, 0x36, 0x12, // 10-17
0xFF, 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, // 18-1F
0xFF, 0x31, 0xFF, 0x2A, 0x2C, 0x0C, 0x1B, 0xFF, // 20-27
0xFF, 0x07, 0x34, 0x06, 0xFF, 0x25, 0x26, 0x0F, // 28-2F
0xFF, 0x19, 0x10, 0x0A, 0xFF, 0xFF, 0xFF, 0x17, // 30-37
0xFF, 0x11, 0x1A, 0xFF, 0x38, 0xFF, 0x18, 0x3A, // 38-3F
}
};
PPU::PPU( NES* parent ) : nes(parent)
{
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) ) {
c |= m;
}
m >>= 1;
}
Bit2Rev[i] = c;
}
}
PPU::~PPU()
{
}
void PPU::Reset()
{
bExtLatch = FALSE;
bChrLatch = FALSE;
bExtNameTable = FALSE;
bExtMono = FALSE;
PPUREG[0] = PPUREG[1] = 0;
PPU56Toggle = 0;
// PPU7_Temp = 0xFF; // VS Excitebike偱偍偐偟偔側傞($2006傪撉傒偵峴偔僶僌偑偁傞)
PPU7_Temp = 0;
loopy_v = loopy_t = 0;
loopy_x = loopy_y = 0;
loopy_shift = 0;
if( lpScreen )
::memset( lpScreen, 0x3F, SCREEN_WIDTH*SCREEN_HEIGHT*sizeof(BYTE) );
if( lpColormode )
::memset( lpColormode, 0, SCREEN_HEIGHT*sizeof(BYTE) );
}
BYTE PPU::Read( WORD 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 = PPU7_Temp; // 懡暘
break;
// Read/Write Register
case 0x2002: // PPU Status Register(R)
data = PPUREG[2] | VSSecurityData;
PPU56Toggle = 0;
PPUREG[2] &= ~PPU_VBLANK_FLAG;
break;
case 0x2004: // SPR_RAM I/O Register(RW)
data = SPRAM[ PPUREG[3]++ ];
break;
case 0x2007: // VRAM I/O Register(RW)
WORD addr = loopy_v & 0x3FFF;
data = PPU7_Temp;
if( PPUREG[0] & PPU_INC32_BIT ) loopy_v+=32;
else loopy_v++;
if( addr >= 0x3000 ) {
if( addr >= 0x3F00 ) {
// data &= 0x3F;
if( !(addr&0x0010) ) {
return BGPAL[addr&0x000F];
} else {
return SPPAL[addr&0x000F];
}
}
addr &= 0xEFFF;
}
PPU7_Temp = PPU_MEM_BANK[addr>>10][addr&0x03FF];
}
return data;
}
void PPU::Write( WORD addr, BYTE data )
{
if( bVSMode && VSSecurityData ) {
if( addr == 0x2000 ) {
addr = 0x2001;
} else if( addr == 0x2001 ){
addr = 0x2000;
}
}
switch( addr ) {
// Read only Register
case 0x2002: // PPU Status register(R)
break;
// Write Register
case 0x2000: // PPU Control Register #1(W)
// NameTable select
// t:0000110000000000=d:00000011
loopy_t = (loopy_t & 0xF3FF)|(((WORD)data & 0x03)<<10);
if( (data & 0x80) && !(PPUREG[0] & 0x80) && (PPUREG[2] & 0x80) ) {
nes->cpu->NMI(); // hmm..
}
PPUREG[0] = data;
break;
case 0x2001: // PPU Control Register #2(W)
PPUREG[1] = data;
break;
case 0x2003: // SPR-RAM Address Register(W)
PPUREG[3] = data;
break;
case 0x2004: // SPR_RAM I/O Register(RW)
SPRAM[ PPUREG[3]++ ] = data;
break;
case 0x2005: // PPU Scroll Register(W2)
if( !PPU56Toggle ) {
// First write
// tile X t:0000000000011111=d:11111000
loopy_t = (loopy_t & 0xFFE0)|(((WORD)data)>>3);
// scroll offset X x=d:00000111
loopy_x = data & 0x07;
} else {
// Second write
// tile Y t:0000001111100000=d:11111000
loopy_t = (loopy_t & 0xFC1F)|((((WORD)data) & 0xF8)<<2);
// scroll offset Y t:0111000000000000=d:00000111
loopy_t = (loopy_t & 0x8FFF)|((((WORD)data) & 0x07)<<12);
}
PPU56Toggle = !PPU56Toggle;
break;
case 0x2006: // VRAM Address Register(W2)
if( !PPU56Toggle ) {
// First write
// t:0011111100000000=d:00111111
// t:1100000000000000=0
loopy_t = (loopy_t & 0x00FF)|((((WORD)data) & 0x3F)<<8);
} else {
// Second write
// t:0000000011111111=d:11111111
loopy_t = (loopy_t & 0xFF00)|(WORD)data;
// v=t
loopy_v = loopy_t;
nes->mapper->PPU_Latch( loopy_v );
}
PPU56Toggle = !PPU56Toggle;
break;
case 0x2007: // VRAM I/O Register(RW)
WORD vaddr = loopy_v & 0x3FFF;
if( PPUREG[0] & PPU_INC32_BIT ) loopy_v+=32;
else loopy_v++;
if( vaddr >= 0x3000 ) {
if( vaddr >= 0x3F00 ) {
data &= 0x3F;
if( bVSMode && nVSColorMap != -1 ) {
BYTE temp = VSColorMap[nVSColorMap][data];
if( temp != 0xFF ) {
data = temp & 0x3F;
}
}
if( !(vaddr&0x000F) ) {
BGPAL[0] = SPPAL[0] = data;
} else if( !(vaddr&0x0010) ) {
BGPAL[vaddr&0x000F] = data;
} else {
SPPAL[vaddr&0x000F] = data;
}
BGPAL[0x04] = BGPAL[0x08] = BGPAL[0x0C] = BGPAL[0x00];
SPPAL[0x00] = SPPAL[0x04] = SPPAL[0x08] = SPPAL[0x0C] = BGPAL[0x00];
return;
}
vaddr &= 0xEFFF;
}
if( PPU_MEM_TYPE[vaddr>>10] != BANKTYPE_VROM ) {
PPU_MEM_BANK[vaddr>>10][vaddr&0x03FF] = data;
}
break;
}
}
void PPU::DMA( BYTE data )
{
WORD addr = data<<8;
for( INT i = 0; i < 256; i++ ) {
SPRAM[i] = nes->Read( addr+i );
}
}
void PPU::VBlankStart()
{
PPUREG[2] |= PPU_VBLANK_FLAG;
// PPUREG[2] |= PPU_SPHIT_FLAG; // VBlank撍擖帪偵昁偢ON丠
}
void PPU::VBlankEnd()
{
PPUREG[2] &= ~PPU_VBLANK_FLAG;
// VBlank扙弌帪偵僋儕傾偝傟傞
// 僄僉僒僀僩僶僀僋偱廳梫
PPUREG[2] &= ~PPU_SPHIT_FLAG;
}
void PPU::FrameStart()
{
if( PPUREG[1] & (PPU_SPDISP_BIT|PPU_BGDISP_BIT) ) {
loopy_v = loopy_t;
loopy_shift = loopy_x;
loopy_y = (loopy_v&0x7000)>>12;
}
if( lpScreen ) {
::memset( lpScreen, 0x3F, SCREEN_WIDTH*sizeof(BYTE) );
}
if( lpColormode ) {
lpColormode[0] = 0;
}
}
void PPU::FrameEnd()
{
}
void PPU::SetRenderScanline( INT scanline )
{
ScanlineNo = scanline;
if( scanline < 240 ) {
lpScanline = lpScreen+SCREEN_WIDTH*scanline;
}
}
void PPU::ScanlineStart()
{
if( PPUREG[1] & (PPU_BGDISP_BIT|PPU_SPDISP_BIT) ) {
loopy_v = (loopy_v & 0xFBE0)|(loopy_t & 0x041F);
loopy_shift = loopy_x;
loopy_y = (loopy_v&0x7000)>>12;
nes->mapper->PPU_Latch( 0x2000 + (loopy_v & 0x0FFF) );
}
}
void PPU::ScanlineNext()
{
if( PPUREG[1] & (PPU_BGDISP_BIT|PPU_SPDISP_BIT) ) {
if( (loopy_v & 0x7000) == 0x7000 ) {
loopy_v &= 0x8FFF;
if( (loopy_v & 0x03E0) == 0x03A0 ) {
loopy_v ^= 0x0800;
loopy_v &= 0xFC1F;
} else {
if( (loopy_v & 0x03E0) == 0x03E0 ) {
loopy_v &= 0xFC1F;
} else {
loopy_v += 0x0020;
}
}
} else {
loopy_v += 0x1000;
}
loopy_y = (loopy_v&0x7000)>>12;
}
}
void PPU::Scanline( INT scanline, BOOL bMax, BOOL bLeftClip )
{
BYTE BGwrite[33+1];
BYTE BGmono[33+1];
ZEROMEMORY( BGwrite, sizeof(BGwrite) );
ZEROMEMORY( BGmono, sizeof(BGmono) );
// Linecolor mode
lpColormode[scanline] = ((PPUREG[1]&PPU_BGCOLOR_BIT)>>5)|((PPUREG[1]&PPU_COLORMODE_BIT)<<7);
// Render BG
if( !(PPUREG[1]&PPU_BGDISP_BIT) ) {
::memset( lpScanline, BGPAL[0], SCREEN_WIDTH );
if( nes->GetRenderMethod() == NES::TILE_RENDER ) {
nes->EmulationCPU( FETCH_CYCLES*4*32 );
}
} else {
if( nes->GetRenderMethod() != NES::TILE_RENDER ) {
if( !bExtLatch ) {
// Without Extension Latch
LPBYTE pScn = lpScanline+(8-loopy_shift);
LPBYTE pBGw = BGwrite;
INT tileofs = (PPUREG[0]&PPU_BGTBL_BIT)<<8;
INT ntbladr = 0x2000+(loopy_v&0x0FFF);
INT attradr = 0x23C0+(loopy_v&0x0C00)+((loopy_v&0x0380)>>4);
INT ntbl_x = ntbladr&0x001F;
INT attrsft = (ntbladr&0x0040)>>4;
LPBYTE pNTBL = PPU_MEM_BANK[ntbladr>>10];
INT tileadr;
INT cache_tile = 0xFFFF0000;
BYTE cache_attr = 0xFF;
BYTE chr_h, chr_l, attr;
attradr &= 0x3FF;
for( INT i = 0; i < 33; i++ ) {
tileadr = tileofs+pNTBL[ntbladr&0x03FF]*0x10+loopy_y;
attr = ((pNTBL[attradr+(ntbl_x>>2)]>>((ntbl_x&2)+attrsft))&3)<<2;
if( cache_tile == tileadr && cache_attr == attr ) {
*(LPDWORD)(pScn+0) = *(LPDWORD)(pScn-8);
*(LPDWORD)(pScn+4) = *(LPDWORD)(pScn-4);
*(pBGw+0) = *(pBGw-1);
} else {
cache_tile = tileadr;
cache_attr = attr;
chr_l = PPU_MEM_BANK[tileadr>>10][ tileadr&0x03FF ];
chr_h = PPU_MEM_BANK[tileadr>>10][(tileadr&0x03FF)+8];
*pBGw = chr_h|chr_l;
LPBYTE pBGPAL = &BGPAL[attr];
{
register INT c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
register INT c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
pScn[0] = pBGPAL[(c1>>6)];
pScn[4] = pBGPAL[(c1>>2)&3];
pScn[1] = pBGPAL[(c2>>6)];
pScn[5] = pBGPAL[(c2>>2)&3];
pScn[2] = pBGPAL[(c1>>4)&3];
pScn[6] = pBGPAL[c1&3];
pScn[3] = pBGPAL[(c2>>4)&3];
pScn[7] = pBGPAL[c2&3];
}
}
pScn+=8;
pBGw++;
// Character latch(For MMC2/MMC4)
if( bChrLatch ) {
nes->mapper->PPU_ChrLatch( tileadr );
}
if( ++ntbl_x == 32 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -