diff --git a/pcsx2/Linux/GtkGui.cpp b/pcsx2/Linux/GtkGui.cpp index 64d5606884..7291db1633 100644 --- a/pcsx2/Linux/GtkGui.cpp +++ b/pcsx2/Linux/GtkGui.cpp @@ -140,11 +140,27 @@ void ExecuteCpu() signal(SIGINT, SignalExit); signal(SIGPIPE, SignalExit); - //timeBeginPeriod( 1 ); + // Make sure any left-over recovery states are cleaned up. + safe_delete( g_RecoveryState ); + + // Just in case they weren't initialized earlier (no harm in calling this multiple times) + if (OpenPlugins(NULL) == -1) return; + + // this needs to be called for every new game! (note: sometimes launching games through bios will give a crc of 0) + if( GSsetGameCRC != NULL ) GSsetGameCRC(ElfCRC, g_ZeroGSOptions); + + // Optimization: We hardcode two versions of the EE here -- one for recs and one for ints. + // This is because recs are performance critical, and being able to inline them into the + // function here helps a small bit (not much but every small bit counts!). + + g_EmulationInProgress = true; + g_ReturnToGui = false; + + PCSX2_MEM_PROTECT_BEGIN(); if( CHECK_EEREC ) { - while( !m_ReturnToGame ) + while( !g_ReturnToGui ) { recExecute(); SysUpdate(); @@ -152,15 +168,13 @@ void ExecuteCpu() } else { - while( !m_ReturnToGame ) + while( !g_ReturnToGui ) { Cpu->Execute(); SysUpdate(); } } - - //timeEndPeriod( 1 ); - + PCSX2_MEM_PROTECT_END(); } void RunExecute( const char* elf_file, bool use_bios ) @@ -195,9 +209,7 @@ void RunExecute( const char* elf_file, bool use_bios ) return; } - SysResetExecutionState() ; - - if (elf_file == 0 ) + if (elf_file == NULL ) { if (g_RecoveryState != NULL) { @@ -222,13 +234,14 @@ void RunExecute( const char* elf_file, bool use_bios ) { // Not recovering a state, so need to execute the bios and load the ELF information. - // Note: if the elf_file is null we use the CDVD elf file. + // if the elf_file is null we use the CDVD elf file. // But if the elf_file is an empty string then we boot the bios instead. - cpuExecuteBios(); char ename[g_MaxPath]; ename[0] = 0; - if( !use_bios ) GetPS2ElfName(ename); + if( !use_bios ) + GetPS2ElfName( ename ); + loadElfFile( ename ); } } @@ -237,15 +250,11 @@ void RunExecute( const char* elf_file, bool use_bios ) // Custom ELF specified (not using CDVD). // Run the BIOS and load the ELF. - cpuExecuteBios(); loadElfFile( elf_file ); } FixCPUState(); - // this needs to be called for every new game! (note: sometimes launching games through bios will give a crc of 0) - if( GSsetGameCRC != NULL ) GSsetGameCRC(ElfCRC, g_ZeroGSOptions); - ExecuteCpu(); } @@ -327,8 +336,8 @@ void OnFile_Exit(GtkMenuItem *menuitem, gpointer user_data) void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data) { - if( g_GameInProgress ) - m_ReturnToGame = 1; + if( g_EmulationInProgress ) + ExecuteCpu(); else RunExecute( NULL, true ); // boots bios if no savestate is to be recovered @@ -336,7 +345,6 @@ void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data) void OnEmu_Reset(GtkMenuItem *menuitem, gpointer user_data) { - safe_free( g_RecoveryState ); SysReset(); } diff --git a/pcsx2/Linux/LnxMain.cpp b/pcsx2/Linux/LnxMain.cpp index bd0b5bc6cf..54a6ba9687 100644 --- a/pcsx2/Linux/LnxMain.cpp +++ b/pcsx2/Linux/LnxMain.cpp @@ -33,6 +33,11 @@ GtkWidget *MsgDlg; static int sinit=0; +// These two status vars replace the old g_GameInProgress status var. + +bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI +bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset) + int main(int argc, char *argv[]) { char *file = NULL; char elfname[g_MaxPath]; @@ -278,19 +283,24 @@ void KeyEvent(keyEvent* ev) { //Config_hacks_backup = Config.Hacks; } - switch (ev->key) { - case XK_F1: ProcessFKeys(1, shift); break; - case XK_F2: ProcessFKeys(2, shift); break; - case XK_F3: ProcessFKeys(3, shift); break; - case XK_F4: ProcessFKeys(4, shift); break; - case XK_F5: ProcessFKeys(5, shift); break; - case XK_F6: ProcessFKeys(6, shift); break; - case XK_F7: ProcessFKeys(7, shift); break; - case XK_F8: ProcessFKeys(8, shift); break; - case XK_F9: ProcessFKeys(9, shift); break; - case XK_F10: ProcessFKeys(10, shift); break; - case XK_F11: ProcessFKeys(11, shift); break; - case XK_F12: ProcessFKeys(12, shift); break; + switch (ev->key) + { + case XK_F1: case XK_F2: case XK_F3: case XK_F4: + case XK_F5: case XK_F6: case XK_F7: case XK_F8: + case XK_F9: case XK_F10: case XK_F11: case XK_F12: + try + { + ProcessFKeys(ev->key-XK_F1 + 1, shift); + } + catch( Exception::CpuStateShutdown& ) + { + // Woops! Something was unrecoverable. Bummer. + // Let's give the user a RunGui! + + g_EmulationInProgress = false; + g_ReturnToGui = true; + } + break; case XK_Escape: signal(SIGINT, SIG_DFL); @@ -305,8 +315,17 @@ void KeyEvent(keyEvent* ev) { ClosePlugins(); if (!UseGui) exit(0); + + // fixme: The GUI is now capable of recieving control back from the + // emulator. Which means that when I set g_ReturnToGui here, the emulation + // loop in ExecuteCpu() will exit. You should be able to set it up so + // that it returns control to the existing GTK event loop, instead of + // always starting a new one via RunGui(). (but could take some trial and + // error) -- (air) + g_ReturnToGui = true; RunGui(); break; + default: GSkeyEvent(ev); break; @@ -385,7 +404,7 @@ bool SysInit() void SysRestorableReset() { // already reset? and saved? - if( !g_GameInProgress ) return; + if( !g_EmulationInProgress ) return; if( g_RecoveryState != NULL ) return; try @@ -393,13 +412,13 @@ void SysRestorableReset() g_RecoveryState = new MemoryAlloc( "Memory Savestate Recovery" ); memSavingState( *g_RecoveryState ).FreezeAll(); cpuShutdown(); - g_GameInProgress = false; + g_EmulationInProgress = false; } - catch( std::runtime_error& ex ) + catch( Exception::RuntimeError& ex ) { Msgbox::Alert( "Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" - "Error: %s", params ex.what() ); + "Error: %s", params ex.cMessage() ); safe_delete( g_RecoveryState ); } } @@ -408,15 +427,18 @@ void SysReset() { if (!sinit) return; - g_GameInProgress = false; + g_EmulationInProgress = false; safe_free( g_RecoveryState ); ResetPlugins(); + + ElfCRC = 0; } void SysClose() { if (sinit == 0) return; cpuShutdown(); + ClosePlugins(); ReleasePlugins(); if (emuLog != NULL) diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index ab6d86bf89..85ce236220 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -333,4 +333,28 @@ void SysResetExecutionState() // make sure the VU1 doesn't have lingering "skip" enabled. vu1MicroDisableSkip(); -} \ No newline at end of file +} + +u8 *SysMmap(uptr base, u32 size, uptr bounds, const char *caller) +{ + u8 *Mem = (u8*)SysMmap( base, size ); + + if( (Mem == NULL) || ((uptr)Mem + size) > bounds ) + { + DevCon::Error( "First try failed allocating %s at address 0x%x", params caller, base ); + + // memory allocation *must* have the top bit clear, so let's try again + // with NULL (let the OS pick something for us). + + SafeSysMunmap( base, size ); + + Mem = (u8*)SysMmap( NULL, size ); + if( (uptr)Mem > bounds ) + { + DevCon::Error( "Second try failed allocating %s, block ptr 0x%x does not meet required criteria.", params caller, Mem ); + SafeSysMunmap( Mem, size ); + } + } + return Mem; +} + diff --git a/pcsx2/System.h b/pcsx2/System.h index b9df91230f..94fa633dcf 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -47,6 +47,12 @@ void SysCloseLibrary(void *lib); // Closes Library // Returns NULL on allocation failure. void *SysMmap(uptr base, u32 size); +// Maps a block of memory for use as a recompiled code buffer, and ensures that the +// allocation is below a certain memory address (specified in "bounds" parameter). +// The allocated block has code execution privileges. +// Returns NULL on allocation failure. +u8 *SysMmap(uptr base, u32 size, uptr bounds, const char *caller="Unnamed"); + // Unmaps a block allocated by SysMmap void SysMunmap(uptr base, u32 size); @@ -232,34 +238,4 @@ int SysMapUserPhysicalPages(void* Addr, uptr NumPages, uptr* pblock, int pageoff #endif - -static __forceinline u8 *SysBoundedMmap(uptr base, u32 size, u32 bounds, char *caller) -{ - u8 *Mem = NULL; - // For Linux we need to use the system virtual memory mapper so that we - // can coerce an allocation below the 2GB line. - - // just try an arbitrary address first... - // maybe there's a better one to pick? - Mem = (u8*)SysMmap( base, size ); - - if( (Mem == NULL) || ((uptr)Mem + size) > bounds ) - { - DevCon::Error("Problem allocating in %s.", params caller); - // memory allocation *must* have the top bit clear, so let's try again - // with NULL (let the OS pick something for us). - - if( Mem != NULL ) SysMunmap( base, size ); - - Mem = (u8*)SysMmap( NULL, size ); - if( (uptr)Mem > bounds ) - { - DevCon::Error("Continuing problems allocating in %s.", params caller); - SysMunmap( Mem, size ); - Mem = NULL; // let the os-independent code below handle the error - } - } - return Mem; -} - #endif /* __SYSTEM_H__ */ diff --git a/pcsx2/vtlb.cpp b/pcsx2/vtlb.cpp index cdfae647f2..06fce249eb 100644 --- a/pcsx2/vtlb.cpp +++ b/pcsx2/vtlb.cpp @@ -571,21 +571,7 @@ void vtlb_Term() u8* vtlb_malloc( uint size, uint align, uptr tryBaseAddress ) { #ifdef __LINUX__ - void* retval = - ( tryBaseAddress != NULL ) ? SysMmap( tryBaseAddress, size ) : NULL; - - if( retval == NULL || (uptr)retval > 0x80000000 ) - { - // memory allocation *must* have the top bit clear, so let's try again - // with NULL (let the OS pick something for us). - - SafeSysMunmap( retval, size ); - - retval = (u8*)SysMmap( NULL, size ); - if( (uptr)retval > 0x80000000 ) - SafeSysMunmap( retval, size ); - } - return (u8*)retval; + return SysMmap( tryBaseAddress, size, "Vtlb" ); #else // Win32 just needs this, since malloc always maps below 2GB. return (u8*)_aligned_malloc(size, align); @@ -599,7 +585,7 @@ void vtlb_free( void* pmem, uint size ) #ifdef __LINUX__ SafeSysMunmap( pmem, size ); #else -// Make sure and unprotect memory first, since CrtDebug will try to write to it. + // Make sure and unprotect memory first, since CrtDebug will try to write to it. DWORD old; VirtualProtect( pmem, size, PAGE_READWRITE, &old ); safe_aligned_free( pmem ); diff --git a/pcsx2/windows/WinSysExec.cpp b/pcsx2/windows/WinSysExec.cpp index e3b4900f68..2694bb6266 100644 --- a/pcsx2/windows/WinSysExec.cpp +++ b/pcsx2/windows/WinSysExec.cpp @@ -36,7 +36,7 @@ MemoryAlloc* g_gsRecoveryState = NULL; bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI -bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset) +bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset) // This instance is not modified by command line overrides so // that command line plugins and stuff won't be saved into the @@ -259,9 +259,6 @@ void RunExecute( const char* elf_file, bool use_bios ) SetPriorityClass(GetCurrentProcess(), Config.ThPriority == THREAD_PRIORITY_HIGHEST ? ABOVE_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS); nDisableSC = 1; - // Issue a cpu reset if the emulation state is invalid or if a recovery state - // is waiting to be loaded. - try { cpuReset(); @@ -718,11 +715,11 @@ void SysRestorableReset() safe_delete( g_gsRecoveryState ); g_EmulationInProgress = false; } - catch( std::runtime_error& ex ) + catch( Exception::RuntimeError& ex ) { Msgbox::Alert( "Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" - "Error: %s", params ex.what() ); + "Error: %s", params ex.cMessage() ); safe_delete( g_RecoveryState ); } } diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index 24b7ea0267..f89ef908f5 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -556,7 +556,7 @@ static void recAlloc() // ... we can't? (air) if( recMem == NULL ) - recMem = (u8*)SysBoundedMmap(0x0f000000, RECMEM_SIZE, 0xf0000000, "recAlloc(3000)"); + recMem = (u8*)SysMmap(0x0f000000, RECMEM_SIZE, 0x10000000, "recAlloc(3000)"); if( recMem == NULL ) throw Exception::OutOfMemory( "R3000a Init > failed to allocate memory for the recompiler." ); diff --git a/pcsx2/x86/iVUzerorec.cpp b/pcsx2/x86/iVUzerorec.cpp index f90ee52e3e..2a85c09cc0 100644 --- a/pcsx2/x86/iVUzerorec.cpp +++ b/pcsx2/x86/iVUzerorec.cpp @@ -329,7 +329,7 @@ void SuperVUAlloc(int vuindex) if( s_recVUMem == NULL ) { // upper 4 bits must be zero! - s_recVUMem = SysBoundedMmap(0x0c000000, VU_EXESIZE, 0x80000000, "SuperVUAlloc"); + s_recVUMem = SysMmap(0x0c000000, VU_EXESIZE, 0x10000000, "SuperVUAlloc"); if( s_recVUMem == NULL ) { diff --git a/pcsx2/x86/ix86-32/aR5900-32.S b/pcsx2/x86/ix86-32/aR5900-32.S index d87fe26605..ca82bb2ef8 100644 --- a/pcsx2/x86/ix86-32/aR5900-32.S +++ b/pcsx2/x86/ix86-32/aR5900-32.S @@ -14,7 +14,6 @@ #define BASEBLOCK_SIZE 2 // in dwords #define PCOFFSET 0x2a8 // this must always match what Pcsx2 displays at startup -#define REG_PC %ecx #define REG_BLOCK %esi ////////////////////////////////////////////////////////////////////////// @@ -31,27 +30,27 @@ Dispatcher: # ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) mov %eax,dword ptr [cpuRegs+PCOFFSET] - mov REG_BLOCK,%eax - mov REG_PC,%eax + mov %ecx,%eax // ecx is the BLOCK address + mov %esi,%eax // esi is the PC address (leave unmodified!) shr %eax,0x10 and %ecx,0xFFFF mov %edx,dword ptr [recLUT] - mov %eax,dword ptr [%edx+%eax*4] - lea REG_BLOCK,[%eax+REG_BLOCK*2] + mov %eax,dword ptr [%edx+%eax*4] + lea %ecx,[%eax+%ecx*2] // check if startpc == cpuRegs.pc //and %ecx, 0x5fffffff // remove higher bits - cmp REG_PC, dword ptr [REG_BLOCK+BLOCKTYPE_STARTPC] + cmp %esi, dword ptr [%ecx+BLOCKTYPE_STARTPC] je Dispatcher_CheckPtr // recompile - push REG_BLOCK // BASEBLOCK - push REG_PC // pc + push %ecx + push %esi // pc call recRecompile add %esp, 4 - pop REG_BLOCK // ecx is now the REG_BLOCK + pop %ecx // ecx is now the REG_BLOCK Dispatcher_CheckPtr: - mov %eax, dword ptr [REG_BLOCK] + mov %eax, dword ptr [%ecx] #ifdef _DEBUG test %eax, %eax @@ -135,27 +134,27 @@ DispatcherClear_Recompile: DispatcherReg: mov %eax,dword ptr [cpuRegs+PCOFFSET] - mov REG_BLOCK,%eax - mov REG_PC,%eax + mov %ecx,%eax // ecx will be the BLOCK + mov %esi,%eax // esi is the PC address (leave unmodified!) shr %eax,0x10 - and REG_BLOCK,0xFFFF + and %ecx,0xFFFF mov %edx,dword ptr [recLUT] mov %eax,dword ptr [%edx+%eax*4] - lea REG_BLOCK,[%eax+REG_BLOCK*2] + lea %ecx,[%eax+%ecx*2] // check if startpc == cpuRegs.pc //and %ecx, 0x5fffffff // remove higher bits - cmp REG_PC, dword ptr [REG_BLOCK+BLOCKTYPE_STARTPC] + cmp %esi, dword ptr [%ecx+BLOCKTYPE_STARTPC] je DispatcherReg_CheckPtr // recompile - push REG_BLOCK - push REG_PC // pc + push %ecx // block + push %esi // pc call recRecompile add %esp, 4 - pop REG_BLOCK + pop %ecx // block DispatcherReg_CheckPtr: - mov %eax, dword ptr [REG_BLOCK] + mov %eax, dword ptr [%ecx] #ifdef _DEBUG test %eax, %eax diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index 7082ff2f7d..521826b88f 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -500,16 +500,18 @@ static void recAlloc() if( recMem == NULL ) { const uint cachememsize = REC_CACHEMEM+0x1000; - - // try an arbitrary address first, and if it doesn't work, try NULL. - recMem = (u8*)SysBoundedMmap(0x0d000000, cachememsize, 0x80000000, "recAlloc(5900)"); + recMem = (u8*)SysMmap(0x0d000000, cachememsize, 0x10000000, "recAlloc(5900)"); } + + if( recMem == NULL ) + throw Exception::OutOfMemory( "R5900-32 > failed to allocate recompiler memory." ); + // Goal: Allocate BASEBLOCKs for every possible branch target in PS2 memory. // Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are // always 4 bytes long). - if( m_recBlockAlloc == NULL ) - m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 ); + if( m_recBlockAlloc == NULL ) + m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 ); if( m_recBlockAlloc == NULL ) throw Exception::OutOfMemory( "R5900-32 Init > Failed to allocate memory for BASEBLOCK tables." );