/* Pcsx2 - Pc Ps2 Emulator * Copyright (C) 2002-2008 Pcsx2 Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "PrecompiledHeader.h" #include "Common.h" #include "VUmicro.h" #include "Threading.h" #include "iR5900.h" #include "iR3000A.h" #include "IopMem.h" #include "iVUzerorec.h" // for SuperVUReset #include "x86/ix86/ix86.h" using namespace std; using namespace Console; // disable all session overrides by default... SessionOverrideFlags g_Session = {false}; bool sysInitialized = false; namespace Exception { BaseException::~BaseException() throw() {} } // I can't believe I had to make my own version of trim. C++'s STL is totally whack. // And I still had to fix it too. I found three samples of trim online and *all* three // were buggy. People really need to learn to code before they start posting trim // functions in their blogs. (air) static void trim( string& line ) { if ( line.empty() ) return; int string_size = line.length(); int beginning_of_string = 0; int end_of_string = string_size - 1; bool encountered_characters = false; // find the start of characters in the string while ( (beginning_of_string < string_size) && (!encountered_characters) ) { if ( (line[ beginning_of_string ] != ' ') && (line[ beginning_of_string ] != '\t') ) encountered_characters = true; else ++beginning_of_string; } // test if no characters were found in the string if ( beginning_of_string == string_size ) return; encountered_characters = false; // find the character in the string while ( (end_of_string > beginning_of_string) && (!encountered_characters) ) { // if a space or tab was found then ignore it if ( (line[ end_of_string ] != ' ') && (line[ end_of_string ] != '\t') ) encountered_characters = true; else --end_of_string; } // return the original string with all whitespace removed from its beginning and end // + 1 at the end to add the space for the string delimiter //line.substr( beginning_of_string, end_of_string - beginning_of_string + 1 ); line.erase( end_of_string+1, string_size ); line.erase( 0, beginning_of_string ); } // This function should be called once during program execution. void SysDetect() { if( sysInitialized ) return; sysInitialized = true; Notice("PCSX2 " PCSX2_VERSION " - compiled on " __DATE__ ); Notice("Savestate version: %x", params g_SaveVersion); // fixme: This line is here for the purpose of creating external ASM code. Yah. >_< DevCon::Notice( "EE pc offset: 0x%x, IOP pc offset: 0x%x", params (u32)&cpuRegs.pc - (u32)&cpuRegs, (u32)&psxRegs.pc - (u32)&psxRegs ); cpudetectInit(); string family( cpuinfo.x86Fam ); trim( family ); SetColor( Console::Color_White ); WriteLn( "x86Init:" ); WriteLn( "\tCPU vendor name = %s\n" "\tFamilyID = %x\n" "\tx86Family = %s\n" "\tCPU speed = %d.%03d Ghz\n" "\tCores = %d physical [%d logical]\n" "\tx86PType = %s\n" "\tx86Flags = %8.8x %8.8x\n" "\tx86EFlags = %8.8x\n", params cpuinfo.x86ID, cpuinfo.x86StepID, family.c_str(), cpuinfo.cpuspeed / 1000, cpuinfo.cpuspeed%1000, cpuinfo.PhysicalCores, cpuinfo.LogicalCores, cpuinfo.x86Type, cpuinfo.x86Flags, cpuinfo.x86Flags2, cpuinfo.x86EFlags ); WriteLn( "Features:" ); WriteLn( "\t%sDetected MMX\n" "\t%sDetected SSE\n" "\t%sDetected SSE2\n" "\t%sDetected SSE3\n" "\t%sDetected SSE4.1\n", params cpucaps.hasMultimediaExtensions ? "" : "Not ", cpucaps.hasStreamingSIMDExtensions ? "" : "Not ", cpucaps.hasStreamingSIMD2Extensions ? "" : "Not ", cpucaps.hasStreamingSIMD3Extensions ? "" : "Not ", cpucaps.hasStreamingSIMD4Extensions ? "" : "Not " ); if ( cpuinfo.x86ID[0] == 'A' ) //AMD cpu { WriteLn( " Extended AMD Features:" ); WriteLn( "\t%sDetected MMX2\n" "\t%sDetected 3DNOW\n" "\t%sDetected 3DNOW2\n", params cpucaps.hasMultimediaExtensionsExt ? "" : "Not ", cpucaps.has3DNOWInstructionExtensions ? "" : "Not ", cpucaps.has3DNOWInstructionExtensionsExt ? "" : "Not " ); } Console::ClearColor(); } // Allocates memory for all PS2 systems. bool SysAllocateMem() { // Allocate PS2 system ram space (required by interpreters and recompilers both) try { memAlloc(); psxMemAlloc(); vuMicroMemAlloc(); } catch( Exception::OutOfMemory& ex ) { // Failures on the core initialization of memory is bad, since it means the emulator is // completely non-functional. If the failure is in the VM build then we can try running // the VTLB build instead. If it's the VTLB build then ... ouch. // VTLB build must fail outright... Msgbox::Alert( "Failed to allocate memory needed to run pcsx2.\n\nError: %s", params ex.cMessage() ); SysShutdownMem(); return false; } return true; } // Allocates memory for all recompilers, and force-disables any recs that fail to initialize. // This should be done asap, since the recompilers tend to demand a lot of system resources, and prefer // to have those resources at specific address ranges. The sooner memory is allocated, the better. // Returns FALSE on *critical* failure (GUI should issue a msg and exit). void SysAllocateDynarecs() { // Attempt to initialize the recompilers. // Most users want to use recs anyway, and if they are using interpreters I don't think the // extra few megs of allocation is going to be an issue. try { // R5900 and R3000a must be rec-enabled together for now so if either fails they both fail. recCpu.Allocate(); psxRec.Allocate(); } catch( Exception::BaseException& ex ) { Msgbox::Alert( "The EE/IOP recompiler failed to initialize with the following error:\n\n" "%s" "\n\nThe EE/IOP interpreter will be used instead (slow!).", params ex.cMessage() ); g_Session.ForceDisableEErec = true; recCpu.Shutdown(); psxRec.Shutdown(); } try { VU0micro::recAlloc(); } catch( Exception::BaseException& ex ) { Msgbox::Alert( "The VU0 recompiler failed to initialize with the following error:\n\n" "%s" "\n\nThe VU0 interpreter will be used for this session (may slow down some games).", params ex.cMessage() ); g_Session.ForceDisableVU0rec = true; VU0micro::recShutdown(); } try { VU1micro::recAlloc(); } catch( Exception::BaseException& ex ) { Msgbox::Alert( "The VU1 recompiler failed to initialize with the following error:\n\n" "%s" "\n\nThe VU1 interpreter will be used for this session (will slow down most games).", params ex.cMessage() ); g_Session.ForceDisableVU1rec = true; VU1micro::recShutdown(); } // If both VUrecs failed, then make sure the SuperVU is totally closed out: if( !CHECK_VU0REC && !CHECK_VU1REC) SuperVUDestroy( -1 ); } // This should be called last thing before Pcsx2 exits. void SysShutdownMem() { cpuShutdown(); vuMicroMemShutdown(); psxMemShutdown(); memShutdown(); } // This should generally be called right before calling SysShutdownMem(), although you can optionally // use it in conjunction with SysAllocDynarecs to allocate/free the dynarec resources on the fly (as // risky as it might be, since dynarecs could very well fail on the second attempt). void SysShutdownDynarecs() { // Special SuperVU "complete" terminator. SuperVUDestroy( -1 ); psxRec.Shutdown(); recCpu.Shutdown(); } // Resets all PS2 cpu execution states, which does not affect that actual PS2 state/condition. // This can be called at any time outside the context of a Cpu->Execute() block without // bad things happening (recompilers will slow down for a brief moment since rec code blocks // are dumped). // Use this method to reset the recs when important global pointers like the MTGS are re-assigned. void SysResetExecutionState() { if( CHECK_EEREC ) { Cpu = &recCpu; psxCpu = &psxRec; } else { Cpu = &intCpu; psxCpu = &psxInt; } Cpu->Reset(); psxCpu->Reset(); vuMicroCpuReset(); // make sure the VU1 doesn't have lingering "skip" enabled. vu1MicroDisableSkip(); } u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller) { u8 *Mem = (u8*)SysMmap( base, size ); if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) ) { DevCon::Notice( "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( Mem, size ); Mem = (u8*)SysMmap( NULL, size ); if( bounds != 0 && (((uptr)Mem + size) > bounds) ) { DevCon::Error( "Fatal Error:\n\tSecond try failed allocating %s, block ptr 0x%x does not meet required criteria.", params caller, Mem ); SafeSysMunmap( Mem, size ); // returns NULL, caller should throw an exception. } } return Mem; }