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

📄 nes.cpp

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