mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
ff0568b105
commit
70583d47cf
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1262
pcsx2/GS.cpp
1262
pcsx2/GS.cpp
File diff suppressed because it is too large
Load Diff
217
pcsx2/GS.h
217
pcsx2/GS.h
|
@ -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
|
||||
|
|
|
@ -1017,9 +1017,8 @@ void MTSAH() {
|
|||
|
||||
///////////////////////////////////////////
|
||||
|
||||
int intInit() {
|
||||
void intInit() {
|
||||
//detect cpu for use the optimaze asm code
|
||||
return 0;
|
||||
}
|
||||
|
||||
void intReset() {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
943
pcsx2/MTGS.cpp
943
pcsx2/MTGS.cpp
|
@ -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 );
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
69
pcsx2/Misc.h
69
pcsx2/Misc.h
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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++ )
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) )
|
||||
|
|
|
@ -101,6 +101,9 @@ protected:
|
|||
void psxRcntFreeze();
|
||||
void sio2Freeze();
|
||||
|
||||
// called by gsFreeze automatically.
|
||||
void mtgsFreeze();
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -19,7 +19,7 @@
|
|||
#ifndef __VU1OPS_H__
|
||||
#define __VU1OPS_H__
|
||||
|
||||
#ifdef __MSCW32__
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244)
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=""$(SolutionDir)\bin\pcsx2d-vm.exe""
|
||||
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=""$(SolutionDir)\bin\pcsx2d-vtlb.exe""
|
||||
LinkIncremental="2"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
|
@ -289,7 +294,7 @@
|
|||
EnableFiberSafeOptimizations="true"
|
||||
WholeProgramOptimization="true"
|
||||
AdditionalIncludeDirectories="./;../../;../../IPU;../../ZLIB;../../DebugTools;../../x86;"../../x86/ix86-32";../libs"
|
||||
PreprocessorDefinitions="NDEBUG,WIN32,_WINDOWS,__MSCW32__,__WIN32__,__i386__,ENABLE_NLS,PACKAGE=\"pcsx2\";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"
|
||||
>
|
||||
|
|
|
@ -115,7 +115,7 @@
|
|||
FavorSizeOrSpeed="1"
|
||||
EnableFiberSafeOptimizations="true"
|
||||
AdditionalIncludeDirectories=""$(InputDir)""
|
||||
PreprocessorDefinitions="PTW32_STATIC_LIB;WIN32;NDEBUG;_LIB"
|
||||
PreprocessorDefinitions="PTW32_STATIC_LIB;PTW32_BUILD_INLINED;WIN32;NDEBUG;_LIB"
|
||||
StringPooling="true"
|
||||
MinimalRebuild="true"
|
||||
ExceptionHandling="0"
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="./;../../;../../IPU;../../ZLIB;../../DebugTools;../../x86;"../../x86/ix86-32";../libs"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;__MSCW32__;__WIN32__;__i386__;ENABLE_NLS;PACKAGE=\"pcsx2\";_CRT_SECURE_NO_DEPRECATE;TIXML_USE_STL"
|
||||
AdditionalIncludeDirectories="./;../../;../../IPU;../../ZLIB;../../DebugTools;../../x86;"../../x86/ix86-32";../libs;../pthreads"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;__i386__;PTW32_STATIC_LIB;ENABLE_NLS;PACKAGE=\"pcsx2\";_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"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
@ -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 */
|
||||
|
|
@ -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 */
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue