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="../../include/Utilities/Console.h" />
<Unit filename="../../include/Utilities/Dependencies.h" />
<Unit filename="../../include/Utilities/EventSource.h" />
<Unit filename="../../include/Utilities/Exceptions.h" />
<Unit filename="../../include/Utilities/General.h" />
<Unit filename="../../include/Utilities/HashMap.h" />
<Unit filename="../../include/Utilities/Listeners.h" />
<Unit filename="../../include/Utilities/MemcpyFast.h" />
<Unit filename="../../include/Utilities/Path.h" />
<Unit filename="../../include/Utilities/RedtapeWindows.h" />

View File

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

View File

@ -16,12 +16,17 @@
#pragma once
#include <list>
#include <wx/event.h>
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:
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();
while( iter != list.end() )
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<EvtType>::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<EvtType>( object ) );
}

View File

@ -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

View File

@ -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();
}

View File

@ -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
//

View File

@ -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);

View File

@ -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();

View File

@ -19,6 +19,7 @@
#include "Common.h"
#include "R5900.h"
#include "R5900OpcodeTables.h"
#include "System/SysThreads.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 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
};

View File

@ -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 ) {}

View File

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

View File

@ -86,6 +86,14 @@ extern bool renderswitch;
std::list<uint> 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();
}

View File

@ -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<PageFaultInfo>((void*)psM, mmap_OnPageFault) );
}
void memShutdown()
{
Source_PageFault.Remove( EventListener<PageFaultInfo>((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()
{

View File

@ -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

View File

@ -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");

View File

@ -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
};

View File

@ -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();

View File

@ -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;

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 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 <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.
// 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 <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
// 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 );

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 "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,7 +274,6 @@ 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();
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<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;
#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
{
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;

View File

@ -21,7 +21,7 @@
#include <wx/docview.h>
#include <wx/apptrait.h>
#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:

View File

@ -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();

View File

@ -244,7 +244,6 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
Connect( pxEVT_CoreThreadStatus, wxCommandEventHandler( Pcsx2App::OnCoreThreadStatus ) );
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );

View File

@ -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 )

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;
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<IniInterface&>(evt) );
mframe->LoadSaveRecentIsoList( evt );
}
// ------------------------------------------------------------------------

View File

@ -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();

View File

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

View File

@ -18,23 +18,23 @@
#include <winnt.h>
#include "Common.h"
#include "System/PageFaultSource.h"
int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
{
const _EXCEPTION_RECORD& ExceptionRecord = *eps->ExceptionRecord;
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)
if( eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION )
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 "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))

View File

@ -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,17 +654,60 @@ 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 );
#if PCSX2_SEH
SysCoreThread::Get().StateCheckInThread();
if( eeRecIsReset )
throw Exception::ForceDispatcherReg();
#else
// 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!
#if PCSX2_SEH
try {
while( true )
{
eeRecIsReset = false;
g_EEFreezeRegs = true;
try {
EnterRecompiledCode();
}
catch( Exception::ForceDispatcherReg& ) { }
}
}
catch( Exception::ExitRecExecute& ) {}
#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
@ -692,62 +716,37 @@ static void StateThreadCheck_LongJmp()
// of the cancelstate here!
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
mtgsThread.RethrowException();
SysCoreThread::Get().StateCheckInThread();
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
}
static void recExecute()
{
StateThreadCheck_LongJmp();
eeRecIsReset = false;
switch( setjmp( SetJmp_RecExecute ) )
{
case SetJmp_Exit: break;
#ifdef _WIN32
__try {
#endif
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();
#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;
}
g_EEFreezeRegs = false;
}
#else
// ---> SEH Model --->
static void recExecute()
{
// Implementation Notes:
// [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired!
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:
g_EEFreezeRegs = true;
try {
EnterRecompiledCode();
}
catch( Exception::ForceDispatcherReg& ) { }
}
}
catch( Exception::ExitRecExecute& ) { }
g_EEFreezeRegs = false;
}
#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,11 +869,9 @@ void CheckForBIOSEnd()
{
xMOV( eax, &cpuRegs.pc );
/*xCMP( eax, 0x00200008 );
xJE(ExitRec);
xCMP( eax, 0x00100008 );
xJE(ExitRec);*/
if( IsDevBuild )
{
// Using CALL retains stacktrace info, useful for debugging.
xCMP( eax, 0x00200008 );
xForwardJE8 CallExitRec;
@ -888,9 +880,18 @@ void CheckForBIOSEnd()
xForwardJNE8 SkipExitRec;
CallExitRec.SetTarget();
xCALL( ExitRec );
xCALL( recExitExecution );
SkipExitRec.SetTarget();
}
else
{
xCMP( eax, 0x00200008 );
xJE(recExitExecution);
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
};