Merge pull request #968 from achurch/ppu-interpreter-fixes

Implement FPU rounding mode and exception checking in the PPU interpreter.
This commit is contained in:
B1ackDaemon 2015-01-18 20:54:08 +02:00
commit 9709f7f821

View File

@ -17,6 +17,8 @@
#define _rotl64(x,r) (((u64)(x) << (r)) | ((u64)(x) >> (64 - (r))))
#endif
#include <fenv.h>
#if 0//def _DEBUG
#define HLE_CALL_DEBUG
#endif
@ -60,6 +62,26 @@ static double SilenceNaN(double x)
return (double&)bits;
}
static void SetHostRoundingMode(u32 rn)
{
switch (rn)
{
case FPSCR_RN_NEAR:
fesetround(FE_TONEAREST);
break;
case FPSCR_RN_ZERO:
fesetround(FE_TOWARDZERO);
break;
case FPSCR_RN_PINF:
fesetround(FE_UPWARD);
break;
case FPSCR_RN_MINF:
fesetround(FE_DOWNWARD);
break;
}
}
namespace ppu_recompiler_llvm {
class Compiler;
}
@ -79,6 +101,13 @@ public:
}
private:
void CheckHostFPExceptions()
{
CPU.SetFPSCR_FI(fetestexcept(FE_INEXACT) != 0);
if (fetestexcept(FE_UNDERFLOW)) CPU.SetFPSCRException(FPSCR_UX);
if (fetestexcept(FE_OVERFLOW)) CPU.SetFPSCRException(FPSCR_OX);
}
void Exit() {}
void SysCall()
@ -245,6 +274,7 @@ private:
}
void VADDFP(u32 vd, u32 va, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float a = CheckVSCR_NJ(CPU.VPR[va]._f[w]);
@ -441,6 +471,7 @@ private:
}
void VCFSX(u32 vd, u32 uimm5, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
u32 scale = 1 << uimm5;
for (uint w = 0; w < 4; w++)
@ -450,6 +481,7 @@ private:
}
void VCFUX(u32 vd, u32 uimm5, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
u32 scale = 1 << uimm5;
for (uint w = 0; w < 4; w++)
@ -786,8 +818,8 @@ private:
CPU.VPR[vd]._s32[w] = (int)0x80000000;
CPU.VSCR.SAT = 1;
}
else // C rounding = Round towards 0
CPU.VPR[vd]._s32[w] = (int)result;
else
CPU.VPR[vd]._s32[w] = (int)trunc(result);
}
}
}
@ -804,7 +836,6 @@ private:
}
else
{
// C rounding = Round towards 0
double result = (double)b * nScale;
if (result > 0xffffffffu)
{
@ -817,7 +848,7 @@ private:
CPU.VSCR.SAT = 1;
}
else
CPU.VPR[vd]._u32[w] = (u32)result;
CPU.VPR[vd]._u32[w] = (u32)trunc(result);
}
}
}
@ -826,6 +857,7 @@ private:
// vd = 2^x
// ISA : Note that the value placed into the element of vD may vary between implementations
// and between different executions on the same implementation.
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float b = CheckVSCR_NJ(CPU.VPR[vb]._f[w]);
@ -839,6 +871,7 @@ private:
{
// ISA : Note that the value placed into the element of vD may vary between implementations
// and between different executions on the same implementation.
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float b = CheckVSCR_NJ(CPU.VPR[vb]._f[w]);
@ -850,6 +883,7 @@ private:
}
void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float a = CheckVSCR_NJ(CPU.VPR[va]._f[w]);
@ -1275,6 +1309,7 @@ private:
}
void VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float a = CheckVSCR_NJ(CPU.VPR[va]._f[w]);
@ -1568,6 +1603,7 @@ private:
}
void VREFP(u32 vd, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float b = CheckVSCR_NJ(CPU.VPR[vb]._f[w]);
@ -1596,7 +1632,10 @@ private:
if (std::isnan(b))
CPU.VPR[vd]._f[w] = SilenceNaN(b);
else
{
SetHostRoundingMode(FPSCR_RN_NEAR);
CPU.VPR[vd]._f[w] = nearbyintf(CPU.VPR[vb]._f[w]);
}
}
}
void VRFIP(u32 vd, u32 vb)
@ -1646,6 +1685,7 @@ private:
}
void VRSQRTEFP(u32 vd, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
//TODO: accurate div
@ -1846,6 +1886,7 @@ private:
}
void VSUBFP(u32 vd, u32 va, u32 vb)
{
SetHostRoundingMode(FPSCR_RN_NEAR);
for (uint w = 0; w < 4; w++)
{
const float a = CheckVSCR_NJ(CPU.VPR[va]._f[w]);
@ -3663,6 +3704,7 @@ private:
void FSQRTS(u32 frd, u32 frb, bool rc) {FSQRT(frd, frb, rc, true);}
void FRES(u32 frd, u32 frb, bool rc)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double b = CPU.FPR[frb];
if(FPRdouble::IsSNaN(b))
{
@ -3693,7 +3735,9 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
CPU.FPR[frd] = static_cast<float>(1.0 / b);
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
if(rc) CPU.UpdateCR1();
@ -3797,6 +3841,7 @@ private:
}
void FRSP(u32 frd, u32 frb, bool rc)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double b = CPU.FPR[frb];
if (FPRdouble::IsSNaN(b))
{
@ -3814,6 +3859,7 @@ private:
{
if (((u64&)b0 & DOUBLE_EXP) < 0x3800000000000000ULL) (u64&)b0 &= DOUBLE_SIGN;
}
feclearexcept(FE_ALL_EXCEPT);
const double r = static_cast<float>(b0);
if (FPRdouble::IsNaN(r))
{
@ -3823,7 +3869,7 @@ private:
else
{
CPU.FPSCR.FR = fabs(r) > fabs(b);
CPU.SetFPSCR_FI(b != r);
CheckHostFPExceptions();
}
u32 type = PPCdouble(r).GetType();
if (type == FPR_PN && r < ldexp(1.0, -126)) type = FPR_PD;
@ -3869,6 +3915,7 @@ private:
switch(rn)
{
case FPSCR_RN_NEAR:
SetHostRoundingMode(FPSCR_RN_NEAR);
i = (s32)nearbyint(b);
break;
case FPSCR_RN_ZERO:
@ -3902,6 +3949,7 @@ private:
void FDIV(u32 frd, u32 fra, u32 frb, bool rc) {FDIV(frd, fra, frb, rc, false);}
void FDIV(u32 frd, u32 fra, u32 frb, bool rc, bool single)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double a = CPU.FPR[fra];
const double b = CPU.FPR[frb];
if(FPRdouble::IsSNaN(a) || FPRdouble::IsSNaN(b))
@ -3960,9 +4008,11 @@ private:
return;
}
}
feclearexcept(FE_ALL_EXCEPT);
const double res = a / b;
if(single) CPU.FPR[frd] = (float)res;
else CPU.FPR[frd] = res;
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
@ -3971,6 +4021,7 @@ private:
void FSUB(u32 frd, u32 fra, u32 frb, bool rc) {FSUB(frd, fra, frb, rc, false);}
void FSUB(u32 frd, u32 fra, u32 frb, bool rc, bool single)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double a = CPU.FPR[fra];
const double b = CPU.FPR[frb];
if(FPRdouble::IsSNaN(a) || FPRdouble::IsSNaN(b))
@ -4006,9 +4057,11 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
const double res = a - b;
if(single) CPU.FPR[frd] = (float)res;
else CPU.FPR[frd] = res;
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
if(rc) CPU.UpdateCR1();
@ -4016,6 +4069,7 @@ private:
void FADD(u32 frd, u32 fra, u32 frb, bool rc) {FADD(frd, fra, frb, rc, false);}
void FADD(u32 frd, u32 fra, u32 frb, bool rc, bool single)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double a = CPU.FPR[fra];
const double b = CPU.FPR[frb];
if(FPRdouble::IsSNaN(a) || FPRdouble::IsSNaN(b))
@ -4051,9 +4105,11 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
const double res = a + b;
if(single) CPU.FPR[frd] = (float)res;
else CPU.FPR[frd] = res;
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
if(rc) CPU.UpdateCR1();
@ -4061,6 +4117,7 @@ private:
void FSQRT(u32 frd, u32 frb, bool rc) {FSQRT(frd, frb, rc, false);}
void FSQRT(u32 frd, u32 frb, bool rc, bool single)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double b = CPU.FPR[frb];
if(FPRdouble::IsSNaN(b))
{
@ -4091,9 +4148,11 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
const double res = sqrt(b);
if(single) CPU.FPR[frd] = (float)res;
else CPU.FPR[frd] = res;
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
if(rc) CPU.UpdateCR1();
@ -4106,6 +4165,7 @@ private:
void FMUL(u32 frd, u32 fra, u32 frc, bool rc) {FMUL(frd, fra, frc, rc, false);}
void FMUL(u32 frd, u32 fra, u32 frc, bool rc, bool single)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double a = CPU.FPR[fra];
const double c = CPU.FPR[frc];
if(FPRdouble::IsSNaN(a) || FPRdouble::IsSNaN(c))
@ -4141,15 +4201,18 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
const double res = a * c;
if(single) CPU.FPR[frd] = (float)res;
else CPU.FPR[frd] = res;
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
if(rc) CPU.UpdateCR1();
}
void FRSQRTE(u32 frd, u32 frb, bool rc)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double b = CPU.FPR[frb];
if(FPRdouble::IsSNaN(b))
{
@ -4192,7 +4255,9 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
CPU.FPR[frd] = 1.0 / sqrt(b);
CheckHostFPExceptions();
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
if(rc) CPU.UpdateCR1();
@ -4201,6 +4266,7 @@ private:
void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) {FMADD(frd, fra, frc, frb, rc, false, false, false);}
void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc, bool neg, bool sub, bool single)
{
SetHostRoundingMode(CPU.FPSCR.RN);
const double a = CPU.FPR[fra];
const double b = CPU.FPR[frb];
const double c = CPU.FPR[frc];
@ -4256,8 +4322,10 @@ private:
}
else
{
feclearexcept(FE_ALL_EXCEPT);
if(single) CPU.FPR[frd] = (float)(neg ? -res : res);
else CPU.FPR[frd] = (neg ? -res : res);
CheckHostFPExceptions();
}
}
CPU.FPSCR.FPRF = CPU.FPR[frd].GetType();
@ -4344,6 +4412,7 @@ private:
switch(rn)
{
case FPSCR_RN_NEAR:
SetHostRoundingMode(FPSCR_RN_NEAR);
i = (s64)nearbyint(b);
break;
case FPSCR_RN_ZERO: