From c126c5a9f96f7ac3ce881aa3071a19c62edc5b97 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Mon, 6 Apr 2009 14:15:02 +0000 Subject: [PATCH] "better" ppc exception handling. I don't see any real world difference, though git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2896 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Core/Src/HW/MemmapFunctions.cpp | 21 ++- .../Interpreter/Interpreter_Branch.cpp | 18 +-- Source/Core/Core/Src/PowerPC/PowerPC.cpp | 127 +++++++++++------- 3 files changed, 109 insertions(+), 57 deletions(-) diff --git a/Source/Core/Core/Src/HW/MemmapFunctions.cpp b/Source/Core/Core/Src/HW/MemmapFunctions.cpp index 86c1cc9d38..99ddecfe61 100644 --- a/Source/Core/Core/Src/HW/MemmapFunctions.cpp +++ b/Source/Core/Core/Src/HW/MemmapFunctions.cpp @@ -518,10 +518,23 @@ void GenerateDSIException(u32 _EffectiveAdress, bool _bWrite) void GenerateISIException() { - // 4 bit for Set if the translation of an attempted access is not found in the primary hash table entry group - // (HTEG), or in the rehashed secondary HTEG, or in the range of a DBAT register (page fault - // condition); otherwise cleared. - PowerPC::ppcState.spr[SPR_DSISR] = 0x4000000; + // shuffle2: ISI exception doesn't modify DSISR at all, to my knowledge... + //PowerPC::ppcState.spr[SPR_DSISR] = 0x4000000; // maybe this was a typo for PPC_EXC_DSISR_PAGE? + + // Instead, it modifies bits 1-4 in SRR1 depending on conditions: + // Bit 1: set if the translation of an attempted access is not found in the primary hash table entry group + // (HTEG), or in the rehashed secondary HTEG, or in the range of a IBAT register (page fault + // condition); otherwise cleared. + // Bit 2: cleared + // Bit 3: Set if the fetch access occurs to a direct-store segment (SR[T] = 1), to a noexecute + // segment (N bit set in segment descriptor), or to guarded memory + // when MSR[IR] = 1. Otherwise, cleared. + // Bit 4: Set if a memory access is not permitted by the page or IBAT protection + // mechanism, described in Chapter 7, “Memory Management”; otherwise cleared. + // Only one of 1,3, or 4 may be set at a time + + // For now let's just say that hash lookup failed + SRR1 = 0x10000000; INFO_LOG(MEMMAP, "Generate ISI Exception"); PowerPC::ppcState.Exceptions |= EXCEPTION_ISI; } diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp index 0f8b50bb60..c8d3a4475e 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp @@ -113,16 +113,18 @@ void CompiledBlock(UGeckoInstruction _inst) void rfi(UGeckoInstruction _inst) { - //Bits SRR1[0,5-9,16-23, 25-27, 30-31] are placed into the corresponding bits of the MSR. - //MSR[13] is set to 0. - const int mask = 0x87C0FF73; + // Restore saved bits from SRR1 to MSR. + const int mask = 0x87C0FFFF; MSR = (MSR & ~mask) | (SRR1 & mask); - MSR &= 0xFFFDFFFF; //TODO: VERIFY - NPC = SRR0; // TODO: VERIFY + //MSR[13] is set to 0. + MSR &= 0xFFFDFFFF; + // Here we should check if there are pending exceptions, and if their corresponding enable bits are set + // if above is true, we'd do: + //PowerPC::CheckExceptions(); + //else + // set NPC to saved offset and resume + NPC = SRR0; // TODO: VERIFY...docs say ignore top two bits? m_EndBlock = true; - // After an RFI, exceptions should be checked IMMEDIATELY without going back into - // other code! TODO(ector): fix this properly - // PowerPC::CheckExceptions(); } void rfid(UGeckoInstruction _inst) diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.cpp b/Source/Core/Core/Src/PowerPC/PowerPC.cpp index c11466c26a..e3950487c4 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/Src/PowerPC/PowerPC.cpp @@ -209,90 +209,128 @@ void CheckExceptions() if (!ppcState.Exceptions) return; - // TODO(ector): // gcemu uses the mask 0x87C0FFFF instead of 0x0780FF77 - // Investigate! + // shuffle2: the MSR bits saved to SRR1 depend on the type of + // exception being taken, the common mask is 0x87C00008. + // I guess gcemu just uses 0x87C0FFFF for simplicity + // I think we should too, or else we would have to check what type of + // exception a rfi is returning from - I doubt a real cpu does this - if (ppcState.Exceptions & EXCEPTION_FPU_UNAVAILABLE) - { - //This happens a lot - Gamecube OS uses deferred FPU context switching - SRR0 = PC; // re-execute the instruction - SRR1 = MSR & 0x0780FF77; - NPC = 0x80000800; + // Example procedure: + // set SRR0 to either PC or NPC + //SRR0 = NPC; + // save specified MSR bits + //SRR1 = MSR & 0x87C0FFFF; + // copy ILE bit to LE + //MSR |= (MSR >> 17) & 1; + // clear MSR as specified + //MSR &= ~0x04EF36; // 0x04FF36 also clears ME (only for machine check exception) + // set to exception type entry point + //NPC = 0x80000x00; - INFO_LOG(GEKKO, "EXCEPTION_FPU_UNAVAILABLE"); - ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE; - SRR1 |= 0x02; //recoverable + if (ppcState.Exceptions & EXCEPTION_ISI) + { + SRR0 = NPC; + //GenerateISIException() sets up SRR1 + SRR1 |= MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; + NPC = 0x80000400; + + INFO_LOG(GEKKO, "EXCEPTION_ISI"); + ppcState.Exceptions &= ~EXCEPTION_ISI; } + else if (ppcState.Exceptions & EXCEPTION_PROGRAM) + { + SRR0 = PC; + SRR1 = MSR & 0x87C0FFFF; + // say that it's a trap exception + SRR1 |= 0x40000; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; + NPC = 0x80000700; + + INFO_LOG(GEKKO, "EXCEPTION_PROGRAM"); + ppcState.Exceptions &= ~EXCEPTION_PROGRAM; + } else if (ppcState.Exceptions & EXCEPTION_SYSCALL) - { - SRR0 = NPC; // execute next instruction when we come back from handler - SRR1 = MSR & 0x0780FF77; + { + SRR0 = NPC; + SRR1 = MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; NPC = 0x80000C00; INFO_LOG(GEKKO, "EXCEPTION_SYSCALL (PC=%08x)", PC); ppcState.Exceptions &= ~EXCEPTION_SYSCALL; - SRR1 |= 0x02; //recoverable + } + else if (ppcState.Exceptions & EXCEPTION_FPU_UNAVAILABLE) + { + //This happens a lot - Gamecube OS uses deferred FPU context switching + SRR0 = PC; // re-execute the instruction + SRR1 = MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; + NPC = 0x80000800; + + INFO_LOG(GEKKO, "EXCEPTION_FPU_UNAVAILABLE"); + ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE; } else if (ppcState.Exceptions & EXCEPTION_DSI) { - SRR0 = PC; // re-execute the instruction - SRR1 = MSR & 0x0780FF77; + SRR0 = PC; + SRR1 = MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; NPC = 0x80000300; + //DSISR and DAR regs are changed in GenerateDSIException() INFO_LOG(GEKKO, "EXCEPTION_DSI"); - ppcState.Exceptions &= ~EXCEPTION_DSI; - //SRR1 |= 0x02; //make recoverable ? - } - else if (ppcState.Exceptions & EXCEPTION_ISI) - { - SRR0 = PC; - SRR1 = (MSR & 0x0780FF77) | 0x40000000; - NPC = 0x80000400; - - INFO_LOG(GEKKO, "EXCEPTION_ISI"); - ppcState.Exceptions &= ~EXCEPTION_ISI; - //SRR1 |= 0x02; //make recoverable ? - } + ppcState.Exceptions &= ~EXCEPTION_DSI; + } else if (ppcState.Exceptions & EXCEPTION_ALIGNMENT) { //This never happens ATM - SRR0 = NPC; - SRR1 = MSR & 0x0780FF77; + // perhaps we can get dcb* instructions to use this :p + SRR0 = PC; + SRR1 = MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; NPC = 0x80000600; + //TODO crazy amount of DSISR options to check out + INFO_LOG(GEKKO, "EXCEPTION_ALIGNMENT"); - ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT; - //SRR1 |= 0x02; //make recoverable ? + ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT; } // EXTERNAL INTTERUPT - else if (MSR & 0x0008000) + else if (MSR & 0x0008000) //hacky...the exception shouldn't be generated if EE isn't set... { if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT) { // Pokemon gets this "too early", it hasn't a handler yet - ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception - SRR0 = NPC; + SRR1 = MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; NPC = 0x80000500; - SRR1 = (MSR & 0x0780FF77); INFO_LOG(GEKKO, "EXCEPTION_EXTERNAL_INT"); + ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; - SRR1 |= 0x02; //set it to recoverable - _dbg_assert_msg_(GEKKO, (SRR1 & 0x02) != 0, "GEKKO", "EXTERNAL_INT unrecoverable???"); // unrecoverable exception !?! + _dbg_assert_msg_(GEKKO, (SRR1 & 0x02) != 0, "GEKKO", "EXTERNAL_INT unrecoverable???"); } else if (ppcState.Exceptions & EXCEPTION_DECREMENTER) { SRR0 = NPC; - SRR1 = MSR & 0x0000FF77; + SRR1 = MSR & 0x87C0FFFF; + MSR |= (MSR >> 17) & 1; + MSR &= ~0x04EF36; NPC = 0x80000900; - ppcState.Exceptions &= ~EXCEPTION_DECREMENTER; - INFO_LOG(GEKKO, "EXCEPTION_DECREMENTER"); - SRR1 |= 0x02; //make recoverable + ppcState.Exceptions &= ~EXCEPTION_DECREMENTER; } else { @@ -300,7 +338,6 @@ void CheckExceptions() ERROR_LOG(GEKKO, "Unknown EXTERNAL INTERRUPT exception: Exceptions == %08x", ppcState.Exceptions); } } - MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore } void OnIdle(u32 _uThreadAddr)