929 lines
50 KiB
C++
929 lines
50 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// 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,
|
||
0x16, 0x13, 0x38, 0x34, 0x20, 0x23, 0x31, 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)
|
||
//DEBUGOUT( "2002 RD L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() );
|
||
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..
|
||
}
|
||
//DEBUGOUT( "W2000 %02X O:%02X S:%02X L:%3d C:%8d\n", data, PPUREG[0], PPUREG[2], ScanlineNo, nes->cpu->GetTotalCycles() );
|
||
|
||
PPUREG[0] = data;
|
||
break;
|
||
case 0x2001: // PPU Control Register #2(W)
|
||
//DEBUGOUT( "W2001 %02X L:%3d C:%8d\n", data, ScanlineNo, nes->cpu->GetTotalCycles() );
|
||
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)
|
||
//DEBUGOUT( "SCR WRT L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() );
|
||
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 ) {
|
||
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;
|
||
|
||
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;
|
||
|
||
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;
|
||
|
||
// 表示期間外であればキャンセル
|
||
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);
|
||
// スキャンライン内にSPRITEが存在するかをチェック
|
||
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];
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
// スプライト0がヒットするかも知れないライン?
|
||
BOOL PPU::IsSprite0( INT scanline )
|
||
{
|
||
// スプライトorBG非表示はキャンセル(ヒットしない)
|
||
if( (PPUREG[1]&(PPU_SPDISP_BIT|PPU_BGDISP_BIT)) != (PPU_SPDISP_BIT|PPU_BGDISP_BIT) )
|
||
return FALSE;
|
||
|
||
// 既にヒットしていたらキャンセル
|
||
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;
|
||
|
||
// スプライト非表示はキャンセル
|
||
if( !(PPUREG[1]&PPU_SPDISP_BIT) )
|
||
return;
|
||
|
||
// 表示期間外であればキャンセル
|
||
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++ ) {
|
||
// スキャンライン内にSPRITEが存在するかをチェック
|
||
if( (scanline < (INT)sp->y+1) || (scanline > ((INT)sp->y+sp_h+1)) ) {
|
||
continue;
|
||
}
|
||
|
||
if( ++spmax > 8-1 ) {
|
||
PPUREG[2] |= PPU_SPMAX_FLAG;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|