⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ppu.cpp

📁 著名的任天堂FC游戏机模拟器VirtuaNes 085版的源码!
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//////////////////////////////////////////////////////////////////////////
//                                                                      //
//      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 + -