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: // 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 class HardwareDeficiency : public virtual RuntimeError
{ {
public: public:

View File

@ -194,6 +194,7 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
if( isBlocking ) if( isBlocking )
{ {
// FIXME: Add deadlock detection and handling here... ?
m_lock_InThread.Wait(); m_lock_InThread.Wait();
Detach(); 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_MACROVU0 // If defined uses mVU for VU Macro (COP2), else uses sVU
#define CHECK_MICROVU0 (EmuConfig.Cpu.Recompiler.UseMicroVU0) #define CHECK_MICROVU0 (EmuConfig.Cpu.Recompiler.UseMicroVU0)
#define CHECK_MICROVU1 (EmuConfig.Cpu.Recompiler.UseMicroVU1) #define CHECK_MICROVU1 (EmuConfig.Cpu.Recompiler.UseMicroVU1)
#define CHECK_EEREC (EmuConfig.Cpu.Recompiler.EnableEE && GetSysCoreAlloc().RecSuccess_EE) #define CHECK_EEREC (EmuConfig.Cpu.Recompiler.EnableEE && GetSysCoreAlloc().IsRecAvailable_EE())
#define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().RecSuccess_IOP) #define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().IsRecAvailable_IOP())
#define CHECK_VU0REC (EmuConfig.Cpu.Recompiler.EnableVU0 && GetSysCoreAlloc().RecSuccess_VU0)
#define CHECK_VU1REC (EmuConfig.Cpu.Recompiler.EnableVU1 && GetSysCoreAlloc().RecSuccess_VU1)
//------------ SPECIAL GAME FIXES!!! --------------- //------------ SPECIAL GAME FIXES!!! ---------------
#define NUM_OF_GAME_FIXES 7 #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). // by UI implementations. (ie, AppCoreThread in PCSX2-wx interface).
vSyncDebugStuff( iFrame ); vSyncDebugStuff( iFrame );
if (CHECK_MICROVU0) vsyncVUrec(0); CpuVU0->Vsync();
if (CHECK_MICROVU1) vsyncVUrec(1); CpuVU1->Vsync();
if ((CSRw & 0x8)) if ((CSRw & 0x8))
{ {

View File

@ -31,7 +31,7 @@ typedef char* (*TdisR5900F)DisFInterface;
// These macros are used to assemble the disassembler functions // These macros are used to assemble the disassembler functions
#define MakeDisF(fn, b) \ #define MakeDisF(fn, b) \
char* fn DisFInterface { \ 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; \ else ostr[0] = 0; \
b; /*ostr[(strlen(ostr) - 1)] = 0;*/ return ostr; \ b; /*ostr[(strlen(ostr) - 1)] = 0;*/ return ostr; \
} }
@ -52,47 +52,47 @@ typedef char* (*TdisR5900F)DisFInterface;
} }
#define dCP2128f(i) { \ #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]); \ 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) { \ #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]); \ else sprintf(ostr, "%s x=%f (%s),", ostr, VU1.VF[i].f.x, disRNameCP2f[i]); \
} \ } \
#define dCP232y(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]); \ else sprintf(ostr, "%s y=%f (%s),", ostr, VU1.VF[i].f.y, disRNameCP2f[i]); \
} \ } \
#define dCP232z(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]); \ else sprintf(ostr, "%s z=%f (%s),", ostr, VU1.VF[i].f.z, disRNameCP2f[i]); \
} }
#define dCP232w(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]); \ else sprintf(ostr, "%s w=%f (%s),", ostr, VU1.VF[i].f.w, disRNameCP2f[i]); \
} }
#define dCP2ACCf() { \ #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); \ 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) { \ #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]); \ else sprintf(ostr, "%s %8.8x (%s),", ostr, VU1.VI[i].UL, disRNameCP2i[i]); \
} }
#define dCP232iF(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]); \ else sprintf(ostr, "%s %f (%s),", ostr, VU1.VI[i].F, disRNameCP2i[i]); \
} }
#define dCP232f(i, j) { \ #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]); \ 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) // (the VU memory operations are different for recs vs. interpreters)
void memMapVUmicro() void memMapVUmicro()
{ {
vtlb_MapHandler(vu0_micro_mem[CHECK_VU0REC ? 0 : 1],0x11000000,0x00004000); vtlb_MapHandler(vu0_micro_mem[CpuVU0->IsInterpreter],0x11000000,0x00004000);
vtlb_MapHandler(vu1_micro_mem[CHECK_VU1REC ? 0 : 1],0x11008000,0x00004000); vtlb_MapHandler(vu1_micro_mem[CpuVU1->IsInterpreter],0x11008000,0x00004000);
// VU0/VU1 memory // VU0/VU1 memory
// (Like IOP memory, these are generally only used by the EE Bios kernel during // (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( dynarec )
{ {
if( vunum==0 ) if( vunum==0 )
VU0micro::recClear(addr,size); CpuVU0->Clear(addr,size);
else else
VU1micro::recClear(addr,size); CpuVU1->Clear(addr,size);
} }
else else
{ {
if( vunum==0 ) if( vunum==0 )
VU0micro::intClear(addr,size); CpuVU0->Clear(addr,size);
else 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 // We're in a BranchTest. All dynarec registers are flushed
// so there is no need to freeze registers here. // 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. // This might be needed to keep the EE and VU0 in sync.
// A better fix will require hefty changes to the VU recs. -_- // 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) if (madr < 0x11004000)
{ {
DbgCon.Warning("scratch pad clearing vu0"); DbgCon.Warning("scratch pad clearing vu0");
CpuVU0.Clear(madr&0xfff, size); CpuVU0->Clear(madr&0xfff, size);
} }
else if (madr >= 0x11008000 && madr < 0x1100c000) else if (madr >= 0x11008000 && madr < 0x1100c000)
{ {
DbgCon.Warning("scratch pad clearing vu1"); 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(); 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! // returns the translated error message for the Virtual Machine failing to allocate!
static wxString GetMemoryErrorVM() static wxString GetMemoryErrorVM()
{ {
@ -129,10 +202,8 @@ SysCoreAllocations::SysCoreAllocations()
Console.WriteLn( "Initializing PS2 virtual machine..." ); Console.WriteLn( "Initializing PS2 virtual machine..." );
RecSuccess_EE = false; m_RecSuccessEE = false;
RecSuccess_IOP = false; m_RecSuccessIOP = false;
RecSuccess_VU0 = false;
RecSuccess_VU1 = false;
try try
{ {
@ -153,13 +224,12 @@ SysCoreAllocations::SysCoreAllocations()
{ {
CleanupMess(); 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( throw Exception::OutOfMemory(
wxsFormat( // Diagnostic (english) L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n" L"Error Details: " + fromUTF8( ex.what() ),
L"Error Details: " + fromUTF8( ex.what() )
),
GetMemoryErrorVM() // translated GetMemoryErrorVM() // translated
); );
@ -167,9 +237,11 @@ SysCoreAllocations::SysCoreAllocations()
Console.WriteLn( "Allocating memory for recompilers..." ); Console.WriteLn( "Allocating memory for recompilers..." );
CpuProviders = new CpuInitializerSet();
try { try {
recCpu.Allocate(); recCpu.Allocate();
RecSuccess_EE = true; m_RecSuccessEE = true;
} }
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
@ -179,7 +251,7 @@ SysCoreAllocations::SysCoreAllocations()
try { try {
psxRec.Allocate(); psxRec.Allocate();
RecSuccess_IOP = true; m_RecSuccessIOP = true;
} }
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
@ -189,42 +261,26 @@ SysCoreAllocations::SysCoreAllocations()
// hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :( // 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 // 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. // actually initializes everything once and then shares it between both VU recs.
if( !RecSuccess_VU0 && !RecSuccess_VU1 ) if( !IsRecAvailable_SuperVU0() && !IsRecAvailable_SuperVU1() )
SuperVUDestroy( -1 ); 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() void SysCoreAllocations::CleanupMess() throw()
{ {
try try
{ {
// Special SuperVU "complete" terminator. // Special SuperVU "complete" terminator (stupid hacky recompiler)
SuperVUDestroy( -1 ); SuperVUDestroy( -1 );
VU1micro::recShutdown();
VU0micro::recShutdown();
psxRec.Shutdown(); psxRec.Shutdown();
recCpu.Shutdown(); recCpu.Shutdown();
@ -243,12 +299,34 @@ SysCoreAllocations::~SysCoreAllocations() throw()
bool SysCoreAllocations::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const bool SysCoreAllocations::HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) const
{ {
return (recOpts.EnableEE && !RecSuccess_EE) || return (recOpts.EnableEE && !IsRecAvailable_EE()) ||
(recOpts.EnableIOP && !RecSuccess_IOP) || (recOpts.EnableIOP && !IsRecAvailable_IOP()) ||
(recOpts.EnableVU0 && !RecSuccess_VU0) || (recOpts.EnableVU0 && recOpts.UseMicroVU0 && !IsRecAvailable_MicroVU0()) ||
(recOpts.EnableVU1 && !RecSuccess_VU1); (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. // 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 // 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 // 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. // Use this method to reset the recs when important global pointers like the MTGS are re-assigned.
void SysClearExecutionCache() void SysClearExecutionCache()
{ {
Cpu = CHECK_EEREC ? &recCpu : &intCpu; GetSysCoreAlloc().SelectCpuProviders();
psxCpu = CHECK_IOPREC ? &psxRec : &psxInt;
// 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(); Cpu->Reset();
psxCpu->Reset(); psxCpu->Reset();
vuMicroCpuReset(); CpuVU0->Reset();
CpuVU1->Reset();
} }
// Maps a block of memory for use as a recompiled code buffer, and ensures that the // Maps a block of memory for use as a recompiled code buffer, and ensures that the
@ -275,17 +357,21 @@ u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) ) 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 // memory allocation *must* have the top bit clear, so let's try again
// with NULL (let the OS pick something for us). // 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) ) 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 ); SafeSysMunmap( Mem, size );
// returns NULL, caller should throw an exception. // 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; static const int PCSX2_VersionLo = 7;
class SysCoreThread; class SysCoreThread;
class CpuInitializerSet;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysCoreAllocations class // SysCoreAllocations class
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysCoreAllocations 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: protected:
ScopedPtr<CpuInitializerSet> CpuProviders;
bool m_RecSuccessEE:1;
bool m_RecSuccessIOP:1;
public: public:
SysCoreAllocations(); SysCoreAllocations();
virtual ~SysCoreAllocations() throw(); virtual ~SysCoreAllocations() throw();
void SelectCpuProviders() const;
bool HadSomeFailures( const Pcsx2Config::RecompilerOptions& recOpts ) 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: protected:
void CleanupMess() throw(); void CleanupMess() throw();
}; };

View File

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

View File

@ -56,7 +56,7 @@ void __fastcall vu0ExecMicro(u32 addr) {
// If an unsigned variable isn't -1? --arcum42 // If an unsigned variable isn't -1? --arcum42
if (addr != (u32)-1) VU0.VI[REG_TPC].UL = addr; if (addr != (u32)-1) VU0.VI[REG_TPC].UL = addr;
_vuExecMicroDebug(VU0); _vuExecMicroDebug(VU0);
CpuVU0.ExecuteBlock(); CpuVU0->ExecuteBlock();
// If the VU0 program didn't finish then we'll want to finish it up // 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) // 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"); if (VU->VF[0].f.w != 1.0f) DbgCon.Error("VF[0].w != 1.0!!!!\n");
} }
namespace VU0micro // --------------------------------------------------------------------------------------
// VU0microInterpreter
// --------------------------------------------------------------------------------------
InterpVU0::InterpVU0()
{ {
void intAlloc() IsInterpreter = true;
{
}
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);
}
}
} }
using namespace VU0micro; void InterpVU0::Step()
const VUmicroCpu intVU0 =
{ {
intReset vu0Exec( &VU0 );
, intStep }
, intExecuteBlock
, intClear 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) while(VU0.VI[REG_VPU_STAT].UL & 0x100)
{ {
VUM_LOG("vu1ExecMicro > Stalling until current microprogram finishes"); VUM_LOG("vu1ExecMicro > Stalling until current microprogram finishes");
CpuVU1.ExecuteBlock(); CpuVU1->ExecuteBlock();
} }
VUM_LOG("vu1ExecMicro %x", addr); VUM_LOG("vu1ExecMicro %x", addr);
@ -59,7 +59,7 @@ void __fastcall vu1ExecMicro(u32 addr)
if (addr != -1) VU1.VI[REG_TPC].UL = addr; if (addr != -1) VU1.VI[REG_TPC].UL = addr;
_vuExecMicroDebug(VU1); _vuExecMicroDebug(VU1);
CpuVU1.ExecuteBlock(); CpuVU1->ExecuteBlock();
} }
_vuRegsTables(VU1, VU1regs); _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"); 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) if( (i < 0) && (VU1.branch || VU1.ebit) )
{ {
} // execute one more
vu1Exec(&VU1);
void intShutdown() }
{ break;
}
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) ) { vu1Exec(&VU1);
// execute one more
vu1Exec(&VU1);
}
} }
} }
using namespace VU1micro;
const VUmicroCpu intVU1 =
{
intReset
, intStep
, intExecuteBlock
, intClear
, true
};

View File

@ -17,49 +17,165 @@
#include "VU.h" #include "VU.h"
#include "VUops.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)(); protected:
void (*Step)(); // allocation counter for multiple init/shutdown calls
void (*ExecuteBlock)(); // VUs should support block-level execution only. // (most or all implementations will need this!)
void (__fastcall *Clear)(u32 Addr, u32 Size); int m_AllocCount;
public:
// this boolean indicates to some generic logging facilities if the VU's registers // 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; 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; // BaseVUmicroCPU
extern const VUmicroCpu recVU0; // --------------------------------------------------------------------------------------
// Layer class for possible future implementation (currently is nothing more than a type-safe
extern VUmicroCpu CpuVU1; // type define).
extern const VUmicroCpu intVU1; //
extern const VUmicroCpu recVU1; class BaseVUmicroCPU : public BaseCpuProvider
namespace VU0micro
{ {
extern void recAlloc(); public:
extern void recShutdown();
extern void __fastcall recClear(u32 Addr, u32 Size);
// Note: Interpreter functions are dummies -- they don't actually do anything. // Called by the PS2 VM's event manager for every internal vertical sync (occurs at either
extern void intAlloc(); // 50hx (pal) or 59.94hx (NTSC).
extern void intShutdown(); //
extern void __fastcall intClear(u32 Addr, u32 Size); // 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(); public:
extern void recShutdown(); InterpVU0();
extern void __fastcall recClear(u32 Addr, u32 Size);
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. // These functions initialize memory for both VUs.
@ -68,20 +184,6 @@ void vuMicroMemAlloc();
void vuMicroMemShutdown(); void vuMicroMemShutdown();
void vuMicroMemReset(); 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. // Everything else does stuff on a per-VU basis.
// //

View File

@ -16,26 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "Common.h" #include "Common.h"
#include "VUmicro.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);
}
static u8* m_vuAllMem = NULL; static u8* m_vuAllMem = NULL;
static const uint m_vuMemSize = 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)) 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); memcpy_fast(VU0.Micro + addr, data, size << 2);
} }
} }

View File

@ -47,7 +47,7 @@ __forceinline void vif1FLUSH()
{ {
do do
{ {
CpuVU1.ExecuteBlock(); CpuVU1->ExecuteBlock();
} }
while (VU0.VI[REG_VPU_STAT].UL & 0x100); 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); pxAssert(VU1.Micro > 0);
if (memcmp(VU1.Micro + addr, data, size << 2)) 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); 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 ) ) 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") ); 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"; message += L"\t* R5900 (EE)\n";
} }
if( !m_CoreAllocs->RecSuccess_IOP ) if( !m_CoreAllocs->IsRecAvailable_IOP() )
{ {
message += L"\t* R3000A (IOP)\n"; 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"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"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"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." 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 ) ) if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
return false; return false;

View File

@ -620,10 +620,6 @@
<Filter <Filter
Name="Dynarec" Name="Dynarec"
> >
<File
RelativePath="..\..\x86\iVU0micro.cpp"
>
</File>
<File <File
RelativePath="..\..\x86\iVU1micro.cpp" 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 "VUmicro.h"
#include "sVU_zerorec.h" #include "sVU_zerorec.h"
#define useMVU1 CHECK_MICROVU1 #define useMVU1 CHECK_MICROVU1
int mVUdebugNow = 0; int mVUdebugNow = 0;
@ -247,6 +248,7 @@ namespace VU1micro
} }
#else #else
/*
namespace VU1micro namespace VU1micro
{ {
void recAlloc() { SuperVUAlloc(1); initVUrec(&VU1, 1); } void recAlloc() { SuperVUAlloc(1); initVUrec(&VU1, 1); }
@ -267,7 +269,7 @@ namespace VU1micro
static void recExecuteBlock() { static void recExecuteBlock() {
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return; 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 #ifdef DEBUG_COMPARE
SysPrintf("(%08d) StartPC = 0x%04x\n", runAmount, VU1.VI[REG_TPC].UL); 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 do { // while loop needed since not always will return finished
SuperVUExecuteProgram(VU1.VI[REG_TPC].UL & 0x3fff, 1); 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(); XMMRegisters::Thaw();
VUtestPause(); VUtestPause();
} }
} }*/
#endif #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 // 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! ;) // Only run this once per VU! ;)
microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) { 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; microVU* mVU = mVUx;
memset(&mVU->prog, 0, sizeof(mVU->prog)); memset(&mVU->prog, 0, sizeof(mVU->prog));
@ -87,7 +99,11 @@ microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
mVU->regAlloc = new microRegAlloc(mVU->regs); mVU->regAlloc = new microRegAlloc(mVU->regs);
mVUprint((vuIndex) ? "microVU1: init" : "microVU0: init"); 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!" ); if (!mVU->cache) throw Exception::OutOfMemory( "microVU Error: Failed to allocate recompiler memory!" );
memset(mVU->cache, 0xcc, mVU->cacheSize + 0x1000); 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 ;) 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); } recMicroVU0::recMicroVU0() {
void closeVUrec(const int vuIndex) { mVUclose(mVUx); } IsInterpreter = false;
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);
} }
void __fastcall runVUrec(u32 startPC, u32 cycles, const int vuIndex) { void recMicroVU0::Allocate() {
if (!vuIndex) ((mVUrecCall)microVU0.startFunct)(startPC, cycles); if( AtomicIncrement( m_AllocCount ) == 0 )
else ((mVUrecCall)microVU1.startFunct)(startPC, cycles); 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(); static void SuperVURecompile();
// allocate VU resources // allocate VU resources
void SuperVUAlloc(int vuindex) static void SuperVUAlloc(int vuindex)
{ {
// The old -1 crap has been depreciated on this function. Please // The old -1 crap has been depreciated on this function. Please
// specify either 0 or 1, thanks. // specify either 0 or 1, thanks.
@ -351,14 +351,18 @@ void SuperVUAlloc(int vuindex)
// Changed "first try base" to 0xf1e0000, since 0x0c000000 liked to fail a lot. (cottonvibes) // Changed "first try base" to 0xf1e0000, since 0x0c000000 liked to fail a lot. (cottonvibes)
s_recVUMem = SysMmapEx(0xf1e0000, VU_EXESIZE, 0x10000000, "SuperVUAlloc"); 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) if (s_recVUMem == NULL)
{ {
throw Exception::OutOfMemory( throw Exception::VirtualMemoryMapConflict(
// untranslated diagnostic msg, use exception's default for translation // 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 // 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 // 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; vector<VuFunctionHeader::RANGE>::iterator itrange;
list<VuFunctionHeader*>::iterator it = s_listVUHeaders[vuindex].begin(); 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"); 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" #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. //Using assembly code from an external file.
#ifdef __LINUX__ #ifdef __LINUX__
extern "C" { extern "C" {
@ -33,7 +29,9 @@ extern void svudispfntemp();
#ifdef __LINUX__ #ifdef __LINUX__
} }
#endif #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 = 0, will write to reg
// read = 1, will read from 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 // if p == 0, flush q else flush p; if wait is != 0, waits for p/q
extern void SuperVUFlush(int p, int wait); 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);
};