mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
02b390b0e1
commit
1698382065
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
25
pcsx2/Hw.cpp
25
pcsx2/Hw.cpp
|
@ -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)
|
||||
|
|
108
pcsx2/R5900.cpp
108
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))
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue