pcsx2/R5900.c

579 lines
15 KiB
C

/* Pcsx2 - Pc Ps2 Emulator
* Copyright (C) 2002-2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <string.h>
#include "Common.h"
#include "Memory.h"
#include "Hw.h"
#include "Debug.h"
#include "R3000A.h"
#include "VUmicro.h"
#include "GS.h"
static int inter;
PCSX2_ALIGNED16(cpuRegisters cpuRegs);
PCSX2_ALIGNED16(fpuRegisters fpuRegs);
PCSX2_ALIGNED16(tlbs tlb[48]);
PCSX2_ALIGNED16(GPR_reg64 g_cpuConstRegs[32]) = {0};
u32 g_cpuHasConstReg = 0, g_cpuFlushedConstReg = 0;
R5900cpu *Cpu;
int EEsCycle;
u32 EEoCycle, IOPoCycle;
extern u32 dwSaveVersion;
int cpuInit()
{
int ret;
SysPrintf("PCSX2 v" PCSX2_VERSION " save ver: %x\n", dwSaveVersion);
SysPrintf("Color Legend: White - PCSX2 message\n");
SysPrintf(COLOR_GREEN " Green - EE sio2 printf\n" COLOR_RESET);
SysPrintf(COLOR_RED " Red - IOP printf\n" COLOR_RESET);
printf("%d", (u32)&cpuRegs.pc - (u32)&cpuRegs);
InitFPUOps();
cpudetectInit();
cpuRegs.constzero = 0;
Cpu = CHECK_EEREC ? &recCpu : &intCpu;
ret = Cpu->Init();
if (ret == -1 && CHECK_EEREC) {
SysMessage(_("Error initializing Recompiler, switching to Interpreter"));
Config.Options &= ~(PCSX2_EEREC|PCSX2_VU1REC|PCSX2_VU0REC);
Cpu = &intCpu;
ret = Cpu->Init();
}
#ifdef WIN32_VIRTUAL_MEM
if (memInit() == -1) {
PROCESS_INFORMATION pi;
STARTUPINFO si;
char strdir[255], strexe[255];
if( MessageBox(NULL, "Failed to allocate enough physical memory to run pcsx2. Try closing\n"
"down background programs, restarting windows, or buying more memory.\n\n"
"Launch TLB version of pcsx2 (pcsx2t.exe)?", "Memory Allocation Error", MB_YESNO) == IDYES ) {
GetCurrentDirectory(ARRAYSIZE(strdir), strdir);
_snprintf(strexe, ARRAYSIZE(strexe), "%s\\pcsx2t.exe", strdir);
memset(&si, 0, sizeof(si));
if( !CreateProcess(strexe, "", NULL, NULL, FALSE, DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP, NULL, strdir, &si, &pi)) {
_snprintf(strdir, ARRAYSIZE(strexe), "Failed to launch %s\n", strexe);
MessageBox(NULL, strdir, "Failure", MB_OK);
}
else {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
return -1;
}
#endif
if (hwInit() == -1) return -1;
if (vu0Init() == -1) return -1;
if (vu1Init() == -1) return -1;
#ifndef WIN32_VIRTUAL_MEM
if (memInit() == -1) return -1;
#endif
#ifdef PCSX2_DEVBUILD
Log = 0;
#endif
return ret;
}
void cpuReset()
{
Cpu->Reset();
memReset();
memset(&cpuRegs, 0, sizeof(cpuRegs));
memset(&fpuRegs, 0, sizeof(fpuRegs));
memset(&tlb, 0, sizeof(tlb));
cpuRegs.pc = 0xbfc00000; ///set pc reg to stack
cpuRegs.CP0.n.Config = 0x440;
cpuRegs.CP0.n.Status.val = 0x70400004; //0x10900000 <-- wrong; // COP0 enabled | BEV = 1 | TS = 1
cpuRegs.CP0.n.PRid = 0x00002e20; // PRevID = Revision ID, same as R5900
fpuRegs.fprc[0] = 0x00002e00; // fpu Revision..
fpuRegs.fprc[31] = 0x01000001; // fpu Status/Control
vu0Reset();
vu1Reset();
hwReset();
vif0Reset();
vif1Reset();
rcntInit();
psxReset();
}
void cpuShutdown()
{
hwShutdown();
// biosShutdown();
psxShutdown();
vu0Shutdown();
vu1Shutdown();
memShutdown();
gsShutdown();
disR5900FreeSyms();
Cpu->Shutdown();
}
void cpuException(u32 code, u32 bd) {
u32 offset;
cpuRegs.CP0.n.Cause = code & 0xffff;
if(cpuRegs.CP0.n.Status.b.ERL == 0){ //Error Level 0-1
if(((code & 0x7C) >= 0x8) && ((code & 0x7C) <= 0xC)) offset = 0x0; //TLB Refill
else if ((code & 0x7C) == 0x0) offset = 0x200; //Interrupt
else offset = 0x180; // Everything else
if (cpuRegs.CP0.n.Status.b.EXL == 0) {
cpuRegs.CP0.n.Status.b.EXL = 1;
if (bd) {
SysPrintf("branch delay!!\n");
cpuRegs.CP0.n.EPC = cpuRegs.pc - 4;
cpuRegs.CP0.n.Cause |= 0x80000000;
} else {
cpuRegs.CP0.n.EPC = cpuRegs.pc;
cpuRegs.CP0.n.Cause &= ~0x80000000;
}
} else {
offset = 0x180; //Overrride the cause
SysPrintf("cpuException: Status.EXL = 1 cause %x\n", code);
}
if (cpuRegs.CP0.n.Status.b.BEV == 0) {
cpuRegs.pc = 0x80000000 + offset;
} else {
cpuRegs.pc = 0xBFC00200 + offset;
}
} else { //Error Level 2
SysPrintf("FIX ME: Level 2 cpuException\n");
if((code & 0x38000) <= 0x8000 ) { //Reset / NMI
cpuRegs.pc = 0xBFC00000;
SysPrintf("Reset request\n");
UpdateCP0Status();
return;
} else if((code & 0x38000) == 0x10000) offset = 0x80; //Performance Counter
else if((code & 0x38000) == 0x18000) offset = 0x100; //Debug
else SysPrintf("Unknown Level 2 Exception!! Cause %x\n", code);
if (cpuRegs.CP0.n.Status.b.EXL == 0) {
cpuRegs.CP0.n.Status.b.EXL = 1;
if (bd) {
SysPrintf("branch delay!!\n");
cpuRegs.CP0.n.EPC = cpuRegs.pc - 4;
cpuRegs.CP0.n.Cause |= 0x80000000;
} else {
cpuRegs.CP0.n.EPC = cpuRegs.pc;
cpuRegs.CP0.n.Cause &= ~0x80000000;
}
} else {
offset = 0x180; //Overrride the cause
SysPrintf("cpuException: Status.EXL = 1 cause %x\n", code);
}
if (cpuRegs.CP0.n.Status.b.DEV == 0) {
cpuRegs.pc = 0x80000000 + offset;
} else {
cpuRegs.pc = 0xBFC00200 + offset;
}
}
UpdateCP0Status();
}
void cpuTlbMiss(u32 addr, u32 bd, u32 excode) {
SysPrintf("cpuTlbMiss %x, %x, status=%x, code=%x\n", cpuRegs.pc, cpuRegs.cycle, cpuRegs.CP0.n.Status.val, excode);
if (bd) {
SysPrintf("branch delay!!\n");
}
cpuRegs.CP0.n.BadVAddr = addr;
cpuRegs.CP0.n.Context &= 0xFF80000F;
cpuRegs.CP0.n.Context |= (addr >> 9) & 0x007FFFF0;
cpuRegs.CP0.n.EntryHi = (addr & 0xFFFFE000) | (cpuRegs.CP0.n.EntryHi & 0x1FFF);
cpuRegs.CP0.n.Cause = excode;
if (!(cpuRegs.CP0.n.Status.val & 0x2)) { // EXL bit
cpuRegs.CP0.n.EPC = cpuRegs.pc - 4;
}
if ((cpuRegs.CP0.n.Status.val & 0x1) == 0) {
cpuRegs.pc = 0x80000000;
} else {
cpuRegs.pc = 0x80000180;
}
cpuRegs.CP0.n.Status.b.EXL = 1;
UpdateCP0Status();
// Log=1; varLog|= 0x40000000;
}
void cpuTlbMissR(u32 addr, u32 bd) {
cpuTlbMiss(addr, bd, EXC_CODE_TLBL);
}
void cpuTlbMissW(u32 addr, u32 bd) {
cpuTlbMiss(addr, bd, EXC_CODE_TLBS);
}
void JumpCheckSym(u32 addr, u32 pc) {
#if 0
// if (addr == 0x80051770) { SysPrintf("Log!: %s\n", PSM(cpuRegs.GPR.n.a0.UL[0])); Log=1; varLog|= 0x40000000; }
if (addr == 0x8002f150) { SysPrintf("printk: %s\n", PSM(cpuRegs.GPR.n.a0.UL[0])); }
if (addr == 0x8002aba0) return;
if (addr == 0x8002f450) return;
if (addr == 0x800dd520) return;
// if (addr == 0x80049300) SysPrintf("register_blkdev: %x\n", cpuRegs.GPR.n.a0.UL[0]);
if (addr == 0x8013cb70) { SysPrintf("change_root: %x\n", cpuRegs.GPR.n.a0.UL[0]); }
// if (addr == 0x8013d1e8) { SysPrintf("Log!\n"); Log++; if (Log==2) exit(0); varLog|= 0x40000000; }
// if (addr == 0x00234e88) { SysPrintf("StoreImage\n"); Log=1; /*psMu32(0x234e88) = 0x03e00008; psMu32(0x234e8c) = 0;*/ }
#endif
/* if ((pc >= 0x00131D50 &&
pc < 0x00132454) ||
(pc >= 0x00786a90 &&
pc < 0x00786ac8))*/
/*if (varLog & 0x40000000) {
char *str;
char *strf;
str = disR5900GetSym(addr);
if (str != NULL) {
strf = disR5900GetUpperSym(pc);
if (strf) {
SysPrintf("Func %8.8x: %s (called by %8.8x: %s)\n", addr, str, pc, strf);
} else {
SysPrintf("Func %8.8x: %s (called by %x)\n", addr, str, pc);
}
if (!strcmp(str, "printf")) { SysPrintf("%s\n", (char*)PSM(cpuRegs.GPR.n.a0.UL[0])); }
if (!strcmp(str, "printk")) { SysPrintf("%s\n", (char*)PSM(cpuRegs.GPR.n.a0.UL[0])); }
}
}*/
}
void JumpCheckSymRet(u32 addr) {
/*if (varLog & 0x40000000) {
char *str;
str = disR5900GetUpperSym(addr);
if (str != NULL) {
SysPrintf("Return : %s, v0=%8.8x\n", str, cpuRegs.GPR.n.v0.UL[0]);
}
}*/
}
__inline void _cpuTestMissingINTC() {
if (cpuRegs.CP0.n.Status.val & 0x400 &&
psHu32(INTC_STAT) & psHu32(INTC_MASK)) {
if ((cpuRegs.interrupt & (1 << 30)) == 0) {
SysPrintf("*PCSX2*: Error, missing INTC Interrupt\n");
}
}
}
__inline void _cpuTestMissingDMAC() {
if (cpuRegs.CP0.n.Status.val & 0x800 &&
(psHu16(0xe012) & psHu16(0xe010) ||
psHu16(0xe010) & 0x8000)) {
if ((cpuRegs.interrupt & (1 << 31)) == 0) {
SysPrintf("*PCSX2*: Error, missing DMAC Interrupt\n");
}
}
}
void cpuTestMissingHwInts() {
if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) {
_cpuTestMissingINTC();
_cpuTestMissingDMAC();
// _cpuTestTIMR();
}
}
#define TESTINT(n, callback) { \
if ( (cpuRegs.interrupt & (1 << n)) ) { \
if( ((int)(cpuRegs.cycle - cpuRegs.sCycle[n]) >= cpuRegs.eCycle[n]) ) { \
if (callback() == 1) { \
cpuRegs.interrupt &= ~(1 << n); \
} \
} \
else if( (int)(g_nextBranchCycle - cpuRegs.sCycle[n]) > cpuRegs.eCycle[n] ) { \
g_nextBranchCycle = cpuRegs.sCycle[n] + cpuRegs.eCycle[n]; \
} \
} \
} \
void _cpuTestInterrupts() {
inter = cpuRegs.interrupt;
/* These are 'pcsx2 interrupts', they handle asynchronous stuff
that depends on the cycle timings */
TESTINT(0, vif0Interrupt);
TESTINT(10, vifMFIFOInterrupt);
TESTINT(1, vif1Interrupt);
TESTINT(11, gifMFIFOInterrupt);
TESTINT(2, gsInterrupt);
TESTINT(3, ipu0Interrupt);
TESTINT(4, ipu1Interrupt);
TESTINT(5, EEsif0Interrupt);
TESTINT(6, EEsif1Interrupt);
TESTINT(8, SPRFROMinterrupt);
TESTINT(9, SPRTOinterrupt);
if ((cpuRegs.CP0.n.Status.val & 0x10007) != 0x10001) return;
TESTINT(30, intcInterrupt);
TESTINT(31, dmacInterrupt);
}
u32 s_iLastCOP0Cycle = 0;
u32 s_iLastPERFCycle[2] = {0,0};
static void _cpuTestTIMR() {
cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle;
s_iLastCOP0Cycle = cpuRegs.cycle;
if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) {
cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0];
s_iLastPERFCycle[0] = cpuRegs.cycle;
}
if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) {
cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1];
s_iLastPERFCycle[1] = cpuRegs.cycle;
}
if ( (cpuRegs.CP0.n.Status.val & 0x8000) &&
cpuRegs.CP0.n.Count >= cpuRegs.CP0.n.Compare && cpuRegs.CP0.n.Count < cpuRegs.CP0.n.Compare+1000 ) {
SysPrintf("timr intr: %x, %x\n", cpuRegs.CP0.n.Count, cpuRegs.CP0.n.Compare);
cpuException(0x808000, cpuRegs.branch);
}
}
#define EE_WAIT_CYCLE 512
// if cpuRegs.cycle is greater than this cycle, should check cpuBranchTest for updates
u32 g_nextBranchCycle = 0;
u32 s_lastvsync[2];
extern u8 g_globalXMMSaved, g_globalMMXSaved;
u32 loaded = 0;
void IntcpuBranchTest()
{
assert( !g_globalXMMSaved && !g_globalMMXSaved );
g_EEFreezeRegs = 0;
g_nextBranchCycle = cpuRegs.cycle + EE_WAIT_CYCLE;
if ((int)(cpuRegs.cycle - nextsCounter) >= nextCounter)
rcntUpdate();
if (cpuRegs.interrupt)
_cpuTestInterrupts();
if( (int)(g_nextBranchCycle-nextsCounter) >= nextCounter )
g_nextBranchCycle = nextsCounter+nextCounter;
//#ifdef CPU_LOG
// cpuTestMissingHwInts();
//#endif
_cpuTestTIMR();
EEsCycle += cpuRegs.cycle - EEoCycle;
EEoCycle = cpuRegs.cycle;
psxCpu->ExecuteBlock();
if (VU0.VI[REG_VPU_STAT].UL & 0x1) {
Cpu->ExecuteVU0Block();
}
if (VU0.VI[REG_VPU_STAT].UL & 0x100) {
Cpu->ExecuteVU1Block();
}
if( (int)cpuRegs.cycle-(int)g_nextBranchCycle > 0 )
g_nextBranchCycle = cpuRegs.cycle+1;
assert( !g_globalXMMSaved && !g_globalMMXSaved );
g_EEFreezeRegs = 1;
}
void cpuBranchTest()
{
assert( !g_globalXMMSaved && !g_globalMMXSaved );
g_EEFreezeRegs = 0;
// if( !loaded && cpuRegs.cycle > 0x08000000 ) {
// char strstate[255];
// sprintf(strstate, "sstates/%8.8x.000", ElfCRC);
// LoadState(strstate);
// loaded = 1;
// }
g_nextBranchCycle = cpuRegs.cycle + EE_WAIT_CYCLE;
if ((int)(cpuRegs.cycle - nextsCounter) >= nextCounter)
rcntUpdate();
if (cpuRegs.interrupt)
_cpuTestInterrupts();
if( (int)(g_nextBranchCycle-nextsCounter) >= nextCounter )
g_nextBranchCycle = nextsCounter+nextCounter;
//#ifdef CPU_LOG
// cpuTestMissingHwInts();
//#endif
_cpuTestTIMR();
EEsCycle += cpuRegs.cycle - EEoCycle;
EEoCycle = cpuRegs.cycle;
psxCpu->ExecuteBlock();
if (VU0.VI[REG_VPU_STAT].UL & 0x1) {
Cpu->ExecuteVU0Block();
}
if( (int)cpuRegs.cycle-(int)g_nextBranchCycle > 0 )
g_nextBranchCycle = cpuRegs.cycle+1;
assert( !g_globalXMMSaved && !g_globalMMXSaved );
g_EEFreezeRegs = 1;
}
static void _cpuTestINTC() {
if (cpuRegs.CP0.n.Status.val & 0x400 ){
if (psHu32(INTC_STAT) & psHu32(INTC_MASK)) {
if ((cpuRegs.interrupt & (1 << 30)) == 0) {
INT(30,4);
}
}
}
}
static void _cpuTestDMAC() {
if (cpuRegs.CP0.n.Status.val & 0x800 ){
if (psHu16(0xe012) & psHu16(0xe010) ||
psHu16(0xe010) & 0x8000) {
if ( (cpuRegs.interrupt & (1 << 31)) == 0) {
INT(31, 4);
}
}
}
}
void cpuTestHwInts() {
if ((cpuRegs.CP0.n.Status.val & 0x10007) != 0x10001) return;
_cpuTestINTC();
_cpuTestDMAC();
_cpuTestTIMR();
}
void cpuTestINTCInts() {
if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) {
_cpuTestINTC();
}
}
void cpuTestDMACInts() {
if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) {
_cpuTestDMAC();
}
}
void cpuTestTIMRInts() {
if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) {
_cpuTestTIMR();
}
}
extern BOOL bExecBIOS;
void cpuExecuteBios()
{
// filter CPU options
if( CHECK_EEREC ) Config.Options |= PCSX2_COP2REC;
else Config.Options &= ~PCSX2_COP2REC;
if( !cpucaps.hasStreamingSIMDExtensions ) {
Config.Options &= ~(PCSX2_VU1REC|PCSX2_VU0REC);
}
// remove frame skipping if GS doesn't support it
switch(CHECK_FRAMELIMIT) {
case PCSX2_FRAMELIMIT_SKIP:
case PCSX2_FRAMELIMIT_VUSKIP:
if( GSsetFrameSkip == NULL )
Config.Options &= ~PCSX2_FRAMELIMIT_MASK;
break;
}
SysPrintf("Using Frame Skipping: ");
switch(CHECK_FRAMELIMIT) {
case PCSX2_FRAMELIMIT_NORMAL: SysPrintf("Normal\n"); break;
case PCSX2_FRAMELIMIT_LIMIT: SysPrintf("Limit\n"); break;
case PCSX2_FRAMELIMIT_SKIP: SysPrintf("Skip\n"); break;
case PCSX2_FRAMELIMIT_VUSKIP: SysPrintf("VU Skip\n"); break;
}
SysPrintf("* PCSX2 *: ExecuteBios\n");
bExecBIOS = TRUE;
while (cpuRegs.pc != 0x00200008 &&
cpuRegs.pc != 0x00100008) {
Cpu->ExecuteBlock();
}
bExecBIOS = FALSE;
// REC_CLEARM(0x00200008);
// REC_CLEARM(0x00100008);
// REC_CLEARM(cpuRegs.pc);
if( CHECK_EEREC ) Cpu->Reset();
SysPrintf("* PCSX2 *: ExecuteBios Complete\n");
GSprintf(5, "PCSX2 v" PCSX2_VERSION "\nExecuteBios Complete\n");
}
void cpuRestartCPU()
{
Cpu = CHECK_EEREC ? &recCpu : &intCpu;
// restart vus
if (Cpu->Init() == -1) {
SysClose();
exit(1);
}
vu0Init();
vu1Init();
Cpu->Reset();
psxRestartCPU();
}