dolphin/Source/Core/Core/PowerPC/PowerPC.cpp

526 lines
12 KiB
C++
Raw Normal View History

// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/FPURoundMode.h"
#include "Common/MathUtil.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Host.h"
#include "Core/HW/CPU.h"
#include "Core/HW/EXI.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h"
#include "Core/PowerPC/CPUCoreBase.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/PPCTables.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
CPUCoreBase *cpu_core_base;
namespace PowerPC
{
// STATE_TO_SAVE
PowerPCState GC_ALIGNED16(ppcState);
static volatile CPUState state = CPU_POWERDOWN;
Interpreter * const interpreter = Interpreter::getInstance();
2014-07-08 15:58:25 +02:00
static CoreMode mode;
Watches watches;
BreakPoints breakpoints;
MemChecks memchecks;
PPCDebugInterface debug_interface;
u32 CompactCR()
{
Optimize PPC CR emulation by using magic 64 bit values PowerPC has a 32 bit CR register, which is used to store flags for results of computations. Most instructions have an optional bit that tells the CPU whether the flags should be updated. This 32 bit register actually contains 8 sets of 4 flags: Summary Overflow (SO), Equals (EQ), Greater Than (GT), Less Than (LT). These 8 sets are usually called CR0-CR7 and accessed independently. In the most common operations, the flags are computed from the result of the operation in the following fashion: * EQ is set iff result == 0 * LT is set iff result < 0 * GT is set iff result > 0 * (Dolphin does not emulate SO) While X86 architectures have a similar concept of flags, it is very difficult to access the FLAGS register directly to translate its value to an equivalent PowerPC value. With the current Dolphin implementation, updating a PPC CR register requires CPU branching, which has a few performance issues: it uses space in the BTB, and in the worst case (!GT, !LT, EQ) requires 2 branches not taken. After some brainstorming on IRC about how this could be improved, calc84maniac figured out a neat trick that makes common CR operations way more efficient to JIT on 64 bit X86 architectures. It relies on emulating each CRn bitfield with a 64 bit register internally, whose value is the result of the operation from which flags are updated, sign extended to 64 bits. Then, checking if a CR bit is set can be done in the following way: * EQ is set iff LOWER_32_BITS(cr_64b_val) == 0 * GT is set iff (s64)cr_64b_val > 0 * LT is set iff bit 62 of cr_64b_val is set To take a few examples, if the result of an operation is: * -1 (0xFFFFFFFFFFFFFFFF) -> lower 32 bits not 0 => !EQ -> (s64)val (-1) is not > 0 => !GT -> bit 62 is set => LT !EQ, !GT, LT * 0 (0x0000000000000000) -> lower 32 bits are 0 => EQ -> (s64)val (0) is not > 0 => !GT -> bit 62 is not set => !LT EQ, !GT, !LT * 1 (0x0000000000000001) -> lower 32 bits not 0 => !EQ -> (s64)val (1) is > 0 => GT -> bit 62 is not set => !LT !EQ, GT, !LT Sometimes we need to convert PPC CR values to these 64 bit values. The following convention is used in this case: * Bit 0 (LSB) is set iff !EQ * Bit 62 is set iff LT * Bit 63 is set iff !GT * Bit 32 always set to disambiguize between EQ and GT Some more examples: * !EQ, GT, LT -> 0x4000000100000001 (!B63, B62, B32, B0) -> lower 32 bits not 0 => !EQ -> (s64)val is > 0 => GT -> bit 62 is set => LT * EQ, GT, !LT -> 0x0000000100000000 -> lower 32 bits are 0 => EQ -> (s64)val is > 0 (note: B32) => GT -> bit 62 is not set => !LT
2014-05-31 00:43:52 +02:00
u32 new_cr = 0;
for (int i = 0; i < 8; i++)
{
Optimize PPC CR emulation by using magic 64 bit values PowerPC has a 32 bit CR register, which is used to store flags for results of computations. Most instructions have an optional bit that tells the CPU whether the flags should be updated. This 32 bit register actually contains 8 sets of 4 flags: Summary Overflow (SO), Equals (EQ), Greater Than (GT), Less Than (LT). These 8 sets are usually called CR0-CR7 and accessed independently. In the most common operations, the flags are computed from the result of the operation in the following fashion: * EQ is set iff result == 0 * LT is set iff result < 0 * GT is set iff result > 0 * (Dolphin does not emulate SO) While X86 architectures have a similar concept of flags, it is very difficult to access the FLAGS register directly to translate its value to an equivalent PowerPC value. With the current Dolphin implementation, updating a PPC CR register requires CPU branching, which has a few performance issues: it uses space in the BTB, and in the worst case (!GT, !LT, EQ) requires 2 branches not taken. After some brainstorming on IRC about how this could be improved, calc84maniac figured out a neat trick that makes common CR operations way more efficient to JIT on 64 bit X86 architectures. It relies on emulating each CRn bitfield with a 64 bit register internally, whose value is the result of the operation from which flags are updated, sign extended to 64 bits. Then, checking if a CR bit is set can be done in the following way: * EQ is set iff LOWER_32_BITS(cr_64b_val) == 0 * GT is set iff (s64)cr_64b_val > 0 * LT is set iff bit 62 of cr_64b_val is set To take a few examples, if the result of an operation is: * -1 (0xFFFFFFFFFFFFFFFF) -> lower 32 bits not 0 => !EQ -> (s64)val (-1) is not > 0 => !GT -> bit 62 is set => LT !EQ, !GT, LT * 0 (0x0000000000000000) -> lower 32 bits are 0 => EQ -> (s64)val (0) is not > 0 => !GT -> bit 62 is not set => !LT EQ, !GT, !LT * 1 (0x0000000000000001) -> lower 32 bits not 0 => !EQ -> (s64)val (1) is > 0 => GT -> bit 62 is not set => !LT !EQ, GT, !LT Sometimes we need to convert PPC CR values to these 64 bit values. The following convention is used in this case: * Bit 0 (LSB) is set iff !EQ * Bit 62 is set iff LT * Bit 63 is set iff !GT * Bit 32 always set to disambiguize between EQ and GT Some more examples: * !EQ, GT, LT -> 0x4000000100000001 (!B63, B62, B32, B0) -> lower 32 bits not 0 => !EQ -> (s64)val is > 0 => GT -> bit 62 is set => LT * EQ, GT, !LT -> 0x0000000100000000 -> lower 32 bits are 0 => EQ -> (s64)val is > 0 (note: B32) => GT -> bit 62 is not set => !LT
2014-05-31 00:43:52 +02:00
new_cr |= GetCRField(i) << (28 - i * 4);
}
return new_cr;
}
void ExpandCR(u32 cr)
{
for (int i = 0; i < 8; i++)
{
Optimize PPC CR emulation by using magic 64 bit values PowerPC has a 32 bit CR register, which is used to store flags for results of computations. Most instructions have an optional bit that tells the CPU whether the flags should be updated. This 32 bit register actually contains 8 sets of 4 flags: Summary Overflow (SO), Equals (EQ), Greater Than (GT), Less Than (LT). These 8 sets are usually called CR0-CR7 and accessed independently. In the most common operations, the flags are computed from the result of the operation in the following fashion: * EQ is set iff result == 0 * LT is set iff result < 0 * GT is set iff result > 0 * (Dolphin does not emulate SO) While X86 architectures have a similar concept of flags, it is very difficult to access the FLAGS register directly to translate its value to an equivalent PowerPC value. With the current Dolphin implementation, updating a PPC CR register requires CPU branching, which has a few performance issues: it uses space in the BTB, and in the worst case (!GT, !LT, EQ) requires 2 branches not taken. After some brainstorming on IRC about how this could be improved, calc84maniac figured out a neat trick that makes common CR operations way more efficient to JIT on 64 bit X86 architectures. It relies on emulating each CRn bitfield with a 64 bit register internally, whose value is the result of the operation from which flags are updated, sign extended to 64 bits. Then, checking if a CR bit is set can be done in the following way: * EQ is set iff LOWER_32_BITS(cr_64b_val) == 0 * GT is set iff (s64)cr_64b_val > 0 * LT is set iff bit 62 of cr_64b_val is set To take a few examples, if the result of an operation is: * -1 (0xFFFFFFFFFFFFFFFF) -> lower 32 bits not 0 => !EQ -> (s64)val (-1) is not > 0 => !GT -> bit 62 is set => LT !EQ, !GT, LT * 0 (0x0000000000000000) -> lower 32 bits are 0 => EQ -> (s64)val (0) is not > 0 => !GT -> bit 62 is not set => !LT EQ, !GT, !LT * 1 (0x0000000000000001) -> lower 32 bits not 0 => !EQ -> (s64)val (1) is > 0 => GT -> bit 62 is not set => !LT !EQ, GT, !LT Sometimes we need to convert PPC CR values to these 64 bit values. The following convention is used in this case: * Bit 0 (LSB) is set iff !EQ * Bit 62 is set iff LT * Bit 63 is set iff !GT * Bit 32 always set to disambiguize between EQ and GT Some more examples: * !EQ, GT, LT -> 0x4000000100000001 (!B63, B62, B32, B0) -> lower 32 bits not 0 => !EQ -> (s64)val is > 0 => GT -> bit 62 is set => LT * EQ, GT, !LT -> 0x0000000100000000 -> lower 32 bits are 0 => EQ -> (s64)val is > 0 (note: B32) => GT -> bit 62 is not set => !LT
2014-05-31 00:43:52 +02:00
SetCRField(i, (cr >> (28 - i * 4)) & 0xF);
}
}
void DoState(PointerWrap &p)
{
// some of this code has been disabled, because
// it changes registers even in MODE_MEASURE (which is suspicious and seems like it could cause desyncs)
// and because the values it's changing have been added to CoreTiming::DoState, so it might conflict to mess with them here.
// rSPR(SPR_DEC) = SystemTimers::GetFakeDecrementer();
// *((u64 *)&TL) = SystemTimers::GetFakeTimeBase(); //works since we are little endian and TL comes first :)
p.DoPOD(ppcState);
// SystemTimers::DecrementerSet();
// SystemTimers::TimeBaseSet();
2013-02-26 13:49:00 -06:00
JitInterface::DoState(p);
}
2014-07-08 14:29:26 +02:00
static void ResetRegisters()
{
memset(ppcState.ps, 0, sizeof(ppcState.ps));
memset(ppcState.gpr, 0, sizeof(ppcState.gpr));
memset(ppcState.spr, 0, sizeof(ppcState.spr));
/*
0x00080200 = lonestar 2.0
0x00088202 = lonestar 2.2
0x70000100 = gekko 1.0
0x00080100 = gekko 2.0
0x00083203 = gekko 2.3a
0x00083213 = gekko 2.3b
0x00083204 = gekko 2.4
0x00083214 = gekko 2.4e (8SE) - retail HW2
*/
ppcState.spr[SPR_PVR] = 0x00083214;
ppcState.spr[SPR_HID1] = 0x80000000; // We're running at 3x the bus clock
ppcState.spr[SPR_ECID_U] = 0x0d96e200;
ppcState.spr[SPR_ECID_M] = 0x1840c00d;
ppcState.spr[SPR_ECID_L] = 0x82bb08e8;
ppcState.fpscr = 0;
ppcState.pc = 0;
ppcState.npc = 0;
ppcState.Exceptions = 0;
Optimize PPC CR emulation by using magic 64 bit values PowerPC has a 32 bit CR register, which is used to store flags for results of computations. Most instructions have an optional bit that tells the CPU whether the flags should be updated. This 32 bit register actually contains 8 sets of 4 flags: Summary Overflow (SO), Equals (EQ), Greater Than (GT), Less Than (LT). These 8 sets are usually called CR0-CR7 and accessed independently. In the most common operations, the flags are computed from the result of the operation in the following fashion: * EQ is set iff result == 0 * LT is set iff result < 0 * GT is set iff result > 0 * (Dolphin does not emulate SO) While X86 architectures have a similar concept of flags, it is very difficult to access the FLAGS register directly to translate its value to an equivalent PowerPC value. With the current Dolphin implementation, updating a PPC CR register requires CPU branching, which has a few performance issues: it uses space in the BTB, and in the worst case (!GT, !LT, EQ) requires 2 branches not taken. After some brainstorming on IRC about how this could be improved, calc84maniac figured out a neat trick that makes common CR operations way more efficient to JIT on 64 bit X86 architectures. It relies on emulating each CRn bitfield with a 64 bit register internally, whose value is the result of the operation from which flags are updated, sign extended to 64 bits. Then, checking if a CR bit is set can be done in the following way: * EQ is set iff LOWER_32_BITS(cr_64b_val) == 0 * GT is set iff (s64)cr_64b_val > 0 * LT is set iff bit 62 of cr_64b_val is set To take a few examples, if the result of an operation is: * -1 (0xFFFFFFFFFFFFFFFF) -> lower 32 bits not 0 => !EQ -> (s64)val (-1) is not > 0 => !GT -> bit 62 is set => LT !EQ, !GT, LT * 0 (0x0000000000000000) -> lower 32 bits are 0 => EQ -> (s64)val (0) is not > 0 => !GT -> bit 62 is not set => !LT EQ, !GT, !LT * 1 (0x0000000000000001) -> lower 32 bits not 0 => !EQ -> (s64)val (1) is > 0 => GT -> bit 62 is not set => !LT !EQ, GT, !LT Sometimes we need to convert PPC CR values to these 64 bit values. The following convention is used in this case: * Bit 0 (LSB) is set iff !EQ * Bit 62 is set iff LT * Bit 63 is set iff !GT * Bit 32 always set to disambiguize between EQ and GT Some more examples: * !EQ, GT, LT -> 0x4000000100000001 (!B63, B62, B32, B0) -> lower 32 bits not 0 => !EQ -> (s64)val is > 0 => GT -> bit 62 is set => LT * EQ, GT, !LT -> 0x0000000100000000 -> lower 32 bits are 0 => EQ -> (s64)val is > 0 (note: B32) => GT -> bit 62 is not set => !LT
2014-05-31 00:43:52 +02:00
for (auto& v : ppcState.cr_val)
v = 0x8000000000000001;
TL = 0;
TU = 0;
SystemTimers::TimeBaseSet();
// MSR should be 0x40, but we don't emulate BS1, so it would never be turned off :}
ppcState.msr = 0;
rDEC = 0xFFFFFFFF;
SystemTimers::DecrementerSet();
}
void Init(int cpu_core)
{
2013-02-26 13:49:00 -06:00
FPURoundMode::SetPrecisionMode(FPURoundMode::PREC_53);
memset(ppcState.sr, 0, sizeof(ppcState.sr));
ppcState.pagetable_base = 0;
ppcState.pagetable_hashmask = 0;
2012-01-02 02:20:22 -08:00
for (int tlb = 0; tlb < 2; tlb++)
{
for (int set = 0; set < 64; set++)
{
ppcState.tlb[tlb][set].recent = 0;
for (int way = 0; way < 2; way++)
{
ppcState.tlb[tlb][set].paddr[way] = 0;
ppcState.tlb[tlb][set].pte[way] = 0;
ppcState.tlb[tlb][set].tag[way] = TLB_TAG_INVALID;
}
}
}
ResetRegisters();
PPCTables::InitTables(cpu_core);
// We initialize the interpreter because
// it is used on boot and code window independently.
interpreter->Init();
switch (cpu_core)
{
case 0:
{
cpu_core_base = interpreter;
break;
}
2013-02-26 13:49:00 -06:00
default:
cpu_core_base = JitInterface::InitJitCore(cpu_core);
if (!cpu_core_base) // Handle Situations where JIT core isn't available
{
WARN_LOG(POWERPC, "Jit core %d not available. Defaulting to interpreter.", cpu_core);
cpu_core_base = interpreter;
}
2013-02-26 13:49:00 -06:00
break;
}
if (cpu_core_base != interpreter)
{
mode = MODE_JIT;
}
else
{
mode = MODE_INTERPRETER;
}
state = CPU_STEPPING;
2012-01-02 02:20:22 -08:00
ppcState.iCache.Init();
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
breakpoints.ClearAllTemporary();
}
void Shutdown()
{
2013-02-26 13:49:00 -06:00
JitInterface::Shutdown();
interpreter->Shutdown();
cpu_core_base = nullptr;
state = CPU_POWERDOWN;
}
CoreMode GetMode()
{
return mode;
}
void SetMode(CoreMode new_mode)
{
if (new_mode == mode)
return; // We don't need to do anything.
mode = new_mode;
switch (mode)
{
case MODE_INTERPRETER: // Switching from JIT to interpreter
cpu_core_base = interpreter;
break;
case MODE_JIT: // Switching from interpreter to JIT.
// Don't really need to do much. It'll work, the cache will refill itself.
2013-02-26 13:49:00 -06:00
cpu_core_base = JitInterface::GetCore();
if (!cpu_core_base) // Has a chance to not get a working JIT core if one isn't active on host
cpu_core_base = interpreter;
break;
}
}
void SingleStep()
{
cpu_core_base->SingleStep();
}
void RunLoop()
{
state = CPU_RUNNING;
cpu_core_base->Run();
Host_UpdateDisasmDialog();
}
CPUState GetState()
{
return state;
}
volatile CPUState *GetStatePtr()
{
return &state;
}
void Start()
{
state = CPU_RUNNING;
Host_UpdateDisasmDialog();
}
void Pause()
{
state = CPU_STEPPING;
Host_UpdateDisasmDialog();
}
void Stop()
{
state = CPU_POWERDOWN;
Host_UpdateDisasmDialog();
}
void UpdatePerformanceMonitor(u32 cycles, u32 num_load_stores, u32 num_fp_inst)
{
switch (MMCR0.PMC1SELECT)
{
case 0: // No change
break;
case 1: // Processor cycles
PowerPC::ppcState.spr[SPR_PMC1] += cycles;
break;
default:
break;
}
switch (MMCR0.PMC2SELECT)
{
case 0: // No change
break;
case 1: // Processor cycles
PowerPC::ppcState.spr[SPR_PMC2] += cycles;
break;
case 11: // Number of loads and stores completed
PowerPC::ppcState.spr[SPR_PMC2] += num_load_stores;
break;
default:
break;
}
switch (MMCR1.PMC3SELECT)
{
case 0: // No change
break;
case 1: // Processor cycles
PowerPC::ppcState.spr[SPR_PMC3] += cycles;
break;
case 11: // Number of FPU instructions completed
PowerPC::ppcState.spr[SPR_PMC3] += num_fp_inst;
break;
default:
break;
}
switch (MMCR1.PMC4SELECT)
{
case 0: // No change
break;
case 1: // Processor cycles
PowerPC::ppcState.spr[SPR_PMC4] += cycles;
break;
default:
break;
}
if ((MMCR0.PMC1INTCONTROL && (PowerPC::ppcState.spr[SPR_PMC1] & 0x80000000) != 0) ||
(MMCR0.PMCINTCONTROL && (PowerPC::ppcState.spr[SPR_PMC2] & 0x80000000) != 0) ||
(MMCR0.PMCINTCONTROL && (PowerPC::ppcState.spr[SPR_PMC3] & 0x80000000) != 0) ||
(MMCR0.PMCINTCONTROL && (PowerPC::ppcState.spr[SPR_PMC4] & 0x80000000) != 0))
PowerPC::ppcState.Exceptions |= EXCEPTION_PERFORMANCE_MONITOR;
}
void CheckExceptions()
{
u32 exceptions = ppcState.Exceptions;
// 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 >> 16) & 1;
// clear MSR as specified
//MSR &= ~0x04EF36; // 0x04FF36 also clears ME (only for machine check exception)
// set to exception type entry point
2012-05-01 20:26:05 +10:00
//NPC = 0x00000x00;
// TODO(delroth): Exception priority is completely wrong here: depending on
// the instruction class, exceptions should be executed in a given order,
// which is very different from the one arbitrarily chosen here. See §6.1.5
// in 6xx_pem.pdf.
if (exceptions & EXCEPTION_ISI)
{
SRR0 = NPC;
// Page fault occurred
SRR1 = (MSR & 0x87C0FFFF) | (1 << 30);
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000400;
INFO_LOG(POWERPC, "EXCEPTION_ISI");
ppcState.Exceptions &= ~EXCEPTION_ISI;
}
else if (exceptions & EXCEPTION_PROGRAM)
{
SRR0 = PC;
// say that it's a trap exception
SRR1 = (MSR & 0x87C0FFFF) | 0x20000;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000700;
INFO_LOG(POWERPC, "EXCEPTION_PROGRAM");
ppcState.Exceptions &= ~EXCEPTION_PROGRAM;
}
else if (exceptions & EXCEPTION_SYSCALL)
{
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000C00;
INFO_LOG(POWERPC, "EXCEPTION_SYSCALL (PC=%08x)", PC);
ppcState.Exceptions &= ~EXCEPTION_SYSCALL;
}
else if (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 >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000800;
INFO_LOG(POWERPC, "EXCEPTION_FPU_UNAVAILABLE");
ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE;
}
else if (exceptions & EXCEPTION_DSI)
{
SRR0 = PC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000300;
//DSISR and DAR regs are changed in GenerateDSIException()
INFO_LOG(POWERPC, "EXCEPTION_DSI");
ppcState.Exceptions &= ~EXCEPTION_DSI;
}
else if (exceptions & EXCEPTION_ALIGNMENT)
{
//This never happens ATM
// perhaps we can get dcb* instructions to use this :p
SRR0 = PC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000600;
//TODO crazy amount of DSISR options to check out
INFO_LOG(POWERPC, "EXCEPTION_ALIGNMENT");
ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT;
}
// EXTERNAL INTERRUPT
else if (MSR & 0x0008000) // Handling is delayed until MSR.EE=1.
{
if (exceptions & EXCEPTION_EXTERNAL_INT)
{
// Pokemon gets this "too early", it hasn't a handler yet
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000500;
INFO_LOG(POWERPC, "EXCEPTION_EXTERNAL_INT");
ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT;
_dbg_assert_msg_(POWERPC, (SRR1 & 0x02) != 0, "EXTERNAL_INT unrecoverable???");
}
else if (exceptions & EXCEPTION_PERFORMANCE_MONITOR)
{
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
PC = NPC = 0x00000F00;
INFO_LOG(POWERPC, "EXCEPTION_PERFORMANCE_MONITOR");
ppcState.Exceptions &= ~EXCEPTION_PERFORMANCE_MONITOR;
}
else if (exceptions & EXCEPTION_DECREMENTER)
{
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000900;
INFO_LOG(POWERPC, "EXCEPTION_DECREMENTER");
ppcState.Exceptions &= ~EXCEPTION_DECREMENTER;
}
}
}
void CheckExternalExceptions()
{
u32 exceptions = ppcState.Exceptions;
// EXTERNAL INTERRUPT
if (exceptions && (MSR & 0x0008000)) // Handling is delayed until MSR.EE=1.
{
if (exceptions & EXCEPTION_EXTERNAL_INT)
{
// Pokemon gets this "too early", it hasn't a handler yet
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000500;
INFO_LOG(POWERPC, "EXCEPTION_EXTERNAL_INT");
ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT;
_dbg_assert_msg_(POWERPC, (SRR1 & 0x02) != 0, "EXTERNAL_INT unrecoverable???");
}
else if (exceptions & EXCEPTION_PERFORMANCE_MONITOR)
{
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
PC = NPC = 0x00000F00;
INFO_LOG(POWERPC, "EXCEPTION_PERFORMANCE_MONITOR");
ppcState.Exceptions &= ~EXCEPTION_PERFORMANCE_MONITOR;
}
else if (exceptions & EXCEPTION_DECREMENTER)
{
SRR0 = NPC;
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 16) & 1;
MSR &= ~0x04EF36;
2012-05-01 20:26:05 +10:00
PC = NPC = 0x00000900;
INFO_LOG(POWERPC, "EXCEPTION_DECREMENTER");
ppcState.Exceptions &= ~EXCEPTION_DECREMENTER;
}
else
{
_dbg_assert_msg_(POWERPC, 0, "Unknown EXT interrupt: Exceptions == %08x", exceptions);
ERROR_LOG(POWERPC, "Unknown EXTERNAL INTERRUPT exception: Exceptions == %08x", exceptions);
}
}
}
void CheckBreakPoints()
{
if (PowerPC::breakpoints.IsAddressBreakPoint(PC))
{
PowerPC::Pause();
if (PowerPC::breakpoints.IsTempBreakPoint(PC))
PowerPC::breakpoints.Remove(PC);
}
}
void OnIdle()
{
CoreTiming::Idle();
}
} // namespace
// FPSCR update functions
void UpdateFPRF(double dvalue)
{
FPSCR.FPRF = MathUtil::ClassifyDouble(dvalue);
//if (FPSCR.FPRF == 0x11)
// PanicAlert("QNAN alert");
}