Re-did the VUmicro's "Cpu Provider" structs into classes, and split sVU and mVU into independent classes.

* Better error handling for when sVU fails allocating memory at a specific location (should fail less).
 * Partial support for detecting and handling non-SSE2 machines with some grace.
 * Improved sVU's allocation chances with a second try at another randomish address (and removed alloc fail spam from the console -- only logs on complete fail now).

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2321 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-12-07 14:00:39 +00:00
parent bb309c8d65
commit f52faead0c
26 changed files with 659 additions and 367 deletions

View File

@ -253,9 +253,18 @@ namespace Exception
// ---------------------------------------------------------------------------------------
// Hardware/OS Exceptions:
// HardwareDeficiency
// HardwareDeficiency / VirtualMemoryMapConflict
// ---------------------------------------------------------------------------------------
// This exception is a specific type of OutOfMemory error that isn't "really" an out of
// memory error. More likely it's caused by a plugin or driver reserving a range of memory
// we'd really like to have access to.
class VirtualMemoryMapConflict : public virtual OutOfMemory
{
public:
DEFINE_RUNTIME_EXCEPTION( VirtualMemoryMapConflict, wxLt("Virtual memory map confict: Unable to claim specific required memory regions.") )
};
class HardwareDeficiency : public virtual RuntimeError
{
public:

View File

@ -194,6 +194,7 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
if( isBlocking )
{
// FIXME: Add deadlock detection and handling here... ?
m_lock_InThread.Wait();
Detach();
}

View File

@ -564,10 +564,8 @@ TraceLogFilters& SetTraceConfig();
#define CHECK_MACROVU0 // If defined uses mVU for VU Macro (COP2), else uses sVU
#define CHECK_MICROVU0 (EmuConfig.Cpu.Recompiler.UseMicroVU0)
#define CHECK_MICROVU1 (EmuConfig.Cpu.Recompiler.UseMicroVU1)
#define CHECK_EEREC (EmuConfig.Cpu.Recompiler.EnableEE && GetSysCoreAlloc().RecSuccess_EE)
#define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().RecSuccess_IOP)
#define CHECK_VU0REC (EmuConfig.Cpu.Recompiler.EnableVU0 && GetSysCoreAlloc().RecSuccess_VU0)
#define CHECK_VU1REC (EmuConfig.Cpu.Recompiler.EnableVU1 && GetSysCoreAlloc().RecSuccess_VU1)
#define CHECK_EEREC (EmuConfig.Cpu.Recompiler.EnableEE && GetSysCoreAlloc().IsRecAvailable_EE())
#define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().IsRecAvailable_IOP())
//------------ SPECIAL GAME FIXES!!! ---------------
#define NUM_OF_GAME_FIXES 7

View File

@ -349,8 +349,8 @@ static __forceinline void VSyncStart(u32 sCycle)
// by UI implementations. (ie, AppCoreThread in PCSX2-wx interface).
vSyncDebugStuff( iFrame );
if (CHECK_MICROVU0) vsyncVUrec(0);
if (CHECK_MICROVU1) vsyncVUrec(1);
CpuVU0->Vsync();
CpuVU1->Vsync();
if ((CSRw & 0x8))
{

View File

@ -31,7 +31,7 @@ typedef char* (*TdisR5900F)DisFInterface;
// These macros are used to assemble the disassembler functions
#define MakeDisF(fn, b) \
char* fn DisFInterface { \
if( !!CpuVU1.IsInterpreter ) sprintf (ostr, "%8.8x %8.8x:", pc, code); \
if( !!CpuVU1->IsInterpreter ) sprintf (ostr, "%8.8x %8.8x:", pc, code); \
else ostr[0] = 0; \
b; /*ostr[(strlen(ostr) - 1)] = 0;*/ return ostr; \
}
@ -52,47 +52,47 @@ typedef char* (*TdisR5900F)DisFInterface;
}
#define dCP2128f(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
else sprintf(ostr, "%s w=%f (%8.8x) z=%f (%8.8x) y=%f (%8.8xl) x=%f (%8.8x) (%s),", ostr, VU1.VF[i].f.w, VU1.VF[i].UL[3], VU1.VF[i].f.z, VU1.VF[i].UL[2], VU1.VF[i].f.y, VU1.VF[i].UL[1], VU1.VF[i].f.x, VU1.VF[i].UL[0], disRNameCP2f[i]); \
} \
#define dCP232x(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
else sprintf(ostr, "%s x=%f (%s),", ostr, VU1.VF[i].f.x, disRNameCP2f[i]); \
} \
#define dCP232y(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
else sprintf(ostr, "%s y=%f (%s),", ostr, VU1.VF[i].f.y, disRNameCP2f[i]); \
} \
#define dCP232z(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
else sprintf(ostr, "%s z=%f (%s),", ostr, VU1.VF[i].f.z, disRNameCP2f[i]); \
}
#define dCP232w(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \
else sprintf(ostr, "%s w=%f (%s),", ostr, VU1.VF[i].f.w, disRNameCP2f[i]); \
}
#define dCP2ACCf() { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s ACC,", ostr); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s ACC,", ostr); \
else sprintf(ostr, "%s w=%f z=%f y=%f x=%f (ACC),", ostr, VU1.ACC.f.w, VU1.ACC.f.z, VU1.ACC.f.y, VU1.ACC.f.x); \
} \
#define dCP232i(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2i[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2i[i]); \
else sprintf(ostr, "%s %8.8x (%s),", ostr, VU1.VI[i].UL, disRNameCP2i[i]); \
}
#define dCP232iF(i) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2i[i]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s,", ostr, disRNameCP2i[i]); \
else sprintf(ostr, "%s %f (%s),", ostr, VU1.VI[i].F, disRNameCP2i[i]); \
}
#define dCP232f(i, j) { \
if( !CpuVU1.IsInterpreter ) sprintf(ostr, "%s %s%s,", ostr, disRNameCP2f[i], CP2VFnames[j]); \
if( !CpuVU1->IsInterpreter ) sprintf(ostr, "%s %s%s,", ostr, disRNameCP2f[i], CP2VFnames[j]); \
else sprintf(ostr, "%s %s=%f (%s),", ostr, CP2VFnames[j], VU1.VF[i].F[j], disRNameCP2f[i]); \
}

View File

@ -122,8 +122,8 @@ vtlbHandler iopHw_by_page_08;
// (the VU memory operations are different for recs vs. interpreters)
void memMapVUmicro()
{
vtlb_MapHandler(vu0_micro_mem[CHECK_VU0REC ? 0 : 1],0x11000000,0x00004000);
vtlb_MapHandler(vu1_micro_mem[CHECK_VU1REC ? 0 : 1],0x11008000,0x00004000);
vtlb_MapHandler(vu0_micro_mem[CpuVU0->IsInterpreter],0x11000000,0x00004000);
vtlb_MapHandler(vu1_micro_mem[CpuVU1->IsInterpreter],0x11008000,0x00004000);
// VU0/VU1 memory
// (Like IOP memory, these are generally only used by the EE Bios kernel during
@ -414,16 +414,16 @@ static __forceinline void ClearVuFunc( u32 addr, u32 size )
if( dynarec )
{
if( vunum==0 )
VU0micro::recClear(addr,size);
CpuVU0->Clear(addr,size);
else
VU1micro::recClear(addr,size);
CpuVU1->Clear(addr,size);
}
else
{
if( vunum==0 )
VU0micro::intClear(addr,size);
CpuVU0->Clear(addr,size);
else
VU1micro::intClear(addr,size);
CpuVU1->Clear(addr,size);
}
}

View File

@ -432,7 +432,7 @@ __forceinline void _cpuBranchTest_Shared()
{
// We're in a BranchTest. All dynarec registers are flushed
// so there is no need to freeze registers here.
CpuVU0.ExecuteBlock();
CpuVU0->ExecuteBlock();
// This might be needed to keep the EE and VU0 in sync.
// A better fix will require hefty changes to the VU recs. -_-

View File

@ -37,12 +37,12 @@ static void TestClearVUs(u32 madr, u32 size)
if (madr < 0x11004000)
{
DbgCon.Warning("scratch pad clearing vu0");
CpuVU0.Clear(madr&0xfff, size);
CpuVU0->Clear(madr&0xfff, size);
}
else if (madr >= 0x11008000 && madr < 0x1100c000)
{
DbgCon.Warning("scratch pad clearing vu1");
CpuVU1.Clear(madr&0x3fff, size);
CpuVU1->Clear(madr&0x3fff, size);
}
}
}

View File

@ -114,6 +114,79 @@ void SysDetect()
Console.Newline();
}
template< typename CpuType >
class CpuInitializer
{
public:
ScopedPtr<CpuType> MyCpu;
CpuInitializer();
virtual ~CpuInitializer() throw();
bool IsAvailable() const
{
return !!MyCpu;
}
CpuType* GetPtr() { return MyCpu.GetPtr(); }
const CpuType* GetPtr() const { return MyCpu.GetPtr(); }
operator CpuType*() { return GetPtr(); }
operator const CpuType*() const { return GetPtr(); }
};
// --------------------------------------------------------------------------------------
// CpuInitializer Template
// --------------------------------------------------------------------------------------
// Helper for initializing various PCSX2 CPU providers, and handing errors and cleanup.
//
template< typename CpuType >
CpuInitializer< CpuType >::CpuInitializer()
{
try {
MyCpu = new CpuType();
MyCpu->Allocate();
}
catch( Exception::RuntimeError& ex )
{
Console.Error( L"MicroVU0 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
MyCpu->Shutdown();
}
catch( std::runtime_error& ex )
{
Console.Error( L"MicroVU0 Recompiler Allocation Failed (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) );
MyCpu->Shutdown();
}
}
template< typename CpuType >
CpuInitializer< CpuType >::~CpuInitializer() throw()
{
if( MyCpu )
MyCpu->Shutdown();
}
class CpuInitializerSet
{
public:
// Note: Allocate sVU first -- it's the most picky.
CpuInitializer<recSuperVU0> superVU0;
CpuInitializer<recSuperVU1> superVU1;
CpuInitializer<recMicroVU0> microVU0;
CpuInitializer<recMicroVU1> microVU1;
CpuInitializer<InterpVU0> interpVU0;
CpuInitializer<InterpVU1> interpVU1;
public:
CpuInitializerSet() {}
virtual ~CpuInitializerSet() throw() {}
};
// returns the translated error message for the Virtual Machine failing to allocate!
static wxString GetMemoryErrorVM()
{
@ -129,10 +202,8 @@ SysCoreAllocations::SysCoreAllocations()
Console.WriteLn( "Initializing PS2 virtual machine..." );
RecSuccess_EE = false;
RecSuccess_IOP = false;
RecSuccess_VU0 = false;
RecSuccess_VU1 = false;
m_RecSuccessEE = false;
m_RecSuccessIOP = false;
try
{
@ -153,13 +224,12 @@ SysCoreAllocations::SysCoreAllocations()
{
CleanupMess();
// re-throw std::bad_alloc as something more friendly.
// re-throw std::bad_alloc as something more friendly. This is needed since
// much of the code uses new/delete internally, which throw std::bad_alloc on fail.
throw Exception::OutOfMemory(
wxsFormat( // Diagnostic (english)
L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
L"Error Details: " + fromUTF8( ex.what() )
),
L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
L"Error Details: " + fromUTF8( ex.what() ),
GetMemoryErrorVM() // translated
);
@ -167,9 +237,11 @@ SysCoreAllocations::SysCoreAllocations()
Console.WriteLn( "Allocating memory for recompilers..." );
CpuProviders = new CpuInitializerSet();
try {
recCpu.Allocate();
RecSuccess_EE = true;
m_RecSuccessEE = true;
}
catch( Exception::RuntimeError& ex )
{
@ -179,7 +251,7 @@ SysCoreAllocations::SysCoreAllocations()
try {
psxRec.Allocate();
RecSuccess_IOP = true;
m_RecSuccessIOP = true;
}
catch( Exception::RuntimeError& ex )
{
@ -189,42 +261,26 @@ SysCoreAllocations::SysCoreAllocations()
// hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :(
try {
VU0micro::recAlloc();
RecSuccess_VU0 = true;
}
catch( Exception::RuntimeError& ex )
{
Console.Error( L"VU0 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
VU0micro::recShutdown();
}
try {
VU1micro::recAlloc();
RecSuccess_VU1 = true;
}
catch( Exception::RuntimeError& ex )
{
Console.Error( L"VU1 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() );
VU1micro::recShutdown();
}
// If both VUrecs failed, then make sure the SuperVU is totally closed out, because it
// actually initializes everything once and then shares it between both VU recs.
if( !RecSuccess_VU0 && !RecSuccess_VU1 )
if( !IsRecAvailable_SuperVU0() && !IsRecAvailable_SuperVU1() )
SuperVUDestroy( -1 );
}
bool SysCoreAllocations::IsRecAvailable_MicroVU0() const { return CpuProviders->microVU0.IsAvailable(); }
bool SysCoreAllocations::IsRecAvailable_MicroVU1() const { return CpuProviders->microVU1.IsAvailable(); }
bool SysCoreAllocations::IsRecAvailable_SuperVU0() const { return CpuProviders->superVU0.IsAvailable(); }
bool SysCoreAllocations::IsRecAvailable_SuperVU1() const { return CpuProviders->superVU1.IsAvailable(); }
void SysCoreAllocations::CleanupMess() throw()
{
try
{
// Special SuperVU "complete" terminator.
// Special SuperVU "complete" terminator (stupid hacky recompiler)
SuperVUDestroy( -1 );
VU1micro::recShutdown();
VU0micro::recShutdown();
psxRec.Shutdown();
recCpu.Shutdown();
@ -243,12 +299,34 @@ SysCoreAllocations::~SysCoreAllocations() throw()
bool SysCoreAllocations::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const
{
return (recOpts.EnableEE && !RecSuccess_EE) ||
(recOpts.EnableIOP && !RecSuccess_IOP) ||
(recOpts.EnableVU0 && !RecSuccess_VU0) ||
(recOpts.EnableVU1 && !RecSuccess_VU1);
return (recOpts.EnableEE && !IsRecAvailable_EE()) ||
(recOpts.EnableIOP && !IsRecAvailable_IOP()) ||
(recOpts.EnableVU0 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU0()) ||
(recOpts.EnableVU1 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU1()) ||
(recOpts.EnableVU0 && !recOpts.UseMicroVU0 && !IsRecAvailable_SuperVU0()) ||
(recOpts.EnableVU1 && !recOpts.UseMicroVU1 && !IsRecAvailable_SuperVU1());
}
BaseVUmicroCPU* CpuVU0 = NULL;
BaseVUmicroCPU* CpuVU1 = NULL;
void SysCoreAllocations::SelectCpuProviders() const
{
Cpu = CHECK_EEREC ? &recCpu : &intCpu;
psxCpu = CHECK_IOPREC ? &psxRec : &psxInt;
CpuVU0 = CpuProviders->interpVU0;
CpuVU1 = CpuProviders->interpVU1;
if( EmuConfig.Cpu.Recompiler.EnableVU0 )
CpuVU0 = EmuConfig.Cpu.Recompiler.UseMicroVU0 ? (BaseVUmicroCPU*)CpuProviders->microVU0 : (BaseVUmicroCPU*)CpuProviders->superVU0;
if( EmuConfig.Cpu.Recompiler.EnableVU1 )
CpuVU1 = EmuConfig.Cpu.Recompiler.UseMicroVU1 ? (BaseVUmicroCPU*)CpuProviders->microVU1 : (BaseVUmicroCPU*)CpuProviders->superVU1;
}
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
// This can be called at any time outside the context of a Cpu->Execute() block without
// bad things happening (recompilers will slow down for a brief moment since rec code blocks
@ -256,13 +334,17 @@ bool SysCoreAllocations::HadSomeFailures( const Pcsx2Config::RecompilerOptions&
// Use this method to reset the recs when important global pointers like the MTGS are re-assigned.
void SysClearExecutionCache()
{
Cpu = CHECK_EEREC ? &recCpu : &intCpu;
psxCpu = CHECK_IOPREC ? &psxRec : &psxInt;
GetSysCoreAlloc().SelectCpuProviders();
// SuperVUreset will do nothing is none of the recs are initialized.
// But it's needed if one or the other is initialized.
SuperVUReset(-1);
Cpu->Reset();
psxCpu->Reset();
vuMicroCpuReset();
CpuVU0->Reset();
CpuVU1->Reset();
}
// Maps a block of memory for use as a recompiled code buffer, and ensures that the
@ -275,17 +357,21 @@ u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) )
{
DevCon.Warning( "First try failed allocating %s at address 0x%x", caller, base );
if( base != NULL )
{
DbgCon.Warning( "First try failed allocating %s at address 0x%x", caller, base );
// memory allocation *must* have the top bit clear, so let's try again
// with NULL (let the OS pick something for us).
// memory allocation *must* have the top bit clear, so let's try again
// with NULL (let the OS pick something for us).
SafeSysMunmap( Mem, size );
SafeSysMunmap( Mem, size );
Mem = (u8*)HostSys::Mmap( NULL, size );
}
Mem = (u8*)HostSys::Mmap( NULL, size );
if( bounds != 0 && (((uptr)Mem + size) > bounds) )
{
DevCon.Warning( "Fatal Error:\n\tSecond try failed allocating %s, block ptr 0x%x does not meet required criteria.", caller, Mem );
DevCon.Warning( "Second try failed allocating %s, block ptr 0x%x does not meet required criteria.", caller, Mem );
SafeSysMunmap( Mem, size );
// returns NULL, caller should throw an exception.

View File

@ -24,31 +24,36 @@ static const int PCSX2_VersionMid = 9;
static const int PCSX2_VersionLo = 7;
class SysCoreThread;
class CpuInitializerSet;
// --------------------------------------------------------------------------------------
// SysCoreAllocations class
// --------------------------------------------------------------------------------------
class SysCoreAllocations
{
public:
// This set of booleans defaults to false and are only set TRUE if the corresponding
// recompilers succeeded to initialize/allocate. The host application should honor
// these booleans when selecting between recompiler or interpreter, since recompilers
// will fail to operate if these are "false."
bool RecSuccess_EE:1,
RecSuccess_IOP:1,
RecSuccess_VU0:1,
RecSuccess_VU1:1;
protected:
ScopedPtr<CpuInitializerSet> CpuProviders;
bool m_RecSuccessEE:1;
bool m_RecSuccessIOP:1;
public:
SysCoreAllocations();
virtual ~SysCoreAllocations() throw();
void SelectCpuProviders() const;
bool HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const;
bool IsRecAvailable_EE() const { return m_RecSuccessEE; }
bool IsRecAvailable_IOP() const { return m_RecSuccessIOP; }
bool IsRecAvailable_MicroVU0() const;
bool IsRecAvailable_MicroVU1() const;
bool IsRecAvailable_SuperVU0() const;
bool IsRecAvailable_SuperVU1() const;
protected:
void CleanupMess() throw();
};

View File

@ -73,7 +73,7 @@ __forceinline void _vu0run(bool breakOnMbit) {
Console.Warning("VU0 perma-stall, breaking execution...");
break; // mVU will never get here (it handles mBit internally)
}
CpuVU0.ExecuteBlock();
CpuVU0->ExecuteBlock();
} while ((VU0.VI[REG_VPU_STAT].UL & 1) // E-bit Termination
&& (!breakOnMbit || !(VU0.flags & VUFLAG_MFLAGSET))); // M-bit Break
@ -342,7 +342,7 @@ void vu0Finish()
int i = 0;
while(i++ < 32) {
CpuVU0.ExecuteBlock();
CpuVU0->ExecuteBlock();
if(!(VU0.VI[REG_VPU_STAT].UL & 0x1))
break;
}

View File

@ -56,7 +56,7 @@ void __fastcall vu0ExecMicro(u32 addr) {
// If an unsigned variable isn't -1? --arcum42
if (addr != (u32)-1) VU0.VI[REG_TPC].UL = addr;
_vuExecMicroDebug(VU0);
CpuVU0.ExecuteBlock();
CpuVU0->ExecuteBlock();
// If the VU0 program didn't finish then we'll want to finish it up
// pretty soon. This fixes vmhacks in some games (Naruto Ultimate Ninja 2)

View File

@ -190,56 +190,39 @@ void vu0Exec(VURegs* VU)
if (VU->VF[0].f.w != 1.0f) DbgCon.Error("VF[0].w != 1.0!!!!\n");
}
namespace VU0micro
// --------------------------------------------------------------------------------------
// VU0microInterpreter
// --------------------------------------------------------------------------------------
InterpVU0::InterpVU0()
{
void intAlloc()
{
}
void intShutdown()
{
}
void __fastcall intClear(u32 Addr, u32 Size)
{
}
static void intReset()
{
}
static void intStep()
{
vu0Exec( &VU0 );
}
static void intExecuteBlock()
{
int i;
for (i = 128; i--;) {
if ((VU0.VI[REG_VPU_STAT].UL & 0x1) == 0)
break;
vu0Exec(&VU0);
}
if( i < 0 && (VU0.branch || VU0.ebit) ) {
// execute one more
vu0Exec(&VU0);
}
}
IsInterpreter = true;
}
using namespace VU0micro;
const VUmicroCpu intVU0 =
void InterpVU0::Step()
{
intReset
, intStep
, intExecuteBlock
, intClear
vu0Exec( &VU0 );
}
void InterpVU0::ExecuteBlock()
{
for (int i = 128; i--;)
{
if ((VU0.VI[REG_VPU_STAT].UL & 0x1) == 0)
{
// Okey.... It apparenly runs an extra instruction on branches or ebits, but
// *only* if the microprogram is longer than 128 instructions. This has got
// to be some kind of random gamefix hack. --air
if( (i < 0) && (VU0.branch || VU0.ebit) )
{
// execute one more
vu0Exec(&VU0);
}
break;
}
vu0Exec(&VU0);
}
}
, true
};

View File

@ -47,7 +47,7 @@ void __fastcall vu1ExecMicro(u32 addr)
while(VU0.VI[REG_VPU_STAT].UL & 0x100)
{
VUM_LOG("vu1ExecMicro > Stalling until current microprogram finishes");
CpuVU1.ExecuteBlock();
CpuVU1->ExecuteBlock();
}
VUM_LOG("vu1ExecMicro %x", addr);
@ -59,7 +59,7 @@ void __fastcall vu1ExecMicro(u32 addr)
if (addr != -1) VU1.VI[REG_TPC].UL = addr;
_vuExecMicroDebug(VU1);
CpuVU1.ExecuteBlock();
CpuVU1->ExecuteBlock();
}
_vuRegsTables(VU1, VU1regs);

View File

@ -176,55 +176,35 @@ void vu1Exec(VURegs* VU)
if (VU->VF[0].f.w != 1.0f) DbgCon.Error("VF[0].w != 1.0!!!!\n");
}
namespace VU1micro
InterpVU1::InterpVU1()
{
void intAlloc()
IsInterpreter = true;
}
void InterpVU1::Step()
{
vu1Exec( &VU1 );
}
void InterpVU1::ExecuteBlock()
{
for (int i = 128; i--;)
{
}
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0)
{
// See VU0Interp's version for details. Or just read the comment below
// that simply reads "execute one more" and wonder: Why? Really... WHY?
// --air
void __fastcall intClear(u32 Addr, u32 Size)
{
}
void intShutdown()
{
}
static void intReset()
{
}
static void intStep()
{
vu1Exec( &VU1 );
}
static void intExecuteBlock()
{
int i;
for (i = 128; i--;) {
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0)
break;
vu1Exec(&VU1);
if( (i < 0) && (VU1.branch || VU1.ebit) )
{
// execute one more
vu1Exec(&VU1);
}
break;
}
if( i < 0 && (VU1.branch || VU1.ebit) ) {
// execute one more
vu1Exec(&VU1);
}
vu1Exec(&VU1);
}
}
using namespace VU1micro;
const VUmicroCpu intVU1 =
{
intReset
, intStep
, intExecuteBlock
, intClear
, true
};

View File

@ -17,49 +17,165 @@
#include "VU.h"
#include "VUops.h"
struct VUmicroCpu
// --------------------------------------------------------------------------------------
// BaseCpuProvider
// --------------------------------------------------------------------------------------
//
// Design Note: This class is only partial C++ style. It still relies on Alloc and Shutdown
// calls for memory and resource management. This is because the underlying implementations
// of our CPU emulators don't have properly encapsulated objects yet -- if we allocate ram
// in a constructor, it won't get free'd if an exception occurs during object construction.
// Once we've resolved all the 'dangling pointers' and stuff in the recompilers, Alloc
// and Shutdown can be removed in favor of constructor/destructor syntax.
//
class BaseCpuProvider
{
void (*Reset)();
void (*Step)();
void (*ExecuteBlock)(); // VUs should support block-level execution only.
void (__fastcall *Clear)(u32 Addr, u32 Size);
protected:
// allocation counter for multiple init/shutdown calls
// (most or all implementations will need this!)
int m_AllocCount;
public:
// this boolean indicates to some generic logging facilities if the VU's registers
// are valid for logging or not. (see DisVU1Micro.cpp, etc)
// are valid for logging or not. (see DisVU1Micro.cpp, etc) [kinda hacky, might
// be removed in the future]
bool IsInterpreter;
public:
BaseCpuProvider()
{
m_AllocCount = 0;
}
virtual ~BaseCpuProvider() throw()
{
if( m_AllocCount != 0 )
Console.Warning( "Cleanup miscount detected on CPU provider. Count=%d", m_AllocCount );
}
virtual const char* GetShortName() const=0;
virtual wxString GetLongName() const=0;
virtual void Allocate()=0;
virtual void Shutdown()=0;
virtual void Reset()=0;
virtual void Step()=0;
virtual void ExecuteBlock()=0;
virtual void Clear(u32 Addr, u32 Size)=0;
};
extern VUmicroCpu CpuVU0;
extern const VUmicroCpu intVU0;
extern const VUmicroCpu recVU0;
extern VUmicroCpu CpuVU1;
extern const VUmicroCpu intVU1;
extern const VUmicroCpu recVU1;
namespace VU0micro
// --------------------------------------------------------------------------------------
// BaseVUmicroCPU
// --------------------------------------------------------------------------------------
// Layer class for possible future implementation (currently is nothing more than a type-safe
// type define).
//
class BaseVUmicroCPU : public BaseCpuProvider
{
extern void recAlloc();
extern void recShutdown();
extern void __fastcall recClear(u32 Addr, u32 Size);
public:
// Note: Interpreter functions are dummies -- they don't actually do anything.
extern void intAlloc();
extern void intShutdown();
extern void __fastcall intClear(u32 Addr, u32 Size);
}
// Called by the PS2 VM's event manager for every internal vertical sync (occurs at either
// 50hx (pal) or 59.94hx (NTSC).
//
// Exceptions:
// This method is not allowed to throw exceptions, since exceptions may not propagate
// safely from the context of recompiled code stackframes.
//
// Thread Affinity:
// Called from the EEcore thread. No locking is performed, so any necessary locks must
// be implemented by the CPU provider manually.
//
virtual void Vsync() throw() { }
namespace VU1micro
virtual void Step()
{
// Ideally this would fall back on interpretation for executing single instructions
// for all CPU types, but due to VU complexities and large discrepancies between
// clamping in recs and ints, it's not really worth bothering with yet.
}
protected:
BaseVUmicroCPU() {}
};
// --------------------------------------------------------------------------------------
// InterpVU0 / InterpVU1
// --------------------------------------------------------------------------------------
class InterpVU0 : public BaseVUmicroCPU
{
extern void recAlloc();
extern void recShutdown();
extern void __fastcall recClear(u32 Addr, u32 Size);
public:
InterpVU0();
const char* GetShortName() const { return "intVU0"; }
wxString GetLongName() const { return L"VU0 Interpreter"; }
void Allocate() { }
void Shutdown() throw() { }
void Reset() { }
void Step();
void ExecuteBlock();
void Clear(u32 addr, u32 size) {}
};
class InterpVU1 : public BaseVUmicroCPU
{
public:
InterpVU1();
const char* GetShortName() const { return "intVU1"; }
wxString GetLongName() const { return L"VU1 Interpreter"; }
void Allocate() { }
void Shutdown() throw() { }
void Reset() { }
void Step();
void ExecuteBlock();
void Clear(u32 addr, u32 size) {}
};
// --------------------------------------------------------------------------------------
// recMicroVU0 / recMicroVU1
// --------------------------------------------------------------------------------------
class recMicroVU0 : public BaseVUmicroCPU
{
public:
recMicroVU0();
const char* GetShortName() const { return "mVU0"; }
wxString GetLongName() const { return L"microVU0 Recompiler"; }
void Allocate();
void Shutdown() throw();
void Reset();
void ExecuteBlock();
void Clear(u32 addr, u32 size);
void Vsync();
};
class recMicroVU1 : public BaseVUmicroCPU
{
public:
recMicroVU1();
const char* GetShortName() const { return "mVU1"; }
wxString GetLongName() const { return L"microVU1 Recompiler"; }
void Allocate();
void Shutdown() throw();
void Reset();
void ExecuteBlock();
void Clear(u32 addr, u32 size);
void Vsync();
};
extern BaseVUmicroCPU* CpuVU0;
extern BaseVUmicroCPU* CpuVU1;
// Note: Interpreter functions are dummies -- they don't actually do anything.
extern void intAlloc();
extern void intShutdown();
extern void __fastcall intClear(u32 Addr, u32 Size);
}
/////////////////////////////////////////////////////////////////
// These functions initialize memory for both VUs.
@ -68,20 +184,6 @@ void vuMicroMemAlloc();
void vuMicroMemShutdown();
void vuMicroMemReset();
// Resets VUs and assigns the cpuVU0 / cpuVU1 pointers as according to
// the CHECK_VU0REC / CHECK_VU1REC config options.
void vuMicroCpuReset();
/////////////////////////////////////////////////////////////////
// microVU Rec Stuff
//
extern void initVUrec(VURegs* vuRegs, const int vuIndex);
extern void closeVUrec(const int vuIndex);
extern void resetVUrec(const int vuIndex);
extern void vsyncVUrec(const int vuIndex);
extern void __fastcall clearVUrec(u32 addr, u32 size, const int vuIndex);
extern void __fastcall runVUrec(u32 startPC, u32 cycles, const int vuIndex);
/////////////////////////////////////////////////////////////////
// Everything else does stuff on a per-VU basis.
//

View File

@ -16,26 +16,7 @@
#include "PrecompiledHeader.h"
#include "Common.h"
#include "sVU_zerorec.h" // SuperVUReset
// The following CpuVU objects are value types instead of handles or pointers because they are
// modified on the fly to implement VU1 Skip.
VUmicroCpu CpuVU0; // contains a working copy of the VU0 cpu functions/API
VUmicroCpu CpuVU1; // contains a working copy of the VU1 cpu functions/API
void vuMicroCpuReset()
{
CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;
CpuVU1 = CHECK_VU1REC ? recVU1 : intVU1;
CpuVU0.Reset();
CpuVU1.Reset();
// SuperVUreset will do nothing is none of the recs are initialized.
// But it's needed if one or the other is initialized.
SuperVUReset(-1);
}
#include "VUmicro.h"
static u8* m_vuAllMem = NULL;
static const uint m_vuMemSize =

View File

@ -97,7 +97,7 @@ static __forceinline void vif0mpgTransfer(u32 addr, u32 *data, int size)
}*/
if (memcmp(VU0.Micro + addr, data, size << 2))
{
CpuVU0.Clear(addr, size << 2); // Clear before writing! :/ (cottonvibes)
CpuVU0->Clear(addr, size << 2); // Clear before writing! :/ (cottonvibes)
memcpy_fast(VU0.Micro + addr, data, size << 2);
}
}

View File

@ -47,7 +47,7 @@ __forceinline void vif1FLUSH()
{
do
{
CpuVU1.ExecuteBlock();
CpuVU1->ExecuteBlock();
}
while (VU0.VI[REG_VPU_STAT].UL & 0x100);
@ -115,7 +115,7 @@ static __forceinline void vif1mpgTransfer(u32 addr, u32 *data, int size)
pxAssert(VU1.Micro > 0);
if (memcmp(VU1.Micro + addr, data, size << 2))
{
CpuVU1.Clear(addr, size << 2); // Clear before writing! :/
CpuVU1->Clear(addr, size << 2); // Clear before writing! :/
memcpy_fast(VU1.Micro + addr, data, size << 2);
}
}

View File

@ -319,34 +319,53 @@ bool Pcsx2App::OnInit()
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{
// HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If
// the user already has all interps configured, for example, then no point in
// popping up this dialog.
// TODO : This should be redone using the ExtensibleConfirmation, and a sub-window
// (static text or something with a vertical scrollbar).
wxString message( _("The following cpu recompilers failed to initialize and will not be available:\n\n") );
if( !m_CoreAllocs->RecSuccess_EE )
if( !m_CoreAllocs->IsRecAvailable_EE() )
{
message += L"\t* R5900 (EE)\n";
}
if( !m_CoreAllocs->RecSuccess_IOP )
if( !m_CoreAllocs->IsRecAvailable_IOP() )
{
message += L"\t* R3000A (IOP)\n";
}
if( !m_CoreAllocs->RecSuccess_VU0 )
if( !m_CoreAllocs->IsRecAvailable_MicroVU0() )
{
message += L"\t* VU0\n";
message += L"\t* microVU0\n";
}
if( !m_CoreAllocs->RecSuccess_VU1 )
if( !m_CoreAllocs->IsRecAvailable_MicroVU1() )
{
message += L"\t* VU1\n";
message += L"\t* microVU1\n";
}
message += pxE( ".Popup Error:EmuCore:MemoryForRecs",
if( !m_CoreAllocs->IsRecAvailable_SuperVU0() )
{
message += L"\t* SuperVU0\n";
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU1() )
{
message += L"\t* SuperVU1\n";
}
// Failures can be SSE-related OR memory related. Should do per-cpu error reports instead...
/*message += pxE( ".Popup Error:EmuCore:MemoryForRecs",
L"These errors are the result of memory allocation failures (see the program log for details). "
L"Closing out some memory hogging background tasks may resolve this error.\n\n"
L"These recompilers have been disabled and interpreters will be used in their place. "
L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2."
);
);*/
if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
return false;

View File

@ -620,10 +620,6 @@
<Filter
Name="Dynarec"
>
<File
RelativePath="..\..\x86\iVU0micro.cpp"
>
</File>
<File
RelativePath="..\..\x86\iVU1micro.cpp"
>

View File

@ -1,64 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "Common.h"
#include "iR5900.h"
#include "VUmicro.h"
#include "sVU_zerorec.h"
#define useMVU0 CHECK_MICROVU0
namespace VU0micro
{
void recAlloc() { SuperVUAlloc(0); initVUrec(&VU0, 0); }
void recShutdown() { SuperVUDestroy(0); closeVUrec(0); }
void __fastcall recClear(u32 Addr, u32 Size) {
if (useMVU0) clearVUrec(Addr, Size, 0);
else SuperVUClear(Addr, Size, 0);
}
static void recReset() {
if (useMVU0) resetVUrec(0);
else SuperVUReset(0);
x86FpuState = FPU_STATE;
}
static void recStep() {}
static void recExecuteBlock()
{
if ((VU0.VI[REG_VPU_STAT].UL & 1) == 0) return;
XMMRegisters::Freeze();
if (useMVU0) runVUrec(VU0.VI[REG_TPC].UL, 0x300, 0);
else SuperVUExecuteProgram(VU0.VI[REG_TPC].UL & 0xfff, 0);
XMMRegisters::Thaw();
}
}
using namespace VU0micro;
const VUmicroCpu recVU0 =
{
recReset
, recStep
, recExecuteBlock
, recClear
, false
};

View File

@ -21,6 +21,7 @@
#include "VUmicro.h"
#include "sVU_zerorec.h"
#define useMVU1 CHECK_MICROVU1
int mVUdebugNow = 0;
@ -247,6 +248,7 @@ namespace VU1micro
}
#else
/*
namespace VU1micro
{
void recAlloc() { SuperVUAlloc(1); initVUrec(&VU1, 1); }
@ -267,7 +269,7 @@ namespace VU1micro
static void recExecuteBlock() {
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return;
assert( (VU1.VI[REG_TPC].UL&7) == 0 );
pxAssert( (VU1.VI[REG_TPC].UL&7) == 0 );
#ifdef DEBUG_COMPARE
SysPrintf("(%08d) StartPC = 0x%04x\n", runAmount, VU1.VI[REG_TPC].UL);
@ -281,22 +283,10 @@ namespace VU1micro
}
do { // while loop needed since not always will return finished
SuperVUExecuteProgram(VU1.VI[REG_TPC].UL & 0x3fff, 1);
} while( VU0.VI[REG_VPU_STAT].UL&0x100 );
} while( VU0.VI[REG_VPU_STAT].UL & 0x100 );
}
XMMRegisters::Thaw();
VUtestPause();
}
}
}*/
#endif
using namespace VU1micro;
const VUmicroCpu recVU1 =
{
recReset
, recStep
, recExecuteBlock
, recClear
, false
};

View File

@ -67,9 +67,21 @@ const __aligned(32) mVU_Globals mVUglob = {
// Micro VU - Main Functions
//------------------------------------------------------------------
microVUt(void) mVUthrowHardwareDeficiency( const wxChar* extFail )
{
throw Exception::HardwareDeficiency(
L"microVU init error: SSE1 is not available!",
wxsFormat(_("%s Extensions not found. microVU requires a host CPU with MMX, SSE, and SSE2 extensions."), L"SSE" )
);
}
// Only run this once per VU! ;)
microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
if(!x86caps.hasMultimediaExtensions) mVUthrowHardwareDeficiency( L"MMX" );
if(!x86caps.hasStreamingSIMDExtensions) mVUthrowHardwareDeficiency( L"SSE" );
if(!x86caps.hasStreamingSIMD2Extensions) mVUthrowHardwareDeficiency( L"SSE2" );
microVU* mVU = mVUx;
memset(&mVU->prog, 0, sizeof(mVU->prog));
@ -87,7 +99,11 @@ microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
mVU->regAlloc = new microRegAlloc(mVU->regs);
mVUprint((vuIndex) ? "microVU1: init" : "microVU0: init");
mVU->cache = SysMmapEx((vuIndex ? 0x5f240000 : 0x5e240000), mVU->cacheSize + 0x1000, 0, (vuIndex ? "Micro VU1" : "Micro VU0"));
// Give SysMmapEx a NULL and let the OS pick the memory for us: mVU can work with any
// address the operating system gives us, and unlike the EE/IOP there's not much convenience
// to debugging if the address is known anyway due to mVU's complex memory layouts (caching).
mVU->cache = SysMmapEx(NULL, mVU->cacheSize + 0x1000, 0, (vuIndex ? "Micro VU1" : "Micro VU0"));
if (!mVU->cache) throw Exception::OutOfMemory( "microVU Error: Failed to allocate recompiler memory!" );
memset(mVU->cache, 0xcc, mVU->cacheSize + 0x1000);
@ -306,21 +322,92 @@ microVUf(int) mVUsearchProg() {
return 1; // If !cleared, then we're still on the same program as last-time ;)
}
//------------------------------------------------------------------
// Wrapper Functions - Called by other parts of the Emu
//------------------------------------------------------------------
// --------------------------------------------------------------------------------------
// recMicroVU0
// --------------------------------------------------------------------------------------
void initVUrec (VURegs* vuRegs, const int vuIndex) { mVUinit(vuRegs, vuIndex); }
void closeVUrec(const int vuIndex) { mVUclose(mVUx); }
void resetVUrec(const int vuIndex) { mVUreset(mVUx); }
void vsyncVUrec(const int vuIndex) { mVUvsyncUpdate(mVUx); }
void __fastcall clearVUrec(u32 addr, u32 size, const int vuIndex) {
mVUclear(mVUx, addr, size);
recMicroVU0::recMicroVU0() {
IsInterpreter = false;
}
void __fastcall runVUrec(u32 startPC, u32 cycles, const int vuIndex) {
if (!vuIndex) ((mVUrecCall)microVU0.startFunct)(startPC, cycles);
else ((mVUrecCall)microVU1.startFunct)(startPC, cycles);
void recMicroVU0::Allocate() {
if( AtomicIncrement( m_AllocCount ) == 0 )
mVUinit( &VU0, 0 );
}
void recMicroVU0::Shutdown() throw() {
if( AtomicDecrement( m_AllocCount ) == 0 )
mVUclose(&microVU0);
}
void recMicroVU0::Reset() {
if( !pxAssertDev( m_AllocCount, "MicroVU0 CPU Provider has not been allocated prior to reset!" ) ) return;
mVUreset(&microVU0);
}
void recMicroVU0::ExecuteBlock() {
pxAssert( m_AllocCount ); // please allocate me first! :|
if ((VU0.VI[REG_VPU_STAT].UL & 1) == 0) return;
XMMRegisters::Freeze();
((mVUrecCall)microVU0.startFunct)(VU0.VI[REG_TPC].UL, 0x300);
XMMRegisters::Thaw();
}
void recMicroVU0::Clear(u32 addr, u32 size) {
pxAssert( m_AllocCount ); // please allocate me first! :|
mVUclear(&microVU0, addr, size);
}
void recMicroVU0::Vsync() {
mVUvsyncUpdate(&microVU0);
}
// --------------------------------------------------------------------------------------
// recMicroVU1
// --------------------------------------------------------------------------------------
recMicroVU1::recMicroVU1()
{
IsInterpreter = false;
}
void recMicroVU1::Allocate()
{
if( AtomicIncrement( m_AllocCount ) == 0 )
mVUinit( &VU1, 1 );
}
void recMicroVU1::Shutdown() throw()
{
if( AtomicDecrement( m_AllocCount ) == 0 )
mVUclose(&microVU1);
}
void recMicroVU1::Reset()
{
if( !pxAssertDev( m_AllocCount, "MicroVU1 CPU Provider has not been allocated prior to reset!" ) ) return;
mVUreset(&microVU1);
}
void recMicroVU1::ExecuteBlock()
{
pxAssert( m_AllocCount ); // please allocate me first! :|
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return;
pxAssert( (VU1.VI[REG_TPC].UL&7) == 0 );
XMMRegisters::Freeze();
((mVUrecCall)microVU1.startFunct)(VU1.VI[REG_TPC].UL, 3000000);
XMMRegisters::Thaw();
}
void recMicroVU1::Clear(u32 addr, u32 size)
{
pxAssert( m_AllocCount ); // please allocate me first! :|
mVUclear(&microVU1, addr, size);
}
void recMicroVU1::Vsync() {
mVUvsyncUpdate(&microVU1);
}

View File

@ -338,7 +338,7 @@ static u32* SuperVUStaticAlloc(u32 size);
static void SuperVURecompile();
// allocate VU resources
void SuperVUAlloc(int vuindex)
static void SuperVUAlloc(int vuindex)
{
// The old -1 crap has been depreciated on this function. Please
// specify either 0 or 1, thanks.
@ -350,15 +350,19 @@ void SuperVUAlloc(int vuindex)
// upper 4 bits must be zero!
// Changed "first try base" to 0xf1e0000, since 0x0c000000 liked to fail a lot. (cottonvibes)
s_recVUMem = SysMmapEx(0xf1e0000, VU_EXESIZE, 0x10000000, "SuperVUAlloc");
// Try again at some other random memory location... whatever. >_<
if( s_recVUMem == NULL )
s_recVUMem = SysMmapEx(0xc2b0000, VU_EXESIZE, 0x10000000, "SuperVUAlloc");
if (s_recVUMem == NULL)
{
throw Exception::OutOfMemory(
throw Exception::VirtualMemoryMapConflict(
// untranslated diagnostic msg, use exception's default for translation
wxsFormat( L"SuperVU failed to allocate recompiler memory (addr: 0x%x)", (u32)s_recVUMem ),
wxsFormat( L"SuperVU failed to allocate virtual memory below 256MB." ),
// Translated message
_("An out of memory error occured while attempting to reserve memory for the core recompilers.")
_("Out of Memory (sorta): The SuperVU recompiler was unable to reserve the specific memory ranges required.")
);
}
@ -479,7 +483,7 @@ void SuperVUReset(int vuindex)
}
// clear the block and any joining blocks
void __fastcall SuperVUClear(u32 startpc, u32 size, int vuindex)
static void __fastcall SuperVUClear(u32 startpc, u32 size, int vuindex)
{
vector<VuFunctionHeader::RANGE>::iterator itrange;
list<VuFunctionHeader*>::iterator it = s_listVUHeaders[vuindex].begin();
@ -4598,3 +4602,89 @@ void recVUunknown(VURegs* VU, s32 info)
{
Console.Warning("Unknown SVU micromode opcode called");
}
// --------------------------------------------------------------------------------------
// recSuperVU0 Interface
// --------------------------------------------------------------------------------------
recSuperVU0::recSuperVU0()
{
IsInterpreter = false;
}
void recSuperVU0::Allocate()
{
SuperVUAlloc( 0 );
}
void recSuperVU0::Shutdown() throw()
{
SuperVUDestroy( 0 );
}
void recSuperVU0::Reset()
{
SuperVUReset( 0 );
}
void recSuperVU0::ExecuteBlock()
{
if ((VU0.VI[REG_VPU_STAT].UL & 1) == 0) return;
XMMRegisters::Freeze();
SuperVUExecuteProgram(VU0.VI[REG_TPC].UL & 0xfff, 0);
XMMRegisters::Thaw();
}
void recSuperVU0::Clear(u32 Addr, u32 Size)
{
SuperVUClear(Addr, Size, 0);
}
// --------------------------------------------------------------------------------------
// recSuperVU1 Interface
// --------------------------------------------------------------------------------------
recSuperVU1::recSuperVU1()
{
IsInterpreter = false;
}
void recSuperVU1::Allocate()
{
SuperVUAlloc( 1 );
}
void recSuperVU1::Shutdown() throw()
{
SuperVUDestroy( 1 );
}
void recSuperVU1::Reset()
{
SuperVUReset( 1 );
}
void recSuperVU1::ExecuteBlock()
{
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return;
pxAssert( (VU1.VI[REG_TPC].UL&7) == 0 );
// [TODO] Debugging pre- and post- hooks?
XMMRegisters::Freeze();
if (VU1.VI[REG_TPC].UL >= VU1.maxmicro) {
Console.Error("VU1 memory overflow!!: %x", VU1.VI[REG_TPC].UL);
}
do { // while loop needed since not always will return finished
SuperVUExecuteProgram(VU1.VI[REG_TPC].UL & 0x3fff, 1);
} while( VU0.VI[REG_VPU_STAT].UL&0x100 );
XMMRegisters::Thaw();
}
void recSuperVU1::Clear(u32 Addr, u32 Size)
{
SuperVUClear(Addr, Size, 1);
}

View File

@ -19,10 +19,6 @@
#include "sVU_Micro.h"
extern void SuperVUAlloc(int vuindex); // global VU resources are automatically allocated if necessary.
extern void SuperVUDestroy(int vuindex); // if vuindex is -1, destroys everything
extern void SuperVUReset(int vuindex); // if vuindex is -1, resets everything
//Using assembly code from an external file.
#ifdef __LINUX__
extern "C" {
@ -33,7 +29,9 @@ extern void svudispfntemp();
#ifdef __LINUX__
}
#endif
extern void __fastcall SuperVUClear(u32 startpc, u32 size, int vuindex);
extern void SuperVUDestroy(int vuindex);
extern void SuperVUReset(int vuindex);
// read = 0, will write to reg
// read = 1, will read from reg
@ -42,3 +40,34 @@ extern u32 SuperVUGetVIAddr(int reg, int read);
// if p == 0, flush q else flush p; if wait is != 0, waits for p/q
extern void SuperVUFlush(int p, int wait);
class recSuperVU0 : public BaseVUmicroCPU
{
public:
recSuperVU0();
const char* GetShortName() const { return "sVU0"; }
wxString GetLongName() const { return L"SuperVU0 Recompiler"; }
void Allocate();
void Shutdown() throw();
void Reset();
void ExecuteBlock();
void Clear(u32 Addr, u32 Size);
};
class recSuperVU1 : public BaseVUmicroCPU
{
public:
recSuperVU1();
const char* GetShortName() const { return "sVU1"; }
wxString GetLongName() const { return L"SuperVU1 Recompiler"; }
void Allocate();
void Shutdown() throw();
void Reset();
void ExecuteBlock();
void Clear(u32 Addr, u32 Size);
};