mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-30 12:32:43 +00:00
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:
commit
9709f7f821
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user