Interpreter_FloatingPoint: Factor out common code from fctiw and fctiwz

fctiwz functions in the same manner as fctiw, with the difference being
that fctiwz always assumes the rounding mode being towards zero. Because
of this, we can implement fctiwz in terms of fctiw's code, but modify it
to accept a rounding mode, allowing us to preserve proper behavior for
both instructions.

We also move Helper_UpdateCR1 to a temporary home in
Interpreter_FPUtils.h for the time being. It would be more desirable to
move it to a new common header for all the helpers, so that even JITs
can use them if they so wish, however, this and the following changes
are intended to only touch the interpreter to keep changes minimal for
fixing instruction behavior.

JitCommon already duplicates the Helper_Mask function within
JitBase.cpp/.h, and the ARM JIT includes the Interpreter header in order
to call Helper_Carry. So a follow up is best suited here, as this
touches two other CPU backends.
This commit is contained in:
Lioncash 2018-05-26 18:53:19 -04:00
parent bda668925a
commit 0125d9b099
3 changed files with 92 additions and 120 deletions

View File

@ -288,7 +288,6 @@ private:
// flag helper
static void Helper_UpdateCR0(u32 value);
static void Helper_UpdateCR1();
// address helper
static u32 Helper_Get_EA(const UGeckoInstruction inst);

View File

@ -52,6 +52,11 @@ inline void UpdateFPSCR()
(FPSCR.ZX & FPSCR.ZE) | (FPSCR.XX & FPSCR.XE);
}
inline void Helper_UpdateCR1()
{
PowerPC::SetCRField(1, (FPSCR.FX << 3) | (FPSCR.FEX << 2) | (FPSCR.VX << 1) | FPSCR.OX);
}
inline double ForceSingle(double value)
{
// convert to float...

View File

@ -11,12 +11,93 @@
#include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
#include "Core/PowerPC/PowerPC.h"
// Extremely rare - actually, never seen.
// Star Wars : Rogue Leader spams that at some point :|
void Interpreter::Helper_UpdateCR1()
namespace
{
PowerPC::SetCRField(1, (FPSCR.FX << 3) | (FPSCR.FEX << 2) | (FPSCR.VX << 1) | FPSCR.OX);
// Apply current rounding mode
enum class RoundingMode
{
Nearest = 0b00,
TowardsZero = 0b01,
TowardsPositiveInfinity = 0b10,
TowardsNegativeInfinity = 0b11
};
void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode)
{
const double b = rPS0(inst.FB);
u32 value;
if (b > static_cast<double>(0x7fffffff))
{
value = 0x7fffffff;
SetFPException(FPSCR_VXCVI);
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else if (b < -static_cast<double>(0x80000000))
{
value = 0x80000000;
SetFPException(FPSCR_VXCVI);
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else
{
s32 i = 0;
switch (rounding_mode)
{
case RoundingMode::Nearest:
{
const double t = b + 0.5;
i = static_cast<s32>(t);
if (t - i < 0 || (t - i == 0 && b > 0))
{
i--;
}
break;
}
case RoundingMode::TowardsZero:
i = static_cast<s32>(b);
break;
case RoundingMode::TowardsPositiveInfinity:
i = static_cast<s32>(b);
if (b - i > 0)
{
i++;
}
break;
case RoundingMode::TowardsNegativeInfinity:
i = static_cast<s32>(b);
if (b - i < 0)
{
i--;
}
break;
}
value = static_cast<u32>(i);
const double di = i;
if (di == b)
{
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else
{
SetFI(1);
FPSCR.FR = fabs(di) > fabs(b);
}
}
// Based on HW tests
// FPRF is not affected
riPS0(inst.FD) = 0xfff8000000000000ull | value;
if (value == 0 && std::signbit(b))
riPS0(inst.FD) |= 0x100000000ull;
if (inst.Rc)
Helper_UpdateCR1();
}
} // Anonymous namespace
void Interpreter::Helper_FloatCompareOrdered(UGeckoInstruction inst, double fa, double fb)
{
@ -103,127 +184,14 @@ void Interpreter::fcmpu(UGeckoInstruction inst)
Helper_FloatCompareUnordered(inst, rPS0(inst.FA), rPS0(inst.FB));
}
// Apply current rounding mode
void Interpreter::fctiwx(UGeckoInstruction inst)
{
const double b = rPS0(inst.FB);
u32 value;
if (b > (double)0x7fffffff)
{
value = 0x7fffffff;
SetFPException(FPSCR_VXCVI);
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else if (b < -(double)0x80000000)
{
value = 0x80000000;
SetFPException(FPSCR_VXCVI);
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else
{
s32 i = 0;
switch (FPSCR.RN)
{
case 0: // nearest
{
double t = b + 0.5;
i = (s32)t;
if (t - i < 0 || (t - i == 0 && b > 0))
{
i--;
}
break;
}
case 1: // zero
i = (s32)b;
break;
case 2: // +inf
i = (s32)b;
if (b - i > 0)
{
i++;
}
break;
case 3: // -inf
i = (s32)b;
if (b - i < 0)
{
i--;
}
break;
}
value = (u32)i;
double di = i;
if (di == b)
{
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else
{
SetFI(1);
FPSCR.FR = fabs(di) > fabs(b);
}
}
// based on HW tests
// FPRF is not affected
riPS0(inst.FD) = 0xfff8000000000000ull | value;
if (value == 0 && std::signbit(b))
riPS0(inst.FD) |= 0x100000000ull;
if (inst.Rc)
Helper_UpdateCR1();
ConvertToInteger(inst, static_cast<RoundingMode>(FPSCR.RN));
}
// Always round toward zero
void Interpreter::fctiwzx(UGeckoInstruction inst)
{
const double b = rPS0(inst.FB);
u32 value;
if (b > (double)0x7fffffff)
{
value = 0x7fffffff;
SetFPException(FPSCR_VXCVI);
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else if (b < -(double)0x80000000)
{
value = 0x80000000;
SetFPException(FPSCR_VXCVI);
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else
{
s32 i = (s32)b;
double di = i;
if (di == b)
{
FPSCR.FI = 0;
FPSCR.FR = 0;
}
else
{
SetFI(1);
FPSCR.FR = fabs(di) > fabs(b);
}
value = (u32)i;
}
// based on HW tests
// FPRF is not affected
riPS0(inst.FD) = 0xfff8000000000000ull | value;
if (value == 0 && std::signbit(b))
riPS0(inst.FD) |= 0x100000000ull;
if (inst.Rc)
Helper_UpdateCR1();
ConvertToInteger(inst, RoundingMode::TowardsZero);
}
void Interpreter::fmrx(UGeckoInstruction inst)