📄 emuthread.cpp
字号:
//
// 僄儈儏儗乕僞僗儗僢僪僋儔僗
//
#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 + -