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

📄 emuthread.cpp

📁 著名的任天堂FC游戏机模拟器VirtuaNes 085版的源码!
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//
// 僄儈儏儗乕僞僗儗僢僪僋儔僗
//
#include "DebugOut.h"

#include "VirtuaNESres.h"
#include "EmuThread.h"
#include "MainFrame.h"
#include "Pathlib.h"

#include "NetPlay.h"

#include "DirectDraw.h"
#include "DirectSound.h"
#include "DirectInput.h"

// 帺暘帺恎
CEmuThread	Emu;

// This億僀儞僞
CEmuThread*	CEmuThread::g_pThis = NULL;
// 僂僀儞僪僂僴儞僪儖
HWND	CEmuThread::g_hWnd = NULL;
// 僄儈儏儗乕僞僆僽僕僃僋僩億僀儞僞
NES*	CEmuThread::g_nes = NULL;
// WAVE儗僐乕僟
CWaveRec CEmuThread::g_WaveRec;

// NetEvent Queue
deque<NETEV>	CEmuThread::NetEventQueue;
string		CEmuThread::strNetStateName;

// 僗僥乕僞僗
INT	CEmuThread::g_Status = CEmuThread::STATUS_NONE;
// 僗儗僢僪僀儀儞僩偲僀儀儞僩僴儞僪儖
INT	CEmuThread::g_Event = CEmuThread::EV_NONE;
LONG	CEmuThread::g_EventParam = 0;
LONG	CEmuThread::g_EventParam2 = 0;
HANDLE	CEmuThread::g_hEvent = NULL;
HANDLE	CEmuThread::g_hEventAccept = NULL;

// 僄儔乕儊僢僙乕僕
CHAR	CEmuThread::g_szErrorMessage[512];

// 僗僩儕儞僌僥乕僽儖
LPCSTR	CEmuThread::g_lpSoundMuteStringTable[] = {
	"Master ",
	"Rectangle 1",
	"Rectangle 2",
	"Triangle ",
	"Noise ",
	"DPCM ",
	"Ex CH1 ",
	"Ex CH2 ",
	"Ex CH3 ",
	"Ex CH4 ",
	"Ex CH5 ",
	"Ex CH6 ",
	"Ex CH7 ",
	"Ex CH8 ",
	NULL,
	NULL,
};

// 僗儗僢僪僾儔僀僆儕僥傿僥乕僽儖
INT	CEmuThread::g_PriorityTable[] = {
	THREAD_PRIORITY_IDLE,
	THREAD_PRIORITY_LOWEST,
	THREAD_PRIORITY_BELOW_NORMAL,
	THREAD_PRIORITY_NORMAL,
	THREAD_PRIORITY_ABOVE_NORMAL,
	THREAD_PRIORITY_HIGHEST,
	THREAD_PRIORITY_TIME_CRITICAL
};

CEmuThread::CEmuThread()
{
	g_pThis = this;

	m_hThread = NULL;
	g_Status = STATUS_NONE;
	m_nPriority = 3;	// Normal
	g_nes = NULL;
	m_nPauseCount = 0;
}

CEmuThread::~CEmuThread()
{
	Stop();
}

void	CEmuThread::SetPriority( INT nPriority )
{
	m_nPriority = nPriority;
	if( IsRunning() ) {
		::SetThreadPriority( m_hThread, g_PriorityTable[m_nPriority] );
	}
}

BOOL	CEmuThread::Start( HWND hWnd, NES* nes )
{
	Stop();

	if( !g_hEvent ) {
		if( !(g_hEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL )) ) {
			DEBUGOUT( "CreateEvent failed.\n" );
			goto	_Start_Failed;
		}
	}
	if( !g_hEventAccept ) {
		if( !(g_hEventAccept = ::CreateEvent( NULL, FALSE, FALSE, NULL )) ) {
			DEBUGOUT( "CreateEvent failed.\n" );
			goto	_Start_Failed;
		}
	}

	::ResetEvent( g_hEvent );
	::ResetEvent( g_hEventAccept );

	g_hWnd = hWnd;
	g_nes = nes;
	g_Event = EV_INITIAL;
	g_Status = STATUS_NONE;
	::SetEvent( g_hEvent );
	m_nPauseCount = 0;

	if( !(m_hThread = ::CreateThread( NULL, 0, ThreadProc, 0, 0, &m_dwThreadID )) ) {
		DEBUGOUT( "CreateThread failed.\n" );
		goto	_Start_Failed;
	}

	// 僗儗僢僪僾儔僀僆儕僥傿偺愝掕
	::SetThreadPriority( m_hThread, g_PriorityTable[m_nPriority] );

	// 偪傖傫偲婲摦偱偒偨偐妋擣偺堊僀儀儞僩傪懸偮
	::WaitForSingleObject( g_hEventAccept, INFINITE );
	g_Status = STATUS_RUN;

//	DEBUGOUT( "CEmuThread:Start() Thread started.\n" );
	return	TRUE;

_Start_Failed:
	CLOSEHANDLE( g_hEvent );
	CLOSEHANDLE( g_hEventAccept );

	DEBUGOUT( "CEmuThread:Start() Thread startup failed!!." );
	return	FALSE;
}

void	CEmuThread::Stop()
{
	if( IsRunning() ) {
		::ResetEvent( g_hEventAccept );

		g_Event = EV_EXIT;
		::SetEvent( g_hEvent );

		::WaitForSingleObject( m_hThread, INFINITE );
		CLOSEHANDLE( m_hThread );
		m_hThread = NULL;
		g_Status = STATUS_NONE;

		CLOSEHANDLE( g_hEvent );
		CLOSEHANDLE( g_hEventAccept );

		// 僱僢僩僾儗僀帪偺愗抐張棟
		NetPlay.Disconnect();

//		DEBUGOUT( "CEmuThread::Stop() Thread stoped.\n" );
	}
}

void	CEmuThread::Pause()
{
	if( IsRunning() ) {
		if( !IsPausing() ) {
			::ResetEvent( g_hEventAccept );
			g_Event = EV_PAUSE;
			::SetEvent( g_hEvent );
			::WaitForSingleObject( g_hEventAccept, INFINITE );
			g_Status = STATUS_PAUSE;
			m_nPauseCount++;
		} else {
			m_nPauseCount++;
		}
//		DEBUGOUT( "CEmuThread::Pause() Thread paused. Count=%d\n", m_nPauseCount );
	}
}

void	CEmuThread::Resume()
{
	if( IsRunning() ) {
		if( IsPausing() ) {
			if( --m_nPauseCount <= 0 ) {
				m_nPauseCount = 0;
				::ResetEvent( g_hEventAccept );
				g_Event = EV_RESUME;
				::SetEvent( g_hEvent );
				::WaitForSingleObject( g_hEventAccept, INFINITE );
				g_Status = STATUS_RUN;
			}
		}
//		DEBUGOUT( "CEmuThread::Resume() Thread resumed. Count=%d\n", m_nPauseCount );
	}
}

void	CEmuThread::Event( EMUEVENT ev )
{
	if( IsRunning() ) {
		::ResetEvent( g_hEventAccept );
		g_Event = ev;
		g_EventParam = 0;
		g_EventParam2 = -1;
		::SetEvent( g_hEvent );
		::WaitForSingleObject( g_hEventAccept, INFINITE );
	}
}

void	CEmuThread::EventParam( EMUEVENT ev, LONG param )
{
	if( IsRunning() ) {
		::ResetEvent( g_hEventAccept );
		g_Event = ev;
		g_EventParam = param;
		g_EventParam2 = -1;
		::SetEvent( g_hEvent );
		::WaitForSingleObject( g_hEventAccept, INFINITE );
	}
}

void	CEmuThread::EventParam2( EMUEVENT ev, LONG param, LONG param2 )
{
	if( IsRunning() ) {
		::ResetEvent( g_hEventAccept );
		g_Event = ev;
		g_EventParam = param;
		g_EventParam2 = param2;
		::SetEvent( g_hEvent );
		::WaitForSingleObject( g_hEventAccept, INFINITE );
	}
}

void	CEmuThread::DiskCommand( BYTE cmd )
{
	switch( cmd ) {
		case	0: // Eject
			if( g_nes->rom->GetDiskNo() > 0 ) {
				g_nes->Command( NES::NESCMD_DISK_EJECT );
				DirectDraw.SetMessageString( "Disk Eject." );
			}
			break;
		case	1: // Disk0 SideA
			if( g_nes->rom->GetDiskNo() > 0 ) {
				g_nes->Command( NES::NESCMD_DISK_0A );
				DirectDraw.SetMessageString( "Change Disk1 SideA." );
			}
			break;
		case	2: // Disk0 SideB
			if( g_nes->rom->GetDiskNo() > 1 ) {
				g_nes->Command( NES::NESCMD_DISK_0B );
				DirectDraw.SetMessageString( "Change Disk1 SideB." );
			}
			break;
		case	3: // Disk1 SideA
			if( g_nes->rom->GetDiskNo() > 2 ) {
				g_nes->Command( NES::NESCMD_DISK_1A );
				DirectDraw.SetMessageString( "Change Disk2 SideA." );
			}
			break;
		case	4: // Disk1 SideB
			if( g_nes->rom->GetDiskNo() > 3 ) {
				g_nes->Command( NES::NESCMD_DISK_1B );
				DirectDraw.SetMessageString( "Change Disk2 SideB." );
			}
			break;
	}
}

BOOL	CEmuThread::FrameInput()
{
static	CHAR	szMes[256];

	DirectInput.Poll();
	if( DirectDraw.GetZapperMode() ) {
		LONG	x, y;
		DirectDraw.GetZapperPos( x, y );
		if( g_nes ) {
			g_nes->SetZapperPos( x, y );
		}
	}
	g_nes->pad->Sync();

	if( !NetPlay.IsConnect() ) {
		g_nes->Movie();
	} else {
		DWORD	paddata = g_nes->pad->GetSyncData();
		BYTE	player1[CNetPlay::SOCKET_BLOCK_SIZE], player2[CNetPlay::SOCKET_BLOCK_SIZE];

		player1[0] = player1[1] = player1[2] = player1[3] = 0;
		if( !NetEventQueue.empty() ) {
			NETEV&	ev = NetEventQueue.front();

			switch( ev.Event ) {
				case	EV_HWRESET:
					player1[0] = 0x80;
					break;
				case	EV_SWRESET:
					player1[0] = 0x81;
					break;
				case	EV_DISK_COMMAND:
					player1[0] = 0x88;
					player1[1] = (BYTE)ev.Param;
					break;
				case	EV_STATE_LOAD:
					player1[0] = 0xF0;
					break;
				case	EV_STATE_SAVE:
					player1[0] = 0xF1;
					break;
				default:
					break;
			}
		}

		player1[4] = (BYTE) paddata;
		player1[5] = (BYTE)(paddata>>8);
		player1[6] = (BYTE)(paddata>>16);
		player1[7] = (BYTE)(paddata>>24);

		INT	ret = NetPlay.ModifyPlayer( player1, player2 );

		if( ret < 0 ) {
			// Network Error
			NetPlay.Disconnect();
			return	FALSE;
		} else if( ret == 0 ) {
			// Queue偺暘偼憲怣偟偨偺偱FIFO偐傜庢傝彍偔
			if( !NetEventQueue.empty() ) {
				NetEventQueue.pop_front();
			}

			// Command check(摨帪偼P1桪愭)
			BYTE	event = 0;
			BYTE	param = 0;
			if( player1[0] ) {
				event = player1[0];
				param = player1[1];
			} else if( player2[0] ) {
				event = player2[0];
				param = player2[1];
			}
			switch( event ) {
				case	0x80:
					g_nes->Command( NES::NESCMD_HWRESET );
					DirectDraw.SetMessageString( "Hardware reset." );
					break;
				case	0x81:
					g_nes->Command( NES::NESCMD_SWRESET );
					DirectDraw.SetMessageString( "Software reset." );
					break;
				case	0x88:
					DiskCommand( param );
					break;
				case	0xF0:
					if( g_nes->LoadState( strNetStateName.c_str() ) ) {
						::wsprintf( szMes, "Net State Load." );
						DirectDraw.SetMessageString( szMes );
					}
					break;
				case	0xF1:
					if( g_nes->SaveState( strNetStateName.c_str() ) ) {
						::wsprintf( szMes, "Net State Save." );
						DirectDraw.SetMessageString( szMes );
					}
					break;
				default:
					break;
			}
		}

		LPBYTE	p1, p2;
		if( NetPlay.IsServer() ) {
			p1 = player1;
			p2 = player2;
		} else {
			p1 = player2;
			p2 = player1;
		}
//DEBUGOUT( "P1:%02X  P2:%02X  P3:%02X  P4:%02X\n", player1[4], player2[4], player1[5], player2[5] );
		g_nes->pad->SetSyncData( ((DWORD)p1[4])|((DWORD)p2[4]<<8)|((DWORD)p1[5]<<16)|((DWORD)p2[5]<<24) );
	}

	return	TRUE;
}

DWORD WINAPI CEmuThread::ThreadProc( LPVOID lpParam )
{
INT	i;
INT	Ev;
LONG	Param, Param2;
BOOL	bLoop = TRUE;
BOOL	bPause = FALSE;		// Thread pause
BOOL	bEmuPause = FALSE;	// Emulation pause
BOOL	bThrottle = FALSE;	// Emulation throttle
INT	nFrameSkip = 0;		// Emulation frameskip
BOOL	bOneStep = FALSE;	// Emulation one step
BOOL	bSleep = FALSE;		// Sleep use

INT	frameskipno;
double	frame_time = 0.0f;
double	frame_period;
DWORD	start_time, current_time, now_time;
LONG	sleep_time;

// FPS
INT	nFrameCount = 0;
DWORD	dwFrameTime[32];
DWORD	FPS = 0;

// Str
CHAR	szStr[256];

// Netplay
NETEV	netev;
INT	nNetTimeoutCount = 0;

	while( bLoop ) {
		try {
			bOneStep = FALSE;

			Ev = EV_NONE;
			if( WAIT_OBJECT_0 == ::WaitForSingleObject(g_hEvent,0) ) {
				Ev = g_Event;
				Param = g_EventParam;
				Param2 = g_EventParam2;
			}

			switch( Ev ) {
				case	EV_NONE:
					break;
				case	EV_INITIAL:
					DirectDraw.SetMessageString( "Emulation start." );
					DirectSound.StreamPlay();
					frame_time = 0.0f;
					start_time = ::timeGetTime();
					if( g_nes ) {
						g_nes->ppu->SetScreenPtr( DirectDraw.GetRenderScreen(), DirectDraw.GetLineColormode() );
					}
					::SetEvent( g_hEventAccept );
					break;
				case	EV_EXIT:
					DirectSound.StreamStop();
					g_WaveRec.Stop();
					bLoop = FALSE;
					return	0;
				case	EV_PAUSE:
					DirectSound.StreamPause();
					bPause = TRUE;
					::SetEvent( g_hEventAccept );
					break;
				case	EV_RESUME:
					if( !bEmuPause )
						DirectSound.StreamResume();
					frame_time = 0.0f;
					start_time = ::timeGetTime();
					bPause = FALSE;
					::SetEvent( g_hEventAccept );
					break;
				case	EV_FULLSCREEN_GDI:
					DirectDraw.SetFullScreenGDI( (BOOL)Param );
					::SetEvent( g_hEventAccept );
					break;

				case	EV_HWRESET:
					if( g_nes ) {
						if( !NetPlay.IsConnect() ) {
							if( !g_nes->IsMoviePlay() ) {
								g_nes->Command( NES::NESCMD_HWRESET );
								DirectDraw.SetMessageString( "Hardware reset." );
							}
						} else {
							netev.Event = EV_HWRESET;
							netev.Param = 0;
							NetEventQueue.push_back( netev );
						}
					}
					::SetEvent( g_hEventAccept );
					break;
				case	EV_SWRESET:
					if( g_nes ) {
						if( !NetPlay.IsConnect() ) {
							if( !g_nes->IsMoviePlay() ) {
								g_nes->Command( NES::NESCMD_SWRESET );
								DirectDraw.SetMessageString( "Software reset." );
							}
						} else {
							netev.Event = EV_SWRESET;
							netev.Param = 0;
							NetEventQueue.push_back( netev );
						}
					}
					::SetEvent( g_hEventAccept );
					break;

				case	EV_NETPLAY_START:
					if( g_nes ) {
						// 堦墳
						g_nes->MovieStop();

						string	pathstr;
						if( Config.path.bStatePath ) {
							pathstr = CPathlib::CreatePath( CApp::GetModulePath(), Config.path.szStatePath );
							::CreateDirectory( pathstr.c_str(), NULL );
						DEBUGOUT( "Path: %s\n", pathstr.c_str() );
						} else {
							pathstr = g_nes->rom->GetRomPath();
						}
						strNetStateName = CPathlib::MakePathExt( pathstr.c_str(), g_nes->rom->GetRomName(), "stn" );
						DEBUGOUT( "NetState Path: %s\n", strNetStateName.c_str() );

						g_nes->Command( NES::NESCMD_HWRESET );
						DirectDraw.SetMessageString( "Netplay start!" );
					}
					bThrottle = FALSE;

					// Queue傪僋儕傾
					NetEventQueue.clear();
					// 嵟弶偼Sync
					NetPlay.Sync();

					::SetEvent( g_hEventAccept );
					break;

				case	EV_EMUPAUSE:
					if( !NetPlay.IsConnect() ) {
						bEmuPause = !bEmuPause;
						if( bEmuPause ) {
							DirectSound.StreamPause();
						} else if( !bPause ) {
							DirectSound.StreamResume();
						}
						DirectDraw.SetMessageString( "Pause." );
					}
					::SetEvent( g_hEventAccept );
					break;

				case	EV_ONEFRAME:
					if( !NetPlay.IsConnect() ) {
						bEmuPause = TRUE;
						DirectSound.StreamPause();
						bOneStep = TRUE;
						DirectDraw.SetMessageString( "One Frame." );
					}
					::SetEvent( g_hEventAccept );
					break;

				case	EV_THROTTLE:
					if( !NetPlay.IsConnect() ) {
						bThrottle = !bThrottle;
						if( bThrottle ) {
							DirectDraw.SetMessageString( "Throttle ON." );
						} else {
							DirectDraw.SetMessageString( "Throttle OFF." );
						}
					}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -