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