Settings work again!

* Switched the SysCoreThread to a static (fully persistent) thread.
 * Added some listeners for when the CoreThread status changes
 * fixed some slowness in savestates, and the emu will now stall until savestates complete, if you try to exit too quick (avoids savestate corruption)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1993 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-10-09 15:17:53 +00:00
parent 2a69c92067
commit e8e61a5cf8
37 changed files with 480 additions and 355 deletions

View File

@ -83,20 +83,24 @@ public:
virtual inline void Add( const ListenerType& listener ); virtual inline void Add( const ListenerType& listener );
virtual inline void RemoveObject( const void* object ); virtual inline void RemoveObject( const void* object );
Handle Add( void* objhandle, typename ListenerType::FuncType* fnptr ) void Add( void* objhandle, typename ListenerType::FuncType* fnptr )
{ {
return Add( Handle( objhandle, fnptr ) ); Add( ListenerType( objhandle, fnptr ) );
} }
void Remove( void* objhandle, typename ListenerType::FuncType* fnptr ) void Remove( void* objhandle, typename ListenerType::FuncType* fnptr )
{ {
Remove( Handle( objhandle, fnptr ) ); Remove( ListenerType( objhandle, fnptr ) );
} }
void Dispatch( const wxCommandEvent& evt ) const void Dispatch( const EvtType& evt ) const
{ {
Handle iter = m_listeners.begin(); // Have to make a complete copy of the list, because the event stack can change:
while( iter != m_listeners.end() )
ListenerList list( m_listeners );
typename ListenerList::const_iterator iter = list.begin();
while( iter != list.end() )
{ {
try try
{ {
@ -108,7 +112,10 @@ public:
} }
catch( Exception::BaseException& ex ) catch( Exception::BaseException& ex )
{ {
if( IsDevBuild ) throw;
Console.Error( L"Ignoring non-runtime BaseException thrown from event listener: " + ex.FormatDiagnosticMessage() );
} }
++iter;
} }
} }
}; };

View File

@ -175,8 +175,8 @@ namespace Threading
// To use this as a base class for your threaded procedure, overload the following virtual // To use this as a base class for your threaded procedure, overload the following virtual
// methods: // methods:
// void OnStart(); // void OnStart();
// void ExecuteTask(); // void ExecuteTaskInThread();
// void OnThreadCleanup(); // void OnCleanupInThread();
// //
// Use the public methods Start() and Cancel() to start and shutdown the thread, and use // Use the public methods Start() and Cancel() to start and shutdown the thread, and use
// m_sem_event internally to post/receive events for the thread (make a public accessor for // m_sem_event internally to post/receive events for the thread (make a public accessor for
@ -229,10 +229,10 @@ namespace Threading
// Start() once necessary locks have been obtained. Do not override Start() directly // Start() once necessary locks have been obtained. Do not override Start() directly
// unless you're really sure that's what you need to do. ;) // unless you're really sure that's what you need to do. ;)
virtual void OnStart()=0; virtual void OnStart()=0;
virtual void OnThreadCleanup()=0; virtual void OnCleanupInThread()=0;
// Implemented by derived class to handle threading actions! // Implemented by derived class to handle threading actions!
virtual void ExecuteTask()=0; virtual void ExecuteTaskInThread()=0;
// Inserts a thread cancellation point. If the thread has received a cancel request, this // Inserts a thread cancellation point. If the thread has received a cancel request, this
// function will throw an SEH exception designed to exit the thread (so make sure to use C++ // function will throw an SEH exception designed to exit the thread (so make sure to use C++
@ -372,7 +372,7 @@ namespace Threading
// all your necessary processing work here. // all your necessary processing work here.
virtual void Task()=0; virtual void Task()=0;
virtual void ExecuteTask(); virtual void ExecuteTaskInThread();
}; };
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////

View File

@ -122,7 +122,7 @@ namespace Threading
// Remarks: // Remarks:
// Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread // Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread
// object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel() // object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel()
// prior, since the ExecuteTask() method requires a valid object state. If you really need // prior, since the ExecuteTaskInThread() method requires a valid object state. If you really need
// fire-and-forget behavior on threads, use pthreads directly for now. // fire-and-forget behavior on threads, use pthreads directly for now.
// //
// This function should not be called from the owner thread. // This function should not be called from the owner thread.
@ -263,7 +263,7 @@ namespace Threading
} }
// invoked internally when canceling or exiting the thread. Extending classes should implement // invoked internally when canceling or exiting the thread. Extending classes should implement
// OnThreadCleanup() to extend cleanup functionality. // OnCleanupInThread() to extend cleanup functionality.
void PersistentThread::_ThreadCleanup() void PersistentThread::_ThreadCleanup()
{ {
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks. pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
@ -273,7 +273,7 @@ namespace Threading
// derived class is implemented. // derived class is implemented.
ScopedLock startlock( m_lock_start ); ScopedLock startlock( m_lock_start );
_try_virtual_invoke( &PersistentThread::OnThreadCleanup ); _try_virtual_invoke( &PersistentThread::OnCleanupInThread );
m_running = false; m_running = false;
m_sem_finished.Post(); m_sem_finished.Post();
@ -288,11 +288,11 @@ namespace Threading
{ {
m_running = true; m_running = true;
_DoSetThreadName( m_name ); _DoSetThreadName( m_name );
_try_virtual_invoke( &PersistentThread::ExecuteTask ); _try_virtual_invoke( &PersistentThread::ExecuteTaskInThread );
} }
void PersistentThread::OnStart() {} void PersistentThread::OnStart() {}
void PersistentThread::OnThreadCleanup() {} void PersistentThread::OnCleanupInThread() {}
// passed into pthread_create, and is used to dispatch the thread's object oriented // passed into pthread_create, and is used to dispatch the thread's object oriented
// callback function // callback function
@ -391,7 +391,7 @@ namespace Threading
m_post_TaskComplete.Reset(); m_post_TaskComplete.Reset();
} }
void BaseTaskThread::ExecuteTask() void BaseTaskThread::ExecuteTaskInThread()
{ {
while( !m_Done ) while( !m_Done )
{ {

View File

@ -128,7 +128,7 @@ FILE *_cdvdOpenNVM()
fd = fopen(file.data(), "wb"); fd = fopen(file.data(), "wb");
if (fd == NULL) if (fd == NULL)
{ {
Console.Error( "\tMEC File Creation failed!" ); Console.Error( "\tNVM File Creation failed!" );
throw Exception::CreateStream( file ); throw Exception::CreateStream( file );
} }
for (int i=0; i<1024; i++) fputc(0, fd); for (int i=0; i<1024; i++) fputc(0, fd);

View File

@ -442,7 +442,7 @@ __forceinline void rcntUpdate_vSync()
if (vsyncCounter.Mode == MODE_VSYNC) if (vsyncCounter.Mode == MODE_VSYNC)
{ {
eeRecIsReset = false; eeRecIsReset = false;
mtgsThread.PollStatus(); mtgsThread.RethrowException();
SysCoreThread::Get().StateCheck(); SysCoreThread::Get().StateCheck();
if( eeRecIsReset ) if( eeRecIsReset )
{ {

View File

@ -126,9 +126,7 @@ public:
mtgsThreadObject(); mtgsThreadObject();
virtual ~mtgsThreadObject() throw(); virtual ~mtgsThreadObject() throw();
void Start();
void OnStart(); void OnStart();
void PollStatus();
// Waits for the GS to empty out the entire ring buffer contents. // Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown. // Used primarily for plugin startup/shutdown.
@ -167,7 +165,7 @@ protected:
// Used internally by SendSimplePacket type functions // Used internally by SendSimplePacket type functions
uint _PrepForSimplePacket(); uint _PrepForSimplePacket();
void _FinishSimplePacket( uint future_writepos ); void _FinishSimplePacket( uint future_writepos );
void ExecuteTask(); void ExecuteTaskInThread();
}; };
extern __aligned16 mtgsThreadObject mtgsThread; extern __aligned16 mtgsThreadObject mtgsThread;

View File

@ -110,13 +110,6 @@ mtgsThreadObject::mtgsThreadObject() :
m_name = L"MTGS"; m_name = L"MTGS";
} }
void mtgsThreadObject::Start()
{
_parent::Start();
m_ExecMode = ExecMode_Suspending;
SetEvent();
}
void mtgsThreadObject::OnStart() void mtgsThreadObject::OnStart()
{ {
gsIsOpened = false; gsIsOpened = false;
@ -130,12 +123,10 @@ void mtgsThreadObject::OnStart()
m_packet_size = 0; m_packet_size = 0;
m_packet_ringpos = 0; m_packet_ringpos = 0;
_parent::OnStart(); m_CopyCommandTally = 0;
} m_CopyDataTally = 0;
void mtgsThreadObject::PollStatus() _parent::OnStart();
{
RethrowException();
} }
mtgsThreadObject::~mtgsThreadObject() throw() mtgsThreadObject::~mtgsThreadObject() throw()
@ -244,7 +235,7 @@ void mtgsThreadObject::OpenPlugin()
GSsetGameCRC( ElfCRC, 0 ); GSsetGameCRC( ElfCRC, 0 );
} }
void mtgsThreadObject::ExecuteTask() void mtgsThreadObject::ExecuteTaskInThread()
{ {
#ifdef RINGBUF_DEBUG_STACK #ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd; PacketTagType prevCmd;
@ -444,7 +435,7 @@ void mtgsThreadObject::WaitGS()
{ {
pxAssertDev( !IsSelf(), "This method is only allowed from threads *not* named MTGS." ); pxAssertDev( !IsSelf(), "This method is only allowed from threads *not* named MTGS." );
if( IsSuspended() ) return; if( !pxAssertDev( m_ExecMode == ExecMode_Running, "MTGS Warning! WaitGS issued on a suspended/paused thread." ) ) return;
// FIXME : Use semaphores instead of spinwaits. // FIXME : Use semaphores instead of spinwaits.
SetEvent(); SetEvent();

View File

@ -114,7 +114,7 @@ vtlbHandler tlb_fallback_8;
vtlbHandler vu0_micro_mem[2]; // 0 - dynarec, 1 - interpreter vtlbHandler vu0_micro_mem[2]; // 0 - dynarec, 1 - interpreter
vtlbHandler vu1_micro_mem[2]; // 0 - dynarec, 1 - interpreter vtlbHandler vu1_micro_mem[2]; // 0 - dynarec, 1 - interpreter
vtlbHandler hw_by_page[0x10]; vtlbHandler hw_by_page[0x10] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
vtlbHandler gs_page_0; vtlbHandler gs_page_0;
vtlbHandler gs_page_1; vtlbHandler gs_page_1;
@ -599,6 +599,19 @@ void memShutdown()
vtlb_Term(); vtlb_Term();
} }
void memBindConditionalHandlers()
{
if( hw_by_page[0xf] == -1 ) return;
vtlbMemR32FP* page0F32( EmuConfig.Speedhacks.IntcStat ? hwRead32_page_0F_INTC_HACK : hwRead32_page_0F );
vtlbMemR64FP* page0F64( EmuConfig.Speedhacks.IntcStat ? hwRead64_generic_INTC_HACK : hwRead64_generic );
vtlb_ReassignHandler( hw_by_page[0xf],
_ext_memRead8<1>, _ext_memRead16<1>, page0F32, page0F64, hwRead128_generic,
_ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0F, hwWrite64_generic, hwWrite128_generic
);
}
// Resets memory mappings, unmaps TLBs, reloads bios roms, etc. // Resets memory mappings, unmaps TLBs, reloads bios roms, etc.
void memReset() void memReset()
{ {
@ -724,13 +737,8 @@ void memReset()
_ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0E, hwWrite64_page_0E, hwWrite128_generic _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0E, hwWrite64_page_0E, hwWrite128_generic
); );
vtlbMemR32FP* page0F32( EmuConfig.Speedhacks.IntcStat ? hwRead32_page_0F_INTC_HACK : hwRead32_page_0F ); hw_by_page[0xf] = vtlb_NewHandler();
vtlbMemR64FP* page0F64( EmuConfig.Speedhacks.IntcStat ? hwRead64_generic_INTC_HACK : hwRead64_generic ); memBindConditionalHandlers();
hw_by_page[0xf] = vtlb_RegisterHandler(
_ext_memRead8<1>, _ext_memRead16<1>, page0F32, page0F64, hwRead128_generic,
_ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0F, hwWrite64_generic, hwWrite128_generic
);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// GS Optimized Mappings // GS Optimized Mappings

View File

@ -127,6 +127,7 @@ extern void memSetSupervisorMode();
extern void memSetUserMode(); extern void memSetUserMode();
extern void memSetPageAddr(u32 vaddr, u32 paddr); extern void memSetPageAddr(u32 vaddr, u32 paddr);
extern void memClearPageAddr(u32 vaddr); extern void memClearPageAddr(u32 vaddr);
extern void memBindConditionalHandlers();
extern void memMapVUmicro(); extern void memMapVUmicro();

View File

@ -915,7 +915,7 @@ void PluginManager::Open()
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
if (GSopen2) mtgsThread.WaitForOpen(); if (GSopen2) mtgsThread.WaitForOpen();
mtgsThread.PollStatus(); mtgsThread.RethrowException();
Console.Status( "Plugins opened successfully." ); Console.Status( "Plugins opened successfully." );
} }

View File

@ -58,7 +58,9 @@ R5900Exception::BaseExcept::~BaseExcept() throw (){}
void cpuReset() void cpuReset()
{ {
mtgsWaitGS(); // GS better be done processing before we reset the EE, just in case. if( mtgsThread.IsExecMode_Running() )
mtgsWaitGS(); // GS better be done processing before we reset the EE, just in case.
cpuIsInitialized = true; cpuIsInitialized = true;
memReset(); memReset();

View File

@ -20,6 +20,7 @@
#include "zlib/zlib.h" #include "zlib/zlib.h"
class _BaseStateThread;
static SafeArray<u8> state_buffer; static SafeArray<u8> state_buffer;
@ -33,6 +34,12 @@ bool sys_resume_lock = false;
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL; static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
static void StateThread_OnAppStatus( void* thr, const enum AppStatusEvent& stat )
{
if( (thr == NULL) || (stat != AppStatus_Exiting) ) return;
((PersistentThread*)thr)->Cancel();
}
enum enum
{ {
StateThreadAction_None = 0, StateThreadAction_None = 0,
@ -46,6 +53,9 @@ class _BaseStateThread : public PersistentThread
{ {
typedef PersistentThread _parent; typedef PersistentThread _parent;
protected:
EventListenerBinding<AppStatusEvent> m_bind_OnExit;
public: public:
virtual ~_BaseStateThread() throw() virtual ~_BaseStateThread() throw()
{ {
@ -53,7 +63,8 @@ public:
} }
protected: protected:
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished ) _BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished ) :
m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppStatusEvent>( this, StateThread_OnAppStatus ) )
{ {
Callback_FreezeFinished = onFinished; Callback_FreezeFinished = onFinished;
m_name = L"StateThread::" + fromUTF8(name); m_name = L"StateThread::" + fromUTF8(name);
@ -94,18 +105,18 @@ protected:
{ {
_parent::OnStart(); _parent::OnStart();
sys_resume_lock = true; sys_resume_lock = true;
sCoreThread.Pause(); CoreThread.Pause();
} }
void ExecuteTask() void ExecuteTaskInThread()
{ {
memSavingState( state_buffer ).FreezeAll(); memSavingState( state_buffer ).FreezeAll();
} }
void OnThreadCleanup() void OnCleanupInThread()
{ {
SendFinishEvent( StateThreadAction_Create ); SendFinishEvent( StateThreadAction_Create );
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
}; };
@ -131,18 +142,18 @@ protected:
} }
sys_resume_lock = true; sys_resume_lock = true;
sCoreThread.Pause(); CoreThread.Pause();
} }
void ExecuteTask() void ExecuteTaskInThread()
{ {
memLoadingState( state_buffer ).FreezeAll(); memLoadingState( state_buffer ).FreezeAll();
} }
void OnThreadCleanup() void OnCleanupInThread()
{ {
SendFinishEvent( StateThreadAction_Restore ); SendFinishEvent( StateThreadAction_Restore );
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
}; };
@ -179,17 +190,28 @@ protected:
throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." ); throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." );
} }
void ExecuteTask() void ExecuteTaskInThread()
{ {
Yield( 2 ); Yield( 3 );
if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() ) //if( gzwrite( m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
throw Exception::BadStream(); // throw Exception::BadStream();
static const int BlockSize = 0x10000;
int curidx = 0;
do
{
int thisBlockSize = std::min( BlockSize, state_buffer.GetSizeInBytes() - curidx );
if( gzwrite( m_gzfp, state_buffer.GetPtr(curidx), thisBlockSize ) < thisBlockSize )
throw Exception::BadStream( m_filename );
curidx += BlockSize;
Yield( 1 );
} while( curidx < state_buffer.GetSizeInBytes() );
} }
void OnThreadCleanup() void OnCleanupInThread()
{ {
SendFinishEvent( StateThreadAction_ZipToDisk ); SendFinishEvent( StateThreadAction_ZipToDisk );
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
}; };
@ -233,7 +255,7 @@ protected:
throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." ); throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." );
} }
void ExecuteTask() void ExecuteTaskInThread()
{ {
// fixme: should start initially with the file size, and then grow from there. // fixme: should start initially with the file size, and then grow from there.
@ -250,10 +272,10 @@ protected:
m_finished = true; m_finished = true;
} }
void OnThreadCleanup() void OnCleanupInThread()
{ {
SendFinishEvent( StateThreadAction_UnzipFromDisk ); SendFinishEvent( StateThreadAction_UnzipFromDisk );
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
}; };
@ -288,7 +310,7 @@ void OnFinished_Resume( const wxCommandEvent& evt )
SysClearExecutionCache(); SysClearExecutionCache();
} }
sCoreThread.Resume(); CoreThread.Resume();
} }
static wxString zip_dest_filename; static wxString zip_dest_filename;
@ -304,7 +326,9 @@ void OnFinished_ZipToDisk( const wxCommandEvent& evt )
} }
// Phase 2: Record to disk!! // Phase 2: Record to disk!!
(new StateThread_ZipToDisk( OnFinished_Resume, zip_dest_filename ))->Start(); (new StateThread_ZipToDisk( NULL, zip_dest_filename ))->Start();
CoreThread.Resume();
} }
void OnFinished_Restore( const wxCommandEvent& evt ) void OnFinished_Restore( const wxCommandEvent& evt )
@ -331,7 +355,7 @@ void StateCopy_SaveToFile( const wxString& file )
void StateCopy_LoadFromFile( const wxString& file ) void StateCopy_LoadFromFile( const wxString& file )
{ {
if( state_buffer_lock.IsLocked() ) return; if( state_buffer_lock.IsLocked() ) return;
sCoreThread.Pause(); CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start(); (new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
} }
@ -341,6 +365,8 @@ void StateCopy_LoadFromFile( const wxString& file )
// the one in the memory save. :) // the one in the memory save. :)
void StateCopy_SaveToSlot( uint num ) void StateCopy_SaveToSlot( uint num )
{ {
if( state_buffer_lock.IsLocked() ) return;
zip_dest_filename = SaveStateBase::GetFilename( num ); zip_dest_filename = SaveStateBase::GetFilename( num );
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start(); (new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
Console.Status( "Saving savestate to slot %d...", num ); Console.Status( "Saving savestate to slot %d...", num );
@ -361,7 +387,7 @@ void StateCopy_LoadFromSlot( uint slot )
Console.Status( "Loading savestate from slot %d...", slot ); Console.Status( "Loading savestate from slot %d...", slot );
Console.Status( wxsFormat(L"\tfilename: %s", file.c_str()) ); Console.Status( wxsFormat(L"\tfilename: %s", file.c_str()) );
sCoreThread.Pause(); CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start(); (new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
} }
@ -392,9 +418,21 @@ void StateCopy_ThawFromMem()
new StateThread_Thaw( OnFinished_Restore ); new StateThread_Thaw( OnFinished_Restore );
} }
void State_ThawFromMem_Blocking()
{
if( !state_buffer_lock.TryLock() )
memLoadingState( state_buffer ).FreezeAll();
state_buffer_lock.Release();
}
void StateCopy_Clear() void StateCopy_Clear()
{ {
if( state_buffer_lock.IsLocked() ) return; if( state_buffer_lock.IsLocked() ) return;
state_buffer.Dispose(); state_buffer.Dispose();
} }
bool StateCopy_IsBusy()
{
return state_buffer_lock.IsLocked();
}

View File

@ -359,14 +359,10 @@ memSavingState::memSavingState( SafeArray<u8>& save_to ) :
// Saving of state data // Saving of state data
void memSavingState::FreezeMem( void* data, int size ) void memSavingState::FreezeMem( void* data, int size )
{ {
const int end = m_idx+size; u8* const dest = m_memory.GetPtr(m_idx);
m_memory.MakeRoomFor( end ); m_idx += size;
m_memory.MakeRoomFor( m_idx );
u8* dest = (u8*)m_memory.GetPtr(); memcpy_fast( dest, data, size );
const u8* src = (u8*)data;
for( ; m_idx<end; ++m_idx, ++src )
dest[m_idx] = *src;
} }
memLoadingState::memLoadingState( const SafeArray<u8>& load_from ) : memLoadingState::memLoadingState( const SafeArray<u8>& load_from ) :
@ -379,10 +375,7 @@ memLoadingState::~memLoadingState() { }
// Loading of state data // Loading of state data
void memLoadingState::FreezeMem( void* data, int size ) void memLoadingState::FreezeMem( void* data, int size )
{ {
const int end = m_idx+size; const u8* const src = m_memory.GetPtr(m_idx);
const u8* src = (u8*)m_memory.GetPtr(); m_idx += size;
u8* dest = (u8*)data; memcpy_fast( data, src, size );
for( ; m_idx<end; ++m_idx, ++dest )
*dest = src[m_idx];
} }

View File

@ -204,4 +204,4 @@ extern void StateCopy_LoadFromFile( const wxString& file );
extern void StateCopy_SaveToSlot( uint num ); extern void StateCopy_SaveToSlot( uint num );
extern void StateCopy_LoadFromSlot( uint slot ); extern void StateCopy_LoadFromSlot( uint slot );
extern void StateCopy_Clear(); extern void StateCopy_Clear();
extern bool StateCopy_IsBusy();

View File

@ -45,6 +45,14 @@ SysSuspendableThread::~SysSuspendableThread() throw()
{ {
} }
void SysSuspendableThread::Start()
{
_parent::Start();
m_ExecMode = ExecMode_Suspending;
m_sem_event.Post();
}
void SysSuspendableThread::OnStart() void SysSuspendableThread::OnStart()
{ {
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return; if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
@ -65,51 +73,68 @@ void SysSuspendableThread::OnStart()
// is mostly useful for starting certain non-Emu related gui activities (improves gui // is mostly useful for starting certain non-Emu related gui activities (improves gui
// responsiveness). // responsiveness).
// //
// Returns:
// The previous suspension state; true if the thread was running or false if it was
// suspended.
//
// Exceptions: // Exceptions:
// CancelEvent - thrown if the thread is already in a Paused state. Because actions that // CancelEvent - thrown if the thread is already in a Paused state. Because actions that
// pause emulation typically rely on plugins remaining loaded/active, Suspension must // pause emulation typically rely on plugins remaining loaded/active, Suspension must
// cansel itself forcefully or risk crashing whatever other action is in progress. // cansel itself forcefully or risk crashing whatever other action is in progress.
// //
void SysSuspendableThread::Suspend( bool isBlocking ) bool SysSuspendableThread::Suspend( bool isBlocking )
{ {
if( IsSelf() || !IsRunning() ) return; if( IsSelf() || !IsRunning() ) return false;
bool retval = false;
{ {
ScopedLock locker( m_lock_ExecMode ); ScopedLock locker( m_lock_ExecMode );
if( m_ExecMode == ExecMode_Suspended ) if( m_ExecMode == ExecMode_Suspended )
return; return false;
if( m_ExecMode == ExecMode_Pausing || m_ExecMode == ExecMode_Paused ) if( m_ExecMode == ExecMode_Pausing || m_ExecMode == ExecMode_Paused )
throw Exception::CancelEvent( "Another thread is pausing the VM state." ); throw Exception::CancelEvent( "Another thread is pausing the VM state." );
if( m_ExecMode == ExecMode_Running ) if( m_ExecMode == ExecMode_Running )
{
m_ExecMode = ExecMode_Suspending; m_ExecMode = ExecMode_Suspending;
retval = true;
}
pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspending..." ); pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspending..." );
} }
m_SuspendEvent.Reset(); m_SuspendEvent.Reset();
m_sem_event.Post(); m_sem_event.Post();
if( isBlocking ) m_SuspendEvent.WaitGui(); if( isBlocking ) m_SuspendEvent.WaitGui();
return retval;
} }
void SysSuspendableThread::Pause() bool SysSuspendableThread::Pause()
{ {
if( IsSelf() || !IsRunning() ) return; if( IsSelf() || !IsRunning() ) return false;
bool retval = false;
{ {
ScopedLock locker( m_lock_ExecMode ); ScopedLock locker( m_lock_ExecMode );
if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused) ) return; if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused) ) return false;
if( m_ExecMode == ExecMode_Running ) if( m_ExecMode == ExecMode_Running )
{
m_ExecMode = ExecMode_Pausing; m_ExecMode = ExecMode_Pausing;
retval = true;
}
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." ); pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
} }
m_SuspendEvent.Reset(); m_SuspendEvent.Reset();
m_sem_event.Post(); m_sem_event.Post();
m_SuspendEvent.WaitGui(); m_SuspendEvent.WaitGui();
return retval;
} }
// Resumes the core execution state, or does nothing is the core is already running. If // Resumes the core execution state, or does nothing is the core is already running. If
@ -151,10 +176,10 @@ void SysSuspendableThread::Resume()
pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused), pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused),
"SysSuspendableThread is not in a suspended/paused state? wtf!" ); "SysSuspendableThread is not in a suspended/paused state? wtf!" );
m_ExecMode = ExecMode_Running;
m_ResumeProtection = true; m_ResumeProtection = true;
OnResumeReady(); OnResumeReady();
m_ResumeProtection = false; m_ResumeProtection = false;
m_ExecMode = ExecMode_Running;
m_ResumeEvent.Post(); m_ResumeEvent.Post();
} }
@ -164,11 +189,11 @@ void SysSuspendableThread::Resume()
// (Called from the context of this thread only) // (Called from the context of this thread only)
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
void SysSuspendableThread::OnThreadCleanup() void SysSuspendableThread::OnCleanupInThread()
{ {
ScopedLock locker( m_lock_ExecMode ); ScopedLock locker( m_lock_ExecMode );
m_ExecMode = ExecMode_NoThreadYet; m_ExecMode = ExecMode_NoThreadYet;
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
void SysSuspendableThread::StateCheck( bool isCancelable ) void SysSuspendableThread::StateCheck( bool isCancelable )
@ -244,11 +269,11 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
// (Called form outside the context of this thread) // (Called form outside the context of this thread)
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
SysCoreThread::SysCoreThread( PluginManager& plugins ) : SysCoreThread::SysCoreThread() :
m_resetRecompilers( false ) m_resetRecompilers( true )
, m_resetProfilers( false ) , m_resetProfilers( true )
, m_shortSuspend( false ) , m_resetVirtualMachine( true )
, m_plugins( plugins ) , m_hasValidState( false )
{ {
m_name = L"EE Core"; m_name = L"EE Core";
} }
@ -260,7 +285,8 @@ SysCoreThread::~SysCoreThread() throw()
void SysCoreThread::Start() void SysCoreThread::Start()
{ {
m_plugins.Init(); if( g_plugins == NULL ) return;
g_plugins->Init();
_parent::Start(); _parent::Start();
} }
@ -275,16 +301,28 @@ void SysCoreThread::Start()
// //
void SysCoreThread::OnResumeReady() void SysCoreThread::OnResumeReady()
{ {
if( m_shortSuspend ) return; if( m_resetVirtualMachine )
{
cpuReset();
m_resetVirtualMachine = false;
m_hasValidState = false;
}
if( m_resetRecompilers || m_resetProfilers ) if( m_resetRecompilers || m_resetProfilers || !m_hasValidState )
{ {
SysClearExecutionCache(); SysClearExecutionCache();
memBindConditionalHandlers();
m_resetRecompilers = false; m_resetRecompilers = false;
m_resetProfilers = false; m_resetProfilers = false;
} }
} }
void SysCoreThread::Reset()
{
Suspend();
m_resetVirtualMachine = true;
}
// Applies a full suite of new settings, which will automatically facilitate the necessary // Applies a full suite of new settings, which will automatically facilitate the necessary
// resets of the core and components (including plugins, if needed). The scope of resetting // resets of the core and components (including plugins, if needed). The scope of resetting
// is determined by comparing the current settings against the new settings. // is determined by comparing the current settings against the new settings.
@ -292,11 +330,12 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
{ {
if( src == EmuConfig ) return; if( src == EmuConfig ) return;
const bool resumeWhenDone = !m_ResumeProtection && !IsSuspended(); // Suspend only if ResumeProtection is false:
if( !m_ResumeProtection ) Suspend(); const bool resumeWhenDone = !m_ResumeProtection && Suspend();
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
const_cast<Pcsx2Config&>(EmuConfig) = src; const_cast<Pcsx2Config&>(EmuConfig) = src;
if( resumeWhenDone ) Resume(); if( resumeWhenDone ) Resume();
@ -314,55 +353,44 @@ SysCoreThread& SysCoreThread::Get()
void SysCoreThread::CpuInitializeMess() void SysCoreThread::CpuInitializeMess()
{ {
cpuReset(); if( m_hasValidState ) return;
SysClearExecutionCache();
if( StateCopy_IsValid() ) wxString elf_file;
if( EmuConfig.SkipBiosSplash )
{ {
// no need to boot bios or detect CDs when loading savestates. // Fetch the ELF filename and CD type from the CDVD provider.
// [TODO] : It might be useful to detect game SLUS/CRC and compare it against wxString ename;
// the savestate info, and issue a warning to the user since, chances are, they int result = GetPS2ElfName( ename );
// don't really want to run a game with the wrong ISO loaded into the emu. switch( result )
StateCopy_ThawFromMem();
}
else
{
wxString elf_file;
if( EmuConfig.SkipBiosSplash )
{ {
// Fetch the ELF filename and CD type from the CDVD provider. case 0:
wxString ename; throw Exception::RuntimeError( wxLt("Fast Boot failed: CDVD image is not a PS1 or PS2 game.") );
int result = GetPS2ElfName( ename );
switch( result )
{
case 0:
throw Exception::RuntimeError( wxLt("Fast Boot failed: CDVD image is not a PS1 or PS2 game.") );
case 1: case 1:
throw Exception::RuntimeError( wxLt("Fast Boot failed: PCSX2 does not support emulation of PS1 games.") ); throw Exception::RuntimeError( wxLt("Fast Boot failed: PCSX2 does not support emulation of PS1 games.") );
case 2: case 2:
// PS2 game. Valid! // PS2 game. Valid!
elf_file = ename; elf_file = ename;
break; break;
jNO_DEFAULT jNO_DEFAULT
}
}
if( !elf_file.IsEmpty() )
{
// Skip Bios Hack -- Runs the PS2 BIOS stub, and then manually loads the ELF
// executable data, and injects the cpuRegs.pc with the address of the
// execution start point.
//
// This hack is necessary for non-CD ELF files, and is optional for game CDs
// (though not recommended for games because of rare ill side effects).
cpuExecuteBios();
loadElfFile( elf_file );
} }
} }
if( !elf_file.IsEmpty() )
{
// Skip Bios Hack -- Runs the PS2 BIOS stub, and then manually loads the ELF
// executable data, and injects the cpuRegs.pc with the address of the
// execution start point.
//
// This hack is necessary for non-CD ELF files, and is optional for game CDs
// (though not recommended for games because of rare ill side effects).
cpuExecuteBios();
loadElfFile( elf_file );
}
m_hasValidState = true;
} }
// special macro which disables inlining on functions that require their own function stackframe. // special macro which disables inlining on functions that require their own function stackframe.
@ -384,33 +412,36 @@ void SysCoreThread::CpuExecute()
PCSX2_MEM_PROTECT_END(); PCSX2_MEM_PROTECT_END();
} }
void SysCoreThread::ExecuteTask() void SysCoreThread::ExecuteTaskInThread()
{ {
tls_coreThread = this; tls_coreThread = this;
StateCheck(); m_sem_event.Wait();
CpuInitializeMess();
StateCheck(); StateCheck();
CpuExecute(); CpuExecute();
} }
void SysCoreThread::OnSuspendInThread() void SysCoreThread::OnSuspendInThread()
{ {
if( !m_shortSuspend ) if( g_plugins == NULL ) return;
m_plugins.Close(); g_plugins->Close();
} }
void SysCoreThread::OnResumeInThread( bool isSuspended ) void SysCoreThread::OnResumeInThread( bool isSuspended )
{ {
if( isSuspended ) if( isSuspended && g_plugins != NULL )
m_plugins.Open(); {
CpuInitializeMess();
g_plugins->Open();
}
} }
// Invoked by the pthread_exit or pthread_cancel // Invoked by the pthread_exit or pthread_cancel
void SysCoreThread::OnThreadCleanup() void SysCoreThread::OnCleanupInThread()
{ {
m_plugins.Shutdown(); //if( g_plugins != NULL )
_parent::OnThreadCleanup(); // g_plugins->Shutdown();
_parent::OnCleanupInThread();
} }

View File

@ -25,8 +25,8 @@ public:
ISysThread() {} ISysThread() {}
virtual ~ISysThread() throw() {}; virtual ~ISysThread() throw() {};
virtual bool IsSuspended() const { return false; } virtual bool Suspend( bool isBlocking = true ) { return false; }
virtual void Suspend( bool isBlocking = true ) { } virtual bool Pause() { return false; }
virtual void Resume() {} virtual void Resume() {}
}; };
@ -57,14 +57,14 @@ public:
explicit SysSuspendableThread(); explicit SysSuspendableThread();
virtual ~SysSuspendableThread() throw(); virtual ~SysSuspendableThread() throw();
bool IsSuspended() const { return (m_ExecMode == ExecMode_Suspended); } bool IsExecMode_Running() const { return m_ExecMode == ExecMode_Running; }
virtual void Suspend( bool isBlocking = true ); virtual bool Suspend( bool isBlocking = true );
virtual void Resume(); virtual void Resume();
virtual void Pause(); virtual bool Pause();
virtual void StateCheck( bool isCancelable = true ); virtual void StateCheck( bool isCancelable = true );
virtual void OnThreadCleanup(); virtual void OnCleanupInThread();
// This function is called by Resume immediately prior to releasing the suspension of // This function is called by Resume immediately prior to releasing the suspension of
// the core emulation thread. You should overload this rather than Resume(), since // the core emulation thread. You should overload this rather than Resume(), since
@ -75,6 +75,9 @@ public:
protected: protected:
// Used internally from Resume(), so let's make it private here.
virtual void Start();
// Extending classes should implement this, but should not call it. The parent class // Extending classes should implement this, but should not call it. The parent class
// handles invocation by the following guidelines: Called *in thread* from StateCheck() // handles invocation by the following guidelines: Called *in thread* from StateCheck()
// prior to suspending the thread (ie, when Suspend() has been called on a separate // prior to suspending the thread (ie, when Suspend() has been called on a separate
@ -108,19 +111,24 @@ class SysCoreThread : public SysSuspendableThread
protected: protected:
bool m_resetRecompilers; bool m_resetRecompilers;
bool m_resetProfilers; bool m_resetProfilers;
bool m_shortSuspend; bool m_resetVirtualMachine;
PluginManager& m_plugins; bool m_hasValidState;
public: public:
static SysCoreThread& Get(); static SysCoreThread& Get();
public: public:
explicit SysCoreThread( PluginManager& plugins ); explicit SysCoreThread();
virtual ~SysCoreThread() throw(); virtual ~SysCoreThread() throw();
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnThreadCleanup();
virtual void OnResumeReady(); virtual void OnResumeReady();
virtual void Reset();
bool HasValidState()
{
return m_hasValidState;
}
protected: protected:
void CpuInitializeMess(); void CpuInitializeMess();
@ -130,5 +138,6 @@ protected:
virtual void OnSuspendInThread(); virtual void OnSuspendInThread();
virtual void OnPauseInThread() {} virtual void OnPauseInThread() {}
virtual void OnResumeInThread( bool IsSuspended ); virtual void OnResumeInThread( bool IsSuspended );
virtual void ExecuteTask(); virtual void OnCleanupInThread();
virtual void ExecuteTaskInThread();
}; };

View File

@ -49,7 +49,7 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 ) DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
DECLARE_EVENT_TYPE( pxEVT_SysExecute, -1 ) DECLARE_EVENT_TYPE( pxEVT_SysExecute, -1 )
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 ) DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
DECLARE_EVENT_TYPE( pxEVT_AppCoreThreadFinished, -1 ) DECLARE_EVENT_TYPE( pxEVT_CoreThreadStatus, -1 )
DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 ) DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 )
END_DECLARE_EVENT_TYPES() END_DECLARE_EVENT_TYPES()
@ -159,6 +159,12 @@ enum DialogIdentifiers
DialogId_About, DialogId_About,
}; };
enum AppStatusEvent
{
// Maybe this will be expanded upon later..?
AppStatus_Exiting
};
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// KeyAcceleratorCode // KeyAcceleratorCode
// A custom keyboard accelerator that I like better than wx's wxAcceleratorEntry. // A custom keyboard accelerator that I like better than wx's wxAcceleratorEntry.
@ -324,7 +330,6 @@ protected:
public: public:
ScopedPtr<SysCoreAllocations> m_CoreAllocs; ScopedPtr<SysCoreAllocations> m_CoreAllocs;
ScopedPtr<PluginManager> m_CorePlugins; ScopedPtr<PluginManager> m_CorePlugins;
ScopedPtr<SysCoreThread> m_CoreThread;
protected: protected:
// Note: Pointers to frames should not be scoped because wxWidgets handles deletion // Note: Pointers to frames should not be scoped because wxWidgets handles deletion
@ -345,7 +350,7 @@ public:
int ThreadedModalDialog( DialogIdentifiers dialogId ); int ThreadedModalDialog( DialogIdentifiers dialogId );
void Ping() const; void Ping() const;
bool PrepForExit(); bool PrepForExit( bool canCancel );
void SysExecute(); void SysExecute();
void SysExecute( CDVD_SourceType cdvdsrc ); void SysExecute( CDVD_SourceType cdvdsrc );
@ -358,13 +363,8 @@ public:
const AppImageIds& GetImgId() const { return m_ImageId; } const AppImageIds& GetImgId() const { return m_ImageId; }
MainEmuFrame& GetMainFrame() const; MainEmuFrame& GetMainFrame() const;
SysCoreThread& GetCoreThread() const; MainEmuFrame* GetMainFramePtr() const { return m_MainFrame; }
bool HasMainFrame() const { return m_MainFrame != NULL; } bool HasMainFrame() const { return m_MainFrame != NULL; }
bool HasCoreThread() const { return m_CoreThread != NULL; }
MainEmuFrame* GetMainFramePtr() const { return m_MainFrame; }
SysCoreThread* GetCoreThreadPtr() const { return m_CoreThread; }
void OpenGsFrame(); void OpenGsFrame();
void OnGsFrameClosed(); void OnGsFrameClosed();
@ -402,10 +402,12 @@ public:
protected: protected:
CmdEvt_Source m_evtsrc_CorePluginStatus; CmdEvt_Source m_evtsrc_CorePluginStatus;
CmdEvt_Source m_evtsrc_CoreThreadStatus; CmdEvt_Source m_evtsrc_CoreThreadStatus;
EventSource<AppStatusEvent> m_evtsrc_AppStatus;
public: public:
CmdEvt_Source& Source_CoreThreadStatus() { return m_evtsrc_CoreThreadStatus; } CmdEvt_Source& Source_CoreThreadStatus() { return m_evtsrc_CoreThreadStatus; }
CmdEvt_Source& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; } CmdEvt_Source& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; }
EventSource<AppStatusEvent>& Source_AppStatus() { return m_evtsrc_AppStatus; }
protected: protected:
void InitDefaultGlobalAccelerators(); void InitDefaultGlobalAccelerators();
@ -422,7 +424,7 @@ protected:
void OnLoadPluginsComplete( wxCommandEvent& evt ); void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnSemaphorePing( wxCommandEvent& evt ); void OnSemaphorePing( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt ); void OnOpenModalDialog( wxCommandEvent& evt );
void OnCoreThreadTerminated( wxCommandEvent& evt ); void OnCoreThreadStatus( wxCommandEvent& evt );
void OnFreezeThreadFinished( wxCommandEvent& evt ); void OnFreezeThreadFinished( wxCommandEvent& evt );
@ -449,6 +451,13 @@ protected:
// AppCoreThread class // AppCoreThread class
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
enum CoreThreadStatus
{
CoreStatus_Resumed,
CoreStatus_Suspended,
CoreStatus_Stopped,
};
class AppCoreThread : public SysCoreThread class AppCoreThread : public SysCoreThread
{ {
typedef SysCoreThread _parent; typedef SysCoreThread _parent;
@ -457,18 +466,18 @@ protected:
wxKeyEvent m_kevt; wxKeyEvent m_kevt;
public: public:
AppCoreThread( PluginManager& plugins ); AppCoreThread();
virtual ~AppCoreThread() throw(); virtual ~AppCoreThread() throw();
virtual void Suspend( bool isBlocking=true ); virtual bool Suspend( bool isBlocking=true );
virtual void Resume(); virtual void Resume();
virtual void StateCheck( bool isCancelable=true ); virtual void StateCheck( bool isCancelable=true );
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
protected: protected:
virtual void OnResumeReady(); virtual void OnResumeReady();
virtual void OnThreadCleanup(); virtual void OnCleanupInThread();
virtual void ExecuteTask(); virtual void ExecuteTaskInThread();
}; };
DECLARE_APP(Pcsx2App) DECLARE_APP(Pcsx2App)
@ -481,8 +490,8 @@ DECLARE_APP(Pcsx2App)
// not be invoked, and an optional "else" clause cn be affixed for handling the end case. // not be invoked, and an optional "else" clause cn be affixed for handling the end case.
// //
// Usage Examples: // Usage Examples:
// sCoreThread.Suspend(); // Suspends the CoreThread, or does nothing if the CoreThread handle is NULL // sMainFrame.ApplySettings();
// sCoreThread.Suspend(); else Console.WriteLn( "Judge Wapner" ); // 'else' clause for handling NULL scenarios. // sMainFrame.ApplySettings(); else Console.WriteLn( "Judge Wapner" ); // 'else' clause for handling NULL scenarios.
// //
// Note! These macros are not "syntax complete", which means they could generat unexpected // Note! These macros are not "syntax complete", which means they could generat unexpected
// syntax errors in some situatins, and more importantly they cannot be used for invoking // syntax errors in some situatins, and more importantly they cannot be used for invoking
@ -497,9 +506,6 @@ DECLARE_APP(Pcsx2App)
#define sApp \ #define sApp \
if( Pcsx2App* __app_ = (Pcsx2App*)wxApp::GetInstance() ) (*__app_) if( Pcsx2App* __app_ = (Pcsx2App*)wxApp::GetInstance() ) (*__app_)
#define sCoreThread \
if( SysCoreThread* __thread_ = GetCoreThreadPtr() ) (*__thread_)
#define sMainFrame \ #define sMainFrame \
if( MainEmuFrame* __frame_ = GetMainFramePtr() ) (*__frame_) if( MainEmuFrame* __frame_ = GetMainFramePtr() ) (*__frame_)
@ -523,11 +529,8 @@ extern bool SysHasValidState();
extern void SysStatus( const wxString& text ); extern void SysStatus( const wxString& text );
extern bool HasMainFrame(); extern bool HasMainFrame();
extern bool HasCoreThread();
extern MainEmuFrame& GetMainFrame(); extern MainEmuFrame& GetMainFrame();
extern SysCoreThread& GetCoreThread();
extern MainEmuFrame* GetMainFramePtr(); extern MainEmuFrame* GetMainFramePtr();
extern SysCoreThread* GetCoreThreadPtr();
extern AppCoreThread CoreThread;

View File

@ -525,8 +525,7 @@ void AppConfig_OnChangedSettingsFolder( bool overwrite )
AppLoadSettings(); AppLoadSettings();
AppApplySettings(); AppApplySettings();
if( HasMainFrame() ) sMainFrame.ReloadRecentLists();
GetMainFrame().ReloadRecentLists();
g_Conf->Folders.Logs.Mkdir(); g_Conf->Folders.Logs.Mkdir();

View File

@ -16,8 +16,9 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "MainFrame.h" #include "MainFrame.h"
AppCoreThread::AppCoreThread( PluginManager& plugins ) : AppCoreThread CoreThread;
SysCoreThread( plugins )
AppCoreThread::AppCoreThread() : SysCoreThread()
, m_kevt() , m_kevt()
{ {
} }
@ -26,11 +27,13 @@ AppCoreThread::~AppCoreThread() throw()
{ {
} }
void AppCoreThread::Suspend( bool isBlocking ) bool AppCoreThread::Suspend( bool isBlocking )
{ {
_parent::Suspend( isBlocking ); bool retval = _parent::Suspend( isBlocking );
if( HasMainFrame() )
GetMainFrame().ApplySettings(); wxCommandEvent evt( pxEVT_CoreThreadStatus );
evt.SetInt( CoreStatus_Suspended );
wxGetApp().AddPendingEvent( evt );
// Clear the sticky key statuses, because hell knows what'll change while the PAD // Clear the sticky key statuses, because hell knows what'll change while the PAD
// plugin is suspended. // plugin is suspended.
@ -38,6 +41,8 @@ void AppCoreThread::Suspend( bool isBlocking )
m_kevt.m_shiftDown = false; m_kevt.m_shiftDown = false;
m_kevt.m_controlDown = false; m_kevt.m_controlDown = false;
m_kevt.m_altDown = false; m_kevt.m_altDown = false;
return retval;
} }
void AppCoreThread::Resume() void AppCoreThread::Resume()
@ -55,26 +60,28 @@ void AppCoreThread::Resume()
void AppCoreThread::OnResumeReady() void AppCoreThread::OnResumeReady()
{ {
if( m_shortSuspend ) return;
ApplySettings( g_Conf->EmuOptions ); ApplySettings( g_Conf->EmuOptions );
if( GSopen2 != NULL ) if( GSopen2 != NULL )
wxGetApp().OpenGsFrame(); wxGetApp().OpenGsFrame();
if( HasMainFrame() ) wxCommandEvent evt( pxEVT_CoreThreadStatus );
GetMainFrame().ApplySettings(); evt.SetInt( CoreStatus_Resumed );
wxGetApp().AddPendingEvent( evt );
_parent::OnResumeReady();
} }
// Called whenever the thread has terminated, for either regular or irregular reasons. // Called whenever the thread has terminated, for either regular or irregular reasons.
// Typically the thread handles all its own errors, so there's no need to have error // Typically the thread handles all its own errors, so there's no need to have error
// handling here. However it's a good idea to update the status of the GUI to reflect // handling here. However it's a good idea to update the status of the GUI to reflect
// the new (lack of) thread status, so this posts a message to the App to do so. // the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::OnThreadCleanup() void AppCoreThread::OnCleanupInThread()
{ {
wxCommandEvent evt( pxEVT_AppCoreThreadFinished ); wxCommandEvent evt( pxEVT_CoreThreadStatus );
evt.SetInt( CoreStatus_Stopped );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
#ifdef __WXGTK__ #ifdef __WXGTK__
@ -84,11 +91,12 @@ void AppCoreThread::OnThreadCleanup()
void AppCoreThread::StateCheck( bool isCancelable ) void AppCoreThread::StateCheck( bool isCancelable )
{ {
_parent::StateCheck( isCancelable ); _parent::StateCheck( isCancelable );
if( !pxAssert(g_plugins!=NULL) ) return;
const keyEvent* ev = PADkeyEvent(); const keyEvent* ev = PADkeyEvent();
if( ev == NULL || (ev->key == 0) ) return; if( ev == NULL || (ev->key == 0) ) return;
m_plugins.KeyEvent( *ev ); g_plugins->KeyEvent( *ev );
m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ); m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP );
const bool isDown = (ev->evt == KEYPRESS); const bool isDown = (ev->evt == KEYPRESS);
@ -116,7 +124,8 @@ void AppCoreThread::StateCheck( bool isCancelable )
// suspended. If the thread has mot been suspended, this call will fail *silently*. // suspended. If the thread has mot been suspended, this call will fail *silently*.
void AppCoreThread::ApplySettings( const Pcsx2Config& src ) void AppCoreThread::ApplySettings( const Pcsx2Config& src )
{ {
if( !IsSuspended() ) return; if( m_ExecMode != ExecMode_Suspended ) return;
if( src == EmuConfig ) return;
// Re-entry guard protects against cases where code wants to manually set core settings // Re-entry guard protects against cases where code wants to manually set core settings
// which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is // which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is
@ -128,19 +137,18 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
SysCoreThread::ApplySettings( src ); SysCoreThread::ApplySettings( src );
} }
void AppCoreThread::ExecuteTask() void AppCoreThread::ExecuteTaskInThread()
{ {
try try
{ {
SysCoreThread::ExecuteTask(); SysCoreThread::ExecuteTaskInThread();
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::FileNotFound& ex ) catch( Exception::FileNotFound& ex )
{ {
m_plugins.Close(); if( g_plugins != NULL ) g_plugins->Close();
if( ex.StreamName == g_Conf->FullpathToBios() ) if( ex.StreamName == g_Conf->FullpathToBios() )
{ {
m_plugins.Close();
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() + bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the BIOS Configuration Panel.") ); _("\n\nPress Ok to go to the BIOS Configuration Panel.") );
@ -160,7 +168,7 @@ void AppCoreThread::ExecuteTask()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::PluginError& ex ) catch( Exception::PluginError& ex )
{ {
m_plugins.Close(); if( g_plugins != NULL ) g_plugins->Close();
Console.Error( ex.FormatDiagnosticMessage() ); Console.Error( ex.FormatDiagnosticMessage() );
Msgbox::Alert( ex.FormatDisplayMessage(), _("Plugin Open Error") ); Msgbox::Alert( ex.FormatDisplayMessage(), _("Plugin Open Error") );
@ -176,7 +184,7 @@ void AppCoreThread::ExecuteTask()
catch( Exception::BaseException& ex ) catch( Exception::BaseException& ex )
{ {
// Sent the exception back to the main gui thread? // Sent the exception back to the main gui thread?
m_plugins.Close(); if( g_plugins != NULL ) g_plugins->Close();
Msgbox::Alert( ex.FormatDisplayMessage() ); Msgbox::Alert( ex.FormatDisplayMessage() );
} }
} }

View File

@ -224,7 +224,7 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) ); Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
Connect( pxEVT_AppCoreThreadFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) ); Connect( pxEVT_CoreThreadStatus, wxCommandEventHandler( Pcsx2App::OnCoreThreadStatus ) );
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) ); Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) ); Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
@ -335,11 +335,10 @@ bool Pcsx2App::OnInit()
void Pcsx2App::CleanupMess() void Pcsx2App::CleanupMess()
{ {
CoreThread.Cancel();
if( m_CorePlugins ) if( m_CorePlugins )
{
m_CorePlugins->Close();
m_CorePlugins->Shutdown(); m_CorePlugins->Shutdown();
}
// Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash, // Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash,
// likely due to some pending message in the queue that references lilypad procs. // likely due to some pending message in the queue that references lilypad procs.

View File

@ -32,7 +32,7 @@ DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins ); DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_SysExecute ); DEFINE_EVENT_TYPE( pxEVT_SysExecute );
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete ); DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
DEFINE_EVENT_TYPE( pxEVT_AppCoreThreadFinished ); DEFINE_EVENT_TYPE( pxEVT_CoreThreadStatus );
DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished ); DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished );
bool UseAdminMode = false; bool UseAdminMode = false;
@ -118,13 +118,13 @@ void Pcsx2App::Ping() const
// Pcsx2App Event Handlers // Pcsx2App Event Handlers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Invoked by the AppCoreThread when the thread has terminated itself. // Invoked by the AppCoreThread when it's internal status has changed.
void Pcsx2App::OnCoreThreadTerminated( wxCommandEvent& evt ) // evt.GetInt() reflects the status at the time the message was sent, which may differ
// from the actual status. Typically listeners bound to this will want to use direct
// polling of the CoreThread rather than the belated status.
void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
{ {
m_evtsrc_CoreThreadStatus.Dispatch( evt );
if( HasMainFrame() )
GetMainFrame().ApplySettings();
m_CoreThread = NULL;
} }
void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt ) void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt )
@ -220,7 +220,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
{ {
// Saved state load failed. // Saved state load failed.
Console.Notice( ex.FormatDiagnosticMessage() ); Console.Notice( ex.FormatDiagnosticMessage() );
sCoreThread.Resume(); CoreThread.Resume();
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::PluginError& ex ) catch( Exception::PluginError& ex )
@ -243,6 +243,14 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
} }
} }
static void OnStateSaveFinished( void* obj, const wxCommandEvent& evt )
{
if( evt.GetInt() == CoreStatus_Resumed )
{
wxGetApp().PostMenuAction( MenuId_Exit );
wxGetApp().Source_CoreThreadStatus().Remove( NULL, OnStateSaveFinished );
}
}
// Common exit handler which can be called from any event (though really it should // Common exit handler which can be called from any event (though really it should
// be called only from CloseWindow handlers since that's the more appropriate way // be called only from CloseWindow handlers since that's the more appropriate way
@ -250,17 +258,37 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
// //
// returns true if the app can close, or false if the close event was canceled by // returns true if the app can close, or false if the close event was canceled by
// the glorious user, whomever (s)he-it might be. // the glorious user, whomever (s)he-it might be.
bool Pcsx2App::PrepForExit() bool Pcsx2App::PrepForExit( bool canCancel )
{ {
m_CoreThread = NULL; // If a savestate is saving, we should wait until it finishes. Otherwise the user
CleanupMess(); // might lose data.
if( StateCopy_IsBusy() )
{
Source_CoreThreadStatus().Add( NULL, OnStateSaveFinished );
throw Exception::CancelEvent( "Savestate in progress, cannot close program (close event delayed)" );
}
if( canCancel )
{
bool resume = CoreThread.Suspend();
if( /* TODO: Confirm with the user? */ false )
{
if(resume) CoreThread.Resume();
return false;
}
}
else
{
m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting );
CleanupMess();
}
return true; return true;
} }
int Pcsx2App::OnExit() int Pcsx2App::OnExit()
{ {
PrepForExit(); CleanupMess();
if( g_Conf ) if( g_Conf )
AppSaveSettings(); AppSaveSettings();
@ -283,16 +311,6 @@ MainEmuFrame& Pcsx2App::GetMainFrame() const
return *m_MainFrame; return *m_MainFrame;
} }
// This method generates debug assertions if the CoreThread handle is NULL (typically
// indicating that there is no active emulation session). In most cases you'll want
// to use HasCoreThread() to test for thread validity first, or use GetCoreThreadPtr()
// and manually check for NULL (choice is a matter of programmer preference).
SysCoreThread& Pcsx2App::GetCoreThread() const
{
pxAssert( m_CoreThread != NULL );
return *m_CoreThread;
}
void AppApplySettings( const AppConfig* oldconf ) void AppApplySettings( const AppConfig* oldconf )
{ {
AllowFromMainThreadOnly(); AllowFromMainThreadOnly();
@ -306,6 +324,8 @@ void AppApplySettings( const AppConfig* oldconf )
g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios(); g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios();
bool resume = CoreThread.Suspend();
// Update the compression attribute on the Memcards folder. // Update the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression. // Memcards generally compress very well via NTFS compression.
@ -323,10 +343,10 @@ void AppApplySettings( const AppConfig* oldconf )
} }
} }
if( HasMainFrame() ) CoreThread.ApplySettings( g_Conf->EmuOptions );
GetMainFrame().ApplySettings();
if( HasCoreThread() ) if( resume )
GetCoreThread().ApplySettings( g_Conf->EmuOptions ); CoreThread.Resume();
} }
void AppLoadSettings() void AppLoadSettings()
@ -368,8 +388,7 @@ void Pcsx2App::OpenGsFrame()
void Pcsx2App::OnGsFrameClosed() void Pcsx2App::OnGsFrameClosed()
{ {
if( m_CoreThread != NULL ) CoreThread.Suspend();
m_CoreThread->Suspend();
m_gsFrame = NULL; m_gsFrame = NULL;
} }
@ -446,17 +465,16 @@ void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
// it, because apparently too much stuff is going on and the emulation states are wonky. // it, because apparently too much stuff is going on and the emulation states are wonky.
if( !m_CorePlugins ) return; if( !m_CorePlugins ) return;
if( evt.GetInt() != -1 ) SysReset(); if( evt.GetInt() != -1 ) SysReset(); else CoreThread.Suspend();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() ); if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
m_CoreThread = new AppCoreThread( *m_CorePlugins ); CoreThread.Resume();
m_CoreThread->Resume();
} }
void Pcsx2App::SysReset() void Pcsx2App::SysReset()
{ {
m_CoreThread = NULL; CoreThread.Reset();
} }
// Returns true if there is a "valid" virtual machine state from the user's perspective. This // Returns true if there is a "valid" virtual machine state from the user's perspective. This
@ -466,8 +484,7 @@ void Pcsx2App::SysReset()
// state (such as saving it), you *must* suspend the Corethread first! // state (such as saving it), you *must* suspend the Corethread first!
__forceinline bool SysHasValidState() __forceinline bool SysHasValidState()
{ {
bool isRunning = HasCoreThread() ? GetCoreThread().IsRunning() : false; return CoreThread.HasValidState() || StateCopy_HasFullState();
return isRunning || StateCopy_HasFullState();
} }
// Writes text to console and updates the window status bar and/or HUD or whateverness. // Writes text to console and updates the window status bar and/or HUD or whateverness.
@ -476,8 +493,7 @@ void SysStatus( const wxString& text )
{ {
// mirror output to the console! // mirror output to the console!
Console.Status( text.c_str() ); Console.Status( text.c_str() );
if( HasMainFrame() ) sMainFrame.SetStatusText( text );
GetMainFrame().SetStatusText( text );
} }
bool HasMainFrame() bool HasMainFrame()
@ -485,11 +501,6 @@ bool HasMainFrame()
return wxTheApp && wxGetApp().HasMainFrame(); return wxTheApp && wxGetApp().HasMainFrame();
} }
bool HasCoreThread()
{
return wxTheApp && wxGetApp().HasCoreThread();
}
// This method generates debug assertions if either the wxApp or MainFrame handles are // This method generates debug assertions if either the wxApp or MainFrame handles are
// NULL (typically indicating that PCSX2 is running in NoGUI mode, or that the main // NULL (typically indicating that PCSX2 is running in NoGUI mode, or that the main
// frame has been closed). In most cases you'll want to use HasMainFrame() to test // frame has been closed). In most cases you'll want to use HasMainFrame() to test
@ -500,11 +511,6 @@ MainEmuFrame& GetMainFrame()
return wxGetApp().GetMainFrame(); return wxGetApp().GetMainFrame();
} }
SysCoreThread& GetCoreThread()
{
return wxGetApp().GetCoreThread();
}
// Returns a pointer to the main frame of the GUI (frame may be hidden from view), or // Returns a pointer to the main frame of the GUI (frame may be hidden from view), or
// NULL if no main frame exists (NoGUI mode and/or the frame has been destroyed). If // NULL if no main frame exists (NoGUI mode and/or the frame has been destroyed). If
// the wxApp is NULL then this will also return NULL. // the wxApp is NULL then this will also return NULL.
@ -512,10 +518,3 @@ MainEmuFrame* GetMainFramePtr()
{ {
return wxTheApp ? wxGetApp().GetMainFramePtr() : NULL; return wxTheApp ? wxGetApp().GetMainFramePtr() : NULL;
} }
// Returns a pointer to the CoreThread of the GUI (thread may be stopped or suspended),
// or NULL if no core thread exists, or if the wxApp is also NULL.
SysCoreThread* GetCoreThreadPtr()
{
return wxTheApp ? wxGetApp().GetCoreThreadPtr() : NULL;
}

View File

@ -99,7 +99,7 @@ void pxLogConsole::DoLog( wxLogLevel level, const wxChar *szString, time_t t )
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void ConsoleTestThread::ExecuteTask() void ConsoleTestThread::ExecuteTaskInThread()
{ {
static int numtrack = 0; static int numtrack = 0;

View File

@ -70,7 +70,7 @@ class ConsoleTestThread : public Threading::PersistentThread
{ {
protected: protected:
volatile bool m_done; volatile bool m_done;
void ExecuteTask(); void ExecuteTaskInThread();
public: public:
ConsoleTestThread() : ConsoleTestThread() :
@ -85,7 +85,7 @@ public:
protected: protected:
void OnStart() {} void OnStart() {}
void OnThreadCleanup() {} void OnCleanupInThread() {}
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -54,8 +54,7 @@ GSFrame::GSFrame(wxWindow* parent, const wxString& title):
GSFrame::~GSFrame() throw() GSFrame::~GSFrame() throw()
{ {
if( HasCoreThread() ) CoreThread.Suspend(); // Just in case...!
GetCoreThread().Suspend(); // Just in case...!
} }
void GSFrame::OnCloseWindow(wxCloseEvent& evt) void GSFrame::OnCloseWindow(wxCloseEvent& evt)

View File

@ -71,14 +71,12 @@ namespace Implementations
void Sys_Suspend() void Sys_Suspend()
{ {
if( HasCoreThread() ) CoreThread.Suspend();
GetCoreThread().Suspend();
} }
void Sys_Resume() void Sys_Resume()
{ {
if( HasCoreThread() ) CoreThread.Resume();
GetCoreThread().Resume();
} }
void Sys_TakeSnapshot() void Sys_TakeSnapshot()
@ -88,10 +86,9 @@ namespace Implementations
void Sys_RenderToggle() void Sys_RenderToggle()
{ {
if( !HasCoreThread() ) return; bool resume = CoreThread.Suspend();
sCoreThread.Suspend();
renderswitch = !renderswitch; renderswitch = !renderswitch;
sCoreThread.Resume(); if( resume ) CoreThread.Resume();
} }
void Sys_LoggingToggle() void Sys_LoggingToggle()

View File

@ -149,7 +149,7 @@ void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
} }
else else
{ {
isClosing = wxGetApp().PrepForExit(); isClosing = wxGetApp().PrepForExit( evt.CanVeto() );
if( !isClosing ) evt.Veto( true ); if( !isClosing ) evt.Veto( true );
} }
@ -223,7 +223,7 @@ void MainEmuFrame::ConnectMenus()
ConnectMenu( MenuId_Exit, Menu_Exit_Click ); ConnectMenu( MenuId_Exit, Menu_Exit_Click );
ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click ); ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click );
ConnectMenu( MenuId_Sys_Reset, Menu_EmuReset_Click ); ConnectMenu( MenuId_Sys_Reset, Menu_SysReset_Click );
ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click ); ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click );
@ -260,6 +260,13 @@ void MainEmuFrame::InitLogBoxPosition( AppConfig::ConsoleLogOptions& conf )
} }
} }
static void OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt )
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplySettings();
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title): MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER) ), wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER) ),
@ -286,7 +293,9 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
m_LoadStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Load01 ) ), m_LoadStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Load01 ) ),
m_SaveStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Save01 ) ), m_SaveStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Save01 ) ),
m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) ) m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) ),
m_Listener_CoreThreadStatus( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener( this, OnCoreThreadStatusChanged ) )
{ {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Initial menubar setup. This needs to be done first so that the menu bar's visible size // Initial menubar setup. This needs to be done first so that the menu bar's visible size
@ -495,8 +504,7 @@ void MainEmuFrame::ApplySettings()
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() ); GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() );
if( HasCoreThread() ) GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, CoreThread.IsExecMode_Running() ? _("Suspend") :_("Resume") );
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, GetCoreThread().IsSuspended() ? _("Resume") :_("Suspend") );
if( m_RecentIsoList ) if( m_RecentIsoList )
{ {

View File

@ -47,22 +47,24 @@ protected:
wxStatusBar& m_statusbar; wxStatusBar& m_statusbar;
wxStaticBitmap m_background; wxStaticBitmap m_background;
wxMenuBar& m_menubar; wxMenuBar& m_menubar;
wxMenu& m_menuBoot; wxMenu& m_menuBoot;
wxMenu& m_menuEmu; wxMenu& m_menuEmu;
wxMenu& m_menuConfig; wxMenu& m_menuConfig;
wxMenu& m_menuMisc; wxMenu& m_menuMisc;
wxMenu& m_menuDebug; wxMenu& m_menuDebug;
wxMenu& m_menuVideo; wxMenu& m_menuVideo;
wxMenu& m_menuAudio; wxMenu& m_menuAudio;
wxMenu& m_menuPad; wxMenu& m_menuPad;
wxMenu& m_LoadStatesSubmenu; wxMenu& m_LoadStatesSubmenu;
wxMenu& m_SaveStatesSubmenu; wxMenu& m_SaveStatesSubmenu;
wxMenuItem& m_MenuItem_Console; wxMenuItem& m_MenuItem_Console;
CmdEvt_ListenerBinding m_Listener_CoreThreadStatus;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// MainEmuFrame Constructors and Member Methods // MainEmuFrame Constructors and Member Methods
@ -107,7 +109,7 @@ protected:
void Menu_Exit_Click(wxCommandEvent &event); void Menu_Exit_Click(wxCommandEvent &event);
void Menu_SuspendResume_Click(wxCommandEvent &event); void Menu_SuspendResume_Click(wxCommandEvent &event);
void Menu_EmuReset_Click(wxCommandEvent &event); void Menu_SysReset_Click(wxCommandEvent &event);
void Menu_ConfigPlugin_Click(wxCommandEvent &event); void Menu_ConfigPlugin_Click(wxCommandEvent &event);

View File

@ -82,13 +82,13 @@ bool MainEmuFrame::_DoSelectIsoBrowser()
void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event ) void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
{ {
sCoreThread.Suspend(); CoreThread.Suspend();
if( !wxFileExists( g_Conf->CurrentIso ) ) if( !wxFileExists( g_Conf->CurrentIso ) )
{ {
if( !_DoSelectIsoBrowser() ) if( !_DoSelectIsoBrowser() )
{ {
sCoreThread.Resume(); CoreThread.Resume();
return; return;
} }
} }
@ -100,7 +100,7 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
if( !result ) if( !result )
{ {
sCoreThread.Resume(); CoreThread.Resume();
return; return;
} }
} }
@ -113,18 +113,18 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
void MainEmuFrame::Menu_IsoBrowse_Click( wxCommandEvent &event ) void MainEmuFrame::Menu_IsoBrowse_Click( wxCommandEvent &event )
{ {
sCoreThread.Suspend(); bool resume = CoreThread.Suspend();
_DoSelectIsoBrowser(); _DoSelectIsoBrowser();
sCoreThread.Resume(); if( resume ) CoreThread.Resume();
} }
void MainEmuFrame::Menu_RunIso_Click( wxCommandEvent &event ) void MainEmuFrame::Menu_RunIso_Click( wxCommandEvent &event )
{ {
sCoreThread.Suspend(); CoreThread.Suspend();
if( !_DoSelectIsoBrowser() ) if( !_DoSelectIsoBrowser() )
{ {
sCoreThread.Resume(); CoreThread.Resume();
return; return;
} }
@ -190,23 +190,19 @@ void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event) void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
{ {
if( !SysHasValidState() ) return; if( !SysHasValidState() ) return;
if( SysCoreThread* thr = GetCoreThreadPtr() )
{ if( !CoreThread.Suspend() )
if( thr->IsSuspended() ) CoreThread.Resume();
thr->Resume();
else
thr->Suspend();
}
} }
void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event) void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
{ {
if( !SysHasValidState() ) return; if( !SysHasValidState() ) return;
bool wasSuspended = HasCoreThread() ? GetCoreThread().IsSuspended() : true; bool resume = CoreThread.Suspend();
sApp.SysReset(); sApp.SysReset();
if( !wasSuspended ) if( resume )
sApp.SysExecute(); sApp.SysExecute();
} }

View File

@ -412,8 +412,8 @@ namespace Panels
protected: protected:
void OnStart() {} void OnStart() {}
void ExecuteTask(); void ExecuteTaskInThread();
void OnThreadCleanup() {} void OnCleanupInThread() {}
}; };
// This panel contains all of the plugin combo boxes. We stick them // This panel contains all of the plugin combo boxes. We stick them

View File

@ -83,9 +83,10 @@ bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess )
// If an exception is thrown above, this code below won't get run. // If an exception is thrown above, this code below won't get run.
// (conveniently skipping any option application! :D) // (conveniently skipping any option application! :D)
// Note: apply first, then save -- in case the apply fails.
AppApplySettings( &confcopy ); AppApplySettings( &confcopy );
if( saveOnSuccess ) if( saveOnSuccess ) AppSaveSettings();
AppSaveSettings();
} }
catch( Exception::CannotApplySettings& ex ) catch( Exception::CannotApplySettings& ex )
{ {

View File

@ -310,7 +310,7 @@ void Panels::PluginSelectorPanel::Apply()
if( pi->shortname != NULL ) if( pi->shortname != NULL )
{ {
if( wxGetApp().m_CoreThread ) if( CoreThread.IsRunning() )
{ {
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover. // [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
} }
@ -554,7 +554,7 @@ void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
m_master.GetEventHandler()->AddPendingEvent( yay ); m_master.GetEventHandler()->AddPendingEvent( yay );
} }
void Panels::PluginSelectorPanel::EnumThread::ExecuteTask() void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
{ {
DevCon.Status( "Plugin Enumeration Thread started..." ); DevCon.Status( "Plugin Enumeration Thread started..." );

View File

@ -58,8 +58,8 @@ public:
protected: protected:
void OnStart() {} void OnStart() {}
void OnThreadCleanup(); void OnCleanupInThread();
void ExecuteTask(); void ExecuteTaskInThread();
}; };
LoadPluginsTask::~LoadPluginsTask() throw() LoadPluginsTask::~LoadPluginsTask() throw()
@ -67,7 +67,7 @@ LoadPluginsTask::~LoadPluginsTask() throw()
PersistentThread::Cancel(); PersistentThread::Cancel();
} }
void LoadPluginsTask::ExecuteTask() void LoadPluginsTask::ExecuteTaskInThread()
{ {
wxGetApp().Ping(); wxGetApp().Ping();
Yield(3); Yield(3);
@ -78,13 +78,13 @@ void LoadPluginsTask::ExecuteTask()
Result = PluginManager_Create( m_folders ); Result = PluginManager_Create( m_folders );
} }
void LoadPluginsTask::OnThreadCleanup() void LoadPluginsTask::OnCleanupInThread()
{ {
wxCommandEvent evt( pxEVT_LoadPluginsComplete ); wxCommandEvent evt( pxEVT_LoadPluginsComplete );
evt.SetClientData( this ); evt.SetClientData( this );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnCleanupInThread();
} }
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
@ -127,7 +127,7 @@ static bool plugin_load_lock = false;
void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt ) void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt )
{ {
if( plugin_load_lock ) return; if( plugin_load_lock ) return;
m_CoreThread = NULL; CoreThread.Cancel();
m_CorePlugins = NULL; m_CorePlugins = NULL;
wxString passins[PluginId_Count]; wxString passins[PluginId_Count];
@ -203,7 +203,7 @@ void LoadPluginsImmediate()
{ {
if( g_plugins != NULL ) return; if( g_plugins != NULL ) return;
wxGetApp().m_CoreThread = NULL; CoreThread.Cancel();
wxString passins[PluginId_Count]; wxString passins[PluginId_Count];
ConvertPluginFilenames( passins ); ConvertPluginFilenames( passins );
@ -213,6 +213,6 @@ void LoadPluginsImmediate()
void UnloadPlugins() void UnloadPlugins()
{ {
wxGetApp().m_CoreThread = NULL; CoreThread.Cancel();
wxGetApp().m_CorePlugins = NULL; wxGetApp().m_CorePlugins = NULL;
} }

View File

@ -309,20 +309,17 @@ void __fastcall vtlbDefaultPhyWrite128(u32 addr,const mem128_t* data) { Console.
// VTLB Public API -- Init/Term/RegisterHandler stuff // VTLB Public API -- Init/Term/RegisterHandler stuff
// //
// Registers a handler into the VTLB's internal handler array. The handler defines specific behavior // Assigns or re-assigns the callbacks for a VTLB memory handler. The handler defines specific behavior
// for how memory pages bound to the handler are read from / written to. If any of the handler pointers // for how memory pages bound to the handler are read from / written to. If any of the handler pointers
// are NULL, the memory operations will be mapped to the BusError handler (thus generating BusError // are NULL, the memory operations will be mapped to the BusError handler (thus generating BusError
// exceptions if the emulated app attempts to access them). // exceptions if the emulated app attempts to access them).
// //
// Note: All handlers persist across calls to vtlb_Reset(), but are wiped/invalidated by calls to vtlb_Init() // Note: All handlers persist across calls to vtlb_Reset(), but are wiped/invalidated by calls to vtlb_Init()
// //
// Returns a handle for the newly created handler See .vtlb_MapHandler for use of the return value. void vtlb_ReassignHandler( vtlbHandler rv,
vtlbHandler vtlb_RegisterHandler( vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128, vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128) vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128 )
{ {
//write the code :p
vtlbHandler rv=vtlbHandlerCount++;
vtlbdata.RWFT[0][0][rv] = (r8!=0) ? (void*)(r8): (void*)vtlbDefaultPhyRead8; vtlbdata.RWFT[0][0][rv] = (r8!=0) ? (void*)(r8): (void*)vtlbDefaultPhyRead8;
vtlbdata.RWFT[1][0][rv] = (r16!=0) ? (void*)r16: (void*)vtlbDefaultPhyRead16; vtlbdata.RWFT[1][0][rv] = (r16!=0) ? (void*)r16: (void*)vtlbDefaultPhyRead16;
vtlbdata.RWFT[2][0][rv] = (r32!=0) ? (void*)r32: (void*)vtlbDefaultPhyRead32; vtlbdata.RWFT[2][0][rv] = (r32!=0) ? (void*)r32: (void*)vtlbDefaultPhyRead32;
@ -340,10 +337,32 @@ vtlbHandler vtlb_RegisterHandler( vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP
vtlbdata.RWFT[2][1][rv] = (void*)((w32!=0) ? w32:vtlbDefaultPhyWrite32); vtlbdata.RWFT[2][1][rv] = (void*)((w32!=0) ? w32:vtlbDefaultPhyWrite32);
vtlbdata.RWFT[3][1][rv] = (void*)((w64!=0) ? w64:vtlbDefaultPhyWrite64); vtlbdata.RWFT[3][1][rv] = (void*)((w64!=0) ? w64:vtlbDefaultPhyWrite64);
vtlbdata.RWFT[4][1][rv] = (void*)((w128!=0) ? w128:vtlbDefaultPhyWrite128); vtlbdata.RWFT[4][1][rv] = (void*)((w128!=0) ? w128:vtlbDefaultPhyWrite128);
}
vtlbHandler vtlb_NewHandler()
{
pxAssertDev( vtlbHandlerCount < 127, "VTLB allowed handler count exceeded!" );
return vtlbHandlerCount++;
}
// Registers a handler into the VTLB's internal handler array. The handler defines specific behavior
// for how memory pages bound to the handler are read from / written to. If any of the handler pointers
// are NULL, the memory operations will be mapped to the BusError handler (thus generating BusError
// exceptions if the emulated app attempts to access them).
//
// Note: All handlers persist across calls to vtlb_Reset(), but are wiped/invalidated by calls to vtlb_Init()
//
// Returns a handle for the newly created handler See vtlb_MapHandler for use of the return value.
//
vtlbHandler vtlb_RegisterHandler( vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128)
{
vtlbHandler rv = vtlb_NewHandler();
vtlb_ReassignHandler( rv, r8, r16, r32, r64, r128, w8, w16, w32, w64, w128 );
return rv; return rv;
} }
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// Maps the given hander (created with vtlb_RegisterHandler) to the specified memory region. // Maps the given hander (created with vtlb_RegisterHandler) to the specified memory region.
// New mappings always assume priority over previous mappings, so place "generic" mappings for // New mappings always assume priority over previous mappings, so place "generic" mappings for

View File

@ -26,9 +26,18 @@ extern u8* vtlb_malloc( uint size, uint align );
extern void vtlb_free( void* pmem, uint size ); extern void vtlb_free( void* pmem, uint size );
//physical stuff extern vtlbHandler vtlb_NewHandler();
vtlbHandler vtlb_RegisterHandler( vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128); extern vtlbHandler vtlb_RegisterHandler(
vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128
);
extern void vtlb_ReassignHandler( vtlbHandler rv,
vtlbMemR8FP* r8,vtlbMemR16FP* r16,vtlbMemR32FP* r32,vtlbMemR64FP* r64,vtlbMemR128FP* r128,
vtlbMemW8FP* w8,vtlbMemW16FP* w16,vtlbMemW32FP* w32,vtlbMemW64FP* w64,vtlbMemW128FP* w128
);
extern void vtlb_MapHandler(vtlbHandler handler,u32 start,u32 size); extern void vtlb_MapHandler(vtlbHandler handler,u32 start,u32 size);
extern void vtlb_MapBlock(void* base,u32 start,u32 size,u32 blocksize=0); extern void vtlb_MapBlock(void* base,u32 start,u32 size,u32 blocksize=0);

View File

@ -169,7 +169,7 @@ public:
protected: protected:
void OnStart() {} void OnStart() {}
void ExecuteTask() void ExecuteTaskInThread()
{ {
try try
{ {
@ -188,7 +188,7 @@ protected:
} }
} }
void OnThreadCleanup() { } void OnCleanupInThread() { }
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -309,6 +309,8 @@ EXPORT_C_(s32) SPU2open(void *pDsp)
}*/ }*/
IsOpened = true; IsOpened = true;
lClocks = (cyclePtr!=NULL) ? *cyclePtr : 0;
try try
{ {
SndBuffer::Init(); SndBuffer::Init();

View File

@ -276,17 +276,23 @@ u32 TicksThread = 0;
__forceinline void TimeUpdate(u32 cClocks) __forceinline void TimeUpdate(u32 cClocks)
{ {
s32 dClocks = cClocks-lClocks; u32 dClocks = cClocks - lClocks;
// [Air]: Sanity Check // Sanity Checks:
// If for some reason our clock value seems way off base, just mix // It's not totally uncommon for the IOP's clock to jump backwards a cycle or two, and in
// out a little bit, skip the rest, and hope the ship "rights" itself later on. // such cases we just want to ignore the TimeUpdate call.
if( dClocks > (u32)-15 ) return;
// But if for some reason our clock value seems way off base (typically due to bad dma
// timings from PCSX2), just mix out a little bit, skip the rest, and hope the ship
// "rights" itself later on.
if( dClocks > TickInterval*SanityInterval ) if( dClocks > TickInterval*SanityInterval )
{ {
ConLog( " * SPU2 > TimeUpdate Sanity Check (Tick Delta: %d) (PS2 Ticks: %d)\n", dClocks/TickInterval, cClocks/TickInterval ); ConLog( " * SPU2 > TimeUpdate Sanity Check (Tick Delta: %d) (PS2 Ticks: %d)\n", dClocks/TickInterval, cClocks/TickInterval );
dClocks = TickInterval*SanityInterval; dClocks = TickInterval * SanityInterval;
lClocks = cClocks-dClocks; lClocks = cClocks - dClocks;
} }
//UpdateDebugDialog(); //UpdateDebugDialog();
@ -350,8 +356,8 @@ __forceinline void TimeUpdate(u32 cClocks)
} }
} }
dClocks-=TickInterval; dClocks -= TickInterval;
lClocks+=TickInterval; lClocks += TickInterval;
Cycles++; Cycles++;
// Note: IOP does not use MMX regs, so no need to save them. // Note: IOP does not use MMX regs, so no need to save them.