AxibugEmuOnline/References/VirtuaNESex_src_191105/NES/PPU.cpp

965 lines
25 KiB
C++
Raw Normal View History

2024-08-05 17:58:53 +08:00
//////////////////////////////////////////////////////////////////////////
// //
// 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;
bVromWe = 0;
// <20><><EFBFBD>E<EFBFBD><45><EFBFBD>]<5D>}<7D>X<EFBFBD>N<EFBFBD>e<EFBFBD>[<5B>u<EFBFBD><75>
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<6B>ł<EFBFBD><C582><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȃ<EFBFBD>($2006<30><36><EFBFBD>ǂ݂ɍs<C98D><73><EFBFBD>o<EFBFBD>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
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; // <20><><EFBFBD><EFBFBD>
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;
}
if( bChrLatch ) {
nes->mapper->PPU_ChrLatch( addr );
}
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 ) {
case 0x2008:
//for SB2000
break;
case 0x2010:
case 0x2011:
case 0x2012:
case 0x2013:
case 0x2014:
case 0x2015:
case 0x2016:
case 0x2017:
case 0x2018:
case 0x2019:
case 0x201A:
case 0x201B:
case 0x201C:
case 0x201D:
case 0x201E:
case 0x201F:
nes->mapper->WriteExPPU(addr, data);
break;
// 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;
}
int bank = vaddr>>10;
vaddr &= 0x03ff;
if( PPU_MEM_TYPE[bank] != BANKTYPE_VROM ){
PPU_MEM_BANK[bank][vaddr] = data;
}else if( bVromWe ) {
PPU_MEM_BANK[bank][vaddr] = data;
VROM_WRITED[PPU_MEM_PAGE[bank]] = 1;
}
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<6E>˓<EFBFBD><CB93><EFBFBD><EFBFBD>ɕK<C995><4B>ON<4F>H
}
void PPU::VBlankEnd()
{
PPUREG[2] &= ~PPU_VBLANK_FLAG;
// VBlank<6E>E<EFBFBD>o<EFBFBD><6F><EFBFBD>ɃN<C983><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20>G<EFBFBD>L<EFBFBD>T<EFBFBD>C<EFBFBD>g<EFBFBD>o<EFBFBD>C<EFBFBD>N<EFBFBD>ŏd<C58F>v
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];
BYTE sppadr_offset; //for YuXing and PYRAMID
ZEROMEMORY( BGwrite, sizeof(BGwrite) );
ZEROMEMORY( BGmono, sizeof(BGmono) );
sppadr_offset = 0;
// 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 ) {
ntbl_x = 0;
ntbladr ^= 0x41F;
attradr = 0x03C0+((ntbladr&0x0380)>>4);
pNTBL = PPU_MEM_BANK[ntbladr>>10];
} else {
ntbladr++;
}
}
} else {
// With Extension Latch(For MMC5)
LPBYTE pScn = lpScanline+(8-loopy_shift);
LPBYTE pBGw = BGwrite;
INT ntbladr = 0x2000+(loopy_v&0x0FFF);
INT ntbl_x = ntbladr & 0x1F;
INT cache_tile = 0xFFFF0000;
BYTE cache_attr = 0xFF;
BYTE chr_h, chr_l, attr, exattr;
sppadr_offset = nes->mapper->PPU_ExtLatchSP(); //for YuXing and PYRAMID
for( INT i = 0; i < 33; i++ ) {
nes->mapper->PPU_ExtLatchX( i );
nes->mapper->PPU_ExtLatch( ntbladr, chr_l, chr_h, exattr );
attr = exattr&0x0C;
if( cache_tile != (((INT)chr_h<<8)+(INT)chr_l) || cache_attr != attr ) {
cache_tile = (((INT)chr_h<<8)+(INT)chr_l);
cache_attr = attr;
*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];
}
} else {
*(DWORD*)(pScn+0) = *(DWORD*)(pScn-8);
*(DWORD*)(pScn+4) = *(DWORD*)(pScn-4);
*(pBGw+0) = *(pBGw-1);
}
pScn+=8;
pBGw++;
if( ++ntbl_x == 32 ) {
ntbl_x = 0;
ntbladr ^= 0x41F;
} else {
ntbladr++;
}
}
}
} else {
if( !bExtLatch ) {
// Without Extension Latch
if( !bExtNameTable ) {
LPBYTE pScn = lpScanline+(8-loopy_shift);
LPBYTE pBGw = BGwrite;
INT ntbladr = 0x2000+(loopy_v&0x0FFF);
INT attradr = 0x03C0+((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 cache_mono = 0x00;
BYTE chr_h, chr_l, attr;
for( INT i = 0; i < 33; i++ ) {
tileadr = ((PPUREG[0]&PPU_BGTBL_BIT)<<8)+pNTBL[ntbladr&0x03FF]*0x10+loopy_y;
if( i != 0 ) {
nes->EmulationCPU( FETCH_CYCLES*4 );
}
attr = ((pNTBL[attradr+(ntbl_x>>2)]>>((ntbl_x&2)+attrsft))&3)<<2;
if( cache_tile != tileadr || cache_attr != attr ) {
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_l|chr_h;
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];
}
} else {
*(DWORD*)(pScn+0) = *(DWORD*)(pScn-8);
*(DWORD*)(pScn+4) = *(DWORD*)(pScn-4);
*(pBGw+0) = *(pBGw-1);
}
pScn+=8;
pBGw++;
// Character latch(For MMC2/MMC4)
if( bChrLatch ) {
nes->mapper->PPU_ChrLatch( tileadr );
}
if( ++ntbl_x == 32 ) {
ntbl_x = 0;
ntbladr ^= 0x41F;
attradr = 0x03C0+((ntbladr&0x0380)>>4);
pNTBL = PPU_MEM_BANK[ntbladr>>10];
} else {
ntbladr++;
}
}
} else {
LPBYTE pScn = lpScanline+(8-loopy_shift);
LPBYTE pBGw = BGwrite;
INT ntbladr;
INT tileadr;
INT cache_tile = 0xFFFF0000;
BYTE cache_attr = 0xFF;
BYTE cache_mono = 0x00;
BYTE chr_h, chr_l, attr;
WORD loopy_v_tmp = loopy_v;
for( INT i = 0; i < 33; i++ ) {
if( i != 0 ) {
nes->EmulationCPU( FETCH_CYCLES*4 );
}
ntbladr = 0x2000+(loopy_v&0x0FFF);
tileadr = ((PPUREG[0]&PPU_BGTBL_BIT)<<8)+PPU_MEM_BANK[ntbladr>>10][ntbladr&0x03FF]*0x10+((loopy_v&0x7000)>>12);
attr = ((PPU_MEM_BANK[ntbladr>>10][0x03C0+((ntbladr&0x0380)>>4)+((ntbladr&0x001C)>>2)]>>(((ntbladr&0x40)>>4)+(ntbladr&0x02)))&3)<<2;
if( cache_tile != tileadr || cache_attr != attr ) {
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_l|chr_h;
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];
}
} else {
*(DWORD*)(pScn+0) = *(DWORD*)(pScn-8);
*(DWORD*)(pScn+4) = *(DWORD*)(pScn-4);
*(pBGw+0) = *(pBGw-1);
}
pScn+=8;
pBGw++;
// Character latch(For MMC2/MMC4)
if( bChrLatch ) {
nes->mapper->PPU_ChrLatch( tileadr );
}
if( (loopy_v & 0x1F) == 0x1F ) {
loopy_v ^= 0x041F;
} else {
loopy_v++;
}
}
loopy_v = loopy_v_tmp;
}
} else {
// With Extension Latch(For MMC5)
LPBYTE pScn = lpScanline+(8-loopy_shift);
LPBYTE pBGw = BGwrite;
INT ntbladr = 0x2000+(loopy_v&0x0FFF);
INT ntbl_x = ntbladr & 0x1F;
INT cache_tile = 0xFFFF0000;
BYTE cache_attr = 0xFF;
BYTE chr_h, chr_l, attr, exattr;
sppadr_offset = nes->mapper->PPU_ExtLatchSP(); //for YuXing and PYRAMID
for( INT i = 0; i < 33; i++ ) {
if( i != 0 ) {
nes->EmulationCPU( FETCH_CYCLES*4 );
}
nes->mapper->PPU_ExtLatchX( i );
nes->mapper->PPU_ExtLatch( ntbladr, chr_l, chr_h, exattr );
attr = exattr&0x0C;
if( cache_tile != (((INT)chr_h<<8)+(INT)chr_l) || cache_attr != attr ) {
cache_tile = (((INT)chr_h<<8)+(INT)chr_l);
cache_attr = attr;
*pBGw = chr_l|chr_h;
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];
}
} else {
*(DWORD*)(pScn+0) = *(DWORD*)(pScn-8);
*(DWORD*)(pScn+4) = *(DWORD*)(pScn-4);
*(pBGw+0) = *(pBGw-1);
}
pScn+=8;
pBGw++;
if( ++ntbl_x == 32 ) {
ntbl_x = 0;
ntbladr ^= 0x41F;
} else {
ntbladr++;
}
}
}
}
if( !(PPUREG[1]&PPU_BGCLIP_BIT) && bLeftClip ) {
LPBYTE pScn = lpScanline+8;
for( INT i = 0; i < 8; i++ ) {
pScn[i] = BGPAL[0];
}
}
}
// Render sprites
PPUREG[2] &= ~PPU_SPMAX_FLAG;
// <20>\<5C><><EFBFBD><EFBFBD><EFBFBD>ԊO<D48A>ł<EFBFBD><C582><EFBFBD><EFBFBD>΃L<CE83><4C><EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD><5A>
if( scanline > 239 )
return;
if( !(PPUREG[1]&PPU_SPDISP_BIT) ) {
return;
}
BYTE SPwrite[33+1];
INT spmax;
INT spraddr, sp_y, sp_h;
BYTE chr_h, chr_l;
LPSPRITE sp;
LPBYTE pBGw = BGwrite;
LPBYTE pSPw = SPwrite;
LPBYTE pBit2Rev = Bit2Rev;
ZEROMEMORY( SPwrite, sizeof(SPwrite) );
spmax = 0;
sp = (LPSPRITE)SPRAM;
sp_h = (PPUREG[0]&PPU_SP16_BIT)?15:7;
// Left clip
if( !(PPUREG[1]&PPU_SPCLIP_BIT) && bLeftClip ) {
SPwrite[0] = 0xFF;
}
for( INT i = 0; i < 64; i++, sp++ ) {
sp_y = scanline - (sp->y+1);
// <20>X<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD><43><EFBFBD><EFBFBD><EFBFBD><EFBFBD>SPRITE<54><45><EFBFBD><EFBFBD><EFBFBD>݂<EFBFBD><DD82><EFBFBD><E982A9><EFBFBD>`<60>F<EFBFBD>b<EFBFBD>N
if( sp_y != (sp_y & sp_h) )
continue;
if( !(PPUREG[0]&PPU_SP16_BIT) ) {
// 8x8 Sprite
spraddr = (((INT)PPUREG[0]&PPU_SPTBL_BIT)<<9)+((INT)sp->tile<<4);
if( !(sp->attr&SP_VMIRROR_BIT) )
spraddr += sp_y;
else
spraddr += 7-sp_y;
} else {
// 8x16 Sprite
spraddr = (((INT)sp->tile&1)<<12)+(((INT)sp->tile&0xFE)<<4);
if( !(sp->attr&SP_VMIRROR_BIT) )
spraddr += ((sp_y&8)<<1)+(sp_y&7);
else
spraddr += ((~sp_y&8)<<1)+(7-(sp_y&7));
}
// Character pattern
chr_l = PPU_MEM_BANK[spraddr>>10][ spraddr&0x3FF ];
chr_h = PPU_MEM_BANK[spraddr>>10][(spraddr&0x3FF)+8];
// Character latch(For MMC2/MMC4)
if( bChrLatch ) {
nes->mapper->PPU_ChrLatch( spraddr );
}
// pattern mask
if( sp->attr&SP_HMIRROR_BIT ) {
chr_l = pBit2Rev[chr_l];
chr_h = pBit2Rev[chr_h];
}
BYTE SPpat = chr_l|chr_h;
// Sprite hitcheck
if( i == 0 && !(PPUREG[2]&PPU_SPHIT_FLAG) ) {
INT BGpos = ((sp->x&0xF8)+((loopy_shift+(sp->x&7))&8))>>3;
INT BGsft = 8-((loopy_shift+sp->x)&7);
BYTE BGmsk = (((WORD)pBGw[BGpos+0]<<8)|(WORD)pBGw[BGpos+1])>>BGsft;
if( SPpat & BGmsk ) {
PPUREG[2] |= PPU_SPHIT_FLAG;
}
}
// Sprite mask
INT SPpos = sp->x/8;
INT SPsft = 8-(sp->x&7);
BYTE SPmsk = (((WORD)pSPw[SPpos+0]<<8)|(WORD)pSPw[SPpos+1])>>SPsft;
WORD SPwrt = (WORD)SPpat<<SPsft;
pSPw[SPpos+0] |= SPwrt >> 8;
pSPw[SPpos+1] |= SPwrt & 0xFF;
SPpat &= ~SPmsk;
if( sp->attr&SP_PRIORITY_BIT ) {
// BG > SP priority
INT BGpos = ((sp->x&0xF8)+((loopy_shift+(sp->x&7))&8))>>3;
INT BGsft = 8-((loopy_shift+sp->x)&7);
BYTE BGmsk = (((WORD)pBGw[BGpos+0]<<8)|(WORD)pBGw[BGpos+1])>>BGsft;
SPpat &= ~BGmsk;
}
// Attribute
LPBYTE pSPPAL = &SPPAL[(sp->attr&SP_COLOR_BIT)<<2] + sppadr_offset; //"sppadr_offset" for YuXing and PYRAMID
// Ptr
LPBYTE pScn = lpScanline+sp->x+8;
if( !bExtMono ) {
register INT c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
register INT c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
if( SPpat&0x80 ) pScn[0] = pSPPAL[(c1>>6)];
if( SPpat&0x08 ) pScn[4] = pSPPAL[(c1>>2)&3];
if( SPpat&0x40 ) pScn[1] = pSPPAL[(c2>>6)];
if( SPpat&0x04 ) pScn[5] = pSPPAL[(c2>>2)&3];
if( SPpat&0x20 ) pScn[2] = pSPPAL[(c1>>4)&3];
if( SPpat&0x02 ) pScn[6] = pSPPAL[c1&3];
if( SPpat&0x10 ) pScn[3] = pSPPAL[(c2>>4)&3];
if( SPpat&0x01 ) pScn[7] = pSPPAL[c2&3];
} else {
// Monocrome effect (for Final Fantasy)
BYTE mono = BGmono[((sp->x&0xF8)+((loopy_shift+(sp->x&7))&8))>>3];
register INT c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
register INT c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
if( SPpat&0x80 ) pScn[0] = pSPPAL[c1>>6] |mono;
if( SPpat&0x08 ) pScn[4] = pSPPAL[(c1>>2)&3] |mono;
if( SPpat&0x40 ) pScn[1] = pSPPAL[c2>>6] |mono;
if( SPpat&0x04 ) pScn[5] = pSPPAL[(c2>>2)&3] |mono;
if( SPpat&0x20 ) pScn[2] = pSPPAL[(c1>>4)&3] |mono;
if( SPpat&0x02 ) pScn[6] = pSPPAL[c1&3] |mono;
if( SPpat&0x10 ) pScn[3] = pSPPAL[(c2>>4)&3] |mono;
if( SPpat&0x01 ) pScn[7] = pSPPAL[c2&3] |mono;
}
if( ++spmax > 8-1 ) {
if( !bMax )
break;
}
}
if( spmax > 8-1 ) {
PPUREG[2] |= PPU_SPMAX_FLAG;
}
}
// <20>X<EFBFBD>v<EFBFBD><76><EFBFBD>C<EFBFBD>g<EFBFBD>O<EFBFBD><4F><EFBFBD>q<EFBFBD>b<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><E982A9><EFBFBD>m<EFBFBD><6D><EFBFBD>Ȃ<EFBFBD><C882><EFBFBD><EFBFBD>C<EFBFBD><43><EFBFBD>H
BOOL PPU::IsSprite0( INT scanline )
{
// <20>X<EFBFBD>v<EFBFBD><76><EFBFBD>C<EFBFBD>gorBG<42><47><EFBFBD>\<5C><><EFBFBD>̓L<CD83><4C><EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD><5A>(<28>q<EFBFBD>b<EFBFBD>g<EFBFBD><67><EFBFBD>Ȃ<EFBFBD>)
if( (PPUREG[1]&(PPU_SPDISP_BIT|PPU_BGDISP_BIT)) != (PPU_SPDISP_BIT|PPU_BGDISP_BIT) )
return FALSE;
// <20><><EFBFBD>Ƀq<C983>b<EFBFBD>g<EFBFBD><67><EFBFBD>Ă<EFBFBD><C482><EFBFBD><EFBFBD><EFBFBD><EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD><5A>
if( PPUREG[2]&PPU_SPHIT_FLAG )
return FALSE;
if( !(PPUREG[0]&PPU_SP16_BIT) ) {
// 8x8
if( (scanline < (INT)SPRAM[0]+1) || (scanline > ((INT)SPRAM[0]+7+1)) )
return FALSE;
} else {
// 8x16
if( (scanline < (INT)SPRAM[0]+1) || (scanline > ((INT)SPRAM[0]+15+1)) )
return FALSE;
}
return TRUE;
}
void PPU::DummyScanline( INT scanline )
{
INT i;
INT spmax;
INT sp_h;
LPSPRITE sp;
PPUREG[2] &= ~PPU_SPMAX_FLAG;
// <20>X<EFBFBD>v<EFBFBD><76><EFBFBD>C<EFBFBD>g<EFBFBD><67><EFBFBD>\<5C><><EFBFBD>̓L<CD83><4C><EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD><5A>
if( !(PPUREG[1]&PPU_SPDISP_BIT) )
return;
// <20>\<5C><><EFBFBD><EFBFBD><EFBFBD>ԊO<D48A>ł<EFBFBD><C582><EFBFBD><EFBFBD>΃L<CE83><4C><EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD><5A>
if( scanline < 0 || scanline > 239 )
return;
sp = (LPSPRITE)SPRAM;
sp_h = (PPUREG[0]&PPU_SP16_BIT)?15:7;
spmax = 0;
// Sprite Max check
for( i = 0; i < 64; i++, sp++ ) {
// <20>X<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD><43><EFBFBD><EFBFBD><EFBFBD><EFBFBD>SPRITE<54><45><EFBFBD><EFBFBD><EFBFBD>݂<EFBFBD><DD82><EFBFBD><E982A9><EFBFBD>`<60>F<EFBFBD>b<EFBFBD>N
if( (scanline < (INT)sp->y+1) || (scanline > ((INT)sp->y+sp_h+1)) ) {
continue;
}
if( ++spmax > 8-1 ) {
PPUREG[2] |= PPU_SPMAX_FLAG;
break;
}
}
}