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:
parent
bda668925a
commit
0125d9b099
|
@ -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);
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue