Switched over to a new statically linked pthreads-based threading system, and rewrote the MTGS so that's (mostly) separated from the GIS/GIF code now. Added detection of CPU cores (both logical/hyperthreaded and physical), and moved the cpuInit code around so that it makes more sense and so the console gets spammed with less redundant info.

Win32-specific: Fixed GUI bugs from Issue 90, Issue 91, and Issue 92.

Developer info: Added new files MTGS.cpp, Threading.h, WinThreads.cpp, System.cpp, and LnxThreads.cpp.

git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@510 a6443dda-0b58-4228-96e9-037be469359c
This commit is contained in:
Jake.Stine 2008-12-28 06:53:24 +00:00 committed by Gregory Hainaut
parent ff0568b105
commit 70583d47cf
53 changed files with 2911 additions and 3614 deletions

View File

@ -24,6 +24,8 @@
#include "GS.h"
#include "VU.h"
using namespace Threading;
u64 profile_starttick = 0;
u64 profile_totalticks = 0;
@ -252,7 +254,6 @@ u32 UpdateVSyncRate()
return (u32)m_iTicks;
}
extern u32 CSRw;
extern u64 SuperVUGetRecTimes(int clear);
extern u32 vu0time;
@ -384,7 +385,7 @@ static __forceinline void frameLimit()
while( sDeltaTime < 0 )
{
_TIMESLICE();
Timeslice();
iEnd = GetCPUTicks();
sDeltaTime = iEnd - uExpectedEnd;
}

View File

@ -22,6 +22,43 @@
#include <stdexcept>
#include <string>
// This class provides an easy and clean method for ensuring objects are not copyable.
class NoncopyableObject
{
protected:
NoncopyableObject() {}
~NoncopyableObject() {}
// Programmer's note:
// No need to provide implementations for these methods since they should
// never be referenced anyway. No references? No Linker Errors! Noncopyable!
private:
// Copy me? I think not!
explicit NoncopyableObject( const NoncopyableObject& );
// Assign me? I think not!
const NoncopyableObject& operator=( const NoncopyableObject& );
};
// Base class used to implement type-safe sealed classes.
// This class should never be used directly. Use the Sealed
// macro instead, which ensures all sealed classes derive from a unique BaseSealed
// (preventing them from accidentally cirumventing sealing by inheriting from
// multiple sealed classes.
template < int T >
class __BaseSealed
{
protected:
__BaseSealed()
{
}
};
// Use this macro/class as a base to seal a class from being derrived from.
// This macro works by providing a unique base class with a protected constructor
// for every class that derives from it.
#define Sealed private virtual __BaseSealed<__COUNTER__>
namespace Exception
{
// This exception exception thrown any time an operation is attempted when an object
@ -34,6 +71,13 @@ namespace Exception
logic_error( msg ) {}
};
class HardwareDeficiency : public std::runtime_error
{
public:
explicit HardwareDeficiency( const std::string& msg="Your machine's hardware is incapable of running Pcsx2. Sorry dood." ) :
runtime_error( msg ) {}
};
// This exception is thrown by the PS2 emulation (R5900, etc) when bad things happen
// that force the emulation state to terminate. The GUI should handle them by returning
// the user to the GUI.
@ -78,6 +122,14 @@ namespace Exception
, plugin_name( plugin ) {}
};
class ThreadCreationError : public std::runtime_error
{
public:
virtual ~ThreadCreationError() throw() {}
explicit ThreadCreationError( const std::string& msg="Thread could not be created." ) :
runtime_error( msg ) {}
};
/**** BEGIN STREAMING EXCEPTIONS ****/
// Generic stream error. Contains the name of the stream and a message.

View File

@ -120,21 +120,22 @@ void WriteFIFO(u32 mem, const u64 *value) {
assert(ret == 0 ); // vif stall code not implemented
}
else if ((mem >= 0x10006000) && (mem < 0x10007000)) {
u64* data;
GIF_LOG("WriteFIFO GIF 0x%08X\n", mem);
psHu64(mem ) = value[0];
psHu64(mem+8) = value[1];
if( CHECK_MULTIGS ) {
data = (u64*)GSRingBufCopy(16, GS_RINGTYPE_P3);
if( mtgsThread != NULL )
{
const uint count = mtgsThread->PrepDataPacket( GIF_PATH_3, value, 16 );
jASSUME( count == 16 );
u64* data = (u64*)mtgsThread->GetDataPacketPtr();
data[0] = value[0];
data[1] = value[1];
GSgifTransferDummy(2, (u8*)data, 1);
GSRINGBUF_DONECOPY((u8*)data, 16);
mtgsThread->SendDataPacket();
}
else {
else
{
FreezeXMMRegs(1);
GSGIFTRANSFER3((u32*)value, 1);
FreezeXMMRegs(0);
@ -146,11 +147,11 @@ void WriteFIFO(u32 mem, const u64 *value) {
//commiting every 16 bytes
while( FIFOto_write((u32*)value, 1) == 0 ) {
SysPrintf("IPU sleeping\n");
_TIMESLICE();
Console::WriteLn("IPU sleeping");
Threading::Timeslice();
}
} else {
SysPrintf("WriteFIFO Unknown %x\n", mem);
Console::Notice("WriteFIFO Unknown %x", mem);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -23,16 +23,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Common.h"
#include "Threading.h"
#include "zlib.h"
struct GSRegSIGBLID
{
u32 SIGID;
u32 LBLID;
};
#define GSPATH3FIX
#ifdef PCSX2_VIRTUAL_MEM
@ -40,17 +35,93 @@ struct GSRegSIGBLID
#define GSIMR *((u32*)(PS2MEM_GS+0x1010))
#define GSSIGLBLID ((GSRegSIGBLID*)(PS2MEM_GS+0x1080))
#else
extern u8 g_RealGSMem[0x2000];
PCSX2_ALIGNED16( extern u8 g_RealGSMem[0x2000] );
#define GSCSRr *((u64*)(g_RealGSMem+0x1000))
#define GSIMR *((u32*)(g_RealGSMem+0x1010))
#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080))
#endif
#define GS_RINGBUFFERBASE GS_RINGBUFF_STOREAGE
#define GS_RINGBUFFERSIZE 0x00300000 // 3Mb
#define GS_RINGBUFFEREND (u8*)(GS_RINGBUFFERBASE+GS_RINGBUFFERSIZE)
/////////////////////////////////////////////////////////////////////////////
// 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,
};
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];
};
struct GIFPath
{
GIFTAG tag;
u32 curreg;
u32 _pad[3];
u8 regs[16];
__forceinline void PrepRegs();
void SetTag(const void* mem);
u32 GetReg();
};
/////////////////////////////////////////////////////////////////////////////
// MTGS Threaded Class Declaration
#define MTGS_RINGBUFFERSIZE 0x00300000 // 3Mb
enum GIF_PATH
{
GIF_PATH_1 = 0,
GIF_PATH_2,
GIF_PATH_3,
};
__declspec(align(4096)) extern u8 GS_RINGBUFF_STOREAGE[GS_RINGBUFFERSIZE];
enum GS_RINGTYPE
{
GS_RINGTYPE_RESTART = 0
@ -73,26 +144,112 @@ enum GS_RINGTYPE
, GS_RINGTYPE_STARTTIME // special case for min==max fps frameskip settings
};
// if returns NULL, don't copy (memory is preserved)
u8* GSRingBufCopy(u32 size, u32 type);
void GSRingBufSimplePacket(int type, int data0, int data1, int data2);
void GSRingBufPointerPacket(int type, u32 data0, void* data1 );
u32 GSgifTransferDummy(int path, const u8 *pMem, u32 size);
class mtgsThreadObject : public Threading::Thread
{
friend class SaveState;
void gsInit();
s32 gsOpen();
void gsClose();
void gsReset();
void gsSetVideoRegionType( u32 isPal );
void gsResetFrameSkip();
void gsSyncLimiterLostTime( s32 deltaTime );
void gsDynamicSkipEnable();
void gsPostVsyncEnd( bool updategs );
protected:
// note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty
const u8* m_RingPos; // cur pos gs is reading from
u8* m_WritePos; // cur pos ee thread is writing to
const u8* const m_RingBufferEnd; // pointer to the end of the ringbuffer (used to detect buffer wraps)
Threading::WaitEvent m_wait_InitDone; // used to regulate thread startup and gsInit
Threading::MutexLock m_lock_RingRestart;
// 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.
int m_CopyCommandTally;
// These vars maintain instance data for sending Data Packets.
// Only one data packet can be constructed and uploaded at a time.
uint m_packet_size; // size of the packet (data only, ie. not including the 16 byte command!)
u8* m_packet_data; // pointer to the data location in the ringbuffer.
#ifdef RINGBUF_DEBUG_STACK
MutexLock m_lock_Stack;
#endif
// the MTGS "dummy" GIFtag info!
PCSX2_ALIGNED16( GIFPath m_path[3] );
// mtgs needs its own memory space separate from the PS2. The PS2 memory is in
// synch with the EE while this stays in sync with the GS (ie, it lags behind)
PCSX2_ALIGNED16( u8 m_gsMem[0x2000] );
PCSX2_ALIGNED( 4096, u8 m_RingBuffer[MTGS_RINGBUFFERSIZE] );
public:
mtgsThreadObject();
virtual ~mtgsThreadObject();
void Reset();
void GIFSoftReset( int mask );
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void WaitGS();
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 u64* srcdata, u32 size );
void SendDataPacket();
void SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 );
void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 );
u8* GetDataPacketPtr() const;
void Freeze( SaveState& state );
void SetEvent();
uptr FnPtr_SimplePacket() const
{
__asm mov eax, SendSimplePacket
//return (uptr)&SendSimplePacket;
}
protected:
// Sets the gsEvent flag and releases a timeslice.
// For use in loops that wait on the GS thread to do certain things.
void SetEventWait();
// 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.
u32 _gifTransferDummy( GIF_PATH pathidx, const u8 *pMem, u32 size );
// Used internally by SendSimplePacket type functions
void _PrepForSimplePacket();
void _FinishSimplePacket();
int Callback();
};
extern mtgsThreadObject* mtgsThread;
void mtgsWaitGS();
bool mtgsOpen();
void mtgsRingBufSimplePacket( s32 command, u32 data0, u32 data1, u32 data2 );
/////////////////////////////////////////////////////////////////////////////
// Generalized GS Functions and Stuff
extern void gsInit();
extern s32 gsOpen();
extern void gsClose();
extern void gsReset();
extern void gsSetVideoRegionType( u32 isPal );
extern void gsResetFrameSkip();
extern void gsSyncLimiterLostTime( s32 deltaTime );
extern void gsDynamicSkipEnable();
extern void gsPostVsyncEnd( bool updategs );
extern void gsFrameSkip( bool forceskip );
// Some functions shared by both the GS and MTGS
extern void _gs_ResetFrameskip();
extern void _gs_ChangeTimings( u32 framerate, u32 iTicks );
// mem and size are the ones from GSRingBufCopy
extern void GSRINGBUF_DONECOPY(const u8 *mem, u32 size);
extern void gsWaitGS();
// used for resetting GIF fifo
void gsGIFReset();
@ -129,6 +286,8 @@ int _GIFchain();
void gifMFIFOInterrupt();
extern u32 g_vu1SkipCount;
extern u32 CSRw;
extern u64 m_iSlowStart;
// GS Playback
#define GSRUN_TRANS1 1

View File

@ -1017,9 +1017,8 @@ void MTSAH() {
///////////////////////////////////////////
int intInit() {
void intInit() {
//detect cpu for use the optimaze asm code
return 0;
}
void intReset() {

View File

@ -178,7 +178,7 @@ int main(int argc, char *argv[]) {
#endif
}
if (SysInit() == -1) return 1;
if (!SysInit()) return 1;
#ifdef PCSX2_DEVBUILD
if( g_pRunGSState ) {
@ -382,14 +382,13 @@ void SysMessage(const char *fmt, ...) {
gtk_main();
}
int SysInit()
bool SysInit()
{
sinit=0;
mkdir(SSTATES_DIR, 0755);
mkdir(MEMCARDS_DIR, 0755);
#ifdef EMU_LOG
mkdir(LOGS_DIR, 0755);
#ifdef PCSX2_DEVBUILD
@ -401,11 +400,9 @@ int SysInit()
if( emuLog != NULL )
setvbuf(emuLog, NULL, _IONBF, 0);
#endif
if(cpuInit() == -1 )
return -1;
SysDetect();
while (LoadPlugins() == -1) {
if (Pcsx2Configure() == FALSE)
{
@ -414,8 +411,11 @@ int SysInit()
}
}
if( !cpuInit() )
return false;
sinit = 1;
return 0;
return true;
}
void SysRestorableReset()

203
pcsx2/Linux/LnxThreads.cpp Normal file
View File

@ -0,0 +1,203 @@
/* Pcsx2 - Pc Ps2 Emulator
* Copyright (C) 2002-2008 Pcsx2 Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "System.h"
#include "Threading.h"
#include "ix86/ix86.h"
// Note: assuming multicore is safer because it forces the interlocked routines to use
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
// having the LOCK prefix is very bad indeed.
static bool isMultiCore = true; // assume more than one CPU (safer)
namespace Threading
{
// Note: Apparently this solution is Linux/Solaris only.
// FreeBSD/OsX need something far more complicated (apparently)
void CountLogicalCores( int LogicalCoresPerPhysicalCore )
{
const uint numCPU = sysconf( _SC_NPROCESSORS_ONLN );
if( numCPU > 0 )
{
isMultiCore = numCpu > 1;
cpuinfo.LogicalCores = numCPU;
cpuinfo.PhysicalCores = numCPU / LogicalCoresPerPhysicalCore;
}
else
{
// Indeterminate?
cpuinfo.LogicalCores = 1;
cpuinfo.PhysicalCores = 1;
}
}
__forceinline void Timeslice()
{
usleep(500);
}
// For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory
// improve performance and reduce cpu power consumption.
__forceinline void SpinWait()
{
// If this doesn't compile you can just comment it out (it only serves as a
// performance hint and isn't required).
__asm__ ( "pause" );
}
void* Thread::_internal_callback( void* itsme )
{
jASSUME( itsme != NULL );
Thread& owner = *((Thread*)itsme);
try
{
owner.m_returncode = owner.Callback();
}
catch( std::exception& ex )
{
Console::Error( "Thread terminated abnormally with error:\n\t%s", ex.what() );
owner.m_returncode = -1;
}
owner.m_terminated = true;
return NULL;
}
}
/////////////////////////////////////////////////////////////////////////
// Cross-platform atomic operations for GCC.
// These are much faster than the old versions for single core CPUs.
__forceinline long pcsx2_InterlockedExchange(volatile long* Target, long Value)
{
long result;
/*
* The XCHG instruction always locks the bus with or without the
* LOCKED prefix. This makes it significantly slower than CMPXCHG on
* uni-processor machines. The Windows InterlockedExchange function
* is nearly 3 times faster than the XCHG instruction, so this routine
* is not yet very useful for speeding up pthreads.
*/
if( isMultiCore )
{
__asm__ __volatile__ (
"xchgl %2,%1"
:"=r" (result)
:"m" (*Target), "0" (Value));
}
}
else
{
/*
* Faster version of XCHG for uni-processor systems because
* it doesn't lock the bus. If an interrupt or context switch
* occurs between the movl and the cmpxchgl then the value in
* 'location' may have changed, in which case we will loop
* back to do the movl again.
*/
__asm__ __volatile__ (
"0:\n\t"
"movl %1,%%eax\n\t"
"cmpxchgl %2,%1\n\t"
"jnz 0b"
:"=&a" (result)
:"m" (*Target), "r" (Value));
}
}
return result;
}
__forceinline long pcsx2_InterlockedExchangeAdd(volatile long* Addend, long Value)
{
if( isMultiCore )
{
__asm__ __volatile__(
".intel_syntax\n"
"lock xadd [%0], %%eax\n"
".att_syntax\n" : : "r"(Addend), "a"(Value) : "memory"
);
}
else
{
__asm__ __volatile__(
".intel_syntax\n"
"xadd [%0], %%eax\n"
".att_syntax\n" : : "r"(Addend), "a"(Value) : "memory"
);
}
}
__forceinline long pcsx2_InterlockedCompareExchange(volatile long *dest, long value, long comp)
{
long result;
if( isMultiCore )
{
__asm__ __volatile__ (
"lock\n\t"
"cmpxchgl %2,%1" /* if (EAX == [location]) */
/* [location] = value */
/* else */
/* EAX = [location] */
:"=a" (result)
:"m" (*dest), "r" (value), "a" (comp));
}
else
{
__asm__ __volatile__ (
"cmpxchgl %2,%1" /* if (EAX == [location]) */
/* [location] = value */
/* else */
/* EAX = [location] */
:"=a" (result)
:"m" (*dest), "r" (value), "a" (comp)
);
}
return result;
}
#ifdef __x86_64__
__forceinline void pcsx2_InterlockedExchange64(volatile s64* Target, s64 Value)
{
__asm__ __volatile__(
".intel_syntax\n"
"lock xchg [%0], %%rax\n"
".att_syntax\n" : : "r"(Target), "a"(Value) : "memory"
);
return 0;
}
__forceinline s64 pcsx2_InterlockedCompareExchange64(volatile s64* dest, s64 exch, s64 comp)
{
s64 old;
__asm__ __volatile__(
"lock; cmpxchgq %q2, %q1"
: "=a" (old)
: "r" (exch), "m" (*dest), "a" (comp)
);
return old;
}
#endif

View File

@ -25,13 +25,956 @@
#include <windows.h>
#endif
#ifdef PCSX2_GSRING_TX_STATS
#include <intrin.h>
#endif
#include "Common.h"
#include "VU.h"
#include "GS.h"
#include "iR5900.h"
using namespace Threading;
#ifdef DEBUG
#define MTGS_LOG SysPrintf
#else
#define MTGS_LOG 0&&
#endif
// forces the compiler to treat a non-volatile value as volatile.
// This allows us to delacre the vars as non-volatile and only use
// them as volatile when appropriate (more optimized).
#define volatize(x) (*(u8* volatile*)&(x)) // for writepos
#define volatize_c(x) (*(u8 * volatile*)&(x)) // for readpos
/////////////////////////////////////////////////////////////////////////////
// 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.
// 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, so we unpack
// them into an 8 bit array.
__forceinline void GIFPath::PrepRegs()
{
if( tag.nreg == 0 )
{
u32 tempreg = tag.regs[0];
for(u32 i=0; i<16; ++i, tempreg >>= 4)
{
if( i == 8 ) tempreg = tag.regs[1];
assert( (tempreg&0xf) < 0x64 );
regs[i] = tempreg & 0xf;
}
}
else
{
u32 tempreg = tag.regs[0];
for(u32 i=0; i<tag.nreg; ++i, tempreg >>= 4)
{
assert( (tempreg&0xf) < 0x64 );
regs[i] = tempreg & 0xf;
}
}
}
void GIFPath::SetTag(const void* mem)
{
tag = *((GIFTAG*)mem);
curreg = 0;
PrepRegs();
}
u32 GIFPath::GetReg()
{
return regs[curreg];
}
static void _mtgsFreezeGIF( SaveState& state, GIFPath paths[3] )
{
for(int i=0; i<3; i++ )
{
state.Freeze( paths[i].tag );
// Earlier versions had an extra u32 in the tag struct:
state.Freeze( paths[i].curreg );
}
for(int i=0; i<3; i++ )
state.Freeze( paths[i].regs );
}
void SaveState::mtgsFreeze()
{
if( mtgsThread != NULL )
{
mtgsThread->Freeze( *this );
}
else
{
// save some zero'd dummy info...
// This isn't ideal, and it could lead to problems in very rare
// circumstances, but most of the time should be perfectly fine.
GIFPath path[3];
memset( &path, 0, sizeof( path ) );
_mtgsFreezeGIF( *this, path );
}
}
static void RegHandlerSIGNAL(const u32* data)
{
MTGS_LOG("MTGS SIGNAL data %x_%x CSRw %x\n",data[0], data[1], CSRw);
GSSIGLBLID->SIGID = (GSSIGLBLID->SIGID&~data[1])|(data[0]&data[1]);
if ((CSRw & 0x1))
GSCSRr |= 1; // signal
if (!(GSIMR&0x100) )
gsIrq();
}
static void RegHandlerFINISH(const u32* data)
{
MTGS_LOG("MTGS FINISH data %x_%x CSRw %x\n",data[0], data[1], CSRw);
if ((CSRw & 0x2))
GSCSRr |= 2; // finish
if (!(GSIMR&0x200) )
gsIrq();
}
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
mtgsThreadObject* mtgsThread = NULL;
// 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
#ifdef RINGBUF_DEBUG_STACK
#include <list>
std::list<uptr> ringposStack;
mutex_t stackLock;
#endif
#ifdef _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 acall to GSRINGBUF_DONECOPY)
static int copyLock = 0;
#endif
typedef void (*GIFRegHandler)(const u32* data);
static GIFRegHandler s_GSHandlers[3] = { RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL };
mtgsThreadObject::mtgsThreadObject() :
m_RingPos( m_RingBuffer )
, m_WritePos( m_RingBuffer )
, m_RingBufferEnd( m_RingBuffer + sizeof( m_RingBuffer ) )
, m_wait_InitDone()
, m_lock_RingRestart()
, m_CopyCommandTally( 0 )
, m_packet_size()
, m_packet_data( NULL )
#ifdef RINGBUF_DEBUG_STACK
, m_lock_Stack()
#endif
{
memcpy_raz_( m_gsMem, PS2MEM_GS, sizeof(m_gsMem) );
GSsetBaseMem( m_gsMem );
// Wait for the thread to finish initialization (it runs GSinit, which can take
// some time since it's creating a new window and all), and then check for errors.
m_wait_InitDone.Wait();
if( m_returncode != 0 ) // means the thread failed to init the GS plugin
throw Exception::PluginFailure( "GS", "An error occured while opening the " );
}
mtgsThreadObject::~mtgsThreadObject()
{
Console::WriteLn( "MTGS > Closing GS thread..." );
SetEvent();
// rest of the cleanup will be handled by the inherited object destructors...
}
void mtgsThreadObject::Reset()
{
// MTGS Reset process:
// * clear the ringbuffer.
// * Signal a reset.
// * clear the path and byRegs structs (used by GIFtagDummy)
AtomicExchangePointer( m_RingPos, m_WritePos );
MTGS_LOG( "MTGS > Sending Reset...\n" );
SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 );
memset(m_path, 0, sizeof(m_path));
}
// 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.
// size - size of the packet in simd128's
__forceinline u32 mtgsThreadObject::_gifTransferDummy( GIF_PATH pathidx, const u8* pMem, u32 size )
{
GIFPath& path = m_path[pathidx];
while(size > 0)
{
bool eop = false;
if(path.tag.nloop == 0)
{
path.SetTag( pMem );
pMem += sizeof(GIFTAG);
--size;
if(pathidx == 2 && path.tag.eop)
Path3transfer = 0;
if( pathidx == 0 )
{
// hack: if too much data for VU1, just ignore.
// The GIF is evil : if nreg is 0, it's really 16. Otherwise it's the value in nreg.
const int numregs = ((path.tag.nreg-1)&15)+1;
if((path.tag.nloop * numregs) > (size * ((path.tag.flg == 1) ? 2 : 1)))
{
path.tag.nloop = 0;
return ++size;
}
}
/*f(path.tag.pre)
{
assert(path.tag.flg != GIF_FLG_IMAGE); // kingdom hearts, ffxii, tales of abyss
if((path.tag.flg & 2) == 0)
{
// Primitive handler... Nothing for the Dummy to do here.
//GIFReg r;
//r.i64 = path.tag.PRIM;
//(this->*m_fpGIFRegHandlers[GIF_A_D_REG_PRIM])(&r);
}
}*/
if(path.tag.eop)
{
eop = true;
}
else if(path.tag.nloop == 0)
{
if(pathidx == 0 && g_FFXHack)
continue;
eop = true;
}
}
if(path.tag.nloop > 0)
{
switch(path.tag.flg)
{
case GIF_FLG_PACKED:
while(size > 0)
{
if( path.GetReg() == 0xe )
{
const int handler = pMem[8];
if(handler >= 0x60 && handler < 0x63)
s_GSHandlers[handler&0x3]((const u32*)pMem);
}
size--;
pMem += 16; // 128 bits! //sizeof(GIFPackedReg);
if((++path.curreg & 0xf) == path.tag.nreg)
{
path.curreg = 0;
path.tag.nloop--;
if(path.tag.nloop == 0)
break;
}
}
break;
case GIF_FLG_REGLIST:
size *= 2;
while(size > 0)
{
const int handler = path.GetReg();
if(handler >= 0x60 && handler < 0x63)
s_GSHandlers[handler&0x3]((const u32*)pMem);
size--;
pMem += 8; //sizeof(GIFReg); -- 64 bits!
if((++path.curreg & 0xf) == path.tag.nreg)
{
path.curreg = 0;
path.tag.nloop--;
if(path.tag.nloop == 0)
{
break;
}
}
}
if(size & 1) pMem += 8; //sizeof(GIFReg);
size /= 2;
break;
case GIF_FLG_IMAGE2: // hmmm
assert(0);
path.tag.nloop = 0;
break;
case GIF_FLG_IMAGE:
{
int len = (int)min(size, path.tag.nloop);
pMem += len * 16;
path.tag.nloop -= len;
size -= len;
}
break;
jNO_DEFAULT;
}
}
if(eop && ((int)size <= 0 || pathidx == 0))
{
break;
}
}
// FIXME: dq8, pcsx2 error probably
if(pathidx == 0)
{
if(!path.tag.eop && path.tag.nloop > 0)
{
path.tag.nloop = 0;
DevCon::Write( "path1 hack! " );
}
}
return size;
}
int mtgsThreadObject::Callback()
{
Console::WriteLn("MTGS > Thread Started, Opening GS Plugin...");
m_returncode = GSopen((void *)&pDsp, "PCSX2", 1);
GSCSRr = 0x551B400F; // 0x55190000
m_wait_InitDone.Set();
if (m_returncode != 0) { return m_returncode; } // error msg will be issued to the user by Plugins.c
Console::WriteLn("MTGS > GSopen Finished.");
#ifdef RINGBUF_DEBUG_STACK
u32 prevCmd=0;
#endif
while( !m_sigterm )
{
m_wait_event.Wait();
// note: m_RingPos is intentionally not volatile, because it should only
// ever be modified by this thread.
while( m_RingPos != volatize(m_WritePos))
{
assert( m_RingPos < m_RingBufferEnd );
u32 tag = *(u32*)m_RingPos;
u32 ringposinc = 16;
#ifdef RINGBUF_DEBUG_STACK
// pop a ringpos off the stack. It should match this one!
EnterCriticalSection( &stackLock );
uptr stackpos = ringposStack.back();
if( stackpos != (uptr)m_RingPos )
{
SysPrintf( "MTGS Ringbuffer Critical Failure ---> %x to %x (prevCmd: %x)\n", stackpos, (long)m_RingPos, prevCmd );
}
assert( stackpos == (long)m_RingPos );
prevCmd = tag;
ringposStack.pop_back();
LeaveCriticalSection( &stackLock );
#endif
switch( tag&0xffff )
{
case GS_RINGTYPE_RESTART:
AtomicExchangePointer(m_RingPos, m_RingBuffer);
// stall for a bit to let the MainThread have time to update the g_pGSWritePos.
m_lock_RingRestart.Lock();
m_lock_RingRestart.Unlock();
continue;
case GS_RINGTYPE_P1:
{
int qsize = (tag>>16);
// make sure that tag>>16 is the MAX size readable
GSgifTransfer1((u32*)(m_RingPos+16) - 0x1000 + 4*qsize, 0x4000-qsize*16);
ringposinc += qsize<<4;
break;
}
case GS_RINGTYPE_P2:
GSgifTransfer2((u32*)(m_RingPos+16), tag>>16);
ringposinc += (tag>>16)<<4;
break;
case GS_RINGTYPE_P3:
GSgifTransfer3((u32*)(m_RingPos+16), tag>>16);
ringposinc += (tag>>16)<<4;
break;
case GS_RINGTYPE_VSYNC:
{
GSvsync(*(u32*)(m_RingPos+4));
gsFrameSkip( !( *(u32*)(m_RingPos+8) ) );
if( PAD1update != NULL ) PAD1update(0);
if( PAD2update != NULL ) PAD2update(1);
break;
}
case GS_RINGTYPE_FRAMESKIP:
_gs_ResetFrameskip();
break;
case GS_RINGTYPE_MEMWRITE8:
m_gsMem[*(u32*)(m_RingPos+4)] = *(u8*)(m_RingPos+8);
break;
case GS_RINGTYPE_MEMWRITE16:
*(u16*)(m_gsMem+*(u32*)(m_RingPos+4)) = *(u16*)(m_RingPos+8);
break;
case GS_RINGTYPE_MEMWRITE32:
*(u32*)(m_gsMem+*(u32*)(m_RingPos+4)) = *(u32*)(m_RingPos+8);
break;
case GS_RINGTYPE_MEMWRITE64:
*(u64*)(m_gsMem+*(u32*)(m_RingPos+4)) = *(u64*)(m_RingPos+8);
break;
case GS_RINGTYPE_SAVE:
case GS_RINGTYPE_LOAD:
{
SaveState* f = (SaveState*)(*(uptr*)(m_RingPos+8));
f->FreezePlugin( "GS", GSfreeze );
break;
}
case GS_RINGTYPE_RECORD:
{
int record = *(u32*)(m_RingPos+4);
if( GSsetupRecording != NULL ) GSsetupRecording(record, NULL);
if( SPU2setupRecording != NULL ) SPU2setupRecording(record, NULL);
break;
}
case GS_RINGTYPE_RESET:
MTGS_LOG( "MTGS > Receiving Reset...\n" );
if( GSreset != NULL ) GSreset();
break;
case GS_RINGTYPE_SOFTRESET:
{
int mask = *(u32*)(m_RingPos+4);
MTGS_LOG( "MTGS > Receiving GIF Soft Reset (mask: %d)\n", mask );
GSgifSoftReset( mask );
break;
}
case GS_RINGTYPE_WRITECSR:
GSwriteCSR( *(u32*)(m_RingPos+4) );
break;
case GS_RINGTYPE_MODECHANGE:
_gs_ChangeTimings( *(u32*)(m_RingPos+4), *(u32*)(m_RingPos+8) );
break;
case GS_RINGTYPE_STARTTIME:
m_iSlowStart += *(u32*)(m_RingPos+4);
break;
#ifdef PCSX2_DEVBUILD
default:
Console::Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag, m_RingPos, m_WritePos);
assert(0);
m_RingPos = m_WritePos;
continue;
#else
// Optimized performance in non-Dev builds.
jNO_DEFAULT;
#endif
}
const u8* newringpos = m_RingPos + ringposinc;
assert( newringpos <= m_RingBufferEnd );
if( newringpos == m_RingBufferEnd )
newringpos = m_RingBuffer;
AtomicExchangePointer( m_RingPos, newringpos );
}
}
GSclose();
return 0;
}
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void mtgsThreadObject::WaitGS()
{
// Freeze registers because some kernel code likes to destroy them
FreezeXMMRegs(1);
FreezeMMXRegs(1);
SetEvent();
while( volatize(m_RingPos) != volatize(m_WritePos) )
{
Timeslice();
//SpinWait();
}
FreezeXMMRegs(0);
FreezeMMXRegs(0);
}
// Sets the gsEvent flag and releases a timeslice.
// For use in loops that wait on the GS thread to do certain things.
void mtgsThreadObject::SetEvent()
{
m_wait_event.Set();
m_CopyCommandTally = 0;
}
void mtgsThreadObject::SetEventWait()
{
// Freeze registers because some kernel code likes to destroy them
FreezeXMMRegs(1);
FreezeMMXRegs(1);
SetEvent();
Timeslice();
FreezeXMMRegs(0);
FreezeMMXRegs(0);
m_CopyCommandTally = 0;
}
u8* mtgsThreadObject::GetDataPacketPtr() const
{
return m_packet_data;
}
// Closes the data packet send command, and initiates the gs thread (if needed).
void mtgsThreadObject::SendDataPacket()
{
// make sure a previous copy block has been started somewhere.
jASSUME( m_packet_data != NULL );
const u8* temp = m_packet_data + m_packet_size;
jASSUME( temp <= m_RingBufferEnd );
if( temp == m_RingBufferEnd )
temp = m_RingBuffer;
#ifdef _DEBUG
else
{
const u8* readpos = volatize(m_RingPos);
if( readpos != m_WritePos )
{
// The writepos should never leapfrog the readpos
// since that indicates a bad write.
if( m_packet_data < readpos )
assert( temp < readpos );
}
// Updating the writepos should never make it equal the readpos, since
// that would stop the buffer prematurely (and indicates bad code in the
// ringbuffer manager)
assert( readpos != temp );
}
#endif
AtomicExchangePointer( m_WritePos, temp );
// if enough copies have queued up then go ahead and initiate the GS thread..
// Optimization notes: 16 was the best value I found so far. Ideally there
// should be a secondary check for data size, since large transfers should
// be counted as multple commands for instance. But I'm weary of adding too
// much clutter to the overhead of this function. In any case, 16 should be
// a pretty decent all-weather value for the majority of games. (Air)
// tested values:
// 24 - very slow on HT machines (+5% drop in fps)
// 8 - roughly 2% slower on HT machines.
if( ++m_CopyCommandTally > 16 )
{
FreezeXMMRegs(1);
FreezeMMXRegs(1);
SetEvent();
FreezeXMMRegs(0);
FreezeMMXRegs(0);
}
m_packet_data = NULL;
}
int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u64* srcdata, u32 size )
{
return PrepDataPacket( pathidx, (u8*)srcdata, size );
}
int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size )
{
return PrepDataPacket( pathidx, (u8*)srcdata, size );
}
// 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
// large for VU1 memory.
int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size )
{
#ifdef PCSX2_GSRING_TX_STATS
ringtx_s+=size;
ringtx_s_ulg+=size&0x7F;
ringtx_s_min=min(ringtx_s_min,size);
ringtx_s_max=max(ringtx_s_max,size);
ringtx_c++;
unsigned long tx_sz;
if (_BitScanReverse(&tx_sz,size))
{
unsigned long tx_algn;
_BitScanForward(&tx_algn,size);
ringtx_inf[tx_sz][tx_algn]++;
ringtx_inf_s[tx_sz]+=size;
}
if (ringtx_s>=128*1024*1024)
{
SysPrintf("GSRingBufCopy:128MB in %d tx -> b/tx: AVG = %.2f , max = %d, min = %d\n",ringtx_c,ringtx_s/(float)ringtx_c,ringtx_s_max,ringtx_s_min);
for (int i=0;i<32;i++)
{
u32 total_bucket=0;
u32 bucket_subitems=0;
for (int j=0;j<32;j++)
{
if (ringtx_inf[i][j])
{
total_bucket+=ringtx_inf[i][j];
bucket_subitems++;
SysPrintf("GSRingBufCopy :tx [%d,%d] algn %d : count= %d [%.2f%%]\n",1<<i,(1<<(i+1))-16,1<<j,ringtx_inf[i][j],ringtx_inf[i][j]/(float)ringtx_c*100);
ringtx_inf[i][j]=0;
}
}
if (total_bucket)
SysPrintf("GSRingBufCopy :tx [%d,%d] total : count= %d [%.2f%%] [%.2f%%]\n",1<<i,(1<<(i+1))-16,total_bucket,total_bucket/(float)ringtx_c*100,ringtx_inf_s[i]/(float)ringtx_s*100);
ringtx_inf_s[i]=0;
}
SysPrintf("GSRingBufCopy :tx ulg count =%d [%.2f%%]\n",ringtx_s_ulg,ringtx_s_ulg/(float)ringtx_s*100);
ringtx_s_ulg=0;
ringtx_c=0;
ringtx_s=0;
ringtx_s_min=0xFFFFFFFF;
ringtx_s_max=0;
}
#endif
// Note on volatiles: g_pGSWritePos is not modified by the GS thread,
// so there's no need to use volatile reads here. We still have to use
// interlocked exchanges when we modify it, however, since the GS thread
// is reading it.
const u8 *writepos = m_WritePos;
// Checks if a previous copy was started without an accompanying call to GSRINGBUF_DONECOPY
jASSUME( m_packet_data == NULL );
// Sanity checks! (within the confines of our ringbuffer please!)
jASSUME( size < MTGS_RINGBUFFERSIZE );
jASSUME( writepos < m_RingBufferEnd );
// Alignment checks! (16 bytes please!)
jASSUME( ((uptr)writepos & 15) == 0 );
jASSUME( (size&15) == 0);
// retval has the amount of data *not* processed, so we only need to reserve
// enough room for size - retval:
int retval = _gifTransferDummy( pathidx, srcdata, size>>4 );
size = size - (retval<<4);
m_packet_size = size;
size += 16; // takes into account our command qword.
if( writepos + size < m_RingBufferEnd )
{
// generic gs wait/stall.
// Waits until the readpos is outside the scope of the write area.
while( true )
{
// two conditionals in the following while() loop, so precache
// the readpos for more efficient behavior:
const u8* readpos = volatize_c(m_RingPos);
// if the writepos is past the readpos then we're safe:
if( writepos >= readpos ) break;
// writepos is behind the readpos, so do a second check to see if the
// readpos is out past the end of the future write pos:
if( writepos+size < readpos ) break;
SetEventWait();
}
}
else if( writepos + size > m_RingBufferEnd )
{
// If the incoming packet doesn't fit, then start over from
// the start of the ring buffer (it's a lot easier than trying
// to wrap the packet around the end of the buffer).
// We have to be careful not to leapfrog our read-position. If it's
// greater than the current write position then we need to stall
// until it loops around to the beginning of the buffer
while( true )
{
const u8* readpos = volatize(m_RingPos);
// is the buffer empty?
if( readpos == writepos ) break;
// Also: Wait for the readpos to go past the start of the buffer
// Otherwise it'll stop dead in its tracks when we set the new write
// position below (bad!)
if( readpos < writepos && readpos != m_RingBuffer ) break;
SetEventWait();
}
m_lock_RingRestart.Lock();
SendSimplePacket( GS_RINGTYPE_RESTART, 0, 0, 0 );
writepos = m_RingBuffer;
AtomicExchangePointer( m_WritePos, writepos );
m_lock_RingRestart.Unlock();
// stall until the read position is past the end of our incoming block,
// or until it reaches the current write position (signals an empty buffer).
while( true )
{
const u8* readpos = volatize(m_RingPos);
if( readpos == m_WritePos ) break;
if( writepos+size < readpos ) break;
SetEventWait();
}
}
else // always true - if( writepos + size == MTGS_RINGBUFFEREND )
{
// Yay. Perfect fit. What are the odds?
//SysPrintf( "MTGS > Perfect Fit!\n");
while( true )
{
const u8* readpos = volatize(m_RingPos);
// is the buffer empty? Don't wait...
if( readpos == writepos ) break;
// Copy is ready so long as readpos is less than writepos and *not*
// equal to the base of the ringbuffer (otherwise the buffer will stop)
if( readpos < writepos && readpos != m_RingBuffer ) break;
SetEventWait();
}
}
#ifdef RINGBUF_DEBUG_STACK
mutex_lock( stackLock );
ringposStack.push_front( (uptr)writepos );
mutex_unlock( stackLock );
#endif
// Command qword: Low word is the command, and the high word is the packet
// length in SIMDs (128 bits).
const uint simd_size = (m_packet_size>>4); // minus the command byte!
*(u32*)m_WritePos = (pathidx+1) | (simd_size<<16);
m_packet_data = m_WritePos + 16;
#ifdef PCSX2_GSRING_SAMPLING_STATS
if( GSRingBufCopySz == 0)
{
__asm
{
mov GSRingBufCopySz,offset __GSRingBufCopyEnd;
sub GSRingBufCopySz,offset GSRingBufCopy;
__GSRingBufCopyEnd:
}
ProfilerRegisterSource("pcsx2:GSRingBufCopy",&GSRingBufCopy,GSRingBufCopySz);
}
#endif
return m_packet_size;
}
__forceinline void mtgsThreadObject::_PrepForSimplePacket()
{
#ifdef RINGBUF_DEBUG_STACK
m_lock_Stack.Lock();
ringposStack.push_front( (uptr)m_WritePos );
m_lock_Stack.Unlock();
#endif
const u8* future_writepos = m_WritePos+16;
jASSUME( future_writepos <= m_RingBufferEnd );
if( future_writepos == m_RingBufferEnd )
future_writepos = m_RingBuffer;
while( future_writepos == volatize(m_RingPos) )
SetEventWait();
}
__forceinline void mtgsThreadObject::_FinishSimplePacket()
{
const u8* future_writepos = m_WritePos+16;
assert( future_writepos != volatize(m_RingPos) );
AtomicExchangePointer( m_WritePos, future_writepos );
}
void mtgsThreadObject::SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 )
{
_PrepForSimplePacket();
*(u32*)m_WritePos = type;
*(u32*)(m_WritePos+4) = data0;
*(u32*)(m_WritePos+8) = data1;
*(u32*)(m_WritePos+12) = data2;
_FinishSimplePacket();
}
void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 )
{
_PrepForSimplePacket();
*(u32*)m_WritePos = type;
*(u32*)(m_WritePos+4) = data0;
*(uptr*)(m_WritePos+8) = (uptr)data1;
_FinishSimplePacket();
}
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void mtgsWaitGS()
{
if( mtgsThread == NULL ) return;
mtgsThread->WaitGS();
}
bool mtgsOpen()
{
// Check the config flag since our thread object has yet to be created
if( !CHECK_MULTIGS ) return false;
// better not be a thread already running, yo!
assert( mtgsThread == NULL );
try
{
mtgsThread = new mtgsThreadObject();
}
catch( Exception::ThreadCreationError& )
{
Console::Error( "MTGS > Thread creation failed!" );
mtgsThread = NULL;
return false;
}
return true;
}
#ifdef PCSX2_GSRING_TX_STATS
u32 ringtx_s=0;
u32 ringtx_s_ulg=0;
u32 ringtx_s_min=0xFFFFFFFF;
u32 ringtx_s_max=0;
u32 ringtx_c=0;
u32 ringtx_inf[32][32];
u32 ringtx_inf_s[32];
#endif
#ifdef PCSX2_GSRING_SAMPLING_STATS
u32 GSRingBufCopySz;
#endif
void mtgsThreadObject::GIFSoftReset( int mask )
{
if(mask & 1) memset(&m_path[0], 0, sizeof(m_path[0]));
if(mask & 2) memset(&m_path[1], 0, sizeof(m_path[1]));
if(mask & 4) memset(&m_path[2], 0, sizeof(m_path[2]));
if( GSgifSoftReset == NULL ) return;
MTGS_LOG( "MTGS > Sending GIF Soft Reset (mask: %d)\n", mask );
mtgsThread->SendSimplePacket( GS_RINGTYPE_SOFTRESET, mask, 0, 0 );
}
void mtgsThreadObject::Freeze( SaveState& state )
{
_mtgsFreezeGIF( state, this->m_path );
}
// this function is needed because of recompiled calls from iGS.cpp
void mtgsRingBufSimplePacket( s32 command, u32 data0, u32 data1, u32 data2 )
{
mtgsThread->SendSimplePacket( (GS_RINGTYPE)command, data0, data1, data2 );
}

View File

@ -2722,13 +2722,15 @@ void loadBiosRom(const char *ext, u8 *dest) {
fclose(fp);
return;
}
SysPrintf("\n\n\n");
SysPrintf("**************\n");
SysPrintf("%s NOT FOUND\n", ext);
SysPrintf("**************\n\n\n");
Console::Error( "\n\n\n"
"**************\n"
"%s NOT FOUND\n"
"**************\n\n\n", ext
);
}
int memReset() {
void memReset() {
struct stat buf;
char Bios[g_MaxPath];
FILE *fp;
@ -2745,9 +2747,11 @@ int memReset() {
CombinePaths( Bios, Config.BiosDir, Config.Bios );
if (stat(Bios, &buf) == -1) {
if (stat(Bios, &buf) == -1)
{
Console::Error(_("Unable to load bios: '%s', PCSX2 can't run without that"), Bios);
return 0;
throw Exception::FileNotFound( Bios,
"The specified Bios file was not found. A bios is required for Pcsx2 to run.\n\nFile not found" );
}
#ifdef PCSX2_VIRTUAL_MEM
@ -2772,7 +2776,7 @@ int memReset() {
fclose(fp);
BiosVersion = GetBiosVersion();
Console::Notice("Bios Version %d.%d\n", BiosVersion >> 8, BiosVersion & 0xff);
Console::MsgLn("Bios Version %d.%d", BiosVersion >> 8, BiosVersion & 0xff);
//injectIRX("host.irx"); //not fully tested; still buggy
@ -2801,7 +2805,6 @@ int memReset() {
#endif
#endif
return 1;
}
void memSetKernelMode() {

View File

@ -213,7 +213,7 @@ extern u8 g_RealGSMem[0x2000];
#define PSMu64(mem) (*(u64*)PSM(mem))
int memInit();
int memReset(); // returns FALSE on error
void memReset(); // clears PS2 ram and loads the bios. Throws Exception::FileNotFound on error.
void memSetKernelMode();
void memSetSupervisorMode();
void memSetUserMode();

View File

@ -625,7 +625,7 @@ void ProcessFKeys(int fkey, int shift)
break;
}
AtomicExchange( Config.Options, newOptions );
Threading::AtomicExchange( Config.Options, newOptions );
Console::Notice("Frame Limit Mode Changed: %s", limitMsg );
@ -660,7 +660,7 @@ void ProcessFKeys(int fkey, int shift)
}
case 11:
if( CHECK_MULTIGS ) {
if( mtgsThread != NULL ) {
Console::Notice( "Cannot make gsstates in MTGS mode" );
}
else {
@ -694,8 +694,8 @@ void ProcessFKeys(int fkey, int shift)
}
else {
g_Pcsx2Recording ^= 1;
if( CHECK_MULTIGS ) {
GSRingBufSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
if( mtgsThread != NULL ) {
mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
}
else {
if( GSsetupRecording != NULL ) GSsetupRecording(g_Pcsx2Recording, NULL);

View File

@ -51,7 +51,7 @@
#ifdef ENABLE_NLS
#ifdef __MSCW32__
#ifdef _WIN32
#include "libintlmsc.h"
#else
#include <locale.h>
@ -71,7 +71,7 @@
#define _(msgid) msgid
#define N_(msgid) msgid
#endif
#endif // ENABLE_NLS
// <<--- End GNU GetText / NLS
@ -171,6 +171,7 @@ struct PcsxConfig {
extern PcsxConfig Config;
extern u32 BiosVersion;
extern char CdromId[12];
extern uptr pDsp; // what the hell is this unused piece of crap passed to every plugin for? (air)
int LoadCdrom();
int CheckCdrom();
@ -184,8 +185,6 @@ extern const char *LabelGreets;
void SaveGSState(const char *file);
void LoadGSState(const char *file);
// <<--- End Savestate Stuff
char *ParseLang(char *id);
void ProcessFKeys(int fkey, int shift); // processes fkey related commands value 1-12
@ -260,24 +259,24 @@ extern bool g_EEFreezeRegs;
#define FreezeXMMRegs(save) if( g_EEFreezeRegs ) { FreezeXMMRegs_(save); }
#ifndef __x86_64__
void FreezeMMXRegs_(int save);
void FreezeMMXRegs_(int save);
#define FreezeMMXRegs(save) if( g_EEFreezeRegs ) { FreezeMMXRegs_(save); }
#else
#define FreezeMMXRegs(save)
# define FreezeMMXRegs(save)
#endif
#if defined(_WIN32) && !defined(__x86_64__)
// faster memcpy
void __fastcall memcpy_raz_u(void *dest, const void *src, size_t bytes);
void __fastcall memcpy_raz_(void *dest, const void *src, size_t qwc);
void * memcpy_amd_(void *dest, const void *src, size_t n);
// faster memcpy
void __fastcall memcpy_raz_u(void *dest, const void *src, size_t bytes);
void __fastcall memcpy_raz_(void *dest, const void *src, size_t qwc);
void * memcpy_amd_(void *dest, const void *src, size_t n);
#define memcpy_fast memcpy_amd_
//#define memcpy_fast memcpy //Dont use normal memcpy, it has sse in 2k5!
//#define memcpy_fast memcpy //Dont use normal memcpy, it has sse in 2k5!
#else
// for now disable linux fast memcpy
#define memcpy_fast memcpy
#define memcpy_raz_ memcpy
#define memcpy_raz_u memcpy
// for now disable linux fast memcpy
#define memcpy_fast memcpy
#define memcpy_raz_ memcpy
#define memcpy_raz_u memcpy
#endif
@ -292,9 +291,10 @@ void injectIRX(const char *filename);
// aligned_malloc: Implement/declare linux equivalents here!
#if !defined(_MSC_VER) && !defined(HAVE_ALIGNED_MALLOC)
static __forceinline void* pcsx2_aligned_malloc(size_t size, size_t align)
{
assert( align < 0x10000 );
assert( align < 0x10000 );
char* p = (char*)malloc(size+align);
int off = 2+align - ((int)(uptr)(p+2) % align);
@ -306,10 +306,10 @@ static __forceinline void* pcsx2_aligned_malloc(size_t size, size_t align)
static __forceinline void pcsx2_aligned_free(void* pmem)
{
if( pmem != NULL ) {
char* p = (char*)pmem;
free(p - (int)*(u16*)(p-2));
}
if( pmem != NULL ) {
char* p = (char*)pmem;
free(p - (int)*(u16*)(p-2));
}
}
#define _aligned_malloc pcsx2_aligned_malloc
@ -317,35 +317,6 @@ static __forceinline void pcsx2_aligned_free(void* pmem)
#endif
// InterlockedExchange: Declare linux/GCC equivalents here
// (implementations are in ThreadTools.cpp)
#ifndef _WIN32
typedef void* PVOID;
extern void InterlockedExchangePointer(PVOID volatile* Target, void* Value);
extern long InterlockedExchange(long volatile* Target, long Value);
extern long InterlockedExchangeAdd(long volatile* Addend, long Value);
extern long InterlockedIncrement( volatile long* Addend );
extern long InterlockedDecrement( volatile long* Addend );
extern long InterlockedCompareExchange(volatile long *dest, long exch, long comp);
extern long InterlockedCompareExchangePointer(PVOID volatile *dest, PVOID exch, long comp);
#endif
extern void AtomicExchange( u32& Target, u32 value );
extern void AtomicExchangeAdd( u32& Target, u32 value );
extern void AtomicIncrement( u32& Target );
extern void AtomicDecrement( u32& Target );
extern void AtomicExchange( s32& Target, s32 value );
extern void AtomicExchangeAdd( s32& Target, u32 value );
extern void AtomicIncrement( s32& Target );
extern void AtomicDecrement( s32& Target );
// No fancy templating or overloading can save us from having to use C-style dereferences here.
#define AtomicExchangePointer( target, value ) \
InterlockedExchangePointer( reinterpret_cast<PVOID volatile*>(&target), reinterpret_cast<uptr>(value) )
// Timeslice releaser for those many idle loop spots through out PCSX2.
extern void _TIMESLICE();
extern void InitCPUTicks();
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();

View File

@ -592,17 +592,6 @@ static bool loadp=false;
int InitPlugins() {
int ret;
if( GSsetBaseMem ) {
if( CHECK_MULTIGS ) {
extern u8 g_MTGSMem[];
GSsetBaseMem(g_MTGSMem);
}
else {
GSsetBaseMem(PS2MEM_GS);
}
}
ret = GSinit();
if (ret != 0) { SysMessage (_("GSinit error: %d"), ret); return -1; }
ret = PAD1init(1);
@ -718,11 +707,6 @@ int OpenPlugins(const char* pTitleFilename) {
cdvdNewDiskCB();
}
//video
// Only bind the gsIrq if we're not running the MTGS.
// The MTGS simulates its own gsIrq in order to maintain proper sync.
GSirqCallback( CHECK_MULTIGS ? NULL : gsIrq );
// GS isn't closed during emu pauses, so only open it once per instance.
if( !OpenStatus.GS ) {
ret = gsOpen();
@ -825,7 +809,7 @@ void ClosePlugins()
// closed)
if( OpenStatus.GS )
gsWaitGS();
mtgsWaitGS();
CLOSE_PLUGIN( CDVD );
CLOSE_PLUGIN( DEV9 );
@ -838,7 +822,7 @@ void ClosePlugins()
void ResetPlugins()
{
gsWaitGS();
mtgsWaitGS();
ShutdownPlugins();
InitPlugins();

View File

@ -54,25 +54,29 @@ bool cpuInit()
if( cpuIsInitialized ) return true;
cpuIsInitialized = true;
int ret;
Console::Notice("PCSX2 " PCSX2_VERSION " save ver: %x", g_SaveVersion);
Console::Notice("EE pc offset: 0x%x, PSX pc offset: 0x%x", (u32)&cpuRegs.pc - (u32)&cpuRegs, (u32)&psxRegs.pc - (u32)&psxRegs);
cpuRegs.constzero = 0;
cpudetectInit();
Cpu = CHECK_EEREC ? &recCpu : &intCpu;
Console::SetColor( Console::Color_White );
ret = Cpu->Init();
Console::ClearColor();
try
{
Cpu->Init();
}
catch( std::exception& ex )
{
Console::Error( "Error > %s", ex.what() );
if (ret == -1 && CHECK_EEREC) {
Console::WriteLn(_("Error initializing Recompiler, switching to Interpreter"));
Config.Options &= ~(PCSX2_EEREC|PCSX2_VU1REC|PCSX2_VU0REC);
Cpu = &intCpu;
ret = Cpu->Init();
if( ret == -1 ) return false;
if( Cpu == &recCpu )
{
Console::Error( _("\t... attempting to initialize the Interpreter (slow!)") );
Config.Options &= ~(PCSX2_EEREC|PCSX2_VU1REC|PCSX2_VU0REC);
Cpu = &intCpu;
Cpu->Init();
// if the interpreter fails, let the exception bubble to the surface.
// fixme: this would probably be better if all exceptions pasesed to
// the caller, and it was the caller's duty to clear the EEREC flag and
// re-run the init process. But for now this is how it works.
}
}
#ifdef PCSX2_VIRTUAL_MEM
@ -112,7 +116,7 @@ bool cpuInit()
return true;
}
bool cpuReset()
void cpuReset()
{
if( cpuIsInitialized )
{
@ -120,12 +124,12 @@ bool cpuReset()
UnmapTLB(i);
}
gsWaitGS(); // GS better be done before we reset the EE..
mtgsWaitGS(); // GS better be done before we reset the EE, just in case.
cpuInit();
Cpu->Reset();
if( !memReset() ) return false;
memReset();
memset(&cpuRegs, 0, sizeof(cpuRegs));
memset(&fpuRegs, 0, sizeof(fpuRegs));
@ -153,15 +157,13 @@ bool cpuReset()
#ifdef _DEBUG
s_vucount = 0;
#endif
return true;
}
void cpuShutdown()
{
if( !cpuIsInitialized ) return;
gsWaitGS();
mtgsWaitGS();
//gsShutdown(); // shut down the GS first because it's running a thread.
for( int i=0; i<48; i++ )

View File

@ -22,7 +22,7 @@
#include <stdio.h>
struct R5900cpu {
int (*Init)();
void (*Init)(); // throws exceptions on failure.
void (*Reset)();
void (*Step)();
void (*Execute)(); /* executes up to a break */
@ -226,7 +226,7 @@ extern PCSX2_ALIGNED16_DECL(tlbs tlb[48]);
#endif
bool cpuInit();
bool cpuReset();
void cpuReset(); // can throw Exception::FileNotFound.
void cpuShutdown();
void cpuException(u32 code, u32 bd);
void cpuTlbMissR(u32 addr, u32 bd);

View File

@ -127,12 +127,10 @@ void SaveState::FreezeAll()
psxRcntFreeze();
sio2Freeze();
if( CHECK_MULTIGS ) {
if( mtgsThread != NULL ) {
// have to call in thread, otherwise weird stuff will start happening
GSRingBufPointerPacket(
IsSaving() ? GS_RINGTYPE_SAVE : GS_RINGTYPE_LOAD, 0, this
);
gsWaitGS();
mtgsThread->SendPointerPacket( GS_RINGTYPE_SAVE, 0, this );
mtgsWaitGS();
}
else {
FreezePlugin( "GS", GSfreeze );
@ -219,6 +217,8 @@ void gzSavingState::FreezeMem( void* data, int size )
void gzLoadingState::FreezeMem( void* data, int size )
{
gzread( m_file, data, size );
if( gzeof( m_file ) )
throw Exception::BadSavedState( m_filename );
}
void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) )

View File

@ -101,6 +101,9 @@ protected:
void psxRcntFreeze();
void sio2Freeze();
// called by gsFreeze automatically.
void mtgsFreeze();
};
/////////////////////////////////////////////////////////////////////////////////

146
pcsx2/System.cpp Normal file
View File

@ -0,0 +1,146 @@
/* Pcsx2 - Pc Ps2 Emulator
* Copyright (C) 2002-2008 Pcsx2 Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <string>
#include "Common.h"
#include "psxCommon.h"
#include "Threading.h"
#include "ix86/ix86.h"
using namespace std;
using namespace Console;
bool sysInitialized = false;
// I can't believe I had to make my own version of trim. C++'s STL is totally whack.
// And I still had to fix it too. I found three samples of trim online and *all* three
// were buggy. People really need to learn to code before they start posting trim
// functions in their blogs. (air)
static void trim( string& line )
{
if ( line.empty() )
return;
int string_size = line.length();
int beginning_of_string = 0;
int end_of_string = string_size - 1;
bool encountered_characters = false;
// find the start of chracters in the string
while ( (beginning_of_string < string_size) && (!encountered_characters) )
{
if ( (line[ beginning_of_string ] != ' ') && (line[ beginning_of_string ] != '\t') )
encountered_characters = true;
else
++beginning_of_string;
}
// test if no characters were found in the string
if ( beginning_of_string == string_size )
return;
encountered_characters = false;
// find the character in the string
while ( (end_of_string > beginning_of_string) && (!encountered_characters) )
{
// if a space or tab was found then ignore it
if ( (line[ end_of_string ] != ' ') && (line[ end_of_string ] != '\t') )
encountered_characters = true;
else
--end_of_string;
}
// return the original string with all whitespace removed from its beginning and end
// + 1 at the end to add the space for the string delimiter
//line.substr( beginning_of_string, end_of_string - beginning_of_string + 1 );
line.erase( end_of_string+1, string_size );
line.erase( 0, beginning_of_string );
}
// This function should be called once during program execution.
void SysDetect()
{
if( sysInitialized ) return;
sysInitialized = true;
Notice("PCSX2 " PCSX2_VERSION " - compiled on " __DATE__ );
Notice("Savestate version: %x", g_SaveVersion);
// fixme: what's the point of this line? Anything? Or just to look "cool"? (air)
DevCon::Notice("EE pc offset: 0x%x, IOP pc offset: 0x%x", (u32)&cpuRegs.pc - (u32)&cpuRegs, (u32)&psxRegs.pc - (u32)&psxRegs);
cpudetectInit();
string family( cpuinfo.x86Fam );
trim( family );
SetColor( Console::Color_White );
WriteLn( "x86Init:" );
MsgLn(
"\tCPU vender name = %s\n"
"\tFamilyID = %x\n"
"\tx86Family = %s\n"
"\tCPU speed = %d.%03d Ghz\n"
"\tCores = %d physical [%d logical]\n"
"\tx86PType = %s\n"
"\tx86Flags = %8.8x %8.8x\n"
"\tx86EFlags = %8.8x\n",
cpuinfo.x86ID, cpuinfo.x86StepID, family.c_str(),
cpuinfo.cpuspeed / 1000, cpuinfo.cpuspeed%1000,
cpuinfo.PhysicalCores, cpuinfo.LogicalCores,
cpuinfo.x86Type, cpuinfo.x86Flags, cpuinfo.x86Flags2,
cpuinfo.x86EFlags
);
WriteLn( "Features:" );
MsgLn(
"\t%sDetected MMX\n"
"\t%sDetected SSE\n"
"\t%sDetected SSE2\n"
"\t%sDetected SSE3\n"
"\t%sDetected SSE4.1\n",
cpucaps.hasMultimediaExtensions ? "" : "Not ",
cpucaps.hasStreamingSIMDExtensions ? "" : "Not ",
cpucaps.hasStreamingSIMD2Extensions ? "" : "Not ",
cpucaps.hasStreamingSIMD3Extensions ? "" : "Not ",
cpucaps.hasStreamingSIMD4Extensions ? "" : "Not "
);
if ( cpuinfo.x86ID[0] == 'A' ) //AMD cpu
{
WriteLn( " Extended AMD Features:" );
MsgLn(
"\t%sDetected MMX2\n"
"\t%sDetected 3DNOW\n"
"\t%sDetected 3DNOW2\n",
cpucaps.hasMultimediaExtensionsExt ? "" : "Not ",
cpucaps.has3DNOWInstructionExtensions ? "" : "Not ",
cpucaps.has3DNOWInstructionExtensionsExt ? "" : "Not "
);
}
Console::ClearColor();
}

View File

@ -19,10 +19,13 @@
#ifndef __SYSTEM_H__
#define __SYSTEM_H__
#include "PS2Etypes.h"
#include "Exceptions.h"
#include "Paths.h"
int SysInit(); // Init mem and plugins
void SysDetect(); // Detects cpu type and fills cpuInfo structs.
bool SysInit(); // Init logfiles, directories, plugins, and other OS-specific junk
void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers.
void SysUpdate(); // Called on VBlank (to update i.e. pads)
void SysRunGui(); // Returns to the Gui

View File

@ -16,158 +16,143 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if defined(_WIN32)
#include <windows.h>
#endif
#include "Threading.h"
#include "Misc.h"
////////////////////////////////////////////////////////////
// Cross-platform atomic operations for GCC
#ifndef _WIN32
typedef void* PVOID;
/*inline unsigned long _Atomic_swap(unsigned long * __p, unsigned long __q) {
# if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64))
return test_and_set(__p, __q);
# else
return __test_and_set(__p, (unsigned long)__q);
# endif
}*/
__forceinline void InterlockedExchangePointer(PVOID volatile* Target, void* Value)
namespace Threading
{
#ifdef __x86_64__
__asm__ __volatile__(".intel_syntax\n"
"lock xchg [%0], %%rax\n"
".att_syntax\n" : : "r"(Target), "a"(Value) : "memory" );
#else
__asm__ __volatile__(".intel_syntax\n"
"lock xchg [%0], %%eax\n"
".att_syntax\n" : : "r"(Target), "a"(Value) : "memory" );
#endif
}
Thread::Thread() :
m_thread()
, m_returncode( 0 )
, m_terminated( false )
, m_sigterm( 0 )
, m_wait_event()
{
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
throw Exception::ThreadCreationError();
}
__forceinline long InterlockedExchange(long volatile* Target, long Value)
{
__asm__ __volatile__(".intel_syntax\n"
"lock xchg [%0], %%eax\n"
".att_syntax\n" : : "r"(Target), "a"(Value) : "memory" );
return 0; // The only function that even looks at this is a debugging function
}
Thread::~Thread()
{
Close();
}
__forceinline long InterlockedExchangeAdd(long volatile* Addend, long Value)
{
__asm__ __volatile__(".intel_syntax\n"
"lock xadd [%0], %%eax\n"
".att_syntax\n" : : "r"(Addend), "a"(Value) : "memory" );
return 0; // The return value is never looked at.
}
void Thread::Close()
{
AtomicExchange( m_sigterm, 1 );
m_wait_event.Set();
pthread_join( m_thread, NULL );
}
__forceinline long InterlockedIncrement( volatile long* Addend )
{
return InterlockedExchangeAdd( Addend, 1 );
}
int Thread::GetReturnCode() const
{
if( !m_terminated )
throw std::logic_error( "Thread is still running. No return code is available." );
__forceinline long InterlockedDecrement( volatile long* Addend )
{
return InterlockedExchangeAdd( Addend, -1 );
}
return m_returncode;
}
__forceinline long InterlockedCompareExchange(volatile long *dest, long exch, long comp)
{
long old;
WaitEvent::WaitEvent()
{
int err = 0;
err = pthread_cond_init(&cond, NULL);
err = pthread_mutex_init(&mutex, NULL);
}
#ifdef __x86_64__
__asm__ __volatile__
(
"lock; cmpxchgq %q2, %1"
: "=a" (old), "=m" (*dest)
: "r" (exch), "m" (*dest), "0" (comp));
#else
__asm__ __volatile__
(
"lock; cmpxchgl %2, %0"
: "=m" (*dest), "=a" (old)
: "r" (exch), "m" (*dest), "a" (comp)
);
#endif
return(old);
}
WaitEvent::~WaitEvent()
{
pthread_cond_destroy( &cond );
pthread_mutex_destroy( &mutex );
}
__forceinline long InterlockedCompareExchangePointer(PVOID volatile *dest, PVOID exch, long comp)
{
long old;
void WaitEvent::Set()
{
pthread_mutex_lock( &mutex );
pthread_cond_signal( &cond );
pthread_mutex_unlock( &mutex );
}
#ifdef __x86_64__
__asm__ __volatile__
(
"lock; cmpxchgq %q2, %1"
: "=a" (old), "=m" (*dest)
: "r" (exch), "m" (*dest), "0" (comp)
);
#else
__asm__ __volatile__
(
"lock; cmpxchgl %2, %0"
: "=m" (*dest), "=a" (old)
: "r" (exch), "m" (*dest), "a" (comp)
);
#endif
return(old);
}
#endif
void WaitEvent::Wait()
{
pthread_mutex_lock( &mutex );
pthread_cond_wait( &cond, &mutex );
pthread_mutex_unlock( &mutex );
}
//////////////////////////////////////////////////////////////////
// define some overloads for InterlockedExchanges
// for commonly used types, like u32 and s32.
MutexLock::MutexLock()
{
int err = 0;
err = pthread_mutex_init( &mutex, NULL );
}
__forceinline void AtomicExchange( u32& Target, u32 value )
{
InterlockedExchange( (volatile LONG*)&Target, value );
}
MutexLock::~MutexLock()
{
pthread_mutex_destroy( &mutex );
}
__forceinline void AtomicExchangeAdd( u32& Target, u32 value )
{
InterlockedExchangeAdd( (volatile LONG*)&Target, value );
}
void MutexLock::Lock()
{
pthread_mutex_lock( &mutex );
}
__forceinline void AtomicIncrement( u32& Target )
{
InterlockedIncrement( (volatile LONG*)&Target );
}
void MutexLock::Unlock()
{
pthread_mutex_unlock( &mutex );
}
__forceinline void AtomicDecrement( u32& Target )
{
InterlockedDecrement( (volatile LONG*)&Target );
}
//////////////////////////////////////////////////////////////////////
// define some overloads for InterlockedExchanges
// for commonly used types, like u32 and s32.
__forceinline void AtomicExchange( s32& Target, s32 value )
{
InterlockedExchange( (volatile LONG*)&Target, value );
}
__forceinline void AtomicExchange( u32& Target, u32 value )
{
pcsx2_InterlockedExchange( (volatile long*)&Target, value );
}
__forceinline void AtomicExchangeAdd( s32& Target, u32 value )
{
InterlockedExchangeAdd( (volatile LONG*)&Target, value );
}
__forceinline void AtomicExchangeAdd( u32& Target, u32 value )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value );
}
__forceinline void AtomicIncrement( s32& Target )
{
InterlockedIncrement( (volatile LONG*)&Target );
}
__forceinline void AtomicIncrement( u32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
}
__forceinline void AtomicDecrement( s32& Target )
{
InterlockedDecrement( (volatile LONG*)&Target );
}
__forceinline void AtomicDecrement( u32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
}
__forceinline void _TIMESLICE()
{
#ifdef _WIN32
Sleep(0);
#else
usleep(500);
#endif
}
__forceinline void AtomicExchange( s32& Target, s32 value )
{
pcsx2_InterlockedExchange( (volatile long*)&Target, value );
}
__forceinline void AtomicExchangeAdd( s32& Target, u32 value )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value );
}
__forceinline void AtomicIncrement( s32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
}
__forceinline void AtomicDecrement( s32& Target )
{
pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
}
__forceinline void _AtomicExchangePointer( const void ** target, const void* value )
{
pcsx2_InterlockedExchange( (volatile long*)target, (long)value );
}
__forceinline void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand )
{
pcsx2_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand );
}
}

122
pcsx2/Threading.h Normal file
View File

@ -0,0 +1,122 @@
/* Pcsx2 - Pc Ps2 Emulator
* Copyright (C) 2002-2008 Pcsx2 Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef _THREADING_H_
#define _THREADING_H_
#include <pthread.h>
#include "PS2Etypes.h"
#include "Exceptions.h"
namespace Threading
{
///////////////////////////////////////////////////////////////
// Define some useful object handles - wait events, mutexes.
struct WaitEvent
{
pthread_cond_t cond;
pthread_mutex_t mutex;
WaitEvent();
~WaitEvent();
void Set();
void Wait();
};
struct MutexLock
{
pthread_mutex_t mutex;
MutexLock();
~MutexLock();
void Lock();
void Unlock();
};
// Returns the number of available logical CPUs (cores plus
// hyperthreaded cpus)
extern void CountLogicalCores( int LogicalCoresPerPhysicalCore );
// Releases a timeslice to other threads.
extern void Timeslice();
// For use in spin/wait loops.
extern void SpinWait();
class Thread : NoncopyableObject
{
protected:
typedef int (*PlainJoeFP)();
pthread_t m_thread;
int m_returncode; // value returned from the thread on close.
bool m_terminated; // set true after the thread has been closed.
u32 m_sigterm; // set to true(1) when the thread has been requested to exit.
WaitEvent m_wait_event; // general wait event that's needed by most threads.
public:
virtual ~Thread();
Thread();
virtual void Close();
// Gets the return code of the thread.
// Throws std::logic_error if the thread has not terminated.
int GetReturnCode() const;
protected:
// Used to dispatch the thread callback function.
// (handles some thread cleanup on Win32, and is basically a typecast
// on linux).
static void* _internal_callback( void* func );
// Implemented by derrived class to handle threading actions!
virtual int Callback()=0;
};
// Our fundamental interlocking functions. All other useful interlocks can
// be derrived from these little beasties!
extern long pcsx2_InterlockedExchange(volatile long* Target, long srcval);
extern long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp );
extern long pcsx2_InterlockedExchangeAdd( volatile long* target, long addval );
extern void AtomicExchange( u32& Target, u32 value );
extern void AtomicExchangeAdd( u32& Target, u32 value );
extern void AtomicIncrement( u32& Target );
extern void AtomicDecrement( u32& Target );
extern void AtomicExchange( s32& Target, s32 value );
extern void AtomicExchangeAdd( s32& Target, u32 value );
extern void AtomicIncrement( s32& Target );
extern void AtomicDecrement( s32& Target );
extern void _AtomicExchangePointer( const void ** target, const void* value );
extern void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand );
#define AtomicExchangePointer( target, value ) \
_AtomicExchangePointer( (const void**)(&target), (const void*)(value) )
#define AtomicCompareExchangePointer( target, value, comparand ) \
_AtomicCompareExchangePointer( (const void**)(&target), (const void*)(value), (const void*)(comparand) )
}
#endif

View File

@ -19,7 +19,7 @@
#ifndef __VU1OPS_H__
#define __VU1OPS_H__
#ifdef __MSCW32__
#ifdef _MSC_VER
#pragma warning(disable:4244)
#endif

View File

@ -1610,21 +1610,20 @@ static int Vif1TransDirectHL(u32 *data){
}
}
//if(splitptr < 4) SysPrintf("Whoopsie\n");
if( CHECK_MULTIGS ) {
u8* gsmem = GSRingBufCopy(16, GS_RINGTYPE_P2);
if( gsmem != NULL )
{
// copy 16 bytes the fast way:
const u64* src = (u64*)splittransfer[0];
u64* dst = (u64*)gsmem;
dst[0] = src[0];
dst[1] = src[1];
if( mtgsThread != NULL )
{
// copy 16 bytes the fast way:
const u64* src = (u64*)splittransfer[0];
const uint count = mtgsThread->PrepDataPacket( GIF_PATH_2, src, 16);
jASSUME( count == 16 );
u64* dst = (u64*)mtgsThread->GetDataPacketPtr();
dst[0] = src[0];
dst[1] = src[1];
GSRINGBUF_DONECOPY(gsmem, 16);
GSgifTransferDummy(1, (u8*)splittransfer[0], 1);
}
mtgsThread->SendDataPacket();
}
else {
else
{
FreezeXMMRegs(1);
FreezeMMXRegs(1);
GSGIFTRANSFER2((u32*)splittransfer[0], 1);
@ -1658,15 +1657,12 @@ static int Vif1TransDirectHL(u32 *data){
//TODO: ret is guaranteed to be qword aligned ?
if( CHECK_MULTIGS ) {
u8* gsmem = GSRingBufCopy(ret<<2, GS_RINGTYPE_P2);
if( gsmem != NULL ) {
//unaligned copy.VIF handling is -very- messy, so i'l use this code til i fix it :)
memcpy_raz_u(gsmem,data,ret<<2);
GSRINGBUF_DONECOPY(gsmem, ret<<2);
GSgifTransferDummy(1, (u8*)data, ret>>2);
}
if( mtgsThread != NULL )
{
//unaligned copy.VIF handling is -very- messy, so i'l use this code til i fix it :)
const uint count = mtgsThread->PrepDataPacket( GIF_PATH_2, data, ret<<2 );
memcpy_raz_u( mtgsThread->GetDataPacketPtr(), data, count );
mtgsThread->SendDataPacket();
}
else {
@ -1676,8 +1672,7 @@ static int Vif1TransDirectHL(u32 *data){
FreezeMMXRegs(0);
FreezeXMMRegs(0);
}
return ret;
}
@ -1685,26 +1680,29 @@ static int Vif1TransDirectHL(u32 *data){
static int Vif1TransUnpack(u32 *data){
FreezeXMMRegs(1);
if (vif1.vifpacketsize < vif1.tag.size) {
/* size is less that the total size, transfer is
'in pieces' */
VIFunpack(data, &vif1.tag, vif1.vifpacketsize, VIF1dmanum);
// g_vifCycles+= size >> 1;
//vif1.tag.addr += size << 2;
vif1.tag.size -= vif1.vifpacketsize;
FreezeXMMRegs(0);
return vif1.vifpacketsize;
} else {
int ret;
/* we got all the data, transfer it fully */
VIFunpack(data, &vif1.tag, vif1.tag.size, VIF1dmanum);
//g_vifCycles+= vif1.tag.size >> 1;
ret = vif1.tag.size;
vif1.tag.size = 0;
vif1.cmd = 0;
FreezeXMMRegs(0);
return ret;
}
if (vif1.vifpacketsize < vif1.tag.size)
{
/* size is less that the total size, transfer is
'in pieces' */
VIFunpack(data, &vif1.tag, vif1.vifpacketsize, VIF1dmanum);
// g_vifCycles+= size >> 1;
//vif1.tag.addr += size << 2;
vif1.tag.size -= vif1.vifpacketsize;
FreezeXMMRegs(0);
return vif1.vifpacketsize;
}
else
{
int ret;
/* we got all the data, transfer it fully */
VIFunpack(data, &vif1.tag, vif1.tag.size, VIF1dmanum);
//g_vifCycles+= vif1.tag.size >> 1;
ret = vif1.tag.size;
vif1.tag.size = 0;
vif1.cmd = 0;
FreezeXMMRegs(0);
return ret;
}
}
@ -2278,7 +2276,7 @@ void dmaVIF1()
if( GSreadFIFO2 == NULL ) {
for (size=vif1ch->qwc; size>0; --size) {
if (size > 1 ) {
gsWaitGS();
mtgsWaitGS();
GSreadFIFO((u64*)&PS2MEM_HW[0x5000]);
}
pMem[0] = psHu64(0x5000);
@ -2286,7 +2284,7 @@ void dmaVIF1()
}
}
else {
gsWaitGS();
mtgsWaitGS();
GSreadFIFO2(pMem, vif1ch->qwc);
// set incase read

View File

@ -53,6 +53,7 @@
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NDEBUG,WIN32_PTHREADS"
SmallerTypeCheck="false"
BufferSecurityCheck="true"
CompileAs="2"
@ -127,6 +128,7 @@
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32_PTHREADS"
MinimalRebuild="true"
SmallerTypeCheck="false"
EnableFunctionLevelLinking="true"
@ -145,6 +147,7 @@
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
OutputFile="&quot;$(SolutionDir)\bin\pcsx2d-vm.exe&quot;"
LinkIncremental="2"
/>
<Tool
Name="VCALinkTool"
@ -203,6 +206,7 @@
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32_PTHREADS"
MinimalRebuild="true"
ExceptionHandling="1"
SmallerTypeCheck="false"
@ -222,6 +226,7 @@
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
OutputFile="&quot;$(SolutionDir)\bin\pcsx2d-vtlb.exe&quot;"
LinkIncremental="2"
/>
<Tool
Name="VCALinkTool"
@ -289,7 +294,7 @@
EnableFiberSafeOptimizations="true"
WholeProgramOptimization="true"
AdditionalIncludeDirectories="./;../../;../../IPU;../../ZLIB;../../DebugTools;../../x86;&quot;../../x86/ix86-32&quot;;../libs"
PreprocessorDefinitions="NDEBUG,WIN32,_WINDOWS,__MSCW32__,__WIN32__,__i386__,ENABLE_NLS,PACKAGE=\&quot;pcsx2\&quot;;PCSX2_DEVBUILD;_CRT_SECURE_NO_DEPRECATE;TIXML_USE_STL"
PreprocessorDefinitions="NDEBUG;WIN32_PTHREADS"
StringPooling="true"
ExceptionHandling="1"
SmallerTypeCheck="false"
@ -374,7 +379,7 @@
<Tool
Name="VCCLCompilerTool"
EnableFiberSafeOptimizations="true"
PreprocessorDefinitions="NDEBUG"
PreprocessorDefinitions="NDEBUG,WIN32_PTHREADS"
SmallerTypeCheck="false"
EnableEnhancedInstructionSet="0"
CompileAs="2"
@ -612,33 +617,9 @@
>
</File>
</Filter>
<Filter
Name="Plugin system"
>
<File
RelativePath="..\..\Plugins.cpp"
>
</File>
<File
RelativePath="..\..\Plugins.h"
>
</File>
<File
RelativePath="..\..\PS2Edefs.h"
>
</File>
<File
RelativePath="..\..\PS2Etypes.h"
>
</File>
</Filter>
<Filter
Name="Misc"
>
<File
RelativePath="..\..\Exceptions.h"
>
</File>
<File
RelativePath="..\..\x86\fast_routines.cpp"
>
@ -663,14 +644,6 @@
RelativePath="..\..\PathUtils.cpp"
>
</File>
<File
RelativePath="..\SamplProf.cpp"
>
</File>
<File
RelativePath="..\..\SamplProf.h"
>
</File>
<File
RelativePath="..\..\SaveState.cpp"
>
@ -683,14 +656,6 @@
RelativePath="..\..\SourceLog.cpp"
>
</File>
<File
RelativePath="..\..\System.h"
>
</File>
<File
RelativePath="..\..\ThreadTools.cpp"
>
</File>
<Filter
Name="TinyXML"
>
@ -2238,6 +2203,58 @@
>
</File>
</Filter>
<Filter
Name="System"
>
<File
RelativePath="..\..\Exceptions.h"
>
</File>
<File
RelativePath="..\..\Plugins.cpp"
>
</File>
<File
RelativePath="..\..\Plugins.h"
>
</File>
<File
RelativePath="..\..\PS2Edefs.h"
>
</File>
<File
RelativePath="..\..\PS2Etypes.h"
>
</File>
<File
RelativePath="..\SamplProf.cpp"
>
</File>
<File
RelativePath="..\..\SamplProf.h"
>
</File>
<File
RelativePath="..\..\System.cpp"
>
</File>
<File
RelativePath="..\..\System.h"
>
</File>
<File
RelativePath="..\..\Threading.h"
>
</File>
<File
RelativePath="..\..\ThreadTools.cpp"
>
</File>
<File
RelativePath="..\WinThreads.cpp"
>
</File>
</Filter>
<File
RelativePath="..\..\Common.h"
>

View File

@ -115,7 +115,7 @@
FavorSizeOrSpeed="1"
EnableFiberSafeOptimizations="true"
AdditionalIncludeDirectories="&quot;$(InputDir)&quot;"
PreprocessorDefinitions="PTW32_STATIC_LIB;WIN32;NDEBUG;_LIB"
PreprocessorDefinitions="PTW32_STATIC_LIB;PTW32_BUILD_INLINED;WIN32;NDEBUG;_LIB"
StringPooling="true"
MinimalRebuild="true"
ExceptionHandling="0"

View File

@ -8,8 +8,8 @@
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="./;../../;../../IPU;../../ZLIB;../../DebugTools;../../x86;&quot;../../x86/ix86-32&quot;;../libs"
PreprocessorDefinitions="WIN32;_WINDOWS;__MSCW32__;__WIN32__;__i386__;ENABLE_NLS;PACKAGE=\&quot;pcsx2\&quot;;_CRT_SECURE_NO_DEPRECATE;TIXML_USE_STL"
AdditionalIncludeDirectories="./;../../;../../IPU;../../ZLIB;../../DebugTools;../../x86;&quot;../../x86/ix86-32&quot;;../libs;../pthreads"
PreprocessorDefinitions="WIN32;_WINDOWS;__i386__;PTW32_STATIC_LIB;ENABLE_NLS;PACKAGE=\&quot;pcsx2\&quot;;_CRT_SECURE_NO_DEPRECATE;TIXML_USE_STL"
StructMemberAlignment="5"
RuntimeTypeInfo="false"
PrecompiledHeaderFile="$(IntDir)/pcsx2.pch"
@ -19,7 +19,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib ws2_32.lib winmm.lib gnu_gettext.lib pthreadVC2.lib"
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib ws2_32.lib winmm.lib gnu_gettext.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="..\;..\libs"
GenerateDebugInformation="true"

View File

@ -48,6 +48,8 @@
#include "Paths.h"
#include "SamplProf.h"
#include "implement.h" // pthreads defines for startup/shutdown
#define COMPILEDATE __DATE__
static bool AccBreak = false;
@ -112,8 +114,14 @@ void RunExecute( const char* elf_file )
g_GameInProgress = false;
if( !cpuReset() )
throw std::runtime_error( "Cpu failed to initialize." );
try
{
cpuReset();
}
catch( std::exception& ex )
{
SysMessage( ex.what() );
}
if (OpenPlugins(g_TestRun.ptitle) == -1)
return;
@ -568,14 +576,17 @@ static int ParseCommandLine( int tokenCount, TCHAR *const *const tokens )
static void WinClose()
{
SysClose();
// Don't check Config.Profiler here -- the Profiler will know if it's running or not.
ProfilerTerm();
timeEndPeriod( 1 );
SysClose();
ReleasePlugins();
Console::Close();
pthread_win32_process_detach_np();
#ifdef PCSX2_VIRTUAL_MEM
VirtualFree(PS2MEM_BASE, 0, MEM_RELEASE);
#endif
@ -595,7 +606,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
InitCommonControls();
pInstance=hInstance;
FirstShow=true;
FirstShow=true; // this is used by cheats.cpp to search for stuff (broken?)
pthread_win32_process_attach_np();
#ifdef PCSX2_VIRTUAL_MEM
@ -728,10 +741,12 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
// timeslices for Sleep calls.
// (may not make much difference on most desktops but can improve performance
// a lot on laptops).
timeBeginPeriod( 1 );
timeBeginPeriod( 1 );
InitCPUTicks();
if (SysInit() == -1) return 1;
if( !SysInit() )
WinClose();
#ifdef PCSX2_DEVBUILD
if( g_TestRun.enabled || g_TestRun.ptitle != NULL ) {
@ -1235,7 +1250,7 @@ LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case ID_FILE_STATES_SAVE_SLOT3:
case ID_FILE_STATES_SAVE_SLOT4:
case ID_FILE_STATES_SAVE_SLOT5:
States_Load(LOWORD(wParam) - ID_FILE_STATES_SAVE_SLOT1);
States_Save(LOWORD(wParam) - ID_FILE_STATES_SAVE_SLOT1);
return FALSE;
case ID_FILE_STATES_SAVE_OTHER:
@ -1319,13 +1334,13 @@ LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
Config.PsxOut = !Config.PsxOut;
if(Config.PsxOut)
{
CheckMenuItem(gApp.hMenu,ID_CONSOLE,MF_UNCHECKED);
Console::Close();
CheckMenuItem(gApp.hMenu,ID_CONSOLE,MF_CHECKED);
Console::Open();
}
else
{
CheckMenuItem(gApp.hMenu,ID_CONSOLE,MF_CHECKED);
Console::Open();
CheckMenuItem(gApp.hMenu,ID_CONSOLE,MF_UNCHECKED);
Console::Close();
}
SaveConfig();
return FALSE;
@ -1598,7 +1613,7 @@ void CreateMainWindow(int nCmdShow) {
h+= rect.bottom - rect.top;
GetMenuItemRect(hWnd, gApp.hMenu, 0, &rect);
h+= rect.bottom - rect.top;
MoveWindow(hWnd, 40, 50, w, h, TRUE);
MoveWindow(hWnd, 60, 60, w, h, TRUE);
DestroyWindow(hStatusWnd);
hStatusWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", hWnd, 100);
@ -1606,6 +1621,7 @@ void CreateMainWindow(int nCmdShow) {
StatusSet(buf);
ShowWindow(hWnd, nCmdShow);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
SetForegroundWindow( hWnd );
}
@ -1655,7 +1671,7 @@ void ChangeLanguage(char *lang) {
static bool sinit=false;
int SysInit()
bool SysInit()
{
if( sinit )
SysClose();
@ -1670,17 +1686,19 @@ int SysInit()
if( emuLog == NULL )
emuLog = fopen(LOGS_DIR "\\emuLog.txt","w");
if( !cpuInit() ) return -1;
SysDetect();
while (LoadPlugins() == -1) {
if (Pcsx2Configure(NULL) == FALSE) {
exit(1); // user cancelled.
return false; // user cancelled.
}
}
sinit = true;
if( !cpuInit() )
return false;
return 0;
sinit = true;
return true;
}
void SysRestorableReset()

View File

@ -0,0 +1,152 @@
/* Pcsx2 - Pc Ps2 Emulator
* Copyright (C) 2002-2008 Pcsx2 Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if defined(_WIN32)
#include <windows.h>
#endif
#include "System.h"
#include "Threading.h"
#include "ix86/ix86.h"
#ifdef _WIN32
#include "implement.h" // win32 pthreads implementations.
#endif
namespace Threading
{
void CountLogicalCores( int LogicalCoresPerPhysicalCore )
{
DWORD vProcessCPUs;
DWORD vSystemCPUs;
cpuinfo.LogicalCores = 1;
if( !GetProcessAffinityMask (GetCurrentProcess (),
&vProcessCPUs, &vSystemCPUs) ) return;
int CPUs = 0;
DWORD bit;
for (bit = 1; bit != 0; bit <<= 1)
{
if (vSystemCPUs & bit)
CPUs++;
}
cpuinfo.LogicalCores = CPUs;
cpuinfo.PhysicalCores = CPUs / LogicalCoresPerPhysicalCore;
ptw32_smp_system = ( cpuinfo.LogicalCores > 1 ) ? TRUE : FALSE;
}
__forceinline void Timeslice()
{
Sleep(0);
}
// For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory
// improve performance and reduce cpu power consumption.
__forceinline void SpinWait()
{
__asm { pause };
}
void* Thread::_internal_callback( void* itsme )
{
jASSUME( itsme != NULL );
Thread& owner = *((Thread*)itsme);
pthread_win32_thread_attach_np();
try
{
owner.m_returncode = owner.Callback();
}
catch( std::exception& ex )
{
Console::Error( "Thread terminated abnormally with error:\n\t%s", ex.what() );
owner.m_returncode = -1;
}
owner.m_terminated = true;
pthread_win32_thread_detach_np();
return NULL;
}
// Note: assuming multicore is safer because it forces the interlocked routines to use
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
// having the LOCK prefix is very bad indeed.
static bool isMultiCore = true; // assume more than one CPU (safer)
//////////////////////////////////////////////////////////////////////
// Win32 versions of InterlockedExchange.
// These are much faster than the Win32 Kernel versions thanks to inlining.
__forceinline long pcsx2_InterlockedExchange( volatile long* target, long srcval )
{
// Use the pthreads-win32 implementation...
return ptw32_InterlockedExchange( target, srcval );
}
__forceinline long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp )
{
// Use the pthreads-win32 implementation...
return ptw32_InterlockedCompareExchange( target, srcval, comp );
}
__forceinline long pcsx2_InterlockedExchangeAdd( volatile long* target, long srcval )
{
long result;
// Use our own implementation...
// Preserving ecx/edx shouldn't be necessary here...
if( ptw32_smp_system )
{
__asm
{
//PUSH ecx
mov ecx,dword ptr [target]
mov eax,dword ptr [srcval]
lock xadd dword ptr [ecx],eax
mov dword ptr [result], eax
//POP ecx
}
}
else
{
__asm
{
//PUSH ecx
//PUSH edx
mov ecx,dword ptr [target]
//L1:
mov eax,dword ptr [srcval]
xadd dword ptr [ecx],eax
//jnz L1
mov dword ptr [result], eax
//POP edx
//POP ecx
}
}
return result;
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,178 +0,0 @@
/*
* Module: sched.h
*
* Purpose:
* Provides an implementation of POSIX realtime extensions
* as defined in
*
* POSIX 1003.1b-1993 (POSIX.1b)
*
* --------------------------------------------------------------------------
*
* Pthreads-win32 - POSIX Threads Library for Win32
* Copyright(C) 1998 John E. Bossom
* Copyright(C) 1999,2005 Pthreads-win32 contributors
*
* Contact Email: rpj@callisto.canberra.edu.au
*
* The current list of contributors is contained
* in the file CONTRIBUTORS included with the source
* code distribution. The list can also be seen at the
* following World Wide Web location:
* http://sources.redhat.com/pthreads-win32/contributors.html
*
* This library 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 Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library in the file COPYING.LIB;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef _SCHED_H
#define _SCHED_H
#undef PTW32_LEVEL
#if defined(_POSIX_SOURCE)
#define PTW32_LEVEL 0
/* Early POSIX */
#endif
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
#undef PTW32_LEVEL
#define PTW32_LEVEL 1
/* Include 1b, 1c and 1d */
#endif
#if defined(INCLUDE_NP)
#undef PTW32_LEVEL
#define PTW32_LEVEL 2
/* Include Non-Portable extensions */
#endif
#define PTW32_LEVEL_MAX 3
#if !defined(PTW32_LEVEL)
#define PTW32_LEVEL PTW32_LEVEL_MAX
/* Include everything */
#endif
#if __GNUC__ && ! defined (__declspec)
# error Please upgrade your GNU compiler to one that supports __declspec.
#endif
/*
* When building the DLL code, you should define PTW32_BUILD so that
* the variables/functions are exported correctly. When using the DLL,
* do NOT define PTW32_BUILD, and then the variables/functions will
* be imported correctly.
*/
#ifndef PTW32_STATIC_LIB
# ifdef PTW32_BUILD
# define PTW32_DLLPORT __declspec (dllexport)
# else
# define PTW32_DLLPORT __declspec (dllimport)
# endif
#else
# define PTW32_DLLPORT
#endif
/*
* This is a duplicate of what is in the autoconf config.h,
* which is only used when building the pthread-win32 libraries.
*/
#ifndef PTW32_CONFIG_H
# if defined(WINCE)
# define NEED_ERRNO
# define NEED_SEM
# endif
# if defined(_UWIN) || defined(__MINGW32__)
# define HAVE_MODE_T
# endif
#endif
/*
*
*/
#if PTW32_LEVEL >= PTW32_LEVEL_MAX
#ifdef NEED_ERRNO
#include "need_errno.h"
#else
#include <errno.h>
#endif
#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
#if defined(__MINGW32__) || defined(_UWIN)
#if PTW32_LEVEL >= PTW32_LEVEL_MAX
/* For pid_t */
# include <sys/types.h>
/* Required by Unix 98 */
# include <time.h>
#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
#else
typedef int pid_t;
#endif
/* Thread scheduling policies */
enum {
SCHED_OTHER = 0,
SCHED_FIFO,
SCHED_RR,
SCHED_MIN = SCHED_OTHER,
SCHED_MAX = SCHED_RR
};
struct sched_param {
int sched_priority;
};
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
PTW32_DLLPORT int __cdecl sched_yield (void);
PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);
PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);
PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);
PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);
/*
* Note that this macro returns ENOTSUP rather than
* ENOSYS as might be expected. However, returning ENOSYS
* should mean that sched_get_priority_{min,max} are
* not implemented as well as sched_rr_get_interval.
* This is not the case, since we just don't support
* round-robin scheduling. Therefore I have chosen to
* return the same value as sched_setscheduler when
* SCHED_RR is passed to it.
*/
#define sched_rr_get_interval(_pid, _interval) \
( errno = ENOTSUP, (int) -1 )
#ifdef __cplusplus
} /* End of extern "C" */
#endif /* __cplusplus */
#undef PTW32_LEVEL
#undef PTW32_LEVEL_MAX
#endif /* !_SCHED_H */

View File

@ -1,166 +0,0 @@
/*
* Module: semaphore.h
*
* Purpose:
* Semaphores aren't actually part of the PThreads standard.
* They are defined by the POSIX Standard:
*
* POSIX 1003.1b-1993 (POSIX.1b)
*
* --------------------------------------------------------------------------
*
* Pthreads-win32 - POSIX Threads Library for Win32
* Copyright(C) 1998 John E. Bossom
* Copyright(C) 1999,2005 Pthreads-win32 contributors
*
* Contact Email: rpj@callisto.canberra.edu.au
*
* The current list of contributors is contained
* in the file CONTRIBUTORS included with the source
* code distribution. The list can also be seen at the
* following World Wide Web location:
* http://sources.redhat.com/pthreads-win32/contributors.html
*
* This library 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 Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library in the file COPYING.LIB;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if !defined( SEMAPHORE_H )
#define SEMAPHORE_H
#undef PTW32_LEVEL
#if defined(_POSIX_SOURCE)
#define PTW32_LEVEL 0
/* Early POSIX */
#endif
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
#undef PTW32_LEVEL
#define PTW32_LEVEL 1
/* Include 1b, 1c and 1d */
#endif
#if defined(INCLUDE_NP)
#undef PTW32_LEVEL
#define PTW32_LEVEL 2
/* Include Non-Portable extensions */
#endif
#define PTW32_LEVEL_MAX 3
#if !defined(PTW32_LEVEL)
#define PTW32_LEVEL PTW32_LEVEL_MAX
/* Include everything */
#endif
#if __GNUC__ && ! defined (__declspec)
# error Please upgrade your GNU compiler to one that supports __declspec.
#endif
/*
* When building the DLL code, you should define PTW32_BUILD so that
* the variables/functions are exported correctly. When using the DLL,
* do NOT define PTW32_BUILD, and then the variables/functions will
* be imported correctly.
*/
#ifndef PTW32_STATIC_LIB
# ifdef PTW32_BUILD
# define PTW32_DLLPORT __declspec (dllexport)
# else
# define PTW32_DLLPORT __declspec (dllimport)
# endif
#else
# define PTW32_DLLPORT
#endif
/*
* This is a duplicate of what is in the autoconf config.h,
* which is only used when building the pthread-win32 libraries.
*/
#ifndef PTW32_CONFIG_H
# if defined(WINCE)
# define NEED_ERRNO
# define NEED_SEM
# endif
# if defined(_UWIN) || defined(__MINGW32__)
# define HAVE_MODE_T
# endif
#endif
/*
*
*/
#if PTW32_LEVEL >= PTW32_LEVEL_MAX
#ifdef NEED_ERRNO
#include "need_errno.h"
#else
#include <errno.h>
#endif
#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
#define _POSIX_SEMAPHORES
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#ifndef HAVE_MODE_T
typedef unsigned int mode_t;
#endif
typedef struct sem_t_ * sem_t;
PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,
int pshared,
unsigned int value);
PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,
const struct timespec * abstime);
PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,
int count);
PTW32_DLLPORT int __cdecl sem_open (const char * name,
int oflag,
mode_t mode,
unsigned int value);
PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_unlink (const char * name);
PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,
int * sval);
#ifdef __cplusplus
} /* End of extern "C" */
#endif /* __cplusplus */
#undef PTW32_LEVEL
#undef PTW32_LEVEL_MAX
#endif /* !SEMAPHORE_H */

View File

@ -0,0 +1,285 @@
This file documents non-portable functions and other issues.
Non-portable functions included in pthreads-win32
-------------------------------------------------
BOOL
pthread_win32_test_features_np(int mask)
This routine allows an application to check which
run-time auto-detected features are available within
the library.
The possible features are:
PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE
Return TRUE if the native version of
InterlockedCompareExchange() is being used.
PTW32_ALERTABLE_ASYNC_CANCEL
Return TRUE is the QueueUserAPCEx package
QUSEREX.DLL is available and the AlertDrv.sys
driver is loaded into Windows, providing
alertable (pre-emptive) asyncronous threads
cancelation. If this feature returns FALSE
then the default async cancel scheme is in
use, which cannot cancel blocked threads.
Features may be Or'ed into the mask parameter, in which case
the routine returns TRUE if any of the Or'ed features would
return TRUE. At this stage it doesn't make sense to Or features
but it may some day.
void *
pthread_timechange_handler_np(void *)
To improve tolerance against operator or time service
initiated system clock changes.
This routine can be called by an application when it
receives a WM_TIMECHANGE message from the system. At
present it broadcasts all condition variables so that
waiting threads can wake up and re-evaluate their
conditions and restart their timed waits if required.
It has the same return type and argument type as a
thread routine so that it may be called directly
through pthread_create(), i.e. as a separate thread.
Parameters
Although a parameter must be supplied, it is ignored.
The value NULL can be used.
Return values
It can return an error EAGAIN to indicate that not
all condition variables were broadcast for some reason.
Otherwise, 0 is returned.
If run as a thread, the return value is returned
through pthread_join().
The return value should be cast to an integer.
HANDLE
pthread_getw32threadhandle_np(pthread_t thread);
Returns the win32 thread handle that the POSIX
thread "thread" is running as.
Applications can use the win32 handle to set
win32 specific attributes of the thread.
int
pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, int kind)
int
pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, int *kind)
These two routines are included for Linux compatibility
and are direct equivalents to the standard routines
pthread_mutexattr_settype
pthread_mutexattr_gettype
pthread_mutexattr_setkind_np accepts the following
mutex kinds:
PTHREAD_MUTEX_FAST_NP
PTHREAD_MUTEX_ERRORCHECK_NP
PTHREAD_MUTEX_RECURSIVE_NP
These are really just equivalent to (respectively):
PTHREAD_MUTEX_NORMAL
PTHREAD_MUTEX_ERRORCHECK
PTHREAD_MUTEX_RECURSIVE
int
pthread_delay_np (const struct timespec *interval);
This routine causes a thread to delay execution for a specific period of time.
This period ends at the current time plus the specified interval. The routine
will not return before the end of the period is reached, but may return an
arbitrary amount of time after the period has gone by. This can be due to
system load, thread priorities, and system timer granularity.
Specifying an interval of zero (0) seconds and zero (0) nanoseconds is
allowed and can be used to force the thread to give up the processor or to
deliver a pending cancelation request.
This routine is a cancelation point.
The timespec structure contains the following two fields:
tv_sec is an integer number of seconds.
tv_nsec is an integer number of nanoseconds.
Return Values
If an error condition occurs, this routine returns an integer value
indicating the type of error. Possible return values are as follows:
0 Successful completion.
[EINVAL] The value specified by interval is invalid.
int
pthread_num_processors_np
This routine (found on HPUX systems) returns the number of processors
in the system. This implementation actually returns the number of
processors available to the process, which can be a lower number
than the system's number, depending on the process's affinity mask.
BOOL
pthread_win32_process_attach_np (void);
BOOL
pthread_win32_process_detach_np (void);
BOOL
pthread_win32_thread_attach_np (void);
BOOL
pthread_win32_thread_detach_np (void);
These functions contain the code normally run via dllMain
when the library is used as a dll but which need to be
called explicitly by an application when the library
is statically linked.
You will need to call pthread_win32_process_attach_np() before
you can call any pthread routines when statically linking.
You should call pthread_win32_process_detach_np() before
exiting your application to clean up.
pthread_win32_thread_attach_np() is currently a no-op, but
pthread_win32_thread_detach_np() is needed to clean up
the implicit pthread handle that is allocated to a Win32 thread if
it calls certain pthreads routines. Call this routine when the
Win32 thread exits.
These functions invariably return TRUE except for
pthread_win32_process_attach_np() which will return FALSE
if pthreads-win32 initialisation fails.
int
pthreadCancelableWait (HANDLE waitHandle);
int
pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout);
These two functions provide hooks into the pthread_cancel
mechanism that will allow you to wait on a Windows handle
and make it a cancellation point. Both functions block
until either the given w32 handle is signaled, or
pthread_cancel has been called. It is implemented using
WaitForMultipleObjects on 'waitHandle' and a manually
reset w32 event used to implement pthread_cancel.
Non-portable issues
-------------------
Thread priority
POSIX defines a single contiguous range of numbers that determine a
thread's priority. Win32 defines priority classes and priority
levels relative to these classes. Classes are simply priority base
levels that the defined priority levels are relative to such that,
changing a process's priority class will change the priority of all
of it's threads, while the threads retain the same relativity to each
other.
A Win32 system defines a single contiguous monotonic range of values
that define system priority levels, just like POSIX. However, Win32
restricts individual threads to a subset of this range on a
per-process basis.
The following table shows the base priority levels for combinations
of priority class and priority value in Win32.
Process Priority Class Thread Priority Level
-----------------------------------------------------------------
1 IDLE_PRIORITY_CLASS THREAD_PRIORITY_IDLE
1 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE
1 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE
1 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE
1 HIGH_PRIORITY_CLASS THREAD_PRIORITY_IDLE
2 IDLE_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
3 IDLE_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
4 IDLE_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
4 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
5 IDLE_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
5 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
5 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
6 IDLE_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
6 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
6 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
7 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
7 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
7 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
8 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
8 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
8 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
8 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
9 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
9 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
9 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
10 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
10 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
11 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
11 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
11 HIGH_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
12 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
12 HIGH_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
13 HIGH_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
14 HIGH_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL
15 IDLE_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL
15 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL
15 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL
15 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL
16 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_IDLE
17 REALTIME_PRIORITY_CLASS -7
18 REALTIME_PRIORITY_CLASS -6
19 REALTIME_PRIORITY_CLASS -5
20 REALTIME_PRIORITY_CLASS -4
21 REALTIME_PRIORITY_CLASS -3
22 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_LOWEST
23 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL
24 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_NORMAL
25 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL
26 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST
27 REALTIME_PRIORITY_CLASS 3
28 REALTIME_PRIORITY_CLASS 4
29 REALTIME_PRIORITY_CLASS 5
30 REALTIME_PRIORITY_CLASS 6
31 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL
Windows NT: Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported.
As you can see, the real priority levels available to any individual
Win32 thread are non-contiguous.
An application using pthreads-win32 should not make assumptions about
the numbers used to represent thread priority levels, except that they
are monotonic between the values returned by sched_get_priority_min()
and sched_get_priority_max(). E.g. Windows 95, 98, NT, 2000, XP make
available a non-contiguous range of numbers between -15 and 15, while
at least one version of WinCE (3.0) defines the minimum priority
(THREAD_PRIORITY_LOWEST) as 5, and the maximum priority
(THREAD_PRIORITY_HIGHEST) as 1.
Internally, pthreads-win32 maps any priority levels between
THREAD_PRIORITY_IDLE and THREAD_PRIORITY_LOWEST to THREAD_PRIORITY_LOWEST,
or between THREAD_PRIORITY_TIME_CRITICAL and THREAD_PRIORITY_HIGHEST to
THREAD_PRIORITY_HIGHEST. Currently, this also applies to
REALTIME_PRIORITY_CLASSi even if levels -7, -6, -5, -4, -3, 3, 4, 5, and 6
are supported.
If it wishes, a Win32 application using pthreads-win32 can use the Win32
defined priority macros THREAD_PRIORITY_IDLE through
THREAD_PRIORITY_TIME_CRITICAL.

View File

@ -54,6 +54,7 @@ int ptw32_features = 0;
BOOL ptw32_smp_system = PTW32_TRUE; /* Safer if assumed true initially. */
#if 0
/*
* Function pointer to InterlockedCompareExchange if it exists, otherwise
* it will be set at runtime to a substitute local version with the same
@ -64,6 +65,7 @@ PTW32_INTERLOCKED_LONG
PTW32_INTERLOCKED_LONG,
PTW32_INTERLOCKED_LONG) =
NULL;
#endif
/*
* Function pointer to QueueUserAPCEx if it exists, otherwise

View File

@ -510,10 +510,12 @@ struct ThreadKeyAssoc
#define PTW32_MIN(a,b) ((a)>(b)?(b):(a))
#if 0
/* Declared in global.c */
extern PTW32_INTERLOCKED_LONG (WINAPI *
ptw32_interlocked_compare_exchange)
(PTW32_INTERLOCKED_LPLONG, PTW32_INTERLOCKED_LONG, PTW32_INTERLOCKED_LONG);
#endif
/* Declared in pthread_cancel.c */
extern DWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD);
@ -521,6 +523,11 @@ extern DWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD);
/* Thread Reuse stack bottom marker. Must not be NULL or any valid pointer to memory. */
#define PTW32_THREAD_REUSE_EMPTY ((ptw32_thread_t *) 1)
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
extern int ptw32_processInitialized;
extern ptw32_thread_t * ptw32_threadReuseTop;
extern ptw32_thread_t * ptw32_threadReuseBottom;
@ -548,10 +555,6 @@ extern CRITICAL_SECTION ptw32_spinlock_test_init_lock;
extern int pthread_count;
#endif
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/*
* =====================
@ -568,12 +571,12 @@ extern "C"
int ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock);
PTW32_INTERLOCKED_LONG WINAPI
ptw32_InterlockedCompareExchange (PTW32_INTERLOCKED_LPLONG location,
ptw32_InterlockedCompareExchange (volatile PTW32_INTERLOCKED_LPLONG location,
PTW32_INTERLOCKED_LONG value,
PTW32_INTERLOCKED_LONG comparand);
LONG WINAPI
ptw32_InterlockedExchange (LPLONG location,
ptw32_InterlockedExchange (volatile PTW32_INTERLOCKED_LPLONG location,
LONG value);
DWORD
@ -669,12 +672,13 @@ extern "C"
* Defaults. Could be overridden when building the inlined version of the dll.
* See ptw32_InterlockedCompareExchange.c
*/
// Default to inlining the pthreads versions... (air)
#ifndef PTW32_INTERLOCKED_COMPARE_EXCHANGE
#define PTW32_INTERLOCKED_COMPARE_EXCHANGE ptw32_interlocked_compare_exchange
#define PTW32_INTERLOCKED_COMPARE_EXCHANGE ptw32_InterlockedCompareExchange
#endif
#ifndef PTW32_INTERLOCKED_EXCHANGE
#define PTW32_INTERLOCKED_EXCHANGE InterlockedExchange
#define PTW32_INTERLOCKED_EXCHANGE ptw32_InterlockedExchange
#endif

View File

@ -563,9 +563,29 @@ extern "C"
* that available with a simple pointer. It should scale for either
* IA-32 or IA-64.
*/
typedef struct {
typedef struct _ptw32_handle_t {
void * p; /* Pointer to actual object */
unsigned int x; /* Extra information - reuse count etc */
#ifdef __cplusplus
// Added support for various operators so that the struct is
// more pthreads-compliant in behavior. (air)
const bool operator ==( const void* rightside )
{
return p == rightside;
}
const bool operator !=( const void* rightside )
{
return p != rightside;
}
bool operator =( void* rightside )
{
p = rightside;
}
#endif
} ptw32_handle_t;
typedef ptw32_handle_t pthread_t;

View File

@ -76,6 +76,11 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
return EINVAL;
}
#ifdef PTW32_STATIC_LIB
// This allos for C++ static initializers to function without crashes. (air)
pthread_win32_process_attach_np();
#endif
if ((attr != NULL && *attr != NULL) &&
((*attr)->pshared == PTHREAD_PROCESS_SHARED))
{

View File

@ -49,6 +49,11 @@ pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr)
return EINVAL;
}
#ifdef PTW32_STATIC_LIB
// This allos for C++ static initializers to function without crashes. (air)
pthread_win32_process_attach_np();
#endif
if (attr != NULL
&& *attr != NULL && (*attr)->pshared == PTHREAD_PROCESS_SHARED)
{

View File

@ -52,6 +52,11 @@ pthread_rwlock_init (pthread_rwlock_t * rwlock,
return EINVAL;
}
#ifdef PTW32_STATIC_LIB
// This allos for C++ static initializers to function without crashes. (air)
pthread_win32_process_attach_np();
#endif
if (attr != NULL && *attr != NULL)
{
result = EINVAL; /* Not supported */

View File

@ -50,6 +50,17 @@ pthread_spin_init (pthread_spinlock_t * lock, int pshared)
return EINVAL;
}
#ifdef PTW32_STATIC_LIB
// This allos for C++ static initializers to function without crashes. (air)
pthread_win32_process_attach_np();
#endif
// Optimized this so that it doesn't do cpu count checks needlessly. (air)
#if _POSIX_THREAD_PROCESS_SHARED >= 0
/*
* Not implemented yet.
*/
if (0 != ptw32_getprocessors (&cpus))
{
cpus = 1;
@ -63,23 +74,20 @@ pthread_spin_init (pthread_spinlock_t * lock, int pshared)
* Creating spinlock that can be shared between
* processes.
*/
#if _POSIX_THREAD_PROCESS_SHARED >= 0
/*
* Not implemented yet.
*/
#error ERROR [__FILE__, line __LINE__]: Process shared spin locks are not supported yet.
#else
return ENOSYS;
#endif /* _POSIX_THREAD_PROCESS_SHARED */
}
}
#else
if (pshared == PTHREAD_PROCESS_SHARED)
return ENOSYS;
#endif /* _POSIX_THREAD_PROCESS_SHARED */
s = (pthread_spinlock_t) calloc (1, sizeof (*s));
if (s == NULL)

View File

@ -54,6 +54,8 @@ pthread_win32_process_attach_np ()
DWORD_PTR vProcessCPUs;
DWORD_PTR vSystemCPUs;
if( ptw32_processInitialized ) return TRUE;
result = ptw32_processInitialize ();
#ifdef _UWIN
@ -91,6 +93,8 @@ pthread_win32_process_attach_np ()
#endif
#if 0
#ifdef WINCE
/*
@ -107,6 +111,7 @@ pthread_win32_process_attach_np ()
#endif
// We're only using pthreads' inline version of InterlockedExchange
ptw32_interlocked_compare_exchange =
(PTW32_INTERLOCKED_LONG (WINAPI *)
(PTW32_INTERLOCKED_LPLONG, PTW32_INTERLOCKED_LONG,
@ -144,6 +149,8 @@ pthread_win32_process_attach_np ()
ptw32_features |= PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE;
}
#endif
/*
* Load QUSEREX.DLL and try to get address of QueueUserAPCEx
*/

View File

@ -46,8 +46,8 @@
* We now use this version wherever possible so we can inline it.
*/
PTW32_INTERLOCKED_LONG WINAPI
ptw32_InterlockedCompareExchange (PTW32_INTERLOCKED_LPLONG location,
INLINE PTW32_INTERLOCKED_LONG WINAPI
ptw32_InterlockedCompareExchange (volatile PTW32_INTERLOCKED_LPLONG location,
PTW32_INTERLOCKED_LONG value,
PTW32_INTERLOCKED_LONG comparand)
{
@ -77,29 +77,29 @@ ptw32_InterlockedCompareExchange (PTW32_INTERLOCKED_LPLONG location,
#define HAVE_INLINABLE_INTERLOCKED_CMPXCHG
{
_asm {
PUSH ecx
PUSH edx
//PUSH ecx
//PUSH edx
MOV ecx,dword ptr [location]
MOV edx,dword ptr [value]
MOV eax,dword ptr [comparand]
LOCK CMPXCHG dword ptr [ecx],edx
MOV dword ptr [result], eax
POP edx
POP ecx
//POP edx
//POP ecx
}
}
else
{
_asm {
PUSH ecx
PUSH edx
//PUSH ecx
//PUSH edx
MOV ecx,dword ptr [location]
MOV edx,dword ptr [value]
MOV eax,dword ptr [comparand]
CMPXCHG dword ptr [ecx],edx
MOV dword ptr [result], eax
POP edx
POP ecx
//POP edx
//POP ecx
}
}
@ -158,8 +158,8 @@ ptw32_InterlockedCompareExchange (PTW32_INTERLOCKED_LPLONG location,
* We now use this version wherever possible so we can inline it.
*/
LONG WINAPI
ptw32_InterlockedExchange (LPLONG location,
INLINE LONG WINAPI
ptw32_InterlockedExchange (volatile PTW32_INTERLOCKED_LPLONG location,
LONG value)
{
@ -188,12 +188,12 @@ ptw32_InterlockedExchange (LPLONG location,
{
_asm {
PUSH ecx
//PUSH ecx
MOV ecx,dword ptr [location]
MOV eax,dword ptr [value]
XCHG dword ptr [ecx],eax
MOV dword ptr [result], eax
POP ecx
//POP ecx
}
}
else
@ -215,16 +215,16 @@ ptw32_InterlockedExchange (LPLONG location,
* Can we do without the PUSH/POP instructions?
*/
_asm {
PUSH ecx
PUSH edx
//PUSH ecx
//PUSH edx
MOV ecx,dword ptr [location]
MOV edx,dword ptr [value]
L1: MOV eax,dword ptr [ecx]
CMPXCHG dword ptr [ecx],edx
JNZ L1
MOV dword ptr [result], eax
POP edx
POP ecx
//POP edx
//POP ecx
}
}

View File

@ -1325,51 +1325,6 @@ EEINSTWRITEBACK* _recCheckWriteBack(int cycle)
return NULL;
}
void cpudetectSSE3(void* pfnCallSSE3)
{
cpucaps.hasStreamingSIMD3Extensions = 1;
#ifdef _MSC_VER
__try {
((void (*)())pfnCallSSE3)();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
cpucaps.hasStreamingSIMD3Extensions = 0;
}
#else // linux
#ifdef PCSX2_FORCESSE3
cpucaps.hasStreamingSIMD3Extensions = 1;
#else
// exception handling doesn't work, so disable for x86 builds of linux
cpucaps.hasStreamingSIMD3Extensions = 0;
#endif
#endif
}
void cpudetectSSE4(void* pfnCallSSE4)
{
return;
cpucaps.hasStreamingSIMD4Extensions = 1;
#ifdef _MSC_VER
__try {
((void (*)())pfnCallSSE4)();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
cpucaps.hasStreamingSIMD4Extensions = 0;
}
#else // linux
#ifdef PCSX2_FORCESSE4
cpucaps.hasStreamingSIMD4Extensions = 1;
#else
// exception handling doesn't work, so disable for x86 builds of linux
cpucaps.hasStreamingSIMD4Extensions = 0;
#endif
#endif
}
struct BASEBLOCKS
{
// 0 - ee, 1 - iop

View File

@ -106,10 +106,11 @@ void _freeX86tempregs();
u8 _hasFreeX86reg();
// see MEM_X defines for argX format
void _callPushArg(u32 arg, uptr argmem, x86IntRegType X86ARG); /// X86ARG is ignored for 32bit recs
void _callFunctionArg1(uptr fn, u32 arg1, uptr arg1mem);
void _callFunctionArg2(uptr fn, u32 arg1, u32 arg2, uptr arg1mem, uptr arg2mem);
void _callFunctionArg3(uptr fn, u32 arg1, u32 arg2, u32 arg3, uptr arg1mem, uptr arg2mem, uptr arg3mem);
extern void _callPushArg(u32 arg, uptr argmem); /// X86ARG is ignored for 32bit recs
extern void _callFunctionArg1(uptr fn, u32 arg1, uptr arg1mem);
extern void _callFunctionArg2(uptr fn, u32 arg1, u32 arg2, uptr arg1mem, uptr arg2mem);
extern void _callFunctionArg3(uptr fn, u32 arg1, u32 arg2, u32 arg3, uptr arg1mem, uptr arg2mem, uptr arg3mem);
// when using mmx/xmm regs, use; 0 is load
// freezes no matter the state
@ -403,8 +404,6 @@ BASEBLOCKEX** GetAllBaseBlocks(int* pnum, int cpu);
#define MODE_8BITREG 0x80 // when allocating x86regs, use only eax, ecx, edx, and ebx
void SetMMXstate();
void cpudetectSSE3(void* pfnCallSSE3);
void cpudetectSSE4(void* pfnCallSSE4);
void _recMove128MtoM(u32 to, u32 from);

View File

@ -35,8 +35,6 @@ using namespace std;
#include "GS.h"
#include "DebugTools/Debug.h"
extern u32 CSRw;
#ifdef PCSX2_VIRTUAL_MEM
#define PS2GS_BASE(mem) ((PS2MEM_BASE+0x12000000)+(mem&0x13ff))
#else
@ -44,6 +42,61 @@ extern u8 g_RealGSMem[0x2000];
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
#endif
// __thiscall -- Calling Convention Notes.
// ** MSVC passes the pointer to the object as ECX. Other parameters are passed normally
// (_cdecl style). Stack is cleaned by the callee.
// ** GCC works just like a __cdecl, except the pointer to the object is pushed onto the
// stack last (passed as the first parameter). Caller cleans up the stack.
// The GCC code below is untested. Hope it works. :| (air)
// Used to send 8, 16, and 32 bit values to the MTGS.
static void __fastcall _rec_mtgs_Send32orSmaller( GS_RINGTYPE ringtype, u32 mem, int mmreg )
{
iFlushCall(0);
PUSH32I( 0 );
_callPushArg( mmreg, 0 );
PUSH32I( mem&0x13ff );
PUSH32I( ringtype );
#ifdef _MSC_VER
MOV32ItoR( ECX, (uptr)mtgsThread );
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
#else // GCC -->
PUSH32I( (uptr)mtgsThread );
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
#ifndef __x86_64__
ADD32ItoR( ESP, 20 );
#endif
#endif
}
// Used to send 64 and 128 bit values to the MTGS (called twice for 128's, which
// is why it doesn't call iFlushCall)
static void __fastcall _rec_mtgs_Send64( uptr gsbase, u32 mem, int mmreg )
{
PUSH32M( gsbase+4 );
PUSH32M( gsbase );
PUSH32I( mem&0x13ff );
PUSH32I( GS_RINGTYPE_MEMWRITE64 );
#ifdef _MSC_VER
MOV32ItoR( ECX, (uptr)mtgsThread );
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
#else // GCC -->
PUSH32I( (uptr)mtgsThread );
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
#ifndef __x86_64__
ADD32ItoR( ESP, 20 );
#endif
#endif
}
void gsConstWrite8(u32 mem, int mmreg)
{
switch (mem&~3) {
@ -59,12 +112,9 @@ void gsConstWrite8(u32 mem, int mmreg)
default:
_eeWriteConstMem8( (uptr)PS2GS_BASE(mem), mmreg );
if( CHECK_MULTIGS ) {
iFlushCall(0);
if( mtgsThread != NULL )
_rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE8, mem, mmreg );
_callFunctionArg3((uptr)GSRingBufSimplePacket, MEM_CONSTTAG, MEM_CONSTTAG, mmreg,
GS_RINGTYPE_MEMWRITE8, mem&0x13ff, 0);
}
break;
}
}
@ -96,11 +146,8 @@ void gsConstWrite16(u32 mem, int mmreg)
default:
_eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg );
if( CHECK_MULTIGS ) {
iFlushCall(0);
_callFunctionArg3((uptr)GSRingBufSimplePacket, MEM_CONSTTAG, MEM_CONSTTAG, mmreg,
GS_RINGTYPE_MEMWRITE16, mem&0x13ff, 0);
}
if( mtgsThread != NULL )
_rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE16, mem, mmreg );
break;
}
@ -157,12 +204,8 @@ void gsConstWrite32(u32 mem, int mmreg) {
default:
_eeWriteConstMem32( (uptr)PS2GS_BASE(mem), mmreg );
if( CHECK_MULTIGS ) {
iFlushCall(0);
_callFunctionArg3((uptr)GSRingBufSimplePacket, MEM_CONSTTAG, MEM_CONSTTAG, mmreg,
GS_RINGTYPE_MEMWRITE32, mem&0x13ff, 0);
}
if( mtgsThread != NULL )
_rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE32, mem, mmreg );
break;
}
@ -191,17 +234,10 @@ void gsConstWrite64(u32 mem, int mmreg)
default:
_eeWriteConstMem64((uptr)PS2GS_BASE(mem), mmreg);
if( CHECK_MULTIGS ) {
iFlushCall(0);
_callPushArg(MEM_MEMORYTAG, (uptr)PS2GS_BASE(mem)+4, X86ARG4);
_callPushArg(MEM_MEMORYTAG, (uptr)PS2GS_BASE(mem), X86ARG3);
_callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2);
_callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE64, X86ARG1);
CALLFunc((uptr)GSRingBufSimplePacket);
#ifndef __x86_64__
ADD32ItoR(ESP, 16);
#endif
if( mtgsThread != NULL )
{
iFlushCall( 0 );
_rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg );
}
break;
@ -232,26 +268,11 @@ void gsConstWrite128(u32 mem, int mmreg)
default:
_eeWriteConstMem128( (uptr)PS2GS_BASE(mem), mmreg);
if( CHECK_MULTIGS ) {
if( mtgsThread != NULL )
{
iFlushCall(0);
_callPushArg(MEM_MEMORYTAG, (uptr)PS2GS_BASE(mem)+4, X86ARG4);
_callPushArg(MEM_MEMORYTAG, (uptr)PS2GS_BASE(mem), X86ARG3);
_callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2);
_callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE64, X86ARG1);
CALLFunc((uptr)GSRingBufSimplePacket);
#ifndef __x86_64__
ADD32ItoR(ESP, 16);
#endif
_callPushArg(MEM_MEMORYTAG, (uptr)PS2GS_BASE(mem)+12, X86ARG4);
_callPushArg(MEM_MEMORYTAG, (uptr)PS2GS_BASE(mem)+8, X86ARG3);
_callPushArg(MEM_CONSTTAG, (mem&0x13ff)+8, X86ARG2);
_callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE64, X86ARG1);
CALLFunc((uptr)GSRingBufSimplePacket);
#ifndef __x86_64__
ADD32ItoR(ESP, 16);
#endif
_rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg );
_rec_mtgs_Send64( (uptr)PS2GS_BASE(mem)+8, mem+8, mmreg );
}
break;

View File

@ -2333,34 +2333,30 @@ void recVUMI_XTOP( VURegs *VU, int info )
//------------------------------------------------------------------
// VU1XGKICK_MTGSTransfer() - Called by ivuZerorec.cpp
//------------------------------------------------------------------
#if defined(_WIN32) && !defined(WIN32_PTHREADS)
extern HANDLE g_hGsEvent;
#else
extern pthread_cond_t g_condGsEvent;
#endif
void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr)
{
u32 size;
u8* pmem;
u32* data = (u32*)((u8*)pMem + (addr&0x3fff));
u32* data = (u32*)((u8*)pMem + (addr&0x3fff));
static int scount = 0;
++scount;
// fixme: The gifTagDummy function in the MTGS (called by PrepDataPacket) has a
// hack that aborts the packet if it goes past the end of VU1 memory.
// Chances are this should be a "loops around memory" situation, and the packet
// should be continued starting at addr zero (0).
size = GSgifTransferDummy(0, (u8*)data, (0x4000-(addr&0x3fff))>>4);
size = 0x4000-(size<<4)-(addr&0x3fff);
assert( size >= 0 );
size = mtgsThread->PrepDataPacket( GIF_PATH_1, data, (0x4000-(addr&0x3fff)));
//size = 0x4000-(size<<4)-(addr&0x3fff);
jASSUME( size > 0 );
if( size > 0 ) {
pmem = GSRingBufCopy(size, GS_RINGTYPE_P1);
assert( pmem != NULL );
FreezeMMXRegs(1);
memcpy_fast(pmem, (u8*)pMem+addr, size);
FreezeMMXRegs(0);
GSRINGBUF_DONECOPY(pmem, size);
//if( size > 0 )
{
u8* pmem = mtgsThread->GetDataPacketPtr();
//FreezeMMXRegs(1);
//memcpy_fast(pmem, (u8*)pMem+addr, size);
//FreezeMMXRegs(0);
// we can use the faster memcpy_raz_ here (src/dest are garaunteed to be aligned)
memcpy_raz_(pmem, (u8*)pMem+addr, size);
mtgsThread->SendDataPacket();
}
}
//------------------------------------------------------------------
@ -2378,10 +2374,10 @@ void recVUMI_XGKICK( VURegs *VU, int info )
iFlushCall(FLUSH_NOCONST);
_callPushArg(MEM_X86TAG, fsreg, X86ARG2);
_callPushArg(MEM_CONSTTAG, (uptr)VU->Mem, X86ARG1);
_callPushArg(MEM_X86TAG, fsreg);
_callPushArg(MEM_CONSTTAG, (uptr)VU->Mem);
if( CHECK_MULTIGS ) {
if( mtgsThread != NULL ) {
CALLFunc((uptr)VU1XGKICK_MTGSTransfer);
#ifndef __x86_64__
ADD32ItoR(ESP, 8);

View File

@ -4095,7 +4095,7 @@ void recSVUMI_XGKICK_( VURegs *VU )
//CALLFunc((u32)countfn);
if( CHECK_MULTIGS ) {
if( mtgsThread != NULL ) {
CALLFunc((uptr)VU1XGKICK_MTGSTransfer);
#ifndef __x86_64__
ADD32ItoR(ESP, 8);

View File

@ -847,7 +847,7 @@ void SetFPUstate() {
}
}
void _callPushArg(u32 arg, uptr argmem, x86IntRegType X86ARG)
__forceinline void _callPushArg(u32 arg, uptr argmem)
{
if( IS_X86REG(arg) ) PUSH32R(arg&0xff);
else if( IS_CONSTREG(arg) ) PUSH32I(argmem);
@ -877,26 +877,26 @@ void _callPushArg(u32 arg, uptr argmem, x86IntRegType X86ARG)
}
}
void _callFunctionArg1(uptr fn, u32 arg1, uptr arg1mem)
__forceinline void _callFunctionArg1(uptr fn, u32 arg1, uptr arg1mem)
{
_callPushArg(arg1, arg1mem, -1);
_callPushArg(arg1, arg1mem);
CALLFunc((uptr)fn);
ADD32ItoR(ESP, 4);
}
void _callFunctionArg2(uptr fn, u32 arg1, u32 arg2, uptr arg1mem, uptr arg2mem)
__forceinline void _callFunctionArg2(uptr fn, u32 arg1, u32 arg2, uptr arg1mem, uptr arg2mem)
{
_callPushArg(arg2, arg2mem, -1);
_callPushArg(arg1, arg1mem, -1);
_callPushArg(arg2, arg2mem);
_callPushArg(arg1, arg1mem);
CALLFunc((uptr)fn);
ADD32ItoR(ESP, 8);
}
void _callFunctionArg3(uptr fn, u32 arg1, u32 arg2, u32 arg3, uptr arg1mem, uptr arg2mem, uptr arg3mem)
__forceinline void _callFunctionArg3(uptr fn, u32 arg1, u32 arg2, u32 arg3, uptr arg1mem, uptr arg2mem, uptr arg3mem)
{
_callPushArg(arg3, arg3mem, -1);
_callPushArg(arg2, arg2mem, -1);
_callPushArg(arg1, arg1mem, -1);
_callPushArg(arg3, arg3mem);
_callPushArg(arg2, arg2mem);
_callPushArg(arg1, arg1mem);
CALLFunc((uptr)fn);
ADD32ItoR(ESP, 12);
}

View File

@ -16,10 +16,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
// recompiler reworked to add dynamic linking zerofrog(@gmail.com) Jan06
// Recompiled completely rewritten to add block level recompilation/reg-caching/
// liveness analysis/constant propagation Apr06 (zerofrog@gmail.com)
#include <stdlib.h>
#include <string.h>
#include <assert.h>
@ -77,7 +73,9 @@ static u8* recPtr = NULL, *recStackPtr = NULL;
static EEINST* s_pInstCache = NULL;
static u32 s_nInstCacheSize = 0;
bool g_EEFreezeRegs = false; // if set, should freeze the regs
// used to disable register freezing during cpuBranchTests (registers
// are safe then since they've been completely flushed)
bool g_EEFreezeRegs = false;
static BASEBLOCK* s_pCurBlock = NULL;
static BASEBLOCKEX* s_pCurBlockEx = NULL;
@ -1486,8 +1484,19 @@ void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR)
#define REC_CACHEMEM 0x01000000
void __fastcall dyna_block_discard(u32 start,u32 sz);
int recInit( void )
static void recInit()
{
// Hardware Requirements Check...
if ( !( cpucaps.hasMultimediaExtensions ) )
throw Exception::HardwareDeficiency( _( "Processor doesn't support MMX" ) );
if ( !( cpucaps.hasStreamingSIMDExtensions ) )
throw Exception::HardwareDeficiency( _( "Processor doesn't support SSE" ) );
if ( !( cpucaps.hasStreamingSIMD2Extensions ) )
throw Exception::HardwareDeficiency( _( "Processor doesn't support SSE2" ) );
int i;
const u8 macarr[16] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
@ -1500,10 +1509,8 @@ int recInit( void )
if( recMem == NULL )
recMem = (u8*)SysMmap(0x0d000000, REC_CACHEMEM+0x1000);
if( recMem == NULL ) {
Console::Error("Error > R5900-32 failed to allocate recompiler memory.");
return 1;
}
if( recMem == NULL )
throw std::bad_alloc("R5900-32 failed to allocate recompiler memory.");
// 32 alignment necessary
if( recRAM == NULL )
@ -1527,8 +1534,7 @@ int recInit( void )
recROM1 == NULL || recMem == NULL || recLUT == NULL ||
recStack == NULL || s_pInstCache == NULL )
{
SysMessage( _( "Error allocating memory" ) );
return -1;
throw std::bad_alloc( _( "Heap-based memory allocation failed." ) );
}
// No errors.. Proceed with initialization:
@ -1565,68 +1571,14 @@ int recInit( void )
x86SetPtr(recMem+REC_CACHEMEM);
dyna_block_discard_recmem=(u8*)x86Ptr;
JMP32( (uptr)&dyna_block_discard - ( (u32)x86Ptr + 5 ));
// SSE3 detection, manually create the code
x86SetPtr(recMem);
SSE3_MOVSLDUP_XMM_to_XMM(XMM0, XMM0);
RET();
cpudetectSSE3(recMem);
x86SetPtr(recMem);
SSE4_DPPS_XMM_to_XMM(XMM0, XMM0, 0);
RET();
cpudetectSSE4(recMem);
SysPrintf( "x86Init: \n" );
SysPrintf( "\tCPU vender name = %s\n", cpuinfo.x86ID );
SysPrintf( "\tFamilyID = %x\n", cpuinfo.x86StepID );
SysPrintf( "\tx86Family = %s\n", cpuinfo.x86Fam );
SysPrintf( "\tCPU speed = %d.%03d Ghz\n", cpuinfo.cpuspeed / 1000, cpuinfo.cpuspeed%1000);
SysPrintf( "\tx86PType = %s\n", cpuinfo.x86Type );
SysPrintf( "\tx86Flags = %8.8x %8.8x\n", cpuinfo.x86Flags, cpuinfo.x86Flags2 );
SysPrintf( "\tx86EFlags = %8.8x\n", cpuinfo.x86EFlags );
SysPrintf( "Features: \n" );
SysPrintf( "\t%sDetected MMX\n", cpucaps.hasMultimediaExtensions ? "" : "Not " );
SysPrintf( "\t%sDetected SSE\n", cpucaps.hasStreamingSIMDExtensions ? "" : "Not " );
SysPrintf( "\t%sDetected SSE2\n", cpucaps.hasStreamingSIMD2Extensions ? "" : "Not " );
SysPrintf( "\t%sDetected SSE3\n", cpucaps.hasStreamingSIMD3Extensions ? "" : "Not " );
SysPrintf( "\t%sDetected SSE4.1\n", cpucaps.hasStreamingSIMD4Extensions ? "" : "Not " );
if ( cpuinfo.x86ID[0] == 'A' ) //AMD cpu
{
SysPrintf( " Extended AMD Features: \n" );
SysPrintf( "\t%sDetected MMX2\n", cpucaps.hasMultimediaExtensionsExt ? "" : "Not " );
SysPrintf( "\t%sDetected 3DNOW\n", cpucaps.has3DNOWInstructionExtensions ? "" : "Not " );
SysPrintf( "\t%sDetected 3DNOW2\n", cpucaps.has3DNOWInstructionExtensionsExt ? "" : "Not " );
}
if ( !( cpucaps.hasMultimediaExtensions ) )
{
SysMessage( _( "Processor doesn't supports MMX, can't run recompiler without that" ) );
return -1;
}
if ( !( cpucaps.hasStreamingSIMDExtensions ) )
{
SysMessage( _( "Processor doesn't supports SSE, can't run recompiler without that" ) );
return -1;
}
if ( !( cpucaps.hasStreamingSIMD2Extensions ) )
{
SysMessage( _( "Processor doesn't supports SSE2, can't run recompiler without that" ) );
return -1;
}
x86FpuState = FPU_STATE;
SuperVUInit(-1);
//SysMessage("recInit: Config.sseMXCSR = %x; Config.sseVUMXCSR = %x \n", Config.sseMXCSR, Config.sseVUMXCSR);
SetCPUState(Config.sseMXCSR, Config.sseVUMXCSR);
return 0;
}
////////////////////////////////////////////////////

View File

@ -180,7 +180,8 @@ struct CAPABILITIES {
u32 hasIntel64BitArchitecture;
u32 hasStreamingSIMD3Extensions;
u32 hasStreamingSIMD4Extensions;
//that is only for AMDs
// AMD-specific CPU Features
u32 hasMultimediaExtensionsExt;
u32 hasAMD64BitArchitecture;
u32 has3DNOWInstructionExtensionsExt;
@ -198,9 +199,12 @@ struct CPUINFO{
u32 x86Flags; // Feature Flags
u32 x86Flags2; // More Feature Flags
u32 x86EFlags; // Extended Feature Flags
//all the above returns hex values
u32 PhysicalCores;
u32 LogicalCores;
s8 x86ID[16]; // Vendor ID //the vendor creator (in %s)
char x86Type[20]; //cpu type in char format //the cpu type (in %s)
char x86Type[20]; //cpu type in char format //the cpu type (in %s)
s8 x86Fam[50]; // family in char format //the original cpu name string (in %s)
u32 cpuspeed; // speed of cpu //this will give cpu speed (in %d)
};

View File

@ -24,6 +24,7 @@
#include "ix86.h"
#include "Misc.h"
#include "Threading.h"
#if defined (_MSC_VER) && _MSC_VER >= 1400
@ -96,9 +97,10 @@ extern s32 iCpuId( u32 cmd, u32 *regs )
#endif // __x86_64__
return 0;
#else
// GCC Assembly Code -->
#ifndef __x86_64__
// see if we can use cpuid
__asm__ __volatile__ (
@ -136,7 +138,7 @@ u64 GetCPUTick( void )
return __rdtsc();
#elif defined(__MSCW32__) && !defined(__x86_64__)
#elif defined(_WIN32) && !defined(__x86_64__)
__asm rdtsc;
@ -149,6 +151,31 @@ u64 GetCPUTick( void )
#endif
}
// Note: This function doesn't support GCC/Linux. Looking online it seems the only
// way to simulate the Micrsoft SEH model is to use unix signals, and the 'sigaction'
// function specifically. Maybe a project for a linux developer at a later date. :)
void cpudetectSSE3(void* pfnCallSSE3)
{
cpucaps.hasStreamingSIMD3Extensions = 1;
#ifdef _MSC_VER
__try {
((void (*)())pfnCallSSE3)();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
cpucaps.hasStreamingSIMD3Extensions = 0;
}
#else // linux
#ifdef PCSX2_FORCESSE3
cpucaps.hasStreamingSIMD3Extensions = 1;
#else
// exception handling doesn't work, so disable for x86 builds of linux
cpucaps.hasStreamingSIMD3Extensions = 0;
#endif
#endif
}
#if defined __LINUX__
#include <sys/time.h>
@ -201,11 +228,13 @@ s64 CPUSpeedHz( unsigned int time )
}
////////////////////////////////////////////////////
int arr[] = {0x65746e49, 0x2952286c, 0x726f4320, 0x4d542865,
0x51203229,0x20646175,0x20555043,0x20202020 ,
0x20202020,0x20402020,0x36362e32,0x7a4847};
int arr[] = {
0x65746e49, 0x2952286c, 0x726f4320, 0x4d542865,
0x51203229,0x20646175,0x20555043,0x20202020 ,
0x20202020,0x20402020,0x36362e32,0x7a4847
};
void cpudetectInit( void )
void cpudetectInit()
{
u32 regs[ 4 ];
u32 cmds;
@ -234,6 +263,8 @@ void cpudetectInit( void )
// Hack - prevents reg[2] & reg[3] from being optimized out of existance!
num = sprintf(str, "\tx86Flags = %8.8x %8.8x\n", regs[3], regs[2]);
u32 LogicalCoresPerPhysicalCore = 0;
if ( cmds >= 0x00000001 )
{
if ( iCpuId( 0x00000001, regs ) != -1 )
@ -242,11 +273,13 @@ void cpudetectInit( void )
cpuinfo.x86Model = (regs[ 0 ] >> 4) & 0xf;
cpuinfo.x86Family = (regs[ 0 ] >> 8) & 0xf;
cpuinfo.x86PType = (regs[ 0 ] >> 12) & 0x3;
LogicalCoresPerPhysicalCore = ( regs[1] >> 16 ) & 0xff;
x86_64_8BITBRANDID = regs[1] & 0xff;
cpuinfo.x86Flags = regs[ 3 ];
cpuinfo.x86Flags2 = regs[ 2 ];
}
}
if ( iCpuId( 0x80000000, regs ) != -1 )
{
cmds = regs[ 0 ];
@ -261,67 +294,99 @@ void cpudetectInit( void )
}
}
switch(cpuinfo.x86PType)
{
case 0:
strcpy( cpuinfo.x86Type, "Standard OEM");
break;
case 1:
strcpy( cpuinfo.x86Type, "Overdrive");
break;
case 2:
strcpy( cpuinfo.x86Type, "Dual");
break;
case 3:
strcpy( cpuinfo.x86Type, "Reserved");
break;
default:
strcpy( cpuinfo.x86Type, "Unknown");
break;
}
if ( cpuinfo.x86ID[ 0 ] == 'G' ){ cputype=0;}//trick lines but if you know a way better ;p
if ( cpuinfo.x86ID[ 0 ] == 'A' ){ cputype=1;}
switch(cpuinfo.x86PType)
{
case 0:
strcpy( cpuinfo.x86Type, "Standard OEM");
break;
case 1:
strcpy( cpuinfo.x86Type, "Overdrive");
break;
case 2:
strcpy( cpuinfo.x86Type, "Dual");
break;
case 3:
strcpy( cpuinfo.x86Type, "Reserved");
break;
default:
strcpy( cpuinfo.x86Type, "Unknown");
break;
}
if ( cpuinfo.x86ID[ 0 ] == 'G' ){ cputype=0;}//trick lines but if you know a way better ;p
if ( cpuinfo.x86ID[ 0 ] == 'A' ){ cputype=1;}
memset(cpuinfo.x86Fam, 0, sizeof(cpuinfo.x86Fam));
iCpuId( 0x80000002, (u32*)cpuinfo.x86Fam);
iCpuId( 0x80000003, (u32*)(cpuinfo.x86Fam+16));
iCpuId( 0x80000004, (u32*)(cpuinfo.x86Fam+32));
memset(cpuinfo.x86Fam, 0, sizeof(cpuinfo.x86Fam));
iCpuId( 0x80000002, (u32*)cpuinfo.x86Fam);
iCpuId( 0x80000003, (u32*)(cpuinfo.x86Fam+16));
iCpuId( 0x80000004, (u32*)(cpuinfo.x86Fam+32));
//capabilities
cpucaps.hasFloatingPointUnit = ( cpuinfo.x86Flags >> 0 ) & 1;
cpucaps.hasVirtual8086ModeEnhancements = ( cpuinfo.x86Flags >> 1 ) & 1;
cpucaps.hasDebuggingExtensions = ( cpuinfo.x86Flags >> 2 ) & 1;
cpucaps.hasPageSizeExtensions = ( cpuinfo.x86Flags >> 3 ) & 1;
cpucaps.hasTimeStampCounter = ( cpuinfo.x86Flags >> 4 ) & 1;
cpucaps.hasModelSpecificRegisters = ( cpuinfo.x86Flags >> 5 ) & 1;
cpucaps.hasPhysicalAddressExtension = ( cpuinfo.x86Flags >> 6 ) & 1;
cpucaps.hasMachineCheckArchitecture = ( cpuinfo.x86Flags >> 7 ) & 1;
cpucaps.hasCOMPXCHG8BInstruction = ( cpuinfo.x86Flags >> 8 ) & 1;
cpucaps.hasAdvancedProgrammableInterruptController = ( cpuinfo.x86Flags >> 9 ) & 1;
cpucaps.hasSEPFastSystemCall = ( cpuinfo.x86Flags >> 11 ) & 1;
cpucaps.hasMemoryTypeRangeRegisters = ( cpuinfo.x86Flags >> 12 ) & 1;
cpucaps.hasPTEGlobalFlag = ( cpuinfo.x86Flags >> 13 ) & 1;
cpucaps.hasMachineCheckArchitecture = ( cpuinfo.x86Flags >> 14 ) & 1;
cpucaps.hasConditionalMoveAndCompareInstructions = ( cpuinfo.x86Flags >> 15 ) & 1;
cpucaps.hasFGPageAttributeTable = ( cpuinfo.x86Flags >> 16 ) & 1;
cpucaps.has36bitPageSizeExtension = ( cpuinfo.x86Flags >> 17 ) & 1;
cpucaps.hasProcessorSerialNumber = ( cpuinfo.x86Flags >> 18 ) & 1;
cpucaps.hasCFLUSHInstruction = ( cpuinfo.x86Flags >> 19 ) & 1;
cpucaps.hasDebugStore = ( cpuinfo.x86Flags >> 21 ) & 1;
cpucaps.hasACPIThermalMonitorAndClockControl = ( cpuinfo.x86Flags >> 22 ) & 1;
cpucaps.hasMultimediaExtensions = ( cpuinfo.x86Flags >> 23 ) & 1; //mmx
cpucaps.hasFastStreamingSIMDExtensionsSaveRestore = ( cpuinfo.x86Flags >> 24 ) & 1;
cpucaps.hasStreamingSIMDExtensions = ( cpuinfo.x86Flags >> 25 ) & 1; //sse
cpucaps.hasStreamingSIMD2Extensions = ( cpuinfo.x86Flags >> 26 ) & 1; //sse2
cpucaps.hasStreamingSIMD4Extensions = ( cpuinfo.x86Flags2 >> 19 ) & 1; //sse4.1
cpucaps.hasSelfSnoop = ( cpuinfo.x86Flags >> 27 ) & 1;
cpucaps.hasHyperThreading = ( cpuinfo.x86Flags >> 28 ) & 1;
cpucaps.hasThermalMonitor = ( cpuinfo.x86Flags >> 29 ) & 1;
cpucaps.hasIntel64BitArchitecture = ( cpuinfo.x86Flags >> 30 ) & 1;
//that is only for AMDs
cpucaps.hasMultimediaExtensionsExt = ( cpuinfo.x86EFlags >> 22 ) & 1; //mmx2
cpucaps.hasAMD64BitArchitecture = ( cpuinfo.x86EFlags >> 29 ) & 1; //64bit cpu
cpucaps.has3DNOWInstructionExtensionsExt = ( cpuinfo.x86EFlags >> 30 ) & 1; //3dnow+
cpucaps.has3DNOWInstructionExtensions = ( cpuinfo.x86EFlags >> 31 ) & 1; //3dnow
cpuinfo.cpuspeed = (u32 )(CPUSpeedHz( 1000 ) / 1000000);
//capabilities
cpucaps.hasFloatingPointUnit = ( cpuinfo.x86Flags >> 0 ) & 1;
cpucaps.hasVirtual8086ModeEnhancements = ( cpuinfo.x86Flags >> 1 ) & 1;
cpucaps.hasDebuggingExtensions = ( cpuinfo.x86Flags >> 2 ) & 1;
cpucaps.hasPageSizeExtensions = ( cpuinfo.x86Flags >> 3 ) & 1;
cpucaps.hasTimeStampCounter = ( cpuinfo.x86Flags >> 4 ) & 1;
cpucaps.hasModelSpecificRegisters = ( cpuinfo.x86Flags >> 5 ) & 1;
cpucaps.hasPhysicalAddressExtension = ( cpuinfo.x86Flags >> 6 ) & 1;
cpucaps.hasMachineCheckArchitecture = ( cpuinfo.x86Flags >> 7 ) & 1;
cpucaps.hasCOMPXCHG8BInstruction = ( cpuinfo.x86Flags >> 8 ) & 1;
cpucaps.hasAdvancedProgrammableInterruptController = ( cpuinfo.x86Flags >> 9 ) & 1;
cpucaps.hasSEPFastSystemCall = ( cpuinfo.x86Flags >> 11 ) & 1;
cpucaps.hasMemoryTypeRangeRegisters = ( cpuinfo.x86Flags >> 12 ) & 1;
cpucaps.hasPTEGlobalFlag = ( cpuinfo.x86Flags >> 13 ) & 1;
cpucaps.hasMachineCheckArchitecture = ( cpuinfo.x86Flags >> 14 ) & 1;
cpucaps.hasConditionalMoveAndCompareInstructions = ( cpuinfo.x86Flags >> 15 ) & 1;
cpucaps.hasFGPageAttributeTable = ( cpuinfo.x86Flags >> 16 ) & 1;
cpucaps.has36bitPageSizeExtension = ( cpuinfo.x86Flags >> 17 ) & 1;
cpucaps.hasProcessorSerialNumber = ( cpuinfo.x86Flags >> 18 ) & 1;
cpucaps.hasCFLUSHInstruction = ( cpuinfo.x86Flags >> 19 ) & 1;
cpucaps.hasDebugStore = ( cpuinfo.x86Flags >> 21 ) & 1;
cpucaps.hasACPIThermalMonitorAndClockControl = ( cpuinfo.x86Flags >> 22 ) & 1;
cpucaps.hasMultimediaExtensions = ( cpuinfo.x86Flags >> 23 ) & 1; //mmx
cpucaps.hasFastStreamingSIMDExtensionsSaveRestore = ( cpuinfo.x86Flags >> 24 ) & 1;
cpucaps.hasStreamingSIMDExtensions = ( cpuinfo.x86Flags >> 25 ) & 1; //sse
cpucaps.hasStreamingSIMD2Extensions = ( cpuinfo.x86Flags >> 26 ) & 1; //sse2
cpucaps.hasSelfSnoop = ( cpuinfo.x86Flags >> 27 ) & 1;
cpucaps.hasHyperThreading = ( cpuinfo.x86Flags >> 28 ) & 1;
cpucaps.hasThermalMonitor = ( cpuinfo.x86Flags >> 29 ) & 1;
cpucaps.hasIntel64BitArchitecture = ( cpuinfo.x86Flags >> 30 ) & 1;
//that is only for AMDs
cpucaps.hasMultimediaExtensionsExt = ( cpuinfo.x86EFlags >> 22 ) & 1; //mmx2
cpucaps.hasAMD64BitArchitecture = ( cpuinfo.x86EFlags >> 29 ) & 1; //64bit cpu
cpucaps.has3DNOWInstructionExtensionsExt = ( cpuinfo.x86EFlags >> 30 ) & 1; //3dnow+
cpucaps.has3DNOWInstructionExtensions = ( cpuinfo.x86EFlags >> 31 ) & 1; //3dnow
cpuinfo.cpuspeed = (u32)(CPUSpeedHz( 1000 ) / 1000000);
// --> SSE 4.1 detection <--
// We don't care about the small subset of CPUs using SSE4 (which is also hard to
// detect, in addition to being of limited use due to the abbreviated instruction set).
// So we'll just leave it at SSE 4.1. SSE4 cpu detection is ignored.
cpucaps.hasStreamingSIMD4Extensions = ( cpuinfo.x86Flags2 >> 19 ) & 1; //sse4.1
// --> SSE3 detection <--
// These instructions may not be recognized by some compilers, or may not have
// intrinsic equivalents available. So we use our own ix86 emitter to generate
// some code and run it that way. :)
u8* recSSE = (u8*)SysMmap( NULL, 0x1000 );
if( recSSE != NULL )
{
x86SetPtr(recSSE);
SSE3_MOVSLDUP_XMM_to_XMM(XMM0, XMM0);
RET();
cpudetectSSE3(recSSE);
SysMunmap( recSSE, 0x1000 );
}
// --> Core Counting <--
// Hopefully this "best guess" method won't break on AMD cpus!
if( !cpucaps.hasHyperThreading || LogicalCoresPerPhysicalCore == 0 )
LogicalCoresPerPhysicalCore = 1;
// This will assign values into cpuinfo.LogicalCores and PhysicalCores
Threading::CountLogicalCores( LogicalCoresPerPhysicalCore );
}

View File

@ -12,20 +12,16 @@ Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug vm|Win32 = Debug vm|Win32
Debug vtlb|Win32 = Debug vtlb|Win32
Debug|Win32 = Debug|Win32
Release vm (dev)|Win32 = Release vm (dev)|Win32
Release vm (nondev) [public]|Win32 = Release vm (nondev) [public]|Win32
Release vtlb (dev)|Win32 = Release vtlb (dev)|Win32
Release vtlb (nondev) [public]|Win32 = Release vtlb (nondev) [public]|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vm|Win32.ActiveCfg = Debug vm|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vm|Win32.Build.0 = Debug vm|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vtlb|Win32.ActiveCfg = Debug vtlb|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vtlb|Win32.Build.0 = Debug vtlb|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug|Win32.ActiveCfg = Debug vtlb|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug|Win32.Build.0 = Debug vtlb|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vm (dev)|Win32.ActiveCfg = Release vm (dev)|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vm (dev)|Win32.Build.0 = Release vm (dev)|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vm (nondev) [public]|Win32.ActiveCfg = Release vm (nondev)|Win32
@ -34,14 +30,10 @@ Global
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb (dev)|Win32.Build.0 = Release vtlb (dev)|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb (nondev) [public]|Win32.ActiveCfg = Release vtlb (nondev)|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb (nondev) [public]|Win32.Build.0 = Release vtlb (nondev)|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release|Win32.ActiveCfg = Release vtlb (nondev)|Win32
{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release|Win32.Build.0 = Release vtlb (nondev)|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Debug vm|Win32.ActiveCfg = Debug|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Debug vm|Win32.Build.0 = Debug|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Debug vtlb|Win32.ActiveCfg = Debug|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Debug vtlb|Win32.Build.0 = Debug|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Debug|Win32.ActiveCfg = Debug|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Debug|Win32.Build.0 = Debug|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release vm (dev)|Win32.ActiveCfg = Release|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release vm (dev)|Win32.Build.0 = Release|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release vm (nondev) [public]|Win32.ActiveCfg = Release|Win32
@ -50,8 +42,6 @@ Global
{26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb (dev)|Win32.Build.0 = Release|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb (nondev) [public]|Win32.ActiveCfg = Release|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb (nondev) [public]|Win32.Build.0 = Release|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release|Win32.ActiveCfg = Release|Win32
{26511268-2902-4997-8421-ECD7055F9E28}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE