diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 84e8402765..59dc69ff67 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -742,25 +742,51 @@ namespace MIPSComp case 27: //VFPU3 switch ((op >> 23) & 7) { case 2: // vmin + { VCMP(fpr.V(sregs[i]), fpr.V(tregs[i])); VMRS_APSR(); - // TODO: Technically should use NaN sign bit. + FixupBranch skipNAN = B_CC(CC_VC); + VMOV(SCRATCHREG1, fpr.V(sregs[i])); + VMOV(SCRATCHREG2, fpr.V(tregs[i])); + // If both are negative, we reverse the comparison. We want the highest mantissa then. + // Also, between -NAN and -5.0, we want -NAN to be less. + TST(SCRATCHREG1, SCRATCHREG2); + SetCC(CC_MI); + CMP(SCRATCHREG2, SCRATCHREG1); + SetCC(CC_PL); + CMP(SCRATCHREG1, SCRATCHREG2); + SetCC(CC_AL); + SetJumpTarget(skipNAN); SetCC(CC_LT); VMOV(fpr.V(tempregs[i]), fpr.V(sregs[i])); SetCC(CC_GE); VMOV(fpr.V(tempregs[i]), fpr.V(tregs[i])); SetCC(CC_AL); break; + } case 3: // vmax + { VCMP(fpr.V(tregs[i]), fpr.V(sregs[i])); VMRS_APSR(); - // TODO: Technically should use NaN sign bit. + FixupBranch skipNAN = B_CC(CC_VC); + VMOV(SCRATCHREG1, fpr.V(sregs[i])); + VMOV(SCRATCHREG2, fpr.V(tregs[i])); + // If both are negative, we reverse the comparison. We want the lowest mantissa then. + // Also, between -NAN and -5.0, we want -5.0 to be greater. + TST(SCRATCHREG2, SCRATCHREG1); + SetCC(CC_MI); + CMP(SCRATCHREG1, SCRATCHREG2); + SetCC(CC_PL); + CMP(SCRATCHREG2, SCRATCHREG1); + SetCC(CC_AL); + SetJumpTarget(skipNAN); SetCC(CC_LT); VMOV(fpr.V(tempregs[i]), fpr.V(sregs[i])); SetCC(CC_GE); VMOV(fpr.V(tempregs[i]), fpr.V(tregs[i])); SetCC(CC_AL); break; + } case 6: // vsge DISABLE; // pending testing VCMP(fpr.V(tregs[i]), fpr.V(sregs[i])); diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 9a555b7d9a..e625abeb43 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -893,7 +893,18 @@ namespace MIPSInt switch (op & 0x3f) { case 12: FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s - case 13: FsI(fd) = F(fs)>=0 ? (int)floorf(F(fs)) : (int)ceilf(F(fs)); break;//trunc.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) == -2147483648) { + FsI(fd) = 2147483647; + } + } 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 } diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 5c503fe649..fdb8ae34bd 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1530,26 +1530,54 @@ namespace MIPSInt int cond = op&15; VectorSize sz = GetVecSize(op); int numElements = GetNumVectorElements(sz); - float s[4]; - float t[4]; - float d[4]; - ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); - ReadVector(t, sz, vt); - ApplySwizzleT(t, sz); + + union FloatBits { + float f[4]; + u32 u[4]; + int i[4]; + }; + + FloatBits s; + FloatBits t; + FloatBits d; + ReadVector(s.f, sz, vs); + ApplySwizzleS(s.f, sz); + ReadVector(t.f, sz, vt); + ApplySwizzleT(t.f, sz); // If both are zero, take t's sign. - // TODO: Otherwise: -NAN < -INF < real < INF < NAN + // Otherwise: -NAN < -INF < real < INF < NAN (higher mantissa is farther from 0.) switch ((op >> 23) & 3) { case 2: // vmin for (int i = 0; i < numElements; i++) { - d[i] = my_isnan(t[i]) ? s[i] : (my_isnan(s[i]) ? t[i] : std::min(t[i], s[i])); + if (my_isnanorinf(s.f[i]) || my_isnanorinf(t.f[i])) { + // If both are negative, we flip the comparison (not two's compliment.) + if (s.i[i] < 0 && t.i[i] < 0) { + // If at least one side is NAN, we take the highest mantissa bits. + d.i[i] = std::max(t.i[i], s.i[i]); + } else { + // Otherwise, we take the lowest value (negative or lowest mantissa.) + d.i[i] = std::min(t.i[i], s.i[i]); + } + } else { + d.f[i] = std::min(t.f[i], s.f[i]); + } } break; case 3: // vmax - for (int i = 0; i < numElements; i++) - d[i] = my_isnan(t[i]) ? t[i] : (my_isnan(s[i]) ? s[i] : std::max(t[i], s[i])); + for (int i = 0; i < numElements; i++) { + // This is the same logic as vmin, just reversed. + if (my_isnanorinf(s.f[i]) || my_isnanorinf(t.f[i])) { + if (s.i[i] < 0 && t.i[i] < 0) { + d.i[i] = std::min(t.i[i], s.i[i]); + } else { + d.i[i] = std::max(t.i[i], s.i[i]); + } + } else { + d.f[i] = std::max(t.f[i], s.f[i]); + } + } break; default: _dbg_assert_msg_(CPU,0,"unknown min/max op %d", cond); @@ -1557,8 +1585,8 @@ namespace MIPSInt EatPrefixes(); return; } - ApplyPrefixD(d, sz); - WriteVector(d, sz, vd); + ApplyPrefixD(d.f, sz); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); }