diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index f6ddd8297a..d4219a7bc7 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -306,17 +306,34 @@ s32 cdvdWriteConfig(const u8* config) static MutexRecursive Mutex_NewDiskCB; // Sets ElfCRC to the CRC of the game bound to the CDVD plugin. -static __forceinline ElfObject *loadElf( const wxString filename ) +static __forceinline ElfObject* loadElf( const wxString filename ) { if (filename.StartsWith(L"host")) return new ElfObject(filename.After(':'), Path::GetFileSize(filename.After(':'))); + // Mimic PS2 behavior! + // Much trial-and-error with changing the ISOFS and BOOT2 contents of an image have shown that + // the PS2 BIOS performs the peculiar task of *ignoring* the version info from the parsed BOOT2 + // filename *and* the ISOFS, when loading the game's ELF image. What this means is: + // + // 1. a valid PS2 ELF can have any version (ISOFS), and the version need not match the one in SYSTEM.CNF. + // 2. the version info on the file in the BOOT2 parameter of SYSTEM.CNF can be missing, 10 chars long, + // or anything else. Its all ignored. + // 3. Games loading their own files do *not* exhibit this behavior; likely due to using newer IOP modules + // or lower level filesystem APIs (fortunately that doesn't affect us). + // + // FIXME: Properly mimicing this behavior is troublesome since we need to add support for "ignoring" + // version information when doing file searches. I'll add this later. For now, assuming a ;1 should + // be sufficient (no known games have their ELF binary as anything but version ;1) + + const wxString fixedname( wxStringTokenizer(filename, L';').GetNextToken() + L";1" ); + + if( fixedname != filename ) + Console.WriteLn( Color_Blue, "(LoadELF) Non-conforming version suffix detected and replaced." ); + IsoFSCDVD isofs; IsoFile file(isofs, filename); - ElfObject *elfptr; - - elfptr = new ElfObject(filename, file); - return elfptr; + return new ElfObject(filename, file); } static __forceinline void _reloadElfInfo(wxString elfpath) @@ -339,7 +356,6 @@ static __forceinline void _reloadElfInfo(wxString elfpath) if (fname.Matches(L"????_???.??*")) DiscSerial = fname(0,4) + L"-" + fname(5,3) + fname(9,2); - Console.WriteLn("Disc ID = %s", DiscSerial.ToUTF8().data()); elfptr = loadElf(elfpath); ElfCRC = elfptr->getCRC(); @@ -348,47 +364,54 @@ static __forceinline void _reloadElfInfo(wxString elfpath) ElfEntry = elfptr->header.e_entry; Console.WriteLn("Entry point = 0x%08x", ElfEntry); - elfptr.Delete(); - - // Set the Game DataBase to the correct game based on Game Serial Code... - if (IGameDatabase* GameDB = AppHost_GetGameDatabase()) { - wxString gameSerial( SysGetDiscID() ); - wxString serialMsg; - if(!DiscSerial.IsEmpty()) - serialMsg = L"serial=" + DiscSerial + L" "; - - Game_Data game; - if (GameDB->findGame(game, gameSerial)) - { - Console.WriteLn(L"(GameDB) Found Game! %s [CRC=%8.8x]", serialMsg.c_str(), ElfCRC ); - // [TODO] Display lots of other info from the database here! - } - else - Console.Warning(L"(GameDB) Game not found! %s [CRC=%8.8x]", serialMsg.c_str(), ElfCRC ); - } + // Note: Do not load game database info here. This code is generic and called from + // BIOS key encryption as well as eeloadReplaceOSDSYS. The first is actually still executing + // BIOS code, and patches and cheats should not be applied yet. (they are applied when + // eeGameStarting is invoked, which is when the VM starts executing the actual game ELF + // binary). } void cdvdReloadElfInfo(wxString elfoverride) { - if (!elfoverride.IsEmpty()) + // called from context of executing VM code (recompilers), so we need to trap exceptions + // and route them through the VM's exception handler. (needed for non-SEH platforms, such + // as Linux/GCC) + + try { - _reloadElfInfo(elfoverride); - return; + if (!elfoverride.IsEmpty()) + { + _reloadElfInfo(elfoverride); + return; + } + + wxString elfpath; + u32 discType = GetPS2ElfName(elfpath); + + if(discType==1) + { + // Is a PS1 disc. + if (!ENABLE_LOADING_PS1_GAMES) + Cpu->ThrowException( Exception::RuntimeError() + .SetDiagMsg(L"PSX game discs are not supported by PCSX2.") + .SetUserMsg(pxE( "Error:PsxDisc", + L"Playstation game discs are not supported by PCSX2. If you want to emulate PSX games " + L"then you'll have to download a PSX-specific emulator, such as ePSXe or PCSX.") + ) + ); + //Console.Error( "Playstation1 game discs are not supported by PCSX2." ); + } + + // Isn't a disc we recognize? + if(discType == 0) return; + + // Recognized and PS2 (BOOT2). Good job, user. + _reloadElfInfo(elfpath); } - - wxString elfpath; - u32 discType = GetPS2ElfName(elfpath); - - switch (discType) - { - case 2: // Is a PS2 disc. - _reloadElfInfo(elfpath); - break; - case 1: // Is a PS1 disc. - if (ENABLE_LOADING_PS1_GAMES) _reloadElfInfo(elfpath); - break; - default: // Isn't a disc we recognise. - break; + catch (Exception::FileNotFound& e) + { + pxFail( "Not in my back yard!" ); + Cpu->ThrowException(e); } } diff --git a/pcsx2/Elfheader.cpp b/pcsx2/Elfheader.cpp index cad4faac61..600af38e25 100644 --- a/pcsx2/Elfheader.cpp +++ b/pcsx2/Elfheader.cpp @@ -434,13 +434,13 @@ int GetPS2ElfName( wxString& name ) while( !file.eof() ) { - wxString original( fromUTF8(file.readLine().c_str()) ); - ParsedAssignmentString parts( original ); + const wxString original( fromUTF8(file.readLine().c_str()) ); + const ParsedAssignmentString parts( original ); if( parts.lvalue.IsEmpty() && parts.rvalue.IsEmpty() ) continue; if( parts.rvalue.IsEmpty() ) { - Console.Error( "(GetElfName) Unusual or malformed entry in SYSTEM.CNF ignored:" ); + Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" ); Console.Indent().WriteLn( original ); continue; } @@ -448,22 +448,22 @@ int GetPS2ElfName( wxString& name ) if( parts.lvalue == L"BOOT2" ) { name = parts.rvalue; - Console.WriteLn( Color_StrongBlue, L"(GetElfName) Detected PS2 Disc = " + name ); + Console.WriteLn( Color_StrongBlue, L"(SYSTEM.CNF) Detected PS2 Disc = " + name ); retype = 2; } else if( parts.lvalue == L"BOOT" ) { name = parts.rvalue; - Console.WriteLn( Color_StrongBlue, L"(GetElfName) Detected PSX/PSone Disc = " + name ); + Console.WriteLn( Color_StrongBlue, L"(SYSTEM.CNF) Detected PSX/PSone Disc = " + name ); retype = 1; } else if( parts.lvalue == L"VMODE" ) { - Console.WriteLn( Color_StrongBlue, L"(GetElfName) Disc region type = " + parts.rvalue ); + Console.WriteLn( Color_Blue, L"(SYSTEM.CNF) Disc region type = " + parts.rvalue ); } else if( parts.lvalue == L"VER" ) { - Console.WriteLn( Color_StrongBlue, L"(GetElfName) Software version = " + parts.rvalue ); + Console.WriteLn( Color_Blue, L"(SYSTEM.CNF) Software version = " + parts.rvalue ); } } @@ -478,9 +478,9 @@ int GetPS2ElfName( wxString& name ) Console.Error(ex.FormatDiagnosticMessage()); return 0; // ISO error } - catch( Exception::FileNotFound& ex ) + catch( Exception::FileNotFound& ) { - Console.Warning(ex.FormatDiagnosticMessage()); + //Console.Warning(ex.FormatDiagnosticMessage()); return 0; // no SYSTEM.CNF, not a PS1/PS2 disc. } diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 7557f06aef..68f115007b 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -423,6 +423,12 @@ static void intThrowException( const BaseR5900Exception& ex ) ex.Rethrow(); } +static void intThrowException( const BaseException& ex ) +{ + // No tricks needed; C++ stack unwnding shoud suffice for MSW and GCC alike. + ex.Rethrow(); +} + R5900cpu intCpu = { intAlloc, @@ -434,5 +440,6 @@ R5900cpu intCpu = intCheckExecutionState, intThrowException, + intThrowException, intClear, }; diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 055449309e..708235a425 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -581,10 +581,10 @@ void __fastcall eeGameStarting() { if (!g_GameStarted) { - Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry ); + //Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry ); g_GameStarted = true; GetCoreThread().GameStartingInThread(); - + // GameStartingInThread may issue a reset of the cpu and/or recompilers. Check for and // handle such things here: Cpu->CheckExecutionState(); @@ -607,7 +607,7 @@ void __fastcall eeloadReplaceOSDSYS() else cdvdReloadElfInfo(); - // didn't recognise an ELF + // didn't recognize an ELF if (ElfEntry == -1) { eeGameStarting(); return; diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index 86232036f4..80891b92ff 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -279,7 +279,7 @@ struct R5900cpu // Can be called from any thread. Execute status must be suspended or stopped // to prevent multi-thread race conditions. // - // Notable Exception Throws: + // Exception Throws: // OutOfMemory - Not enough memory, or the memory areas required were already // reserved. // @@ -303,7 +303,7 @@ struct R5900cpu // Can be called from any thread. Execute status must be suspended or stopped // to prevent multi-thread race conditions. // - // Exception Throws: Emulator-defined. Common exception types to look for: + // Exception Throws: Emulator-defined. Common exception types to expect are // OutOfMemory, Stream Exceptions // void (*Reset)(); @@ -322,7 +322,9 @@ struct R5900cpu // call to return at the nearest state check (typically handled internally using // either C++ exceptions or setjmp/longjmp). // - // Exception Throws: [TODO] (possible execution-related throws to be added) + // Exception Throws: + // Throws BaseR5900Exception and all derivatives. + // Throws FileNotFound or other Streaming errors (typically related to BIOS MEC/NVM) // void (*Execute)(); @@ -333,23 +335,28 @@ struct R5900cpu // // Implementation note: Because of the nuances of recompiled code execution, setjmp // may be used in place of thread cancellation or C++ exceptions (non-SEH exceptions - // cannot unwind through the recompiled code stackframes). + // cannot unwind through the recompiled code stackframes, thus longjmp must be used). // // Thread Affinity: - // Must be called on the same thread as Execute only. + // Must be called on the same thread as Execute. // // Exception Throws: - // May throw threading/Pthreads cancellations if the compiler supports SEH. - // ThreadTimedOut - For canceling VM execution in response to MTGS deadlock. (if the - // core emulator does not support multithreaded GS then this will not be a throw - // exception). + // May throw Execution/Pthreads cancellations if the compiler supports SEH. // void (*CheckExecutionState)(); // Safely throws host exceptions from executing code (either recompiled or interpreted). // If this function is called outside the context of the CPU's code execution, then the // given exception will be re-thrown automatically. - void (*ThrowException)( const BaseR5900Exception& ex ); + // + // Exception Throws: + // (SEH) Rethrows the given exception immediately. + // (setjmp) Re-throws immediately if called from outside the context of dynamically + // generated code (either non-executing contexts or interpreters). Does not throw + // otherwise. + // + void (*ThrowException)( const BaseException& ex ); + void (*ThrowCpuException)( const BaseR5900Exception& ex ); // Manual recompiled code cache clear; typically useful to recompilers only. Size is // in MIPS words (32 bits). Dev note: this callback is nearly obsolete, and might be diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index 88018707b3..aa933601e0 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -260,6 +260,8 @@ void SysCoreThread::OnResumeInThread( bool isSuspended ) // Invoked by the pthread_exit or pthread_cancel. void SysCoreThread::OnCleanupInThread() { + m_ExecMode = ExecMode_Closing; + m_hasActiveMachine = false; m_resetVirtualMachine = true; @@ -269,5 +271,7 @@ void SysCoreThread::OnCleanupInThread() _mm_setcsr( m_mxcsr_saved.bitmask ); Threading::DisableHiresScheduler(); _parent::OnCleanupInThread(); + + m_ExecMode = ExecMode_NoThreadYet; } diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h index 427058d174..b686777269 100644 --- a/pcsx2/System/SysThreads.h +++ b/pcsx2/System/SysThreads.h @@ -95,13 +95,16 @@ public: } bool IsClosed() const { return !IsOpen(); } - bool IsPaused() const { return !IsRunning() || (m_ExecMode <= ExecMode_Paused); } + bool IsClosing() const + { + return !IsRunning() || (m_ExecMode <= ExecMode_Closed) || (m_ExecMode == ExecMode_Closing); + } + bool HasPendingStateChangeRequest() const { - ExecutionMode mode = m_ExecMode; - return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing); + return m_ExecMode >= ExecMode_Closing; } ExecutionMode GetExecutionMode() const { return m_ExecMode; } diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 5f8c435edf..7e4e2c4767 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -384,6 +384,7 @@ void AppCoreThread::OnSuspendInThread() // the new (lack of) thread status, so this posts a message to the App to do so. void AppCoreThread::OnCleanupInThread() { + m_ExecMode = ExecMode_Closing; PostCoreStatus( CoreThread_Stopped ); _parent::OnCleanupInThread(); } diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index 822cb7532a..2cc10d9c37 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -541,7 +541,7 @@ void MainEmuFrame::ApplyCoreStatus() if( susres ) { - if( CoreThread.IsOpen() ) + if( !CoreThread.IsClosing() ) { susres->Enable(); susres->SetText(_("Suspend")); diff --git a/pcsx2/vtlb.cpp b/pcsx2/vtlb.cpp index ae89e81940..7c2dbbf78c 100644 --- a/pcsx2/vtlb.cpp +++ b/pcsx2/vtlb.cpp @@ -233,7 +233,7 @@ void __fastcall vtlb_memWrite128(u32 mem, const mem128_t *value) static __forceinline void vtlb_Miss(u32 addr,u32 mode) { if( IsDevBuild ) - Cpu->ThrowException( R5900Exception::TLBMiss( addr, !!mode ) ); + Cpu->ThrowCpuException( R5900Exception::TLBMiss( addr, !!mode ) ); else Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() ); } @@ -247,7 +247,7 @@ static __forceinline void vtlb_BusError(u32 addr,u32 mode) // the PC prior to invoking the indirect handlers. if( IsDevBuild ) - Cpu->ThrowException( R5900Exception::BusError( addr, !!mode ) ); + Cpu->ThrowCpuException( R5900Exception::BusError( addr, !!mode ) ); else Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() ); } diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index 0dfa6b50d1..fe4510b4a5 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -63,8 +63,6 @@ bool g_cpuFlushedPC, g_cpuFlushedCode, g_recompilingDelaySlot, g_maySignalExcept #define X86 static const int RECCONSTBUF_SIZE = 16384 * 2; // 64 bit consts in 32 bit units -static ScopedPtr m_vmException; - static u8 *recMem = NULL; // the recompiled blocks will be here static u32* recConstBuf = NULL; // 64-bit pseudo-immediates static BASEBLOCK *recRAM = NULL; // and the ptr to the blocks here @@ -714,7 +712,15 @@ void recStep( void ) { } -static jmp_buf m_SetJmp_StateCheck; +#if !PCSX2_SEH +# define SETJMP_CODE(x) x + static jmp_buf m_SetJmp_StateCheck; + static ScopedPtr m_cpuException; + static ScopedPtr m_Exception; +#else +# define SETJMP_CODE(x) +#endif + static void recExitExecution() { @@ -732,7 +738,7 @@ static void recExitExecution() static void recCheckExecutionState() { - if( eeRecIsReset || m_vmException || GetCoreThread().HasPendingStateChangeRequest() ) + if( SETJMP_CODE(m_cpuException || m_Exception ||) eeRecIsReset || GetCoreThread().HasPendingStateChangeRequest() ) { recExitExecution(); } @@ -747,7 +753,6 @@ static void recExecute() #if PCSX2_SEH eeRecIsReset = false; - m_vmException = NULL; ScopedBool executing(m_recExecutingCode); try { @@ -758,6 +763,8 @@ static void recExecute() #else int oldstate; + m_cpuException = NULL; + m_Exception = NULL; if( !setjmp( m_SetJmp_StateCheck ) ) { @@ -777,9 +784,10 @@ static void recExecute() { pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); } -#endif - if(m_vmException) m_vmException->Rethrow(); + if(m_cpuException) m_cpuException->Rethrow(); + if(m_Exception) m_Exception->Rethrow(); +#endif } //////////////////////////////////////////////////// @@ -1838,12 +1846,25 @@ StartRecomp: // SEH unwind (MSW) or setjmp/longjmp (GCC). static void recThrowException( const BaseR5900Exception& ex ) { +#if PCSX2_SEH + ex.Rethrow(); +#else if (!m_recExecutingCode) ex.Rethrow(); - - m_vmException = ex.Clone(); + m_cpuException = ex.Clone(); recExitExecution(); +#endif } +static void recThrowException( const BaseException& ex ) +{ +#if PCSX2_SEH + ex.Rethrow(); +#else + if (!m_recExecutingCode) ex.Rethrow(); + m_Exception = ex.Clone(); + recExitExecution(); +#endif +} R5900cpu recCpu = { @@ -1856,5 +1877,6 @@ R5900cpu recCpu = recCheckExecutionState, recThrowException, + recThrowException, recClear, };