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
This commit is contained in:
Jake.Stine 2010-09-05 15:11:19 +00:00
parent 02b390b0e1
commit 1698382065
4 changed files with 47 additions and 110 deletions

View File

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

View File

@ -50,8 +50,7 @@ void hwReset()
{
hwInit();
memzero_ptr<Ps2MemSize::Hardware>( 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)

View File

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

View File

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