More work on the Linux side of things. This blind coding without being able to test stuff is challenging. >_< Also did some cleanups and error corrections in the way recs and vtlb allocate memory.

git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@668 a6443dda-0b58-4228-96e9-037be469359c
This commit is contained in:
Jake.Stine 2009-01-31 02:14:13 +00:00 committed by Gregory Hainaut
parent 65a1ef18c8
commit 5fbaf63663
10 changed files with 130 additions and 116 deletions

View File

@ -140,11 +140,27 @@ void ExecuteCpu()
signal(SIGINT, SignalExit); signal(SIGINT, SignalExit);
signal(SIGPIPE, 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 ) if( CHECK_EEREC )
{ {
while( !m_ReturnToGame ) while( !g_ReturnToGui )
{ {
recExecute(); recExecute();
SysUpdate(); SysUpdate();
@ -152,15 +168,13 @@ void ExecuteCpu()
} }
else else
{ {
while( !m_ReturnToGame ) while( !g_ReturnToGui )
{ {
Cpu->Execute(); Cpu->Execute();
SysUpdate(); SysUpdate();
} }
} }
PCSX2_MEM_PROTECT_END();
//timeEndPeriod( 1 );
} }
void RunExecute( const char* elf_file, bool use_bios ) void RunExecute( const char* elf_file, bool use_bios )
@ -195,9 +209,7 @@ void RunExecute( const char* elf_file, bool use_bios )
return; return;
} }
SysResetExecutionState() ; if (elf_file == NULL )
if (elf_file == 0 )
{ {
if (g_RecoveryState != 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. // 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. // But if the elf_file is an empty string then we boot the bios instead.
cpuExecuteBios();
char ename[g_MaxPath]; char ename[g_MaxPath];
ename[0] = 0; ename[0] = 0;
if( !use_bios ) GetPS2ElfName(ename); if( !use_bios )
GetPS2ElfName( ename );
loadElfFile( ename ); loadElfFile( ename );
} }
} }
@ -237,15 +250,11 @@ void RunExecute( const char* elf_file, bool use_bios )
// Custom ELF specified (not using CDVD). // Custom ELF specified (not using CDVD).
// Run the BIOS and load the ELF. // Run the BIOS and load the ELF.
cpuExecuteBios();
loadElfFile( elf_file ); loadElfFile( elf_file );
} }
FixCPUState(); 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(); ExecuteCpu();
} }
@ -327,8 +336,8 @@ void OnFile_Exit(GtkMenuItem *menuitem, gpointer user_data)
void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data) void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data)
{ {
if( g_GameInProgress ) if( g_EmulationInProgress )
m_ReturnToGame = 1; ExecuteCpu();
else else
RunExecute( NULL, true ); // boots bios if no savestate is to be recovered 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) void OnEmu_Reset(GtkMenuItem *menuitem, gpointer user_data)
{ {
safe_free( g_RecoveryState );
SysReset(); SysReset();
} }

View File

@ -33,6 +33,11 @@ GtkWidget *MsgDlg;
static int sinit=0; 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[]) { int main(int argc, char *argv[]) {
char *file = NULL; char *file = NULL;
char elfname[g_MaxPath]; char elfname[g_MaxPath];
@ -278,19 +283,24 @@ void KeyEvent(keyEvent* ev) {
//Config_hacks_backup = Config.Hacks; //Config_hacks_backup = Config.Hacks;
} }
switch (ev->key) { switch (ev->key)
case XK_F1: ProcessFKeys(1, shift); break; {
case XK_F2: ProcessFKeys(2, shift); break; case XK_F1: case XK_F2: case XK_F3: case XK_F4:
case XK_F3: ProcessFKeys(3, shift); break; case XK_F5: case XK_F6: case XK_F7: case XK_F8:
case XK_F4: ProcessFKeys(4, shift); break; case XK_F9: case XK_F10: case XK_F11: case XK_F12:
case XK_F5: ProcessFKeys(5, shift); break; try
case XK_F6: ProcessFKeys(6, shift); break; {
case XK_F7: ProcessFKeys(7, shift); break; ProcessFKeys(ev->key-XK_F1 + 1, shift);
case XK_F8: ProcessFKeys(8, shift); break; }
case XK_F9: ProcessFKeys(9, shift); break; catch( Exception::CpuStateShutdown& )
case XK_F10: ProcessFKeys(10, shift); break; {
case XK_F11: ProcessFKeys(11, shift); break; // Woops! Something was unrecoverable. Bummer.
case XK_F12: ProcessFKeys(12, shift); break; // Let's give the user a RunGui!
g_EmulationInProgress = false;
g_ReturnToGui = true;
}
break;
case XK_Escape: case XK_Escape:
signal(SIGINT, SIG_DFL); signal(SIGINT, SIG_DFL);
@ -305,8 +315,17 @@ void KeyEvent(keyEvent* ev) {
ClosePlugins(); ClosePlugins();
if (!UseGui) exit(0); 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(); RunGui();
break; break;
default: default:
GSkeyEvent(ev); GSkeyEvent(ev);
break; break;
@ -385,7 +404,7 @@ bool SysInit()
void SysRestorableReset() void SysRestorableReset()
{ {
// already reset? and saved? // already reset? and saved?
if( !g_GameInProgress ) return; if( !g_EmulationInProgress ) return;
if( g_RecoveryState != NULL ) return; if( g_RecoveryState != NULL ) return;
try try
@ -393,13 +412,13 @@ void SysRestorableReset()
g_RecoveryState = new MemoryAlloc<u8>( "Memory Savestate Recovery" ); g_RecoveryState = new MemoryAlloc<u8>( "Memory Savestate Recovery" );
memSavingState( *g_RecoveryState ).FreezeAll(); memSavingState( *g_RecoveryState ).FreezeAll();
cpuShutdown(); cpuShutdown();
g_GameInProgress = false; g_EmulationInProgress = false;
} }
catch( std::runtime_error& ex ) catch( Exception::RuntimeError& ex )
{ {
Msgbox::Alert( Msgbox::Alert(
"Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" "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 ); safe_delete( g_RecoveryState );
} }
} }
@ -408,15 +427,18 @@ void SysReset()
{ {
if (!sinit) return; if (!sinit) return;
g_GameInProgress = false; g_EmulationInProgress = false;
safe_free( g_RecoveryState ); safe_free( g_RecoveryState );
ResetPlugins(); ResetPlugins();
ElfCRC = 0;
} }
void SysClose() { void SysClose() {
if (sinit == 0) return; if (sinit == 0) return;
cpuShutdown(); cpuShutdown();
ClosePlugins();
ReleasePlugins(); ReleasePlugins();
if (emuLog != NULL) if (emuLog != NULL)

View File

@ -333,4 +333,28 @@ void SysResetExecutionState()
// make sure the VU1 doesn't have lingering "skip" enabled. // make sure the VU1 doesn't have lingering "skip" enabled.
vu1MicroDisableSkip(); vu1MicroDisableSkip();
} }
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;
}

View File

@ -47,6 +47,12 @@ void SysCloseLibrary(void *lib); // Closes Library
// Returns NULL on allocation failure. // Returns NULL on allocation failure.
void *SysMmap(uptr base, u32 size); 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 // Unmaps a block allocated by SysMmap
void SysMunmap(uptr base, u32 size); void SysMunmap(uptr base, u32 size);
@ -232,34 +238,4 @@ int SysMapUserPhysicalPages(void* Addr, uptr NumPages, uptr* pblock, int pageoff
#endif #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__ */ #endif /* __SYSTEM_H__ */

View File

@ -571,21 +571,7 @@ void vtlb_Term()
u8* vtlb_malloc( uint size, uint align, uptr tryBaseAddress ) u8* vtlb_malloc( uint size, uint align, uptr tryBaseAddress )
{ {
#ifdef __LINUX__ #ifdef __LINUX__
void* retval = return SysMmap( tryBaseAddress, size, "Vtlb" );
( 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;
#else #else
// Win32 just needs this, since malloc always maps below 2GB. // Win32 just needs this, since malloc always maps below 2GB.
return (u8*)_aligned_malloc(size, align); return (u8*)_aligned_malloc(size, align);
@ -599,7 +585,7 @@ void vtlb_free( void* pmem, uint size )
#ifdef __LINUX__ #ifdef __LINUX__
SafeSysMunmap( pmem, size ); SafeSysMunmap( pmem, size );
#else #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; DWORD old;
VirtualProtect( pmem, size, PAGE_READWRITE, &old ); VirtualProtect( pmem, size, PAGE_READWRITE, &old );
safe_aligned_free( pmem ); safe_aligned_free( pmem );

View File

@ -36,7 +36,7 @@ MemoryAlloc<u8>* g_gsRecoveryState = NULL;
bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI 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 // This instance is not modified by command line overrides so
// that command line plugins and stuff won't be saved into the // 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); SetPriorityClass(GetCurrentProcess(), Config.ThPriority == THREAD_PRIORITY_HIGHEST ? ABOVE_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS);
nDisableSC = 1; nDisableSC = 1;
// Issue a cpu reset if the emulation state is invalid or if a recovery state
// is waiting to be loaded.
try try
{ {
cpuReset(); cpuReset();
@ -718,11 +715,11 @@ void SysRestorableReset()
safe_delete( g_gsRecoveryState ); safe_delete( g_gsRecoveryState );
g_EmulationInProgress = false; g_EmulationInProgress = false;
} }
catch( std::runtime_error& ex ) catch( Exception::RuntimeError& ex )
{ {
Msgbox::Alert( Msgbox::Alert(
"Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" "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 ); safe_delete( g_RecoveryState );
} }
} }

View File

@ -556,7 +556,7 @@ static void recAlloc()
// ... we can't? (air) // ... we can't? (air)
if( recMem == NULL ) if( recMem == NULL )
recMem = (u8*)SysBoundedMmap(0x0f000000, RECMEM_SIZE, 0xf0000000, "recAlloc(3000)"); recMem = (u8*)SysMmap(0x0f000000, RECMEM_SIZE, 0x10000000, "recAlloc(3000)");
if( recMem == NULL ) if( recMem == NULL )
throw Exception::OutOfMemory( "R3000a Init > failed to allocate memory for the recompiler." ); throw Exception::OutOfMemory( "R3000a Init > failed to allocate memory for the recompiler." );

View File

@ -329,7 +329,7 @@ void SuperVUAlloc(int vuindex)
if( s_recVUMem == NULL ) if( s_recVUMem == NULL )
{ {
// upper 4 bits must be zero! // 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 ) if( s_recVUMem == NULL )
{ {

View File

@ -14,7 +14,6 @@
#define BASEBLOCK_SIZE 2 // in dwords #define BASEBLOCK_SIZE 2 // in dwords
#define PCOFFSET 0x2a8 // this must always match what Pcsx2 displays at startup #define PCOFFSET 0x2a8 // this must always match what Pcsx2 displays at startup
#define REG_PC %ecx
#define REG_BLOCK %esi #define REG_BLOCK %esi
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -31,27 +30,27 @@ Dispatcher:
# ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) # ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff)))
mov %eax,dword ptr [cpuRegs+PCOFFSET] mov %eax,dword ptr [cpuRegs+PCOFFSET]
mov REG_BLOCK,%eax mov %ecx,%eax // ecx is the BLOCK address
mov REG_PC,%eax mov %esi,%eax // esi is the PC address (leave unmodified!)
shr %eax,0x10 shr %eax,0x10
and %ecx,0xFFFF and %ecx,0xFFFF
mov %edx,dword ptr [recLUT] mov %edx,dword ptr [recLUT]
mov %eax,dword ptr [%edx+%eax*4] mov %eax,dword ptr [%edx+%eax*4]
lea REG_BLOCK,[%eax+REG_BLOCK*2] lea %ecx,[%eax+%ecx*2]
// check if startpc == cpuRegs.pc // check if startpc == cpuRegs.pc
//and %ecx, 0x5fffffff // remove higher bits //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 je Dispatcher_CheckPtr
// recompile // recompile
push REG_BLOCK // BASEBLOCK push %ecx
push REG_PC // pc push %esi // pc
call recRecompile call recRecompile
add %esp, 4 add %esp, 4
pop REG_BLOCK // ecx is now the REG_BLOCK pop %ecx // ecx is now the REG_BLOCK
Dispatcher_CheckPtr: Dispatcher_CheckPtr:
mov %eax, dword ptr [REG_BLOCK] mov %eax, dword ptr [%ecx]
#ifdef _DEBUG #ifdef _DEBUG
test %eax, %eax test %eax, %eax
@ -135,27 +134,27 @@ DispatcherClear_Recompile:
DispatcherReg: DispatcherReg:
mov %eax,dword ptr [cpuRegs+PCOFFSET] mov %eax,dword ptr [cpuRegs+PCOFFSET]
mov REG_BLOCK,%eax mov %ecx,%eax // ecx will be the BLOCK
mov REG_PC,%eax mov %esi,%eax // esi is the PC address (leave unmodified!)
shr %eax,0x10 shr %eax,0x10
and REG_BLOCK,0xFFFF and %ecx,0xFFFF
mov %edx,dword ptr [recLUT] mov %edx,dword ptr [recLUT]
mov %eax,dword ptr [%edx+%eax*4] mov %eax,dword ptr [%edx+%eax*4]
lea REG_BLOCK,[%eax+REG_BLOCK*2] lea %ecx,[%eax+%ecx*2]
// check if startpc == cpuRegs.pc // check if startpc == cpuRegs.pc
//and %ecx, 0x5fffffff // remove higher bits //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 je DispatcherReg_CheckPtr
// recompile // recompile
push REG_BLOCK push %ecx // block
push REG_PC // pc push %esi // pc
call recRecompile call recRecompile
add %esp, 4 add %esp, 4
pop REG_BLOCK pop %ecx // block
DispatcherReg_CheckPtr: DispatcherReg_CheckPtr:
mov %eax, dword ptr [REG_BLOCK] mov %eax, dword ptr [%ecx]
#ifdef _DEBUG #ifdef _DEBUG
test %eax, %eax test %eax, %eax

View File

@ -500,16 +500,18 @@ static void recAlloc()
if( recMem == NULL ) if( recMem == NULL )
{ {
const uint cachememsize = REC_CACHEMEM+0x1000; const uint cachememsize = REC_CACHEMEM+0x1000;
recMem = (u8*)SysMmap(0x0d000000, cachememsize, 0x10000000, "recAlloc(5900)");
// try an arbitrary address first, and if it doesn't work, try NULL.
recMem = (u8*)SysBoundedMmap(0x0d000000, cachememsize, 0x80000000, "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. // 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 // Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are
// always 4 bytes long). // always 4 bytes long).
if( m_recBlockAlloc == NULL ) if( m_recBlockAlloc == NULL )
m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 ); m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 );
if( m_recBlockAlloc == NULL ) if( m_recBlockAlloc == NULL )
throw Exception::OutOfMemory( "R5900-32 Init > Failed to allocate memory for BASEBLOCK tables." ); throw Exception::OutOfMemory( "R5900-32 Init > Failed to allocate memory for BASEBLOCK tables." );