📄 nes.cpp
字号:
//////////////////////////////////////////////////////////////////////////
// //
// NES Emulation core //
// Norix //
// written 2001/02/22 //
// last modify ----/--/-- //
//////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "typedef.h"
#include "macro.h"
#include "VirtuaNESres.h"
#include "DebugOut.h"
#include "App.h"
#include "Pathlib.h"
#include "Config.h"
#include "Crclib.h"
#include "nes.h"
#include "mmu.h"
#include "cpu.h"
#include "ppu.h"
#include "apu.h"
#include "pad.h"
#include "rom.h"
#include "mapper.h"
#include "DirectDraw.h"
#include "DirectSound.h"
#include "DirectInput.h"
#include "pngwrite.h"
NESCONFIG NESCONFIG_NTSC = {
21477270.0f, // Base clock
1789772.5f, // Cpu clock
262, // Total scanlines
1364, // Scanline total cycles(15.75KHz)
1024, // H-Draw cycles
340, // H-Blank cycles
4, // End cycles
1364*262, // Frame cycles
29830, // FrameIRQ cycles
60, // Frame rate(Be originally 59.94Hz)
1000.0f/60.0f // Frame period(ms)
};
NESCONFIG NESCONFIG_PAL = {
21281364.0f, // Base clock
1773447.0f, // Cpu clock
312, // Total scanlines
1362, // Scanline total cycles(15.625KHz)
1024, // H-Draw cycles
338, // H-Blank cycles
2, // End cycles
1362*312, // Frame cycles
35469, // FrameIRQ cycles
50, // Frame rate(Hz)
1000.0f/50.0f // Frame period(ms)
};
// Pad disp
BYTE NES::m_PadImg[] = {
28, 8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F,
0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
BYTE NES::m_KeyImg0[] = {
2, 2,
0x2A, 0x2A,
0x2A, 0x2A,
};
BYTE NES::m_KeyImg1[] = {
3, 3,
0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A,
};
BYTE NES::m_KeyImg2[] = {
4, 4,
0xFF, 0x2A, 0x2A, 0xFF,
0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A,
0xFF, 0x2A, 0x2A, 0xFF,
};
//
//
//
NES::NES( const char* fname )
{
DEBUGOUT( "VirtuaNES - NES Emulator for Win32 by Norix (C)2001\n" );
m_bDiskThrottle = FALSE;
m_CommandRequest = 0;
m_nSnapNo = 0;
m_bNsfPlaying = FALSE;
m_bMoviePlay = m_bMovieRec = FALSE;
m_fpMovie = NULL;
m_bTapePlay = m_bTapeRec = FALSE;
m_fpTape = NULL;
m_TapeCycles = 0.0;
m_TapeIn = m_TapeOut = 0;
m_bBarcode = FALSE;
m_BarcodeOut = 0;
m_BarcodePtr = 0;
m_BarcodeCycles = 0;
m_bBarcode2 = FALSE;
m_TurboFileBank = 0;
cpu = NULL;
ppu = NULL;
apu = NULL;
rom = NULL;
pad = NULL;
mapper = NULL;
SAVERAM_SIZE = 8*1024; // 8K byte
// IRQ type
nIRQtype = 0;
// FrameIRQ mode
bFrameIRQ = TRUE;
// NTSC/PAL VideoMode
bVideoMode = FALSE;
// Default config
nescfg = &NESCONFIG_NTSC;
// Cheat
CheatInitial();
// TEST
m_dwTotalCycle = 0;
m_dwTotalTempCycle = 0;
m_dwProfileTotalCycle = 0;
m_dwProfileTotalCount = 0;
m_dwProfileCycle = 0;
m_dwProfileTempCycle = 0;
try {
DEBUGOUT( "Allocating CPU..." );
if( !(cpu = new CPU(this)) )
throw "Allocating CPU failed.";
DEBUGOUT( "Ok.\n" );
DEBUGOUT( "Allocating PPU..." );
if( !(ppu = new PPU(this)) )
throw "Allocating PPU failed.";
DEBUGOUT( "Ok.\n" );
DEBUGOUT( "Allocating APU..." );
if( !(apu = new APU(this)) )
throw "Allocating APU failed.";
DEBUGOUT( "Ok.\n" );
DEBUGOUT( "Allocating PAD..." );
if( !(pad = new PAD(this)) )
throw "Allocating PAD failed.";
DEBUGOUT( "Ok.\n" );
DEBUGOUT( "Loading ROM Image...\n" );
if( !(rom = new ROM(fname)) )
throw "Allocating ROM failed.";
if( !(mapper = CreateMapper(this, rom->GetMapperNo())) ) {
// 枹僒億乕僩偺儅僢僷乕偱偡
LPCSTR szErrStr = CApp::GetErrorString( IDS_ERROR_UNSUPPORTMAPPER );
sprintf( szErrorString, szErrStr, rom->GetMapperNo() );
throw szErrorString;
}
DEBUGOUT( "Ok.\n" );
DEBUGOUT( "ROM status\n" );
DEBUGOUT( " %s\n", rom->GetRomName() );
DEBUGOUT( " Mapper : %03d\n", rom->GetMapperNo() );
DEBUGOUT( " PRG SIZE : %4dK\n", 16*(INT)rom->GetPROM_SIZE() );
DEBUGOUT( " CHR SIZE : %4dK\n", 8*(INT)rom->GetVROM_SIZE() );
DEBUGOUT( " V MIRROR : " );
if( rom->IsVMIRROR() ) DEBUGOUT( "Yes\n" );
else DEBUGOUT( "No\n" );
DEBUGOUT( " 4 SCREEN : " );
if( rom->Is4SCREEN() ) DEBUGOUT( "Yes\n" );
else DEBUGOUT( "No\n" );
DEBUGOUT( " SAVE RAM : " );
if( rom->IsSAVERAM() ) DEBUGOUT( "Yes\n" );
else DEBUGOUT( "No\n" );
DEBUGOUT( " TRAINER : " );
if( rom->IsTRAINER() ) DEBUGOUT( "Yes\n" );
else DEBUGOUT( "No\n" );
DEBUGOUT( " VS-Unisystem : " );
if( rom->IsVSUNISYSTEM() ) DEBUGOUT( "Yes\n" );
else DEBUGOUT( "No\n" );
NesSub_MemoryInitial();
LoadSRAM();
LoadDISK();
{
// Pad僋儔僗撪偩偲弶婜壔僞僀儈儞僌偑抶偄偺偱偙偙偱
DWORD crc = rom->GetPROM_CRC();
if( crc == 0xe792de94 // Best Play - Pro Yakyuu (New) (J)
|| crc == 0xf79d684a // Best Play - Pro Yakyuu (Old) (J)
|| crc == 0xc2ef3422 // Best Play - Pro Yakyuu 2 (J)
|| crc == 0x974e8840 // Best Play - Pro Yakyuu '90 (J)
|| crc == 0xb8747abf // Best Play - Pro Yakyuu Special (J)
|| crc == 0x9fa1c11f // Castle Excellent (J)
|| crc == 0x0b0d4d1b // Derby Stallion - Zenkoku Ban (J)
|| crc == 0x728c3d98 // Downtown - Nekketsu Monogatari (J)
|| crc == 0xd68a6f33 // Dungeon Kid (J)
|| crc == 0x3a51eb04 // Fleet Commander (J)
|| crc == 0x7c46998b // Haja no Fuuin (J)
|| crc == 0x7e5d2f1a // Itadaki Street - Watashi no Mise ni Yottette (J)
|| crc == 0xcee5857b // Ninjara Hoi! (J)
|| crc == 0x50ec5e8b // Wizardry - Legacy of Llylgamyn (J)
|| crc == 0x343e9146 // Wizardry - Proving Grounds of the Mad Overlord (J)
|| crc == 0x33d07e45 ) { // Wizardry - The Knight of Diamonds (J)
pad->SetExController( PAD::EXCONTROLLER_TURBOFILE );
}
}
LoadTurboFile();
// VS-Unisystem偺僨僼僅儖僩愝掕
if( rom->IsVSUNISYSTEM() ) {
DWORD crc = rom->GetPROM_CRC();
m_VSDipValue = GetVSDefaultDipSwitchValue( crc );
m_VSDipTable = FindVSDipSwitchTable( crc );
#include "VS_Setting.h"
} else {
m_VSDipValue = 0;
m_VSDipTable = vsdip_default;
}
Reset();
// 僎乕儉屌桳偺僨僼僅儖僩僆僾僔儑儞傪愝掕(愝掕栠偡帪偵巊偆堊)
GameOption.defRenderMethod = (INT)GetRenderMethod();
GameOption.defIRQtype = (INT)GetIrqType();
GameOption.defFrameIRQ = GetFrameIRQmode();
GameOption.defVideoMode = GetVideoMode();
// 愝掕傪儘乕僪偟偰愝掕偡傞(僄儞僩儕偑柍偗傟偽僨僼僅儖僩偑擖傞)
if( rom->GetMapperNo() != 20 ) {
GameOption.Load( rom->GetPROM_CRC() );
} else {
GameOption.Load( rom->GetGameID(), rom->GetMakerID() );
}
SetRenderMethod( (RENDERMETHOD)GameOption.nRenderMethod );
SetIrqType ( GameOption.nIRQtype );
SetFrameIRQmode( GameOption.bFrameIRQ );
SetVideoMode ( GameOption.bVideoMode );
} catch( CHAR* str ) {
DELETEPTR( cpu );
DELETEPTR( ppu );
DELETEPTR( apu );
DELETEPTR( pad );
DELETEPTR( rom );
DELETEPTR( mapper );
throw str;
#ifndef _DEBUG
} catch( ... ) {
DELETEPTR( cpu );
DELETEPTR( ppu );
DELETEPTR( apu );
DELETEPTR( pad );
DELETEPTR( rom );
DELETEPTR( mapper );
// 晄柧側僄儔乕偑敪惗偟傑偟偨
throw CApp::GetErrorString( IDS_ERROR_UNKNOWN );
#endif
}
DEBUGOUT( "Starting emulation!\n" );
}
NES::~NES()
{
MovieStop();
SaveSRAM();
SaveDISK();
SaveTurboFile();
DEBUGOUT( "Free NES..." );
DELETEPTR( cpu );
DELETEPTR( ppu );
DELETEPTR( apu );
DELETEPTR( pad );
DELETEPTR( rom );
DELETEPTR( mapper );
DEBUGOUT( "Ok.\n" );
}
void NES::SetVideoMode( BOOL bMode )
{
bVideoMode = bMode;
if( !bVideoMode ) {
nescfg = &NESCONFIG_NTSC;
} else {
nescfg = &NESCONFIG_PAL;
}
apu->SoundSetup();
}
void NES::Reset()
{
SaveSRAM();
SaveDISK();
SaveTurboFile();
// RAM Clear
ZEROMEMORY( RAM, sizeof(RAM) );
if( rom->GetPROM_CRC() == 0x29401686 ) { // Minna no Taabou no Nakayoshi Dai Sakusen(J)
::memset( RAM, 0xFF, sizeof(RAM) );
}
// RAM set
if( !rom->IsSAVERAM() && rom->GetMapperNo() != 20 ) {
::memset( WRAM, 0xFF, sizeof(WRAM) );
}
ZEROMEMORY( CRAM, sizeof(CRAM) );
ZEROMEMORY( VRAM, sizeof(VRAM) );
ZEROMEMORY( SPRAM, sizeof(SPRAM) );
ZEROMEMORY( BGPAL, sizeof(BGPAL) );
ZEROMEMORY( SPPAL, sizeof(SPPAL) );
ZEROMEMORY( CPUREG, sizeof(CPUREG) );
ZEROMEMORY( PPUREG, sizeof(PPUREG) );
m_bDiskThrottle = FALSE;
SetRenderMethod( PRE_RENDER );
if( rom->IsPAL() ) {
SetVideoMode( TRUE );
}
PROM = rom->GetPROM();
VROM = rom->GetVROM();
PROM_8K_SIZE = rom->GetPROM_SIZE()*2;
PROM_16K_SIZE = rom->GetPROM_SIZE();
PROM_32K_SIZE = rom->GetPROM_SIZE()/2;
VROM_1K_SIZE = rom->GetVROM_SIZE()*8;
VROM_2K_SIZE = rom->GetVROM_SIZE()*4;
VROM_4K_SIZE = rom->GetVROM_SIZE()*2;
VROM_8K_SIZE = rom->GetVROM_SIZE();
// 僨僼僅儖僩僶儞僋
if( VROM_8K_SIZE ) {
SetVROM_8K_Bank( 0 );
} else {
SetCRAM_8K_Bank( 0 );
}
// 儈儔乕
if( rom->Is4SCREEN() ) {
SetVRAM_Mirror( VRAM_MIRROR4 );
} else if( rom->IsVMIRROR() ) {
SetVRAM_Mirror( VRAM_VMIRROR );
} else {
SetVRAM_Mirror( VRAM_HMIRROR );
}
apu->SelectExSound( 0 );
ppu->Reset();
mapper->Reset();
// Trainer
if( rom->IsTRAINER() ) {
::memcpy( WRAM+0x1000, rom->GetTRAINER(), 512 );
}
pad->Reset();
cpu->Reset();
apu->Reset();
if( rom->IsNSF() ) {
mapper->Reset();
}
base_cycles = emul_cycles = 0;
}
void NES::SoftReset()
{
pad->Reset();
cpu->Reset();
apu->Reset();
if( rom->IsNSF() ) {
mapper->Reset();
}
m_bDiskThrottle = FALSE;
base_cycles = emul_cycles = 0;
}
//--------------------------------------------------------------------
#define _CLOCKCOUNT FALSE
#ifdef _DEBUGOUT
#if _CLOCKCOUNT
static DWORD clktemp_all;
static DWORD clktemp_all_s;
static DWORD clktemp_cpu;
static DWORD clktemp_cpu_s;
static INT clktemp_cnt = 0;
#endif
#endif
//--------------------------------------------------------------------
void NES::EmulationCPU( INT basecycles )
{
INT cycles;
base_cycles += basecycles;
cycles = (INT)((base_cycles/12)-emul_cycles);
if( cycles > 0 ) {
#ifdef _DEBUGOUT
#if _CLOCKCOUNT
__asm {
rdtsc
mov clktemp_cpu_s, eax
}
#endif
#endif
emul_cycles += cpu->EXEC( cycles );
#ifdef _DEBUGOUT
#if _CLOCKCOUNT
__asm {
rdtsc
sub eax, clktemp_cpu_s
add clktemp_cpu, eax
}
#endif
#endif
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -