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(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();
}

View File

@ -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<u8>( "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)

View File

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

View File

@ -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 );

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_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 );
}
}

View File

@ -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." );

View File

@ -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 )
{

View File

@ -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

View File

@ -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." );