diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h index d10b2725be..bee8eac33c 100644 --- a/common/include/Utilities/Exceptions.h +++ b/common/include/Utilities/Exceptions.h @@ -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: diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index 772992f479..642a7046e5 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -194,6 +194,7 @@ void Threading::PersistentThread::Cancel( bool isBlocking ) if( isBlocking ) { + // FIXME: Add deadlock detection and handling here... ? m_lock_InThread.Wait(); Detach(); } diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 548806c141..721593dc84 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -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 diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index df2078b56a..536b668835 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -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)) { diff --git a/pcsx2/DebugTools/DisVU1Micro.cpp b/pcsx2/DebugTools/DisVU1Micro.cpp index d4d2fb1fc4..167c1fc26e 100644 --- a/pcsx2/DebugTools/DisVU1Micro.cpp +++ b/pcsx2/DebugTools/DisVU1Micro.cpp @@ -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]); \ } diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp index 881d4a9c34..8ddf972bf9 100644 --- a/pcsx2/Memory.cpp +++ b/pcsx2/Memory.cpp @@ -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); } } diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 1f0e89ffb8..a0bc0ca8ae 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -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. -_- diff --git a/pcsx2/SPR.cpp b/pcsx2/SPR.cpp index 86122daf67..4bcbb93f6c 100644 --- a/pcsx2/SPR.cpp +++ b/pcsx2/SPR.cpp @@ -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); } } } diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 588975773b..8f4b9c523d 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -114,6 +114,79 @@ void SysDetect() Console.Newline(); } +template< typename CpuType > +class CpuInitializer +{ +public: + ScopedPtr 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 superVU0; + CpuInitializer superVU1; + + CpuInitializer microVU0; + CpuInitializer microVU1; + + CpuInitializer interpVU0; + CpuInitializer 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. diff --git a/pcsx2/System.h b/pcsx2/System.h index 3da30065a1..a7a3100e52 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -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 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(); }; diff --git a/pcsx2/VU0.cpp b/pcsx2/VU0.cpp index ddb120c3df..08d00efec2 100644 --- a/pcsx2/VU0.cpp +++ b/pcsx2/VU0.cpp @@ -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; } diff --git a/pcsx2/VU0micro.cpp b/pcsx2/VU0micro.cpp index 05f1987113..5be613d9cf 100644 --- a/pcsx2/VU0micro.cpp +++ b/pcsx2/VU0micro.cpp @@ -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) diff --git a/pcsx2/VU0microInterp.cpp b/pcsx2/VU0microInterp.cpp index 7adca3a0e1..3fb3f9f7b6 100644 --- a/pcsx2/VU0microInterp.cpp +++ b/pcsx2/VU0microInterp.cpp @@ -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 -}; diff --git a/pcsx2/VU1micro.cpp b/pcsx2/VU1micro.cpp index 0bb089b127..3060fb30c9 100644 --- a/pcsx2/VU1micro.cpp +++ b/pcsx2/VU1micro.cpp @@ -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); diff --git a/pcsx2/VU1microInterp.cpp b/pcsx2/VU1microInterp.cpp index 5a06a0893f..604bd78593 100644 --- a/pcsx2/VU1microInterp.cpp +++ b/pcsx2/VU1microInterp.cpp @@ -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 -}; diff --git a/pcsx2/VUmicro.h b/pcsx2/VUmicro.h index 766050c6f8..50d7f3601f 100644 --- a/pcsx2/VUmicro.h +++ b/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. // diff --git a/pcsx2/VUmicroMem.cpp b/pcsx2/VUmicroMem.cpp index 777a64f427..25df25777b 100644 --- a/pcsx2/VUmicroMem.cpp +++ b/pcsx2/VUmicroMem.cpp @@ -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 = diff --git a/pcsx2/Vif0Dma.cpp b/pcsx2/Vif0Dma.cpp index a7af288f55..daa780b459 100644 --- a/pcsx2/Vif0Dma.cpp +++ b/pcsx2/Vif0Dma.cpp @@ -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); } } diff --git a/pcsx2/Vif1Dma.cpp b/pcsx2/Vif1Dma.cpp index e2b34e2104..d16b2c86fd 100644 --- a/pcsx2/Vif1Dma.cpp +++ b/pcsx2/Vif1Dma.cpp @@ -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); } } diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index 108550b737..c5feea509a 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -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; diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 6dfcffb1a7..9e68d69425 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -620,10 +620,6 @@ - - diff --git a/pcsx2/x86/iVU0micro.cpp b/pcsx2/x86/iVU0micro.cpp deleted file mode 100644 index 72f14b9d14..0000000000 --- a/pcsx2/x86/iVU0micro.cpp +++ /dev/null @@ -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 . - */ - - -#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 -}; diff --git a/pcsx2/x86/iVU1micro.cpp b/pcsx2/x86/iVU1micro.cpp index 7f3ada40e3..11c6553b9d 100644 --- a/pcsx2/x86/iVU1micro.cpp +++ b/pcsx2/x86/iVU1micro.cpp @@ -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 -}; diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 7258efa8c0..697581be89 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -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); +} diff --git a/pcsx2/x86/sVU_zerorec.cpp b/pcsx2/x86/sVU_zerorec.cpp index be69b19025..3f4187e188 100644 --- a/pcsx2/x86/sVU_zerorec.cpp +++ b/pcsx2/x86/sVU_zerorec.cpp @@ -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::iterator itrange; list::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); +} diff --git a/pcsx2/x86/sVU_zerorec.h b/pcsx2/x86/sVU_zerorec.h index 4f37111ffb..9582c7981c 100644 --- a/pcsx2/x86/sVU_zerorec.h +++ b/pcsx2/x86/sVU_zerorec.h @@ -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); +};