* Fix for Issue 493 -- non-standard SYSTEM.CNF contents are handled better now.

* Some minor exception/error handling fixes and improvements.

DevNote: the BOOT2 elf loader fix is still a hackfix.  I documented the proper fix for mimicking PS2 BOOT2 parsing, but not in a mood to do the full proper implementation right now.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3442 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-07-09 16:51:48 +00:00
parent 9d5a1b44ba
commit d36bb19612
11 changed files with 145 additions and 78 deletions

View File

@ -306,17 +306,34 @@ s32 cdvdWriteConfig(const u8* config)
static MutexRecursive Mutex_NewDiskCB; static MutexRecursive Mutex_NewDiskCB;
// Sets ElfCRC to the CRC of the game bound to the CDVD plugin. // 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")) if (filename.StartsWith(L"host"))
return new ElfObject(filename.After(':'), Path::GetFileSize(filename.After(':'))); 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; IsoFSCDVD isofs;
IsoFile file(isofs, filename); IsoFile file(isofs, filename);
ElfObject *elfptr; return new ElfObject(filename, file);
elfptr = new ElfObject(filename, file);
return elfptr;
} }
static __forceinline void _reloadElfInfo(wxString elfpath) static __forceinline void _reloadElfInfo(wxString elfpath)
@ -339,7 +356,6 @@ static __forceinline void _reloadElfInfo(wxString elfpath)
if (fname.Matches(L"????_???.??*")) if (fname.Matches(L"????_???.??*"))
DiscSerial = fname(0,4) + L"-" + fname(5,3) + fname(9,2); DiscSerial = fname(0,4) + L"-" + fname(5,3) + fname(9,2);
Console.WriteLn("Disc ID = %s", DiscSerial.ToUTF8().data());
elfptr = loadElf(elfpath); elfptr = loadElf(elfpath);
ElfCRC = elfptr->getCRC(); ElfCRC = elfptr->getCRC();
@ -348,28 +364,21 @@ static __forceinline void _reloadElfInfo(wxString elfpath)
ElfEntry = elfptr->header.e_entry; ElfEntry = elfptr->header.e_entry;
Console.WriteLn("Entry point = 0x%08x", ElfEntry); Console.WriteLn("Entry point = 0x%08x", ElfEntry);
elfptr.Delete(); // 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
// Set the Game DataBase to the correct game based on Game Serial Code... // BIOS code, and patches and cheats should not be applied yet. (they are applied when
if (IGameDatabase* GameDB = AppHost_GetGameDatabase()) { // eeGameStarting is invoked, which is when the VM starts executing the actual game ELF
wxString gameSerial( SysGetDiscID() ); // binary).
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 );
}
} }
void cdvdReloadElfInfo(wxString elfoverride) void cdvdReloadElfInfo(wxString elfoverride)
{ {
// 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
{
if (!elfoverride.IsEmpty()) if (!elfoverride.IsEmpty())
{ {
_reloadElfInfo(elfoverride); _reloadElfInfo(elfoverride);
@ -379,16 +388,30 @@ void cdvdReloadElfInfo(wxString elfoverride)
wxString elfpath; wxString elfpath;
u32 discType = GetPS2ElfName(elfpath); u32 discType = GetPS2ElfName(elfpath);
switch (discType) if(discType==1)
{ {
case 2: // Is a PS2 disc. // 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); _reloadElfInfo(elfpath);
break; }
case 1: // Is a PS1 disc. catch (Exception::FileNotFound& e)
if (ENABLE_LOADING_PS1_GAMES) _reloadElfInfo(elfpath); {
break; pxFail( "Not in my back yard!" );
default: // Isn't a disc we recognise. Cpu->ThrowException(e);
break;
} }
} }

View File

@ -434,13 +434,13 @@ int GetPS2ElfName( wxString& name )
while( !file.eof() ) while( !file.eof() )
{ {
wxString original( fromUTF8(file.readLine().c_str()) ); const wxString original( fromUTF8(file.readLine().c_str()) );
ParsedAssignmentString parts( original ); const ParsedAssignmentString parts( original );
if( parts.lvalue.IsEmpty() && parts.rvalue.IsEmpty() ) continue; if( parts.lvalue.IsEmpty() && parts.rvalue.IsEmpty() ) continue;
if( parts.rvalue.IsEmpty() ) 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 ); Console.Indent().WriteLn( original );
continue; continue;
} }
@ -448,22 +448,22 @@ int GetPS2ElfName( wxString& name )
if( parts.lvalue == L"BOOT2" ) if( parts.lvalue == L"BOOT2" )
{ {
name = parts.rvalue; 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; retype = 2;
} }
else if( parts.lvalue == L"BOOT" ) else if( parts.lvalue == L"BOOT" )
{ {
name = parts.rvalue; 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; retype = 1;
} }
else if( parts.lvalue == L"VMODE" ) 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" ) 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()); Console.Error(ex.FormatDiagnosticMessage());
return 0; // ISO error 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. return 0; // no SYSTEM.CNF, not a PS1/PS2 disc.
} }

View File

@ -423,6 +423,12 @@ static void intThrowException( const BaseR5900Exception& ex )
ex.Rethrow(); 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 = R5900cpu intCpu =
{ {
intAlloc, intAlloc,
@ -434,5 +440,6 @@ R5900cpu intCpu =
intCheckExecutionState, intCheckExecutionState,
intThrowException, intThrowException,
intThrowException,
intClear, intClear,
}; };

View File

@ -581,7 +581,7 @@ void __fastcall eeGameStarting()
{ {
if (!g_GameStarted) 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; g_GameStarted = true;
GetCoreThread().GameStartingInThread(); GetCoreThread().GameStartingInThread();
@ -607,7 +607,7 @@ void __fastcall eeloadReplaceOSDSYS()
else else
cdvdReloadElfInfo(); cdvdReloadElfInfo();
// didn't recognise an ELF // didn't recognize an ELF
if (ElfEntry == -1) { if (ElfEntry == -1) {
eeGameStarting(); eeGameStarting();
return; return;

View File

@ -279,7 +279,7 @@ struct R5900cpu
// Can be called from any thread. Execute status must be suspended or stopped // Can be called from any thread. Execute status must be suspended or stopped
// to prevent multi-thread race conditions. // to prevent multi-thread race conditions.
// //
// Notable Exception Throws: // Exception Throws:
// OutOfMemory - Not enough memory, or the memory areas required were already // OutOfMemory - Not enough memory, or the memory areas required were already
// reserved. // reserved.
// //
@ -303,7 +303,7 @@ struct R5900cpu
// Can be called from any thread. Execute status must be suspended or stopped // Can be called from any thread. Execute status must be suspended or stopped
// to prevent multi-thread race conditions. // 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 // OutOfMemory, Stream Exceptions
// //
void (*Reset)(); void (*Reset)();
@ -322,7 +322,9 @@ struct R5900cpu
// call to return at the nearest state check (typically handled internally using // call to return at the nearest state check (typically handled internally using
// either C++ exceptions or setjmp/longjmp). // 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)(); void (*Execute)();
@ -333,23 +335,28 @@ struct R5900cpu
// //
// Implementation note: Because of the nuances of recompiled code execution, setjmp // 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 // 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: // Thread Affinity:
// Must be called on the same thread as Execute only. // Must be called on the same thread as Execute.
// //
// Exception Throws: // Exception Throws:
// May throw threading/Pthreads cancellations if the compiler supports SEH. // May throw Execution/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).
// //
void (*CheckExecutionState)(); void (*CheckExecutionState)();
// Safely throws host exceptions from executing code (either recompiled or interpreted). // 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 // If this function is called outside the context of the CPU's code execution, then the
// given exception will be re-thrown automatically. // 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 // 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 // in MIPS words (32 bits). Dev note: this callback is nearly obsolete, and might be

View File

@ -260,6 +260,8 @@ void SysCoreThread::OnResumeInThread( bool isSuspended )
// Invoked by the pthread_exit or pthread_cancel. // Invoked by the pthread_exit or pthread_cancel.
void SysCoreThread::OnCleanupInThread() void SysCoreThread::OnCleanupInThread()
{ {
m_ExecMode = ExecMode_Closing;
m_hasActiveMachine = false; m_hasActiveMachine = false;
m_resetVirtualMachine = true; m_resetVirtualMachine = true;
@ -269,5 +271,7 @@ void SysCoreThread::OnCleanupInThread()
_mm_setcsr( m_mxcsr_saved.bitmask ); _mm_setcsr( m_mxcsr_saved.bitmask );
Threading::DisableHiresScheduler(); Threading::DisableHiresScheduler();
_parent::OnCleanupInThread(); _parent::OnCleanupInThread();
m_ExecMode = ExecMode_NoThreadYet;
} }

View File

@ -95,13 +95,16 @@ public:
} }
bool IsClosed() const { return !IsOpen(); } bool IsClosed() const { return !IsOpen(); }
bool IsPaused() const { return !IsRunning() || (m_ExecMode <= ExecMode_Paused); } 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 bool HasPendingStateChangeRequest() const
{ {
ExecutionMode mode = m_ExecMode; return m_ExecMode >= ExecMode_Closing;
return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing);
} }
ExecutionMode GetExecutionMode() const { return m_ExecMode; } ExecutionMode GetExecutionMode() const { return m_ExecMode; }

View File

@ -384,6 +384,7 @@ void AppCoreThread::OnSuspendInThread()
// the new (lack of) thread status, so this posts a message to the App to do so. // the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::OnCleanupInThread() void AppCoreThread::OnCleanupInThread()
{ {
m_ExecMode = ExecMode_Closing;
PostCoreStatus( CoreThread_Stopped ); PostCoreStatus( CoreThread_Stopped );
_parent::OnCleanupInThread(); _parent::OnCleanupInThread();
} }

View File

@ -541,7 +541,7 @@ void MainEmuFrame::ApplyCoreStatus()
if( susres ) if( susres )
{ {
if( CoreThread.IsOpen() ) if( !CoreThread.IsClosing() )
{ {
susres->Enable(); susres->Enable();
susres->SetText(_("Suspend")); susres->SetText(_("Suspend"));

View File

@ -233,7 +233,7 @@ void __fastcall vtlb_memWrite128(u32 mem, const mem128_t *value)
static __forceinline void vtlb_Miss(u32 addr,u32 mode) static __forceinline void vtlb_Miss(u32 addr,u32 mode)
{ {
if( IsDevBuild ) if( IsDevBuild )
Cpu->ThrowException( R5900Exception::TLBMiss( addr, !!mode ) ); Cpu->ThrowCpuException( R5900Exception::TLBMiss( addr, !!mode ) );
else else
Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() ); 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. // the PC prior to invoking the indirect handlers.
if( IsDevBuild ) if( IsDevBuild )
Cpu->ThrowException( R5900Exception::BusError( addr, !!mode ) ); Cpu->ThrowCpuException( R5900Exception::BusError( addr, !!mode ) );
else else
Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() ); Console.Error( R5900Exception::TLBMiss( addr, !!mode ).FormatMessage() );
} }

View File

@ -63,8 +63,6 @@ bool g_cpuFlushedPC, g_cpuFlushedCode, g_recompilingDelaySlot, g_maySignalExcept
#define X86 #define X86
static const int RECCONSTBUF_SIZE = 16384 * 2; // 64 bit consts in 32 bit units static const int RECCONSTBUF_SIZE = 16384 * 2; // 64 bit consts in 32 bit units
static ScopedPtr<BaseR5900Exception> m_vmException;
static u8 *recMem = NULL; // the recompiled blocks will be here static u8 *recMem = NULL; // the recompiled blocks will be here
static u32* recConstBuf = NULL; // 64-bit pseudo-immediates static u32* recConstBuf = NULL; // 64-bit pseudo-immediates
static BASEBLOCK *recRAM = NULL; // and the ptr to the blocks here 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<BaseR5900Exception> m_cpuException;
static ScopedPtr<BaseException> m_Exception;
#else
# define SETJMP_CODE(x)
#endif
static void recExitExecution() static void recExitExecution()
{ {
@ -732,7 +738,7 @@ static void recExitExecution()
static void recCheckExecutionState() static void recCheckExecutionState()
{ {
if( eeRecIsReset || m_vmException || GetCoreThread().HasPendingStateChangeRequest() ) if( SETJMP_CODE(m_cpuException || m_Exception ||) eeRecIsReset || GetCoreThread().HasPendingStateChangeRequest() )
{ {
recExitExecution(); recExitExecution();
} }
@ -747,7 +753,6 @@ static void recExecute()
#if PCSX2_SEH #if PCSX2_SEH
eeRecIsReset = false; eeRecIsReset = false;
m_vmException = NULL;
ScopedBool executing(m_recExecutingCode); ScopedBool executing(m_recExecutingCode);
try { try {
@ -758,6 +763,8 @@ static void recExecute()
#else #else
int oldstate; int oldstate;
m_cpuException = NULL;
m_Exception = NULL;
if( !setjmp( m_SetJmp_StateCheck ) ) if( !setjmp( m_SetJmp_StateCheck ) )
{ {
@ -777,9 +784,10 @@ static void recExecute()
{ {
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); 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). // SEH unwind (MSW) or setjmp/longjmp (GCC).
static void recThrowException( const BaseR5900Exception& ex ) static void recThrowException( const BaseR5900Exception& ex )
{ {
#if PCSX2_SEH
ex.Rethrow();
#else
if (!m_recExecutingCode) ex.Rethrow(); if (!m_recExecutingCode) ex.Rethrow();
m_cpuException = ex.Clone();
m_vmException = ex.Clone();
recExitExecution(); 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 = R5900cpu recCpu =
{ {
@ -1856,5 +1877,6 @@ R5900cpu recCpu =
recCheckExecutionState, recCheckExecutionState,
recThrowException, recThrowException,
recThrowException,
recClear, recClear,
}; };