diff --git a/common/build/Utilities/Utilities.cbp b/common/build/Utilities/Utilities.cbp
index 0e98cfa9e5..a224f48887 100644
--- a/common/build/Utilities/Utilities.cbp
+++ b/common/build/Utilities/Utilities.cbp
@@ -1,127 +1,127 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj
index 4e2eb43e68..c3a5881e2d 100644
--- a/common/build/Utilities/utilities.vcproj
+++ b/common/build/Utilities/utilities.vcproj
@@ -425,6 +425,10 @@
RelativePath="..\..\include\Utilities\Dependencies.h"
>
+
+
@@ -441,10 +445,6 @@
RelativePath="..\..\include\intrin_x86.h"
>
-
-
diff --git a/common/include/Utilities/Listeners.h b/common/include/Utilities/EventSource.h
similarity index 78%
rename from common/include/Utilities/Listeners.h
rename to common/include/Utilities/EventSource.h
index 7fab62b2ee..3a06f263e2 100644
--- a/common/include/Utilities/Listeners.h
+++ b/common/include/Utilities/EventSource.h
@@ -16,12 +16,17 @@
#pragma once
#include
-#include
+
+class wxCommandEvent;
+
+// --------------------------------------------------------------------------------------
+// EventListener< typename EvtType >
+// --------------------------------------------------------------------------------------
template< typename EvtType >
struct EventListener
{
- typedef void FuncType( void* object, const EvtType& evt );
+ typedef void __fastcall FuncType( void* object, EvtType& evt );
void* object;
FuncType* OnEvent;
@@ -49,6 +54,10 @@ struct EventListener
}
};
+// --------------------------------------------------------------------------------------
+// EventSource< template EvtType >
+// --------------------------------------------------------------------------------------
+
template< typename EvtType >
class EventSource
{
@@ -58,23 +67,38 @@ public:
typedef typename ListenerList::iterator Handle;
protected:
+ typedef typename ListenerList::const_iterator ConstIterator;
+
ListenerList m_listeners;
+
+ // This is a cached copy of the listener list used to handle standard dispatching, which
+ // allows for self-modification of the EventSource's listener list by the listeners.
+ // Translation: The dispatcher uses this copy instead, to avoid iterator invalidation.
+ ListenerList m_cache_copy;
+ bool m_cache_valid;
public:
+ EventSource() : m_cache_valid( false )
+ {
+ }
+
virtual ~EventSource() throw() {}
virtual void Remove( const ListenerType& listener )
{
+ m_cache_valid = false;
m_listeners.remove( listener );
}
virtual void Remove( const Handle& listenerHandle )
{
+ m_cache_valid = false;
m_listeners.erase( listenerHandle );
}
virtual Handle AddFast( const ListenerType& listener )
{
+ m_cache_valid = false;
m_listeners.push_front( listener );
return m_listeners.begin();
}
@@ -93,14 +117,21 @@ public:
Remove( ListenerType( objhandle, fnptr ) );
}
- void Dispatch( const EvtType& evt ) const
+ void Dispatch( EvtType& evt )
{
- // Have to make a complete copy of the list, because the event stack can change:
-
- ListenerList list( m_listeners );
-
- typename ListenerList::const_iterator iter = list.begin();
- while( iter != list.end() )
+ if( !m_cache_valid )
+ {
+ m_cache_copy = m_listeners;
+ m_cache_valid = true;
+ }
+
+ _DispatchRaw( m_cache_copy.begin(), m_cache_copy.end(), evt );
+ }
+
+protected:
+ __forceinline void _DispatchRaw( ConstIterator& iter, const ConstIterator& iend, EvtType& evt )
+ {
+ while( iter != iend )
{
try
{
@@ -118,14 +149,15 @@ public:
++iter;
}
}
+
};
// --------------------------------------------------------------------------------------
-// EventListenerBinding
+// EventListenerBinding< typename EvtType ?
// --------------------------------------------------------------------------------------
// Encapsulated event listener binding, provides the "benefits" of object unwinding.
//
-template< typename EvtType = wxCommandEvent >
+template< typename EvtType >
class EventListenerBinding
{
public:
@@ -211,9 +243,7 @@ public:
template< typename EvtType >
void EventSource::RemoveObject( const void* object )
{
- // Iso C++ rules regarding temporaries, specifically that non-const temporaries are disallowed,
- // also removes any actual convenience factor that unary predicates may have actually offered. >_<
-
+ m_cache_valid = false;
m_listeners.remove_if( PredicatesAreTheThingsOfNightmares( object ) );
}
diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h
index 5c767d876f..86a20934db 100644
--- a/common/include/Utilities/Threading.h
+++ b/common/include/Utilities/Threading.h
@@ -280,6 +280,7 @@ namespace Threading
bool IsRunning() const;
bool IsSelf() const;
wxString GetName() const;
+ bool HasPendingException() const { return !!m_except; }
protected:
// Extending classes should always implement your own OnStart(), which is called by
diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp
index 623ed3f1b7..4a355731f8 100644
--- a/common/src/Utilities/ThreadTools.cpp
+++ b/common/src/Utilities/ThreadTools.cpp
@@ -142,6 +142,8 @@ void Threading::PersistentThread::Start()
Detach(); // clean up previous thread handle, if one exists.
OnStart();
+ m_except = NULL;
+
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
throw Exception::ThreadCreationError();
}
diff --git a/common/src/x86emitter/tools.cpp b/common/src/x86emitter/tools.cpp
index 9afa00e338..ad45b1f4b9 100644
--- a/common/src/x86emitter/tools.cpp
+++ b/common/src/x86emitter/tools.cpp
@@ -20,6 +20,10 @@
// use Freeze/Thaw in MMXRegisters, XMMRegisters, & Registers.
+// used to disable register freezing during cpuBranchTests (registers
+// are safe then since they've been completely flushed)
+bool g_EEFreezeRegs = false;
+
/////////////////////////////////////////////////////////////////////
// MMX Register Freezing
//
diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp
index c8042b2fce..ecb33e8290 100644
--- a/pcsx2/Counters.cpp
+++ b/pcsx2/Counters.cpp
@@ -440,8 +440,6 @@ __forceinline void rcntUpdate_hScanline()
}
}
-bool CoreCancelDamnit = false;
-
__forceinline void rcntUpdate_vSync()
{
s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle);
@@ -449,28 +447,7 @@ __forceinline void rcntUpdate_vSync()
if (vsyncCounter.Mode == MODE_VSYNC)
{
- eeRecIsReset = false;
-
-#ifndef PCSX2_SEH
- if( CoreCancelDamnit || SysCoreThread::Get().HasPendingStateChangeRequest() )
- {
- longjmp( SetJmp_StateCheck, 1 );
- }
-#else
- mtgsThread.RethrowException();
- SysCoreThread::Get().StateCheckInThread();
-#endif
- if( eeRecIsReset )
- {
- eeRecIsReset = false;
- cpuSetBranch();
-
-#ifndef PCSX2_SEH
- longjmp( SetJmp_RecExecute, SetJmp_Dispatcher );
-#else
- throw Exception::ForceDispatcherReg();
-#endif
- }
+ Cpu->CheckExecutionState();
VSyncEnd(vsyncCounter.sCycle);
diff --git a/pcsx2/GS.h b/pcsx2/GS.h
index f2bfd6994b..bbf0b2e431 100644
--- a/pcsx2/GS.h
+++ b/pcsx2/GS.h
@@ -123,6 +123,8 @@ public:
mtgsThreadObject();
virtual ~mtgsThreadObject() throw();
+ static mtgsThreadObject& Get();
+
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void WaitGS();
diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp
index d479a1682e..9b79096f0e 100644
--- a/pcsx2/Interpreter.cpp
+++ b/pcsx2/Interpreter.cpp
@@ -19,6 +19,7 @@
#include "Common.h"
#include "R5900.h"
#include "R5900OpcodeTables.h"
+#include "System/SysThreads.h"
#include
@@ -30,6 +31,8 @@ static int branch2 = 0;
static u32 cpuBlockCycles = 0; // 3 bit fixed point version of cycle count
static std::string disOut;
+static void intEventTest();
+
// These macros are used to assemble the repassembler functions
static void debugI()
@@ -356,24 +359,24 @@ void JALR()
////////////////////////////////////////////////////////
-void intAlloc()
+static void intAlloc()
{
// fixme : detect cpu for use the optimize asm code
}
-void intReset()
+static void intReset()
{
cpuRegs.branch = 0;
branch2 = 0;
}
-void intEventTest()
+static void intEventTest()
{
// Perform counters, ints, and IOP updates:
_cpuBranchTest_Shared();
}
-void intExecute()
+static void intExecute()
{
g_EEFreezeRegs = false;
@@ -386,6 +389,11 @@ void intExecute()
}
}
+static void intCheckExecutionState()
+{
+ SysCoreThread::Get().StateCheckInThread();
+}
+
static void intStep()
{
g_EEFreezeRegs = false;
@@ -404,6 +412,7 @@ R5900cpu intCpu = {
intReset,
intStep,
intExecute,
+ intCheckExecutionState,
intClear,
intShutdown
};
diff --git a/pcsx2/Linux/LnxHostSys.cpp b/pcsx2/Linux/LnxHostSys.cpp
index aaa13173ae..63430b94cb 100644
--- a/pcsx2/Linux/LnxHostSys.cpp
+++ b/pcsx2/Linux/LnxHostSys.cpp
@@ -23,36 +23,34 @@ extern void SignalExit(int sig);
static const uptr m_pagemask = getpagesize()-1;
-void InstallLinuxExceptionHandler()
+// Linux implementation of SIGSEGV handler. Bind it using sigaction().
+static void SysPageFaultSignalFilter( int signal, siginfo_t *info, void * )
+{
+ // Note: Use of most stdio functions isn't safe here. Avoid console logs,
+ // assertions, file logs, or just about anything else useful.
+
+ PageFaultInfo info( (uptr)info->si_addr & ~m_pagemask );
+ Source_AccessViolation.DispatchException( info );
+
+ // resumes execution right where we left off (re-executes instruction that
+ // caused the SIGSEGV).
+ if( info.handled ) return;
+
+ // Bad mojo! Completely invalid address.
+ // Instigate a trap if we're in a debugger, and if not then do a SIGKILL.
+
+ wxTrap();
+ if( !IsDebugBuild ) raise( SIGKILL );
+}
+
+void InstallSignalHandler()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
- sa.sa_sigaction = &SysPageFaultExceptionFilter;
+ sa.sa_sigaction = SysPageFaultSignalFilter;
sigaction(SIGSEGV, &sa, NULL);
}
-void ReleaseLinuxExceptionHandler()
-{
- // Code this later.
-}
-
-// Linux implementation of SIGSEGV handler. Bind it using sigaction().
-void SysPageFaultExceptionFilter( int signal, siginfo_t *info, void * )
-{
- // get bad virtual address
- uptr offset = (u8*)info->si_addr - psM;
-
- if (offset>=Ps2MemSize::Base)
- {
- // Bad mojo! Completely invalid address.
- // Instigate a crash or abort emulation or something.
- wxTrap();
- if( !IsDebugBuild )
- raise( SIGKILL );
- }
-
- DevCon.Status( "Protected memory cleanup. Offset 0x%x", offset );
- mmap_ClearCpuBlock( offset & ~m_pagemask );
-}
+void NTFS_CompressFile( const wxString& file, bool compressStatus=true ) {}
diff --git a/pcsx2/Linux/pcsx2.cbp b/pcsx2/Linux/pcsx2.cbp
index caa497e2c0..a1961001c1 100644
--- a/pcsx2/Linux/pcsx2.cbp
+++ b/pcsx2/Linux/pcsx2.cbp
@@ -1,466 +1,468 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp
index 5559bada1c..e1480697d6 100644
--- a/pcsx2/MTGS.cpp
+++ b/pcsx2/MTGS.cpp
@@ -86,6 +86,14 @@ extern bool renderswitch;
std::list ringposStack;
#endif
+static __threadlocal mtgsThreadObject* tls_mtgsThread = NULL;
+
+mtgsThreadObject& mtgsThreadObject::Get()
+{
+ pxAssertMsg( tls_mtgsThread != NULL, L"This function must be called from the context of a running mtgsThreadObject." );
+ return *tls_mtgsThread;
+}
+
mtgsThreadObject::mtgsThreadObject() :
SysThreadBase()
, m_RingPos( 0 )
@@ -162,8 +170,8 @@ void mtgsThreadObject::PostVsyncEnd( bool updategs )
if( m_WritePos == volatize( m_RingPos ) )
{
// MTGS ringbuffer is empty, but we still have queued frames in the counter? Ouch!
- Console.Error( "MTGS > Queued framecount mismatch = %d", m_QueuedFrames );
- m_QueuedFrames = 0;
+ int count = AtomicExchange( m_QueuedFrames, 0 );
+ Console.Error( "MTGS > Queued framecount mismatch = %d", count );
break;
}
Threading::Sleep( 2 ); // Sleep off quite a bit of time, since we're obviously *waaay* ahead.
@@ -226,14 +234,20 @@ void mtgsThreadObject::OpenPlugin()
void mtgsThreadObject::ExecuteTaskInThread()
{
+ tls_mtgsThread = this;
+
#ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd;
#endif
while( true )
{
- m_sem_event.WaitRaw(); // ... because this does a cancel test itself..
- StateCheckInThread( false ); // false disables cancel test here!
+ // Performance note: Both of these perform cancellation tests, but pthread_testcancel
+ // is very optimized (only 1 instruction test in most cases), so no point in trying
+ // to avoid it.
+
+ m_sem_event.WaitRaw();
+ StateCheckInThread();
m_RingBufferIsBusy = true;
@@ -268,8 +282,7 @@ void mtgsThreadObject::ExecuteTaskInThread()
// stall for a bit to let the MainThread have time to update the g_pGSWritePos.
m_lock_RingRestart.Wait();
-
- StateCheckInThread( false ); // disable cancel since the above locks are cancelable already
+ StateCheckInThread();
continue;
case GS_RINGTYPE_P1:
@@ -427,6 +440,7 @@ void mtgsThreadObject::OnResumeInThread( bool isSuspended )
void mtgsThreadObject::OnCleanupInThread()
{
ClosePlugin();
+ tls_mtgsThread = NULL;
_parent::OnCleanupInThread();
}
diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp
index 864d6216d3..06ff81af8c 100644
--- a/pcsx2/Memory.cpp
+++ b/pcsx2/Memory.cpp
@@ -48,7 +48,7 @@ BIOS
#include "VUmicro.h"
#include "GS.h"
#include "IPU/IPU.h"
-#include "AppConfig.h"
+#include "System/PageFaultSource.h"
#ifdef ENABLECACHE
@@ -574,6 +574,8 @@ void memClearPageAddr(u32 vaddr)
///////////////////////////////////////////////////////////////////////////
// PS2 Memory Init / Reset / Shutdown
+static void __fastcall mmap_OnPageFault( void* basemem, PageFaultInfo& info );
+
static const uint m_allMemSize =
Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2 + Ps2MemSize::ERom +
Ps2MemSize::Base + Ps2MemSize::Hardware + Ps2MemSize::Scratch;
@@ -596,10 +598,14 @@ void memAlloc()
psER = curpos; curpos += Ps2MemSize::ERom;
psH = curpos; curpos += Ps2MemSize::Hardware;
psS = curpos; //curpos += Ps2MemSize::Scratch;
+
+ Source_PageFault.Add( EventListener((void*)psM, mmap_OnPageFault) );
}
void memShutdown()
{
+ Source_PageFault.Remove( EventListener((void*)psM, mmap_OnPageFault) );
+
vtlb_free( m_psAllMem, m_allMemSize );
m_psAllMem = NULL;
psM = psR = psR1 = psR2 = psER = psS = psH = NULL;
@@ -623,8 +629,7 @@ void memBindConditionalHandlers()
void memReset()
{
// VTLB Protection Preparations.
-
- HostSys::MemProtect( m_psAllMem, m_allMemSize, Protect_ReadWrite );
+ //HostSys::MemProtect( m_psAllMem, m_allMemSize, Protect_ReadWrite );
// Note!! Ideally the vtlb should only be initialized once, and then subsequent
// resets of the system hardware would only clear vtlb mappings, but since the
@@ -874,28 +879,33 @@ void mmap_MarkCountedRamPage( u32 paddr )
HostSys::MemProtect( &psM[rampage<<12], 1, Protect_ReadOnly );
}
-// offset - offset of address relative to psM. The exception handler for the platform/host
-// OS should ensure that only addresses within psM address space are passed. Anything else
-// will produce undefined results (ie, crashes).
-void mmap_ClearCpuBlock( uint offset )
+// offset - offset of address relative to psM.
+static __forceinline void mmap_ClearCpuBlock( uint offset )
{
int rampage = offset >> 12;
// Assertion: This function should never be run on a block that's already under
// manual protection. Indicates a logic error in the recompiler or protection code.
- jASSUME( m_PageProtectInfo[rampage].Mode != ProtMode_Manual );
-
- //#ifndef __LINUX__ // this function is called from the signal handler
- //DbgCon.WriteLn( "Manual page @ 0x%05x", m_PageProtectInfo[rampage].ReverseRamMap>>12 );
- //#endif
+ pxAssertMsg( m_PageProtectInfo[rampage].Mode != ProtMode_Manual,
+ "Attempted to clear a block that is already under manual protection." );
HostSys::MemProtect( &psM[rampage<<12], 1, Protect_ReadWrite );
m_PageProtectInfo[rampage].Mode = ProtMode_Manual;
Cpu->Clear( m_PageProtectInfo[rampage].ReverseRamMap, 0x400 );
}
+static void __fastcall mmap_OnPageFault( void* basemem, PageFaultInfo& info )
+{
+ // get bad virtual address
+ uptr offset = info.addr - (uptr)basemem;
+ if( offset >= Ps2MemSize::Base ) return;
+
+ mmap_ClearCpuBlock( offset );
+ info.handled = true;
+}
+
// Clears all block tracking statuses, manual protection flags, and write protection.
-// This does not clear any recompiler blocks. IT is assumed (and necessary) for the caller
+// This does not clear any recompiler blocks. It is assumed (and necessary) for the caller
// to ensure the EErec is also reset in conjunction with calling this function.
void mmap_ResetBlockTracking()
{
diff --git a/pcsx2/Memory.h b/pcsx2/Memory.h
index faa494c5d3..e3154de121 100644
--- a/pcsx2/Memory.h
+++ b/pcsx2/Memory.h
@@ -134,7 +134,6 @@ extern void memMapVUmicro();
extern int mmap_GetRamPageInfo( u32 paddr );
extern void mmap_MarkCountedRamPage( u32 paddr );
extern void mmap_ResetBlockTracking();
-extern void mmap_ClearCpuBlock( uint offset );
#define memRead8 vtlb_memRead8
#define memRead16 vtlb_memRead16
diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp
index fe32764854..3691814d8d 100644
--- a/pcsx2/R5900.cpp
+++ b/pcsx2/R5900.cpp
@@ -34,6 +34,8 @@
#include "SPR.h"
#include "Sif.h"
+#include "System/SysThreads.h"
+
#include "R5900Exceptions.h"
using namespace R5900; // for R5900 disasm tools
@@ -61,6 +63,8 @@ void cpuReset()
if( mtgsThread.IsOpen() )
mtgsThread.WaitGS(); // GS better be done processing before we reset the EE, just in case.
+ SysClearExecutionCache();
+
cpuIsInitialized = true;
memReset();
@@ -560,7 +564,6 @@ void cpuExecuteBios()
Console.Status( "Executing Bios Stub..." );
- PCSX2_MEM_PROTECT_BEGIN();
g_ExecBiosHack = true;
while( cpuRegs.pc != 0x00200008 &&
cpuRegs.pc != 0x00100008 )
@@ -568,7 +571,6 @@ void cpuExecuteBios()
Cpu->Execute();
}
g_ExecBiosHack = false;
- PCSX2_MEM_PROTECT_END();
// {
// FILE* f = fopen("eebios.bin", "wb");
diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h
index 45dcf10835..45f5005164 100644
--- a/pcsx2/R5900.h
+++ b/pcsx2/R5900.h
@@ -26,7 +26,6 @@
extern bool g_EEFreezeRegs;
extern bool g_ExecBiosHack;
-extern volatile bool eeRecIsReset;
namespace Exception
{
@@ -256,7 +255,6 @@ extern bool eeEventTestIsActive;
extern u32 s_iLastCOP0Cycle;
extern u32 s_iLastPERFCycle[2];
-void intEventTest();
void intSetBranch();
// This is a special form of the interpreter's doBranch that is run from various
@@ -272,6 +270,7 @@ struct R5900cpu
void (*Reset)();
void (*Step)();
void (*Execute)();
+ void (*CheckExecutionState)();
void (*Clear)(u32 Addr, u32 Size);
void (*Shutdown)(); // deallocates memory reserved by Allocate
};
diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp
index 46b8f35750..5de0288bc2 100644
--- a/pcsx2/RecoverySystem.cpp
+++ b/pcsx2/RecoverySystem.cpp
@@ -34,7 +34,7 @@ int sys_resume_lock = 0;
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
-static void StateThread_OnAppStatus( void* thr, const enum AppEventType& stat )
+static void __fastcall StateThread_OnAppStatus( void* thr, AppEventType& stat )
{
if( (thr == NULL) || (stat != AppStatus_Exiting) ) return;
((PersistentThread*)thr)->Cancel();
diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp
index 6944116389..141623f60f 100644
--- a/pcsx2/System.cpp
+++ b/pcsx2/System.cpp
@@ -25,8 +25,10 @@
#include "sVU_zerorec.h" // for SuperVUReset
#include "R5900Exceptions.h"
-
#include "CDVD/CDVD.h"
+#include "System/PageFaultSource.h"
+
+SrcType_PageFault Source_PageFault;
#if _MSC_VER
# include "svnrev.h"
@@ -106,6 +108,8 @@ static wxString GetMemoryErrorVM()
SysCoreAllocations::SysCoreAllocations()
{
+ InstallSignalHandler();
+
Console.Status( "Initializing PS2 virtual machine..." );
RecSuccess_EE = false;
diff --git a/pcsx2/System.h b/pcsx2/System.h
index 21241154fe..c76286ba08 100644
--- a/pcsx2/System.h
+++ b/pcsx2/System.h
@@ -60,62 +60,35 @@ extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed");
extern void vSyncDebugStuff( uint frame );
+extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true );
// --------------------------------------------------------------------------------------
-// Memory Protection (Used by VTLB, Recompilers, and Texture caches)
-// --------------------------------------------------------------------------------------
-#ifdef __LINUX__
-
-# include
-
- extern void SysPageFaultExceptionFilter( int signal, siginfo_t *info, void * );
- extern void __fastcall InstallLinuxExceptionHandler();
- extern void __fastcall ReleaseLinuxExceptionHandler();
- static void NTFS_CompressFile( const wxString& file, bool compressStatus=true ) {}
-
-# define PCSX2_MEM_PROTECT_BEGIN() InstallLinuxExceptionHandler()
-# define PCSX2_MEM_PROTECT_END() ReleaseLinuxExceptionHandler()
-
-#elif defined( _WIN32 )
-
- extern int SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps);
- extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true );
-
-# define PCSX2_MEM_PROTECT_BEGIN() __try {
-# define PCSX2_MEM_PROTECT_END() } __except(SysPageFaultExceptionFilter(GetExceptionInformation())) {}
-
-#else
-# error PCSX2 - Unsupported operating system platform.
-#endif
-
-// --------------------------------------------------------------------------------------
-// PCSX2_SEH - Defines existence of "built in" Structed Exception Handling support.
+// PCSX2_SEH - Defines existence of "built in" Structured Exception Handling support.
// --------------------------------------------------------------------------------------
// This should be available on Windows, via Microsoft or Intel compilers (I'm pretty sure Intel
// supports native SEH model). GNUC in Windows, or any compiler in a non-windows platform, will
// need to use setjmp/longjmp instead to exit recompiled code.
//
-#if defined(_WIN32) && !defined(__GNUC__)
-# define PCSX2_SEH
-#else
-# include
+//#define PCSX2_SEH 0 // use this to force disable SEH on win32, to test setjmp functionality.
- // Platforms without SEH need to use SetJmp / LongJmp to deal with exiting the recompiled
- // code execution pipelines in an efficient manner, since standard C++ exceptions cannot
- // unwind across dynamically recompiled code.
-
- enum
- {
- SetJmp_Dispatcher = 1,
- SetJmp_Exit,
- };
-
- extern jmp_buf SetJmp_RecExecute;
- extern jmp_buf SetJmp_StateCheck;
+#ifndef PCSX2_SEH
+# if defined(_WIN32) && !defined(__GNUC__)
+# define PCSX2_SEH 1
+# else
+# define PCSX2_SEH 0
+# endif
+#endif
+
+// special macro which disables inlining on functions that require their own function stackframe.
+// This is due to how Win32 handles structured exception handling. Linux uses signals instead
+// of SEH, and so these functions can be inlined.
+#ifdef _WIN32
+# define __unique_stackframe __noinline
+#else
+# define __unique_stackframe
#endif
-class pxMessageBoxEvent;
//////////////////////////////////////////////////////////////////////////////////////////
// Different types of message boxes that the emulator can employ from the friendly confines
@@ -123,6 +96,9 @@ class pxMessageBoxEvent;
// blocking behavior -- they prompt the user for action and only return after the user has
// responded to the prompt.
//
+
+class pxMessageBoxEvent;
+
namespace Msgbox
{
extern void OnEvent( pxMessageBoxEvent& evt );
diff --git a/pcsx2/System/PageFaultSource.h b/pcsx2/System/PageFaultSource.h
new file mode 100644
index 0000000000..0bc8e0d674
--- /dev/null
+++ b/pcsx2/System/PageFaultSource.h
@@ -0,0 +1,75 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2009 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#pragma once
+
+// =====================================================================================================
+// Cross-Platform Memory Protection (Used by VTLB, Recompilers and Texture caches)
+// =====================================================================================================
+// Win32 platforms use the SEH model: __try {} __except {}
+// Linux platforms use the POSIX Signals model: sigaction()
+
+#include "Utilities/EventSource.h"
+
+struct PageFaultInfo
+{
+ uptr addr;
+ bool handled;
+
+ PageFaultInfo( uptr address )
+ {
+ addr = address;
+ handled = false;
+ }
+};
+
+class SrcType_PageFault : public EventSource
+{
+public:
+ SrcType_PageFault() {}
+ virtual ~SrcType_PageFault() throw() { }
+
+ void DispatchException( PageFaultInfo& evt )
+ {
+ if( m_listeners.empty() ) return;
+
+ ConstIterator iter( m_listeners.begin() );
+ const ConstIterator iend( m_listeners.end() );
+
+ do {
+ iter->OnEvent( iter->object, evt );
+ } while( (++iter != iend) && !evt.handled );
+ }
+};
+
+extern SrcType_PageFault Source_PageFault;
+
+extern void InstallSignalHandler();
+
+#ifdef __LINUX__
+
+# define PCSX2_PAGEFAULT_PROTECT()
+# define PCSX2_PAGEFAULT_EXCEPT()
+
+#elif defined( _WIN32 )
+
+ extern int SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps);
+
+# define PCSX2_PAGEFAULT_PROTECT __try
+# define PCSX2_PAGEFAULT_EXCEPT __except(SysPageFaultExceptionFilter(GetExceptionInformation())) {}
+
+#else
+# error PCSX2 - Unsupported operating system platform.
+#endif
diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp
new file mode 100644
index 0000000000..6ae86a7afc
--- /dev/null
+++ b/pcsx2/System/SysCoreThread.cpp
@@ -0,0 +1,234 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2009 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+#include "Common.h"
+
+#include "System.h"
+#include "PageFaultSource.h"
+#include "SysThreads.h"
+
+#include "SaveState.h"
+#include "Elfheader.h"
+#include "Plugins.h"
+#include "R5900.h"
+#include "R3000A.h"
+#include "VUmicro.h"
+
+#include "GS.h"
+
+static __threadlocal SysCoreThread* tls_coreThread = NULL;
+
+// --------------------------------------------------------------------------------------
+// SysCoreThread *External Thread* Implementations
+// (Called from outside the context of this thread)
+// --------------------------------------------------------------------------------------
+
+SysCoreThread::SysCoreThread() :
+ m_resetRecompilers( true )
+, m_resetProfilers( true )
+, m_resetVirtualMachine( true )
+, m_hasValidState( false )
+{
+ m_name = L"EE Core";
+}
+
+SysCoreThread::~SysCoreThread() throw()
+{
+ SysCoreThread::Cancel();
+}
+
+void SysCoreThread::Cancel( bool isBlocking )
+{
+ m_CoreCancelDamnit = true;
+ _parent::Cancel();
+}
+
+void SysCoreThread::Start()
+{
+ if( g_plugins == NULL ) return;
+ g_plugins->Init();
+ m_CoreCancelDamnit = false; // belongs in OnStart actually, but I'm tired :P
+ _parent::Start();
+}
+
+// Resumes the core execution state, or does nothing is the core is already running. If
+// settings were changed, resets will be performed as needed and emulation state resumed from
+// memory savestates.
+//
+// Exceptions (can occur on first call only):
+// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
+// on the first time the thread is resumed from it's initial idle state)
+// ThreadCreationError - Insufficient system resources to create thread.
+//
+void SysCoreThread::OnResumeReady()
+{
+ if( m_resetVirtualMachine )
+ {
+ cpuReset();
+ m_resetVirtualMachine = false;
+ m_hasValidState = false;
+ }
+
+ if( !m_hasValidState )
+ m_resetRecompilers = true;
+}
+
+void SysCoreThread::Reset()
+{
+ Suspend();
+ m_resetVirtualMachine = true;
+}
+
+// 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
+// is determined by comparing the current settings against the new settings.
+void SysCoreThread::ApplySettings( const Pcsx2Config& src )
+{
+ if( src == EmuConfig ) return;
+
+ const bool resumeWhenDone = Suspend();
+
+ m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
+ m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
+
+ const_cast(EmuConfig) = src;
+
+ if( resumeWhenDone ) Resume();
+}
+
+// --------------------------------------------------------------------------------------
+// EECoreThread *Worker* Implementations
+// (Called from the context of this thread only)
+// --------------------------------------------------------------------------------------
+SysCoreThread& SysCoreThread::Get()
+{
+ pxAssertMsg( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." );
+ return *tls_coreThread;
+}
+
+bool SysCoreThread::HasPendingStateChangeRequest() const
+{
+ return m_CoreCancelDamnit || mtgsThread.HasPendingException() || _parent::HasPendingStateChangeRequest();
+}
+
+
+void SysCoreThread::CpuInitializeMess()
+{
+ if( m_hasValidState ) return;
+
+ // Some recompiler mess might be left over -- nuke it here:
+ SysClearExecutionCache();
+ memBindConditionalHandlers();
+ m_resetRecompilers = false;
+ m_resetProfilers = false;
+
+ wxString elf_file;
+ if( EmuConfig.SkipBiosSplash )
+ {
+ // Fetch the ELF filename and CD type from the CDVD provider.
+ wxString ename;
+ 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:
+ throw Exception::RuntimeError( wxLt("Fast Boot failed: PCSX2 does not support emulation of PS1 games.") );
+
+ case 2:
+ // PS2 game. Valid!
+ elf_file = ename;
+ break;
+
+ 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).
+
+ m_hasValidState = true;
+ cpuExecuteBios();
+ m_hasValidState = false; // because loadElfFile might error...
+ loadElfFile( elf_file );
+ }
+ m_hasValidState = true;
+}
+
+void SysCoreThread::StateCheckInThread()
+{
+ mtgsThread.RethrowException();
+ _parent::StateCheckInThread();
+ if( !m_hasValidState )
+ throw Exception::RuntimeError( "Invalid emulation state detected; Virtual machine threads have been cancelled." );
+
+ if( m_resetRecompilers || m_resetProfilers )
+ {
+ SysClearExecutionCache();
+ memBindConditionalHandlers();
+ m_resetRecompilers = false;
+ m_resetProfilers = false;
+ }
+}
+
+void SysCoreThread::ExecuteTaskInThread()
+{
+ Threading::EnableHiresScheduler();
+
+ tls_coreThread = this;
+
+ m_sem_event.WaitRaw();
+ PCSX2_PAGEFAULT_PROTECT {
+ StateCheckInThread();
+ Cpu->Execute();
+ } PCSX2_PAGEFAULT_EXCEPT;
+}
+
+void SysCoreThread::OnSuspendInThread()
+{
+ if( g_plugins != NULL )
+ g_plugins->Close();
+}
+
+void SysCoreThread::OnResumeInThread( bool isSuspended )
+{
+ if( isSuspended && g_plugins != NULL )
+ {
+ g_plugins->Open();
+ CpuInitializeMess();
+ }
+}
+
+
+// Invoked by the pthread_exit or pthread_cancel.
+void SysCoreThread::OnCleanupInThread()
+{
+ Threading::DisableHiresScheduler();
+
+ if( g_plugins != NULL )
+ g_plugins->Close();
+
+ tls_coreThread = NULL;
+ _parent::OnCleanupInThread();
+}
+
diff --git a/pcsx2/System/SysThreads.cpp b/pcsx2/System/SysThreadBase.cpp
similarity index 63%
rename from pcsx2/System/SysThreads.cpp
rename to pcsx2/System/SysThreadBase.cpp
index 69edd48b2b..7d7158089b 100644
--- a/pcsx2/System/SysThreads.cpp
+++ b/pcsx2/System/SysThreadBase.cpp
@@ -14,18 +14,9 @@
*/
#include "PrecompiledHeader.h"
-#include "Common.h"
+
#include "System.h"
#include "SysThreads.h"
-#include "SaveState.h"
-#include "Elfheader.h"
-#include "Plugins.h"
-
-#include "R5900.h"
-#include "R3000A.h"
-#include "VUmicro.h"
-
-static __threadlocal SysCoreThread* tls_coreThread = NULL;
// --------------------------------------------------------------------------------------
// SysThreadBase *External Thread* Implementations
@@ -267,7 +258,7 @@ void SysThreadBase::OnCleanupInThread()
void SysThreadBase::OnSuspendInThread() {}
void SysThreadBase::OnResumeInThread( bool isSuspended ) {}
-void SysThreadBase::StateCheckInThread( bool isCancelable )
+void SysThreadBase::StateCheckInThread()
{
switch( m_ExecMode )
{
@@ -283,8 +274,7 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
case ExecMode_Opened:
// Yup, need this a second time. Variable state could have changed while we
// were trying to acquire the lock above.
- if( isCancelable )
- TestCancel();
+ TestCancel();
break;
// -------------------------------------
@@ -324,197 +314,3 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
jNO_DEFAULT;
}
}
-
-// --------------------------------------------------------------------------------------
-// EECoreThread *External Thread* Implementations
-// (Called from outside the context of this thread)
-// --------------------------------------------------------------------------------------
-
-SysCoreThread::SysCoreThread() :
- m_resetRecompilers( true )
-, m_resetProfilers( true )
-, m_resetVirtualMachine( true )
-, m_hasValidState( false )
-{
- m_name = L"EE Core";
-}
-
-SysCoreThread::~SysCoreThread() throw()
-{
- SysCoreThread::Cancel();
-}
-
-extern bool CoreCancelDamnit;
-void SysCoreThread::Cancel( bool isBlocking )
-{
- CoreCancelDamnit = true;
- _parent::Cancel();
-}
-
-void SysCoreThread::Start()
-{
- if( g_plugins == NULL ) return;
- g_plugins->Init();
- CoreCancelDamnit = false; // belongs in OnStart actually, but I'm tired :P
- _parent::Start();
-}
-
-// Resumes the core execution state, or does nothing is the core is already running. If
-// settings were changed, resets will be performed as needed and emulation state resumed from
-// memory savestates.
-//
-// Exceptions (can occur on first call only):
-// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
-// on the first time the thread is resumed from it's initial idle state)
-// ThreadCreationError - Insufficient system resources to create thread.
-//
-void SysCoreThread::OnResumeReady()
-{
- if( m_resetVirtualMachine )
- {
- cpuReset();
- m_resetVirtualMachine = false;
- m_hasValidState = false;
- }
-
- if( m_resetRecompilers || m_resetProfilers || !m_hasValidState )
- {
- SysClearExecutionCache();
- memBindConditionalHandlers();
- m_resetRecompilers = false;
- m_resetProfilers = false;
- }
-}
-
-void SysCoreThread::Reset()
-{
- Suspend();
- m_resetVirtualMachine = true;
-}
-
-// 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
-// is determined by comparing the current settings against the new settings.
-void SysCoreThread::ApplySettings( const Pcsx2Config& src )
-{
- if( src == EmuConfig ) return;
-
- const bool resumeWhenDone = Suspend();
-
- m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
- m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
-
- const_cast(EmuConfig) = src;
-
- if( resumeWhenDone ) Resume();
-}
-
-// --------------------------------------------------------------------------------------
-// EECoreThread *Worker* Implementations
-// (Called from the context of this thread only)
-// --------------------------------------------------------------------------------------
-SysCoreThread& SysCoreThread::Get()
-{
- pxAssertMsg( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." );
- return *tls_coreThread;
-}
-
-void SysCoreThread::CpuInitializeMess()
-{
- if( m_hasValidState ) return;
-
- wxString elf_file;
- if( EmuConfig.SkipBiosSplash )
- {
- // Fetch the ELF filename and CD type from the CDVD provider.
- wxString ename;
- 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:
- throw Exception::RuntimeError( wxLt("Fast Boot failed: PCSX2 does not support emulation of PS1 games.") );
-
- case 2:
- // PS2 game. Valid!
- elf_file = ename;
- break;
-
- 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 );
- }
- m_hasValidState = true;
-}
-
-// special macro which disables inlining on functions that require their own function stackframe.
-// This is due to how Win32 handles structured exception handling. Linux uses signals instead
-// of SEH, and so these functions can be inlined.
-#ifdef _WIN32
-# define __unique_stackframe __noinline
-#else
-# define __unique_stackframe
-#endif
-
-// On Win32 this function invokes SEH, which requires it be in a function all by itself
-// with inlining disabled.
-__unique_stackframe
-void SysCoreThread::CpuExecute()
-{
- PCSX2_MEM_PROTECT_BEGIN();
- Cpu->Execute();
- PCSX2_MEM_PROTECT_END();
-}
-
-void SysCoreThread::ExecuteTaskInThread()
-{
- Threading::EnableHiresScheduler();
-
- tls_coreThread = this;
-
- m_sem_event.WaitRaw();
- StateCheckInThread();
- CpuExecute();
-}
-
-void SysCoreThread::OnSuspendInThread()
-{
- if( g_plugins != NULL )
- g_plugins->Close();
-}
-
-void SysCoreThread::OnResumeInThread( bool isSuspended )
-{
- if( isSuspended && g_plugins != NULL )
- {
- g_plugins->Open();
- CpuInitializeMess();
- }
-}
-
-
-// Invoked by the pthread_exit or pthread_cancel
-void SysCoreThread::OnCleanupInThread()
-{
- Threading::DisableHiresScheduler();
-
- if( g_plugins != NULL )
- g_plugins->Close();
-
- _parent::OnCleanupInThread();
-}
-
diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h
index 97e47bc19a..8d7dd0d8e0 100644
--- a/pcsx2/System/SysThreads.h
+++ b/pcsx2/System/SysThreads.h
@@ -19,6 +19,25 @@
using namespace Threading;
+
+#if !PCSX2_SEH
+# include
+
+ // Platforms without SEH need to use SetJmp / LongJmp to deal with exiting the recompiled
+ // code execution pipelines in an efficient manner, since standard C++ exceptions cannot
+ // unwind across dynamically recompiled code.
+
+ enum
+ {
+ SetJmp_Dispatcher = 1,
+ SetJmp_Exit,
+ };
+
+#endif
+
+// --------------------------------------------------------------------------------------
+// ISysThread
+// --------------------------------------------------------------------------------------
class ISysThread : public virtual IThread
{
public:
@@ -30,6 +49,9 @@ public:
virtual void Resume() {}
};
+// --------------------------------------------------------------------------------------
+// SysThreadBase
+// --------------------------------------------------------------------------------------
class SysThreadBase : public PersistentThread, public virtual ISysThread
{
@@ -93,7 +115,7 @@ public:
return m_ExecMode > ExecMode_Closed;
}
- bool HasPendingStateChangeRequest()
+ bool HasPendingStateChangeRequest() const
{
ExecutionMode mode = m_ExecMode;
return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing);
@@ -108,8 +130,6 @@ public:
virtual void Resume();
virtual bool Pause();
- virtual void StateCheckInThread( bool isCancelable = true );
-
protected:
virtual void OnStart();
@@ -118,6 +138,7 @@ protected:
// Resume() has a lot of checks and balances to prevent re-entrance and race conditions.
virtual void OnResumeReady() {}
+ virtual void StateCheckInThread();
virtual void OnCleanupInThread();
virtual void OnStartInThread();
@@ -147,8 +168,10 @@ protected:
virtual void OnResumeInThread( bool isSuspended )=0;
};
+
+
// --------------------------------------------------------------------------------------
-// EECoreThread class
+// SysCoreThread class
// --------------------------------------------------------------------------------------
class SysCoreThread : public SysThreadBase
{
@@ -160,6 +183,9 @@ protected:
bool m_resetVirtualMachine;
bool m_hasValidState;
+ // Used by SETJMP only, but ifdef'ing it out clutters up the code.
+ bool m_CoreCancelDamnit;
+
public:
static SysCoreThread& Get();
@@ -177,9 +203,12 @@ public:
return m_hasValidState;
}
+ bool HasPendingStateChangeRequest() const;
+
+ virtual void StateCheckInThread();
+
protected:
void CpuInitializeMess();
- void CpuExecute();
virtual void Start();
virtual void OnSuspendInThread();
@@ -187,6 +216,8 @@ protected:
virtual void OnResumeInThread( bool IsSuspended );
virtual void OnCleanupInThread();
virtual void ExecuteTaskInThread();
+
+ void _StateCheckThrows();
};
extern int sys_resume_lock;
diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h
index fd64db75f1..f50a86e168 100644
--- a/pcsx2/gui/App.h
+++ b/pcsx2/gui/App.h
@@ -21,7 +21,7 @@
#include
#include
-#include "Utilities/Listeners.h"
+#include "Utilities/EventSource.h"
#include "IniInterface.h"
//class IniInterface;
@@ -490,7 +490,7 @@ public:
virtual bool Suspend( bool isBlocking=true );
virtual void Resume();
- virtual void StateCheckInThread( bool isCancelable=true );
+ virtual void StateCheckInThread();
virtual void ApplySettings( const Pcsx2Config& src );
protected:
diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp
index a646a70f5e..ce8b5f2f03 100644
--- a/pcsx2/gui/AppCoreThread.cpp
+++ b/pcsx2/gui/AppCoreThread.cpp
@@ -128,9 +128,9 @@ void AppCoreThread::OnCleanupInThread()
extern int TranslateGDKtoWXK( u32 keysym );
#endif
-void AppCoreThread::StateCheckInThread( bool isCancelable )
+void AppCoreThread::StateCheckInThread()
{
- _parent::StateCheckInThread( isCancelable );
+ _parent::StateCheckInThread();
if( !pxAssert(g_plugins!=NULL) ) return;
const keyEvent* ev = PADkeyEvent();
diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp
index d121884c9f..944cef4239 100644
--- a/pcsx2/gui/AppInit.cpp
+++ b/pcsx2/gui/AppInit.cpp
@@ -243,9 +243,8 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
- Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
-
- Connect( pxEVT_CoreThreadStatus, wxCommandEventHandler( Pcsx2App::OnCoreThreadStatus ) );
+ Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
+ Connect( pxEVT_CoreThreadStatus, wxCommandEventHandler( Pcsx2App::OnCoreThreadStatus ) );
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp
index b05714a1ad..6087c1038c 100644
--- a/pcsx2/gui/AppMain.cpp
+++ b/pcsx2/gui/AppMain.cpp
@@ -210,8 +210,7 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const
{
- try
- {
+ try {
(handler->*func)(event);
}
// ----------------------------------------------------------------------------
@@ -247,7 +246,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
}
}
-static void OnStateSaveFinished( void* obj, const wxCommandEvent& evt )
+static void __fastcall OnStateSaveFinished( void* obj, wxCommandEvent& evt )
{
if( evt.GetInt() == CoreStatus_Resumed )
{
@@ -290,7 +289,8 @@ bool Pcsx2App::PrepForExit( bool canCancel )
}
}*/
- m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting );
+ AppEventType toSend = AppStatus_Exiting;
+ m_evtsrc_AppStatus.Dispatch( toSend );
CleanupMess();
return true;
@@ -354,7 +354,8 @@ void AppApplySettings( const AppConfig* oldconf, bool saveOnSuccess )
}
}
- sApp.Source_SettingsApplied().Dispatch( 0 );
+ int toSend = 0;
+ sApp.Source_SettingsApplied().Dispatch( toSend );
CoreThread.ApplySettings( g_Conf->EmuOptions );
if( resume )
diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp
index 9732e54a31..8a8e0d50f3 100644
--- a/pcsx2/gui/MainFrame.cpp
+++ b/pcsx2/gui/MainFrame.cpp
@@ -255,34 +255,33 @@ void MainEmuFrame::InitLogBoxPosition( AppConfig::ConsoleLogOptions& conf )
}
}
-void MainEmuFrame::OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt )
+void __fastcall MainEmuFrame::OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt )
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplyCoreStatus();
}
-void MainEmuFrame::OnCorePluginStatusChanged( void* obj, const wxCommandEvent& evt )
+void __fastcall MainEmuFrame::OnCorePluginStatusChanged( void* obj, wxCommandEvent& evt )
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplyCoreStatus();
}
-void MainEmuFrame::OnSettingsApplied( void* obj, const int& evt )
+void __fastcall MainEmuFrame::OnSettingsApplied( void* obj, int& evt )
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplySettings();
}
-void MainEmuFrame::OnSettingsLoadSave( void* obj, const IniInterface& evt )
+void __fastcall MainEmuFrame::OnSettingsLoadSave( void* obj, IniInterface& evt )
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
- // FIXME: Evil const cast hack!
- mframe->LoadSaveRecentIsoList( const_cast(evt) );
+ mframe->LoadSaveRecentIsoList( evt );
}
// ------------------------------------------------------------------------
diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h
index 3ced855c21..c75bc1a42d 100644
--- a/pcsx2/gui/MainFrame.h
+++ b/pcsx2/gui/MainFrame.h
@@ -85,10 +85,10 @@ public:
void ReloadRecentLists();
protected:
- static void OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt );
- static void OnCorePluginStatusChanged( void* obj, const wxCommandEvent& evt );
- static void OnSettingsApplied( void* obj, const int& evt );
- static void OnSettingsLoadSave( void* obj, const IniInterface& evt );
+ static void __fastcall OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt );
+ static void __fastcall OnCorePluginStatusChanged( void* obj, wxCommandEvent& evt );
+ static void __fastcall OnSettingsApplied( void* obj, int& evt );
+ static void __fastcall OnSettingsLoadSave( void* obj, IniInterface& evt );
void LoadSaveRecentIsoList( IniInterface& conf );
void ApplySettings();
diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
index 35148176f9..15a71fff90 100644
--- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
+++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
@@ -380,16 +380,16 @@
RelativePath="..\..\Stats.cpp"
>
+
+
-
-
+
+
@@ -479,6 +483,10 @@
RelativePath="..\..\System.h"
>
+
+
#include "Common.h"
+#include "System/PageFaultSource.h"
int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
{
- const _EXCEPTION_RECORD& ExceptionRecord = *eps->ExceptionRecord;
+ if( eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION )
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ PageFaultInfo info( (uptr)eps->ExceptionRecord->ExceptionInformation[1] );
+
+ Source_PageFault.DispatchException( info );
+
+ if( info.handled ) return EXCEPTION_CONTINUE_EXECUTION;
- if (ExceptionRecord.ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
- {
- return EXCEPTION_CONTINUE_SEARCH;
- }
-
- // get bad virtual address
- u32 offset = (u8*)ExceptionRecord.ExceptionInformation[1]-psM;
-
- if (offset>=Ps2MemSize::Base)
- return EXCEPTION_CONTINUE_SEARCH;
-
- mmap_ClearCpuBlock( offset );
-
- return EXCEPTION_CONTINUE_EXECUTION;
+ return EXCEPTION_CONTINUE_SEARCH;
}
+
+void InstallSignalHandler()
+{
+ // NOP on Win32 systems -- we use __try{} __except{} instead.
+}
\ No newline at end of file
diff --git a/pcsx2/x86/iR5900.h b/pcsx2/x86/iR5900.h
index 9a07ddefd0..e9d118e158 100644
--- a/pcsx2/x86/iR5900.h
+++ b/pcsx2/x86/iR5900.h
@@ -22,11 +22,8 @@
#include "VU.h"
#include "iCore.h"
-#define PC_GETBLOCK(x) PC_GETBLOCK_(x, recLUT)
-
extern u32 pc;
extern int branch;
-extern uptr recLUT[];
extern u32 maxrecmem;
extern u32 pc; // recompiler pc (also used by the SuperVU! .. why? (air))
diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp
index 656f88a0fa..2e1689ffa9 100644
--- a/pcsx2/x86/ix86-32/iR5900-32.cpp
+++ b/pcsx2/x86/ix86-32/iR5900-32.cpp
@@ -19,42 +19,23 @@
#include "Memory.h"
#include "R5900OpcodeTables.h"
#include "iR5900.h"
-#include "iR5900AritImm.h"
-#include "iR5900Arit.h"
-#include "iR5900MultDiv.h"
-#include "iR5900Shift.h"
-#include "iR5900Branch.h"
-#include "iR5900Jump.h"
-#include "iR5900LoadStore.h"
-#include "iR5900Move.h"
#include "BaseblockEx.h"
-
-#include "iMMI.h"
-#include "iFPU.h"
-#include "iCOP0.h"
-#include "sVU_Micro.h"
-#include "VU.h"
-#include "VUmicro.h"
-
-#include "sVU_zerorec.h"
#include "vtlb.h"
-
#include "SamplProf.h"
-
-#include "NakedAsm.h"
#include "Dump.h"
+#include "SysThreads.h"
+#include "GS.h"
+
using namespace x86Emitter;
using namespace R5900;
-// used to disable register freezing during cpuBranchTests (registers
-// are safe then since they've been completely flushed)
-bool g_EEFreezeRegs = false;
+#define PC_GETBLOCK(x) PC_GETBLOCK_(x, recLUT)
u32 maxrecmem = 0;
-uptr recLUT[0x10000];
-uptr hwLUT[0x10000];
+static uptr recLUT[0x10000];
+static uptr hwLUT[0x10000];
#define HWADDR(mem) (hwLUT[mem >> 16] + (mem))
@@ -584,13 +565,12 @@ struct ManualPageTracking
static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12];
static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12];
-volatile bool eeRecIsReset = false;
+static bool eeRecIsReset = false;
////////////////////////////////////////////////////
void recResetEE( void )
{
Console.Status( "Issuing EE/iR5900-32 Recompiler Reset [mem/structure cleanup]" );
- eeRecIsReset = true;
maxrecmem = 0;
@@ -652,6 +632,7 @@ void recResetEE( void )
branch = 0;
SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR);
+ eeRecIsReset = true;
}
static void recShutdown( void )
@@ -673,81 +654,99 @@ void recStep( void )
{
}
-#ifndef PCSX2_SEH
+static jmp_buf m_SetJmp_StateCheck;
-// <--- setjmp/longjmp model <---
-
-#include "GS.h"
-#include "System/SysThreads.h"
-
-static void StateThreadCheck_LongJmp()
+static void recCheckExecutionState()
{
- setjmp( SetJmp_StateCheck );
-
- int oldstate;
-
- // Important! Most of the console logging and such has cancel points in it. This is great
- // in Windows, where SEH lets us safely kill a thread from anywhere we want. This is bad
- // in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing
- // of the cancelstate here!
-
- pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
- mtgsThread.RethrowException();
+#if PCSX2_SEH
SysCoreThread::Get().StateCheckInThread();
- pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
-}
-
-static void recExecute()
-{
- StateThreadCheck_LongJmp();
-
- switch( setjmp( SetJmp_RecExecute ) )
- {
- case SetJmp_Exit: break;
-
- case 0:
- case SetJmp_Dispatcher:
-
- // Typically the Dispatcher is invoked from the EventTest code, which clears
- // the FreezeRegs flag, so always be sure to reset it here:
- g_EEFreezeRegs = true;
-
- while( true )
- EnterRecompiledCode();
- break;
- }
-
- g_EEFreezeRegs = false;
-}
-
+
+ if( eeRecIsReset )
+ throw Exception::ForceDispatcherReg();
#else
-// ---> SEH Model --->
+ // Without SEH we'll need to hop to a safehouse point outside the scope of recompiled
+ // code. C++ exceptions can't cross the mighty chasm in the stackframe that the recompiler
+ // creates. However, the longjump is slow so we only want to do one when absolutely
+ // necessary:
+
+ pxAssert( !eeRecIsReset ); // should only be changed during suspended thread states
+ if( SysCoreThread::Get().HasPendingStateChangeRequest() )
+ {
+ longjmp( m_SetJmp_StateCheck, SetJmp_Dispatcher );
+ }
+
+#endif
+}
static void recExecute()
{
// Implementation Notes:
// [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired!
- try
- {
+#if PCSX2_SEH
+ try {
while( true )
{
- // Typically the Dispatcher is invoked from the EventTest code, which clears
- // the FreezeRegs flag, so always be sure to reset it here:
+ eeRecIsReset = false;
g_EEFreezeRegs = true;
try {
EnterRecompiledCode();
}
catch( Exception::ForceDispatcherReg& ) { }
+
}
}
- catch( Exception::ExitRecExecute& ) { }
+ catch( Exception::ExitRecExecute& ) {}
- g_EEFreezeRegs = false;
-}
+#else
+
+ switch( setjmp( m_SetJmp_StateCheck ) )
+ {
+ case 0: // first run, fall through to Dispatcher
+ case SetJmp_Dispatcher:
+ while( true )
+ {
+ int oldstate;
+
+ // Important! Most of the console logging and such has cancel points in it. This is great
+ // in Windows, where SEH lets us safely kill a thread from anywhere we want. This is bad
+ // in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing
+ // of the cancelstate here!
+
+ pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
+ SysCoreThread::Get().StateCheckInThread();
+ pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
+
+ eeRecIsReset = false;
+
+ #ifdef _WIN32
+ __try {
+ #endif
+
+ EnterRecompiledCode();
+
+ #ifdef _WIN32
+ } __finally
+ {
+ // This assertion is designed to help me troubleshoot the setjmp behavior from Win32.
+ // If the recompiler throws an unhandled SEH exception with SEH support disabled (which
+ // is typically a pthread_cancel) then this will fire and let me know.
+
+ // FIXME: Doesn't work because SEH is remarkably clever and executes the _finally block
+ // even when I use longjmp to restart the loop. Maybe a workaround exists? :/
+
+ //pxFailDev( "Recompiler threw an SEH exception with SEH disabled; possibly due to pthread_cancel." );
+ }
+ #endif
+ }
+ break;
+
+ case SetJmp_Exit: break;
+ }
#endif
+}
////////////////////////////////////////////////////
void R5900::Dynarec::OpcodeImpl::recSYSCALL( void )
@@ -856,17 +855,12 @@ void recClear(u32 addr, u32 size)
}
-#ifndef PCSX2_SEH
- jmp_buf SetJmp_RecExecute;
- jmp_buf SetJmp_StateCheck;
-#endif
-
-static void ExitRec()
+static void recExitExecution()
{
-#ifdef PCSX2_SEH
+#if PCSX2_SEH
throw Exception::ExitRecExecute();
#else
- longjmp( SetJmp_RecExecute, SetJmp_Exit );
+ longjmp( m_SetJmp_StateCheck, SetJmp_Exit );
#endif
}
@@ -875,22 +869,29 @@ void CheckForBIOSEnd()
{
xMOV( eax, &cpuRegs.pc );
- /*xCMP( eax, 0x00200008 );
- xJE(ExitRec);
+ if( IsDevBuild )
+ {
+ // Using CALL retains stacktrace info, useful for debugging.
- xCMP( eax, 0x00100008 );
- xJE(ExitRec);*/
+ xCMP( eax, 0x00200008 );
+ xForwardJE8 CallExitRec;
- xCMP( eax, 0x00200008 );
- xForwardJE8 CallExitRec;
+ xCMP( eax, 0x00100008 );
+ xForwardJNE8 SkipExitRec;
- xCMP( eax, 0x00100008 );
- xForwardJNE8 SkipExitRec;
+ CallExitRec.SetTarget();
+ xCALL( recExitExecution );
- CallExitRec.SetTarget();
- xCALL( ExitRec );
+ SkipExitRec.SetTarget();
+ }
+ else
+ {
+ xCMP( eax, 0x00200008 );
+ xJE(recExitExecution);
- SkipExitRec.SetTarget();
+ xCMP( eax, 0x00100008 );
+ xJE(recExitExecution);
+ }
}
static int *s_pCode;
@@ -1765,11 +1766,13 @@ StartRecomp:
s_pCurBlockEx = NULL;
}
-R5900cpu recCpu = {
+R5900cpu recCpu =
+{
recAlloc,
recResetEE,
recStep,
recExecute,
+ recCheckExecutionState,
recClear,
recShutdown
};