mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
bb309c8d65
commit
f52faead0c
|
@ -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:
|
||||
|
|
|
@ -194,6 +194,7 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
|
|||
|
||||
if( isBlocking )
|
||||
{
|
||||
// FIXME: Add deadlock detection and handling here... ?
|
||||
m_lock_InThread.Wait();
|
||||
Detach();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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]); \
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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. -_-
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
184
pcsx2/System.cpp
184
pcsx2/System.cpp
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
194
pcsx2/VUmicro.h
194
pcsx2/VUmicro.h
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -620,10 +620,6 @@
|
|||
<Filter
|
||||
Name="Dynarec"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\x86\iVU0micro.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\x86\iVU1micro.cpp"
|
||||
>
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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(µVU0);
|
||||
}
|
||||
|
||||
void recMicroVU0::Reset() {
|
||||
if( !pxAssertDev( m_AllocCount, "MicroVU0 CPU Provider has not been allocated prior to reset!" ) ) return;
|
||||
mVUreset(µVU0);
|
||||
}
|
||||
|
||||
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(µVU0, addr, size);
|
||||
}
|
||||
|
||||
void recMicroVU0::Vsync() {
|
||||
mVUvsyncUpdate(µVU0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// 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(µVU1);
|
||||
}
|
||||
|
||||
void recMicroVU1::Reset()
|
||||
{
|
||||
if( !pxAssertDev( m_AllocCount, "MicroVU1 CPU Provider has not been allocated prior to reset!" ) ) return;
|
||||
mVUreset(µVU1);
|
||||
}
|
||||
|
||||
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(µVU1, addr, size);
|
||||
}
|
||||
|
||||
void recMicroVU1::Vsync() {
|
||||
mVUvsyncUpdate(µVU1);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue