New less intrusive BIOS skip hack replacing the old BIOS skip hack. Works with Ico, Tekken 5, and probably any other games that were problematic with the old hack.

Changes to patch system required by this and fix for boot time (first field = 0) patches when booting through BIOS.
Store Sony part number for the game disc globally for future use in selecting game fixes, patches, providing information to the user, etc.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2874 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
sudonim1 2010-04-19 23:46:12 +00:00
parent d857fec233
commit 36c9d63495
11 changed files with 201 additions and 254 deletions

View File

@ -28,6 +28,8 @@
#include "Elfheader.h"
#include "ps2/BiosTools.h"
wxString DiscID;
static cdvdStruct cdvd;
static __forceinline void SetResultSize(u8 size)
@ -306,7 +308,7 @@ s32 cdvdWriteConfig(const u8* config)
static MutexLockRecursive Mutex_NewDiskCB;
// Sets ElfCRC to the CRC of the game bound to the CDVD plugin.
static __forceinline ElfObject *loadElfCRC( const wxString filename )
static __forceinline ElfObject *loadElf( const wxString filename )
{
// Note: calling loadElfFile here causes bad things to happen.
IsoFSCDVD isofs;
@ -314,35 +316,51 @@ static __forceinline ElfObject *loadElfCRC( const wxString filename )
ElfObject *elfptr;
elfptr = new ElfObject(filename, file);
elfptr->getCRC();
Console.WriteLn(wxsFormat(L"loadElfCRC(" + filename + L") = %8.8X", ElfCRC));
return elfptr;
}
static __forceinline void _reloadElfInfo(wxString str)
static __forceinline void _reloadElfInfo(wxString elfpath)
{
ScopedPtr<ElfObject> elfptr;
// Now's a good time to reload the ELF info...
ScopedLock locker( Mutex_NewDiskCB );
elfptr = loadElfCRC(str);
elfptr->applyPatches();
wxString fname = elfpath.AfterLast('\\');
if (!fname)
fname = elfpath.AfterLast('/');
if (!fname)
fname = elfpath.AfterLast(':');
if (fname.Matches(L"????_???.??*"))
DiscID = fname(0,4) + L"-" + fname(5,3) + fname(9,2);
Console.WriteLn("Disc ID = %s", DiscID.ToUTF8());
elfptr = loadElf(elfpath);
ElfCRC = elfptr->getCRC();
Console.WriteLn("ELF (%s) CRC = %8.8X", elfpath.ToUTF8(), ElfCRC);
ElfEntry = elfptr->header.e_entry;
Console.WriteLn("Entry point = 0x%08x", ElfEntry);
elfptr.Delete();
}
static __forceinline void reloadElfInfo(u32 discType, wxString str)
void cdvdReloadElfInfo()
{
wxString elfpath;
u32 discType = GetPS2ElfName(elfpath);
if (ElfCRC == 0)
{
switch (discType)
{
case 2: // Is a PS2 disc.
_reloadElfInfo(str);
_reloadElfInfo(elfpath);
break;
case 1: // Is a PS1 disc.
if (ENABLE_LOADING_PS1_GAMES) _reloadElfInfo(str);
if (ENABLE_LOADING_PS1_GAMES) _reloadElfInfo(elfpath);
break;
default: // Isn't a disc we recognise.
break;
@ -363,23 +381,17 @@ void cdvdReadKey(u8 arg0, u16 arg1, u32 arg2, u8* key)
u32 key_0_3;
u8 key_4, key_14;
wxString fname, exeName;
// Get the main elf name.
u32 discType = GetPS2ElfName(fname);
exeName = fname(8, 11);
DevCon.Warning(L"exeName = " + exeName);
cdvdReloadElfInfo();
// convert the number characters to a real 32 bit number
numbers = StrToS32(exeName(5,3) + exeName(9,2));
numbers = StrToS32(DiscID(5,5));
// combine the lower 7 bits of each char
// to make the 4 letters fit into a single u32
letters = (s32)((exeName[3]&0x7F)<< 0) |
(s32)((exeName[2]&0x7F)<< 7) |
(s32)((exeName[1]&0x7F)<<14) |
(s32)((exeName[0]&0x7F)<<21);
letters = (s32)((DiscID[3]&0x7F)<< 0) |
(s32)((DiscID[2]&0x7F)<< 7) |
(s32)((DiscID[1]&0x7F)<<14) |
(s32)((DiscID[0]&0x7F)<<21);
// calculate magic numbers
key_0_3 = ((numbers & 0x1FC00) >> 10) | ((0x01FFFFFF & letters) << 7); // numbers = 7F letters = FFFFFF80
@ -424,8 +436,6 @@ void cdvdReadKey(u8 arg0, u16 arg1, u32 arg2, u8* key)
Console.WriteLn( "CDVD.KEY = %02X,%02X,%02X,%02X,%02X,%02X,%02X",
cdvd.Key[0],cdvd.Key[1],cdvd.Key[2],cdvd.Key[3],cdvd.Key[4],cdvd.Key[14],cdvd.Key[15] );
reloadElfInfo(discType, fname);
}
s32 cdvdGetToc(void* toc)
@ -551,8 +561,7 @@ static void cdvdDetectDisk()
{
wxString str;
cdvd.Type = DoCDVDdetectDiskType();
reloadElfInfo(GetPS2ElfName(str), str);
cdvdReloadElfInfo();
}
void cdvdNewDiskCB()

View File

@ -142,3 +142,7 @@ extern void cdvdReadInterrupt();
extern void cdvdNewDiskCB();
extern u8 cdvdRead(u8 key);
extern void cdvdWrite(u8 key, u8 rt);
void cdvdReloadElfInfo();
extern wxString DiscID;

View File

@ -20,9 +20,9 @@
#include "Elfheader.h"
using namespace std;
extern void InitPatch(const wxString& crc);
u32 ElfCRC;
u32 ElfEntry;
// uncomment this to enable pcsx2hostfs loading when using "load elf"
//#define USE_HOSTFS
@ -274,7 +274,7 @@ void ElfObject::checkElfSize(s64 elfsize)
throw Exception::BadStream( filename, wxLt("Unexpected end of ELF file: ") );
}
void ElfObject::getCRC()
u32 ElfObject::getCRC()
{
u32 CRC = 0;
@ -282,7 +282,7 @@ void ElfObject::getCRC()
for(u32 i=data.GetSizeInBytes()/4; i; --i, ++srcdata)
CRC ^= *srcdata;
ElfCRC = CRC;
return CRC;
}
void ElfObject::loadProgramHeaders()
@ -418,17 +418,6 @@ void ElfObject::loadHeaders()
loadSectionHeaders();
}
void ElfObject::applyPatches()
{
wxString filename( wxsFormat( L"%8.8x", ElfCRC ) );
// if patches found the following status msg will be overwritten
Console.SetTitle(L"Game running [CRC=" + filename +L"]");
if (EmuConfig.EnablePatches) InitPatch(filename);
GetMTGS().SendGameCRC(ElfCRC);
}
// Loads the elf binary data from the specified file into PS2 memory, and injects the ELF's
// starting execution point into cpuRegs.pc. If the filename is a cdrom URI in the form
// of "cdrom0:" or "cdrom1:" then the CDVD is used as the source; otherwise the ELF is loaded
@ -448,7 +437,6 @@ void loadElfFile(const wxString& filename)
#endif
{
ScopedPtr<ElfObject> elfptr;
bool iscdvd;
#ifdef USE_HOSTFS
wxString filename = _filename;
@ -459,53 +447,36 @@ void loadElfFile(const wxString& filename)
Console.WriteLn( L"loadElfFile: " + filename );
if (filename.StartsWith(L"cdrom:") && !ENABLE_LOADING_PS1_GAMES)
throw Exception::RuntimeError( wxLt("This is not a Ps2 disc. (And we don't currently emulate PS1 games)") );
iscdvd = (filename.StartsWith(L"cdrom:") || filename.StartsWith(L"cdrom0:") || filename.StartsWith(L"cdrom1:"));
if (iscdvd)
{
// It's a game disc.
DevCon.WriteLn(L"Loading from a CD rom or CD image.");
IsoFSCDVD isofs;
IsoFile file(isofs, filename);
elfptr = new ElfObject(filename, file);
}
else
{
// It's an elf file.
DevCon.WriteLn("Loading from a file (or non-cd image).");
#ifdef USE_HOSTFS
parameters = filename;
filename = wxT("pcsx2hostfs_ldr.elf");
parameters = filename;
filename = wxT("pcsx2hostfs_ldr.elf");
#endif
elfptr = new ElfObject(filename, Path::GetFileSize(filename));
elfptr = new ElfObject(filename, Path::GetFileSize(filename));
#ifdef USE_HOSTFS
filename = wxT("host:pcsx2hostfs_ldr.elf");
filename = wxT("host:pcsx2hostfs_ldr.elf");
#endif
}
if (!elfptr->hasProgramHeaders())
{
throw Exception::BadStream( filename, elfptr->isCdvd ?
wxLt("Invalid ELF file header. The CD-Rom may be damaged, or the ISO image corrupted.") :
wxLt("Invalid ELF file.")
);
throw Exception::BadStream(filename, wxLt("Invalid ELF file."));
}
elfptr->loadHeaders();
ElfCRC = elfptr->getCRC();
Console.WriteLn( L"loadElfFile: %s; CRC = %8.8X", filename.c_str(), ElfCRC );
ElfEntry = elfptr->header.e_entry;
elfptr.Delete();
//2002-09-19 (Florin)
//args_ptr = 0xFFFFFFFF; //big value, searching for minimum [used by parseCommandLine]
elfptr->loadHeaders();
cpuRegs.pc = elfptr->header.e_entry; //set pc to proper place
cpuRegs.pc = ElfEntry; //set pc to proper place
ELF_LOG( "PC set to: %8.8lx", cpuRegs.pc );
cpuRegs.GPR.n.sp.UL[0] = 0x81f00000;
cpuRegs.GPR.n.gp.UL[0] = 0x81f80000; // might not be 100% ok
//cpuRegs.GPR.n.a0.UL[0] = parseCommandLine( filename ); // see #ifdef'd out parseCommendLine for details.
#ifdef USE_HOSTFS
//HACK!!!!!!!!!!!!!
@ -524,30 +495,6 @@ void loadElfFile(const wxString& filename)
#endif
for( uint i = 0; i < 0x100000; i++ )
{
if( memcmp( "rom0:OSDSYS", (char*)PSM( i ), 11 ) == 0 )
{
// All right, this is not likely to be right. It's fine if it is a cd, but
// We really do not want to pass the elf file with full path and drive letter
// into pcsx2 memory, like this.
strcpy((char*)PSM(i), filename.ToUTF8());
// We could test and only do it if (iscdvd) is true, or we could do something
// like this if it isn't a cdvd:
// strcpy((char*)PSM(i), wxsFormat(L"rom0:"+Path::GetFilename(filename)).ToUTF8());
// Though the question is what device name to pass if we do that...
// Not sure, so I'm leaving it the known incorrect way for now. --arcum42
DevCon.WriteLn( wxsFormat(L"loadElfFile: addr %x \"rom0:OSDSYS\" -> \"" + filename + L"\"", i));
}
}
elfptr->getCRC();
Console.WriteLn( L"loadElfFile: %s; CRC = %8.8X", filename.c_str(), ElfCRC );
elfptr->applyPatches();
elfptr.Delete();
return;
}
@ -608,6 +555,10 @@ int GetPS2ElfName( wxString& name )
return 0;
}
}
catch (Exception::BadStream&)
{
return 0; // ISO error
}
catch( Exception::FileNotFound& )
{
return 0; // no SYSTEM.CNF, not a PS1/PS2 disc.

View File

@ -155,8 +155,7 @@ class ElfObject
bool hasSectionHeaders();
bool hasHeaders();
void getCRC();
void applyPatches();
u32 getCRC();
};
//-------------------
@ -165,5 +164,6 @@ extern int GetPS2ElfName( wxString& dest );
extern u32 ElfCRC;
extern u32 ElfEntry;
#endif

View File

@ -20,6 +20,8 @@
#include "R5900OpcodeTables.h"
#include "System/SysThreads.h"
#include "ElfHeader.h"
#include <float.h>
using namespace R5900; // for OPCODE and OpcodeImpl
@ -383,39 +385,22 @@ static void intExecute()
// done in a more optimized fashion.
try {
while( true )
execI();
} catch( Exception::ExitCpuExecute& ) { }
}
static void intExecuteBiosStub()
{
g_EEFreezeRegs = false;
// We need to be wary of events that could occur during vsyncs, which means
// making sure to exit this function for ExitCpuExecute. The calling function
// will update UI status, and then re-enter if the bios stub execution criteria
// wasn't met yet.
try {
while( (cpuRegs.pc != 0x00200008) && (cpuRegs.pc != 0x00100008) ) {
execI();
if (g_SkipBiosHack) {
do
execI();
while (cpuRegs.pc != EELOAD_START);
eeloadReplaceOSDSYS();
}
if (ElfEntry != -1) {
do
execI();
while (cpuRegs.pc != ElfEntry);
eeGameStarting();
} else {
while (true)
execI();
}
} catch( Exception::ExitCpuExecute& ) { }
// ... some maual bios injection hack from a century ago, me thinks. Leaving the
// code intact for posterity. --air
// {
// FILE* f = fopen("eebios.bin", "wb");
// fwrite(PSM(0x80000000), 0x100000, 1, f);
// fclose(f);
// exit(0);
// f = fopen("iopbios.bin", "wb");
// fwrite(PS2MEM_PSX, 0x80000, 1, f);
// fclose(f);
// }
}
static void intCheckExecutionState()
@ -445,7 +430,6 @@ R5900cpu intCpu =
intReset,
intStep,
intExecute,
intExecuteBiosStub,
intCheckExecutionState,
intClear,

View File

@ -465,19 +465,25 @@ void _ApplyPatch(IniPatch *p)
switch (p->type)
{
case BYTE_T:
memWrite8(p->addr, (u8)p->data);
if (memRead8(p->addr) != (u8)p->data)
memWrite8(p->addr, (u8)p->data);
break;
case SHORT_T:
memWrite16(p->addr, (u16)p->data);
if (memRead16(p->addr) != (u16)p->data)
memWrite16(p->addr, (u16)p->data);
break;
case WORD_T:
memWrite32(p->addr, (u32)p->data);
if (memRead32(p->addr) != (u32)p->data)
memWrite32(p->addr, (u32)p->data);
break;
case DOUBLE_T:
memWrite64(p->addr, &p->data);
u64 mem;
memRead64(p->addr, &mem);
if (mem != p->data)
memWrite64(p->addr, &p->data);
break;
case EXTENDED_T:
@ -493,13 +499,16 @@ void _ApplyPatch(IniPatch *p)
switch (p->type)
{
case BYTE_T:
iopMemWrite8(p->addr, (u8)p->data);
if (iopMemRead8(p->addr) != (u8)p->data)
iopMemWrite8(p->addr, (u8)p->data);
break;
case SHORT_T:
iopMemWrite16(p->addr, (u16)p->data);
if (iopMemRead16(p->addr) != (u16)p->data)
iopMemWrite16(p->addr, (u16)p->data);
break;
case WORD_T:
iopMemWrite32(p->addr, (u32)p->data);
if (iopMemRead32(p->addr) != (u32)p->data)
iopMemWrite32(p->addr, (u32)p->data);
break;
default:
break;
@ -525,7 +534,6 @@ void InitPatch(const wxString& crc)
{
inifile_read(crc);
Console.WriteLn("patchnumber: %d", patchnumber);
ApplyPatch(0);
}
void ResetPatch( void )

View File

@ -60,6 +60,7 @@ void inifile_read( const wxString& name );
void inifile_command( const wxString& cmd );
void inifile_trim( wxString& buffer );
void InitPatch(const wxString& crc);
int AddPatch(int Mode, int Place, int Address, int Size, u64 data);
void ApplyPatch( int place = 1);
void ResetPatch( void );

View File

@ -27,6 +27,10 @@
#include "Hardware.h"
#include "ElfHeader.h"
#include "CDVD/CDVD.h"
#include "Patch.h"
using namespace R5900; // for R5900 disasm tools
s32 EEsCycle; // used to sync the IOP to the EE
@ -37,7 +41,8 @@ __aligned16 fpuRegisters fpuRegs;
__aligned16 tlbs tlb[48];
R5900cpu *Cpu = NULL;
bool g_ExecBiosHack = false; // set if the BIOS has already been executed
bool g_SkipBiosHack; // set at boot if the skip bios hack is on, reset before the game has started
bool g_GameStarted; // set when we reach the game's entry point or earlier if the entry point cannot be determined
static const uint eeWaitCycles = 3072;
@ -75,6 +80,13 @@ void cpuReset()
vif1Reset();
rcntInit();
psxReset();
g_GameStarted = false;
g_SkipBiosHack = EmuConfig.SkipBiosSplash;
ElfCRC = 0;
DiscID = L"";
ElfEntry = -1;
}
__releaseinline void cpuException(u32 code, u32 bd)
@ -555,4 +567,70 @@ __forceinline void CPU_INT( u32 n, s32 ecycle)
}
cpuSetNextBranchDelta( cpuRegs.eCycle[n] );
}
}
void __fastcall eeGameStarting()
{
if (!g_GameStarted && ElfCRC) {
wxString filename( wxsFormat( L"%8.8x", ElfCRC ) );
// if patches found the following status msg will be overwritten
Console.SetTitle(L"Game running [CRC=" + filename +L"]");
if (EmuConfig.EnablePatches) InitPatch(filename);
GetMTGS().SendGameCRC(ElfCRC);
g_GameStarted = true;
}
ApplyPatch(0);
}
void __fastcall eeloadReplaceOSDSYS()
{
g_SkipBiosHack = false;
const wxString &elf_override = GetCoreThread().GetElfOverride();
if (!elf_override.IsEmpty()) {
loadElfFile(elf_override);
return;
}
cdvdReloadElfInfo();
// didn't recognise an ELF
if (ElfEntry == -1) {
eeGameStarting();
return;
}
static u32 osdsys = 0, osdsys_p = 0;
// Memory this high is safe before the game's running presumably
// Other options are kernel memory (first megabyte) or the scratchpad
// PS2LOGO is loaded at 16MB, let's use 17MB
const u32 safemem = 0x1100000;
// The strings are all 64-bit aligned. Why? I don't know, but they are
for (u32 i = EELOAD_START; i < EELOAD_START + EELOAD_SIZE; i += 8) {
if (!strcmp((char*)PSM(i), "rom0:OSDSYS")) {
osdsys = i;
break;
}
}
pxAssert(osdsys);
for (u32 i = osdsys - 4; i >= EELOAD_START; i -= 4) {
if (memRead32(i) == osdsys) {
osdsys_p = i;
break;
}
}
pxAssert(osdsys_p);
wxString elfname;
if (GetPS2ElfName(elfname) == 2) {
strcpy((char*)PSM(safemem), elfname.ToUTF8());
memWrite32(osdsys_p, safemem);
}
}

View File

@ -25,7 +25,8 @@
// right now, so we're sticking them here for now until a better solution comes along.
extern bool g_EEFreezeRegs;
extern bool g_ExecBiosHack;
extern bool g_SkipBiosHack;
extern bool g_GameStarted;
namespace Exception
{
@ -255,6 +256,15 @@ void intSetBranch();
// parts of the Recs (namely COP0's branch codes and stuff).
void __fastcall intDoBranch(u32 target);
// modules loaded at hardcoded addresses by the kernel
const u32 EEKERNEL_START = 0;
const u32 EENULL_START = 0x81FC0;
const u32 EELOAD_START = 0x82000;
const u32 EELOAD_SIZE = 0x20000; // overestimate for searching
void __fastcall eeGameStarting();
void __fastcall eeloadReplaceOSDSYS();
////////////////////////////////////////////////////////////////////
// R5900 Public Interface / API
//
@ -319,20 +329,6 @@ struct R5900cpu
//
void (*Execute)();
// This function performs a "hackish" execution of the BIOS stub, which initializes
// EE memory and hardware. It forcefully breaks execution when the stub is finished,
// prior to the PS2 logos being displayed. This allows us to "shortcut" right into
// a game without having to wait through the logos or endure game/bios localization
// checks.
//
// Use of this function must be followed by the proper injection of the elf header's
// code execution entry point into cpuRegs.pc. Failure to modify cpuRegs.pc will
// result in the bios continuing its normal unimpeded splash screen execution.
//
// Exception Throws: [TODO] (possible execution-related throws to be added)
//
void (*ExecuteBiosStub)();
// Checks for execution suspension or cancellation. In pthreads terms this provides
// a "cancellation point." Execution state checks are typically performed at Vsyncs
// by the generic VM event handlers in R5900.cpp/Counters.cpp (applies to both recs

View File

@ -273,57 +273,6 @@ void SysCoreThread::CpuInitializeMess()
ScopedBool_ClearOnError sbcoe( m_hasValidState );
wxString elf_file( m_elf_override );
if( elf_file.IsEmpty() && EmuConfig.SkipBiosSplash && (CDVDsys_GetSourceType() != CDVDsrc_NoDisc))
{
// Fetch the ELF filename and CD type from the CDVD provider.
wxString ename;
int result = GetPS2ElfName( ename );
switch( result )
{
case 0:
throw Exception::RuntimeError( wxLt("Fast Boot failed: CDVD image is not a PS1 or PS2 game.") );
case 1:
if (!ENABLE_LOADING_PS1_GAMES)
throw Exception::RuntimeError( wxLt("Fast Boot failed: PCSX2 does not support emulation of PS1 games.") );
case 2:
// PS2 game. Valid!
elf_file = ename;
break;
jNO_DEFAULT
}
}
if( !elf_file.IsEmpty() )
{
// Skip Bios Hack *or* Manual ELF override:
// Runs the PS2 BIOS stub, and then manually loads the ELF executable data, and
// injects the cpuRegs.pc with the address of the execution start point.
//
// This hack is necessary for non-CD ELF files, and is optional for game CDs as a
// fast boot up option. (though not recommended for games because of rare ill side
// effects).
SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR );
Console.WriteLn( Color_StrongGreen, "(PCSX2 Core) Executing Bios Stub..." );
do {
// Even the BiosStub invokes vsyncs, so we need to be weary of state
// changes and premature loop exits, and re-enter the stub executer until
// the critera is met.
StateCheckInThread();
Cpu->ExecuteBiosStub();
} while( cpuRegs.pc != 0x00200008 && cpuRegs.pc != 0x00100008 );
Console.WriteLn( Color_StrongGreen, "(PCSX2 Core) Execute Bios Stub Complete");
loadElfFile( elf_file );
}
sbcoe.Success();
}

View File

@ -28,6 +28,9 @@
#include "System/SysThreads.h"
#include "GS.h"
#include "CDVD/CDVD.h"
#include "ElfHeader.h"
#if !PCSX2_SEH
# include <csetjmp>
#endif
@ -763,19 +766,6 @@ static void recExecute()
#endif
}
static void recExecuteBiosStub()
{
g_ExecBiosHack = true;
recExecute();
g_ExecBiosHack = false;
// Reset the EErecs here, because the bios generates "slow" blocks that have
// g_ExecBiosHack checks in them. This deletes them so that the recs replace them
// with new faster versions:
recResetEE();
}
////////////////////////////////////////////////////
void R5900::Dynarec::OpcodeImpl::recSYSCALL( void )
{
@ -886,36 +876,6 @@ static void recExitExecution()
#endif
}
// check for end of bios
void CheckForBIOSEnd()
{
xMOV( eax, &cpuRegs.pc );
if( IsDevBuild )
{
// Using CALL retains stacktrace info, useful for debugging.
xCMP( eax, 0x00200008 );
xForwardJE8 CallExitRec;
xCMP( eax, 0x00100008 );
xForwardJNE8 SkipExitRec;
CallExitRec.SetTarget();
xCALL( recExitExecution );
SkipExitRec.SetTarget();
}
else
{
xCMP( eax, 0x00200008 );
xJE(recExitExecution);
xCMP( eax, 0x00100008 );
xJE(recExitExecution);
}
}
static int *s_pCode;
void SetBranchReg( u32 reg )
@ -1127,8 +1087,6 @@ static void iBranchTest(u32 newpc)
{
_DynGen_StackFrameCheck();
if( g_ExecBiosHack ) CheckForBIOSEnd();
// Check the Event scheduler if our "cycle target" has been reached.
// Equiv code to:
// cpuRegs.cycle += blockcycles;
@ -1394,6 +1352,16 @@ static void __fastcall recRecompile( const u32 startpc )
pxAssume(s_pCurBlockEx);
if (g_SkipBiosHack && HWADDR(startpc) == EELOAD_START) {
xCALL(eeloadReplaceOSDSYS);
xCMP(ptr32[&cpuRegs.pc], startpc);
xJNE(DispatcherReg);
}
// this is the only way patches get applied, doesn't depend on a hack
if (HWADDR(startpc) == ElfEntry)
xCALL(eeGameStarting);
branch = 0;
// reset recomp state variables
@ -1857,8 +1825,7 @@ R5900cpu recCpu =
recResetEE,
recStep,
recExecute,
recExecuteBiosStub,
recCheckExecutionState,
recClear,
};