Expand Up @@ -450,7 +450,7 @@ void Interpreter::dcbi(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand Down Expand Up @@ -514,7 +514,7 @@ void Interpreter::dcbz_l(UGeckoInstruction inst)
{
if (!HID2.LCE)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
return;
}

Expand Down Expand Up @@ -1041,7 +1041,7 @@ void Interpreter::tlbie(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand All @@ -1055,7 +1055,7 @@ void Interpreter::tlbsync(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
}

// Ignored
Expand Down
Expand Up @@ -311,7 +311,7 @@ void Interpreter::psq_l(UGeckoInstruction inst)
{
if (HID2.LSQE == 0)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
return;
}

Expand All @@ -323,7 +323,7 @@ void Interpreter::psq_lu(UGeckoInstruction inst)
{
if (HID2.LSQE == 0)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
return;
}

Expand All @@ -342,7 +342,7 @@ void Interpreter::psq_st(UGeckoInstruction inst)
{
if (HID2.LSQE == 0)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
return;
}

Expand All @@ -354,7 +354,7 @@ void Interpreter::psq_stu(UGeckoInstruction inst)
{
if (HID2.LSQE == 0)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
return;
}

Expand Down
Expand Up @@ -25,31 +25,18 @@ mffsx: 80036650 (huh?)
*/

static void FPSCRUpdated(UReg_FPSCR fp)
static void FPSCRUpdated(UReg_FPSCR* fpscr)
{
UpdateFPExceptionSummary(fpscr);
PowerPC::RoundingModeUpdated();

if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE)
{
// PanicAlert("FPSCR - exceptions enabled. Please report. VE=%i OE=%i UE=%i ZE=%i XE=%i",
// fp.VE, fp.OE, fp.UE, fp.ZE, fp.XE);
// Pokemon Colosseum does this. Gah.
}
}

static void UpdateFPSCR(UReg_FPSCR* fpscr)
{
fpscr->VX = (fpscr->Hex & FPSCR_VX_ANY) != 0;
fpscr->FEX = (fpscr->VX & fpscr->VE) | (fpscr->OX & fpscr->OE) | (fpscr->UX & fpscr->UE) |
(fpscr->ZX & fpscr->ZE) | (fpscr->XX & fpscr->XE);
}

void Interpreter::mtfsb0x(UGeckoInstruction inst)
{
u32 b = 0x80000000 >> inst.CRBD;

FPSCR.Hex &= ~b;
FPSCRUpdated(FPSCR);
FPSCRUpdated(&FPSCR);

if (inst.Rc)
PowerPC::ppcState.UpdateCR1();
Expand All @@ -66,7 +53,7 @@ void Interpreter::mtfsb1x(UGeckoInstruction inst)
else
FPSCR |= b;

FPSCRUpdated(FPSCR);
FPSCRUpdated(&FPSCR);

if (inst.Rc)
PowerPC::ppcState.UpdateCR1();
Expand All @@ -81,7 +68,7 @@ void Interpreter::mtfsfix(UGeckoInstruction inst)

FPSCR = (FPSCR.Hex & ~mask) | (imm >> (4 * field));

FPSCRUpdated(FPSCR);
FPSCRUpdated(&FPSCR);

if (inst.Rc)
PowerPC::ppcState.UpdateCR1();
Expand All @@ -98,7 +85,7 @@ void Interpreter::mtfsfx(UGeckoInstruction inst)
}

FPSCR = (FPSCR.Hex & ~m) | (static_cast<u32>(rPS(inst.FB).PS0AsU64()) & m);
FPSCRUpdated(FPSCR);
FPSCRUpdated(&FPSCR);

if (inst.Rc)
PowerPC::ppcState.UpdateCR1();
Expand Down Expand Up @@ -141,7 +128,7 @@ void Interpreter::mfmsr(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand All @@ -152,7 +139,7 @@ void Interpreter::mfsr(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand All @@ -163,7 +150,7 @@ void Interpreter::mfsrin(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand All @@ -175,11 +162,15 @@ void Interpreter::mtmsr(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

MSR.Hex = rGPR[inst.RS];

// FE0/FE1 may have been set
CheckFPExceptions(FPSCR);

PowerPC::CheckExceptions();
m_end_block = true;
}
Expand All @@ -190,7 +181,7 @@ void Interpreter::mtsr(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand All @@ -203,7 +194,7 @@ void Interpreter::mtsrin(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand All @@ -227,7 +218,7 @@ void Interpreter::mfspr(UGeckoInstruction inst)
if (MSR.PR && index != SPR_XER && index != SPR_LR && index != SPR_CTR && index != SPR_TL &&
index != SPR_TU)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand Down Expand Up @@ -270,7 +261,7 @@ void Interpreter::mtspr(UGeckoInstruction inst)
// XER, LR, and CTR are the only ones available to be written to in user mode
if (MSR.PR && index != SPR_XER && index != SPR_LR && index != SPR_CTR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

Expand Down Expand Up @@ -564,22 +555,18 @@ void Interpreter::isync(UGeckoInstruction inst)

void Interpreter::mcrfs(UGeckoInstruction inst)
{
UpdateFPSCR(&FPSCR);
const u32 shift = 4 * (7 - inst.CRFS);
const u32 fpflags = (FPSCR.Hex >> shift) & 0xF;

// If any exception bits were read, clear them
FPSCR.Hex &= ~((0xF << shift) & (FPSCR_FX | FPSCR_ANY_X));
FPSCRUpdated(&FPSCR);

PowerPC::ppcState.cr.SetField(inst.CRFD, fpflags);
}

void Interpreter::mffsx(UGeckoInstruction inst)
{
// load from FPSCR
// TODO(ector): grab all overflow flags etc and set them in FPSCR

UpdateFPSCR(&FPSCR);
rPS(inst.FD).SetPS0(UINT64_C(0xFFF8000000000000) | FPSCR.Hex);

if (inst.Rc)
Expand Down
94 changes: 47 additions & 47 deletions Source/Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp

Large diffs are not rendered by default.

22 changes: 20 additions & 2 deletions Source/Core/Core/PowerPC/Jit64/Jit.cpp
Expand Up @@ -335,7 +335,7 @@ void Jit64::Init()
jo.fastmem_arena = SConfig::GetInstance().bFastmem && Memory::InitFastmemArena();
jo.optimizeGatherPipe = true;
jo.accurateSinglePrecision = true;
UpdateMemoryOptions();
UpdateMemoryAndExceptionOptions();
js.fastmemLoadStore = nullptr;
js.compilerPC = 0;

Expand Down Expand Up @@ -389,7 +389,7 @@ void Jit64::ClearCache()
m_const_pool.Clear();
ClearCodeSpace();
Clear();
UpdateMemoryOptions();
UpdateMemoryAndExceptionOptions();
ResetFreeMemoryRanges();
}

Expand Down Expand Up @@ -453,6 +453,24 @@ void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
SetJumpTarget(c);
}
}
else if (ShouldHandleFPExceptionForInstruction(js.op))
{
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_PROGRAM));
FixupBranch exception = J_CC(CC_NZ, true);

SwitchToFarCode();
SetJumpTarget(exception);

RCForkGuard gpr_guard = gpr.Fork();
RCForkGuard fpr_guard = fpr.Fork();

gpr.Flush();
fpr.Flush();

MOV(32, PPCSTATE(pc), Imm32(js.op->address));
WriteExceptionExit();
SwitchToNearCode();
}
}

void Jit64::HLEFunction(u32 hook_index)
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/Core/PowerPC/Jit64/Jit.h
Expand Up @@ -116,11 +116,12 @@ class Jit64 : public JitBase, public QuantizedMemoryRoutines
void ClearCRFieldBit(int field, int bit);
void SetCRFieldBit(int field, int bit);
void FixGTBeforeSettingCRFieldBit(Gen::X64Reg reg);

// Generates a branch that will check if a given bit of a CR register part
// is set or not.
Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set = true);

void UpdateFPExceptionSummary(Gen::X64Reg fpscr, Gen::X64Reg tmp1, Gen::X64Reg tmp2);

void SetFPRFIfNeeded(const Gen::OpArg& xmm, bool single);
void FinalizeSingleResult(Gen::X64Reg output, const Gen::OpArg& input, bool packed = true,
bool duplicate = false);
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/Core/PowerPC/Jit64/Jit_FloatingPoint.cpp
Expand Up @@ -208,6 +208,7 @@ void Jit64::fp_arith(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || (jo.div_by_zero_exceptions && inst.SUBOP5 == 18));

int a = inst.FA;
int b = inst.FB;
Expand Down Expand Up @@ -292,6 +293,7 @@ void Jit64::fmaddXX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

// We would like to emulate FMA instructions accurately without rounding error if possible, but
// unfortunately emulating FMA in software is just too slow on CPUs that are too old to have FMA
Expand Down Expand Up @@ -733,6 +735,7 @@ void Jit64::fcmpX(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(jo.fp_exceptions);

FloatCompare(inst);
}
Expand All @@ -742,6 +745,7 @@ void Jit64::fctiwx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

int d = inst.RD;
int b = inst.RB;
Expand Down Expand Up @@ -784,6 +788,7 @@ void Jit64::frspx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
int b = inst.FB;
int d = inst.FD;
bool packed = js.op->fprIsDuplicated[b] && !cpu_info.bAtom;
Expand All @@ -800,6 +805,7 @@ void Jit64::frsqrtex(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB;
int d = inst.FD;

Expand All @@ -818,6 +824,7 @@ void Jit64::fresx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB;
int d = inst.FD;

Expand Down
3 changes: 3 additions & 0 deletions Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp
Expand Up @@ -11,7 +11,9 @@
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/x64Emitter.h"

#include "Core/CoreTiming.h"
#include "Core/PowerPC/Interpreter/ExceptionUtils.h"
#include "Core/PowerPC/Jit64/Jit.h"
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
Expand Down Expand Up @@ -2562,6 +2564,7 @@ void Jit64::twX(UGeckoInstruction inst)
}
LOCK();
OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_PROGRAM));
MOV(32, PPCSTATE_SRR1, Imm32(static_cast<u32>(ProgramExceptionCause::Trap)));

gpr.Flush();
fpr.Flush();
Expand Down
5 changes: 5 additions & 0 deletions Source/Core/Core/PowerPC/Jit64/Jit_Paired.cpp
Expand Up @@ -33,6 +33,7 @@ void Jit64::ps_sum(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

int d = inst.FD;
int a = inst.FA;
Expand Down Expand Up @@ -84,6 +85,7 @@ void Jit64::ps_muls(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

int d = inst.FD;
int a = inst.FA;
Expand Down Expand Up @@ -152,6 +154,7 @@ void Jit64::ps_rsqrte(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB;
int d = inst.FD;

Expand All @@ -176,6 +179,7 @@ void Jit64::ps_res(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB;
int d = inst.FD;

Expand All @@ -199,6 +203,7 @@ void Jit64::ps_cmpXX(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(jo.fp_exceptions);

FloatCompare(inst, !!(inst.SUBOP10 & 64));
}
130 changes: 107 additions & 23 deletions Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp
Expand Up @@ -4,7 +4,9 @@
#include "Common/BitSet.h"
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/x64Emitter.h"

#include "Core/CoreTiming.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/PowerPC/Jit64/Jit.h"
Expand Down Expand Up @@ -185,6 +187,33 @@ FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
return FixupBranch();
}

// Could be done with one temp register, but with two temp registers it's faster
void Jit64::UpdateFPExceptionSummary(X64Reg fpscr, X64Reg tmp1, X64Reg tmp2)
{
// Kill dependency on tmp1 (not required for correctness, since SHL will shift out upper bytes)
XOR(32, R(tmp1), R(tmp1));

// fpscr.VX = (fpscr & FPSCR_VX_ANY) != 0
TEST(32, R(fpscr), Imm32(FPSCR_VX_ANY));
SETcc(CC_NZ, R(tmp1));
SHL(32, R(tmp1), Imm8(IntLog2(FPSCR_VX)));
AND(32, R(fpscr), Imm32(~(FPSCR_VX | FPSCR_FEX)));
OR(32, R(fpscr), R(tmp1));

// fpscr.FEX = ((fpscr >> 22) & (fpscr & FPSCR_ANY_E)) != 0
MOV(32, R(tmp1), R(fpscr));
MOV(32, R(tmp2), R(fpscr));
SHR(32, R(tmp1), Imm8(22));
AND(32, R(tmp2), Imm32(FPSCR_ANY_E));
TEST(32, R(tmp1), R(tmp2));
// Unfortunately we eat a partial register stall below - we can't zero any of the registers before
// the TEST, and we can't use XOR right after the TEST since that would overwrite flags. However,
// there is no false dependency, since SETcc depends on TEST's flags and TEST depends on tmp1.
SETcc(CC_NZ, R(tmp1));
SHL(32, R(tmp1), Imm8(IntLog2(FPSCR_FEX)));
OR(32, R(fpscr), R(tmp1));
}

static void DoICacheReset()
{
PowerPC::ppcState.iCache.Reset();
Expand Down Expand Up @@ -395,6 +424,7 @@ void Jit64::mtmsr(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(jo.fp_exceptions);

{
RCOpArg Rs = gpr.BindOrImm(inst.RS, RCMode::Read);
Expand Down Expand Up @@ -637,6 +667,19 @@ void Jit64::mcrfs(UGeckoInstruction inst)
// Only clear exception bits (but not FEX/VX).
mask &= FPSCR_FX | FPSCR_ANY_X;

RCX64Reg scratch_guard;
X64Reg scratch;
if (mask != 0)
{
scratch_guard = gpr.Scratch();
RegCache::Realize(scratch_guard);
scratch = scratch_guard;
}
else
{
scratch = RSCRATCH;
}

if (cpu_info.bBMI1)
{
MOV(32, R(RSCRATCH), PPCSTATE(fpscr));
Expand All @@ -652,14 +695,17 @@ void Jit64::mcrfs(UGeckoInstruction inst)
SHR(32, R(RSCRATCH2), Imm8(shift));
AND(32, R(RSCRATCH2), Imm32(0xF));
}

LEA(64, scratch, MConst(PowerPC::ConditionRegister::s_crTable));
MOV(64, R(scratch), MComplex(scratch, RSCRATCH2, SCALE_8, 0));
MOV(64, CROffset(inst.CRFD), R(scratch));

if (mask != 0)
{
AND(32, R(RSCRATCH), Imm32(~mask));
UpdateFPExceptionSummary(RSCRATCH, RSCRATCH2, scratch);
MOV(32, PPCSTATE(fpscr), R(RSCRATCH));
}
LEA(64, RSCRATCH, MConst(PowerPC::ConditionRegister::s_crTable));
MOV(64, R(RSCRATCH), MComplex(RSCRATCH, RSCRATCH2, SCALE_8, 0));
MOV(64, CROffset(inst.CRFD), R(RSCRATCH));
}

void Jit64::mffsx(UGeckoInstruction inst)
Expand All @@ -670,18 +716,6 @@ void Jit64::mffsx(UGeckoInstruction inst)

MOV(32, R(RSCRATCH), PPCSTATE(fpscr));

// FPSCR.FEX = 0 (and VX for below)
AND(32, R(RSCRATCH), Imm32(~0x60000000));

// FPSCR.VX = (FPSCR.Hex & FPSCR_VX_ANY) != 0;
XOR(32, R(RSCRATCH2), R(RSCRATCH2));
TEST(32, R(RSCRATCH), Imm32(FPSCR_VX_ANY));
SETcc(CC_NZ, R(RSCRATCH2));
SHL(32, R(RSCRATCH2), Imm8(31 - 2));
OR(32, R(RSCRATCH), R(RSCRATCH2));

MOV(32, PPCSTATE(fpscr), R(RSCRATCH));

int d = inst.FD;
RCX64Reg Rd = fpr.Bind(d, RCMode::Write);
RegCache::Realize(Rd);
Expand Down Expand Up @@ -710,17 +744,32 @@ void Jit64::mtfsb0x(UGeckoInstruction inst)
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);

u32 mask = ~(0x80000000 >> inst.CRBD);
if (inst.CRBD < 29)
const u32 mask = 0x80000000 >> inst.CRBD;
const u32 inverted_mask = ~mask;

if (mask == FPSCR_FEX || mask == FPSCR_VX)
return;

if (inst.CRBD < 29 && (mask & (FPSCR_ANY_X | FPSCR_ANY_E)) == 0)
{
AND(32, PPCSTATE(fpscr), Imm32(mask));
AND(32, PPCSTATE(fpscr), Imm32(inverted_mask));
}
else
{
MOV(32, R(RSCRATCH), PPCSTATE(fpscr));
AND(32, R(RSCRATCH), Imm32(mask));
AND(32, R(RSCRATCH), Imm32(inverted_mask));

if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
{
RCX64Reg scratch = gpr.Scratch();
RegCache::Realize(scratch);

UpdateFPExceptionSummary(RSCRATCH, RSCRATCH2, scratch);
}

MOV(32, PPCSTATE(fpscr), R(RSCRATCH));
UpdateMXCSR();
if (inst.CRBD >= 29)
UpdateMXCSR();
}
}

Expand All @@ -729,10 +778,15 @@ void Jit64::mtfsb1x(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 mask = 0x80000000 >> inst.CRBD;

if (mask == FPSCR_FEX || mask == FPSCR_VX)
return;

u32 mask = 0x80000000 >> inst.CRBD;
MOV(32, R(RSCRATCH), PPCSTATE(fpscr));
if (mask & FPSCR_ANY_X)
if ((mask & FPSCR_ANY_X) != 0)
{
BTS(32, R(RSCRATCH), Imm32(31 - inst.CRBD));
FixupBranch dont_set_fx = J_CC(CC_C);
Expand All @@ -743,6 +797,15 @@ void Jit64::mtfsb1x(UGeckoInstruction inst)
{
OR(32, R(RSCRATCH), Imm32(mask));
}

if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
{
RCX64Reg scratch = gpr.Scratch();
RegCache::Realize(scratch);

UpdateFPExceptionSummary(RSCRATCH, RSCRATCH2, scratch);
}

MOV(32, PPCSTATE(fpscr), R(RSCRATCH));
if (inst.CRBD >= 29)
UpdateMXCSR();
Expand All @@ -753,14 +816,25 @@ void Jit64::mtfsfix(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

u8 imm = (inst.hex >> (31 - 19)) & 0xF;
u32 mask = 0xF0000000 >> (4 * inst.CRFD);
u32 or_mask = imm << (28 - 4 * inst.CRFD);
u32 and_mask = ~(0xF0000000 >> (4 * inst.CRFD));
u32 and_mask = ~mask;

MOV(32, R(RSCRATCH), PPCSTATE(fpscr));
AND(32, R(RSCRATCH), Imm32(and_mask));
OR(32, R(RSCRATCH), Imm32(or_mask));

if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
{
RCX64Reg scratch = gpr.Scratch();
RegCache::Realize(scratch);

UpdateFPExceptionSummary(RSCRATCH, RSCRATCH2, scratch);
}

MOV(32, PPCSTATE(fpscr), R(RSCRATCH));

// Field 7 contains NI and RN.
Expand All @@ -773,6 +847,7 @@ void Jit64::mtfsfx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

u32 mask = 0;
for (int i = 0; i < 8; i++)
Expand All @@ -798,6 +873,15 @@ void Jit64::mtfsfx(UGeckoInstruction inst)
AND(32, R(RSCRATCH2), Imm32(~mask));
OR(32, R(RSCRATCH), R(RSCRATCH2));
}

if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
{
RCX64Reg scratch = gpr.Scratch();
RegCache::Realize(scratch);

UpdateFPExceptionSummary(RSCRATCH, RSCRATCH2, scratch);
}

MOV(32, PPCSTATE(fpscr), R(RSCRATCH));

if (inst.FM & 1)
Expand Down
45 changes: 27 additions & 18 deletions Source/Core/Core/PowerPC/JitArm64/Jit.cpp
Expand Up @@ -50,7 +50,7 @@ void JitArm64::Init()
jo.fastmem_arena = SConfig::GetInstance().bFastmem && Memory::InitFastmemArena();
jo.enableBlocklink = true;
jo.optimizeGatherPipe = true;
UpdateMemoryOptions();
UpdateMemoryAndExceptionOptions();
gpr.Init(this);
fpr.Init(this);
blocks.Init();
Expand Down Expand Up @@ -129,7 +129,7 @@ void JitArm64::ClearCache()
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
ClearCodeSpace();
farcode.ClearCodeSpace();
UpdateMemoryOptions();
UpdateMemoryAndExceptionOptions();

GenerateAsm();
}
Expand Down Expand Up @@ -193,25 +193,14 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
gpr.Unlock(WA);
}
}
else if (ShouldHandleFPExceptionForInstruction(js.op))
{
WriteConditionalExceptionExit(EXCEPTION_PROGRAM);
}

if (jo.memcheck && (js.op->opinfo->flags & FL_LOADSTORE))
{
ARM64Reg WA = gpr.GetReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
FixupBranch noException = TBZ(WA, IntLog2(EXCEPTION_DSI));

FixupBranch handleException = B();
SwitchToFarCode();
SetJumpTarget(handleException);

gpr.Flush(FlushMode::MaintainState, WA);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);

WriteExceptionExit(js.compilerPC, false, true);

SwitchToNearCode();
SetJumpTarget(noException);
gpr.Unlock(WA);
WriteConditionalExceptionExit(EXCEPTION_DSI);
}
}

Expand Down Expand Up @@ -495,6 +484,26 @@ void JitArm64::WriteExceptionExit(ARM64Reg dest, bool only_external, bool always
B(dispatcher);
}

void JitArm64::WriteConditionalExceptionExit(int exception)
{
ARM64Reg WA = gpr.GetReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
FixupBranch noException = TBZ(WA, IntLog2(exception));

FixupBranch handleException = B();
SwitchToFarCode();
SetJumpTarget(handleException);

gpr.Flush(FlushMode::MaintainState, WA);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);

WriteExceptionExit(js.compilerPC, false, true);

SwitchToNearCode();
SetJumpTarget(noException);
gpr.Unlock(WA);
}

bool JitArm64::HandleFunctionHooking(u32 address)
{
return HLE::ReplaceFunctionIfPossible(address, [&](u32 hook_index, HLE::HookType type) {
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/PowerPC/JitArm64/Jit.h
Expand Up @@ -268,11 +268,13 @@ class JitArm64 : public JitBase, public Arm64Gen::ARM64CodeBlock, public CommonA
bool always_exception = false);
void WriteExceptionExit(Arm64Gen::ARM64Reg dest, bool only_external = false,
bool always_exception = false);
void WriteConditionalExceptionExit(int exception);
void FakeLKExit(u32 exit_address_after_return);
void WriteBLRExit(Arm64Gen::ARM64Reg dest);

Arm64Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set);
void FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg);
void UpdateFPExceptionSummary(Arm64Gen::ARM64Reg fpscr);
void UpdateRoundingMode();

void ComputeRC0(Arm64Gen::ARM64Reg reg);
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Core/PowerPC/JitArm64/JitArm64_FloatingPoint.cpp
Expand Up @@ -67,6 +67,7 @@ void JitArm64::fp_arith(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || (jo.div_by_zero_exceptions && inst.SUBOP5 == 18));

u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
u32 op5 = inst.SUBOP5;
Expand Down Expand Up @@ -339,6 +340,7 @@ void JitArm64::frspx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 b = inst.FB;
const u32 d = inst.FD;
Expand Down Expand Up @@ -500,6 +502,7 @@ void JitArm64::fcmpX(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(jo.fp_exceptions);

FloatCompare(inst);
}
Expand All @@ -509,6 +512,7 @@ void JitArm64::fctiwzx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 b = inst.FB;
const u32 d = inst.FD;
Expand Down Expand Up @@ -551,6 +555,7 @@ void JitArm64::fresx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);

const u32 b = inst.FB;
const u32 d = inst.FD;
Expand Down Expand Up @@ -579,6 +584,7 @@ void JitArm64::frsqrtex(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);

const u32 b = inst.FB;
const u32 d = inst.FD;
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Core/PowerPC/JitArm64/JitArm64_Paired.cpp
Expand Up @@ -75,6 +75,7 @@ void JitArm64::ps_mulsX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 a = inst.FA;
const u32 c = inst.FC;
Expand Down Expand Up @@ -125,6 +126,7 @@ void JitArm64::ps_maddXX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 a = inst.FA;
const u32 b = inst.FB;
Expand Down Expand Up @@ -316,6 +318,7 @@ void JitArm64::ps_sumX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 a = inst.FA;
const u32 b = inst.FB;
Expand Down Expand Up @@ -362,6 +365,7 @@ void JitArm64::ps_res(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);

const u32 b = inst.FB;
const u32 d = inst.FD;
Expand Down Expand Up @@ -394,6 +398,7 @@ void JitArm64::ps_rsqrte(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);

const u32 b = inst.FB;
const u32 d = inst.FD;
Expand Down Expand Up @@ -425,6 +430,7 @@ void JitArm64::ps_cmpXX(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(bJITPairedOff);
FALLBACK_IF(jo.fp_exceptions);

const bool upper = inst.SUBOP10 & 64;
FloatCompare(inst, upper);
Expand Down
116 changes: 88 additions & 28 deletions Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp
Expand Up @@ -4,9 +4,11 @@
#include "Common/Arm64Emitter.h"
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"

#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/PowerPC/Interpreter/ExceptionUtils.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/PPCTables.h"
#include "Core/PowerPC/PowerPC.h"
Expand Down Expand Up @@ -48,6 +50,25 @@ void JitArm64::FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg)
gpr.Unlock(WA);
}

void JitArm64::UpdateFPExceptionSummary(ARM64Reg fpscr)
{
ARM64Reg WA = gpr.GetReg();

// fpscr.VX = (fpscr & FPSCR_VX_ANY) != 0
MOVI2R(WA, FPSCR_VX_ANY);
TST(WA, fpscr);
CSET(WA, CCFlags::CC_NEQ);
BFI(fpscr, WA, IntLog2(FPSCR_VX), 1);

// fpscr.FEX = ((fpscr >> 22) & (fpscr & FPSCR_ANY_E)) != 0
AND(WA, fpscr, LogicalImm(FPSCR_ANY_E, 32));
TST(WA, fpscr, ArithOption(fpscr, ShiftType::LSR, 22));
CSET(WA, CCFlags::CC_NEQ);
BFI(fpscr, WA, IntLog2(FPSCR_FEX), 1);

gpr.Unlock(WA);
}

void JitArm64::UpdateRoundingMode()
{
const BitSet32 gprs_to_save = gpr.GetCallerSavedUsed();
Expand All @@ -65,6 +86,7 @@ void JitArm64::mtmsr(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(jo.fp_exceptions);

gpr.BindToRegister(inst.RS, true);
STR(IndexType::Unsigned, gpr.R(inst.RS), PPC_REG, PPCSTATE_OFF(msr));
Expand Down Expand Up @@ -233,6 +255,9 @@ void JitArm64::twx(UGeckoInstruction inst)
ORR(WA, WA, LogicalImm(EXCEPTION_PROGRAM, 32));
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));

MOVI2R(WA, static_cast<u32>(ProgramExceptionCause::Trap));
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_SRR1));

WriteExceptionExit(js.compilerPC, false, true);

SwitchToNearCode();
Expand Down Expand Up @@ -728,6 +753,8 @@ void JitArm64::mcrfs(UGeckoInstruction inst)
{
const u32 inverted_mask = ~mask;
AND(WA, WA, LogicalImm(inverted_mask, 32));

UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
}

Expand All @@ -749,24 +776,11 @@ void JitArm64::mffsx(UGeckoInstruction inst)
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

ARM64Reg VD = fpr.RW(inst.FD, RegType::LowerPair);
ARM64Reg WB = gpr.GetReg();

// FPSCR.FEX = 0;
// FPSCR.VX = (FPSCR.Hex & FPSCR_VX_ANY) != 0;
// (FEX is right next to VX, so we can set both using one BFI instruction)
MOVI2R(WB, FPSCR_VX_ANY);
TST(WA, WB);
CSET(WB, CCFlags::CC_NEQ);
BFI(WA, WB, 31 - 2, 2);

STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

// Vd = FPSCR.Hex | 0xFFF8'0000'0000'0000;
ORR(XA, XA, LogicalImm(0xFFF8'0000'0000'0000, 64));
m_float_emit.FMOV(EncodeRegToDouble(VD), XA);

gpr.Unlock(WA);
gpr.Unlock(WB);
}

void JitArm64::mtfsb0x(UGeckoInstruction inst)
Expand All @@ -775,12 +789,20 @@ void JitArm64::mtfsb0x(UGeckoInstruction inst)
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);

u32 mask = ~(0x80000000 >> inst.CRBD);
const u32 mask = 0x80000000 >> inst.CRBD;
const u32 inverted_mask = ~mask;

if (mask == FPSCR_FEX || mask == FPSCR_VX)
return;

ARM64Reg WA = gpr.GetReg();

LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
AND(WA, WA, LogicalImm(mask, 32));

AND(WA, WA, LogicalImm(inverted_mask, 32));

if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

gpr.Unlock(WA);
Expand All @@ -794,13 +816,18 @@ void JitArm64::mtfsb1x(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

const u32 mask = 0x80000000 >> inst.CRBD;

u32 mask = 0x80000000 >> inst.CRBD;
if (mask == FPSCR_FEX || mask == FPSCR_VX)
return;

ARM64Reg WA = gpr.GetReg();

LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
if (mask & FPSCR_ANY_X)

if ((mask & FPSCR_ANY_X) != 0)
{
ARM64Reg WB = gpr.GetReg();
TST(WA, LogicalImm(mask, 32));
Expand All @@ -809,6 +836,9 @@ void JitArm64::mtfsb1x(UGeckoInstruction inst)
gpr.Unlock(WB);
}
ORR(WA, WA, LogicalImm(mask, 32));

if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

gpr.Unlock(WA);
Expand All @@ -822,16 +852,19 @@ void JitArm64::mtfsfix(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

u8 imm = (inst.hex >> (31 - 19)) & 0xF;
u8 shift = 28 - 4 * inst.CRFD;
u32 mask = 0xF << shift;

ARM64Reg WA = gpr.GetReg();

LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

if (imm == 0xF)
{
ORR(WA, WA, LogicalImm(0xF << shift, 32));
ORR(WA, WA, LogicalImm(mask, 32));
}
else if (imm == 0x0)
{
Expand All @@ -845,7 +878,10 @@ void JitArm64::mtfsfix(UGeckoInstruction inst)
gpr.Unlock(WB);
}

if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

gpr.Unlock(WA);

// Field 7 contains NI and RN.
Expand All @@ -858,6 +894,7 @@ void JitArm64::mtfsfx(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);

u32 mask = 0;
for (int i = 0; i < 8; i++)
Expand All @@ -869,24 +906,47 @@ void JitArm64::mtfsfx(UGeckoInstruction inst)
if (mask == 0xFFFFFFFF)
{
ARM64Reg VB = fpr.R(inst.FB, RegType::LowerPair);
ARM64Reg WA = gpr.GetReg();

m_float_emit.FMOV(WA, EncodeRegToSingle(VB));

m_float_emit.STR(32, IndexType::Unsigned, VB, PPC_REG, PPCSTATE_OFF(fpscr));
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

gpr.Unlock(WA);
}
else if (mask != 0)
{
ARM64Reg VB = fpr.R(inst.FB, RegType::LowerPair);

ARM64Reg V0 = fpr.GetReg();
ARM64Reg V1 = fpr.GetReg();
ARM64Reg WA = gpr.GetReg();
ARM64Reg WB = gpr.GetReg();

LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
m_float_emit.FMOV(WB, EncodeRegToSingle(VB));

m_float_emit.LDR(32, IndexType::Unsigned, V0, PPC_REG, PPCSTATE_OFF(fpscr));
MOVI2R(WA, mask);
m_float_emit.FMOV(EncodeRegToSingle(V1), WA);
m_float_emit.BIT(EncodeRegToDouble(V0), EncodeRegToDouble(VB), EncodeRegToDouble(V1));
m_float_emit.STR(32, IndexType::Unsigned, V0, PPC_REG, PPCSTATE_OFF(fpscr));
if (LogicalImm imm = LogicalImm(mask, 32))
{
AND(WA, WA, LogicalImm(~mask, 32));
AND(WB, WB, imm);
}
else
{
ARM64Reg WC = gpr.GetReg();

MOVI2R(WC, mask);
BIC(WA, WA, WC);
AND(WB, WB, WC);

gpr.Unlock(WC);
}
ORR(WA, WA, WB);

gpr.Unlock(WB);

if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));

fpr.Unlock(V0, V1);
gpr.Unlock(WA);
}

Expand Down
14 changes: 13 additions & 1 deletion Source/Core/Core/PowerPC/JitCommon/JitBase.cpp
Expand Up @@ -41,9 +41,21 @@ bool JitBase::CanMergeNextInstructions(int count) const
return true;
}

void JitBase::UpdateMemoryOptions()
void JitBase::UpdateMemoryAndExceptionOptions()
{
bool any_watchpoints = PowerPC::memchecks.HasAny();
jo.fastmem = SConfig::GetInstance().bFastmem && jo.fastmem_arena && (MSR.DR || !any_watchpoints);
jo.memcheck = SConfig::GetInstance().bMMU || any_watchpoints;
jo.fp_exceptions = SConfig::GetInstance().bFloatExceptions;
jo.div_by_zero_exceptions = SConfig::GetInstance().bDivideByZeroExceptions;
}

bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op)
{
if (jo.fp_exceptions)
return (op->opinfo->flags & FL_FLOAT_EXCEPTION) != 0;
else if (jo.div_by_zero_exceptions)
return (op->opinfo->flags & FL_FLOAT_DIV) != 0;
else
return false;
}
6 changes: 5 additions & 1 deletion Source/Core/Core/PowerPC/JitCommon/JitBase.h
Expand Up @@ -63,6 +63,8 @@ class JitBase : public CPUCoreBase
bool fastmem;
bool fastmem_arena;
bool memcheck;
bool fp_exceptions;
bool div_by_zero_exceptions;
bool profile_blocks;
};
struct JitState
Expand Down Expand Up @@ -113,7 +115,9 @@ class JitBase : public CPUCoreBase

bool CanMergeNextInstructions(int count) const;

void UpdateMemoryOptions();
void UpdateMemoryAndExceptionOptions();

bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op);

public:
JitBase();
Expand Down
25 changes: 15 additions & 10 deletions Source/Core/Core/PowerPC/PPCAnalyst.cpp
Expand Up @@ -524,8 +524,12 @@ void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code, const Gekk
code->wantsCR0 = false;
code->wantsCR1 = false;

bool first_fpu_instruction = false;
if (opinfo->flags & FL_USE_FPU)
{
first_fpu_instruction = !block->m_fpa->any;
block->m_fpa->any = true;
}

if (opinfo->flags & FL_TIMER)
block->m_gpa->anyTimer = true;
Expand All @@ -550,9 +554,10 @@ void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code, const Gekk
code->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0;
code->canEndBlock = (opinfo->flags & FL_ENDBLOCK) != 0;

// TODO: Is it possible to determine that some FPU instructions never cause exceptions?
code->canCauseException =
(opinfo->flags & (FL_LOADSTORE | FL_USE_FPU | FL_PROGRAMEXCEPTION)) != 0;
first_fpu_instruction || (opinfo->flags & (FL_LOADSTORE | FL_PROGRAMEXCEPTION)) != 0 ||
(SConfig::GetInstance().bFloatExceptions && (opinfo->flags & FL_FLOAT_EXCEPTION)) ||
(SConfig::GetInstance().bDivideByZeroExceptions && (opinfo->flags & FL_FLOAT_DIV));

code->wantsCA = (opinfo->flags & FL_READ_CA) != 0;
code->outputCA = (opinfo->flags & FL_SET_CA) != 0;
Expand Down Expand Up @@ -928,14 +933,14 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
const bool opWantsCR1 = op.wantsCR1;
const bool opWantsFPRF = op.wantsFPRF;
const bool opWantsCA = op.wantsCA;
op.wantsCR0 = wantsCR0 || op.canEndBlock;
op.wantsCR1 = wantsCR1 || op.canEndBlock;
op.wantsFPRF = wantsFPRF || op.canEndBlock;
op.wantsCA = wantsCA || op.canEndBlock;
wantsCR0 |= opWantsCR0 || op.canEndBlock;
wantsCR1 |= opWantsCR1 || op.canEndBlock;
wantsFPRF |= opWantsFPRF || op.canEndBlock;
wantsCA |= opWantsCA || op.canEndBlock;
op.wantsCR0 = wantsCR0 || op.canEndBlock || op.canCauseException;
op.wantsCR1 = wantsCR1 || op.canEndBlock || op.canCauseException;
op.wantsFPRF = wantsFPRF || op.canEndBlock || op.canCauseException;
op.wantsCA = wantsCA || op.canEndBlock || op.canCauseException;
wantsCR0 |= opWantsCR0 || op.canEndBlock || op.canCauseException;
wantsCR1 |= opWantsCR1 || op.canEndBlock || op.canCauseException;
wantsFPRF |= opWantsFPRF || op.canEndBlock || op.canCauseException;
wantsCA |= opWantsCA || op.canEndBlock || op.canCauseException;
wantsCR0 &= !op.outputCR0 || opWantsCR0;
wantsCR1 &= !op.outputCR1 || opWantsCR1;
wantsFPRF &= !op.outputFPRF || opWantsFPRF;
Expand Down
4 changes: 3 additions & 1 deletion Source/Core/Core/PowerPC/PPCTables.h
Expand Up @@ -64,7 +64,9 @@ enum InstructionFlags : u64
FL_IN_FLOAT_C_BITEXACT = (1ull << 31), // The output is based on the exact bits in frC.
FL_IN_FLOAT_AB_BITEXACT = FL_IN_FLOAT_A_BITEXACT | FL_IN_FLOAT_B_BITEXACT,
FL_IN_FLOAT_BC_BITEXACT = FL_IN_FLOAT_B_BITEXACT | FL_IN_FLOAT_C_BITEXACT,
FL_PROGRAMEXCEPTION = (1ull << 32), // May generate a system exception.
FL_PROGRAMEXCEPTION = (1ull << 32), // May generate a program exception (not floating point).
FL_FLOAT_EXCEPTION = (1ull << 33), // May generate a program exception (floating point).
FL_FLOAT_DIV = (1ull << 34), // May generate a program exception (FP) due to division by 0.
};

enum class OpType
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/Core/PowerPC/PowerPC.cpp
Expand Up @@ -483,8 +483,8 @@ void CheckExceptions()
else if (exceptions & EXCEPTION_PROGRAM)
{
SRR0 = PC;
// say that it's a trap exception
SRR1 = (MSR.Hex & 0x87C0FFFF) | 0x20000;
// SRR1 was partially set by GenerateProgramException, so bitwise or is used here
SRR1 |= MSR.Hex & 0x87C0FFFF;
MSR.LE = MSR.ILE;
MSR.Hex &= ~0x04EF36;
PC = NPC = 0x00000700;
Expand Down