mirror of https://github.com/PCSX2/pcsx2.git
Improved MTGS (added better suspend/resume support), and work on savestates a bit (still not working tho)
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1908 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
2046b8e7b4
commit
aaa3b773c6
|
@ -120,6 +120,8 @@ struct u128
|
||||||
u128( u32 src ) :
|
u128( u32 src ) :
|
||||||
lo( src )
|
lo( src )
|
||||||
, hi( 0 ) {}
|
, hi( 0 ) {}
|
||||||
|
|
||||||
|
u128() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s128
|
struct s128
|
||||||
|
@ -136,6 +138,8 @@ struct s128
|
||||||
s128( s32 src ) :
|
s128( s32 src ) :
|
||||||
lo( src )
|
lo( src )
|
||||||
, hi( 0 ) {}
|
, hi( 0 ) {}
|
||||||
|
|
||||||
|
s128() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -20,10 +20,11 @@
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// <<< Important Notes to Plugin Authors >>>
|
// <<< Important Notes to Plugin Authors >>>
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// * Exceptions thrown by plugins may not be handled correctly if allowed to escape the
|
// * C++ only: Exceptions thrown by plugins may not be handled correctly if allowed to
|
||||||
// scope of the plugin, and could result in odd crashes. For C++ plugins this means
|
// escape the scope of the plugin, and could result in unexpected behavior or odd crashes.
|
||||||
// ensuring that any code that uses 'new' or STL containers (string, list, vector, etc)
|
// For C++ plugins this means ensuring that any code that uses 'new' or STL containers
|
||||||
// are contained within a try{} block, since the STL can throw std::bad_alloc.
|
// (string, list, vector, etc) are contained within a try{} block, since the STL can
|
||||||
|
// throw std::bad_alloc.
|
||||||
//
|
//
|
||||||
// * Many callbacks are optional, and have been marked as such. Any optional callback can be
|
// * Many callbacks are optional, and have been marked as such. Any optional callback can be
|
||||||
// left NULL. Any callback not marked optional and left NULL will cause the emulator to
|
// left NULL. Any callback not marked optional and left NULL will cause the emulator to
|
||||||
|
@ -39,9 +40,9 @@
|
||||||
// allocated by a plugin must be freed by that plugin.
|
// allocated by a plugin must be freed by that plugin.
|
||||||
//
|
//
|
||||||
// * C++ exception handling cannot be used by either plugin callbacks or emulator callbacks.
|
// * C++ exception handling cannot be used by either plugin callbacks or emulator callbacks.
|
||||||
// This includes the Console callbacks, for example, since the nature of C++ RTTI could
|
// This includes the emulator's Console callbacks, for example, since the nature of C++
|
||||||
// cause a C++ plugin wth its own catch handlers to catch exceptions of mismatched types
|
// ID-based RTTI could cause a C++ plugin with its own catch handlers to catch exceptions
|
||||||
// from the emulator.
|
// of mismatched types from the emulator.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
@ -700,9 +701,6 @@ typedef struct _PS2E_ComponentAPI_Mcd
|
||||||
// Returns:
|
// Returns:
|
||||||
// 0 if the card is not available, or 1 if it is available.
|
// 0 if the card is not available, or 1 if it is available.
|
||||||
//
|
//
|
||||||
// Exceptions:
|
|
||||||
// None. This function should not throw.
|
|
||||||
//
|
|
||||||
BOOL (PS2E_CALLBACK* McdIsPresent)( PS2E_THISPTR thisptr, uint port, uint slot );
|
BOOL (PS2E_CALLBACK* McdIsPresent)( PS2E_THISPTR thisptr, uint port, uint slot );
|
||||||
|
|
||||||
// McdRead
|
// McdRead
|
||||||
|
@ -802,9 +800,6 @@ typedef struct _PS2E_ComponentAPI_Pad
|
||||||
// Returns:
|
// Returns:
|
||||||
// 0 if the card is not available, or 1 if it is available.
|
// 0 if the card is not available, or 1 if it is available.
|
||||||
//
|
//
|
||||||
// Exceptions:
|
|
||||||
// None. This function should not throw.
|
|
||||||
//
|
|
||||||
BOOL (PS2E_CALLBACK* PadIsPresent)( PS2E_THISPTR thisptr, uint port, uint slot );
|
BOOL (PS2E_CALLBACK* PadIsPresent)( PS2E_THISPTR thisptr, uint port, uint slot );
|
||||||
|
|
||||||
// PadStartPoll
|
// PadStartPoll
|
||||||
|
@ -813,9 +808,6 @@ typedef struct _PS2E_ComponentAPI_Pad
|
||||||
// Returns:
|
// Returns:
|
||||||
// First byte in response to the poll (Typically 0xff).
|
// First byte in response to the poll (Typically 0xff).
|
||||||
//
|
//
|
||||||
// Exceptions:
|
|
||||||
// None. This function should not throw.
|
|
||||||
//
|
|
||||||
u8 (PS2E_CALLBACK* PadStartPoll)( PS2E_THISPTR thisptr, uint port, uint slot );
|
u8 (PS2E_CALLBACK* PadStartPoll)( PS2E_THISPTR thisptr, uint port, uint slot );
|
||||||
|
|
||||||
// PadPoll
|
// PadPoll
|
||||||
|
@ -824,9 +816,6 @@ typedef struct _PS2E_ComponentAPI_Pad
|
||||||
// Returns:
|
// Returns:
|
||||||
// Next byte in response to the poll.
|
// Next byte in response to the poll.
|
||||||
//
|
//
|
||||||
// Exceptions:
|
|
||||||
// None. This function should not throw.
|
|
||||||
//
|
|
||||||
u8 (PS2E_CALLBACK* PadPoll)( PS2E_THISPTR thisptr, u8 value );
|
u8 (PS2E_CALLBACK* PadPoll)( PS2E_THISPTR thisptr, u8 value );
|
||||||
|
|
||||||
// PadKeyEvent
|
// PadKeyEvent
|
||||||
|
@ -836,9 +825,6 @@ typedef struct _PS2E_ComponentAPI_Pad
|
||||||
// PS2E_KeyEvent: Key being pressed or released. Should stay valid until next call to
|
// PS2E_KeyEvent: Key being pressed or released. Should stay valid until next call to
|
||||||
// PadKeyEvent or plugin is closed with EmuClose.
|
// PadKeyEvent or plugin is closed with EmuClose.
|
||||||
//
|
//
|
||||||
// Exceptions:
|
|
||||||
// None. This function should not throw.
|
|
||||||
//
|
|
||||||
typedef PS2E_KeyEvent* (CALLBACK* PadKeyEvent)();
|
typedef PS2E_KeyEvent* (CALLBACK* PadKeyEvent)();
|
||||||
|
|
||||||
void* reserved[8];
|
void* reserved[8];
|
||||||
|
|
|
@ -549,9 +549,9 @@ public:
|
||||||
template< class Key, class T >
|
template< class Key, class T >
|
||||||
class HashMap : public google::dense_hash_map<Key, T, CommonHashClass>
|
class HashMap : public google::dense_hash_map<Key, T, CommonHashClass>
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
typedef typename google::dense_hash_map<Key, T, CommonHashClass> _parent;
|
typedef typename google::dense_hash_map<Key, T, CommonHashClass> _parent;
|
||||||
|
|
||||||
|
public:
|
||||||
using _parent::operator[];
|
using _parent::operator[];
|
||||||
using _parent::end;
|
using _parent::end;
|
||||||
typedef typename _parent::const_iterator const_iterator;
|
typedef typename _parent::const_iterator const_iterator;
|
||||||
|
|
|
@ -53,18 +53,20 @@ wxString GetTranslation( const char* msg )
|
||||||
// LogicErrors enabled as First-Chance exceptions regardless, so do it now. :)
|
// LogicErrors enabled as First-Chance exceptions regardless, so do it now. :)
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// FALSE if the assertion succeeded (condition is valid), or true if the assertion
|
// TRUE if the assertion succeeded (condition is valid), or FALSE if the assertion
|
||||||
// failed. The true clause is only reachable in release builds, and can be used by code
|
// failed. The true clause is only reachable in release builds, and can be used by code
|
||||||
// to provide a "stable" escape clause for unexpected behavior.
|
// to provide a "stable" escape clause for unexpected behavior.
|
||||||
//
|
//
|
||||||
DEVASSERT_INLINE bool DevAssert( bool condition, const char* msg )
|
DEVASSERT_INLINE bool DevAssert( bool condition, const char* msg )
|
||||||
{
|
{
|
||||||
if( condition ) return false;
|
if( condition ) return true;
|
||||||
if( IsDevBuild )
|
|
||||||
throw Exception::LogicError( msg );
|
|
||||||
|
|
||||||
wxASSERT_MSG_A( false, msg );
|
wxASSERT_MSG_A( false, msg );
|
||||||
return true;
|
|
||||||
|
if( IsDevBuild && !IsDebugBuild )
|
||||||
|
throw Exception::LogicError( msg );
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -123,8 +123,6 @@ namespace Threading
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pthread_detach( m_thread );
|
pthread_detach( m_thread );
|
||||||
|
|
||||||
m_running = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocks execution of the calling thread until this thread completes its task. The
|
// Blocks execution of the calling thread until this thread completes its task. The
|
||||||
|
@ -137,25 +135,15 @@ namespace Threading
|
||||||
//
|
//
|
||||||
sptr PersistentThread::Block()
|
sptr PersistentThread::Block()
|
||||||
{
|
{
|
||||||
if( _InterlockedExchange( &m_detached, true ) )
|
DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
|
||||||
{
|
|
||||||
// already detached: if we're still running then its an invalid operation
|
|
||||||
if( m_running )
|
|
||||||
throw Exception::InvalidOperation( "Blocking on detached threads requires manual semaphore implementation." );
|
|
||||||
|
|
||||||
return m_returncode;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
|
|
||||||
|
|
||||||
|
if( m_running )
|
||||||
#if wxUSE_GUI
|
#if wxUSE_GUI
|
||||||
m_sem_finished.WaitGui();
|
m_sem_finished.WaitGui();
|
||||||
#else
|
#else
|
||||||
m_sem_finished.Wait();
|
m_sem_finished.Wait();
|
||||||
#endif
|
#endif
|
||||||
return m_returncode;
|
return m_returncode;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PersistentThread::IsSelf() const
|
bool PersistentThread::IsSelf() const
|
||||||
|
|
|
@ -377,7 +377,7 @@ void cdvdReadKey(u8 arg0, u16 arg1, u32 arg2, u8* key) {
|
||||||
{
|
{
|
||||||
ElfCRC = loadElfCRC( str );
|
ElfCRC = loadElfCRC( str );
|
||||||
ElfApplyPatches();
|
ElfApplyPatches();
|
||||||
GSsetGameCRC( ElfCRC, 0 );
|
mtgsThread.SendGameCRC( ElfCRC );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ void cdvdDetectDisk()
|
||||||
{
|
{
|
||||||
ElfCRC = loadElfCRC( str.ToAscii().data() );
|
ElfCRC = loadElfCRC( str.ToAscii().data() );
|
||||||
ElfApplyPatches();
|
ElfApplyPatches();
|
||||||
GSsetGameCRC( ElfCRC, 0 );
|
mtgsThread.SendGameCRC( ElfCRC );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -443,6 +443,7 @@ __forceinline void rcntUpdate_vSync()
|
||||||
if (vsyncCounter.Mode == MODE_VSYNC)
|
if (vsyncCounter.Mode == MODE_VSYNC)
|
||||||
{
|
{
|
||||||
eeRecIsReset = false;
|
eeRecIsReset = false;
|
||||||
|
mtgsThread.PollStatus();
|
||||||
SysCoreThread::Get().StateCheck();
|
SysCoreThread::Get().StateCheck();
|
||||||
if( eeRecIsReset )
|
if( eeRecIsReset )
|
||||||
{
|
{
|
||||||
|
|
|
@ -175,11 +175,11 @@ void __fastcall WriteFIFO_page_6(u32 mem, const mem128_t *value)
|
||||||
psHu64(0x6008) = value[1];
|
psHu64(0x6008) = value[1];
|
||||||
|
|
||||||
FreezeRegs(1);
|
FreezeRegs(1);
|
||||||
mtgsThread->PrepDataPacket(GIF_PATH_3, nloop0_packet, 1);
|
mtgsThread.PrepDataPacket(GIF_PATH_3, nloop0_packet, 1);
|
||||||
u64* data = (u64*)mtgsThread->GetDataPacketPtr();
|
u64* data = (u64*)mtgsThread.GetDataPacketPtr();
|
||||||
data[0] = value[0];
|
data[0] = value[0];
|
||||||
data[1] = value[1];
|
data[1] = value[1];
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
FreezeRegs(0);
|
FreezeRegs(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
82
pcsx2/GS.cpp
82
pcsx2/GS.cpp
|
@ -67,7 +67,7 @@ void _gs_ChangeTimings( u32 framerate, u32 iTicks )
|
||||||
|
|
||||||
void gsOnModeChanged( u32 framerate, u32 newTickrate )
|
void gsOnModeChanged( u32 framerate, u32 newTickrate )
|
||||||
{
|
{
|
||||||
mtgsThread->SendSimplePacket( GS_RINGTYPE_MODECHANGE, framerate, newTickrate, 0 );
|
mtgsThread.SendSimplePacket( GS_RINGTYPE_MODECHANGE, framerate, newTickrate, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gsIsInterlaced = false;
|
static bool gsIsInterlaced = false;
|
||||||
|
@ -92,9 +92,7 @@ void gsInit()
|
||||||
|
|
||||||
void gsReset()
|
void gsReset()
|
||||||
{
|
{
|
||||||
// Sanity check in case the plugin hasn't been initialized...
|
mtgsThread.ResetGS();
|
||||||
if( mtgsThread == NULL ) return;
|
|
||||||
mtgsThread->Reset();
|
|
||||||
|
|
||||||
gsOnModeChanged(
|
gsOnModeChanged(
|
||||||
(gsRegionMode == Region_NTSC) ? FRAMERATE_NTSC : FRAMERATE_PAL,
|
(gsRegionMode == Region_NTSC) ? FRAMERATE_NTSC : FRAMERATE_PAL,
|
||||||
|
@ -110,36 +108,8 @@ void gsReset()
|
||||||
psHu32(GIF_MODE) = 0;
|
psHu32(GIF_MODE) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gsGIFSoftReset( int mask )
|
|
||||||
{
|
|
||||||
if( GSgifSoftReset == NULL )
|
|
||||||
{
|
|
||||||
static bool warned = false;
|
|
||||||
if( !warned )
|
|
||||||
{
|
|
||||||
Console::Notice( "GIF Warning > Soft reset requested, but the GS plugin doesn't support it!" );
|
|
||||||
//warned = true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mtgsThread->GIFSoftReset( mask );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gsGIFReset()
|
void gsGIFReset()
|
||||||
{
|
{
|
||||||
// fixme - should this be here? (air)
|
|
||||||
//memzero_obj(g_RealGSMem);
|
|
||||||
// none of this should be here, its a GIF reset, not GS, only the dma side of it is reset. (Refraction)
|
|
||||||
|
|
||||||
// perform a soft reset (but do not do a full reset if the soft reset API is unavailable)
|
|
||||||
//gsGIFSoftReset( 7 );
|
|
||||||
|
|
||||||
|
|
||||||
//GSCSRr = 0x551B4000; // Set the FINISH bit to 1 for now
|
|
||||||
//GSIMR = 0x7f00;
|
|
||||||
psHu32(GIF_STAT) = 0;
|
psHu32(GIF_STAT) = 0;
|
||||||
psHu32(GIF_CTRL) = 0;
|
psHu32(GIF_CTRL) = 0;
|
||||||
psHu32(GIF_MODE) = 0;
|
psHu32(GIF_MODE) = 0;
|
||||||
|
@ -147,33 +117,35 @@ void gsGIFReset()
|
||||||
|
|
||||||
void gsCSRwrite(u32 value)
|
void gsCSRwrite(u32 value)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Our emulated GS has no FIFO...
|
|
||||||
/*if( value & 0x100 ) { // FLUSH
|
|
||||||
//Console::WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (value & 0x200) { // resetGS
|
if (value & 0x200) { // resetGS
|
||||||
|
|
||||||
// perform a soft reset -- and fall back to doing a full reset if the plugin doesn't
|
// perform a soft reset -- which is a clearing of all GIFpaths -- and fall back to doing
|
||||||
// support soft resets.
|
// a full reset if the plugin doesn't support soft resets.
|
||||||
|
|
||||||
if( !gsGIFSoftReset( 7 ) )
|
|
||||||
mtgsThread->SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
|
|
||||||
|
|
||||||
|
if( GSgifSoftReset != NULL )
|
||||||
|
{
|
||||||
|
GIFPath_Clear( GIF_PATH_1 );
|
||||||
|
GIFPath_Clear( GIF_PATH_2 );
|
||||||
|
GIFPath_Clear( GIF_PATH_3 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtgsThread.SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
CSRw |= 0x1f;
|
CSRw |= 0x1f;
|
||||||
GSCSRr = 0x551B4000; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib)
|
GSCSRr = 0x551B4000; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib)
|
||||||
GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1
|
GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1
|
||||||
}
|
}
|
||||||
else if( value & 0x100 ) // FLUSH
|
else if( value & 0x100 ) // FLUSH
|
||||||
{
|
{
|
||||||
|
// Our emulated GS has no FIFO, but if it did, it would flush it here...
|
||||||
//Console::WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr);
|
//Console::WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CSRw |= value & 0x1f;
|
CSRw |= value & 0x1f;
|
||||||
mtgsThread->SendSimplePacket( GS_RINGTYPE_WRITECSR, CSRw, 0, 0 );
|
mtgsThread.SendSimplePacket( GS_RINGTYPE_WRITECSR, CSRw, 0, 0 );
|
||||||
GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f);
|
GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +176,7 @@ __forceinline void gsWrite8(u32 mem, u8 value)
|
||||||
gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break;
|
gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break;
|
||||||
default:
|
default:
|
||||||
*PS2GS_BASE(mem) = value;
|
*PS2GS_BASE(mem) = value;
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE8, mem&0x13ff, value, 0);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE8, mem&0x13ff, value, 0);
|
||||||
}
|
}
|
||||||
GIF_LOG("GS write 8 at %8.8lx with data %8.8lx", mem, value);
|
GIF_LOG("GS write 8 at %8.8lx with data %8.8lx", mem, value);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +220,7 @@ __forceinline void gsWrite16(u32 mem, u16 value)
|
||||||
}
|
}
|
||||||
|
|
||||||
*(u16*)PS2GS_BASE(mem) = value;
|
*(u16*)PS2GS_BASE(mem) = value;
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE16, mem&0x13ff, value, 0);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE16, mem&0x13ff, value, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -273,7 +245,7 @@ __forceinline void gsWrite32(u32 mem, u32 value)
|
||||||
}
|
}
|
||||||
|
|
||||||
*(u32*)PS2GS_BASE(mem) = value;
|
*(u32*)PS2GS_BASE(mem) = value;
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE32, mem&0x13ff, value, 0);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE32, mem&0x13ff, value, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -309,7 +281,7 @@ void __fastcall gsWrite64_generic( u32 mem, const mem64_t* value )
|
||||||
GIF_LOG("GS Write64 at %8.8lx with data %8.8x_%8.8x", mem, srcval32[1], srcval32[0]);
|
GIF_LOG("GS Write64 at %8.8lx with data %8.8x_%8.8x", mem, srcval32[1], srcval32[0]);
|
||||||
|
|
||||||
*(u64*)PS2GS_BASE(mem) = *value;
|
*(u64*)PS2GS_BASE(mem) = *value;
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, mem&0x13ff, srcval32[0], srcval32[1]);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE64, mem&0x13ff, srcval32[0], srcval32[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -350,8 +322,8 @@ void __fastcall gsWrite128_generic( u32 mem, const mem128_t* value )
|
||||||
writeTo[0] = value[0];
|
writeTo[0] = value[0];
|
||||||
writeTo[1] = value[1];
|
writeTo[1] = value[1];
|
||||||
|
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem, srcval32[0], srcval32[1]);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem, srcval32[0], srcval32[1]);
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem+8, srcval32[2], srcval32[3]);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem+8, srcval32[2], srcval32[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline u8 gsRead8(u32 mem)
|
__forceinline u8 gsRead8(u32 mem)
|
||||||
|
@ -393,7 +365,7 @@ void gsSyncLimiterLostTime( s32 deltaTime )
|
||||||
|
|
||||||
//Console::WriteLn("LostTime on the EE!");
|
//Console::WriteLn("LostTime on the EE!");
|
||||||
|
|
||||||
mtgsThread->SendSimplePacket(
|
mtgsThread.SendSimplePacket(
|
||||||
GS_RINGTYPE_STARTTIME,
|
GS_RINGTYPE_STARTTIME,
|
||||||
deltaTime,
|
deltaTime,
|
||||||
0,
|
0,
|
||||||
|
@ -537,7 +509,7 @@ __forceinline void gsFrameSkip( bool forceskip )
|
||||||
void gsPostVsyncEnd( bool updategs )
|
void gsPostVsyncEnd( bool updategs )
|
||||||
{
|
{
|
||||||
*(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field
|
*(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field
|
||||||
mtgsThread->PostVsyncEnd( updategs );
|
mtgsThread.PostVsyncEnd( updategs );
|
||||||
}
|
}
|
||||||
|
|
||||||
void _gs_ResetFrameskip()
|
void _gs_ResetFrameskip()
|
||||||
|
@ -548,7 +520,7 @@ void _gs_ResetFrameskip()
|
||||||
// Disables the GS Frameskip at runtime without any racy mess...
|
// Disables the GS Frameskip at runtime without any racy mess...
|
||||||
void gsResetFrameSkip()
|
void gsResetFrameSkip()
|
||||||
{
|
{
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gsDynamicSkipEnable()
|
void gsDynamicSkipEnable()
|
||||||
|
@ -564,5 +536,5 @@ void SaveStateBase::gsFreeze()
|
||||||
{
|
{
|
||||||
FreezeMem(PS2MEM_GS, 0x2000);
|
FreezeMem(PS2MEM_GS, 0x2000);
|
||||||
Freeze(CSRw);
|
Freeze(CSRw);
|
||||||
mtgsFreeze();
|
gifPathFreeze();
|
||||||
}
|
}
|
||||||
|
|
192
pcsx2/GS.h
192
pcsx2/GS.h
|
@ -16,12 +16,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Utilities/Threading.h"
|
#include "SysThreads.h"
|
||||||
|
|
||||||
PCSX2_ALIGNED16( extern u8 g_RealGSMem[0x2000] );
|
PCSX2_ALIGNED16( extern u8 g_RealGSMem[Ps2MemSize::GSregs] );
|
||||||
#define GSCSRr *((u64*)(g_RealGSMem+0x1000))
|
|
||||||
#define GSIMR *((u32*)(g_RealGSMem+0x1010))
|
#define PS2MEM_GS g_RealGSMem
|
||||||
#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080))
|
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
|
||||||
|
|
||||||
|
#define GSCSRr ((u64&)*(g_RealGSMem+0x1000))
|
||||||
|
#define GSIMR ((u32&)*(g_RealGSMem+0x1010))
|
||||||
|
#define GSSIGLBLID ((GSRegSIGBLID&)*(g_RealGSMem+0x1080))
|
||||||
|
|
||||||
enum GS_RegionMode
|
enum GS_RegionMode
|
||||||
{
|
{
|
||||||
|
@ -29,105 +33,6 @@ enum GS_RegionMode
|
||||||
Region_PAL
|
Region_PAL
|
||||||
};
|
};
|
||||||
|
|
||||||
extern GS_RegionMode gsRegionMode;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MTGS GIFtag Parser - Declaration
|
|
||||||
//
|
|
||||||
// The MTGS needs a dummy "GS plugin" for processing SIGNAL, FINISH, and LABEL
|
|
||||||
// commands. These commands trigger gsIRQs, which need to be handled accurately
|
|
||||||
// in synch with the EE (which can be running several frames ahead of the MTGS)
|
|
||||||
//
|
|
||||||
// Yeah, it's a lot of work, but the performance gains are huge, even on HT cpus.
|
|
||||||
|
|
||||||
struct GSRegSIGBLID
|
|
||||||
{
|
|
||||||
u32 SIGID;
|
|
||||||
u32 LBLID;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum GIF_FLG
|
|
||||||
{
|
|
||||||
GIF_FLG_PACKED = 0,
|
|
||||||
GIF_FLG_REGLIST = 1,
|
|
||||||
GIF_FLG_IMAGE = 2,
|
|
||||||
GIF_FLG_IMAGE2 = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
enum GIF_REG
|
|
||||||
{
|
|
||||||
GIF_REG_PRIM = 0x00,
|
|
||||||
GIF_REG_RGBA = 0x01,
|
|
||||||
GIF_REG_STQ = 0x02,
|
|
||||||
GIF_REG_UV = 0x03,
|
|
||||||
GIF_REG_XYZF2 = 0x04,
|
|
||||||
GIF_REG_XYZ2 = 0x05,
|
|
||||||
GIF_REG_TEX0_1 = 0x06,
|
|
||||||
GIF_REG_TEX0_2 = 0x07,
|
|
||||||
GIF_REG_CLAMP_1 = 0x08,
|
|
||||||
GIF_REG_CLAMP_2 = 0x09,
|
|
||||||
GIF_REG_FOG = 0x0a,
|
|
||||||
GIF_REG_XYZF3 = 0x0c,
|
|
||||||
GIF_REG_XYZ3 = 0x0d,
|
|
||||||
GIF_REG_A_D = 0x0e,
|
|
||||||
GIF_REG_NOP = 0x0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
// GIFTAG
|
|
||||||
// Members of this structure are in CAPS to help visually denote that they are representative
|
|
||||||
// of actual hw register states of the GIF, unlike the internal tracking vars in GIFPath, which
|
|
||||||
// are modified during the GIFtag unpacking process.
|
|
||||||
struct GIFTAG
|
|
||||||
{
|
|
||||||
u32 NLOOP : 15;
|
|
||||||
u32 EOP : 1;
|
|
||||||
u32 dummy0 : 16;
|
|
||||||
u32 dummy1 : 14;
|
|
||||||
u32 PRE : 1;
|
|
||||||
u32 PRIM : 11;
|
|
||||||
u32 FLG : 2;
|
|
||||||
u32 NREG : 4;
|
|
||||||
u32 REGS[2];
|
|
||||||
|
|
||||||
GIFTAG() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GIFPath
|
|
||||||
{
|
|
||||||
const GIFTAG tag; // The "original tag -- modification allowed only by SetTag(), so let's make it const.
|
|
||||||
u8 regs[16]; // positioned after tag ensures 16-bit aligned (in case we SSE optimize later)
|
|
||||||
|
|
||||||
u32 nloop; // local copy nloop counts toward zero, and leaves the tag copy unmodified.
|
|
||||||
u32 curreg; // reg we left of on (for traversing through loops)
|
|
||||||
u32 numregs; // number of regs (when NREG is 0, numregs is 16)
|
|
||||||
u8 hasADreg; // has an A+D reg, if it doesn't have one, then it no need to check for gs interrupts
|
|
||||||
|
|
||||||
GIFPath();
|
|
||||||
|
|
||||||
__forceinline void PrepPackedRegs();
|
|
||||||
__forceinline void SetTag(const void* mem);
|
|
||||||
__forceinline bool StepReg() {
|
|
||||||
if ((++curreg & 0xf) == tag.NREG) {
|
|
||||||
curreg = 0;
|
|
||||||
if (--nloop == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
__forceinline u8 GetReg() {
|
|
||||||
return regs[curreg&0xf];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MTGS Threaded Class Declaration
|
|
||||||
|
|
||||||
// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads
|
|
||||||
// and writes stay synchronized. Warning: the debug stack is VERY slow.
|
|
||||||
//#define RINGBUF_DEBUG_STACK
|
|
||||||
|
|
||||||
enum GIF_PATH
|
enum GIF_PATH
|
||||||
{
|
{
|
||||||
GIF_PATH_1 = 0,
|
GIF_PATH_1 = 0,
|
||||||
|
@ -135,6 +40,18 @@ enum GIF_PATH
|
||||||
GIF_PATH_3,
|
GIF_PATH_3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern int GIFPath_ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size);
|
||||||
|
extern void GIFPath_Reset();
|
||||||
|
extern void GIFPath_Clear( GIF_PATH pathidx );
|
||||||
|
|
||||||
|
extern GS_RegionMode gsRegionMode;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MTGS Threaded Class Declaration
|
||||||
|
|
||||||
|
// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads
|
||||||
|
// and writes stay synchronized. Warning: the debug stack is VERY slow.
|
||||||
|
//#define RINGBUF_DEBUG_STACK
|
||||||
|
|
||||||
enum GS_RINGTYPE
|
enum GS_RINGTYPE
|
||||||
{
|
{
|
||||||
|
@ -154,8 +71,8 @@ enum GS_RINGTYPE
|
||||||
, GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF
|
, GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF
|
||||||
, GS_RINGTYPE_WRITECSR
|
, GS_RINGTYPE_WRITECSR
|
||||||
, GS_RINGTYPE_MODECHANGE // for issued mode changes.
|
, GS_RINGTYPE_MODECHANGE // for issued mode changes.
|
||||||
|
, GS_RINGTYPE_CRC
|
||||||
, GS_RINGTYPE_STARTTIME // special case for min==max fps frameskip settings
|
, GS_RINGTYPE_STARTTIME // special case for min==max fps frameskip settings
|
||||||
, GS_RINGTYPE_QUIT
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -165,42 +82,27 @@ struct MTGS_FreezeData
|
||||||
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
|
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
|
||||||
};
|
};
|
||||||
|
|
||||||
class mtgsThreadObject : public Threading::PersistentThread
|
class mtgsThreadObject : public SysSuspendableThread
|
||||||
{
|
{
|
||||||
friend class SaveStateBase;
|
typedef SysSuspendableThread _parent;
|
||||||
|
|
||||||
protected:
|
|
||||||
// Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s.
|
|
||||||
// (actual size is 1<<m_RingBufferSizeFactor simd vectors [128-bit values])
|
|
||||||
// A value of 19 is a 8meg ring buffer. 18 would be 4 megs, and 20 would be 16 megs.
|
|
||||||
// Default was 2mb, but some games with lots of MTGS activity want 8mb to run fast (rama)
|
|
||||||
static const uint m_RingBufferSizeFactor = 19;
|
|
||||||
|
|
||||||
// size of the ringbuffer in simd128's.
|
|
||||||
static const uint m_RingBufferSize = 1<<m_RingBufferSizeFactor;
|
|
||||||
|
|
||||||
// Mask to apply to ring buffer indices to wrap the pointer from end to
|
|
||||||
// start (the wrapping is what makes it a ringbuffer, yo!)
|
|
||||||
static const uint m_RingBufferMask = m_RingBufferSize - 1;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty
|
// note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty
|
||||||
uint m_RingPos; // cur pos gs is reading from
|
uint m_RingPos; // cur pos gs is reading from
|
||||||
uint m_WritePos; // cur pos ee thread is writing to
|
uint m_WritePos; // cur pos ee thread is writing to
|
||||||
|
|
||||||
// used to regulate thread startup and gsInit
|
Semaphore m_sem_OpenDone;
|
||||||
Threading::Semaphore m_sem_InitDone;
|
MutexLock m_lock_RingRestart;
|
||||||
|
|
||||||
Threading::MutexLock m_lock_RingRestart;
|
|
||||||
|
|
||||||
// used to keep multiple threads from sending packets to the ringbuffer concurrently.
|
// used to keep multiple threads from sending packets to the ringbuffer concurrently.
|
||||||
Threading::MutexLock m_PacketLocker;
|
MutexLock m_PacketLocker;
|
||||||
|
|
||||||
// Used to delay the sending of events. Performance is better if the ringbuffer
|
// Used to delay the sending of events. Performance is better if the ringbuffer
|
||||||
// has more than one command in it when the thread is kicked.
|
// has more than one command in it when the thread is kicked.
|
||||||
int m_CopyCommandTally;
|
int m_CopyCommandTally;
|
||||||
int m_CopyDataTally;
|
int m_CopyDataTally;
|
||||||
volatile u32 m_RingBufferIsBusy;
|
volatile bool m_RingBufferIsBusy;
|
||||||
|
volatile bool m_LoadState;
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -209,7 +111,7 @@ protected:
|
||||||
|
|
||||||
// Protection lock for the frame queue counter -- needed because we can't safely
|
// Protection lock for the frame queue counter -- needed because we can't safely
|
||||||
// AtomicExchange from two threads.
|
// AtomicExchange from two threads.
|
||||||
Threading::MutexLock m_lock_FrameQueueCounter;
|
MutexLock m_lock_FrameQueueCounter;
|
||||||
|
|
||||||
// These vars maintain instance data for sending Data Packets.
|
// These vars maintain instance data for sending Data Packets.
|
||||||
// Only one data packet can be constructed and uploaded at a time.
|
// Only one data packet can be constructed and uploaded at a time.
|
||||||
|
@ -221,40 +123,39 @@ protected:
|
||||||
Threading::MutexLock m_lock_Stack;
|
Threading::MutexLock m_lock_Stack;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// contains aligned memory allocations for gs and Ringbuffer.
|
|
||||||
SafeAlignedArray<u128,16> m_RingBuffer;
|
|
||||||
|
|
||||||
// mtgs needs its own memory space separate from the PS2. The PS2 memory is in
|
|
||||||
// sync with the EE while this stays in sync with the GS (ie, it lags behind)
|
|
||||||
u8* const m_gsMem;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mtgsThreadObject();
|
mtgsThreadObject();
|
||||||
virtual ~mtgsThreadObject() throw();
|
virtual ~mtgsThreadObject() throw();
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
void Cancel();
|
void PollStatus();
|
||||||
void Reset();
|
|
||||||
void GIFSoftReset( int mask );
|
|
||||||
|
|
||||||
// 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();
|
||||||
|
void ResetGS();
|
||||||
|
|
||||||
int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size );
|
int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size );
|
||||||
int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size );
|
int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size );
|
||||||
void SendDataPacket();
|
void SendDataPacket();
|
||||||
|
void SendGameCRC( u32 crc );
|
||||||
|
void WaitForOpen();
|
||||||
|
void Freeze( int mode, MTGS_FreezeData& data );
|
||||||
|
|
||||||
void SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 );
|
void SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 );
|
||||||
void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 );
|
void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 );
|
||||||
|
|
||||||
u8* GetDataPacketPtr() const;
|
u8* GetDataPacketPtr() const;
|
||||||
void Freeze( SaveStateBase& state );
|
|
||||||
void SetEvent();
|
void SetEvent();
|
||||||
|
|
||||||
void PostVsyncEnd( bool updategs );
|
void PostVsyncEnd( bool updategs );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void OpenPlugin();
|
||||||
|
void OnSuspendInThread();
|
||||||
|
void OnResumeInThread();
|
||||||
|
|
||||||
|
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.
|
||||||
void PrepEventWait();
|
void PrepEventWait();
|
||||||
|
@ -262,23 +163,15 @@ protected:
|
||||||
// Restores MMX/XMM REGS. For use in surrounding loops that wait on the mtgs.
|
// Restores MMX/XMM REGS. For use in surrounding loops that wait on the mtgs.
|
||||||
void PostEventWait() const;
|
void PostEventWait() const;
|
||||||
|
|
||||||
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
|
|
||||||
// Used to keep interrupts in sync with the EE, while the GS itself
|
|
||||||
// runs potentially several frames behind.
|
|
||||||
int gifTransferDummy(GIF_PATH pathidx, const u8 *pMem, u32 size);
|
|
||||||
int _gifTransferDummy(GIF_PATH pathidx, const u8 *pMem, u32 size);
|
|
||||||
|
|
||||||
// Used internally by SendSimplePacket type functions
|
// Used internally by SendSimplePacket type functions
|
||||||
uint _PrepForSimplePacket();
|
uint _PrepForSimplePacket();
|
||||||
void _FinishSimplePacket( uint future_writepos );
|
void _FinishSimplePacket( uint future_writepos );
|
||||||
void _RingbufferLoop();
|
|
||||||
sptr ExecuteTask();
|
sptr ExecuteTask();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern mtgsThreadObject* mtgsThread;
|
PCSX2_ALIGNED16_EXTERN( mtgsThreadObject mtgsThread );
|
||||||
|
|
||||||
void mtgsWaitGS();
|
void mtgsWaitGS();
|
||||||
void mtgsOpen();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// Generalized GS Functions and Stuff
|
// Generalized GS Functions and Stuff
|
||||||
|
@ -301,7 +194,6 @@ extern void _gs_ChangeTimings( u32 framerate, u32 iTicks );
|
||||||
|
|
||||||
|
|
||||||
// used for resetting GIF fifo
|
// used for resetting GIF fifo
|
||||||
bool gsGIFSoftReset( int mask );
|
|
||||||
void gsGIFReset();
|
void gsGIFReset();
|
||||||
void gsCSRwrite(u32 value);
|
void gsCSRwrite(u32 value);
|
||||||
|
|
||||||
|
|
|
@ -98,12 +98,12 @@ static u32 WRITERING_DMA(u32 *pMem, u32 qwc)
|
||||||
{
|
{
|
||||||
psHu32(GIF_STAT) |= GIF_STAT_APATH3 | GIF_STAT_OPH;
|
psHu32(GIF_STAT) |= GIF_STAT_APATH3 | GIF_STAT_OPH;
|
||||||
|
|
||||||
int size = mtgsThread->PrepDataPacket(GIF_PATH_3, pMem, qwc);
|
int size = mtgsThread.PrepDataPacket(GIF_PATH_3, pMem, qwc);
|
||||||
u8* pgsmem = mtgsThread->GetDataPacketPtr();
|
u8* pgsmem = mtgsThread.GetDataPacketPtr();
|
||||||
|
|
||||||
memcpy_aligned(pgsmem, pMem, size<<4);
|
memcpy_aligned(pgsmem, pMem, size<<4);
|
||||||
|
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ int _GIFchain()
|
||||||
if (pMem == NULL)
|
if (pMem == NULL)
|
||||||
{
|
{
|
||||||
// reset path3, fixes dark cloud 2
|
// reset path3, fixes dark cloud 2
|
||||||
gsGIFSoftReset(4);
|
GIFPath_Clear( GIF_PATH_3 );
|
||||||
|
|
||||||
//must increment madr and clear qwc, else it loops
|
//must increment madr and clear qwc, else it loops
|
||||||
gif->madr += gif->qwc * 16;
|
gif->madr += gif->qwc * 16;
|
||||||
|
|
606
pcsx2/MTGS.cpp
606
pcsx2/MTGS.cpp
|
@ -13,21 +13,19 @@
|
||||||
* If not, see <http://www.gnu.org/licenses/>.
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include <wx/datetime.h>
|
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "VU.h"
|
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
|
||||||
|
#include "VU.h"
|
||||||
#include "iR5900.h"
|
#include "iR5900.h"
|
||||||
#include "VifDma.h"
|
#include "VifDma.h"
|
||||||
|
|
||||||
#include "SamplProf.h"
|
#include "SamplProf.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <wx/datetime.h>
|
||||||
|
|
||||||
// Uncomment this to enable profiling of the GS RingBufferCopy function.
|
// Uncomment this to enable profiling of the GS RingBufferCopy function.
|
||||||
//#define PCSX2_GSRING_SAMPLING_STATS
|
//#define PCSX2_GSRING_SAMPLING_STATS
|
||||||
|
|
||||||
|
@ -46,152 +44,60 @@ using namespace std;
|
||||||
|
|
||||||
#define volatize(x) (*reinterpret_cast<volatile uint*>(&(x)))
|
#define volatize(x) (*reinterpret_cast<volatile uint*>(&(x)))
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// BEGIN -- MTGS GIFtag Parse Implementation
|
|
||||||
//
|
|
||||||
// The MTGS needs a dummy "GS plugin" for processing SIGNAL, FINISH, and LABEL
|
|
||||||
// commands. These commands trigger gsIRQs, which need to be handled accurately
|
|
||||||
// in synch with the EE (which can be running several frames ahead of the MTGS)
|
|
||||||
//
|
|
||||||
// Yeah, it's a lot of work, but the performance gains are huge, even on HT cpus.
|
|
||||||
|
|
||||||
|
// =====================================================================================================
|
||||||
// the MTGS "dummy" GIFtag info!
|
|
||||||
// fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
|
|
||||||
// separate paths. But for that to work properly we need also interlocked path sources.
|
|
||||||
// That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
|
|
||||||
// this is not emulated!
|
|
||||||
PCSX2_ALIGNED16( static GIFPath s_path[3] );
|
|
||||||
|
|
||||||
GIFPath::GIFPath() :
|
|
||||||
tag()
|
|
||||||
{
|
|
||||||
memzero_obj( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpack the registers - registers are stored as a sequence of 4 bit values in the
|
|
||||||
// upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
|
|
||||||
// coming in from paths 2 and 3, so we unpack them into an 8 bit array here.
|
|
||||||
//
|
|
||||||
__forceinline void GIFPath::PrepPackedRegs()
|
|
||||||
{
|
|
||||||
// Only unpack registers if we're starting a new pack. Otherwise the unpacked
|
|
||||||
// array should have already been initialized by a previous partial transfer.
|
|
||||||
|
|
||||||
if (curreg != 0) return;
|
|
||||||
|
|
||||||
u32 tempreg = tag.REGS[0];
|
|
||||||
numregs = ((tag.NREG-1)&0xf) + 1;
|
|
||||||
|
|
||||||
for (u32 i = 0; i < numregs; i++) {
|
|
||||||
if (i == 8) tempreg = tag.REGS[1];
|
|
||||||
regs[i] = tempreg & 0xf;
|
|
||||||
tempreg >>= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__forceinline void GIFPath::SetTag(const void* mem)
|
|
||||||
{
|
|
||||||
const_cast<GIFTAG&>(tag) = *((GIFTAG*)mem);
|
|
||||||
|
|
||||||
nloop = tag.NLOOP;
|
|
||||||
curreg = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _mtgsFreezeGIF( SaveStateBase& state, GIFPath (&paths)[3] )
|
|
||||||
{
|
|
||||||
for(int i=0; i<3; i++ )
|
|
||||||
{
|
|
||||||
state.Freeze( paths[i].tag );
|
|
||||||
state.Freeze( paths[i].nloop );
|
|
||||||
state.Freeze( paths[i].curreg );
|
|
||||||
state.Freeze( paths[i].numregs );
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0; i<3; i++ )
|
|
||||||
{
|
|
||||||
state.Freeze( paths[i].regs );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveStateBase::mtgsFreeze()
|
|
||||||
{
|
|
||||||
FreezeTag( "mtgs" );
|
|
||||||
mtgsThread->Freeze( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void RegHandlerSIGNAL(const u32* data)
|
|
||||||
{
|
|
||||||
MTGS_LOG("MTGS SIGNAL data %x_%x CSRw %x IMR %x CSRr\n",data[0], data[1], CSRw, GSIMR, GSCSRr);
|
|
||||||
|
|
||||||
GSSIGLBLID->SIGID = (GSSIGLBLID->SIGID&~data[1])|(data[0]&data[1]);
|
|
||||||
|
|
||||||
if ((CSRw & 0x1))
|
|
||||||
{
|
|
||||||
if (!(GSIMR&0x100) )
|
|
||||||
{
|
|
||||||
gsIrq();
|
|
||||||
}
|
|
||||||
|
|
||||||
GSCSRr |= 1; // signal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RegHandlerFINISH(const u32* data)
|
|
||||||
{
|
|
||||||
MTGS_LOG("MTGS FINISH data %x_%x CSRw %x\n", data[0], data[1], CSRw);
|
|
||||||
|
|
||||||
if ((CSRw & 0x2))
|
|
||||||
{
|
|
||||||
if (!(GSIMR&0x200))
|
|
||||||
gsIrq();
|
|
||||||
|
|
||||||
GSCSRr |= 2; // finish
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RegHandlerLABEL(const u32* data)
|
|
||||||
{
|
|
||||||
GSSIGLBLID->LBLID = (GSSIGLBLID->LBLID&~data[1])|(data[0]&data[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// END -- MTGS GIFtag Parse Implementation
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MTGS Threaded Class Implementation
|
// MTGS Threaded Class Implementation
|
||||||
|
// =====================================================================================================
|
||||||
|
|
||||||
|
// Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s.
|
||||||
|
// (actual size is 1<<m_RingBufferSizeFactor simd vectors [128-bit values])
|
||||||
|
// A value of 19 is a 8meg ring buffer. 18 would be 4 megs, and 20 would be 16 megs.
|
||||||
|
// Default was 2mb, but some games with lots of MTGS activity want 8mb to run fast (rama)
|
||||||
|
static const uint RingBufferSizeFactor = 19;
|
||||||
|
|
||||||
|
// size of the ringbuffer in simd128's.
|
||||||
|
static const uint RingBufferSize = 1<<RingBufferSizeFactor;
|
||||||
|
|
||||||
|
// Mask to apply to ring buffer indices to wrap the pointer from end to
|
||||||
|
// start (the wrapping is what makes it a ringbuffer, yo!)
|
||||||
|
static const uint RingBufferMask = RingBufferSize - 1;
|
||||||
|
|
||||||
|
PCSX2_ALIGNED16( mtgsThreadObject mtgsThread );
|
||||||
|
|
||||||
|
struct MTGS_BufferedData
|
||||||
|
{
|
||||||
|
u128 m_Ring[RingBufferSize];
|
||||||
|
u8 Regs[Ps2MemSize::GSregs];
|
||||||
|
|
||||||
|
MTGS_BufferedData() {}
|
||||||
|
|
||||||
|
u128& operator[]( uint idx )
|
||||||
|
{
|
||||||
|
jASSUME( idx < RingBufferSize );
|
||||||
|
return m_Ring[idx];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PCSX2_ALIGNED( 32, static MTGS_BufferedData RingBuffer );
|
||||||
|
|
||||||
mtgsThreadObject* mtgsThread = NULL;
|
|
||||||
|
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
#include <list>
|
#include <list>
|
||||||
std::list<uint> ringposStack;
|
std::list<uint> ringposStack;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PCSX2_DEBUG
|
|
||||||
// debug variable used to check for bad code bits where copies are started
|
|
||||||
// but never closed, or closed without having been started. (GSRingBufCopy calls
|
|
||||||
// should always be followed by a call to GSRINGBUF_DONECOPY)
|
|
||||||
// And it's not even used in the debug code.
|
|
||||||
//static int copyLock = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void (*GIFRegHandler)(const u32* data);
|
|
||||||
static GIFRegHandler s_GSHandlers[3] = { RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL };
|
|
||||||
|
|
||||||
mtgsThreadObject::mtgsThreadObject() :
|
mtgsThreadObject::mtgsThreadObject() :
|
||||||
PersistentThread()
|
SysSuspendableThread()
|
||||||
, m_RingPos( 0 )
|
, m_RingPos( 0 )
|
||||||
, m_WritePos( 0 )
|
, m_WritePos( 0 )
|
||||||
|
|
||||||
, m_sem_InitDone()
|
|
||||||
, m_lock_RingRestart()
|
, m_lock_RingRestart()
|
||||||
, m_PacketLocker( true ) // true - makes it a recursive lock
|
, m_PacketLocker( true ) // true - makes it a recursive lock
|
||||||
|
|
||||||
, m_CopyCommandTally( 0 )
|
, m_CopyCommandTally( 0 )
|
||||||
, m_CopyDataTally( 0 )
|
, m_CopyDataTally( 0 )
|
||||||
, m_RingBufferIsBusy( 0 )
|
, m_RingBufferIsBusy( false )
|
||||||
|
, m_LoadState( false )
|
||||||
, m_QueuedFrames( 0 )
|
, m_QueuedFrames( 0 )
|
||||||
, m_lock_FrameQueueCounter()
|
, m_lock_FrameQueueCounter()
|
||||||
, m_packet_size( 0 )
|
, m_packet_size( 0 )
|
||||||
|
@ -200,41 +106,47 @@ mtgsThreadObject::mtgsThreadObject() :
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
, m_lock_Stack()
|
, m_lock_Stack()
|
||||||
#endif
|
#endif
|
||||||
, m_RingBuffer( m_RingBufferSize + (Ps2MemSize::GSregs/sizeof(u128)) )
|
|
||||||
, m_gsMem( (u8*)m_RingBuffer.GetPtr( m_RingBufferSize ) )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::Start()
|
void mtgsThreadObject::Start()
|
||||||
{
|
{
|
||||||
m_sem_InitDone.Reset();
|
m_returncode = 0;
|
||||||
PersistentThread::Start();
|
m_RingPos = 0;
|
||||||
|
m_WritePos = 0;
|
||||||
|
|
||||||
// Wait for the thread to finish initialization (it runs GSopen, which can take
|
m_RingBufferIsBusy = false;
|
||||||
// some time since it's creating a new window and all), and then check for errors.
|
m_LoadState = false;
|
||||||
|
|
||||||
m_sem_InitDone.WaitGui();
|
m_QueuedFrames = 0;
|
||||||
|
m_packet_size = 0;
|
||||||
|
m_packet_ringpos = 0;
|
||||||
|
|
||||||
|
_parent::Start();
|
||||||
|
m_ExecMode = ExecMode_Suspending;
|
||||||
|
SetEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::PollStatus()
|
||||||
|
{
|
||||||
|
if( m_ExecMode == ExecMode_NoThreadYet )
|
||||||
|
{
|
||||||
|
if( m_returncode != 0 ) // means the thread failed to init the GS plugin
|
||||||
|
throw Exception::PluginOpenError( PluginId_GS );
|
||||||
|
}
|
||||||
|
|
||||||
if( m_returncode != 0 ) // means the thread failed to init the GS plugin
|
|
||||||
throw Exception::PluginOpenError( PluginId_GS );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mtgsThreadObject::~mtgsThreadObject() throw()
|
mtgsThreadObject::~mtgsThreadObject() throw()
|
||||||
{
|
{
|
||||||
mtgsThreadObject::Cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the GS "forcefully" without waiting for it to finish rendering it's pending
|
void mtgsThreadObject::OnResumeReady()
|
||||||
// queue of GS data.
|
|
||||||
void mtgsThreadObject::Cancel()
|
|
||||||
{
|
{
|
||||||
//SendSimplePacket( GS_RINGTYPE_QUIT, 0, 0, 0 );
|
m_sem_OpenDone.Reset();
|
||||||
//SetEvent();
|
|
||||||
//m_sem_finished.WaitGui();
|
|
||||||
PersistentThread::Cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::Reset()
|
void mtgsThreadObject::ResetGS()
|
||||||
{
|
{
|
||||||
// MTGS Reset process:
|
// MTGS Reset process:
|
||||||
// * clear the ringbuffer.
|
// * clear the ringbuffer.
|
||||||
|
@ -246,133 +158,9 @@ void mtgsThreadObject::Reset()
|
||||||
MTGS_LOG( "MTGS: Sending Reset..." );
|
MTGS_LOG( "MTGS: Sending Reset..." );
|
||||||
SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
|
SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
|
||||||
SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 );
|
SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 );
|
||||||
|
SetEvent();
|
||||||
|
|
||||||
memzero_obj( s_path );
|
GIFPath_Reset();
|
||||||
}
|
|
||||||
|
|
||||||
#define incTag(x, y) do { \
|
|
||||||
pMem += (x); \
|
|
||||||
size -= (y); \
|
|
||||||
if ((pathidx==GIF_PATH_1)&&(pMem>=vuMemEnd)) pMem -= 0x4000; \
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
#define aMin(x, y) ((x < y) ? (x) : (y))
|
|
||||||
#define subVal(x, y) ((x > y) ? (x-y) : 0 )
|
|
||||||
#define optPrint(x, y) { if (x > y) DevCon::Status("Loops Optimized = %d", x); }
|
|
||||||
|
|
||||||
__forceinline void gsHandler(const u8* pMem) {
|
|
||||||
const int handler = pMem[8];
|
|
||||||
if (handler >= 0x60 && handler < 0x63) {
|
|
||||||
//DevCon::Status("GIF Tag Interrupt");
|
|
||||||
s_GSHandlers[handler&0x3]((const u32*)pMem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parameters:
|
|
||||||
// size (path1) - difference between the end of VU memory and pMem.
|
|
||||||
// size (path2/3) - max size of incoming data stream, in qwc (simd128)
|
|
||||||
__forceinline int mtgsThreadObject::_gifTransferDummy(GIF_PATH pathidx, const u8* pMem, u32 size)
|
|
||||||
{
|
|
||||||
GIFPath& path = s_path[pathidx]; // Current Path
|
|
||||||
const u8* vuMemEnd = pMem + (size<<4); // End of VU1 Mem
|
|
||||||
if (pathidx==GIF_PATH_1) size = 0x400; // VU1 mem size
|
|
||||||
u32 startSize = size; // Start Size
|
|
||||||
|
|
||||||
while (size > 0) {
|
|
||||||
if (!path.nloop) {
|
|
||||||
|
|
||||||
path.SetTag(pMem);
|
|
||||||
incTag(16, 1);
|
|
||||||
|
|
||||||
if (pathidx == GIF_PATH_3) {
|
|
||||||
if (path.tag.FLG&2) Path3progress = IMAGE_MODE;
|
|
||||||
else Path3progress = TRANSFER_MODE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch(path.tag.FLG) {
|
|
||||||
case GIF_FLG_PACKED:
|
|
||||||
path.PrepPackedRegs();
|
|
||||||
do {
|
|
||||||
if (path.GetReg() == 0xe) {
|
|
||||||
gsHandler(pMem);
|
|
||||||
}
|
|
||||||
incTag(16, 1);
|
|
||||||
} while(path.StepReg() && size > 0);
|
|
||||||
break;
|
|
||||||
case GIF_FLG_REGLIST:
|
|
||||||
{
|
|
||||||
size *= 2;
|
|
||||||
|
|
||||||
do { incTag(8, 1); }
|
|
||||||
while(path.StepReg() && size > 0);
|
|
||||||
|
|
||||||
if (size & 1) { incTag(8, 1); }
|
|
||||||
size /= 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GIF_FLG_IMAGE:
|
|
||||||
case GIF_FLG_IMAGE2:
|
|
||||||
{
|
|
||||||
int len = aMin(size, path.nloop);
|
|
||||||
incTag((len * 16), len);
|
|
||||||
path.nloop -= len;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.tag.EOP && !path.nloop) {
|
|
||||||
if (pathidx != GIF_PATH_2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size = (startSize - size);
|
|
||||||
|
|
||||||
if (pathidx == GIF_PATH_3) {
|
|
||||||
if (path.tag.EOP && !path.nloop) {
|
|
||||||
Path3progress = STOPPED_MODE;
|
|
||||||
}
|
|
||||||
gif->madr += size * 16;
|
|
||||||
gif->qwc -= size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
|
|
||||||
// Used to keep interrupts in sync with the EE, while the GS itself
|
|
||||||
// runs potentially several frames behind.
|
|
||||||
// Parameters:
|
|
||||||
// size - max size of incoming data stream, in qwc (simd128)
|
|
||||||
__forceinline int mtgsThreadObject::gifTransferDummy(GIF_PATH pathidx, const u8* pMem, u32 size)
|
|
||||||
{
|
|
||||||
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
|
||||||
static uptr profStartPtr = 0;
|
|
||||||
static uptr profEndPtr = 0;
|
|
||||||
if (profStartPtr == 0) {
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
__beginfunc:
|
|
||||||
mov profStartPtr, offset __beginfunc;
|
|
||||||
mov profEndPtr, offset __endfunc;
|
|
||||||
}
|
|
||||||
ProfilerRegisterSource( "GSRingBufCopy", (void*)profStartPtr, profEndPtr - profStartPtr );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int retSize = _gifTransferDummy(pathidx, pMem, size);
|
|
||||||
|
|
||||||
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
__endfunc:
|
|
||||||
nop;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return retSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::PostVsyncEnd( bool updategs )
|
void mtgsThreadObject::PostVsyncEnd( bool updategs )
|
||||||
|
@ -410,29 +198,74 @@ struct PacketTagType
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool renderswitch;
|
extern bool renderswitch;
|
||||||
|
static volatile long gsIsOpened = 0;
|
||||||
|
|
||||||
static void _clean_close_gs( void* obj )
|
static void _clean_close_gs( void* obj )
|
||||||
{
|
{
|
||||||
if( g_plugins != NULL )
|
int result = InterlockedExchange( &gsIsOpened, 0 );
|
||||||
|
if( result && (g_plugins != NULL) )
|
||||||
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
|
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::_RingbufferLoop()
|
static void dummyIrqCallback()
|
||||||
{
|
{
|
||||||
|
// dummy, because MTGS doesn't need this mess!
|
||||||
|
// (and zerogs does >_<)
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::OpenPlugin()
|
||||||
|
{
|
||||||
|
if( gsIsOpened ) return;
|
||||||
|
|
||||||
|
memcpy_aligned( RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS) );
|
||||||
|
GSsetBaseMem( RingBuffer.Regs );
|
||||||
|
GSirqCallback( dummyIrqCallback );
|
||||||
|
|
||||||
|
if( renderswitch )
|
||||||
|
Console::WriteLn( "\t\tForced software switch enabled." );
|
||||||
|
|
||||||
|
if( GSopen2 != NULL )
|
||||||
|
m_returncode = GSopen2( (void*)&pDsp, 1 | (renderswitch ? 4 : 0) );
|
||||||
|
else
|
||||||
|
m_returncode = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
|
||||||
|
|
||||||
|
gsIsOpened = 1;
|
||||||
|
m_sem_OpenDone.Post();
|
||||||
|
|
||||||
|
if( m_returncode != 0 )
|
||||||
|
{
|
||||||
|
DevCon::WriteLn( "MTGS: GSopen Finished, return code: 0x%x", m_returncode );
|
||||||
|
pthread_exit( (void*)m_returncode );
|
||||||
|
}
|
||||||
|
|
||||||
|
GSCSRr = 0x551B4000; // 0x55190000
|
||||||
|
GSsetGameCRC( ElfCRC, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
sptr mtgsThreadObject::ExecuteTask()
|
||||||
|
{
|
||||||
|
SetName( "MTGS" );
|
||||||
|
|
||||||
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
|
PacketTagType prevCmd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gsIsOpened = false;
|
||||||
pthread_cleanup_push( _clean_close_gs, this );
|
pthread_cleanup_push( _clean_close_gs, this );
|
||||||
while( true )
|
while( true )
|
||||||
{
|
{
|
||||||
m_sem_event.Wait();
|
m_sem_event.Wait(); // ... because this does a cancel test itself..
|
||||||
|
StateCheck( false ); // false disables cancel test here!
|
||||||
|
|
||||||
AtomicExchange( m_RingBufferIsBusy, 1 );
|
m_RingBufferIsBusy = true;
|
||||||
|
|
||||||
// note: m_RingPos is intentionally not volatile, because it should only
|
// note: m_RingPos is intentionally not volatile, because it should only
|
||||||
// ever be modified by this thread.
|
// ever be modified by this thread.
|
||||||
while( m_RingPos != volatize(m_WritePos))
|
while( m_RingPos != volatize(m_WritePos))
|
||||||
{
|
{
|
||||||
wxASSERT( m_RingPos < m_RingBufferSize );
|
wxASSERT( m_RingPos < RingBufferSize );
|
||||||
|
|
||||||
const PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_RingPos];
|
const PacketTagType& tag = (PacketTagType&)RingBuffer[m_RingPos];
|
||||||
u32 ringposinc = 1;
|
u32 ringposinc = 1;
|
||||||
|
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
|
@ -458,12 +291,14 @@ void mtgsThreadObject::_RingbufferLoop()
|
||||||
// stall for a bit to let the MainThread have time to update the g_pGSWritePos.
|
// stall for a bit to let the MainThread have time to update the g_pGSWritePos.
|
||||||
m_lock_RingRestart.Lock();
|
m_lock_RingRestart.Lock();
|
||||||
m_lock_RingRestart.Unlock();
|
m_lock_RingRestart.Unlock();
|
||||||
|
|
||||||
|
StateCheck( false ); // disable cancel since the above locks are cancelable already
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case GS_RINGTYPE_P1:
|
case GS_RINGTYPE_P1:
|
||||||
{
|
{
|
||||||
const int qsize = tag.data[0];
|
const int qsize = tag.data[0];
|
||||||
const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 );
|
const u128* data = &RingBuffer[m_RingPos+1];
|
||||||
|
|
||||||
// make sure that tag>>16 is the MAX size readable
|
// make sure that tag>>16 is the MAX size readable
|
||||||
GSgifTransfer1((u32*)(data - 0x400 + qsize), 0x4000-qsize*16);
|
GSgifTransfer1((u32*)(data - 0x400 + qsize), 0x4000-qsize*16);
|
||||||
|
@ -475,7 +310,7 @@ void mtgsThreadObject::_RingbufferLoop()
|
||||||
case GS_RINGTYPE_P2:
|
case GS_RINGTYPE_P2:
|
||||||
{
|
{
|
||||||
const int qsize = tag.data[0];
|
const int qsize = tag.data[0];
|
||||||
const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 );
|
const u128* data = &RingBuffer[m_RingPos+1];
|
||||||
GSgifTransfer2((u32*)data, qsize);
|
GSgifTransfer2((u32*)data, qsize);
|
||||||
ringposinc += qsize;
|
ringposinc += qsize;
|
||||||
}
|
}
|
||||||
|
@ -484,7 +319,7 @@ void mtgsThreadObject::_RingbufferLoop()
|
||||||
case GS_RINGTYPE_P3:
|
case GS_RINGTYPE_P3:
|
||||||
{
|
{
|
||||||
const int qsize = tag.data[0];
|
const int qsize = tag.data[0];
|
||||||
const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 );
|
const u128* data = &RingBuffer[m_RingPos+1];
|
||||||
GSgifTransfer3((u32*)data, qsize);
|
GSgifTransfer3((u32*)data, qsize);
|
||||||
ringposinc += qsize;
|
ringposinc += qsize;
|
||||||
}
|
}
|
||||||
|
@ -514,16 +349,16 @@ void mtgsThreadObject::_RingbufferLoop()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_MEMWRITE8:
|
case GS_RINGTYPE_MEMWRITE8:
|
||||||
m_gsMem[tag.data[0]] = (u8)tag.data[1];
|
RingBuffer.Regs[tag.data[0]] = (u8)tag.data[1];
|
||||||
break;
|
break;
|
||||||
case GS_RINGTYPE_MEMWRITE16:
|
case GS_RINGTYPE_MEMWRITE16:
|
||||||
*(u16*)(m_gsMem+tag.data[0]) = (u16)tag.data[1];
|
*(u16*)(RingBuffer.Regs+tag.data[0]) = (u16)tag.data[1];
|
||||||
break;
|
break;
|
||||||
case GS_RINGTYPE_MEMWRITE32:
|
case GS_RINGTYPE_MEMWRITE32:
|
||||||
*(u32*)(m_gsMem+tag.data[0]) = tag.data[1];
|
*(u32*)(RingBuffer.Regs+tag.data[0]) = tag.data[1];
|
||||||
break;
|
break;
|
||||||
case GS_RINGTYPE_MEMWRITE64:
|
case GS_RINGTYPE_MEMWRITE64:
|
||||||
*(u64*)(m_gsMem+tag.data[0]) = *(u64*)&tag.data[1];
|
*(u64*)(RingBuffer.Regs+tag.data[0]) = *(u64*)&tag.data[1];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_FREEZE:
|
case GS_RINGTYPE_FREEZE:
|
||||||
|
@ -562,16 +397,15 @@ void mtgsThreadObject::_RingbufferLoop()
|
||||||
case GS_RINGTYPE_MODECHANGE:
|
case GS_RINGTYPE_MODECHANGE:
|
||||||
_gs_ChangeTimings( tag.data[0], tag.data[1] );
|
_gs_ChangeTimings( tag.data[0], tag.data[1] );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GS_RINGTYPE_CRC:
|
||||||
|
GSsetGameCRC( tag.data[0], 0 );
|
||||||
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_STARTTIME:
|
case GS_RINGTYPE_STARTTIME:
|
||||||
m_iSlowStart += tag.data[0];
|
m_iSlowStart += tag.data[0];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GS_RINGTYPE_QUIT:
|
|
||||||
// have to use some low level code, because all the standard Close api does is
|
|
||||||
// trigger this very ringbuffer message!
|
|
||||||
return;
|
|
||||||
|
|
||||||
#ifdef PCSX2_DEVBUILD
|
#ifdef PCSX2_DEVBUILD
|
||||||
default:
|
default:
|
||||||
Console::Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag.command, m_RingPos, m_WritePos);
|
Console::Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag.command, m_RingPos, m_WritePos);
|
||||||
|
@ -585,62 +419,39 @@ void mtgsThreadObject::_RingbufferLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
uint newringpos = m_RingPos + ringposinc;
|
uint newringpos = m_RingPos + ringposinc;
|
||||||
wxASSERT( newringpos <= m_RingBufferSize );
|
wxASSERT( newringpos <= RingBufferSize );
|
||||||
newringpos &= m_RingBufferMask;
|
newringpos &= RingBufferMask;
|
||||||
AtomicExchange( m_RingPos, newringpos );
|
AtomicExchange( m_RingPos, newringpos );
|
||||||
}
|
}
|
||||||
AtomicExchange( m_RingBufferIsBusy, 0 );
|
m_RingBufferIsBusy = false;
|
||||||
}
|
}
|
||||||
pthread_cleanup_pop( true );
|
pthread_cleanup_pop( true );
|
||||||
}
|
|
||||||
|
|
||||||
static void dummyIrqCallback()
|
|
||||||
{
|
|
||||||
// dummy, because MTGS doesn't need this mess!
|
|
||||||
// (and zerogs does >_<)
|
|
||||||
}
|
|
||||||
|
|
||||||
sptr mtgsThreadObject::ExecuteTask()
|
|
||||||
{
|
|
||||||
SetName( "MTGS" );
|
|
||||||
|
|
||||||
memcpy_aligned( m_gsMem, PS2MEM_GS, sizeof(PS2MEM_GS) );
|
|
||||||
GSsetBaseMem( m_gsMem );
|
|
||||||
GSirqCallback( dummyIrqCallback );
|
|
||||||
|
|
||||||
if( renderswitch )
|
|
||||||
Console::WriteLn( "\t\tForced software switch enabled." );
|
|
||||||
|
|
||||||
if( GSopen2 != NULL )
|
|
||||||
m_returncode = GSopen2( (void*)&pDsp, 1 | (renderswitch ? 4 : 0) );
|
|
||||||
else
|
|
||||||
m_returncode = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
|
|
||||||
|
|
||||||
DevCon::WriteLn( "MTGS: GSopen Finished, return code: 0x%x", m_returncode );
|
|
||||||
|
|
||||||
GSCSRr = 0x551B4000; // 0x55190000
|
|
||||||
m_sem_InitDone.Post();
|
|
||||||
if (m_returncode != 0) { return m_returncode; } // error msg will be issued to the user by Plugins.c
|
|
||||||
|
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
|
||||||
PacketTagType prevCmd;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_RingbufferLoop();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::OnSuspendInThread()
|
||||||
|
{
|
||||||
|
_clean_close_gs( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::OnResumeInThread()
|
||||||
|
{
|
||||||
|
if( !m_LoadState )
|
||||||
|
OpenPlugin();
|
||||||
|
}
|
||||||
|
|
||||||
// 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 mtgsThreadObject::WaitGS()
|
void mtgsThreadObject::WaitGS()
|
||||||
{
|
{
|
||||||
// Freeze registers because some kernel code likes to destroy them
|
DevAssert( !IsSelf(), "This method is only allowed from threads *not* named MTGS." );
|
||||||
|
|
||||||
|
if( IsSuspended() ) return;
|
||||||
|
|
||||||
|
// FIXME : Use semaphores instead of spinwaits.
|
||||||
SetEvent();
|
SetEvent();
|
||||||
while( volatize(m_RingPos) != volatize(m_WritePos) )
|
while( volatize(m_RingPos) != volatize(m_WritePos) ) Timeslice();
|
||||||
{
|
|
||||||
Timeslice();
|
|
||||||
//SpinWait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the gsEvent flag and releases a timeslice.
|
// Sets the gsEvent flag and releases a timeslice.
|
||||||
|
@ -665,7 +476,7 @@ void mtgsThreadObject::PostEventWait() const
|
||||||
|
|
||||||
u8* mtgsThreadObject::GetDataPacketPtr() const
|
u8* mtgsThreadObject::GetDataPacketPtr() const
|
||||||
{
|
{
|
||||||
return (u8*)m_RingBuffer.GetPtr( m_packet_ringpos );
|
return (u8*)&RingBuffer[m_packet_ringpos];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the data packet send command, and initiates the gs thread (if needed).
|
// Closes the data packet send command, and initiates the gs thread (if needed).
|
||||||
|
@ -675,12 +486,12 @@ void mtgsThreadObject::SendDataPacket()
|
||||||
jASSUME( m_packet_size != 0 );
|
jASSUME( m_packet_size != 0 );
|
||||||
|
|
||||||
uint temp = m_packet_ringpos + m_packet_size;
|
uint temp = m_packet_ringpos + m_packet_size;
|
||||||
jASSUME( temp <= m_RingBufferSize );
|
jASSUME( temp <= RingBufferSize );
|
||||||
temp &= m_RingBufferMask;
|
temp &= RingBufferMask;
|
||||||
|
|
||||||
if( IsDebugBuild )
|
if( IsDebugBuild )
|
||||||
{
|
{
|
||||||
if( m_packet_ringpos + m_packet_size < m_RingBufferSize )
|
if( m_packet_ringpos + m_packet_size < RingBufferSize )
|
||||||
{
|
{
|
||||||
uint readpos = volatize(m_RingPos);
|
uint readpos = volatize(m_RingPos);
|
||||||
if( readpos != m_WritePos )
|
if( readpos != m_WritePos )
|
||||||
|
@ -742,10 +553,6 @@ static u32 ringtx_inf[32][32];
|
||||||
static u32 ringtx_inf_s[32];
|
static u32 ringtx_inf_s[32];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
|
||||||
static u32 GSRingBufCopySz = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// returns the amount of giftag data not processed (in simd128 values).
|
// returns the amount of giftag data not processed (in simd128 values).
|
||||||
// Return value is used by VU1 XGKICK to hack-fix data packets which are too
|
// Return value is used by VU1 XGKICK to hack-fix data packets which are too
|
||||||
// large for VU1 memory.
|
// large for VU1 memory.
|
||||||
|
@ -810,13 +617,13 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
|
||||||
jASSUME( m_packet_size == 0 );
|
jASSUME( m_packet_size == 0 );
|
||||||
|
|
||||||
// Sanity checks! (within the confines of our ringbuffer please!)
|
// Sanity checks! (within the confines of our ringbuffer please!)
|
||||||
jASSUME( size < m_RingBufferSize );
|
jASSUME( size < RingBufferSize );
|
||||||
jASSUME( writepos < m_RingBufferSize );
|
jASSUME( writepos < RingBufferSize );
|
||||||
|
|
||||||
m_packet_size = gifTransferDummy(pathidx, srcdata, size);
|
m_packet_size = GIFPath_ParseTag(pathidx, srcdata, size);
|
||||||
size = m_packet_size + 1; // takes into account our command qword.
|
size = m_packet_size + 1; // takes into account our command qword.
|
||||||
|
|
||||||
if( writepos + size < m_RingBufferSize )
|
if( writepos + size < RingBufferSize )
|
||||||
{
|
{
|
||||||
// generic gs wait/stall.
|
// generic gs wait/stall.
|
||||||
// if the writepos is past the readpos then we're safe.
|
// if the writepos is past the readpos then we're safe.
|
||||||
|
@ -840,7 +647,7 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
|
||||||
PostEventWait();
|
PostEventWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( writepos + size > m_RingBufferSize )
|
else if( writepos + size > RingBufferSize )
|
||||||
{
|
{
|
||||||
// If the incoming packet doesn't fit, then start over from
|
// If the incoming packet doesn't fit, then start over from
|
||||||
// the start of the ring buffer (it's a lot easier than trying
|
// the start of the ring buffer (it's a lot easier than trying
|
||||||
|
@ -918,7 +725,7 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
|
||||||
// Command qword: Low word is the command, and the high word is the packet
|
// Command qword: Low word is the command, and the high word is the packet
|
||||||
// length in SIMDs (128 bits).
|
// length in SIMDs (128 bits).
|
||||||
|
|
||||||
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos];
|
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
|
||||||
tag.command = pathidx+1;
|
tag.command = pathidx+1;
|
||||||
tag.data[0] = m_packet_size;
|
tag.data[0] = m_packet_size;
|
||||||
m_packet_ringpos = m_WritePos + 1;
|
m_packet_ringpos = m_WritePos + 1;
|
||||||
|
@ -935,9 +742,9 @@ __forceinline uint mtgsThreadObject::_PrepForSimplePacket()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint future_writepos = m_WritePos+1;
|
uint future_writepos = m_WritePos+1;
|
||||||
jASSUME( future_writepos <= m_RingBufferSize );
|
jASSUME( future_writepos <= RingBufferSize );
|
||||||
|
|
||||||
future_writepos &= m_RingBufferMask;
|
future_writepos &= RingBufferMask;
|
||||||
|
|
||||||
if( future_writepos == volatize(m_RingPos) )
|
if( future_writepos == volatize(m_RingPos) )
|
||||||
{
|
{
|
||||||
|
@ -963,7 +770,7 @@ void mtgsThreadObject::SendSimplePacket( GS_RINGTYPE type, int data0, int data1,
|
||||||
//ScopedLock locker( m_PacketLocker );
|
//ScopedLock locker( m_PacketLocker );
|
||||||
|
|
||||||
const uint thefuture = _PrepForSimplePacket();
|
const uint thefuture = _PrepForSimplePacket();
|
||||||
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos];
|
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
|
||||||
|
|
||||||
tag.command = type;
|
tag.command = type;
|
||||||
tag.data[0] = data0;
|
tag.data[0] = data0;
|
||||||
|
@ -978,7 +785,7 @@ void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* dat
|
||||||
//ScopedLock locker( m_PacketLocker );
|
//ScopedLock locker( m_PacketLocker );
|
||||||
|
|
||||||
const uint thefuture = _PrepForSimplePacket();
|
const uint thefuture = _PrepForSimplePacket();
|
||||||
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos];
|
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
|
||||||
|
|
||||||
tag.command = type;
|
tag.command = type;
|
||||||
tag.data[0] = data0;
|
tag.data[0] = data0;
|
||||||
|
@ -987,60 +794,39 @@ void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* dat
|
||||||
_FinishSimplePacket( thefuture );
|
_FinishSimplePacket( thefuture );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::SendGameCRC( u32 crc )
|
||||||
|
{
|
||||||
|
SendSimplePacket( GS_RINGTYPE_CRC, crc, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::WaitForOpen()
|
||||||
|
{
|
||||||
|
if( !gsIsOpened )
|
||||||
|
m_sem_OpenDone.WaitGui();
|
||||||
|
m_sem_OpenDone.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
||||||
|
{
|
||||||
|
if( mode == FREEZE_LOAD )
|
||||||
|
{
|
||||||
|
AtomicExchange( m_RingPos, m_WritePos );
|
||||||
|
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||||
|
m_LoadState = true;
|
||||||
|
SetEvent();
|
||||||
|
Resume();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||||
|
|
||||||
|
mtgsWaitGS();
|
||||||
|
m_LoadState = false;
|
||||||
|
}
|
||||||
|
|
||||||
// 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 mtgsWaitGS()
|
void mtgsWaitGS()
|
||||||
{
|
{
|
||||||
if( mtgsThread == NULL ) return;
|
mtgsThread.WaitGS();
|
||||||
mtgsThread->WaitGS();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exceptions:
|
|
||||||
// ThreadCreationError - Thready could not be created (indicates OS resource limitations)
|
|
||||||
// PluginFailure - GS plugin's "GSopen" call failed.
|
|
||||||
//
|
|
||||||
void mtgsOpen()
|
|
||||||
{
|
|
||||||
// better not be a thread already running, yo!
|
|
||||||
if( mtgsThread != NULL ) return;
|
|
||||||
|
|
||||||
mtgsThread = new mtgsThreadObject();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mtgsThread->Start();
|
|
||||||
}
|
|
||||||
catch( ... )
|
|
||||||
{
|
|
||||||
// if the thread start fails for any reason then set the handle to null.
|
|
||||||
// The handle is used as a NULL test of thread running status, which is why
|
|
||||||
// we really need to do this. :)
|
|
||||||
safe_delete( mtgsThread );
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mtgsThreadObject::GIFSoftReset( int mask )
|
|
||||||
{
|
|
||||||
if(mask & 1) memzero_obj(s_path[0]);
|
|
||||||
if(mask & 2) memzero_obj(s_path[1]);
|
|
||||||
if(mask & 4) memzero_obj(s_path[2]);
|
|
||||||
|
|
||||||
if( GSgifSoftReset == NULL ) return;
|
|
||||||
|
|
||||||
MTGS_LOG( "MTGS: Sending GIF Soft Reset (mask: %d)", mask );
|
|
||||||
mtgsThread->SendSimplePacket( GS_RINGTYPE_SOFTRESET, mask, 0, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void mtgsThreadObject::Freeze( SaveStateBase& state )
|
|
||||||
{
|
|
||||||
_mtgsFreezeGIF( state, s_path );
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function is needed because of recompiled calls from iGS.cpp
|
|
||||||
// (currently used in GCC only)
|
|
||||||
//void mtgsRingBufSimplePacket( s32 command, u32 data0, u32 data1, u32 data2 )
|
|
||||||
//{
|
|
||||||
// mtgsThread->SendSimplePacket( (GS_RINGTYPE)command, data0, data1, data2 );
|
|
||||||
//}
|
|
||||||
|
|
|
@ -38,9 +38,6 @@ extern u8 *psS; //0.015 mb, scratch pad
|
||||||
#define PS2MEM_EROM psER
|
#define PS2MEM_EROM psER
|
||||||
#define PS2MEM_SCRATCH psS
|
#define PS2MEM_SCRATCH psS
|
||||||
|
|
||||||
#define PS2MEM_GS g_RealGSMem
|
|
||||||
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
|
|
||||||
|
|
||||||
// Various useful locations
|
// Various useful locations
|
||||||
#define spr0 ((DMACh*)&PS2MEM_HW[0xD000])
|
#define spr0 ((DMACh*)&PS2MEM_HW[0xD000])
|
||||||
#define spr1 ((DMACh*)&PS2MEM_HW[0xD400])
|
#define spr1 ((DMACh*)&PS2MEM_HW[0xD400])
|
||||||
|
@ -57,11 +54,6 @@ extern u8 *psS; //0.015 mb, scratch pad
|
||||||
#define ipu0dma ((DMACh *)&PS2MEM_HW[0xb000])
|
#define ipu0dma ((DMACh *)&PS2MEM_HW[0xb000])
|
||||||
#define ipu1dma ((DMACh *)&PS2MEM_HW[0xb400])
|
#define ipu1dma ((DMACh *)&PS2MEM_HW[0xb400])
|
||||||
|
|
||||||
// From Gif.h
|
|
||||||
#define GSCSRr *((u64*)(g_RealGSMem+0x1000))
|
|
||||||
#define GSIMR *((u32*)(g_RealGSMem+0x1010))
|
|
||||||
#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080))
|
|
||||||
|
|
||||||
#define PSM(mem) (vtlb_GetPhyPtr((mem)&0x1fffffff)) //pcsx2 is a competition.The one with most hacks wins :D
|
#define PSM(mem) (vtlb_GetPhyPtr((mem)&0x1fffffff)) //pcsx2 is a competition.The one with most hacks wins :D
|
||||||
|
|
||||||
#define psHs8(mem) (*(s8 *)&PS2MEM_HW[(mem) & 0xffff])
|
#define psHs8(mem) (*(s8 *)&PS2MEM_HW[(mem) & 0xffff])
|
||||||
|
|
|
@ -828,16 +828,14 @@ extern void spu2Irq();
|
||||||
|
|
||||||
static bool OpenPlugin_CDVD()
|
static bool OpenPlugin_CDVD()
|
||||||
{
|
{
|
||||||
if( CDVDapi_Plugin.open( NULL ) ) return false;
|
if( CDVDapi_Plugin.open(NULL) ) return false;
|
||||||
CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB );
|
CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool OpenPlugin_GS()
|
static bool OpenPlugin_GS()
|
||||||
{
|
{
|
||||||
if( mtgsThread != NULL ) return true;
|
mtgsThread.Resume();
|
||||||
mtgsOpen(); // mtgsOpen raises its own exception on error
|
|
||||||
GSsetGameCRC( ElfCRC, 0 );
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +846,7 @@ static bool OpenPlugin_PAD()
|
||||||
|
|
||||||
static bool OpenPlugin_SPU2()
|
static bool OpenPlugin_SPU2()
|
||||||
{
|
{
|
||||||
if( SPU2open( (void*)&pDsp ) ) return false;
|
if( SPU2open((void*)&pDsp) ) return false;
|
||||||
|
|
||||||
SPU2irqCallback( spu2Irq, spu2DMA4Irq, spu2DMA7Irq );
|
SPU2irqCallback( spu2Irq, spu2DMA4Irq, spu2DMA7Irq );
|
||||||
if( SPU2setDMABaseAddr != NULL ) SPU2setDMABaseAddr((uptr)psxM);
|
if( SPU2setDMABaseAddr != NULL ) SPU2setDMABaseAddr((uptr)psxM);
|
||||||
|
@ -870,7 +868,7 @@ static bool OpenPlugin_USB()
|
||||||
{
|
{
|
||||||
usbHandler = NULL;
|
usbHandler = NULL;
|
||||||
|
|
||||||
if( USBopen( (void*)&pDsp ) ) return false;
|
if( USBopen((void*)&pDsp) ) return false;
|
||||||
USBirqCallback( usbIrq );
|
USBirqCallback( usbIrq );
|
||||||
usbHandler = USBirqHandler();
|
usbHandler = USBirqHandler();
|
||||||
if( USBsetRAM != NULL )
|
if( USBsetRAM != NULL )
|
||||||
|
@ -880,7 +878,7 @@ static bool OpenPlugin_USB()
|
||||||
|
|
||||||
static bool OpenPlugin_FW()
|
static bool OpenPlugin_FW()
|
||||||
{
|
{
|
||||||
if( FWopen( (void*)&pDsp ) ) return false;
|
if( FWopen((void*)&pDsp) ) return false;
|
||||||
FWirqCallback( fwIrq );
|
FWirqCallback( fwIrq );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -916,9 +914,12 @@ void PluginManager::Open()
|
||||||
Console::Status( "Opening plugins..." );
|
Console::Status( "Opening plugins..." );
|
||||||
|
|
||||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||||
g_plugins->Open( pi->id );
|
Open( pi->id );
|
||||||
} while( ++pi, pi->shortname != NULL );
|
} while( ++pi, pi->shortname != NULL );
|
||||||
|
|
||||||
|
mtgsThread.WaitForOpen();
|
||||||
|
mtgsThread.PollStatus();
|
||||||
|
|
||||||
Console::Status( "Plugins opened successfully." );
|
Console::Status( "Plugins opened successfully." );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -929,11 +930,9 @@ void PluginManager::Close( PluginsEnum_t pid )
|
||||||
|
|
||||||
if( pid == PluginId_GS )
|
if( pid == PluginId_GS )
|
||||||
{
|
{
|
||||||
if( mtgsThread == NULL ) return;
|
|
||||||
|
|
||||||
// force-close PAD before GS, because the PAD depends on the GS window.
|
// force-close PAD before GS, because the PAD depends on the GS window.
|
||||||
Close( PluginId_PAD );
|
Close( PluginId_PAD );
|
||||||
safe_delete( mtgsThread );
|
mtgsThread.Suspend();
|
||||||
}
|
}
|
||||||
else if( pid == PluginId_CDVD )
|
else if( pid == PluginId_CDVD )
|
||||||
DoCDVDclose();
|
DoCDVDclose();
|
||||||
|
@ -1007,6 +1006,8 @@ void PluginManager::Init()
|
||||||
//
|
//
|
||||||
void PluginManager::Shutdown()
|
void PluginManager::Shutdown()
|
||||||
{
|
{
|
||||||
|
mtgsThread.Cancel(); // speedier shutdown!
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
DbgCon::Status( "Shutting down plugins..." );
|
DbgCon::Status( "Shutting down plugins..." );
|
||||||
|
|
||||||
|
@ -1039,10 +1040,10 @@ bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
|
||||||
{
|
{
|
||||||
if( (pid == PluginId_GS) && wxThread::IsMain() )
|
if( (pid == PluginId_GS) && wxThread::IsMain() )
|
||||||
{
|
{
|
||||||
MTGS_FreezeData woot = { data, 0 };
|
|
||||||
// GS needs some thread safety love...
|
// GS needs some thread safety love...
|
||||||
mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &woot );
|
|
||||||
mtgsWaitGS();
|
MTGS_FreezeData woot = { data, 0 };
|
||||||
|
mtgsThread.Freeze( mode, woot );
|
||||||
return woot.retval != -1;
|
return woot.retval != -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
|
|
||||||
#include "App.h"
|
#include "App.h"
|
||||||
#include "HostGui.h"
|
#include "HostGui.h"
|
||||||
|
#include "zlib/zlib.h"
|
||||||
|
|
||||||
|
using namespace Threading;
|
||||||
|
|
||||||
static wxScopedPtr< SafeArray<u8> > g_RecoveryState;
|
static wxScopedPtr< SafeArray<u8> > g_RecoveryState;
|
||||||
|
|
||||||
|
@ -38,11 +41,68 @@ namespace StateRecovery {
|
||||||
StateRecovery::Clear();
|
StateRecovery::Clear();
|
||||||
SysClearExecutionCache();
|
SysClearExecutionCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SafeArray<u8> gzSavingBuffer;
|
||||||
|
|
||||||
|
class gzThreadClass : public PersistentThread
|
||||||
|
{
|
||||||
|
typedef PersistentThread _parent;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
gzFile m_file;
|
||||||
|
|
||||||
|
public:
|
||||||
|
gzThreadClass( const wxString& file ) :
|
||||||
|
m_file( gzopen( file.ToUTF8().data(), "wb" ) )
|
||||||
|
{
|
||||||
|
if( m_file == NULL )
|
||||||
|
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
|
||||||
|
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void DoThreadCleanup()
|
||||||
|
{
|
||||||
|
gzSavingBuffer.Dispose();
|
||||||
|
if( m_file != NULL )
|
||||||
|
{
|
||||||
|
gzclose( m_file );
|
||||||
|
m_file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parent::DoThreadCleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~gzThreadClass() throw()
|
||||||
|
{
|
||||||
|
// fixme: something a little more graceful than Block, perhaps?
|
||||||
|
Block();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int ExecuteTask()
|
||||||
|
{
|
||||||
|
if( (m_file == NULL) || (gzSavingBuffer.GetSizeInBytes() == 0) ) return 0 ;
|
||||||
|
SetName( "Savestate::gzipper" );
|
||||||
|
gzwrite( m_file, gzSavingBuffer.GetPtr(), gzSavingBuffer.GetSizeInBytes() );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wxScopedPtr<gzThreadClass> gzThread;
|
||||||
|
|
||||||
void SaveToFile( const wxString& file )
|
void SaveToFile( const wxString& file )
|
||||||
{
|
{
|
||||||
SafeArray<u8> buf;
|
SysSuspend( false );
|
||||||
memSavingState( buf ).FreezeAll();
|
gzThread.reset( NULL ); // blocks on any existing gzipping business.
|
||||||
|
|
||||||
|
memSavingState( gzSavingBuffer ).FreezeAll();
|
||||||
|
|
||||||
|
// start that encoding thread:
|
||||||
|
gzThread.reset( new gzThreadClass( file ) );
|
||||||
|
|
||||||
|
SysResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves recovery state info to the given saveslot, or saves the active emulation state
|
// Saves recovery state info to the given saveslot, or saves the active emulation state
|
||||||
|
|
|
@ -112,7 +112,7 @@ void SaveStateBase::FreezeBios()
|
||||||
|
|
||||||
if( !m_DidBios )
|
if( !m_DidBios )
|
||||||
{
|
{
|
||||||
if( memcmp( descin, descout, 128 ) != 0 )
|
if( memcmp( descin, descout.ToAscii().data(), 128 ) != 0 )
|
||||||
{
|
{
|
||||||
Console::Error(
|
Console::Error(
|
||||||
"\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n"
|
"\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n"
|
||||||
|
@ -144,8 +144,8 @@ void SaveStateBase::FreezeMainMemory()
|
||||||
|
|
||||||
void SaveStateBase::FreezeRegisters()
|
void SaveStateBase::FreezeRegisters()
|
||||||
{
|
{
|
||||||
if( IsLoading() )
|
//if( IsLoading() )
|
||||||
PreLoadPrep();
|
// PreLoadPrep();
|
||||||
|
|
||||||
// Second Block - Various CPU Registers and States
|
// Second Block - Various CPU Registers and States
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
|
@ -206,7 +206,7 @@ bool SaveStateBase::FreezeSection()
|
||||||
FreezeTag( "BiosVersion" );
|
FreezeTag( "BiosVersion" );
|
||||||
Freeze( sectlen );
|
Freeze( sectlen );
|
||||||
|
|
||||||
if( sectlen != MainMemorySizeInBytes )
|
if( sectlen != 128 )
|
||||||
{
|
{
|
||||||
throw Exception::BadSavedState( wxEmptyString,
|
throw Exception::BadSavedState( wxEmptyString,
|
||||||
L"Invalid size encountered on BiosVersion section.",
|
L"Invalid size encountered on BiosVersion section.",
|
||||||
|
@ -223,6 +223,7 @@ bool SaveStateBase::FreezeSection()
|
||||||
{
|
{
|
||||||
FreezeTag( "MainMemory" );
|
FreezeTag( "MainMemory" );
|
||||||
|
|
||||||
|
int seekpos = m_idx+4;
|
||||||
int sectlen = MainMemorySizeInBytes;
|
int sectlen = MainMemorySizeInBytes;
|
||||||
Freeze( sectlen );
|
Freeze( sectlen );
|
||||||
if( sectlen != MainMemorySizeInBytes )
|
if( sectlen != MainMemorySizeInBytes )
|
||||||
|
@ -234,6 +235,8 @@ bool SaveStateBase::FreezeSection()
|
||||||
}
|
}
|
||||||
|
|
||||||
FreezeMainMemory();
|
FreezeMainMemory();
|
||||||
|
int realsectsize = m_idx - seekpos;
|
||||||
|
wxASSERT( sectlen == realsectsize );
|
||||||
m_sectid++;
|
m_sectid++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -241,7 +244,7 @@ bool SaveStateBase::FreezeSection()
|
||||||
case FreezeId_Registers:
|
case FreezeId_Registers:
|
||||||
{
|
{
|
||||||
FreezeTag( "HardwareRegisters" );
|
FreezeTag( "HardwareRegisters" );
|
||||||
int seekpos = m_idx;
|
int seekpos = m_idx+4;
|
||||||
int sectsize;
|
int sectsize;
|
||||||
Freeze( sectsize );
|
Freeze( sectsize );
|
||||||
|
|
||||||
|
@ -263,13 +266,14 @@ bool SaveStateBase::FreezeSection()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_sectid++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FreezeId_Plugin:
|
case FreezeId_Plugin:
|
||||||
{
|
{
|
||||||
FreezeTag( "Plugin" );
|
FreezeTag( "Plugin" );
|
||||||
int seekpos = m_idx;
|
int seekpos = m_idx+4;
|
||||||
int sectsize;
|
int sectsize;
|
||||||
Freeze( sectsize );
|
Freeze( sectsize );
|
||||||
|
|
||||||
|
@ -296,34 +300,33 @@ bool SaveStateBase::FreezeSection()
|
||||||
|
|
||||||
// following increments only affect Saving mode, are ignored by Loading mode.
|
// following increments only affect Saving mode, are ignored by Loading mode.
|
||||||
m_pid++;
|
m_pid++;
|
||||||
if( m_pid > PluginId_Count )
|
if( m_pid >= PluginId_Count )
|
||||||
m_sectid++;
|
m_sectid = FreezeId_End;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FreezeId_Unknown:
|
case FreezeId_Unknown:
|
||||||
default:
|
default:
|
||||||
if( IsSaving() )
|
wxASSERT( IsSaving() );
|
||||||
m_sectid = FreezeId_End;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Skip unknown sections with a warning log.
|
|
||||||
// Maybe it'll work! (haha?)
|
|
||||||
|
|
||||||
int size;
|
// Skip unknown sections with a warning log.
|
||||||
Freeze( m_tagspace );
|
// Maybe it'll work! (haha?)
|
||||||
Freeze( size );
|
|
||||||
m_tagspace[sizeof(m_tagspace)-1] = 0;
|
|
||||||
|
|
||||||
Console::Notice(
|
int size;
|
||||||
"Warning: Unknown tag encountered while loading savestate; going to ignore it!\n"
|
Freeze( m_tagspace );
|
||||||
"\tTagname: %s, Size: %d", m_tagspace, size
|
Freeze( size );
|
||||||
);
|
m_tagspace[sizeof(m_tagspace)-1] = 0;
|
||||||
m_idx += size;
|
|
||||||
}
|
Console::Notice(
|
||||||
|
"Warning: Unknown tag encountered while loading savestate; going to ignore it!\n"
|
||||||
|
"\tTagname: %s, Size: %d", m_tagspace, size
|
||||||
|
);
|
||||||
|
m_idx += size;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxSafeYield( NULL );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +335,7 @@ void SaveStateBase::FreezeAll()
|
||||||
m_sectid = (int)FreezeId_End+1;
|
m_sectid = (int)FreezeId_End+1;
|
||||||
m_pid = PluginId_GS;
|
m_pid = PluginId_GS;
|
||||||
|
|
||||||
while( FreezeSection() ) ;
|
while( FreezeSection() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -36,13 +36,13 @@ enum FreezeSectionId
|
||||||
{
|
{
|
||||||
FreezeId_End,
|
FreezeId_End,
|
||||||
|
|
||||||
FreezeId_Memory,
|
|
||||||
FreezeId_Registers,
|
|
||||||
|
|
||||||
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
|
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
|
||||||
// but can be skipped if the savestate has only plugins.
|
// but can be skipped if the savestate has only plugins.
|
||||||
FreezeId_Bios,
|
FreezeId_Bios,
|
||||||
|
|
||||||
|
FreezeId_Memory,
|
||||||
|
FreezeId_Registers,
|
||||||
|
|
||||||
FreezeId_Plugin,
|
FreezeId_Plugin,
|
||||||
|
|
||||||
// anything here and beyond we can skip, with a warning
|
// anything here and beyond we can skip, with a warning
|
||||||
|
@ -159,9 +159,7 @@ protected:
|
||||||
void psxRcntFreeze();
|
void psxRcntFreeze();
|
||||||
void sio2Freeze();
|
void sio2Freeze();
|
||||||
|
|
||||||
// called by gsFreeze automatically.
|
void gifPathFreeze(); // called by gsFreeze
|
||||||
void mtgsFreeze();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -263,6 +263,20 @@ void SysClearExecutionCache()
|
||||||
void SysLoadState( const wxString& srcfile )
|
void SysLoadState( const wxString& srcfile )
|
||||||
{
|
{
|
||||||
SafeArray<u8> buf;
|
SafeArray<u8> buf;
|
||||||
|
gzFile gzfp = gzopen( srcfile.ToUTF8().data(), "rb" );
|
||||||
|
if( gzfp == NULL )
|
||||||
|
throw Exception::BadSavedState( srcfile, "File not found, or permission denied!" );
|
||||||
|
|
||||||
|
int curidx = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
buf.MakeRoomFor( curidx+327680 );
|
||||||
|
gzread( gzfp, buf.GetPtr(curidx), 327680 );
|
||||||
|
curidx += 327680;
|
||||||
|
} while( !gzeof(gzfp) );
|
||||||
|
|
||||||
|
gzclose( gzfp );
|
||||||
|
|
||||||
memLoadingState joe( buf ); // this could throw n StateLoadError.
|
memLoadingState joe( buf ); // this could throw n StateLoadError.
|
||||||
|
|
||||||
// we perform a full backup to memory first so that we can restore later if the
|
// we perform a full backup to memory first so that we can restore later if the
|
||||||
|
@ -272,7 +286,8 @@ void SysLoadState( const wxString& srcfile )
|
||||||
|
|
||||||
SysClearExecutionCache();
|
SysClearExecutionCache();
|
||||||
cpuReset();
|
cpuReset();
|
||||||
joe.FreezeAll();
|
//joe.FreezeAll();
|
||||||
|
StateRecovery::Recover();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maps a block of memory for use as a recompiled code buffer, and ensures that the
|
// Maps a block of memory for use as a recompiled code buffer, and ensures that the
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
|
#include "SysThreads.h"
|
||||||
#include "SaveState.h"
|
#include "SaveState.h"
|
||||||
#include "Elfheader.h"
|
#include "Elfheader.h"
|
||||||
#include "Plugins.h"
|
#include "Plugins.h"
|
||||||
#include "CoreEmuThread.h"
|
|
||||||
|
|
||||||
#include "R5900.h"
|
#include "R5900.h"
|
||||||
#include "R3000A.h"
|
#include "R3000A.h"
|
||||||
|
@ -27,6 +27,248 @@
|
||||||
|
|
||||||
static __threadlocal SysCoreThread* tls_coreThread = NULL;
|
static __threadlocal SysCoreThread* tls_coreThread = NULL;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// SysSuspendableThread *External Thread* Implementations
|
||||||
|
// (Called form outside the context of this thread)
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SysSuspendableThread::SysSuspendableThread() :
|
||||||
|
m_ExecMode( ExecMode_NoThreadYet )
|
||||||
|
, m_lock_ExecMode()
|
||||||
|
, m_ResumeEvent()
|
||||||
|
, m_SuspendEvent()
|
||||||
|
, m_ResumeProtection( false )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SysSuspendableThread::~SysSuspendableThread() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysSuspendableThread::Start()
|
||||||
|
{
|
||||||
|
if( !DevAssert( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
|
||||||
|
|
||||||
|
m_ResumeEvent.Reset();
|
||||||
|
m_SuspendEvent.Reset();
|
||||||
|
|
||||||
|
_parent::Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
|
||||||
|
// thread; or does nothing if the core is already suspended. Calling this thread from the
|
||||||
|
// Core thread will result in deadlock.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// isNonblocking - if set to true then the function will not block for emulation suspension.
|
||||||
|
// Defaults to false if parameter is not specified. Performing non-blocking suspension
|
||||||
|
// is mostly useful for starting certain non-Emu related gui activities (improves gui
|
||||||
|
// responsiveness).
|
||||||
|
//
|
||||||
|
void SysSuspendableThread::Suspend( bool isBlocking )
|
||||||
|
{
|
||||||
|
if( IsSelf() || !IsRunning() ) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopedLock locker( m_lock_ExecMode );
|
||||||
|
|
||||||
|
if( m_ExecMode == ExecMode_Suspended )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_ExecMode == ExecMode_Running )
|
||||||
|
m_ExecMode = ExecMode_Suspending;
|
||||||
|
|
||||||
|
DevAssert( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspended..." );
|
||||||
|
}
|
||||||
|
m_sem_event.Post();
|
||||||
|
m_SuspendEvent.WaitGui();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 SysSuspendableThread::Resume()
|
||||||
|
{
|
||||||
|
if( IsSelf() ) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopedLock locker( m_lock_ExecMode );
|
||||||
|
|
||||||
|
switch( m_ExecMode )
|
||||||
|
{
|
||||||
|
case ExecMode_Running: return;
|
||||||
|
|
||||||
|
case ExecMode_NoThreadYet:
|
||||||
|
Start();
|
||||||
|
m_ExecMode = ExecMode_Suspending;
|
||||||
|
// fall through...
|
||||||
|
|
||||||
|
case ExecMode_Suspending:
|
||||||
|
// we need to make sure and wait for the emuThread to enter a fully suspended
|
||||||
|
// state before continuing...
|
||||||
|
|
||||||
|
locker.Unlock(); // no deadlocks please, thanks. :)
|
||||||
|
m_SuspendEvent.WaitGui();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DevAssert( m_ExecMode == ExecMode_Suspended,
|
||||||
|
"SysSuspendableThread is not in a suspended/idle state? wtf!" );
|
||||||
|
|
||||||
|
m_ExecMode = ExecMode_Running;
|
||||||
|
m_ResumeProtection = true;
|
||||||
|
OnResumeReady();
|
||||||
|
m_ResumeProtection = false;
|
||||||
|
m_ResumeEvent.Post();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// SysSuspendableThread *Worker* Implementations
|
||||||
|
// (Called from the context of this thread only)
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void SysSuspendableThread::DoThreadCleanup()
|
||||||
|
{
|
||||||
|
ScopedLock locker( m_lock_ExecMode );
|
||||||
|
m_ExecMode = ExecMode_NoThreadYet;
|
||||||
|
_parent::DoThreadCleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysSuspendableThread::StateCheck( bool isCancelable )
|
||||||
|
{
|
||||||
|
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
||||||
|
if( m_ExecMode == ExecMode_Running )
|
||||||
|
{
|
||||||
|
if( isCancelable ) pthread_testcancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oh, seems we need a full lock, because something special is happening!
|
||||||
|
ScopedLock locker( m_lock_ExecMode );
|
||||||
|
|
||||||
|
switch( m_ExecMode )
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds.
|
||||||
|
case ExecMode_NoThreadYet:
|
||||||
|
// threads should never have this state set while the thread is in any way
|
||||||
|
// active or alive. (for obvious reasons!!)
|
||||||
|
DevAssert( false, "Invalid execution state detected." );
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case ExecMode_Running:
|
||||||
|
// Yup, need this a second time. Variable state could have changed while we
|
||||||
|
// were trying to acquire the lock above.
|
||||||
|
if( isCancelable )
|
||||||
|
pthread_testcancel();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExecMode_Suspending:
|
||||||
|
{
|
||||||
|
OnSuspendInThread();
|
||||||
|
m_ExecMode = ExecMode_Suspended;
|
||||||
|
m_SuspendEvent.Post();
|
||||||
|
}
|
||||||
|
// fall through...
|
||||||
|
|
||||||
|
case ExecMode_Suspended:
|
||||||
|
m_lock_ExecMode.Unlock();
|
||||||
|
while( m_ExecMode == ExecMode_Suspended )
|
||||||
|
m_ResumeEvent.WaitGui();
|
||||||
|
|
||||||
|
OnResumeInThread();
|
||||||
|
break;
|
||||||
|
|
||||||
|
jNO_DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// EECoreThread *External Thread* Implementations
|
||||||
|
// (Called form outside the context of this thread)
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SysCoreThread::SysCoreThread( PluginManager& plugins ) :
|
||||||
|
m_resetRecompilers( false )
|
||||||
|
, m_resetProfilers( false )
|
||||||
|
, m_shortSuspend( false )
|
||||||
|
, m_plugins( plugins )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SysCoreThread::~SysCoreThread() throw()
|
||||||
|
{
|
||||||
|
_parent::Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysCoreThread::Start()
|
||||||
|
{
|
||||||
|
m_plugins.Init();
|
||||||
|
_parent::Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspends the system without closing plugins or updating GUI status.
|
||||||
|
// Should be used for savestates or other actions which happen very quickly.
|
||||||
|
void SysCoreThread::ShortSuspend()
|
||||||
|
{
|
||||||
|
m_shortSuspend = true;
|
||||||
|
Suspend();
|
||||||
|
m_shortSuspend = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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_shortSuspend ) return;
|
||||||
|
|
||||||
|
if( m_resetRecompilers || m_resetProfilers )
|
||||||
|
{
|
||||||
|
SysClearExecutionCache();
|
||||||
|
m_resetRecompilers = false;
|
||||||
|
m_resetProfilers = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = !m_ResumeProtection && !IsSuspended();
|
||||||
|
if( !m_ResumeProtection ) Suspend();
|
||||||
|
|
||||||
|
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
|
||||||
|
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
|
||||||
|
const_cast<Pcsx2Config&>(EmuConfig) = src;
|
||||||
|
|
||||||
|
if( resumeWhenDone ) Resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// EECoreThread *Worker* Implementations
|
||||||
|
// (Called from the context of this thread only)
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
SysCoreThread& SysCoreThread::Get()
|
SysCoreThread& SysCoreThread::Get()
|
||||||
{
|
{
|
||||||
wxASSERT_MSG( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." );
|
wxASSERT_MSG( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." );
|
||||||
|
@ -35,10 +277,8 @@ SysCoreThread& SysCoreThread::Get()
|
||||||
|
|
||||||
void SysCoreThread::CpuInitializeMess()
|
void SysCoreThread::CpuInitializeMess()
|
||||||
{
|
{
|
||||||
m_plugins.Open();
|
|
||||||
cpuReset();
|
cpuReset();
|
||||||
SysClearExecutionCache();
|
SysClearExecutionCache();
|
||||||
m_plugins.Open();
|
|
||||||
|
|
||||||
if( StateRecovery::HasState() )
|
if( StateRecovery::HasState() )
|
||||||
{
|
{
|
||||||
|
@ -86,8 +326,6 @@ void SysCoreThread::CpuInitializeMess()
|
||||||
loadElfFile( elf_file );
|
loadElfFile( elf_file );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GSsetGameCRC( ElfCRC, 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// special macro which disables inlining on functions that require their own function stackframe.
|
// special macro which disables inlining on functions that require their own function stackframe.
|
||||||
|
@ -119,11 +357,7 @@ sptr SysCoreThread::ExecuteTask()
|
||||||
SetName( "EE Core" );
|
SetName( "EE Core" );
|
||||||
tls_coreThread = this;
|
tls_coreThread = this;
|
||||||
|
|
||||||
while( m_ExecMode != ExecMode_Running )
|
StateCheck();
|
||||||
{
|
|
||||||
m_ResumeEvent.WaitGui();
|
|
||||||
}
|
|
||||||
|
|
||||||
CpuInitializeMess();
|
CpuInitializeMess();
|
||||||
StateCheck();
|
StateCheck();
|
||||||
CpuExecute();
|
CpuExecute();
|
||||||
|
@ -131,170 +365,22 @@ sptr SysCoreThread::ExecuteTask()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysCoreThread::StateCheck()
|
void SysCoreThread::OnSuspendInThread()
|
||||||
{
|
{
|
||||||
ScopedLock locker( m_lock_ExecMode );
|
if( !m_shortSuspend )
|
||||||
|
m_plugins.Close();
|
||||||
switch( m_ExecMode )
|
|
||||||
{
|
|
||||||
case ExecMode_NoThreadYet:
|
|
||||||
case ExecMode_Idle:
|
|
||||||
// threads should never have an idle execution state set while the
|
|
||||||
// thread is in any way active or alive.
|
|
||||||
DevAssert( false, "Invalid execution state detected." );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ExecMode_Running:
|
|
||||||
pthread_testcancel();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ExecMode_Suspending:
|
|
||||||
{
|
|
||||||
m_plugins.Close();
|
|
||||||
m_ExecMode = ExecMode_Suspended;
|
|
||||||
m_SuspendEvent.Post();
|
|
||||||
}
|
|
||||||
// fall through...
|
|
||||||
|
|
||||||
case ExecMode_Suspended:
|
|
||||||
m_lock_ExecMode.Unlock();
|
|
||||||
while( m_ExecMode == ExecMode_Suspended )
|
|
||||||
m_ResumeEvent.WaitGui();
|
|
||||||
|
|
||||||
m_plugins.Open();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SysCoreThread::SysCoreThread( PluginManager& plugins ) :
|
void SysCoreThread::OnResumeInThread()
|
||||||
m_ExecMode( ExecMode_NoThreadYet )
|
|
||||||
, m_lock_ExecMode()
|
|
||||||
|
|
||||||
, m_resetRecompilers( false )
|
|
||||||
, m_resetProfilers( false )
|
|
||||||
|
|
||||||
, m_plugins( plugins )
|
|
||||||
, m_ResumeEvent()
|
|
||||||
, m_SuspendEvent()
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
m_plugins.Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Invoked by the pthread_exit or pthread_cancel
|
// Invoked by the pthread_exit or pthread_cancel
|
||||||
void SysCoreThread::DoThreadCleanup()
|
void SysCoreThread::DoThreadCleanup()
|
||||||
{
|
{
|
||||||
m_plugins.Shutdown();
|
m_plugins.Shutdown();
|
||||||
PersistentThread::DoThreadCleanup();
|
_parent::DoThreadCleanup();
|
||||||
}
|
|
||||||
|
|
||||||
SysCoreThread::~SysCoreThread() throw()
|
|
||||||
{
|
|
||||||
PersistentThread::Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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::Resume()
|
|
||||||
{
|
|
||||||
if( IsSelf() ) return;
|
|
||||||
|
|
||||||
{
|
|
||||||
ScopedLock locker( m_lock_ExecMode );
|
|
||||||
|
|
||||||
if( m_ExecMode == ExecMode_NoThreadYet )
|
|
||||||
{
|
|
||||||
m_plugins.Init();
|
|
||||||
Start();
|
|
||||||
m_ExecMode = ExecMode_Idle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_ExecMode == ExecMode_Running )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( m_ExecMode == ExecMode_Suspending )
|
|
||||||
{
|
|
||||||
// if there are resets to be done, then we need to make sure and wait for the
|
|
||||||
// emuThread to enter a fully suspended state before continuing...
|
|
||||||
|
|
||||||
if( m_resetRecompilers || m_resetProfilers )
|
|
||||||
{
|
|
||||||
locker.Unlock(); // no deadlocks please, thanks. :)
|
|
||||||
m_SuspendEvent.WaitGui();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ExecMode = ExecMode_Running;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DevAssert( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Idle),
|
|
||||||
"EmuCoreThread is not in a suspended or idle state? wtf!" );
|
|
||||||
|
|
||||||
OnResumeReady();
|
|
||||||
|
|
||||||
if( m_resetRecompilers || m_resetProfilers )
|
|
||||||
{
|
|
||||||
SysClearExecutionCache();
|
|
||||||
m_resetRecompilers = false;
|
|
||||||
m_resetProfilers = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ExecMode = ExecMode_Running;
|
|
||||||
m_ResumeEvent.Post();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
|
|
||||||
// thread; or does nothing if the core is already suspended. Calling this thread from the
|
|
||||||
// Core thread will result in deadlock.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// isNonblocking - if set to true then the function will not block for emulation suspension.
|
|
||||||
// Defaults to false if parameter is not specified. Performing non-blocking suspension
|
|
||||||
// is mostly useful for starting certain non-Emu related gui activities (improves gui
|
|
||||||
// responsiveness).
|
|
||||||
//
|
|
||||||
void SysCoreThread::Suspend( bool isBlocking )
|
|
||||||
{
|
|
||||||
if( IsSelf() || !IsRunning() ) return;
|
|
||||||
|
|
||||||
{
|
|
||||||
ScopedLock locker( m_lock_ExecMode );
|
|
||||||
|
|
||||||
if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Idle) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( m_ExecMode == ExecMode_Running )
|
|
||||||
m_ExecMode = ExecMode_Suspending;
|
|
||||||
|
|
||||||
DevAssert( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspended..." );
|
|
||||||
}
|
|
||||||
|
|
||||||
m_SuspendEvent.WaitGui();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 isSuspended = IsSuspended();
|
|
||||||
Suspend();
|
|
||||||
|
|
||||||
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
|
|
||||||
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
|
|
||||||
const_cast<Pcsx2Config&>(EmuConfig) = src;
|
|
||||||
|
|
||||||
if( !isSuspended ) Resume();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2059,8 +2059,8 @@ void _vuXGKICK(VURegs * VU)
|
||||||
|
|
||||||
u8* data = ((u8*)VU->Mem + ((VU->VI[_Is_].US[0]*16) & 0x3fff));
|
u8* data = ((u8*)VU->Mem + ((VU->VI[_Is_].US[0]*16) & 0x3fff));
|
||||||
u32 size;
|
u32 size;
|
||||||
size = mtgsThread->PrepDataPacket( GIF_PATH_1, data, (0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)) >> 4);
|
size = mtgsThread.PrepDataPacket( GIF_PATH_1, data, (0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)) >> 4);
|
||||||
u8* pmem = mtgsThread->GetDataPacketPtr();
|
u8* pmem = mtgsThread.GetDataPacketPtr();
|
||||||
|
|
||||||
if((size << 4) > (u32)(0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)))
|
if((size << 4) > (u32)(0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)))
|
||||||
{
|
{
|
||||||
|
@ -2074,7 +2074,7 @@ void _vuXGKICK(VURegs * VU)
|
||||||
else {
|
else {
|
||||||
memcpy_aligned(pmem, (u8*)VU->Mem+((VU->VI[_Is_].US[0]*16) & 0x3fff), size<<4);
|
memcpy_aligned(pmem, (u8*)VU->Mem+((VU->VI[_Is_].US[0]*16) & 0x3fff), size<<4);
|
||||||
}
|
}
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _vuXTOP(VURegs * VU) {
|
void _vuXTOP(VURegs * VU) {
|
||||||
|
|
|
@ -1887,12 +1887,12 @@ static int __fastcall Vif1TransDirectHL(u32 *data)
|
||||||
FreezeRegs(1);
|
FreezeRegs(1);
|
||||||
// copy 16 bytes the fast way:
|
// copy 16 bytes the fast way:
|
||||||
const u64* src = (u64*)splittransfer[0];
|
const u64* src = (u64*)splittransfer[0];
|
||||||
mtgsThread->PrepDataPacket(GIF_PATH_2, nloop0_packet, 1);
|
mtgsThread.PrepDataPacket(GIF_PATH_2, nloop0_packet, 1);
|
||||||
u64* dst = (u64*)mtgsThread->GetDataPacketPtr();
|
u64* dst = (u64*)mtgsThread.GetDataPacketPtr();
|
||||||
dst[0] = src[0];
|
dst[0] = src[0];
|
||||||
dst[1] = src[1];
|
dst[1] = src[1];
|
||||||
|
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
FreezeRegs(0);
|
FreezeRegs(0);
|
||||||
|
|
||||||
if (vif1.tag.size == 0) vif1.cmd = 0;
|
if (vif1.tag.size == 0) vif1.cmd = 0;
|
||||||
|
@ -1928,9 +1928,9 @@ static int __fastcall Vif1TransDirectHL(u32 *data)
|
||||||
FreezeRegs(1);
|
FreezeRegs(1);
|
||||||
|
|
||||||
// Round ret up, just in case it's not 128bit aligned.
|
// Round ret up, just in case it's not 128bit aligned.
|
||||||
const uint count = mtgsThread->PrepDataPacket(GIF_PATH_2, data, (ret + 3) >> 2);
|
const uint count = mtgsThread.PrepDataPacket(GIF_PATH_2, data, (ret + 3) >> 2);
|
||||||
memcpy_fast(mtgsThread->GetDataPacketPtr(), data, count << 4);
|
memcpy_fast(mtgsThread.GetDataPacketPtr(), data, count << 4);
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
|
|
||||||
FreezeRegs(0);
|
FreezeRegs(0);
|
||||||
|
|
||||||
|
|
|
@ -219,10 +219,11 @@ typedef HashTools::Dictionary<const GlobalCommandDescriptor*> CommandDictionary;
|
||||||
|
|
||||||
class AcceleratorDictionary : public HashTools::HashMap<int, const GlobalCommandDescriptor*>
|
class AcceleratorDictionary : public HashTools::HashMap<int, const GlobalCommandDescriptor*>
|
||||||
{
|
{
|
||||||
|
typedef HashTools::HashMap<int, const GlobalCommandDescriptor*> _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef HashTools::HashMap<int, const GlobalCommandDescriptor*> _parent;
|
|
||||||
using _parent::operator[];
|
using _parent::operator[];
|
||||||
|
|
||||||
AcceleratorDictionary();
|
AcceleratorDictionary();
|
||||||
|
@ -438,6 +439,8 @@ protected:
|
||||||
|
|
||||||
class AppEmuThread : public SysCoreThread
|
class AppEmuThread : public SysCoreThread
|
||||||
{
|
{
|
||||||
|
typedef SysCoreThread _parent;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxKeyEvent m_kevt;
|
wxKeyEvent m_kevt;
|
||||||
|
|
||||||
|
@ -446,7 +449,7 @@ public:
|
||||||
virtual ~AppEmuThread() throw();
|
virtual ~AppEmuThread() throw();
|
||||||
|
|
||||||
virtual void Suspend( bool isBlocking=true );
|
virtual void Suspend( bool isBlocking=true );
|
||||||
virtual void StateCheck();
|
virtual void StateCheck( bool isCancelable=true );
|
||||||
virtual void ApplySettings( const Pcsx2Config& src );
|
virtual void ApplySettings( const Pcsx2Config& src );
|
||||||
virtual void OnResumeReady();
|
virtual void OnResumeReady();
|
||||||
|
|
||||||
|
@ -484,7 +487,7 @@ extern void AppSaveSettings();
|
||||||
extern void AppApplySettings( const AppConfig* oldconf=NULL );
|
extern void AppApplySettings( const AppConfig* oldconf=NULL );
|
||||||
|
|
||||||
extern void SysStatus( const wxString& text );
|
extern void SysStatus( const wxString& text );
|
||||||
extern void SysSuspend();
|
extern void SysSuspend( bool closePlugins = true );
|
||||||
extern void SysResume();
|
extern void SysResume();
|
||||||
extern void SysReset();
|
extern void SysReset();
|
||||||
extern void SysExecute();
|
extern void SysExecute();
|
||||||
|
|
|
@ -77,7 +77,7 @@ AppEmuThread::~AppEmuThread() throw()
|
||||||
|
|
||||||
void AppEmuThread::Suspend( bool isBlocking )
|
void AppEmuThread::Suspend( bool isBlocking )
|
||||||
{
|
{
|
||||||
SysCoreThread::Suspend( isBlocking );
|
_parent::Suspend( isBlocking );
|
||||||
AppInvoke( MainFrame, ApplySettings() );
|
AppInvoke( MainFrame, ApplySettings() );
|
||||||
|
|
||||||
// Clear the sticky key statuses, because hell knows what'll change while the PAD
|
// Clear the sticky key statuses, because hell knows what'll change while the PAD
|
||||||
|
@ -90,7 +90,9 @@ void AppEmuThread::Suspend( bool isBlocking )
|
||||||
|
|
||||||
void AppEmuThread::OnResumeReady()
|
void AppEmuThread::OnResumeReady()
|
||||||
{
|
{
|
||||||
DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." );
|
if( !DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." ) ) return;
|
||||||
|
|
||||||
|
if( m_shortSuspend ) return;
|
||||||
|
|
||||||
ApplySettings( g_Conf->EmuOptions );
|
ApplySettings( g_Conf->EmuOptions );
|
||||||
|
|
||||||
|
@ -109,18 +111,15 @@ static const int pxID_PadHandler_Keydown = 8030;
|
||||||
extern int TranslateGDKtoWXK( u32 keysym );
|
extern int TranslateGDKtoWXK( u32 keysym );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void AppEmuThread::StateCheck()
|
void AppEmuThread::StateCheck( bool isCancelable )
|
||||||
{
|
{
|
||||||
SysCoreThread::StateCheck();
|
_parent::StateCheck( isCancelable );
|
||||||
|
|
||||||
const keyEvent* ev = PADkeyEvent();
|
const keyEvent* ev = PADkeyEvent();
|
||||||
|
|
||||||
if( ev == NULL || (ev->key == 0) ) return;
|
if( ev == NULL || (ev->key == 0) ) return;
|
||||||
|
|
||||||
GetPluginManager().KeyEvent( *ev );
|
GetPluginManager().KeyEvent( *ev );
|
||||||
|
|
||||||
m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP );
|
m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP );
|
||||||
|
|
||||||
const bool isDown = (ev->evt == KEYPRESS);
|
const bool isDown = (ev->evt == KEYPRESS);
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
|
@ -881,9 +880,12 @@ void SysResume()
|
||||||
AppInvoke( CoreThread, Resume() );
|
AppInvoke( CoreThread, Resume() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysSuspend()
|
void SysSuspend( bool closePlugins )
|
||||||
{
|
{
|
||||||
AppInvoke( CoreThread, Suspend() );
|
if( closePlugins )
|
||||||
|
AppInvoke( CoreThread, Suspend(closePlugins) );
|
||||||
|
else
|
||||||
|
AppInvoke( CoreThread, ShortSuspend() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysReset()
|
void SysReset()
|
||||||
|
|
|
@ -141,7 +141,7 @@ namespace Implementations
|
||||||
{
|
{
|
||||||
g_Pcsx2Recording ^= 1;
|
g_Pcsx2Recording ^= 1;
|
||||||
|
|
||||||
mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
|
mtgsThread.SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
|
||||||
if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL);
|
if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +157,10 @@ namespace Implementations
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// CommandDeclarations table
|
// CommandDeclarations table
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
// This is our manualized introspection/reflection table. In a cool language like C# we'd
|
||||||
|
// have just grabbed this info from enumerating the members of a class and assigning
|
||||||
|
// properties to each method in the class. But since this is C++, we have to do the the
|
||||||
|
// goold old fashioned way! :)
|
||||||
|
|
||||||
static const GlobalCommandDescriptor CommandDeclarations[] =
|
static const GlobalCommandDescriptor CommandDeclarations[] =
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,6 +37,8 @@ bool States_isSlotUsed(int num)
|
||||||
// returns true if the new state was loaded, or false if nothing happened.
|
// returns true if the new state was loaded, or false if nothing happened.
|
||||||
void States_Load( const wxString& file )
|
void States_Load( const wxString& file )
|
||||||
{
|
{
|
||||||
|
SysSuspend();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SysLoadState( file );
|
SysLoadState( file );
|
||||||
|
|
|
@ -15,58 +15,4 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Utilities/Threading.h"
|
#include "SysThreads.h"
|
||||||
|
|
||||||
using namespace Threading;
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
// SysCoreThread class
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
class SysCoreThread : public PersistentThread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
enum ExecutionMode
|
|
||||||
{
|
|
||||||
ExecMode_NoThreadYet,
|
|
||||||
ExecMode_Idle,
|
|
||||||
ExecMode_Running,
|
|
||||||
ExecMode_Suspending,
|
|
||||||
ExecMode_Suspended
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
volatile ExecutionMode m_ExecMode;
|
|
||||||
MutexLock m_lock_ExecMode;
|
|
||||||
|
|
||||||
bool m_resetRecompilers;
|
|
||||||
bool m_resetProfilers;
|
|
||||||
|
|
||||||
PluginManager& m_plugins;
|
|
||||||
Semaphore m_ResumeEvent;
|
|
||||||
Semaphore m_SuspendEvent;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static SysCoreThread& Get();
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SysCoreThread( PluginManager& plugins );
|
|
||||||
virtual ~SysCoreThread() throw();
|
|
||||||
|
|
||||||
bool IsSuspended() const { return (m_ExecMode == ExecMode_Suspended); }
|
|
||||||
virtual void Suspend( bool isBlocking = true );
|
|
||||||
virtual void Resume();
|
|
||||||
virtual void ApplySettings( const Pcsx2Config& src );
|
|
||||||
virtual void StateCheck();
|
|
||||||
|
|
||||||
virtual void DoThreadCleanup();
|
|
||||||
|
|
||||||
// This function is called by Resume immediately prior to releasing the suspension of
|
|
||||||
// the core emulation thread. You should overload this rather than Resume(), since
|
|
||||||
// Resume() has a lot of checks and balances to prevent re-entrance and race conditions.
|
|
||||||
virtual void OnResumeReady() {};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void CpuInitializeMess();
|
|
||||||
void CpuExecute();
|
|
||||||
virtual sptr ExecuteTask();
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,377 @@
|
||||||
|
/* PCSX2 - PS2 Emulator for PCs
|
||||||
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||||
|
*
|
||||||
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||||
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
* ation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
#include "Common.h"
|
||||||
|
#include "GS.h"
|
||||||
|
|
||||||
|
#include "VifDma.h"
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// GIFpath -- the GIFtag Parser
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct GSRegSIGBLID
|
||||||
|
{
|
||||||
|
u32 SIGID;
|
||||||
|
u32 LBLID;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GIF_FLG
|
||||||
|
{
|
||||||
|
GIF_FLG_PACKED = 0,
|
||||||
|
GIF_FLG_REGLIST = 1,
|
||||||
|
GIF_FLG_IMAGE = 2,
|
||||||
|
GIF_FLG_IMAGE2 = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GIF_REG
|
||||||
|
{
|
||||||
|
GIF_REG_PRIM = 0x00,
|
||||||
|
GIF_REG_RGBA = 0x01,
|
||||||
|
GIF_REG_STQ = 0x02,
|
||||||
|
GIF_REG_UV = 0x03,
|
||||||
|
GIF_REG_XYZF2 = 0x04,
|
||||||
|
GIF_REG_XYZ2 = 0x05,
|
||||||
|
GIF_REG_TEX0_1 = 0x06,
|
||||||
|
GIF_REG_TEX0_2 = 0x07,
|
||||||
|
GIF_REG_CLAMP_1 = 0x08,
|
||||||
|
GIF_REG_CLAMP_2 = 0x09,
|
||||||
|
GIF_REG_FOG = 0x0a,
|
||||||
|
GIF_REG_XYZF3 = 0x0c,
|
||||||
|
GIF_REG_XYZ3 = 0x0d,
|
||||||
|
GIF_REG_A_D = 0x0e,
|
||||||
|
GIF_REG_NOP = 0x0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
// GIFTAG
|
||||||
|
// Members of this structure are in CAPS to help visually denote that they are representative
|
||||||
|
// of actual hw register states of the GIF, unlike the internal tracking vars in GIFPath, which
|
||||||
|
// are modified during the GIFtag unpacking process.
|
||||||
|
struct GIFTAG
|
||||||
|
{
|
||||||
|
u32 NLOOP : 15;
|
||||||
|
u32 EOP : 1;
|
||||||
|
u32 dummy0 : 16;
|
||||||
|
u32 dummy1 : 14;
|
||||||
|
u32 PRE : 1;
|
||||||
|
u32 PRIM : 11;
|
||||||
|
u32 FLG : 2;
|
||||||
|
u32 NREG : 4;
|
||||||
|
u32 REGS[2];
|
||||||
|
|
||||||
|
GIFTAG() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// GIFPath -- PS2 GIFtag info (one for each path).
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
|
||||||
|
// separate paths. But for that to work properly we need also interlocked path sources.
|
||||||
|
// That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
|
||||||
|
// this is not emulated!
|
||||||
|
|
||||||
|
struct GIFPath
|
||||||
|
{
|
||||||
|
const GIFTAG tag; // The "original tag -- modification allowed only by SetTag(), so let's make it const.
|
||||||
|
u8 regs[16]; // positioned after tag ensures 16-bit aligned (in case we SSE optimize later)
|
||||||
|
|
||||||
|
u32 nloop; // local copy nloop counts toward zero, and leaves the tag copy unmodified.
|
||||||
|
u32 curreg; // reg we left of on (for traversing through loops)
|
||||||
|
u32 numregs; // number of regs (when NREG is 0, numregs is 16)
|
||||||
|
|
||||||
|
GIFPath();
|
||||||
|
|
||||||
|
void PrepPackedRegs();
|
||||||
|
void SetTag(const void* mem);
|
||||||
|
bool StepReg();
|
||||||
|
u8 GetReg();
|
||||||
|
|
||||||
|
int ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*GIFRegHandler)(const u32* data);
|
||||||
|
|
||||||
|
struct GifPathStruct
|
||||||
|
{
|
||||||
|
const GIFRegHandler Handlers[3];
|
||||||
|
GIFPath path[3];
|
||||||
|
|
||||||
|
__forceinline GIFPath& operator[]( int idx ) { return path[idx]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// SIGNAL / FINISH / LABEL (WIP!!)
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// The current implementation for these is very incomplete, especially SIGNAL, which needs
|
||||||
|
// an extra VM-state status var to be handled correctly.
|
||||||
|
//
|
||||||
|
|
||||||
|
// SIGNAL : This register is a double-throw. If the SIGNAL bit in CSR is clear, set the CSR
|
||||||
|
// and raise a gsIrq. If CSR is already *set*, then ignore all subsequent drawing operations
|
||||||
|
// and writes to general purpose registers to the GS. (note: I'm pretty sure this includes
|
||||||
|
// direct GS and GSreg accesses, as well as those coming through the GIFpath -- but that
|
||||||
|
// behavior isn't confirmed yet). Privlidged writes are still active.
|
||||||
|
//
|
||||||
|
// Ignorance continues until the SIGNAL bit in CSR is manually cleared by the EE. And here's
|
||||||
|
// the tricky part: the interrupt from the second SIGNAL is still pending, and should be
|
||||||
|
// raised once the EE has reset the *IMR* mask for SIGNAL -- meaning setting the bit to 1
|
||||||
|
// (disabled/masked) and then back to 0 (enabled/unmasked).
|
||||||
|
//
|
||||||
|
static void RegHandlerSIGNAL(const u32* data)
|
||||||
|
{
|
||||||
|
GIF_LOG("MTGS SIGNAL data %x_%x CSRw %x IMR %x CSRr\n",data[0], data[1], CSRw, GSIMR, GSCSRr);
|
||||||
|
|
||||||
|
GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]);
|
||||||
|
|
||||||
|
if ((CSRw & 0x1))
|
||||||
|
{
|
||||||
|
if (!(GSIMR&0x100) )
|
||||||
|
{
|
||||||
|
gsIrq();
|
||||||
|
}
|
||||||
|
|
||||||
|
GSCSRr |= 1; // signal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FINISH : Enables end-of-draw signaling. When FINISH is written it tells the GIF to
|
||||||
|
// raise a gsIrq and set the FINISH bit of CSR when the current operation is finished.
|
||||||
|
// As far as I can figure, this feature is meant for EE/GS synchronization when the EE
|
||||||
|
// wants to utilize GS post-processing effects. We don't need to emulate that part of
|
||||||
|
// it since we flush/interlock the GS for those specific read operations.
|
||||||
|
//
|
||||||
|
// However! We should properly emulate handling partial-DMA transfers on PATH2 and
|
||||||
|
// PATH3 of the GIF, which means only signaling FINISH if nloop==0.
|
||||||
|
//
|
||||||
|
static void RegHandlerFINISH(const u32* data)
|
||||||
|
{
|
||||||
|
GIF_LOG("GIFpath FINISH data %x_%x CSRw %x\n", data[0], data[1], CSRw);
|
||||||
|
|
||||||
|
if ((CSRw & 0x2))
|
||||||
|
{
|
||||||
|
if (!(GSIMR&0x200))
|
||||||
|
gsIrq();
|
||||||
|
|
||||||
|
GSCSRr |= 2; // finish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RegHandlerLABEL(const u32* data)
|
||||||
|
{
|
||||||
|
GIF_LOG( "GIFpath LABEL" );
|
||||||
|
GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID&~data[1])|(data[0]&data[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
PCSX2_ALIGNED16( static GifPathStruct s_gifPath ) =
|
||||||
|
{
|
||||||
|
RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// GIFPath Method Implementations
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
GIFPath::GIFPath() : tag()
|
||||||
|
{
|
||||||
|
memzero_obj( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline bool GIFPath::StepReg()
|
||||||
|
{
|
||||||
|
if ((++curreg & 0xf) == tag.NREG) {
|
||||||
|
curreg = 0;
|
||||||
|
if (--nloop == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline u8 GIFPath::GetReg() { return regs[curreg]; }
|
||||||
|
|
||||||
|
// unpack the registers - registers are stored as a sequence of 4 bit values in the
|
||||||
|
// upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
|
||||||
|
// coming in from paths 2 and 3, so we unpack them into an 8 bit array here.
|
||||||
|
//
|
||||||
|
__forceinline void GIFPath::PrepPackedRegs()
|
||||||
|
{
|
||||||
|
// Only unpack registers if we're starting a new pack. Otherwise the unpacked
|
||||||
|
// array should have already been initialized by a previous partial transfer.
|
||||||
|
|
||||||
|
if (curreg != 0) return;
|
||||||
|
|
||||||
|
u32 tempreg = tag.REGS[0];
|
||||||
|
numregs = ((tag.NREG-1)&0xf) + 1;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < numregs; i++) {
|
||||||
|
if (i == 8) tempreg = tag.REGS[1];
|
||||||
|
regs[i] = tempreg & 0xf;
|
||||||
|
tempreg >>= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline void GIFPath::SetTag(const void* mem)
|
||||||
|
{
|
||||||
|
const_cast<GIFTAG&>(tag) = *((GIFTAG*)mem);
|
||||||
|
|
||||||
|
nloop = tag.NLOOP;
|
||||||
|
curreg = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveStateBase::gifPathFreeze()
|
||||||
|
{
|
||||||
|
FreezeTag( "GIFpath" );
|
||||||
|
Freeze( s_gifPath.path );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static __forceinline void gsHandler(const u8* pMem) {
|
||||||
|
const int handler = pMem[8];
|
||||||
|
if (handler >= 0x60 && handler < 0x63) {
|
||||||
|
//DevCon::Status("GIF Tag Interrupt");
|
||||||
|
s_gifPath.Handlers[handler&0x3]((const u32*)pMem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define incTag(x, y) do { \
|
||||||
|
pMem += (x); \
|
||||||
|
size -= (y); \
|
||||||
|
if ((pathidx==GIF_PATH_1)&&(pMem>=vuMemEnd)) pMem -= 0x4000; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define aMin(x, y) ((x < y) ? (x) : (y))
|
||||||
|
#define subVal(x, y) ((x > y) ? (x-y) : 0 )
|
||||||
|
|
||||||
|
// Parameters:
|
||||||
|
// size (path1) - difference between the end of VU memory and pMem.
|
||||||
|
// size (path2/3) - max size of incoming data stream, in qwc (simd128)
|
||||||
|
__forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
||||||
|
{
|
||||||
|
const u8* vuMemEnd = pMem + (size<<4); // End of VU1 Mem
|
||||||
|
if (pathidx==GIF_PATH_1) size = 0x400; // VU1 mem size
|
||||||
|
const u32 startSize = size; // Start Size
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
if (!nloop) {
|
||||||
|
|
||||||
|
SetTag(pMem);
|
||||||
|
incTag(16, 1);
|
||||||
|
|
||||||
|
if (pathidx == GIF_PATH_3) {
|
||||||
|
if (tag.FLG&2) Path3progress = IMAGE_MODE;
|
||||||
|
else Path3progress = TRANSFER_MODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch(tag.FLG) {
|
||||||
|
case GIF_FLG_PACKED:
|
||||||
|
PrepPackedRegs();
|
||||||
|
do {
|
||||||
|
if (GetReg() == 0xe) {
|
||||||
|
gsHandler(pMem);
|
||||||
|
}
|
||||||
|
incTag(16, 1);
|
||||||
|
} while(StepReg() && size > 0);
|
||||||
|
break;
|
||||||
|
case GIF_FLG_REGLIST:
|
||||||
|
{
|
||||||
|
size *= 2;
|
||||||
|
|
||||||
|
do { incTag(8, 1); }
|
||||||
|
while(StepReg() && size > 0);
|
||||||
|
|
||||||
|
if (size & 1) { incTag(8, 1); }
|
||||||
|
size /= 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GIF_FLG_IMAGE:
|
||||||
|
case GIF_FLG_IMAGE2:
|
||||||
|
{
|
||||||
|
int len = aMin(size, nloop);
|
||||||
|
incTag((len * 16), len);
|
||||||
|
nloop -= len;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag.EOP && !nloop) {
|
||||||
|
if (pathidx != GIF_PATH_2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size = (startSize - size);
|
||||||
|
|
||||||
|
if (pathidx == GIF_PATH_3) {
|
||||||
|
if (tag.EOP && !nloop) {
|
||||||
|
Path3progress = STOPPED_MODE;
|
||||||
|
}
|
||||||
|
gif->madr += size * 16;
|
||||||
|
gif->qwc -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
|
||||||
|
// Used to keep interrupts in sync with the EE, while the GS itself
|
||||||
|
// runs potentially several frames behind.
|
||||||
|
// Parameters:
|
||||||
|
// size - max size of incoming data stream, in qwc (simd128)
|
||||||
|
__forceinline int GIFPath_ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
||||||
|
{
|
||||||
|
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
||||||
|
static uptr profStartPtr = 0;
|
||||||
|
static uptr profEndPtr = 0;
|
||||||
|
if (profStartPtr == 0) {
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
__beginfunc:
|
||||||
|
mov profStartPtr, offset __beginfunc;
|
||||||
|
mov profEndPtr, offset __endfunc;
|
||||||
|
}
|
||||||
|
ProfilerRegisterSource( "GSRingBufCopy", (void*)profStartPtr, profEndPtr - profStartPtr );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int retSize = s_gifPath[pathidx].ParseTag(pathidx, pMem, size);
|
||||||
|
|
||||||
|
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
__endfunc:
|
||||||
|
nop;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return retSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears all GIFpath data to zero.
|
||||||
|
void GIFPath_Reset()
|
||||||
|
{
|
||||||
|
memzero_obj( s_gifPath.path );
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a hackfix tool provided for "canceling" the contents of the GIFpath when
|
||||||
|
// invalid GIFdma states are encountered (tpyically needed for PATH3 only).
|
||||||
|
__forceinline void GIFPath_Clear( GIF_PATH pathidx )
|
||||||
|
{
|
||||||
|
memzero_obj(s_gifPath.path[pathidx]);
|
||||||
|
if( GSgifSoftReset == NULL ) return;
|
||||||
|
mtgsThread.SendSimplePacket( GS_RINGTYPE_SOFTRESET, (1<<pathidx), 0, 0 );
|
||||||
|
}
|
|
@ -492,7 +492,7 @@
|
||||||
</File>
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="EmuCore"
|
Name="System"
|
||||||
>
|
>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\Dump.cpp"
|
RelativePath="..\..\Dump.cpp"
|
||||||
|
@ -534,6 +534,14 @@
|
||||||
RelativePath="..\..\System.cpp"
|
RelativePath="..\..\System.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\System\SysThreads.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\System\SysThreads.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<Filter
|
<Filter
|
||||||
Name="ISO"
|
Name="ISO"
|
||||||
>
|
>
|
||||||
|
@ -685,10 +693,6 @@
|
||||||
RelativePath="..\..\ps2\BiosTools.cpp"
|
RelativePath="..\..\ps2\BiosTools.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\ps2\CoreEmuThread.cpp"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<Filter
|
<Filter
|
||||||
Name="EmotionEngine"
|
Name="EmotionEngine"
|
||||||
>
|
>
|
||||||
|
@ -1448,6 +1452,10 @@
|
||||||
<Filter
|
<Filter
|
||||||
Name="GS"
|
Name="GS"
|
||||||
>
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\ps2\GIFpath.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\GS.cpp"
|
RelativePath="..\..\GS.cpp"
|
||||||
>
|
>
|
||||||
|
@ -1884,10 +1892,6 @@
|
||||||
RelativePath="..\..\ps2\BiosTools.h"
|
RelativePath="..\..\ps2\BiosTools.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\ps2\CoreEmuThread.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
</Filter>
|
</Filter>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
|
@ -2370,7 +2374,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCustomBuildTool"
|
Name="VCCustomBuildTool"
|
||||||
Description=""
|
Description=""
|
||||||
CommandLine=""$(InputDir)\bin2cpp.cmd" $(InputFileName)"
|
CommandLine=""$(InputDir)\bin2cpp.cmd" $(InputFileName)
"
|
||||||
Outputs=""$(InputDir)\$(InputName).h"
|
Outputs=""$(InputDir)\$(InputName).h"
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
|
@ -2380,7 +2384,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCustomBuildTool"
|
Name="VCCustomBuildTool"
|
||||||
Description=""
|
Description=""
|
||||||
CommandLine=""$(InputDir)\bin2cpp.cmd" $(InputFileName)"
|
CommandLine=""$(InputDir)\bin2cpp.cmd" $(InputFileName)
"
|
||||||
Outputs=""$(InputDir)\$(InputName).h"
|
Outputs=""$(InputDir)\$(InputName).h"
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
|
@ -2390,7 +2394,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCustomBuildTool"
|
Name="VCCustomBuildTool"
|
||||||
Description=""
|
Description=""
|
||||||
CommandLine=""$(InputDir)\bin2cpp.cmd" $(InputFileName)"
|
CommandLine=""$(InputDir)\bin2cpp.cmd" $(InputFileName)
"
|
||||||
Outputs=""$(InputDir)\$(InputName).h"
|
Outputs=""$(InputDir)\$(InputName).h"
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
>
|
>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
AdditionalIncludeDirectories="./;../../;../../x86;"../../x86/ix86-32";../libs;../../IPU"
|
AdditionalIncludeDirectories="./;../../;../../x86;"../../x86/ix86-32";../../IPU;../../System"
|
||||||
PreprocessorDefinitions="__i386__;TIXML_USE_STL;_SCL_SECURE_NO_WARNINGS"
|
PreprocessorDefinitions="__i386__;TIXML_USE_STL;_SCL_SECURE_NO_WARNINGS"
|
||||||
RuntimeTypeInfo="false"
|
RuntimeTypeInfo="false"
|
||||||
PrecompiledHeaderFile="$(IntDir)/pcsx2.pch"
|
PrecompiledHeaderFile="$(IntDir)/pcsx2.pch"
|
||||||
|
|
|
@ -1106,8 +1106,8 @@ void __fastcall mVU_XGKICK_(u32 addr) {
|
||||||
addr &= 0x3ff;
|
addr &= 0x3ff;
|
||||||
u8* data = microVU1.regs->Mem + (addr*16);
|
u8* data = microVU1.regs->Mem + (addr*16);
|
||||||
u32 diff = 0x400 - addr;
|
u32 diff = 0x400 - addr;
|
||||||
u32 size = mtgsThread->PrepDataPacket(GIF_PATH_1, data, diff);
|
u32 size = mtgsThread.PrepDataPacket(GIF_PATH_1, data, diff);
|
||||||
u8* pDest = mtgsThread->GetDataPacketPtr();
|
u8* pDest = mtgsThread.GetDataPacketPtr();
|
||||||
|
|
||||||
if (size > diff) {
|
if (size > diff) {
|
||||||
// fixme: one of these days the following *16's will get cleaned up when we introduce
|
// fixme: one of these days the following *16's will get cleaned up when we introduce
|
||||||
|
@ -1121,7 +1121,7 @@ void __fastcall mVU_XGKICK_(u32 addr) {
|
||||||
else {
|
else {
|
||||||
memcpy_aligned(pDest, microVU1.regs->Mem + (addr*16), size*16);
|
memcpy_aligned(pDest, microVU1.regs->Mem + (addr*16), size*16);
|
||||||
}
|
}
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
microVUt(void) mVU_XGKICK_DELAY(mV, bool memVI) {
|
microVUt(void) mVU_XGKICK_DELAY(mV, bool memVI) {
|
||||||
|
|
|
@ -1974,10 +1974,10 @@ void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr)
|
||||||
u32 size;
|
u32 size;
|
||||||
u8* data = ((u8*)pMem + (addr&0x3fff));
|
u8* data = ((u8*)pMem + (addr&0x3fff));
|
||||||
|
|
||||||
size = mtgsThread->PrepDataPacket(GIF_PATH_1, data, (0x4000-(addr&0x3fff)) / 16);
|
size = mtgsThread.PrepDataPacket(GIF_PATH_1, data, (0x4000-(addr&0x3fff)) / 16);
|
||||||
jASSUME( size > 0 );
|
jASSUME( size > 0 );
|
||||||
|
|
||||||
u8* pmem = mtgsThread->GetDataPacketPtr();
|
u8* pmem = mtgsThread.GetDataPacketPtr();
|
||||||
|
|
||||||
if((size << 4) > (0x4000-(addr&0x3fff)))
|
if((size << 4) > (0x4000-(addr&0x3fff)))
|
||||||
{
|
{
|
||||||
|
@ -1992,6 +1992,6 @@ void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr)
|
||||||
memcpy_aligned(pmem, (u8*)pMem+addr, size<<4);
|
memcpy_aligned(pmem, (u8*)pMem+addr, size<<4);
|
||||||
}
|
}
|
||||||
|
|
||||||
mtgsThread->SendDataPacket();
|
mtgsThread.SendDataPacket();
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue