mirror of https://github.com/PCSX2/pcsx2.git
aligned_stack branch progress!
* Implemented setjmp/longjmp method of escaping recompiled code, circumventing the deadly pitfalls of the g++ exception mess. * Use longjmp to jump to a "safe" location (outside recompiled code) for suspending and canceling the SysCoreThread (see StateThreadCheck_LongJmp) * I'm the stack daddy mack. That's right. Uh huh. Uh huh. * Test for m_detached in PersistentThread::IsSelf, since Linux likes to recycle thread handles in a hurry (like, as in, consecutively) * Fix a deadlock in the Consoole Log (seems to be GTK+ specific, and in fact I'm pretty sure wxApp->Yield() isn't thread safe and thinks it's recursive even when it's not) git-svn-id: http://pcsx2.googlecode.com/svn/branches/aligned_stack@2035 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
047726a60c
commit
e3a5229076
|
@ -38,7 +38,7 @@
|
||||||
Console.Error( ex.what() ); \
|
Console.Error( ex.what() ); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUG__
|
||||||
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __PRETTY_FUNCTION__ )
|
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __PRETTY_FUNCTION__ )
|
||||||
#else
|
#else
|
||||||
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __FUNCTION__ )
|
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __FUNCTION__ )
|
||||||
|
@ -88,7 +88,7 @@ namespace Exception
|
||||||
// Returns a message suitable for end-user display.
|
// Returns a message suitable for end-user display.
|
||||||
// This message is usually meant for display in a user popup or such.
|
// This message is usually meant for display in a user popup or such.
|
||||||
virtual wxString FormatDisplayMessage() const { return m_message_user; }
|
virtual wxString FormatDisplayMessage() const { return m_message_user; }
|
||||||
|
|
||||||
virtual void Rethrow() const=0;
|
virtual void Rethrow() const=0;
|
||||||
virtual BaseException* Clone() const=0;
|
virtual BaseException* Clone() const=0;
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ namespace Exception
|
||||||
// instead issues a "silent" cancelation that is handled by the app gracefully (generates
|
// instead issues a "silent" cancelation that is handled by the app gracefully (generates
|
||||||
// log, and resumes messages queue processing).
|
// log, and resumes messages queue processing).
|
||||||
//
|
//
|
||||||
// I chose to have this exception derive from RuntimeError, since if one is thrown from outside
|
// I chose to have this exception derive from RuntimeError, since if one is thrown from outside
|
||||||
// an App message loop we'll still want it to be handled in a reasonably graceful manner.
|
// an App message loop we'll still want it to be handled in a reasonably graceful manner.
|
||||||
class CancelEvent : public virtual RuntimeError
|
class CancelEvent : public virtual RuntimeError
|
||||||
{
|
{
|
||||||
|
@ -188,13 +188,13 @@ namespace Exception
|
||||||
m_message_diag = fromUTF8( logmsg );
|
m_message_diag = fromUTF8( logmsg );
|
||||||
// overridden message formatters only use the diagnostic version...
|
// overridden message formatters only use the diagnostic version...
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit CancelEvent( const wxString& logmsg=L"No reason given." )
|
explicit CancelEvent( const wxString& logmsg=L"No reason given." )
|
||||||
{
|
{
|
||||||
m_message_diag = logmsg;
|
m_message_diag = logmsg;
|
||||||
// overridden message formatters only use the diagnostic version...
|
// overridden message formatters only use the diagnostic version...
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual wxString FormatDisplayMessage() const;
|
virtual wxString FormatDisplayMessage() const;
|
||||||
virtual wxString FormatDiagnosticMessage() const;
|
virtual wxString FormatDiagnosticMessage() const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -187,7 +187,7 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cancel( m_thread );
|
pthread_cancel( m_thread );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isBlocking )
|
if( isBlocking )
|
||||||
|
@ -214,7 +214,9 @@ void Threading::PersistentThread::Block()
|
||||||
|
|
||||||
bool Threading::PersistentThread::IsSelf() const
|
bool Threading::PersistentThread::IsSelf() const
|
||||||
{
|
{
|
||||||
return pthread_self() == m_thread;
|
// Detached threads may have their pthread handles recycled as newer threads, causing
|
||||||
|
// false IsSelf reports.
|
||||||
|
return !m_detached && (pthread_self() == m_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Threading::PersistentThread::IsRunning() const
|
bool Threading::PersistentThread::IsRunning() const
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* PCSX2 - PS2 Emulator for PCs
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||||
*
|
*
|
||||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
* 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-
|
* 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.
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
@ -42,3 +42,25 @@
|
||||||
|
|
||||||
extern void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR);
|
extern void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR);
|
||||||
extern u32 g_sseVUMXCSR, g_sseMXCSR;
|
extern u32 g_sseVUMXCSR, g_sseMXCSR;
|
||||||
|
|
||||||
|
// SEH - "Built in" Structed Exception Handling support.
|
||||||
|
// This should be available on Windows, via Microsoft or Intel compilers (I'm pretty sure Intel
|
||||||
|
// supports native SEH model). GUNC in Windows, or any compiler in a non-windows platform, will
|
||||||
|
// ned to use setjmp/longjmp instead to exit recompiled code.
|
||||||
|
//
|
||||||
|
#if defined(_WIN32) && !defined(__GNUG__)
|
||||||
|
# define PCSX2_SEH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PCSX2_SEH
|
||||||
|
# include <setjmp.h>
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SetJmp_Dispatcher = 1,
|
||||||
|
SetJmp_Exit,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern __threadlocal jmp_buf SetJmp_RecExecute;
|
||||||
|
extern __threadlocal jmp_buf SetJmp_StateCheck;
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* PCSX2 - PS2 Emulator for PCs
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||||
*
|
*
|
||||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
* 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-
|
* 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.
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
@ -45,7 +45,7 @@ SyncCounter hsyncCounter;
|
||||||
SyncCounter vsyncCounter;
|
SyncCounter vsyncCounter;
|
||||||
|
|
||||||
u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
|
u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
|
||||||
s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
|
s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
|
||||||
|
|
||||||
void rcntReset(int index) {
|
void rcntReset(int index) {
|
||||||
counters[index].count = 0;
|
counters[index].count = 0;
|
||||||
|
@ -64,7 +64,7 @@ static __forceinline void _rcntSet( int cntidx )
|
||||||
|
|
||||||
// Stopped or special hsync gate?
|
// Stopped or special hsync gate?
|
||||||
if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3) ) return;
|
if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3) ) return;
|
||||||
|
|
||||||
// check for special cases where the overflow or target has just passed
|
// check for special cases where the overflow or target has just passed
|
||||||
// (we probably missed it because we're doing/checking other things)
|
// (we probably missed it because we're doing/checking other things)
|
||||||
if( counter.count > 0x10000 || counter.count > counter.target )
|
if( counter.count > 0x10000 || counter.count > counter.target )
|
||||||
|
@ -80,17 +80,17 @@ static __forceinline void _rcntSet( int cntidx )
|
||||||
|
|
||||||
c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
|
c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
|
||||||
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
|
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
|
||||||
if (c < nextCounter)
|
if (c < nextCounter)
|
||||||
{
|
{
|
||||||
nextCounter = c;
|
nextCounter = c;
|
||||||
cpuSetNextBranch( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
|
cpuSetNextBranch( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore target diff if target is currently disabled.
|
// Ignore target diff if target is currently disabled.
|
||||||
// (the overflow is all we care about since it goes first, and then the
|
// (the overflow is all we care about since it goes first, and then the
|
||||||
// target will be turned on afterward, and handled in the next event test).
|
// target will be turned on afterward, and handled in the next event test).
|
||||||
|
|
||||||
if( counter.target & EECNT_FUTURE_TARGET )
|
if( counter.target & EECNT_FUTURE_TARGET )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,11 +98,11 @@ static __forceinline void _rcntSet( int cntidx )
|
||||||
{
|
{
|
||||||
c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
|
c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT);
|
||||||
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
|
c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate();
|
||||||
if (c < nextCounter)
|
if (c < nextCounter)
|
||||||
{
|
{
|
||||||
nextCounter = c;
|
nextCounter = c;
|
||||||
cpuSetNextBranch( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
|
cpuSetNextBranch( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ void rcntInit() {
|
||||||
|
|
||||||
hsyncCounter.Mode = MODE_HRENDER;
|
hsyncCounter.Mode = MODE_HRENDER;
|
||||||
hsyncCounter.sCycle = cpuRegs.cycle;
|
hsyncCounter.sCycle = cpuRegs.cycle;
|
||||||
vsyncCounter.Mode = MODE_VRENDER;
|
vsyncCounter.Mode = MODE_VRENDER;
|
||||||
vsyncCounter.sCycle = cpuRegs.cycle;
|
vsyncCounter.sCycle = cpuRegs.cycle;
|
||||||
|
|
||||||
UpdateVSyncRate();
|
UpdateVSyncRate();
|
||||||
|
@ -148,7 +148,7 @@ void rcntInit() {
|
||||||
|
|
||||||
// debug code, used for stats
|
// debug code, used for stats
|
||||||
int g_nhsyncCounter;
|
int g_nhsyncCounter;
|
||||||
static uint iFrame = 0;
|
static uint iFrame = 0;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -190,11 +190,11 @@ static void vSyncInfoCalc( vSyncTimingInfo* info, u32 framesPerSecond, u32 scans
|
||||||
// Important! The hRender/hBlank timers should be 50/50 for best results.
|
// Important! The hRender/hBlank timers should be 50/50 for best results.
|
||||||
// In theory a 70%/30% ratio would be more correct but in practice it runs
|
// In theory a 70%/30% ratio would be more correct but in practice it runs
|
||||||
// like crap and totally screws audio synchronization and other things.
|
// like crap and totally screws audio synchronization and other things.
|
||||||
|
|
||||||
u64 Scanline = Frame / scansPerFrame;
|
u64 Scanline = Frame / scansPerFrame;
|
||||||
u64 hBlank = Scanline / 2;
|
u64 hBlank = Scanline / 2;
|
||||||
u64 hRender = Scanline - hBlank;
|
u64 hRender = Scanline - hBlank;
|
||||||
|
|
||||||
info->Framerate = framesPerSecond;
|
info->Framerate = framesPerSecond;
|
||||||
info->Render = (u32)(Render/10000);
|
info->Render = (u32)(Render/10000);
|
||||||
info->Blank = (u32)(Blank/10000);
|
info->Blank = (u32)(Blank/10000);
|
||||||
|
@ -202,14 +202,14 @@ static void vSyncInfoCalc( vSyncTimingInfo* info, u32 framesPerSecond, u32 scans
|
||||||
info->hRender = (u32)(hRender/10000);
|
info->hRender = (u32)(hRender/10000);
|
||||||
info->hBlank = (u32)(hBlank/10000);
|
info->hBlank = (u32)(hBlank/10000);
|
||||||
info->hScanlinesPerFrame = scansPerFrame;
|
info->hScanlinesPerFrame = scansPerFrame;
|
||||||
|
|
||||||
// Apply rounding:
|
// Apply rounding:
|
||||||
if( ( Render - info->Render ) >= 5000 ) info->Render++;
|
if( ( Render - info->Render ) >= 5000 ) info->Render++;
|
||||||
else if( ( Blank - info->Blank ) >= 5000 ) info->Blank++;
|
else if( ( Blank - info->Blank ) >= 5000 ) info->Blank++;
|
||||||
|
|
||||||
if( ( hRender - info->hRender ) >= 5000 ) info->hRender++;
|
if( ( hRender - info->hRender ) >= 5000 ) info->hRender++;
|
||||||
else if( ( hBlank - info->hBlank ) >= 5000 ) info->hBlank++;
|
else if( ( hBlank - info->hBlank ) >= 5000 ) info->hBlank++;
|
||||||
|
|
||||||
// Calculate accumulative hSync rounding error per half-frame:
|
// Calculate accumulative hSync rounding error per half-frame:
|
||||||
{
|
{
|
||||||
u32 hSyncCycles = ((info->hRender + info->hBlank) * scansPerFrame) / 2;
|
u32 hSyncCycles = ((info->hRender + info->hBlank) * scansPerFrame) / 2;
|
||||||
|
@ -284,12 +284,12 @@ void frameLimitReset()
|
||||||
// Framelimiter - Measures the delta time between calls and stalls until a
|
// Framelimiter - Measures the delta time between calls and stalls until a
|
||||||
// certain amount of time passes if such time hasn't passed yet.
|
// certain amount of time passes if such time hasn't passed yet.
|
||||||
// See the GS FrameSkip function for details on why this is here and not in the GS.
|
// See the GS FrameSkip function for details on why this is here and not in the GS.
|
||||||
extern int limitOn;
|
extern int limitOn;
|
||||||
static __forceinline void frameLimit()
|
static __forceinline void frameLimit()
|
||||||
{
|
{
|
||||||
// 999 means the user would rather just have framelimiting turned off...
|
// 999 means the user would rather just have framelimiting turned off...
|
||||||
if( /*!EmuConfig.Video.EnableFrameLimiting*/ !limitOn || EmuConfig.Video.FpsLimit >= 999 ) return;
|
if( /*!EmuConfig.Video.EnableFrameLimiting*/ !limitOn || EmuConfig.Video.FpsLimit >= 999 ) return;
|
||||||
|
|
||||||
s64 sDeltaTime;
|
s64 sDeltaTime;
|
||||||
u64 uExpectedEnd;
|
u64 uExpectedEnd;
|
||||||
u64 iEnd;
|
u64 iEnd;
|
||||||
|
@ -302,7 +302,7 @@ static __forceinline void frameLimit()
|
||||||
// If the framerate drops too low, reset the expected value. This avoids
|
// If the framerate drops too low, reset the expected value. This avoids
|
||||||
// excessive amounts of "fast forward" syndrome which would occur if we
|
// excessive amounts of "fast forward" syndrome which would occur if we
|
||||||
// tried to catch up too much.
|
// tried to catch up too much.
|
||||||
|
|
||||||
if( sDeltaTime > m_iTicks*8 )
|
if( sDeltaTime > m_iTicks*8 )
|
||||||
{
|
{
|
||||||
m_iStart = iEnd - m_iTicks;
|
m_iStart = iEnd - m_iTicks;
|
||||||
|
@ -335,9 +335,9 @@ static __forceinline void VSyncStart(u32 sCycle)
|
||||||
EECNT_LOG( "///////// EE COUNTER VSYNC START \\\\\\\\\\\\\\\\\\\\ (frame: %d)", iFrame );
|
EECNT_LOG( "///////// EE COUNTER VSYNC START \\\\\\\\\\\\\\\\\\\\ (frame: %d)", iFrame );
|
||||||
vSyncDebugStuff( iFrame ); // EE Profiling and Debug code
|
vSyncDebugStuff( iFrame ); // EE Profiling and Debug code
|
||||||
|
|
||||||
if ((CSRw & 0x8))
|
if ((CSRw & 0x8))
|
||||||
{
|
{
|
||||||
if (!(GSIMR&0x800))
|
if (!(GSIMR&0x800))
|
||||||
{
|
{
|
||||||
gsIrq();
|
gsIrq();
|
||||||
}
|
}
|
||||||
|
@ -384,7 +384,7 @@ static __forceinline void VSyncEnd(u32 sCycle)
|
||||||
frameLimit(); // limit FPS
|
frameLimit(); // limit FPS
|
||||||
|
|
||||||
// This doesn't seem to be needed here. Games only seem to break with regard to the
|
// This doesn't seem to be needed here. Games only seem to break with regard to the
|
||||||
// vsyncstart irq.
|
// vsyncstart irq.
|
||||||
//cpuRegs.eCycle[30] = 2;
|
//cpuRegs.eCycle[30] = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,16 +402,16 @@ __forceinline void rcntUpdate_hScanline()
|
||||||
if (hsyncCounter.Mode & MODE_HBLANK) { //HBLANK Start
|
if (hsyncCounter.Mode & MODE_HBLANK) { //HBLANK Start
|
||||||
rcntStartGate(false, hsyncCounter.sCycle);
|
rcntStartGate(false, hsyncCounter.sCycle);
|
||||||
psxCheckStartGate16(0);
|
psxCheckStartGate16(0);
|
||||||
|
|
||||||
// Setup the hRender's start and end cycle information:
|
// Setup the hRender's start and end cycle information:
|
||||||
hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
|
hsyncCounter.sCycle += vSyncInfo.hBlank; // start (absolute cycle value)
|
||||||
hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
|
hsyncCounter.CycleT = vSyncInfo.hRender; // endpoint (delta from start value)
|
||||||
hsyncCounter.Mode = MODE_HRENDER;
|
hsyncCounter.Mode = MODE_HRENDER;
|
||||||
}
|
}
|
||||||
else { //HBLANK END / HRENDER Begin
|
else { //HBLANK END / HRENDER Begin
|
||||||
if (CSRw & 0x4)
|
if (CSRw & 0x4)
|
||||||
{
|
{
|
||||||
if (!(GSIMR&0x400))
|
if (!(GSIMR&0x400))
|
||||||
{
|
{
|
||||||
gsIrq();
|
gsIrq();
|
||||||
}
|
}
|
||||||
|
@ -431,6 +431,8 @@ __forceinline void rcntUpdate_hScanline()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CoreCancelDamnit = false;
|
||||||
|
|
||||||
__forceinline void rcntUpdate_vSync()
|
__forceinline void rcntUpdate_vSync()
|
||||||
{
|
{
|
||||||
s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle);
|
s32 diff = (cpuRegs.cycle - vsyncCounter.sCycle);
|
||||||
|
@ -439,17 +441,24 @@ __forceinline void rcntUpdate_vSync()
|
||||||
if (vsyncCounter.Mode == MODE_VSYNC)
|
if (vsyncCounter.Mode == MODE_VSYNC)
|
||||||
{
|
{
|
||||||
eeRecIsReset = false;
|
eeRecIsReset = false;
|
||||||
|
|
||||||
|
#ifndef PCSX2_SEH
|
||||||
|
if( CoreCancelDamnit || SysCoreThread::Get().HasPendingStateChangeRequest() )
|
||||||
|
{
|
||||||
|
longjmp( SetJmp_StateCheck, 1 );
|
||||||
|
}
|
||||||
|
#else
|
||||||
mtgsThread.RethrowException();
|
mtgsThread.RethrowException();
|
||||||
SysCoreThread::Get().StateCheckInThread();
|
SysCoreThread::Get().StateCheckInThread();
|
||||||
|
#endif
|
||||||
if( eeRecIsReset )
|
if( eeRecIsReset )
|
||||||
{
|
{
|
||||||
eeRecIsReset = false;
|
eeRecIsReset = false;
|
||||||
cpuSetBranch();
|
cpuSetBranch();
|
||||||
|
|
||||||
// Hack! GCC is unwilling to let us throw exceptions here.
|
#ifndef PCSX2_SEH
|
||||||
// (Ones in Exception::*, anyways.) Work around it by skipping
|
longjmp( SetJmp_RecExecute, SetJmp_Dispatcher );
|
||||||
// it.
|
#else
|
||||||
#ifdef _MSC_VER
|
|
||||||
throw Exception::ForceDispatcherReg();
|
throw Exception::ForceDispatcherReg();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -470,7 +479,7 @@ __forceinline void rcntUpdate_vSync()
|
||||||
|
|
||||||
// Accumulate hsync rounding errors:
|
// Accumulate hsync rounding errors:
|
||||||
hsyncCounter.sCycle += vSyncInfo.hSyncError;
|
hsyncCounter.sCycle += vSyncInfo.hSyncError;
|
||||||
|
|
||||||
if (CHECK_MICROVU0) vsyncVUrec(0);
|
if (CHECK_MICROVU0) vsyncVUrec(0);
|
||||||
if (CHECK_MICROVU1) vsyncVUrec(1);
|
if (CHECK_MICROVU1) vsyncVUrec(1);
|
||||||
|
|
||||||
|
@ -502,20 +511,20 @@ static __forceinline void _cpuTestTarget( int i )
|
||||||
counters[i].count -= counters[i].target; // Reset on target
|
counters[i].count -= counters[i].target; // Reset on target
|
||||||
else
|
else
|
||||||
counters[i].target |= EECNT_FUTURE_TARGET;
|
counters[i].target |= EECNT_FUTURE_TARGET;
|
||||||
}
|
}
|
||||||
else counters[i].target |= EECNT_FUTURE_TARGET;
|
else counters[i].target |= EECNT_FUTURE_TARGET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __forceinline void _cpuTestOverflow( int i )
|
static __forceinline void _cpuTestOverflow( int i )
|
||||||
{
|
{
|
||||||
if (counters[i].count <= 0xffff) return;
|
if (counters[i].count <= 0xffff) return;
|
||||||
|
|
||||||
if (counters[i].mode.OverflowInterrupt) {
|
if (counters[i].mode.OverflowInterrupt) {
|
||||||
EECNT_LOG("EE Counter[%d] OVERFLOW - mode=%x, count=%x", i, counters[i].mode, counters[i].count);
|
EECNT_LOG("EE Counter[%d] OVERFLOW - mode=%x, count=%x", i, counters[i].mode, counters[i].count);
|
||||||
counters[i].mode.OverflowReached = 1;
|
counters[i].mode.OverflowReached = 1;
|
||||||
hwIntcIrq(counters[i].interrupt);
|
hwIntcIrq(counters[i].interrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrap counter back around zero, and enable the future target:
|
// wrap counter back around zero, and enable the future target:
|
||||||
counters[i].count -= 0x10000;
|
counters[i].count -= 0x10000;
|
||||||
counters[i].target &= 0xffff;
|
counters[i].target &= 0xffff;
|
||||||
|
@ -530,14 +539,14 @@ __forceinline void rcntUpdate()
|
||||||
rcntUpdate_vSync();
|
rcntUpdate_vSync();
|
||||||
|
|
||||||
// Update counters so that we can perform overflow and target tests.
|
// Update counters so that we can perform overflow and target tests.
|
||||||
|
|
||||||
for (int i=0; i<=3; i++)
|
for (int i=0; i<=3; i++)
|
||||||
{
|
{
|
||||||
// We want to count gated counters (except the hblank which exclude below, and are
|
// We want to count gated counters (except the hblank which exclude below, and are
|
||||||
// counted by the hblank timer instead)
|
// counted by the hblank timer instead)
|
||||||
|
|
||||||
//if ( gates & (1<<i) ) continue;
|
//if ( gates & (1<<i) ) continue;
|
||||||
|
|
||||||
if (!counters[i].mode.IsCounting ) continue;
|
if (!counters[i].mode.IsCounting ) continue;
|
||||||
|
|
||||||
if(counters[i].mode.ClockSource != 0x3) // don't count hblank sources
|
if(counters[i].mode.ClockSource != 0x3) // don't count hblank sources
|
||||||
|
@ -552,7 +561,7 @@ __forceinline void rcntUpdate()
|
||||||
// Check Counter Targets and Overflows:
|
// Check Counter Targets and Overflows:
|
||||||
_cpuTestTarget( i );
|
_cpuTestTarget( i );
|
||||||
_cpuTestOverflow( i );
|
_cpuTestOverflow( i );
|
||||||
}
|
}
|
||||||
else counters[i].sCycleT = cpuRegs.cycle;
|
else counters[i].sCycleT = cpuRegs.cycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,20 +619,20 @@ __forceinline void rcntStartGate(bool isVblank, u32 sCycle)
|
||||||
|
|
||||||
switch (counters[i].mode.GateMode) {
|
switch (counters[i].mode.GateMode) {
|
||||||
case 0x0: //Count When Signal is low (off)
|
case 0x0: //Count When Signal is low (off)
|
||||||
|
|
||||||
// Just set the start cycle (sCycleT) -- counting will be done as needed
|
// Just set the start cycle (sCycleT) -- counting will be done as needed
|
||||||
// for events (overflows, targets, mode changes, and the gate off below)
|
// for events (overflows, targets, mode changes, and the gate off below)
|
||||||
|
|
||||||
counters[i].mode.IsCounting = 1;
|
counters[i].mode.IsCounting = 1;
|
||||||
counters[i].sCycleT = sCycle;
|
counters[i].sCycleT = sCycle;
|
||||||
EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x",
|
EECNT_LOG("EE Counter[%d] %s StartGate Type0, count = %x",
|
||||||
isVblank ? "vblank" : "hblank", i, counters[i].count );
|
isVblank ? "vblank" : "hblank", i, counters[i].count );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x2: // reset and start counting on vsync end
|
case 0x2: // reset and start counting on vsync end
|
||||||
// this is the vsync start so do nothing.
|
// this is the vsync start so do nothing.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1: //Reset and start counting on Vsync start
|
case 0x1: //Reset and start counting on Vsync start
|
||||||
case 0x3: //Reset and start counting on Vsync start and end
|
case 0x3: //Reset and start counting on Vsync start and end
|
||||||
counters[i].mode.IsCounting = 1;
|
counters[i].mode.IsCounting = 1;
|
||||||
|
@ -686,7 +695,7 @@ __forceinline void rcntEndGate(bool isVblank , u32 sCycle)
|
||||||
// rcntUpdate, since we're being called from there anyway.
|
// rcntUpdate, since we're being called from there anyway.
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void rcntWmode(int index, u32 value)
|
__forceinline void rcntWmode(int index, u32 value)
|
||||||
{
|
{
|
||||||
if(counters[index].mode.IsCounting) {
|
if(counters[index].mode.IsCounting) {
|
||||||
if(counters[index].mode.ClockSource != 0x3) {
|
if(counters[index].mode.ClockSource != 0x3) {
|
||||||
|
@ -705,7 +714,7 @@ __forceinline void rcntWmode(int index, u32 value)
|
||||||
// Clear OverflowReached and TargetReached flags (0xc00 mask), but *only* if they are set to 1 in the
|
// Clear OverflowReached and TargetReached flags (0xc00 mask), but *only* if they are set to 1 in the
|
||||||
// given value. (yes, the bits are cleared when written with '1's).
|
// given value. (yes, the bits are cleared when written with '1's).
|
||||||
|
|
||||||
counters[index].modeval &= ~(value & 0xc00);
|
counters[index].modeval &= ~(value & 0xc00);
|
||||||
counters[index].modeval = (counters[index].modeval & 0xc00) | (value & 0x3ff);
|
counters[index].modeval = (counters[index].modeval & 0xc00) | (value & 0x3ff);
|
||||||
EECNT_LOG("EE Counter[%d] writeMode = %x passed value=%x", index, counters[index].modeval, value );
|
EECNT_LOG("EE Counter[%d] writeMode = %x passed value=%x", index, counters[index].modeval, value );
|
||||||
|
|
||||||
|
@ -715,17 +724,17 @@ __forceinline void rcntWmode(int index, u32 value)
|
||||||
case 2: counters[index].rate = 512; break;
|
case 2: counters[index].rate = 512; break;
|
||||||
case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break;
|
case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_rcntSetGate( index );
|
_rcntSetGate( index );
|
||||||
_rcntSet( index );
|
_rcntSet( index );
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void rcntWcount(int index, u32 value)
|
__forceinline void rcntWcount(int index, u32 value)
|
||||||
{
|
{
|
||||||
EECNT_LOG("EE Counter[%d] writeCount = %x, oldcount=%x, target=%x", index, value, counters[index].count, counters[index].target );
|
EECNT_LOG("EE Counter[%d] writeCount = %x, oldcount=%x, target=%x", index, value, counters[index].count, counters[index].target );
|
||||||
|
|
||||||
counters[index].count = value & 0xffff;
|
counters[index].count = value & 0xffff;
|
||||||
|
|
||||||
// reset the target, and make sure we don't get a premature target.
|
// reset the target, and make sure we don't get a premature target.
|
||||||
counters[index].target &= 0xffff;
|
counters[index].target &= 0xffff;
|
||||||
if( counters[index].count > counters[index].target )
|
if( counters[index].count > counters[index].target )
|
||||||
|
@ -740,7 +749,7 @@ __forceinline void rcntWcount(int index, u32 value)
|
||||||
counters[index].sCycleT = cpuRegs.cycle - change;
|
counters[index].sCycleT = cpuRegs.cycle - change;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else counters[index].sCycleT = cpuRegs.cycle;
|
else counters[index].sCycleT = cpuRegs.cycle;
|
||||||
|
|
||||||
_rcntSet( index );
|
_rcntSet( index );
|
||||||
|
@ -786,9 +795,9 @@ __forceinline u32 rcntRcount(int index)
|
||||||
u32 ret;
|
u32 ret;
|
||||||
|
|
||||||
// only count if the counter is turned on (0x80) and is not an hsync gate (!0x03)
|
// only count if the counter is turned on (0x80) and is not an hsync gate (!0x03)
|
||||||
if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
|
if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
|
||||||
ret = counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
|
ret = counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
|
||||||
else
|
else
|
||||||
ret = counters[index].count;
|
ret = counters[index].count;
|
||||||
|
|
||||||
// Spams the Console.
|
// Spams the Console.
|
||||||
|
@ -798,9 +807,9 @@ __forceinline u32 rcntRcount(int index)
|
||||||
|
|
||||||
__forceinline u32 rcntCycle(int index)
|
__forceinline u32 rcntCycle(int index)
|
||||||
{
|
{
|
||||||
if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
|
if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3))
|
||||||
return counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
|
return counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate);
|
||||||
else
|
else
|
||||||
return counters[index].count;
|
return counters[index].count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
pcsx2/GS.h
11
pcsx2/GS.h
|
@ -102,6 +102,7 @@ protected:
|
||||||
int m_CopyCommandTally;
|
int m_CopyCommandTally;
|
||||||
int m_CopyDataTally;
|
int m_CopyDataTally;
|
||||||
volatile bool m_RingBufferIsBusy;
|
volatile bool m_RingBufferIsBusy;
|
||||||
|
volatile bool m_PluginOpened;
|
||||||
|
|
||||||
// Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to
|
// Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to
|
||||||
// throttle the number of frames allowed to be rendered ahead of time for games that
|
// throttle the number of frames allowed to be rendered ahead of time for games that
|
||||||
|
@ -122,8 +123,6 @@ public:
|
||||||
mtgsThreadObject();
|
mtgsThreadObject();
|
||||||
virtual ~mtgsThreadObject() throw();
|
virtual ~mtgsThreadObject() throw();
|
||||||
|
|
||||||
void OnStart();
|
|
||||||
|
|
||||||
// Waits for the GS to empty out the entire ring buffer contents.
|
// Waits for the GS to empty out the entire ring buffer contents.
|
||||||
// Used primarily for plugin startup/shutdown.
|
// Used primarily for plugin startup/shutdown.
|
||||||
void WaitGS();
|
void WaitGS();
|
||||||
|
@ -145,11 +144,15 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OpenPlugin();
|
void OpenPlugin();
|
||||||
|
void ClosePlugin();
|
||||||
|
|
||||||
|
void OnStart();
|
||||||
|
void OnResumeReady();
|
||||||
|
|
||||||
void OnSuspendInThread();
|
void OnSuspendInThread();
|
||||||
void OnPauseInThread() {}
|
void OnPauseInThread() {}
|
||||||
void OnResumeInThread( bool IsSuspended );
|
void OnResumeInThread( bool IsSuspended );
|
||||||
|
void OnCleanupInThread();
|
||||||
void OnResumeReady();
|
|
||||||
|
|
||||||
// Saves MMX/XMM REGS, posts an event to the mtgsThread flag and releases a timeslice.
|
// Saves MMX/XMM REGS, posts an event to the mtgsThread flag and releases a timeslice.
|
||||||
// For use in surrounding loops that wait on the mtgs.
|
// For use in surrounding loops that wait on the mtgs.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* PCSX2 - PS2 Emulator for PCs
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||||
*
|
*
|
||||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
* 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-
|
* 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.
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
* If not, see <http://www.gnu.org/licenses/>.
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
@ -50,7 +50,8 @@ void SysPageFaultExceptionFilter( int signal, siginfo_t *info, void * )
|
||||||
{
|
{
|
||||||
// Bad mojo! Completely invalid address.
|
// Bad mojo! Completely invalid address.
|
||||||
// Instigate a crash or abort emulation or something.
|
// Instigate a crash or abort emulation or something.
|
||||||
assert( false );
|
wxTrap();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmap_ClearCpuBlock( offset & ~m_pagemask );
|
mmap_ClearCpuBlock( offset & ~m_pagemask );
|
||||||
|
|
|
@ -79,7 +79,6 @@ struct MTGS_BufferedData
|
||||||
|
|
||||||
static __aligned(32) MTGS_BufferedData RingBuffer;
|
static __aligned(32) MTGS_BufferedData RingBuffer;
|
||||||
extern bool renderswitch;
|
extern bool renderswitch;
|
||||||
static volatile bool gsIsOpened = false;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
|
@ -98,6 +97,7 @@ mtgsThreadObject::mtgsThreadObject() :
|
||||||
, m_CopyCommandTally( 0 )
|
, m_CopyCommandTally( 0 )
|
||||||
, m_CopyDataTally( 0 )
|
, m_CopyDataTally( 0 )
|
||||||
, m_RingBufferIsBusy( false )
|
, m_RingBufferIsBusy( false )
|
||||||
|
, m_PluginOpened( false )
|
||||||
, m_QueuedFrames( 0 )
|
, m_QueuedFrames( 0 )
|
||||||
, m_packet_size( 0 )
|
, m_packet_size( 0 )
|
||||||
, m_packet_ringpos( 0 )
|
, m_packet_ringpos( 0 )
|
||||||
|
@ -111,7 +111,7 @@ mtgsThreadObject::mtgsThreadObject() :
|
||||||
|
|
||||||
void mtgsThreadObject::OnStart()
|
void mtgsThreadObject::OnStart()
|
||||||
{
|
{
|
||||||
gsIsOpened = false;
|
m_PluginOpened = false;
|
||||||
|
|
||||||
m_RingPos = 0;
|
m_RingPos = 0;
|
||||||
m_WritePos = 0;
|
m_WritePos = 0;
|
||||||
|
@ -187,14 +187,6 @@ struct PacketTagType
|
||||||
u32 data[3];
|
u32 data[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void _clean_close_gs( void* obj )
|
|
||||||
{
|
|
||||||
if( !gsIsOpened ) return;
|
|
||||||
gsIsOpened = false;
|
|
||||||
if( g_plugins != NULL )
|
|
||||||
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dummyIrqCallback()
|
static void dummyIrqCallback()
|
||||||
{
|
{
|
||||||
// dummy, because MTGS doesn't need this mess!
|
// dummy, because MTGS doesn't need this mess!
|
||||||
|
@ -203,7 +195,7 @@ static void dummyIrqCallback()
|
||||||
|
|
||||||
void mtgsThreadObject::OpenPlugin()
|
void mtgsThreadObject::OpenPlugin()
|
||||||
{
|
{
|
||||||
if( gsIsOpened ) return;
|
if( m_PluginOpened ) return;
|
||||||
|
|
||||||
memcpy_aligned( RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS) );
|
memcpy_aligned( RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS) );
|
||||||
GSsetBaseMem( RingBuffer.Regs );
|
GSsetBaseMem( RingBuffer.Regs );
|
||||||
|
@ -225,7 +217,7 @@ void mtgsThreadObject::OpenPlugin()
|
||||||
throw Exception::PluginOpenError( PluginId_GS );
|
throw Exception::PluginOpenError( PluginId_GS );
|
||||||
}
|
}
|
||||||
|
|
||||||
gsIsOpened = true;
|
m_PluginOpened = true;
|
||||||
m_sem_OpenDone.Post();
|
m_sem_OpenDone.Post();
|
||||||
|
|
||||||
GSCSRr = 0x551B4000; // 0x55190000
|
GSCSRr = 0x551B4000; // 0x55190000
|
||||||
|
@ -238,7 +230,6 @@ void mtgsThreadObject::ExecuteTaskInThread()
|
||||||
PacketTagType prevCmd;
|
PacketTagType prevCmd;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pthread_cleanup_push( _clean_close_gs, this );
|
|
||||||
while( true )
|
while( true )
|
||||||
{
|
{
|
||||||
m_sem_event.WaitRaw(); // ... because this does a cancel test itself..
|
m_sem_event.WaitRaw(); // ... because this does a cancel test itself..
|
||||||
|
@ -409,18 +400,34 @@ void mtgsThreadObject::ExecuteTaskInThread()
|
||||||
}
|
}
|
||||||
m_RingBufferIsBusy = false;
|
m_RingBufferIsBusy = false;
|
||||||
}
|
}
|
||||||
pthread_cleanup_pop( true );
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::ClosePlugin()
|
||||||
|
{
|
||||||
|
if( !m_PluginOpened ) return;
|
||||||
|
m_PluginOpened = false;
|
||||||
|
if( g_plugins != NULL )
|
||||||
|
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::OnSuspendInThread()
|
void mtgsThreadObject::OnSuspendInThread()
|
||||||
{
|
{
|
||||||
_clean_close_gs( NULL );
|
ClosePlugin();
|
||||||
|
_parent::OnSuspendInThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::OnResumeInThread( bool isSuspended )
|
void mtgsThreadObject::OnResumeInThread( bool isSuspended )
|
||||||
{
|
{
|
||||||
if( isSuspended )
|
if( isSuspended )
|
||||||
OpenPlugin();
|
OpenPlugin();
|
||||||
|
|
||||||
|
_parent::OnResumeInThread( isSuspended );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::OnCleanupInThread()
|
||||||
|
{
|
||||||
|
ClosePlugin();
|
||||||
|
_parent::OnCleanupInThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits for the GS to empty out the entire ring buffer contents.
|
// Waits for the GS to empty out the entire ring buffer contents.
|
||||||
|
@ -783,7 +790,7 @@ void mtgsThreadObject::SendGameCRC( u32 crc )
|
||||||
|
|
||||||
void mtgsThreadObject::WaitForOpen()
|
void mtgsThreadObject::WaitForOpen()
|
||||||
{
|
{
|
||||||
if( gsIsOpened ) return;
|
if( m_PluginOpened ) return;
|
||||||
Resume();
|
Resume();
|
||||||
|
|
||||||
// Two-phase timeout on MTGS opening, so that possible errors are handled
|
// Two-phase timeout on MTGS opening, so that possible errors are handled
|
||||||
|
@ -798,11 +805,11 @@ void mtgsThreadObject::WaitForOpen()
|
||||||
if( !m_sem_OpenDone.Wait( wxTimeSpan(0, 0, 4, 0) ) )
|
if( !m_sem_OpenDone.Wait( wxTimeSpan(0, 0, 4, 0) ) )
|
||||||
{
|
{
|
||||||
RethrowException();
|
RethrowException();
|
||||||
|
|
||||||
// Not opened yet, and no exceptions. Weird? You decide!
|
// Not opened yet, and no exceptions. Weird? You decide!
|
||||||
// TODO : implement a user confirmation to cancel the action and exit the
|
// TODO : implement a user confirmation to cancel the action and exit the
|
||||||
// emulator forcefully, or to continue waiting on the GS.
|
// emulator forcefully, or to continue waiting on the GS.
|
||||||
|
|
||||||
throw Exception::PluginOpenError( PluginId_GS, "The MTGS thread has become unresponsive while waiting for the GS plugin to open." );
|
throw Exception::PluginOpenError( PluginId_GS, "The MTGS thread has become unresponsive while waiting for the GS plugin to open." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ void SysThreadBase::Start()
|
||||||
RethrowException();
|
RethrowException();
|
||||||
if( pxAssertDev( m_ExecMode == ExecMode_Closing, "Unexpected thread status during SysThread startup." ) )
|
if( pxAssertDev( m_ExecMode == ExecMode_Closing, "Unexpected thread status during SysThread startup." ) )
|
||||||
{
|
{
|
||||||
throw Exception::ThreadCreationError(
|
throw Exception::ThreadCreationError(
|
||||||
wxsFormat( L"Timeout occurred while attempting to start the %s thread.", m_name.c_str() ),
|
wxsFormat( L"Timeout occurred while attempting to start the %s thread.", m_name.c_str() ),
|
||||||
wxEmptyString
|
wxEmptyString
|
||||||
);
|
);
|
||||||
|
@ -253,6 +253,9 @@ void SysThreadBase::OnCleanupInThread()
|
||||||
m_RunningLock.Unlock();
|
m_RunningLock.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SysThreadBase::OnSuspendInThread() {}
|
||||||
|
void SysThreadBase::OnResumeInThread( bool isSuspended ) {}
|
||||||
|
|
||||||
void SysThreadBase::StateCheckInThread( bool isCancelable )
|
void SysThreadBase::StateCheckInThread( bool isCancelable )
|
||||||
{
|
{
|
||||||
switch( m_ExecMode )
|
switch( m_ExecMode )
|
||||||
|
@ -327,6 +330,13 @@ SysCoreThread::SysCoreThread() :
|
||||||
|
|
||||||
SysCoreThread::~SysCoreThread() throw()
|
SysCoreThread::~SysCoreThread() throw()
|
||||||
{
|
{
|
||||||
|
SysCoreThread::Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern bool CoreCancelDamnit;
|
||||||
|
void SysCoreThread::Cancel( bool isBlocking )
|
||||||
|
{
|
||||||
|
CoreCancelDamnit = true;
|
||||||
_parent::Cancel();
|
_parent::Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +344,7 @@ void SysCoreThread::Start()
|
||||||
{
|
{
|
||||||
if( g_plugins == NULL ) return;
|
if( g_plugins == NULL ) return;
|
||||||
g_plugins->Init();
|
g_plugins->Init();
|
||||||
|
CoreCancelDamnit = false; // belongs in OnStart actually, but I'm tired :P
|
||||||
_parent::Start();
|
_parent::Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,12 @@ public:
|
||||||
return m_ExecMode > ExecMode_Closed;
|
return m_ExecMode > ExecMode_Closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasPendingStateChangeRequest()
|
||||||
|
{
|
||||||
|
ExecutionMode mode = m_ExecMode;
|
||||||
|
return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing);
|
||||||
|
}
|
||||||
|
|
||||||
bool IsClosed() const { return !IsOpen(); }
|
bool IsClosed() const { return !IsOpen(); }
|
||||||
|
|
||||||
ExecutionMode GetExecutionMode() const { return m_ExecMode; }
|
ExecutionMode GetExecutionMode() const { return m_ExecMode; }
|
||||||
|
@ -164,6 +170,7 @@ public:
|
||||||
virtual void ApplySettings( const Pcsx2Config& src );
|
virtual void ApplySettings( const Pcsx2Config& src );
|
||||||
virtual void OnResumeReady();
|
virtual void OnResumeReady();
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
virtual void Cancel( bool isBlocking=true );
|
||||||
|
|
||||||
bool HasValidState()
|
bool HasValidState()
|
||||||
{
|
{
|
||||||
|
|
|
@ -500,6 +500,8 @@ void ConsoleLogFrame::OnSemaphoreWait( wxCommandEvent& event )
|
||||||
m_semaphore.Post();
|
m_semaphore.Post();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const wxTimeSpan high_volume_timeout( 0, 0, 0, 500 );
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Deadlock protection: High volume logs will over-tax our message pump and cause the
|
// Deadlock protection: High volume logs will over-tax our message pump and cause the
|
||||||
// GUI to become inaccessible. The cool solution would be a threaded log window, but wx
|
// GUI to become inaccessible. The cool solution would be a threaded log window, but wx
|
||||||
|
@ -515,13 +517,21 @@ void ConsoleLogFrame::CountMessage()
|
||||||
{
|
{
|
||||||
if( !wxThread::IsMain() )
|
if( !wxThread::IsMain() )
|
||||||
{
|
{
|
||||||
// Append an event that'll post up our semaphore. It'll get run "in
|
// Append an event that'll post up our semaphore. It'll typically get run "in
|
||||||
// order" which means when it posts all queued messages will have been
|
// order" which means when it posts all queued messages will have been processed.
|
||||||
// processed.
|
|
||||||
|
// GTK+ / Timeout: We need a timeout on our semaphore to avoid deadlocking in GTK+,
|
||||||
|
// because for some reason it can't friggen process messages from a wxYield()
|
||||||
|
// (which is used from mutex and semaphore locks on the main thread to handle
|
||||||
|
// messages from child threads, like this one!).
|
||||||
|
|
||||||
|
// Leaving it enabled on Windows as well for now since it's probably a "good idea" to avoid
|
||||||
|
// deadlocking in some totally unforseeably random happenstance sircumstance, and I don't
|
||||||
|
// think it'll have an impact on performance. --air
|
||||||
|
|
||||||
wxCommandEvent evt( wxEVT_SemaphoreWait );
|
wxCommandEvent evt( wxEVT_SemaphoreWait );
|
||||||
GetEventHandler()->AddPendingEvent( evt );
|
GetEventHandler()->AddPendingEvent( evt );
|
||||||
m_semaphore.WaitRaw();
|
m_semaphore.WaitRaw( high_volume_timeout );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -313,8 +313,6 @@ u32* recGetImm64(u32 hi, u32 lo)
|
||||||
// R5900 Dispatchers
|
// R5900 Dispatchers
|
||||||
// =====================================================================================================
|
// =====================================================================================================
|
||||||
|
|
||||||
extern "C" void recEventTest();
|
|
||||||
|
|
||||||
static u32 g_lastpc = 0;
|
static u32 g_lastpc = 0;
|
||||||
static u32 s_store_ebp, s_store_esp;
|
static u32 s_store_ebp, s_store_esp;
|
||||||
|
|
||||||
|
@ -330,6 +328,13 @@ static DynGenFunc* JITCompileInBlock = NULL;
|
||||||
static DynGenFunc* EnterRecompiledCode = NULL;
|
static DynGenFunc* EnterRecompiledCode = NULL;
|
||||||
static DynGenFunc* ExitRecompiledCode = NULL;
|
static DynGenFunc* ExitRecompiledCode = NULL;
|
||||||
|
|
||||||
|
static void recEventTest()
|
||||||
|
{
|
||||||
|
pxAssert( !g_globalXMMSaved && !g_globalMMXSaved );
|
||||||
|
_cpuBranchTest_Shared();
|
||||||
|
pxAssert( !g_globalXMMSaved && !g_globalMMXSaved );
|
||||||
|
}
|
||||||
|
|
||||||
// parameters:
|
// parameters:
|
||||||
// espORebp - 0 for ESP, or 1 for EBP.
|
// espORebp - 0 for ESP, or 1 for EBP.
|
||||||
// regval - current value of the register at the time the fault was detected (predates the
|
// regval - current value of the register at the time the fault was detected (predates the
|
||||||
|
@ -443,9 +448,10 @@ static DynGenFunc* _DynGen_EnterRecompiledCode()
|
||||||
xMOV( ptr[ebp-4], ebx );
|
xMOV( ptr[ebp-4], ebx );
|
||||||
|
|
||||||
// Simulate a CALL function by pushing the call address and EBP onto the stack.
|
// Simulate a CALL function by pushing the call address and EBP onto the stack.
|
||||||
xMOV( ptr32[esp+0x10+12], 0xffeeff );
|
xMOV( ptr32[esp+0x1c], 0xffeeff );
|
||||||
uptr& imm = *(uptr*)(xGetPtr()-4);
|
uptr& imm = *(uptr*)(xGetPtr()-4);
|
||||||
xMOV( ptr32[esp+0x10+8], ebp );
|
xMOV( ptr32[esp+0x18], ebp );
|
||||||
|
xLEA( ebp, ptr32[esp+0x18] );
|
||||||
|
|
||||||
xMOV( &s_store_esp, esp );
|
xMOV( &s_store_esp, esp );
|
||||||
xMOV( &s_store_ebp, ebp );
|
xMOV( &s_store_ebp, ebp );
|
||||||
|
@ -668,59 +674,72 @@ void recStep( void )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PCSX2_SEH
|
||||||
|
|
||||||
extern "C" void recEventTest()
|
// <--- setjmp/longjmp model <---
|
||||||
|
|
||||||
|
#include "GS.h"
|
||||||
|
#include "System/SysThreads.h"
|
||||||
|
|
||||||
|
static void StateThreadCheck_LongJmp()
|
||||||
{
|
{
|
||||||
#ifdef PCSX2_DEVBUILD
|
setjmp( SetJmp_StateCheck );
|
||||||
// dont' remove this check unless doing an official release
|
|
||||||
if( g_globalXMMSaved || g_globalMMXSaved)
|
|
||||||
{
|
|
||||||
DevCon.Error("PCSX2 Foopah! Frozen regs have not been restored!!!");
|
|
||||||
DevCon.Error("g_globalXMMSaved = %d,g_globalMMXSaved = %d", g_globalXMMSaved, g_globalMMXSaved);
|
|
||||||
}
|
|
||||||
assert( !g_globalXMMSaved && !g_globalMMXSaved);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Perform counters, interrupts, and IOP updates:
|
mtgsThread.RethrowException();
|
||||||
_cpuBranchTest_Shared();
|
SysCoreThread::Get().StateCheckInThread();
|
||||||
|
|
||||||
#ifdef PCSX2_DEVBUILD
|
|
||||||
assert( !g_globalXMMSaved && !g_globalMMXSaved);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// ---> SEH Model --->
|
||||||
|
|
||||||
static void recExecute()
|
static void recExecute()
|
||||||
{
|
{
|
||||||
// Implementation Notes:
|
// Implementation Notes:
|
||||||
// This function enter an endless loop, which is only escapable via C++ exception handling.
|
// [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired!
|
||||||
// The loop is needed because some things in the rec use "ret" as a shortcut to
|
|
||||||
// invoking DispatcherReg. These things are code bits which are called infrequently,
|
|
||||||
// such as dyna_block_discard and dyna_page_reset.
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while( true )
|
while( true )
|
||||||
{
|
{
|
||||||
// Note: make sure the FreezeRegs boolean is reset to true here, because
|
// Typically the Dispatcher is invoked from the EventTest code, which clears
|
||||||
// it might be set to false, depending on if the rec exits from the context of
|
// the FreezeRegs flag, so always be sure to reset it here:
|
||||||
// an EventTest or not.
|
|
||||||
|
|
||||||
g_EEFreezeRegs = true;
|
g_EEFreezeRegs = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
EnterRecompiledCode();
|
EnterRecompiledCode();
|
||||||
}
|
}
|
||||||
catch( Exception::ForceDispatcherReg& )
|
catch( Exception::ForceDispatcherReg& ) { }
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( Exception::ExitRecExecute& )
|
catch( Exception::ExitRecExecute& ) { }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
g_EEFreezeRegs = false;
|
g_EEFreezeRegs = false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
void R5900::Dynarec::OpcodeImpl::recSYSCALL( void )
|
void R5900::Dynarec::OpcodeImpl::recSYSCALL( void )
|
||||||
|
@ -828,9 +847,19 @@ void recClear(u32 addr, u32 size)
|
||||||
ClearRecLUT(PC_GETBLOCK(lowerextent), (upperextent - lowerextent) / 4);
|
ClearRecLUT(PC_GETBLOCK(lowerextent), (upperextent - lowerextent) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __GNUG__
|
||||||
|
__threadlocal jmp_buf SetJmp_RecExecute;
|
||||||
|
__threadlocal jmp_buf SetJmp_StateCheck;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void ExitRec()
|
static void ExitRec()
|
||||||
{
|
{
|
||||||
|
#ifdef __GNUG__
|
||||||
|
longjmp( SetJmp_RecExecute, SetJmp_Exit );
|
||||||
|
#else
|
||||||
throw Exception::ExitRecExecute();
|
throw Exception::ExitRecExecute();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for end of bios
|
// check for end of bios
|
||||||
|
@ -1273,8 +1302,14 @@ void __fastcall dyna_block_discard(u32 start,u32 sz)
|
||||||
DevCon.WriteLn("dyna_block_discard .. start=0x%08X size=%d", start, sz*4);
|
DevCon.WriteLn("dyna_block_discard .. start=0x%08X size=%d", start, sz*4);
|
||||||
recClear(start, sz);
|
recClear(start, sz);
|
||||||
|
|
||||||
// Note: this function is accessed via a JMP, and thus the RET here will exit
|
// Stack trick: This function was invoked via a direct jmp, so manually pop the
|
||||||
// recompiled code and take us back to recExecute.
|
// EBP/stackframe before issuing a RET, else esp/ebp will be incorrect.
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__asm leave; __asm jmp [ExitRecompiledCode]
|
||||||
|
#else
|
||||||
|
__asm__ __volatile__( "leave\n jmp *%[exitRec]\n" : : [exitRec] "m" (ExitRecompiledCode) : );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// called when a block under manual protection has been run enough times to be a
|
// called when a block under manual protection has been run enough times to be a
|
||||||
|
@ -1285,10 +1320,11 @@ void __fastcall dyna_page_reset(u32 start,u32 sz)
|
||||||
manual_counter[start >> 12]++;
|
manual_counter[start >> 12]++;
|
||||||
mmap_MarkCountedRamPage( start );
|
mmap_MarkCountedRamPage( start );
|
||||||
|
|
||||||
// Note: this function is accessed via a JMP, and thus the RET here will exit
|
#ifdef _MSC_VER
|
||||||
// recompiled code and take us back to recExecute.
|
__asm leave; __asm jmp [ExitRecompiledCode]
|
||||||
|
#else
|
||||||
__asm__ __volatile__( "leave\n jmp %[exitRec]\n" : : [exitRec] "m" (ExitRecompiledCode) : );
|
__asm__ __volatile__( "leave\n jmp *%[exitRec]\n" : : [exitRec] "m" (ExitRecompiledCode) : );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void recRecompile( const u32 startpc )
|
void recRecompile( const u32 startpc )
|
||||||
|
|
Loading…
Reference in New Issue