diff --git a/Common/Math/math_util.h b/Common/Math/math_util.h index ee68806836..974fb56be6 100644 --- a/Common/Math/math_util.h +++ b/Common/Math/math_util.h @@ -127,6 +127,19 @@ inline int is_even(float d) { return 2.0f * int_part == d; } +// Float to int the way the PSP's FPU does it: at or past the int32 range the result is the nearest +// limit, and a NaN gives INT_MAX whatever its sign (cpu/fpu/roundmode). The plain cast is undefined +// there, and x86 makes it INT_MIN. Takes a value that's already been rounded. +inline int32_t SaturatedFloatToInt(double d) { + if (d >= 2147483648.0) + return 0x7FFFFFFF; + if (d <= -2147483648.0) + return (int32_t)0x80000000; + if (d != d) + return 0x7FFFFFFF; + return (int32_t)d; +} + // Rounds *.5 to closest even number inline double round_ieee_754(double d) { float i = (float)floor(d); diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index e9bde15ce5..78d72c033b 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -928,58 +928,17 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { mips->r[inst->dest] = mips->vfpuCtrl[inst->src1]; break; case IROp::FRound: - { - float value = mips->f[inst->src1]; - if (my_isnanorinf(value)) { - mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; - break; - } else { - mips->fs[inst->dest] = (int)round_ieee_754(value); - } + mips->fs[inst->dest] = SaturatedFloatToInt(round_ieee_754(mips->f[inst->src1])); break; - } case IROp::FTrunc: - { - float value = mips->f[inst->src1]; - if (my_isnanorinf(value)) { - mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; - break; - } else { - if (value >= 0.0f) { - mips->fs[inst->dest] = (int)floorf(value); - // Overflow, but it was positive. - if (mips->fs[inst->dest] == -2147483648LL) { - mips->fs[inst->dest] = 2147483647LL; - } - } else { - // Overflow happens to be the right value anyway. - mips->fs[inst->dest] = (int)ceilf(value); - } - break; - } - } + mips->fs[inst->dest] = SaturatedFloatToInt(truncf(mips->f[inst->src1])); + break; case IROp::FCeil: - { - float value = mips->f[inst->src1]; - if (my_isnanorinf(value)) { - mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; - break; - } else { - mips->fs[inst->dest] = (int)ceilf(value); - } + mips->fs[inst->dest] = SaturatedFloatToInt(ceilf(mips->f[inst->src1])); break; - } case IROp::FFloor: - { - float value = mips->f[inst->src1]; - if (my_isnanorinf(value)) { - mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; - break; - } else { - mips->fs[inst->dest] = (int)floorf(value); - } + mips->fs[inst->dest] = SaturatedFloatToInt(floorf(mips->f[inst->src1])); break; - } case IROp::FCmp: switch (inst->dest) { case IRFpCompareMode::False: @@ -1019,16 +978,12 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { case IROp::FCvtWS: { float src = mips->f[inst->src1]; - if (my_isnanorinf(src)) { - mips->fs[inst->dest] = my_isinf(src) && src < 0.0f ? -2147483648LL : 2147483647LL; - break; - } // TODO: Inline assembly to use here would be better. switch (IRRoundMode(mips->fcr31 & 3)) { - case IRRoundMode::RINT_0: mips->fs[inst->dest] = (int)round_ieee_754(src); break; - case IRRoundMode::CAST_1: mips->fs[inst->dest] = (int)src; break; - case IRRoundMode::CEIL_2: mips->fs[inst->dest] = (int)ceilf(src); break; - case IRRoundMode::FLOOR_3: mips->fs[inst->dest] = (int)floorf(src); break; + case IRRoundMode::RINT_0: mips->fs[inst->dest] = SaturatedFloatToInt(round_ieee_754(src)); break; + case IRRoundMode::CAST_1: mips->fs[inst->dest] = SaturatedFloatToInt(truncf(src)); break; + case IRRoundMode::CEIL_2: mips->fs[inst->dest] = SaturatedFloatToInt(ceilf(src)); break; + case IRRoundMode::FLOOR_3: mips->fs[inst->dest] = SaturatedFloatToInt(floorf(src)); break; } break; //cvt.w.s } diff --git a/Core/MIPS/Interpreter.cpp b/Core/MIPS/Interpreter.cpp index 206bc8b253..b29ad12c57 100644 --- a/Core/MIPS/Interpreter.cpp +++ b/Core/MIPS/Interpreter.cpp @@ -1130,46 +1130,25 @@ namespace MIPSInt { case 13: case 14: case 15: - if (my_isnanorinf(F(fs))) - { - FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL; - break; - } switch (op & 0x3f) { // round.w.s is round-half-to-even, not half-away-from-zero - and its mode is fixed, // so unlike cvt.w.s below it must not follow fcr31. round_ieee_754 is both. - case 12: FsI(fd) = (int)round_ieee_754(F(fs)); break; //round.w.s - case 13: //trunc.w.s - if (F(fs) >= 0.0f) { - FsI(fd) = (int)floorf(F(fs)); - // Overflow, but it was positive. - if (FsI(fd) == -2147483648LL) { - FsI(fd) = 2147483647LL; - } - } else { - // Overflow happens to be the right value anyway. - FsI(fd) = (int)ceilf(F(fs)); - } - break; - case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s - case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s + case 12: FsI(fd) = SaturatedFloatToInt(round_ieee_754(F(fs))); break; //round.w.s + case 13: FsI(fd) = SaturatedFloatToInt(truncf(F(fs))); break; //trunc.w.s + case 14: FsI(fd) = SaturatedFloatToInt(ceilf(F(fs))); break; //ceil.w.s + case 15: FsI(fd) = SaturatedFloatToInt(floorf(F(fs))); break; //floor.w.s } break; case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w case 36: - if (my_isnanorinf(F(fs))) - { - FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL; - break; - } switch (mips->fcr31 & 3) { - case 0: FsI(fd) = (int)round_ieee_754(F(fs)); break; // RINT_0 - case 1: FsI(fd) = (int)F(fs); break; // CAST_1 - case 2: FsI(fd) = (int)ceilf(F(fs)); break; // CEIL_2 - case 3: FsI(fd) = (int)floorf(F(fs)); break; // FLOOR_3 + case 0: FsI(fd) = SaturatedFloatToInt(round_ieee_754(F(fs))); break; // RINT_0 + case 1: FsI(fd) = SaturatedFloatToInt(truncf(F(fs))); break; // CAST_1 + case 2: FsI(fd) = SaturatedFloatToInt(ceilf(F(fs))); break; // CEIL_2 + case 3: FsI(fd) = SaturatedFloatToInt(floorf(F(fs))); break; // FLOOR_3 } break; //cvt.w.s default: