From 1698382065d7c4e5963a64642aa89ed36c36b33d Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Sun, 5 Sep 2010 15:11:19 +0000 Subject: [PATCH] Simplified CPU-level exception behavior: * Both INTC and DMAC exceptions are now issued together when possible (0x400 | 0x800 to the CAUSE register, respectively) * CPU exceptions are checked on every event now, instead of using scheduled interrupts on bits 30/31. This removes the need to constantly reschedule events during interrupt-disabled states. * CPU exception test is moved to the top of the EE event test. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3728 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/COP0.cpp | 20 +++++---- pcsx2/Hw.cpp | 25 +++++------ pcsx2/R5900.cpp | 108 +++++++++--------------------------------------- pcsx2/R5900.h | 4 +- 4 files changed, 47 insertions(+), 110 deletions(-) diff --git a/pcsx2/COP0.cpp b/pcsx2/COP0.cpp index 8ce8290f29..d537bde76b 100644 --- a/pcsx2/COP0.cpp +++ b/pcsx2/COP0.cpp @@ -20,9 +20,10 @@ u32 s_iLastCOP0Cycle = 0; u32 s_iLastPERFCycle[2] = { 0, 0 }; -__ri void UpdateCP0Status() { - //currently the 2 memory modes are not implemented. Given this function is called so much, - //it's commented out for now. Only the interrupt test is needed. (rama) +// Updates the CPU's mode of operation (either, Kernel, Supervisor, or User modes). +// Currently the different modes are not implemented. +// Given this function is called so much, it's commented out for now. (rama) +__ri void cpuUpdateOperationMode() { //u32 value = cpuRegs.CP0.n.Status.val; @@ -32,12 +33,12 @@ __ri void UpdateCP0Status() { //} else { // User Mode // memSetUserMode(); //} - cpuTestHwInts(); } void __fastcall WriteCP0Status(u32 value) { cpuRegs.CP0.n.Status.val = value; - UpdateCP0Status(); + cpuUpdateOperationMode(); + cpuSetNextBranchDelta(4); } void MapTLB(int i) @@ -532,7 +533,8 @@ void ERET() { cpuRegs.pc = cpuRegs.CP0.n.EPC; cpuRegs.CP0.n.Status.b.EXL = 0; } - UpdateCP0Status(); + cpuUpdateOperationMode(); + cpuSetNextBranchDelta(4); intSetBranch(); } @@ -540,7 +542,8 @@ void DI() { if (cpuRegs.CP0.n.Status.b._EDI || cpuRegs.CP0.n.Status.b.EXL || cpuRegs.CP0.n.Status.b.ERL || (cpuRegs.CP0.n.Status.b.KSU == 0)) { cpuRegs.CP0.n.Status.b.EIE = 0; - //UpdateCP0Status(); // ints are disabled so checking for them is kinda silly... + // IRQs are disabled so no need to do a cpu exception/event test... + //cpuSetNextBranchDelta(); } } @@ -548,7 +551,8 @@ void EI() { if (cpuRegs.CP0.n.Status.b._EDI || cpuRegs.CP0.n.Status.b.EXL || cpuRegs.CP0.n.Status.b.ERL || (cpuRegs.CP0.n.Status.b.KSU == 0)) { cpuRegs.CP0.n.Status.b.EIE = 1; - UpdateCP0Status(); + // schedule an event test, which will check for and raise pending IRQs. + cpuSetNextBranchDelta(4); } } diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp index 44a0003d6a..1202a1911e 100644 --- a/pcsx2/Hw.cpp +++ b/pcsx2/Hw.cpp @@ -50,8 +50,7 @@ void hwReset() { hwInit(); - memzero_ptr( eeHw ); - //memset(eeHw+0x2000, 0, 0x0000e000); + memzero( eeHw ); psHu32(SBUS_F260) = 0x1D000060; @@ -73,16 +72,16 @@ void hwReset() ipuDmaReset(); } -__fi void intcInterrupt() +__fi uint intcInterrupt() { if ((psHu32(INTC_STAT)) == 0) { //DevCon.Warning("*PCSX2*: intcInterrupt already cleared"); - return; + return 0; } if ((psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0) { //DevCon.Warning("*PCSX2*: No valid interrupt INTC_MASK: %x INTC_STAT: %x", psHu32(INTC_MASK), psHu32(INTC_STAT)); - return; + return 0; } HW_LOG("intcInterrupt %x", psHu32(INTC_STAT) & psHu32(INTC_MASK)); @@ -91,27 +90,29 @@ __fi void intcInterrupt() counters[1].hold = rcntRcount(1); } - cpuException(0x400, cpuRegs.branch); + //cpuException(0x400, cpuRegs.branch); + return 0x400; } -__fi void dmacInterrupt() +__fi uint dmacInterrupt() { if( ((psHu16(DMAC_STAT + 2) & psHu16(DMAC_STAT)) == 0 ) && ( psHu16(DMAC_STAT) & 0x8000) == 0 ) { //DevCon.Warning("No valid DMAC interrupt MASK %x STAT %x", psHu16(DMAC_STAT+2), psHu16(DMAC_STAT)); - return; + return 0; } - if (!(dmacRegs.ctrl.DMAE) || psHu8(DMAC_ENABLER+2) == 1) + if (!dmacRegs.ctrl.DMAE || psHu8(DMAC_ENABLER+2) == 1) { //DevCon.Warning("DMAC Suspended or Disabled on interrupt"); - return; + return 0; } HW_LOG("dmacInterrupt %x", (psHu16(DMAC_STAT + 2) & psHu16(DMAC_STAT) | - psHu16(DMAC_STAT) & 0x8000)); + psHu16(DMAC_STAT) & 0x8000)); - cpuException(0x800, cpuRegs.branch); + //cpuException(0x800, cpuRegs.branch); + return 0x800; } void hwIntcIrq(int n) diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 30c7ab6989..d774c0cd9d 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -130,7 +130,7 @@ __ri void cpuException(u32 code, u32 bd) //Reset / NMI cpuRegs.pc = 0xBFC00000; Console.Warning("Reset request"); - UpdateCP0Status(); + cpuUpdateOperationMode(); return; } else if((code & 0x38000) == 0x10000) @@ -167,7 +167,7 @@ __ri void cpuException(u32 code, u32 bd) else cpuRegs.pc = 0xBFC00200 + offset; - UpdateCP0Status(); + cpuUpdateOperationMode(); } void cpuTlbMiss(u32 addr, u32 bd, u32 excode) @@ -196,7 +196,7 @@ void cpuTlbMiss(u32 addr, u32 bd, u32 excode) } cpuRegs.CP0.n.Status.b.EXL = 1; - UpdateCP0Status(); + cpuUpdateOperationMode(); // Log=1; varLog|= 0x40000000; } @@ -208,33 +208,6 @@ void cpuTlbMissW(u32 addr, u32 bd) { cpuTlbMiss(addr, bd, EXC_CODE_TLBS); } -__fi void _cpuTestMissingINTC() { - if (cpuRegs.CP0.n.Status.val & 0x400 && - psHu32(INTC_STAT) & psHu32(INTC_MASK)) { - if ((cpuRegs.interrupt & (1 << 30)) == 0) { - Console.Error("*PCSX2*: Error, missing INTC Interrupt"); - } - } -} - -__fi void _cpuTestMissingDMAC() { - if (cpuRegs.CP0.n.Status.val & 0x800 && - (psHu16(0xe012) & psHu16(0xe010) || - psHu16(0xe010) & 0x8000)) { - if ((cpuRegs.interrupt & (1 << 31)) == 0) { - Console.Error("*PCSX2*: Error, missing DMAC Interrupt"); - } - } -} - -void cpuTestMissingHwInts() { - if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) { - _cpuTestMissingINTC(); - _cpuTestMissingDMAC(); -// _cpuTestTIMR(); - } -} - // sets a branch test to occur some time from an arbitrary starting point. __fi void cpuSetNextBranch( u32 startCycle, s32 delta ) { @@ -253,7 +226,7 @@ __fi void cpuSetNextBranchDelta( s32 delta ) cpuSetNextBranch( cpuRegs.cycle, delta ); } -// tests the cpu cycle agaisnt the given start and delta values. +// tests the cpu cycle against the given start and delta values. // Returns true if the delta time has passed. __fi int cpuTestCycle( u32 startCycle, s32 delta ) { @@ -361,8 +334,8 @@ static bool cpuIntsEnabled(int Interrupt) { bool IntType = !!(cpuRegs.CP0.n.Status.val & Interrupt); //Choose either INTC or DMAC, depending on what called it - return cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE && - !cpuRegs.CP0.n.Status.b.EXL && (cpuRegs.CP0.n.Status.b.ERL == 0) && IntType; + return IntType && cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE && + !cpuRegs.CP0.n.Status.b.EXL && (cpuRegs.CP0.n.Status.b.ERL == 0); } // if cpuRegs.cycle is greater than this cycle, should check cpuBranchTest for updates @@ -375,10 +348,19 @@ __fi void _cpuBranchTest_Shared() ScopedBool etest(eeEventTestIsActive); g_nextBranchCycle = cpuRegs.cycle + eeWaitCycles; + // ---- INTC / DMAC (CPU-level Exceptions) ----------------- + // Done first because exceptions raised during event tests need to be postponed a few + // cycles (fixes Grandia II [PAL], which does a spin loop on a vsync and expects to + // be able to read the value before the exception handler clears it). + + uint mask = intcInterrupt() | dmacInterrupt(); + if (cpuIntsEnabled(mask)) cpuException(mask, cpuRegs.branch); + + // ---- Counters ------------- // Important: the vsync counter must be the first to be checked. It includes emulation // escape/suspend hooks, and it's really a good idea to suspend/resume emulation before - // doing any actual meaninful branchtest logic. + // doing any actual meaningful branchtest logic. if( cpuTestCycle( nextsCounter, nextCounter ) ) { @@ -391,10 +373,10 @@ __fi void _cpuBranchTest_Shared() _cpuTestTIMR(); // ---- Interrupts ------------- - // Handles all interrupts except 30 and 31, which are handled later. + // These are basically just DMAC-related events, which also piggy-back the same bits as + // the PS2's own DMA channel IRQs and IRQ Masks. - if( cpuRegs.interrupt & ~(3<<30) ) - _cpuTestInterrupts(); + _cpuTestInterrupts(); // ---- IOP ------------- // * It's important to run a psxBranchTest before calling ExecuteBlock. This @@ -418,34 +400,7 @@ __fi void _cpuBranchTest_Shared() //if( EEsCycle < -450 ) // Console.WriteLn( " IOP ahead by: %d cycles", -EEsCycle ); - // Experimental and Probably Unnecessary Logic --> - // Check if the EE already has an exception pending, and if so we shouldn't - // waste too much time updating the IOP. Theory being that the EE and IOP should - // run closely in sync during raised exception events. But in practice it didn't - // seem to make much of a difference. - - // Note: The IOP is very good about chaining blocks together so it tends to - // run lots of cycles, even with only 32 (4 IOP) cycles specified here. That's - // probably why it doesn't improve sync much. - - /*bool eeExceptPending = cpuIntsEnabled() && - //( cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE && (cpuRegs.CP0.n.Status.b.ERL == 0) ) && - //( (cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001 ) && - ( (cpuRegs.interrupt & (3<<30)) != 0 ); - - if( eeExceptPending ) - { - // ExecuteBlock returns a negative value, so subtract it from the cycle count - // specified to get the total cycles processed! :D - int cycleCount = std::min( EEsCycle, (s32)(eeWaitCycles>>4) ); - int cyclesRun = cycleCount - psxCpu->ExecuteBlock( cycleCount ); - EEsCycle -= cyclesRun; - //Console.Warning( "IOP Exception-Pending Execution -- EEsCycle: %d", EEsCycle ); - } - else*/ - { - EEsCycle = psxCpu->ExecuteBlock( EEsCycle ); - } + EEsCycle = psxCpu->ExecuteBlock( EEsCycle ); iopBranchAction = false; } @@ -479,32 +434,16 @@ __fi void _cpuBranchTest_Shared() // Apply vsync and other counter nextCycles cpuSetNextBranch( nextsCounter, nextCounter ); - - // ---- INTC / DMAC Exceptions ----------------- - // Raise the INTC and DMAC interrupts here, which usually throw exceptions. - // This should be done last since the IOP and the VU0 can raise several EE - // exceptions. - - //if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) - if( cpuIntsEnabled(0x400) ) TESTINT(30, intcInterrupt); - if( cpuIntsEnabled(0x800) ) TESTINT(31, dmacInterrupt); } __ri void cpuTestINTCInts() { - // Check the internal Event System -- if one's already scheduled then don't bother: - if( cpuRegs.interrupt & (1 << 30) ) return; - // Check the COP0's Status register for general interrupt disables, and the 0x400 // bit (which is INTC master toggle). if( !cpuIntsEnabled(0x400) ) return; if( (psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0 ) return; - cpuRegs.interrupt|= 1 << 30; - cpuRegs.sCycle[30] = cpuRegs.cycle; - cpuRegs.eCycle[30] = 4; //Needs to be 4 to account for bus delays/pipelines etc - cpuSetNextBranchDelta( 4 ); if(eeEventTestIsActive && (psxCycleEE > 0)) { @@ -515,9 +454,6 @@ __ri void cpuTestINTCInts() __fi void cpuTestDMACInts() { - // Check the internal Event System -- if one's already scheduled then don't bother: - if ( cpuRegs.interrupt & (1 << 31) ) return; - // Check the COP0's Status register for general interrupt disables, and the 0x800 // bit (which is the DMAC master toggle). if( !cpuIntsEnabled(0x800) ) return; @@ -525,10 +461,6 @@ __fi void cpuTestDMACInts() if ( ( (psHu16(0xe012) & psHu16(0xe010)) == 0) && ( (psHu16(0xe010) & 0x8000) == 0) ) return; - cpuRegs.interrupt|= 1 << 31; - cpuRegs.sCycle[31] = cpuRegs.cycle; - cpuRegs.eCycle[31] = 4; //Needs to be 4 to account for bus delays/pipelines etc - cpuSetNextBranchDelta( 4 ); if(eeEventTestIsActive && (psxCycleEE > 0)) { diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index 1793e01406..c3431d6119 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -403,8 +403,8 @@ enum EE_EventType }; extern void CPU_INT( EE_EventType n, s32 ecycle ); -extern void intcInterrupt(); -extern void dmacInterrupt(); +extern uint intcInterrupt(); +extern uint dmacInterrupt(); extern void cpuInit();