Reorganized the exception/signal handlers, setjmp/longjmp, and SysCoreThread stuff:

* Exception/Signal handling now uses an EventSource, so that multiple handlers can be registered.  This is in preparation for (eventual) more complete MIPS TLB support in the VTLB memory model.
 * Improved code isolation, so that recompiler-specific code is primarily in iR5900-32.cpp (cleans up Counters.cpp and SysCoreThread.cpp)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2063 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-10-23 20:24:59 +00:00
parent cdb2d8f276
commit efc35405f8
33 changed files with 1263 additions and 1091 deletions

View File

@ -84,10 +84,10 @@
<Unit filename="../../../3rdparty/google/type_traits.h" /> <Unit filename="../../../3rdparty/google/type_traits.h" />
<Unit filename="../../include/Utilities/Console.h" /> <Unit filename="../../include/Utilities/Console.h" />
<Unit filename="../../include/Utilities/Dependencies.h" /> <Unit filename="../../include/Utilities/Dependencies.h" />
<Unit filename="../../include/Utilities/EventSource.h" />
<Unit filename="../../include/Utilities/Exceptions.h" /> <Unit filename="../../include/Utilities/Exceptions.h" />
<Unit filename="../../include/Utilities/General.h" /> <Unit filename="../../include/Utilities/General.h" />
<Unit filename="../../include/Utilities/HashMap.h" /> <Unit filename="../../include/Utilities/HashMap.h" />
<Unit filename="../../include/Utilities/Listeners.h" />
<Unit filename="../../include/Utilities/MemcpyFast.h" /> <Unit filename="../../include/Utilities/MemcpyFast.h" />
<Unit filename="../../include/Utilities/Path.h" /> <Unit filename="../../include/Utilities/Path.h" />
<Unit filename="../../include/Utilities/RedtapeWindows.h" /> <Unit filename="../../include/Utilities/RedtapeWindows.h" />

View File

@ -425,6 +425,10 @@
RelativePath="..\..\include\Utilities\Dependencies.h" RelativePath="..\..\include\Utilities\Dependencies.h"
> >
</File> </File>
<File
RelativePath="..\..\include\Utilities\EventSource.h"
>
</File>
<File <File
RelativePath="..\..\include\Utilities\Exceptions.h" RelativePath="..\..\include\Utilities\Exceptions.h"
> >
@ -441,10 +445,6 @@
RelativePath="..\..\include\intrin_x86.h" RelativePath="..\..\include\intrin_x86.h"
> >
</File> </File>
<File
RelativePath="..\..\include\Utilities\Listeners.h"
>
</File>
<File <File
RelativePath="..\..\include\Utilities\lnx_memzero.h" RelativePath="..\..\include\Utilities\lnx_memzero.h"
> >

View File

@ -16,12 +16,17 @@
#pragma once #pragma once
#include <list> #include <list>
#include <wx/event.h>
class wxCommandEvent;
// --------------------------------------------------------------------------------------
// EventListener< typename EvtType >
// --------------------------------------------------------------------------------------
template< typename EvtType > template< typename EvtType >
struct EventListener struct EventListener
{ {
typedef void FuncType( void* object, const EvtType& evt ); typedef void __fastcall FuncType( void* object, EvtType& evt );
void* object; void* object;
FuncType* OnEvent; FuncType* OnEvent;
@ -49,6 +54,10 @@ struct EventListener
} }
}; };
// --------------------------------------------------------------------------------------
// EventSource< template EvtType >
// --------------------------------------------------------------------------------------
template< typename EvtType > template< typename EvtType >
class EventSource class EventSource
{ {
@ -58,23 +67,38 @@ public:
typedef typename ListenerList::iterator Handle; typedef typename ListenerList::iterator Handle;
protected: protected:
typedef typename ListenerList::const_iterator ConstIterator;
ListenerList m_listeners; 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: public:
EventSource() : m_cache_valid( false )
{
}
virtual ~EventSource() throw() {} virtual ~EventSource() throw() {}
virtual void Remove( const ListenerType& listener ) virtual void Remove( const ListenerType& listener )
{ {
m_cache_valid = false;
m_listeners.remove( listener ); m_listeners.remove( listener );
} }
virtual void Remove( const Handle& listenerHandle ) virtual void Remove( const Handle& listenerHandle )
{ {
m_cache_valid = false;
m_listeners.erase( listenerHandle ); m_listeners.erase( listenerHandle );
} }
virtual Handle AddFast( const ListenerType& listener ) virtual Handle AddFast( const ListenerType& listener )
{ {
m_cache_valid = false;
m_listeners.push_front( listener ); m_listeners.push_front( listener );
return m_listeners.begin(); return m_listeners.begin();
} }
@ -93,14 +117,21 @@ public:
Remove( ListenerType( objhandle, fnptr ) ); 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: if( !m_cache_valid )
{
m_cache_copy = m_listeners;
m_cache_valid = true;
}
ListenerList list( m_listeners ); _DispatchRaw( m_cache_copy.begin(), m_cache_copy.end(), evt );
}
typename ListenerList::const_iterator iter = list.begin(); protected:
while( iter != list.end() ) __forceinline void _DispatchRaw( ConstIterator& iter, const ConstIterator& iend, EvtType& evt )
{
while( iter != iend )
{ {
try try
{ {
@ -118,14 +149,15 @@ public:
++iter; ++iter;
} }
} }
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// EventListenerBinding // EventListenerBinding< typename EvtType ?
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Encapsulated event listener binding, provides the "benefits" of object unwinding. // Encapsulated event listener binding, provides the "benefits" of object unwinding.
// //
template< typename EvtType = wxCommandEvent > template< typename EvtType >
class EventListenerBinding class EventListenerBinding
{ {
public: public:
@ -211,9 +243,7 @@ public:
template< typename EvtType > template< typename EvtType >
void EventSource<EvtType>::RemoveObject( const void* object ) void EventSource<EvtType>::RemoveObject( const void* object )
{ {
// Iso C++ rules regarding temporaries, specifically that non-const temporaries are disallowed, m_cache_valid = false;
// also removes any actual convenience factor that unary predicates may have actually offered. >_<
m_listeners.remove_if( PredicatesAreTheThingsOfNightmares<EvtType>( object ) ); m_listeners.remove_if( PredicatesAreTheThingsOfNightmares<EvtType>( object ) );
} }

View File

@ -280,6 +280,7 @@ namespace Threading
bool IsRunning() const; bool IsRunning() const;
bool IsSelf() const; bool IsSelf() const;
wxString GetName() const; wxString GetName() const;
bool HasPendingException() const { return !!m_except; }
protected: protected:
// Extending classes should always implement your own OnStart(), which is called by // Extending classes should always implement your own OnStart(), which is called by

View File

@ -142,6 +142,8 @@ void Threading::PersistentThread::Start()
Detach(); // clean up previous thread handle, if one exists. Detach(); // clean up previous thread handle, if one exists.
OnStart(); OnStart();
m_except = NULL;
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
throw Exception::ThreadCreationError(); throw Exception::ThreadCreationError();
} }

View File

@ -20,6 +20,10 @@
// use Freeze/Thaw in MMXRegisters, XMMRegisters, & Registers. // 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 // MMX Register Freezing
// //

View File

@ -440,8 +440,6 @@ __forceinline void rcntUpdate_hScanline()
} }
} }
bool CoreCancelDamnit = false;
__forceinline void rcntUpdate_vSync() __forceinline void rcntUpdate_vSync()
{ {
s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle); s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle);
@ -449,28 +447,7 @@ __forceinline void rcntUpdate_vSync()
if (vsyncCounter.Mode == MODE_VSYNC) if (vsyncCounter.Mode == MODE_VSYNC)
{ {
eeRecIsReset = false; Cpu->CheckExecutionState();
#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
}
VSyncEnd(vsyncCounter.sCycle); VSyncEnd(vsyncCounter.sCycle);

View File

@ -123,6 +123,8 @@ public:
mtgsThreadObject(); mtgsThreadObject();
virtual ~mtgsThreadObject() throw(); virtual ~mtgsThreadObject() throw();
static mtgsThreadObject& Get();
// 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.
void WaitGS(); void WaitGS();

View File

@ -19,6 +19,7 @@
#include "Common.h" #include "Common.h"
#include "R5900.h" #include "R5900.h"
#include "R5900OpcodeTables.h" #include "R5900OpcodeTables.h"
#include "System/SysThreads.h"
#include <float.h> #include <float.h>
@ -30,6 +31,8 @@ static int branch2 = 0;
static u32 cpuBlockCycles = 0; // 3 bit fixed point version of cycle count static u32 cpuBlockCycles = 0; // 3 bit fixed point version of cycle count
static std::string disOut; static std::string disOut;
static void intEventTest();
// These macros are used to assemble the repassembler functions // These macros are used to assemble the repassembler functions
static void debugI() static void debugI()
@ -356,24 +359,24 @@ void JALR()
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
void intAlloc() static void intAlloc()
{ {
// fixme : detect cpu for use the optimize asm code // fixme : detect cpu for use the optimize asm code
} }
void intReset() static void intReset()
{ {
cpuRegs.branch = 0; cpuRegs.branch = 0;
branch2 = 0; branch2 = 0;
} }
void intEventTest() static void intEventTest()
{ {
// Perform counters, ints, and IOP updates: // Perform counters, ints, and IOP updates:
_cpuBranchTest_Shared(); _cpuBranchTest_Shared();
} }
void intExecute() static void intExecute()
{ {
g_EEFreezeRegs = false; g_EEFreezeRegs = false;
@ -386,6 +389,11 @@ void intExecute()
} }
} }
static void intCheckExecutionState()
{
SysCoreThread::Get().StateCheckInThread();
}
static void intStep() static void intStep()
{ {
g_EEFreezeRegs = false; g_EEFreezeRegs = false;
@ -404,6 +412,7 @@ R5900cpu intCpu = {
intReset, intReset,
intStep, intStep,
intExecute, intExecute,
intCheckExecutionState,
intClear, intClear,
intShutdown intShutdown
}; };

View File

@ -23,36 +23,34 @@ extern void SignalExit(int sig);
static const uptr m_pagemask = getpagesize()-1; 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; struct sigaction sa;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO; sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = &SysPageFaultExceptionFilter; sa.sa_sigaction = SysPageFaultSignalFilter;
sigaction(SIGSEGV, &sa, NULL); sigaction(SIGSEGV, &sa, NULL);
} }
void ReleaseLinuxExceptionHandler() void NTFS_CompressFile( const wxString& file, bool compressStatus=true ) {}
{
// 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 );
}

View File

@ -236,8 +236,10 @@
<Unit filename="../StringUtils.h" /> <Unit filename="../StringUtils.h" />
<Unit filename="../System.cpp" /> <Unit filename="../System.cpp" />
<Unit filename="../System.h" /> <Unit filename="../System.h" />
<Unit filename="../System/SysThreads.cpp" /> <Unit filename="../System/PageFaultSource.h" />
<Unit filename="../System/SysThreads.h" /> <Unit filename="../System/SysCoreThread.cpp" />
<Unit filename="../System/SysThreadBase.cpp" />
<Unit filename="../System/SysThreads.h" />
<Unit filename="../Tags.h" /> <Unit filename="../Tags.h" />
<Unit filename="../Utilities/AsciiFile.h" /> <Unit filename="../Utilities/AsciiFile.h" />
<Unit filename="../Utilities/FileUtils.cpp" /> <Unit filename="../Utilities/FileUtils.cpp" />

View File

@ -86,6 +86,14 @@ extern bool renderswitch;
std::list<uint> ringposStack; std::list<uint> ringposStack;
#endif #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() : mtgsThreadObject::mtgsThreadObject() :
SysThreadBase() SysThreadBase()
, m_RingPos( 0 ) , m_RingPos( 0 )
@ -162,8 +170,8 @@ void mtgsThreadObject::PostVsyncEnd( bool updategs )
if( m_WritePos == volatize( m_RingPos ) ) if( m_WritePos == volatize( m_RingPos ) )
{ {
// MTGS ringbuffer is empty, but we still have queued frames in the counter? Ouch! // MTGS ringbuffer is empty, but we still have queued frames in the counter? Ouch!
Console.Error( "MTGS > Queued framecount mismatch = %d", m_QueuedFrames ); int count = AtomicExchange( m_QueuedFrames, 0 );
m_QueuedFrames = 0; Console.Error( "MTGS > Queued framecount mismatch = %d", count );
break; break;
} }
Threading::Sleep( 2 ); // Sleep off quite a bit of time, since we're obviously *waaay* ahead. 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() void mtgsThreadObject::ExecuteTaskInThread()
{ {
tls_mtgsThread = this;
#ifdef RINGBUF_DEBUG_STACK #ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd; PacketTagType prevCmd;
#endif #endif
while( true ) while( true )
{ {
m_sem_event.WaitRaw(); // ... because this does a cancel test itself.. // Performance note: Both of these perform cancellation tests, but pthread_testcancel
StateCheckInThread( false ); // false disables cancel test here! // 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; 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. // stall for a bit to let the MainThread have time to update the g_pGSWritePos.
m_lock_RingRestart.Wait(); m_lock_RingRestart.Wait();
StateCheckInThread();
StateCheckInThread( false ); // disable cancel since the above locks are cancelable already
continue; continue;
case GS_RINGTYPE_P1: case GS_RINGTYPE_P1:
@ -427,6 +440,7 @@ void mtgsThreadObject::OnResumeInThread( bool isSuspended )
void mtgsThreadObject::OnCleanupInThread() void mtgsThreadObject::OnCleanupInThread()
{ {
ClosePlugin(); ClosePlugin();
tls_mtgsThread = NULL;
_parent::OnCleanupInThread(); _parent::OnCleanupInThread();
} }

View File

@ -48,7 +48,7 @@ BIOS
#include "VUmicro.h" #include "VUmicro.h"
#include "GS.h" #include "GS.h"
#include "IPU/IPU.h" #include "IPU/IPU.h"
#include "AppConfig.h" #include "System/PageFaultSource.h"
#ifdef ENABLECACHE #ifdef ENABLECACHE
@ -574,6 +574,8 @@ void memClearPageAddr(u32 vaddr)
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// PS2 Memory Init / Reset / Shutdown // PS2 Memory Init / Reset / Shutdown
static void __fastcall mmap_OnPageFault( void* basemem, PageFaultInfo& info );
static const uint m_allMemSize = static const uint m_allMemSize =
Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2 + Ps2MemSize::ERom + Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2 + Ps2MemSize::ERom +
Ps2MemSize::Base + Ps2MemSize::Hardware + Ps2MemSize::Scratch; Ps2MemSize::Base + Ps2MemSize::Hardware + Ps2MemSize::Scratch;
@ -596,10 +598,14 @@ void memAlloc()
psER = curpos; curpos += Ps2MemSize::ERom; psER = curpos; curpos += Ps2MemSize::ERom;
psH = curpos; curpos += Ps2MemSize::Hardware; psH = curpos; curpos += Ps2MemSize::Hardware;
psS = curpos; //curpos += Ps2MemSize::Scratch; psS = curpos; //curpos += Ps2MemSize::Scratch;
Source_PageFault.Add( EventListener<PageFaultInfo>((void*)psM, mmap_OnPageFault) );
} }
void memShutdown() void memShutdown()
{ {
Source_PageFault.Remove( EventListener<PageFaultInfo>((void*)psM, mmap_OnPageFault) );
vtlb_free( m_psAllMem, m_allMemSize ); vtlb_free( m_psAllMem, m_allMemSize );
m_psAllMem = NULL; m_psAllMem = NULL;
psM = psR = psR1 = psR2 = psER = psS = psH = NULL; psM = psR = psR1 = psR2 = psER = psS = psH = NULL;
@ -623,8 +629,7 @@ void memBindConditionalHandlers()
void memReset() void memReset()
{ {
// VTLB Protection Preparations. // 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 // 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 // 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 ); HostSys::MemProtect( &psM[rampage<<12], 1, Protect_ReadOnly );
} }
// offset - offset of address relative to psM. The exception handler for the platform/host // offset - offset of address relative to psM.
// OS should ensure that only addresses within psM address space are passed. Anything else static __forceinline void mmap_ClearCpuBlock( uint offset )
// will produce undefined results (ie, crashes).
void mmap_ClearCpuBlock( uint offset )
{ {
int rampage = offset >> 12; int rampage = offset >> 12;
// Assertion: This function should never be run on a block that's already under // 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. // manual protection. Indicates a logic error in the recompiler or protection code.
jASSUME( m_PageProtectInfo[rampage].Mode != ProtMode_Manual ); pxAssertMsg( m_PageProtectInfo[rampage].Mode != ProtMode_Manual,
"Attempted to clear a block that is already under manual protection." );
//#ifndef __LINUX__ // this function is called from the signal handler
//DbgCon.WriteLn( "Manual page @ 0x%05x", m_PageProtectInfo[rampage].ReverseRamMap>>12 );
//#endif
HostSys::MemProtect( &psM[rampage<<12], 1, Protect_ReadWrite ); HostSys::MemProtect( &psM[rampage<<12], 1, Protect_ReadWrite );
m_PageProtectInfo[rampage].Mode = ProtMode_Manual; m_PageProtectInfo[rampage].Mode = ProtMode_Manual;
Cpu->Clear( m_PageProtectInfo[rampage].ReverseRamMap, 0x400 ); 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. // 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. // to ensure the EErec is also reset in conjunction with calling this function.
void mmap_ResetBlockTracking() void mmap_ResetBlockTracking()
{ {

View File

@ -134,7 +134,6 @@ extern void memMapVUmicro();
extern int mmap_GetRamPageInfo( u32 paddr ); extern int mmap_GetRamPageInfo( u32 paddr );
extern void mmap_MarkCountedRamPage( u32 paddr ); extern void mmap_MarkCountedRamPage( u32 paddr );
extern void mmap_ResetBlockTracking(); extern void mmap_ResetBlockTracking();
extern void mmap_ClearCpuBlock( uint offset );
#define memRead8 vtlb_memRead8 #define memRead8 vtlb_memRead8
#define memRead16 vtlb_memRead16 #define memRead16 vtlb_memRead16

View File

@ -34,6 +34,8 @@
#include "SPR.h" #include "SPR.h"
#include "Sif.h" #include "Sif.h"
#include "System/SysThreads.h"
#include "R5900Exceptions.h" #include "R5900Exceptions.h"
using namespace R5900; // for R5900 disasm tools using namespace R5900; // for R5900 disasm tools
@ -61,6 +63,8 @@ void cpuReset()
if( mtgsThread.IsOpen() ) if( mtgsThread.IsOpen() )
mtgsThread.WaitGS(); // GS better be done processing before we reset the EE, just in case. mtgsThread.WaitGS(); // GS better be done processing before we reset the EE, just in case.
SysClearExecutionCache();
cpuIsInitialized = true; cpuIsInitialized = true;
memReset(); memReset();
@ -560,7 +564,6 @@ void cpuExecuteBios()
Console.Status( "Executing Bios Stub..." ); Console.Status( "Executing Bios Stub..." );
PCSX2_MEM_PROTECT_BEGIN();
g_ExecBiosHack = true; g_ExecBiosHack = true;
while( cpuRegs.pc != 0x00200008 && while( cpuRegs.pc != 0x00200008 &&
cpuRegs.pc != 0x00100008 ) cpuRegs.pc != 0x00100008 )
@ -568,7 +571,6 @@ void cpuExecuteBios()
Cpu->Execute(); Cpu->Execute();
} }
g_ExecBiosHack = false; g_ExecBiosHack = false;
PCSX2_MEM_PROTECT_END();
// { // {
// FILE* f = fopen("eebios.bin", "wb"); // FILE* f = fopen("eebios.bin", "wb");

View File

@ -26,7 +26,6 @@
extern bool g_EEFreezeRegs; extern bool g_EEFreezeRegs;
extern bool g_ExecBiosHack; extern bool g_ExecBiosHack;
extern volatile bool eeRecIsReset;
namespace Exception namespace Exception
{ {
@ -256,7 +255,6 @@ extern bool eeEventTestIsActive;
extern u32 s_iLastCOP0Cycle; extern u32 s_iLastCOP0Cycle;
extern u32 s_iLastPERFCycle[2]; extern u32 s_iLastPERFCycle[2];
void intEventTest();
void intSetBranch(); void intSetBranch();
// This is a special form of the interpreter's doBranch that is run from various // This is a special form of the interpreter's doBranch that is run from various
@ -272,6 +270,7 @@ struct R5900cpu
void (*Reset)(); void (*Reset)();
void (*Step)(); void (*Step)();
void (*Execute)(); void (*Execute)();
void (*CheckExecutionState)();
void (*Clear)(u32 Addr, u32 Size); void (*Clear)(u32 Addr, u32 Size);
void (*Shutdown)(); // deallocates memory reserved by Allocate void (*Shutdown)(); // deallocates memory reserved by Allocate
}; };

View File

@ -34,7 +34,7 @@ int sys_resume_lock = 0;
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL; 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; if( (thr == NULL) || (stat != AppStatus_Exiting) ) return;
((PersistentThread*)thr)->Cancel(); ((PersistentThread*)thr)->Cancel();

View File

@ -25,8 +25,10 @@
#include "sVU_zerorec.h" // for SuperVUReset #include "sVU_zerorec.h" // for SuperVUReset
#include "R5900Exceptions.h" #include "R5900Exceptions.h"
#include "CDVD/CDVD.h" #include "CDVD/CDVD.h"
#include "System/PageFaultSource.h"
SrcType_PageFault Source_PageFault;
#if _MSC_VER #if _MSC_VER
# include "svnrev.h" # include "svnrev.h"
@ -106,6 +108,8 @@ static wxString GetMemoryErrorVM()
SysCoreAllocations::SysCoreAllocations() SysCoreAllocations::SysCoreAllocations()
{ {
InstallSignalHandler();
Console.Status( "Initializing PS2 virtual machine..." ); Console.Status( "Initializing PS2 virtual machine..." );
RecSuccess_EE = false; RecSuccess_EE = false;

View File

@ -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 u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed");
extern void vSyncDebugStuff( uint frame ); extern void vSyncDebugStuff( uint frame );
extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true );
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Memory Protection (Used by VTLB, Recompilers, and Texture caches) // PCSX2_SEH - Defines existence of "built in" Structured Exception Handling support.
// --------------------------------------------------------------------------------------
#ifdef __LINUX__
# include <signal.h>
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.
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// This should be available on Windows, via Microsoft or Intel compilers (I'm pretty sure Intel // 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 // 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. // need to use setjmp/longjmp instead to exit recompiled code.
// //
#if defined(_WIN32) && !defined(__GNUC__)
# define PCSX2_SEH
#else
# include <setjmp.h> //#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 #ifndef PCSX2_SEH
// code execution pipelines in an efficient manner, since standard C++ exceptions cannot # if defined(_WIN32) && !defined(__GNUC__)
// unwind across dynamically recompiled code. # define PCSX2_SEH 1
# else
enum # define PCSX2_SEH 0
{ # endif
SetJmp_Dispatcher = 1, #endif
SetJmp_Exit,
}; // 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
extern jmp_buf SetJmp_RecExecute; // of SEH, and so these functions can be inlined.
extern jmp_buf SetJmp_StateCheck; #ifdef _WIN32
# define __unique_stackframe __noinline
#else
# define __unique_stackframe
#endif #endif
class pxMessageBoxEvent;
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// Different types of message boxes that the emulator can employ from the friendly confines // 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 // blocking behavior -- they prompt the user for action and only return after the user has
// responded to the prompt. // responded to the prompt.
// //
class pxMessageBoxEvent;
namespace Msgbox namespace Msgbox
{ {
extern void OnEvent( pxMessageBoxEvent& evt ); extern void OnEvent( pxMessageBoxEvent& evt );

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<PageFaultInfo>
{
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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<Pcsx2Config&>(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();
}

View File

@ -14,18 +14,9 @@
*/ */
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "Common.h"
#include "System.h" #include "System.h"
#include "SysThreads.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 // SysThreadBase *External Thread* Implementations
@ -267,7 +258,7 @@ void SysThreadBase::OnCleanupInThread()
void SysThreadBase::OnSuspendInThread() {} void SysThreadBase::OnSuspendInThread() {}
void SysThreadBase::OnResumeInThread( bool isSuspended ) {} void SysThreadBase::OnResumeInThread( bool isSuspended ) {}
void SysThreadBase::StateCheckInThread( bool isCancelable ) void SysThreadBase::StateCheckInThread()
{ {
switch( m_ExecMode ) switch( m_ExecMode )
{ {
@ -283,8 +274,7 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
case ExecMode_Opened: case ExecMode_Opened:
// Yup, need this a second time. Variable state could have changed while we // Yup, need this a second time. Variable state could have changed while we
// were trying to acquire the lock above. // were trying to acquire the lock above.
if( isCancelable ) TestCancel();
TestCancel();
break; break;
// ------------------------------------- // -------------------------------------
@ -324,197 +314,3 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
jNO_DEFAULT; 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<Pcsx2Config&>(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();
}

View File

@ -19,6 +19,25 @@
using namespace Threading; using namespace Threading;
#if !PCSX2_SEH
# include <setjmp.h>
// 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 class ISysThread : public virtual IThread
{ {
public: public:
@ -30,6 +49,9 @@ public:
virtual void Resume() {} virtual void Resume() {}
}; };
// --------------------------------------------------------------------------------------
// SysThreadBase
// --------------------------------------------------------------------------------------
class SysThreadBase : public PersistentThread, public virtual ISysThread class SysThreadBase : public PersistentThread, public virtual ISysThread
{ {
@ -93,7 +115,7 @@ public:
return m_ExecMode > ExecMode_Closed; return m_ExecMode > ExecMode_Closed;
} }
bool HasPendingStateChangeRequest() bool HasPendingStateChangeRequest() const
{ {
ExecutionMode mode = m_ExecMode; ExecutionMode mode = m_ExecMode;
return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing); return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing);
@ -108,8 +130,6 @@ public:
virtual void Resume(); virtual void Resume();
virtual bool Pause(); virtual bool Pause();
virtual void StateCheckInThread( bool isCancelable = true );
protected: protected:
virtual void OnStart(); virtual void OnStart();
@ -118,6 +138,7 @@ protected:
// Resume() has a lot of checks and balances to prevent re-entrance and race conditions. // Resume() has a lot of checks and balances to prevent re-entrance and race conditions.
virtual void OnResumeReady() {} virtual void OnResumeReady() {}
virtual void StateCheckInThread();
virtual void OnCleanupInThread(); virtual void OnCleanupInThread();
virtual void OnStartInThread(); virtual void OnStartInThread();
@ -147,8 +168,10 @@ protected:
virtual void OnResumeInThread( bool isSuspended )=0; virtual void OnResumeInThread( bool isSuspended )=0;
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// EECoreThread class // SysCoreThread class
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysCoreThread : public SysThreadBase class SysCoreThread : public SysThreadBase
{ {
@ -160,6 +183,9 @@ protected:
bool m_resetVirtualMachine; bool m_resetVirtualMachine;
bool m_hasValidState; bool m_hasValidState;
// Used by SETJMP only, but ifdef'ing it out clutters up the code.
bool m_CoreCancelDamnit;
public: public:
static SysCoreThread& Get(); static SysCoreThread& Get();
@ -177,9 +203,12 @@ public:
return m_hasValidState; return m_hasValidState;
} }
bool HasPendingStateChangeRequest() const;
virtual void StateCheckInThread();
protected: protected:
void CpuInitializeMess(); void CpuInitializeMess();
void CpuExecute();
virtual void Start(); virtual void Start();
virtual void OnSuspendInThread(); virtual void OnSuspendInThread();
@ -187,6 +216,8 @@ protected:
virtual void OnResumeInThread( bool IsSuspended ); virtual void OnResumeInThread( bool IsSuspended );
virtual void OnCleanupInThread(); virtual void OnCleanupInThread();
virtual void ExecuteTaskInThread(); virtual void ExecuteTaskInThread();
void _StateCheckThrows();
}; };
extern int sys_resume_lock; extern int sys_resume_lock;

View File

@ -21,7 +21,7 @@
#include <wx/docview.h> #include <wx/docview.h>
#include <wx/apptrait.h> #include <wx/apptrait.h>
#include "Utilities/Listeners.h" #include "Utilities/EventSource.h"
#include "IniInterface.h" #include "IniInterface.h"
//class IniInterface; //class IniInterface;
@ -490,7 +490,7 @@ public:
virtual bool Suspend( bool isBlocking=true ); virtual bool Suspend( bool isBlocking=true );
virtual void Resume(); virtual void Resume();
virtual void StateCheckInThread( bool isCancelable=true ); virtual void StateCheckInThread();
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
protected: protected:

View File

@ -128,9 +128,9 @@ void AppCoreThread::OnCleanupInThread()
extern int TranslateGDKtoWXK( u32 keysym ); extern int TranslateGDKtoWXK( u32 keysym );
#endif #endif
void AppCoreThread::StateCheckInThread( bool isCancelable ) void AppCoreThread::StateCheckInThread()
{ {
_parent::StateCheckInThread( isCancelable ); _parent::StateCheckInThread();
if( !pxAssert(g_plugins!=NULL) ) return; if( !pxAssert(g_plugins!=NULL) ) return;
const keyEvent* ev = PADkeyEvent(); const keyEvent* ev = PADkeyEvent();

View File

@ -243,9 +243,8 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) ); Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) ); Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
Connect( pxEVT_CoreThreadStatus, wxCommandEventHandler( Pcsx2App::OnCoreThreadStatus ) );
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 ) );

View File

@ -210,8 +210,7 @@ void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const
{ {
try try {
{
(handler->*func)(event); (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 ) 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(); CleanupMess();
return true; 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 ); CoreThread.ApplySettings( g_Conf->EmuOptions );
if( resume ) if( resume )

View File

@ -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; if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj; MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplyCoreStatus(); mframe->ApplyCoreStatus();
} }
void MainEmuFrame::OnCorePluginStatusChanged( void* obj, const wxCommandEvent& evt ) void __fastcall MainEmuFrame::OnCorePluginStatusChanged( void* obj, wxCommandEvent& evt )
{ {
if( obj == NULL ) return; if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj; MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplyCoreStatus(); mframe->ApplyCoreStatus();
} }
void MainEmuFrame::OnSettingsApplied( void* obj, const int& evt ) void __fastcall MainEmuFrame::OnSettingsApplied( void* obj, int& evt )
{ {
if( obj == NULL ) return; if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj; MainEmuFrame* mframe = (MainEmuFrame*)obj;
mframe->ApplySettings(); mframe->ApplySettings();
} }
void MainEmuFrame::OnSettingsLoadSave( void* obj, const IniInterface& evt ) void __fastcall MainEmuFrame::OnSettingsLoadSave( void* obj, IniInterface& evt )
{ {
if( obj == NULL ) return; if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj; MainEmuFrame* mframe = (MainEmuFrame*)obj;
// FIXME: Evil const cast hack! mframe->LoadSaveRecentIsoList( evt );
mframe->LoadSaveRecentIsoList( const_cast<IniInterface&>(evt) );
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -85,10 +85,10 @@ public:
void ReloadRecentLists(); void ReloadRecentLists();
protected: protected:
static void OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt ); static void __fastcall OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt );
static void OnCorePluginStatusChanged( void* obj, const wxCommandEvent& evt ); static void __fastcall OnCorePluginStatusChanged( void* obj, wxCommandEvent& evt );
static void OnSettingsApplied( void* obj, const int& evt ); static void __fastcall OnSettingsApplied( void* obj, int& evt );
static void OnSettingsLoadSave( void* obj, const IniInterface& evt ); static void __fastcall OnSettingsLoadSave( void* obj, IniInterface& evt );
void LoadSaveRecentIsoList( IniInterface& conf ); void LoadSaveRecentIsoList( IniInterface& conf );
void ApplySettings(); void ApplySettings();

View File

@ -380,16 +380,16 @@
RelativePath="..\..\Stats.cpp" RelativePath="..\..\Stats.cpp"
> >
</File> </File>
<File
RelativePath="..\..\System\SysCoreThread.cpp"
>
</File>
<File <File
RelativePath="..\..\System.cpp" RelativePath="..\..\System.cpp"
> >
</File> </File>
<File <File
RelativePath="..\..\System\SysThreads.cpp" RelativePath="..\..\System\SysThreadBase.cpp"
>
</File>
<File
RelativePath="..\..\System\SysThreads.h"
> >
</File> </File>
<Filter <Filter
@ -455,6 +455,10 @@
RelativePath="..\..\NakedAsm.h" RelativePath="..\..\NakedAsm.h"
> >
</File> </File>
<File
RelativePath="..\..\System\PageFaultSource.h"
>
</File>
<File <File
RelativePath="..\..\Paths.h" RelativePath="..\..\Paths.h"
> >
@ -479,6 +483,10 @@
RelativePath="..\..\System.h" RelativePath="..\..\System.h"
> >
</File> </File>
<File
RelativePath="..\..\System\SysThreads.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Linux" Name="Linux"

View File

@ -18,23 +18,23 @@
#include <winnt.h> #include <winnt.h>
#include "Common.h" #include "Common.h"
#include "System/PageFaultSource.h"
int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps ) int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
{ {
const _EXCEPTION_RECORD& ExceptionRecord = *eps->ExceptionRecord; if( eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION )
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; return EXCEPTION_CONTINUE_SEARCH;
mmap_ClearCpuBlock( offset ); PageFaultInfo info( (uptr)eps->ExceptionRecord->ExceptionInformation[1] );
return EXCEPTION_CONTINUE_EXECUTION; Source_PageFault.DispatchException( info );
if( info.handled ) return EXCEPTION_CONTINUE_EXECUTION;
return EXCEPTION_CONTINUE_SEARCH;
}
void InstallSignalHandler()
{
// NOP on Win32 systems -- we use __try{} __except{} instead.
} }

View File

@ -22,11 +22,8 @@
#include "VU.h" #include "VU.h"
#include "iCore.h" #include "iCore.h"
#define PC_GETBLOCK(x) PC_GETBLOCK_(x, recLUT)
extern u32 pc; extern u32 pc;
extern int branch; extern int branch;
extern uptr recLUT[];
extern u32 maxrecmem; extern u32 maxrecmem;
extern u32 pc; // recompiler pc (also used by the SuperVU! .. why? (air)) extern u32 pc; // recompiler pc (also used by the SuperVU! .. why? (air))

View File

@ -19,42 +19,23 @@
#include "Memory.h" #include "Memory.h"
#include "R5900OpcodeTables.h" #include "R5900OpcodeTables.h"
#include "iR5900.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 "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 "vtlb.h"
#include "SamplProf.h" #include "SamplProf.h"
#include "NakedAsm.h"
#include "Dump.h" #include "Dump.h"
#include "SysThreads.h"
#include "GS.h"
using namespace x86Emitter; using namespace x86Emitter;
using namespace R5900; using namespace R5900;
// used to disable register freezing during cpuBranchTests (registers #define PC_GETBLOCK(x) PC_GETBLOCK_(x, recLUT)
// are safe then since they've been completely flushed)
bool g_EEFreezeRegs = false;
u32 maxrecmem = 0; u32 maxrecmem = 0;
uptr recLUT[0x10000]; static uptr recLUT[0x10000];
uptr hwLUT[0x10000]; static uptr hwLUT[0x10000];
#define HWADDR(mem) (hwLUT[mem >> 16] + (mem)) #define HWADDR(mem) (hwLUT[mem >> 16] + (mem))
@ -584,13 +565,12 @@ struct ManualPageTracking
static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12]; static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12];
static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12]; static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12];
volatile bool eeRecIsReset = false; static bool eeRecIsReset = false;
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
void recResetEE( void ) void recResetEE( void )
{ {
Console.Status( "Issuing EE/iR5900-32 Recompiler Reset [mem/structure cleanup]" ); Console.Status( "Issuing EE/iR5900-32 Recompiler Reset [mem/structure cleanup]" );
eeRecIsReset = true;
maxrecmem = 0; maxrecmem = 0;
@ -652,6 +632,7 @@ void recResetEE( void )
branch = 0; branch = 0;
SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR); SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR);
eeRecIsReset = true;
} }
static void recShutdown( void ) static void recShutdown( void )
@ -673,81 +654,99 @@ void recStep( void )
{ {
} }
#ifndef PCSX2_SEH static jmp_buf m_SetJmp_StateCheck;
// <--- setjmp/longjmp model <--- static void recCheckExecutionState()
#include "GS.h"
#include "System/SysThreads.h"
static void StateThreadCheck_LongJmp()
{ {
setjmp( SetJmp_StateCheck ); #if PCSX2_SEH
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();
SysCoreThread::Get().StateCheckInThread(); 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 #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() static void recExecute()
{ {
// Implementation Notes: // Implementation Notes:
// [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired! // [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired!
try #if PCSX2_SEH
{ try {
while( true ) while( true )
{ {
// Typically the Dispatcher is invoked from the EventTest code, which clears eeRecIsReset = false;
// the FreezeRegs flag, so always be sure to reset it here:
g_EEFreezeRegs = true; g_EEFreezeRegs = true;
try { try {
EnterRecompiledCode(); EnterRecompiledCode();
} }
catch( Exception::ForceDispatcherReg& ) { } 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 #endif
}
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
void R5900::Dynarec::OpcodeImpl::recSYSCALL( void ) void R5900::Dynarec::OpcodeImpl::recSYSCALL( void )
@ -856,17 +855,12 @@ void recClear(u32 addr, u32 size)
} }
#ifndef PCSX2_SEH static void recExitExecution()
jmp_buf SetJmp_RecExecute;
jmp_buf SetJmp_StateCheck;
#endif
static void ExitRec()
{ {
#ifdef PCSX2_SEH #if PCSX2_SEH
throw Exception::ExitRecExecute(); throw Exception::ExitRecExecute();
#else #else
longjmp( SetJmp_RecExecute, SetJmp_Exit ); longjmp( m_SetJmp_StateCheck, SetJmp_Exit );
#endif #endif
} }
@ -875,22 +869,29 @@ void CheckForBIOSEnd()
{ {
xMOV( eax, &cpuRegs.pc ); xMOV( eax, &cpuRegs.pc );
/*xCMP( eax, 0x00200008 ); if( IsDevBuild )
xJE(ExitRec); {
// Using CALL retains stacktrace info, useful for debugging.
xCMP( eax, 0x00100008 ); xCMP( eax, 0x00200008 );
xJE(ExitRec);*/ xForwardJE8 CallExitRec;
xCMP( eax, 0x00200008 ); xCMP( eax, 0x00100008 );
xForwardJE8 CallExitRec; xForwardJNE8 SkipExitRec;
xCMP( eax, 0x00100008 ); CallExitRec.SetTarget();
xForwardJNE8 SkipExitRec; xCALL( recExitExecution );
CallExitRec.SetTarget(); SkipExitRec.SetTarget();
xCALL( ExitRec ); }
else
{
xCMP( eax, 0x00200008 );
xJE(recExitExecution);
SkipExitRec.SetTarget(); xCMP( eax, 0x00100008 );
xJE(recExitExecution);
}
} }
static int *s_pCode; static int *s_pCode;
@ -1765,11 +1766,13 @@ StartRecomp:
s_pCurBlockEx = NULL; s_pCurBlockEx = NULL;
} }
R5900cpu recCpu = { R5900cpu recCpu =
{
recAlloc, recAlloc,
recResetEE, recResetEE,
recStep, recStep,
recExecute, recExecute,
recCheckExecutionState,
recClear, recClear,
recShutdown recShutdown
}; };