加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
InfoNES.cpp 34.67 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
/*===================================================================*/
/* */
/* InfoNES.cpp : NES Emulator for Win32, Linux(x86), Linux(PS2) */
/* */
/* 2000/05/18 InfoNES Project ( based on pNesX ) */
/* */
/*===================================================================*/
/*-------------------------------------------------------------------
* File List :
*
* [NES Hardware]
* InfoNES.cpp
* InfoNES.h
* K6502_rw.h
*
* [Mapper function]
* InfoNES_Mapper.cpp
* InfoNES_Mapper.h
*
* [The function which depends on a system]
* InfoNES_System_ooo.cpp (ooo is a system name. win, ...)
* InfoNES_System.h
*
* [CPU]
* K6502.cpp
* K6502.h
*
* [Others]
* InfoNES_Types.h
*
--------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Include files */
/*-------------------------------------------------------------------*/
#include "InfoNES.h"
#include "InfoNES_System.h"
#include "InfoNES_Mapper.h"
#include "InfoNES_pAPU.h"
#include "K6502.h"
/*-------------------------------------------------------------------*/
/* NES resources */
/*-------------------------------------------------------------------*/
/* RAM */
BYTE RAM[ RAM_SIZE ];
/* SRAM */
BYTE SRAM[ SRAM_SIZE ];
/* ROM */
BYTE *ROM;
/* SRAM BANK ( 8Kb ) */
BYTE *SRAMBANK;
/* ROM BANK ( 8Kb * 4 ) */
BYTE *ROMBANK0;
BYTE *ROMBANK1;
BYTE *ROMBANK2;
BYTE *ROMBANK3;
/*-------------------------------------------------------------------*/
/* PPU resources */
/*-------------------------------------------------------------------*/
/* PPU RAM */
BYTE PPURAM[ PPURAM_SIZE ];
/* VROM */
BYTE *VROM;
/* PPU BANK ( 1Kb * 16 ) */
BYTE *PPUBANK[ 16 ];
/* Sprite RAM */
BYTE SPRRAM[ SPRRAM_SIZE ];
/* PPU Register */
BYTE PPU_R0;
BYTE PPU_R1;
BYTE PPU_R2;
BYTE PPU_R3;
BYTE PPU_R7;
/* Vertical scroll value */
BYTE PPU_Scr_V;
BYTE PPU_Scr_V_Next;
BYTE PPU_Scr_V_Byte;
BYTE PPU_Scr_V_Byte_Next;
BYTE PPU_Scr_V_Bit;
BYTE PPU_Scr_V_Bit_Next;
/* Horizontal scroll value */
BYTE PPU_Scr_H;
BYTE PPU_Scr_H_Next;
BYTE PPU_Scr_H_Byte;
BYTE PPU_Scr_H_Byte_Next;
BYTE PPU_Scr_H_Bit;
BYTE PPU_Scr_H_Bit_Next;
/* PPU Address */
WORD PPU_Addr;
/* PPU Address */
WORD PPU_Temp;
/* The increase value of the PPU Address */
WORD PPU_Increment;
/* Current Scanline */
WORD PPU_Scanline;
/* Scanline Table */
BYTE PPU_ScanTable[ 263 ];
/* Name Table Bank */
BYTE PPU_NameTableBank;
/* BG Base Address */
BYTE *PPU_BG_Base;
/* Sprite Base Address */
BYTE *PPU_SP_Base;
/* Sprite Height */
WORD PPU_SP_Height;
/* Sprite #0 Scanline Hit Position */
int SpriteJustHit;
/* VRAM Write Enable ( 0: Disable, 1: Enable ) */
BYTE byVramWriteEnable;
/* PPU Address and Scroll Latch Flag*/
BYTE PPU_Latch_Flag;
/* Up and Down Clipping Flag ( 0: non-clip, 1: clip ) */
BYTE PPU_UpDown_Clip;
/* Frame IRQ ( 0: Disabled, 1: Enabled )*/
BYTE FrameIRQ_Enable;
WORD FrameStep;
/*-------------------------------------------------------------------*/
/* Display and Others resouces */
/*-------------------------------------------------------------------*/
/* Frame Skip */
WORD FrameSkip;
WORD FrameCnt;
/* Display Buffer */
#if 0
WORD DoubleFrame[ 2 ][ NES_DISP_WIDTH * NES_DISP_HEIGHT ];
WORD *WorkFrame;
WORD WorkFrameIdx;
#else
WORD WorkFrame[ NES_DISP_WIDTH * NES_DISP_HEIGHT ];
#endif
/* Character Buffer */
BYTE ChrBuf[ 256 * 2 * 8 * 8 ];
/* Update flag for ChrBuf */
BYTE ChrBufUpdate;
/* Palette Table */
WORD PalTable[ 32 ];
/* Table for Mirroring */
BYTE PPU_MirrorTable[][ 4 ] =
{
{ NAME_TABLE0, NAME_TABLE0, NAME_TABLE1, NAME_TABLE1 },
{ NAME_TABLE0, NAME_TABLE1, NAME_TABLE0, NAME_TABLE1 },
{ NAME_TABLE1, NAME_TABLE1, NAME_TABLE1, NAME_TABLE1 },
{ NAME_TABLE0, NAME_TABLE0, NAME_TABLE0, NAME_TABLE0 },
{ NAME_TABLE0, NAME_TABLE1, NAME_TABLE2, NAME_TABLE3 },
{ NAME_TABLE0, NAME_TABLE0, NAME_TABLE0, NAME_TABLE1 }
};
/*-------------------------------------------------------------------*/
/* APU and Pad resources */
/*-------------------------------------------------------------------*/
/* APU Register */
BYTE APU_Reg[ 0x18 ];
/* APU Mute ( 0:OFF, 1:ON ) */
int APU_Mute = 0;
/* Pad data */
DWORD PAD1_Latch;
DWORD PAD2_Latch;
DWORD PAD_System;
DWORD PAD1_Bit;
DWORD PAD2_Bit;
/*-------------------------------------------------------------------*/
/* Mapper Function */
/*-------------------------------------------------------------------*/
/* Initialize Mapper */
void (*MapperInit)();
/* Write to Mapper */
void (*MapperWrite)( WORD wAddr, BYTE byData );
/* Write to SRAM */
void (*MapperSram)( WORD wAddr, BYTE byData );
/* Write to Apu */
void (*MapperApu)( WORD wAddr, BYTE byData );
/* Read from Apu */
BYTE (*MapperReadApu)( WORD wAddr );
/* Callback at VSync */
void (*MapperVSync)();
/* Callback at HSync */
void (*MapperHSync)();
/* Callback at PPU read/write */
void (*MapperPPU)( WORD wAddr );
/* Callback at Rendering Screen 1:BG, 0:Sprite */
void (*MapperRenderScreen)( BYTE byMode );
/*-------------------------------------------------------------------*/
/* ROM information */
/*-------------------------------------------------------------------*/
/* .nes File Header */
struct NesHeader_tag NesHeader;
/* Mapper Number */
BYTE MapperNo;
/* Mirroring 0:Horizontal 1:Vertical */
BYTE ROM_Mirroring;
/* It has SRAM */
BYTE ROM_SRAM;
/* It has Trainer */
BYTE ROM_Trainer;
/* Four screen VRAM */
BYTE ROM_FourScr;
/*===================================================================*/
/* */
/* InfoNES_Init() : Initialize InfoNES */
/* */
/*===================================================================*/
void InfoNES_Init()
{
/*
* Initialize InfoNES
*
* Remarks
* Initialize K6502 and Scanline Table.
*/
int nIdx;
// Initialize 6502
K6502_Init();
// Initialize Scanline Table
for ( nIdx = 0; nIdx < 263; ++nIdx )
{
if ( nIdx < SCAN_ON_SCREEN_START )
PPU_ScanTable[ nIdx ] = SCAN_ON_SCREEN;
else
if ( nIdx < SCAN_BOTTOM_OFF_SCREEN_START )
PPU_ScanTable[ nIdx ] = SCAN_ON_SCREEN;
else
if ( nIdx < SCAN_UNKNOWN_START )
PPU_ScanTable[ nIdx ] = SCAN_ON_SCREEN;
else
if ( nIdx < SCAN_VBLANK_START )
PPU_ScanTable[ nIdx ] = SCAN_UNKNOWN;
else
PPU_ScanTable[ nIdx ] = SCAN_VBLANK;
}
}
/*===================================================================*/
/* */
/* InfoNES_Fin() : Completion treatment */
/* */
/*===================================================================*/
void InfoNES_Fin()
{
/*
* Completion treatment
*
* Remarks
* Release resources
*/
// Finalize pAPU
InfoNES_pAPUDone();
// Release a memory for ROM
InfoNES_ReleaseRom();
}
/*===================================================================*/
/* */
/* InfoNES_Load() : Load a cassette */
/* */
/*===================================================================*/
int InfoNES_Load( const char *pszFileName )
{
/*
* Load a cassette
*
* Parameters
* const char *pszFileName (Read)
* File name of ROM image
*
* Return values
* 0 : It was finished normally.
* -1 : An error occurred.
*
* Remarks
* Read a ROM image in the memory.
* Reset InfoNES.
*/
// Release a memory for ROM
InfoNES_ReleaseRom();
// Read a ROM image in the memory
if ( InfoNES_ReadRom( pszFileName ) < 0 )
return -1;
// Reset InfoNES
if ( InfoNES_Reset() < 0 )
return -1;
// Successful
return 0;
}
/*===================================================================*/
/* */
/* InfoNES_Reset() : Reset InfoNES */
/* */
/*===================================================================*/
int InfoNES_Reset()
{
/*
* Reset InfoNES
*
* Return values
* 0 : Normally
* -1 : Non support mapper
*
* Remarks
* Initialize Resources, PPU and Mapper.
* Reset CPU.
*/
int nIdx;
/*-------------------------------------------------------------------*/
/* Get information on the cassette */
/*-------------------------------------------------------------------*/
// Get Mapper Number
MapperNo = NesHeader.byInfo1 >> 4;
// Check bit counts of Mapper No.
for ( nIdx = 4; nIdx < 8 && NesHeader.byReserve[ nIdx ] == 0; ++nIdx )
;
if ( nIdx == 8 )
{
// Mapper Number is 8bits
MapperNo |= ( NesHeader.byInfo2 & 0xf0 );
}
// Get information on the ROM
ROM_Mirroring = NesHeader.byInfo1 & 1;
ROM_SRAM = NesHeader.byInfo1 & 2;
ROM_Trainer = NesHeader.byInfo1 & 4;
ROM_FourScr = NesHeader.byInfo1 & 8;
/*-------------------------------------------------------------------*/
/* Initialize resources */
/*-------------------------------------------------------------------*/
// Clear RAM
InfoNES_MemorySet( RAM, 0, sizeof RAM );
// Reset frame skip and frame count
FrameSkip = 0;
FrameCnt = 0;
#if 0
// Reset work frame
WorkFrame = DoubleFrame[ 0 ];
WorkFrameIdx = 0;
#endif
// Reset update flag of ChrBuf
ChrBufUpdate = 0xff;
// Reset palette table
InfoNES_MemorySet( PalTable, 0, sizeof PalTable );
// Reset APU register
InfoNES_MemorySet( APU_Reg, 0, sizeof APU_Reg );
// Reset joypad
PAD1_Latch = PAD2_Latch = PAD_System = 0;
PAD1_Bit = PAD2_Bit = 0;
/*-------------------------------------------------------------------*/
/* Initialize PPU */
/*-------------------------------------------------------------------*/
InfoNES_SetupPPU();
/*-------------------------------------------------------------------*/
/* Initialize pAPU */
/*-------------------------------------------------------------------*/
InfoNES_pAPUInit();
/*-------------------------------------------------------------------*/
/* Initialize Mapper */
/*-------------------------------------------------------------------*/
// Get Mapper Table Index
for ( nIdx = 0; MapperTable[ nIdx ].nMapperNo != -1; ++nIdx )
{
if ( MapperTable[ nIdx ].nMapperNo == MapperNo )
break;
}
if ( MapperTable[ nIdx ].nMapperNo == -1 )
{
// Non support mapper
InfoNES_MessageBox( "Mapper #%d is unsupported.\n" );
return -1;
}
// Set up a mapper initialization function
MapperTable[ nIdx ].pMapperInit();
/*-------------------------------------------------------------------*/
/* Reset CPU */
/*-------------------------------------------------------------------*/
K6502_Reset();
// Successful
return 0;
}
/*===================================================================*/
/* */
/* InfoNES_SetupPPU() : Initialize PPU */
/* */
/*===================================================================*/
void InfoNES_SetupPPU()
{
/*
* Initialize PPU
*
*/
int nPage;
// Clear PPU and Sprite Memory
InfoNES_MemorySet( PPURAM, 0, sizeof PPURAM );
InfoNES_MemorySet( SPRRAM, 0, sizeof SPRRAM );
// Reset PPU Register
PPU_R0 = PPU_R1 = PPU_R2 = PPU_R3 = PPU_R7 = 0;
// Reset latch flag
PPU_Latch_Flag = 0;
// Reset up and down clipping flag
PPU_UpDown_Clip = 0;
FrameStep = 0;
FrameIRQ_Enable = 0;
// Reset Scroll values
PPU_Scr_V = PPU_Scr_V_Next = PPU_Scr_V_Byte = PPU_Scr_V_Byte_Next = PPU_Scr_V_Bit = PPU_Scr_V_Bit_Next = 0;
PPU_Scr_H = PPU_Scr_H_Next = PPU_Scr_H_Byte = PPU_Scr_H_Byte_Next = PPU_Scr_H_Bit = PPU_Scr_H_Bit_Next = 0;
// Reset PPU address
PPU_Addr = 0;
PPU_Temp = 0;
// Reset scanline
PPU_Scanline = 0;
// Reset hit position of sprite #0
SpriteJustHit = 0;
// Reset information on PPU_R0
PPU_Increment = 1;
PPU_NameTableBank = NAME_TABLE0;
PPU_BG_Base = ChrBuf;
PPU_SP_Base = ChrBuf + 256 * 64;
PPU_SP_Height = 8;
// Reset PPU banks
for ( nPage = 0; nPage < 16; ++nPage )
PPUBANK[ nPage ] = &PPURAM[ nPage * 0x400 ];
/* Mirroring of Name Table */
InfoNES_Mirroring( ROM_Mirroring );
/* Reset VRAM Write Enable */
byVramWriteEnable = ( NesHeader.byVRomSize == 0 ) ? 1 : 0;
}
/*===================================================================*/
/* */
/* InfoNES_Mirroring() : Set up a Mirroring of Name Table */
/* */
/*===================================================================*/
void InfoNES_Mirroring( int nType )
{
/*
* Set up a Mirroring of Name Table
*
* Parameters
* int nType (Read)
* Mirroring Type
* 0 : Horizontal
* 1 : Vertical
* 2 : One Screen 0x2400
* 3 : One Screen 0x2000
* 4 : Four Screen
* 5 : Special for Mapper #233
*/
PPUBANK[ NAME_TABLE0 ] = &PPURAM[ PPU_MirrorTable[ nType ][ 0 ] * 0x400 ];
PPUBANK[ NAME_TABLE1 ] = &PPURAM[ PPU_MirrorTable[ nType ][ 1 ] * 0x400 ];
PPUBANK[ NAME_TABLE2 ] = &PPURAM[ PPU_MirrorTable[ nType ][ 2 ] * 0x400 ];
PPUBANK[ NAME_TABLE3 ] = &PPURAM[ PPU_MirrorTable[ nType ][ 3 ] * 0x400 ];
}
/*===================================================================*/
/* */
/* InfoNES_Main() : The main loop of InfoNES */
/* */
/*===================================================================*/
void InfoNES_Main()
{
/*
* The main loop of InfoNES
*
*/
// Initialize InfoNES
InfoNES_Init();
// Main loop
while ( 1 )
{
/*-------------------------------------------------------------------*/
/* To the menu screen */
/*-------------------------------------------------------------------*/
if ( InfoNES_Menu() == -1 )
break; // Quit
/*-------------------------------------------------------------------*/
/* Start a NES emulation */
/*-------------------------------------------------------------------*/
InfoNES_Cycle();
}
// Completion treatment
InfoNES_Fin();
}
/*===================================================================*/
/* */
/* InfoNES_Cycle() : The loop of emulation */
/* */
/*===================================================================*/
void InfoNES_Cycle()
{
/*
* The loop of emulation
*
*/
// Set the PPU adress to the buffered value
if ( ( PPU_R1 & R1_SHOW_SP ) || ( PPU_R1 & R1_SHOW_SCR ) )
PPU_Addr = PPU_Temp;
// Emulation loop
for (;;)
{
int nStep;
// Set a flag if a scanning line is a hit in the sprite #0
if ( SpriteJustHit == PPU_Scanline &&
PPU_ScanTable[ PPU_Scanline ] == SCAN_ON_SCREEN )
{
// # of Steps to execute before sprite #0 hit
nStep = SPRRAM[ SPR_X ] * STEP_PER_SCANLINE / NES_DISP_WIDTH;
// Execute instructions
K6502_Step( nStep );
// Set a sprite hit flag
if ( ( PPU_R1 & R1_SHOW_SP ) && ( PPU_R1 & R1_SHOW_SCR ) )
PPU_R2 |= R2_HIT_SP;
// NMI is required if there is necessity
if ( ( PPU_R0 & R0_NMI_SP ) && ( PPU_R1 & R1_SHOW_SP ) )
NMI_REQ;
// Execute instructions
K6502_Step( STEP_PER_SCANLINE - nStep );
}
else
{
// Execute instructions
K6502_Step( STEP_PER_SCANLINE );
}
// Frame IRQ in H-Sync
FrameStep += STEP_PER_SCANLINE;
if ( FrameStep > STEP_PER_FRAME && FrameIRQ_Enable )
{
FrameStep %= STEP_PER_FRAME;
IRQ_REQ;
APU_Reg[ 0x4015 ] |= 0x40;
}
// A mapper function in H-Sync
MapperHSync();
// A function in H-Sync
if ( InfoNES_HSync() == -1 )
return; // To the menu screen
// HSYNC Wait
InfoNES_Wait();
}
}
/*===================================================================*/
/* */
/* InfoNES_HSync() : A function in H-Sync */
/* */
/*===================================================================*/
int InfoNES_HSync()
{
/*
* A function in H-Sync
*
* Return values
* 0 : Normally
* -1 : Exit an emulation
*/
/*-------------------------------------------------------------------*/
/* Render a scanline */
/*-------------------------------------------------------------------*/
if ( FrameCnt == 0 &&
PPU_ScanTable[ PPU_Scanline ] == SCAN_ON_SCREEN )
{
InfoNES_DrawLine();
}
/*-------------------------------------------------------------------*/
/* Set new scroll values */
/*-------------------------------------------------------------------*/
PPU_Scr_V = PPU_Scr_V_Next;
PPU_Scr_V_Byte = PPU_Scr_V_Byte_Next;
PPU_Scr_V_Bit = PPU_Scr_V_Bit_Next;
PPU_Scr_H = PPU_Scr_H_Next;
PPU_Scr_H_Byte = PPU_Scr_H_Byte_Next;
PPU_Scr_H_Bit = PPU_Scr_H_Bit_Next;
/*-------------------------------------------------------------------*/
/* Next Scanline */
/*-------------------------------------------------------------------*/
PPU_Scanline = ( PPU_Scanline == SCAN_VBLANK_END ) ? 0 : PPU_Scanline + 1;
/*-------------------------------------------------------------------*/
/* Operation in the specific scanning line */
/*-------------------------------------------------------------------*/
switch ( PPU_Scanline )
{
case SCAN_TOP_OFF_SCREEN:
// Reset a PPU status
PPU_R2 = 0;
// Set up a character data
if ( NesHeader.byVRomSize == 0 && FrameCnt == 0 )
InfoNES_SetupChr();
// Get position of sprite #0
InfoNES_GetSprHitY();
break;
case SCAN_UNKNOWN_START:
if ( FrameCnt == 0 )
{
// Transfer the contents of work frame on the screen
InfoNES_LoadFrame();
#if 0
// Switching of the double buffer
WorkFrameIdx = 1 - WorkFrameIdx;
WorkFrame = DoubleFrame[ WorkFrameIdx ];
#endif
}
break;
case SCAN_VBLANK_START:
// FrameCnt + 1
FrameCnt = ( FrameCnt >= FrameSkip ) ? 0 : FrameCnt + 1;
// Set a V-Blank flag
PPU_R2 = R2_IN_VBLANK;
// Reset latch flag
PPU_Latch_Flag = 0;
// pAPU Sound function in V-Sync
if ( !APU_Mute )
InfoNES_pAPUVsync();
// A mapper function in V-Sync
MapperVSync();
// Get the condition of the joypad
InfoNES_PadState( &PAD1_Latch, &PAD2_Latch, &PAD_System );
// NMI on V-Blank
if ( PPU_R0 & R0_NMI_VB )
NMI_REQ;
// Exit an emulation if a QUIT button is pushed
if ( PAD_PUSH( PAD_System, PAD_SYS_QUIT ) )
return -1; // Exit an emulation
break;
}
// Successful
return 0;
}
/*===================================================================*/
/* */
/* InfoNES_DrawLine() : Render a scanline */
/* */
/*===================================================================*/
void InfoNES_DrawLine()
{
/*
* Render a scanline
*
*/
int nX;
int nY;
int nY4;
int nYBit;
WORD *pPalTbl;
BYTE *pAttrBase;
WORD *pPoint;
int nNameTable;
BYTE *pbyNameTable;
BYTE *pbyChrData;
BYTE *pSPRRAM;
int nAttr;
int nSprCnt;
int nIdx;
int nSprData;
BYTE bySprCol;
BYTE pSprBuf[ NES_DISP_WIDTH + 7 ];
/*-------------------------------------------------------------------*/
/* Render Background */
/*-------------------------------------------------------------------*/
/* MMC5 VROM switch */
MapperRenderScreen( 1 );
// Pointer to the render position
pPoint = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
// Clear a scanline if screen is off
if ( !( PPU_R1 & R1_SHOW_SCR ) )
{
InfoNES_MemorySet( pPoint, 0, NES_DISP_WIDTH << 1 );
}
else
{
nNameTable = PPU_NameTableBank;
nY = PPU_Scr_V_Byte + ( PPU_Scanline >> 3 );
nYBit = PPU_Scr_V_Bit + ( PPU_Scanline & 7 );
if ( nYBit > 7 )
{
++nY;
nYBit &= 7;
}
nYBit <<= 3;
if ( nY > 29 )
{
// Next NameTable (An up-down direction)
nNameTable ^= NAME_TABLE_V_MASK;
nY -= 30;
}
nX = PPU_Scr_H_Byte;
nY4 = ( ( nY & 2 ) << 1 );
/*-------------------------------------------------------------------*/
/* Rendering of the block of the left end */
/*-------------------------------------------------------------------*/
pbyNameTable = PPUBANK[ nNameTable ] + nY * 32 + nX;
pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
pAttrBase = PPUBANK[ nNameTable ] + 0x3c0 + ( nY / 4 ) * 8;
pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
for ( nIdx = PPU_Scr_H_Bit; nIdx < 8; ++nIdx )
{
*( pPoint++ ) = pPalTbl[ pbyChrData[ nIdx ] ];
}
// Callback at PPU read/write
MapperPPU( PATTBL( pbyChrData ) );
++nX;
++pbyNameTable;
/*-------------------------------------------------------------------*/
/* Rendering of the left table */
/*-------------------------------------------------------------------*/
for ( ; nX < 32; ++nX )
{
pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
pPoint[ 0 ] = pPalTbl[ pbyChrData[ 0 ] ];
pPoint[ 1 ] = pPalTbl[ pbyChrData[ 1 ] ];
pPoint[ 2 ] = pPalTbl[ pbyChrData[ 2 ] ];
pPoint[ 3 ] = pPalTbl[ pbyChrData[ 3 ] ];
pPoint[ 4 ] = pPalTbl[ pbyChrData[ 4 ] ];
pPoint[ 5 ] = pPalTbl[ pbyChrData[ 5 ] ];
pPoint[ 6 ] = pPalTbl[ pbyChrData[ 6 ] ];
pPoint[ 7 ] = pPalTbl[ pbyChrData[ 7 ] ];
pPoint += 8;
// Callback at PPU read/write
MapperPPU( PATTBL( pbyChrData ) );
++pbyNameTable;
}
// Holizontal Mirror
nNameTable ^= NAME_TABLE_H_MASK;
pbyNameTable = PPUBANK[ nNameTable ] + nY * 32;
pAttrBase = PPUBANK[ nNameTable ] + 0x3c0 + ( nY / 4 ) * 8;
/*-------------------------------------------------------------------*/
/* Rendering of the right table */
/*-------------------------------------------------------------------*/
for ( nX = 0; nX < PPU_Scr_H_Byte; ++nX )
{
pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
pPoint[ 0 ] = pPalTbl[ pbyChrData[ 0 ] ];
pPoint[ 1 ] = pPalTbl[ pbyChrData[ 1 ] ];
pPoint[ 2 ] = pPalTbl[ pbyChrData[ 2 ] ];
pPoint[ 3 ] = pPalTbl[ pbyChrData[ 3 ] ];
pPoint[ 4 ] = pPalTbl[ pbyChrData[ 4 ] ];
pPoint[ 5 ] = pPalTbl[ pbyChrData[ 5 ] ];
pPoint[ 6 ] = pPalTbl[ pbyChrData[ 6 ] ];
pPoint[ 7 ] = pPalTbl[ pbyChrData[ 7 ] ];
pPoint += 8;
// Callback at PPU read/write
MapperPPU( PATTBL( pbyChrData ) );
++pbyNameTable;
}
/*-------------------------------------------------------------------*/
/* Rendering of the block of the right end */
/*-------------------------------------------------------------------*/
pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
for ( nIdx = 0; nIdx < PPU_Scr_H_Bit; ++nIdx )
{
pPoint[ nIdx ] = pPalTbl[ pbyChrData[ nIdx ] ];
}
// Callback at PPU read/write
MapperPPU( PATTBL( pbyChrData ) );
/*-------------------------------------------------------------------*/
/* Backgroud Clipping */
/*-------------------------------------------------------------------*/
if ( !( PPU_R1 & R1_CLIP_BG ) )
{
WORD *pPointTop;
pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
InfoNES_MemorySet( pPointTop, 0, 8 << 1 );
}
/*-------------------------------------------------------------------*/
/* Clear a scanline if up and down clipping flag is set */
/*-------------------------------------------------------------------*/
if ( PPU_UpDown_Clip &&
( SCAN_ON_SCREEN_START > PPU_Scanline || PPU_Scanline > SCAN_BOTTOM_OFF_SCREEN_START ) )
{
WORD *pPointTop;
pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
InfoNES_MemorySet( pPointTop, 0, NES_DISP_WIDTH << 1 );
}
}
/*-------------------------------------------------------------------*/
/* Render a sprite */
/*-------------------------------------------------------------------*/
/* MMC5 VROM switch */
MapperRenderScreen( 0 );
if ( PPU_R1 & R1_SHOW_SP )
{
// Reset Scanline Sprite Count
PPU_R2 &= ~R2_MAX_SP;
// Reset sprite buffer
InfoNES_MemorySet( pSprBuf, 0, sizeof pSprBuf );
// Render a sprite to the sprite buffer
nSprCnt = 0;
for ( pSPRRAM = SPRRAM + ( 63 << 2 ); pSPRRAM >= SPRRAM; pSPRRAM -= 4 )
{
nY = pSPRRAM[ SPR_Y ] + 1;
if ( nY > PPU_Scanline || nY + PPU_SP_Height <= PPU_Scanline )
continue; // Next sprite
/*-------------------------------------------------------------------*/
/* A sprite in scanning line */
/*-------------------------------------------------------------------*/
// Holizontal Sprite Count +1
++nSprCnt;
nAttr = pSPRRAM[ SPR_ATTR ];
nYBit = PPU_Scanline - nY;
nYBit = ( nAttr & SPR_ATTR_V_FLIP ) ? ( PPU_SP_Height - nYBit - 1 ) << 3 : nYBit << 3;
if ( PPU_R0 & R0_SP_SIZE )
{
// Sprite size 8x16
if ( pSPRRAM[ SPR_CHR ] & 1 )
{
pbyChrData = ChrBuf + 256 * 64 + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit;
}
else
{
pbyChrData = ChrBuf + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit;
}
}
else
{
// Sprite size 8x8
pbyChrData = PPU_SP_Base + ( pSPRRAM[ SPR_CHR ] << 6 ) + nYBit;
}
nAttr ^= SPR_ATTR_PRI;
bySprCol = ( nAttr & ( SPR_ATTR_COLOR | SPR_ATTR_PRI ) ) << 2;
nX = pSPRRAM[ SPR_X ];
if ( nAttr & SPR_ATTR_H_FLIP )
{
// Horizontal flip
if ( pbyChrData[ 7 ] )
pSprBuf[ nX ] = bySprCol | pbyChrData[ 7 ];
if ( pbyChrData[ 6 ] )
pSprBuf[ nX + 1 ] = bySprCol | pbyChrData[ 6 ];
if ( pbyChrData[ 5 ] )
pSprBuf[ nX + 2 ] = bySprCol | pbyChrData[ 5 ];
if ( pbyChrData[ 4 ] )
pSprBuf[ nX + 3 ] = bySprCol | pbyChrData[ 4 ];
if ( pbyChrData[ 3 ] )
pSprBuf[ nX + 4 ] = bySprCol | pbyChrData[ 3 ];
if ( pbyChrData[ 2 ] )
pSprBuf[ nX + 5 ] = bySprCol | pbyChrData[ 2 ];
if ( pbyChrData[ 1 ] )
pSprBuf[ nX + 6 ] = bySprCol | pbyChrData[ 1 ];
if ( pbyChrData[ 0 ] )
pSprBuf[ nX + 7 ] = bySprCol | pbyChrData[ 0 ];
}
else
{
// Non flip
if ( pbyChrData[ 0 ] )
pSprBuf[ nX ] = bySprCol | pbyChrData[ 0 ];
if ( pbyChrData[ 1 ] )
pSprBuf[ nX + 1 ] = bySprCol | pbyChrData[ 1 ];
if ( pbyChrData[ 2 ] )
pSprBuf[ nX + 2 ] = bySprCol | pbyChrData[ 2 ];
if ( pbyChrData[ 3 ] )
pSprBuf[ nX + 3 ] = bySprCol | pbyChrData[ 3 ];
if ( pbyChrData[ 4 ] )
pSprBuf[ nX + 4 ] = bySprCol | pbyChrData[ 4 ];
if ( pbyChrData[ 5 ] )
pSprBuf[ nX + 5 ] = bySprCol | pbyChrData[ 5 ];
if ( pbyChrData[ 6 ] )
pSprBuf[ nX + 6 ] = bySprCol | pbyChrData[ 6 ];
if ( pbyChrData[ 7 ] )
pSprBuf[ nX + 7 ] = bySprCol | pbyChrData[ 7 ];
}
}
// Rendering sprite
pPoint -= ( NES_DISP_WIDTH - PPU_Scr_H_Bit );
for ( nX = 0; nX < NES_DISP_WIDTH; ++nX )
{
nSprData = pSprBuf[ nX ];
if ( nSprData && ( nSprData & 0x80 || pPoint[ nX ]/* & 0x8000*/ ) )
{
pPoint[ nX ] = PalTable[ ( nSprData & 0xf ) + 0x10 ];
}
}
/*-------------------------------------------------------------------*/
/* Sprite Clipping */
/*-------------------------------------------------------------------*/
if ( !( PPU_R1 & R1_CLIP_SP ) )
{
WORD *pPointTop;
pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
InfoNES_MemorySet( pPointTop, 0, 8 << 1 );
}
if ( nSprCnt >= 8 )
PPU_R2 |= R2_MAX_SP; // Set a flag of maximum sprites on scanline
}
}
/*===================================================================*/
/* */
/* InfoNES_GetSprHitY() : Get a position of scanline hits sprite #0 */
/* */
/*===================================================================*/
void InfoNES_GetSprHitY()
{
/*
* Get a position of scanline hits sprite #0
*
*/
int nYBit;
DWORD *pdwChrData;
int nOff;
if ( SPRRAM[ SPR_ATTR ] & SPR_ATTR_V_FLIP )
{
// Vertical flip
nYBit = ( PPU_SP_Height - 1 ) << 3;
nOff = -2;
}
else
{
// Non flip
nYBit = 0;
nOff = 2;
}
if ( PPU_R0 & R0_SP_SIZE )
{
// Sprite size 8x16
if ( SPRRAM[ SPR_CHR ] & 1 )
{
pdwChrData = (DWORD *)( ChrBuf + 256 * 64 + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit );
}
else
{
pdwChrData = (DWORD * )( ChrBuf + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit );
}
}
else
{
// Sprite size 8x8
pdwChrData = (DWORD *)( PPU_SP_Base + ( SPRRAM[ SPR_CHR ] << 6 ) + nYBit );
}
if ( ( SPRRAM[ SPR_Y ] + 1 <= SCAN_UNKNOWN_START ) && ( SPRRAM[SPR_Y] > 0 ) )
{
for ( int nLine = 0; nLine < PPU_SP_Height; nLine++ )
{
if ( pdwChrData[ 0 ] | pdwChrData[ 1 ] )
{
// Scanline hits sprite #0
SpriteJustHit = SPRRAM[SPR_Y] + 1 + nLine;
nLine = SCAN_VBLANK_END;
}
pdwChrData += nOff;
}
} else {
// Scanline didn't hit sprite #0
SpriteJustHit = SCAN_UNKNOWN_START + 1;
}
}
/*===================================================================*/
/* */
/* InfoNES_SetupChr() : Develop character data */
/* */
/*===================================================================*/
void InfoNES_SetupChr()
{
/*
* Develop character data
*
*/
BYTE *pbyBGData;
BYTE byData1;
BYTE byData2;
int nIdx;
int nY;
int nOff;
static BYTE *pbyPrevBank[ 8 ];
int nBank;
for ( nBank = 0; nBank < 8; ++nBank )
{
if ( pbyPrevBank[ nBank ] == PPUBANK[ nBank ] && !( ( ChrBufUpdate >> nBank ) & 1 ) )
continue; // Next bank
/*-------------------------------------------------------------------*/
/* An address is different from the last time */
/* or */
/* An update flag is being set */
/*-------------------------------------------------------------------*/
for ( nIdx = 0; nIdx < 64; ++nIdx )
{
nOff = ( nBank << 12 ) + ( nIdx << 6 );
for ( nY = 0; nY < 8; ++nY )
{
pbyBGData = PPUBANK[ nBank ] + ( nIdx << 4 ) + nY;
byData1 = ( ( pbyBGData[ 0 ] >> 1 ) & 0x55 ) | ( pbyBGData[ 8 ] & 0xAA );
byData2 = ( pbyBGData[ 0 ] & 0x55 ) | ( ( pbyBGData[ 8 ] << 1 ) & 0xAA );
ChrBuf[ nOff ] = ( byData1 >> 6 ) & 3;
ChrBuf[ nOff + 1 ] = ( byData2 >> 6 ) & 3;
ChrBuf[ nOff + 2 ] = ( byData1 >> 4 ) & 3;
ChrBuf[ nOff + 3 ] = ( byData2 >> 4 ) & 3;
ChrBuf[ nOff + 4 ] = ( byData1 >> 2 ) & 3;
ChrBuf[ nOff + 5 ] = ( byData2 >> 2 ) & 3;
ChrBuf[ nOff + 6 ] = byData1 & 3;
ChrBuf[ nOff + 7 ] = byData2 & 3;
nOff += 8;
}
}
// Keep this address
pbyPrevBank[ nBank ] = PPUBANK[ nBank ];
}
// Reset update flag
ChrBufUpdate = 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化