diff --git a/Core/MIPS/InterpreterVFPU.cpp b/Core/MIPS/InterpreterVFPU.cpp index 0d28796e69..3012731a2b 100644 --- a/Core/MIPS/InterpreterVFPU.cpp +++ b/Core/MIPS/InterpreterVFPU.cpp @@ -102,6 +102,27 @@ inline float nanclamp(float f, float lower, float upper) return nanmin(nanmax(f, lower), upper); } +// The ordering the hardware's min/max family compares with: denormals count as zero, so ±0 and +// every denormal tie with each other, and inf/NaN sort by sign and magnitude (-NaN < -inf < finite +// < inf < NaN). A tie returns the second operand (cpu/vfpu/minmax_tie, minmax_zero, specials). +inline s32 vfpu_order_key(float f) { + u32 u; + memcpy(&u, &f, sizeof(u)); + u32 mag = u & 0x7FFFFFFF; + if (mag < 0x00800000) { + mag = 0; + } + return (u & 0x80000000) ? -(s32)mag : (s32)mag; +} + +inline float vfpu_min(float a, float b) { + return vfpu_order_key(a) < vfpu_order_key(b) ? a : b; +} + +inline float vfpu_max(float a, float b) { + return vfpu_order_key(a) > vfpu_order_key(b) ? a : b; +} + static void ApplyPrefixST(MIPSState *mips, float *r, u32 data, VectorSize size, float invalid = 0.0f) { // Check for no prefix. if (data == 0xe4) @@ -1319,11 +1340,12 @@ namespace MIPSInt u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2); ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); - // TODO: May mishandle NAN / negative zero / etc. - d[0] = std::min(s[0], t[0]); - d[1] = std::max(s[1], t[1]); - d[2] = std::min(s[2], t[2]); - d[3] = std::max(s[3], t[3]); + // Each pair is compared as (y, x) and (w, z), so a tie keeps the lower lane; vsrt3 and + // vsrt4 compare the other way round and keep the upper one. + d[0] = vfpu_min(t[0], s[0]); + d[1] = vfpu_max(s[1], t[1]); + d[2] = vfpu_min(t[2], s[2]); + d[3] = vfpu_max(s[3], t[3]); RetainInvalidSwizzleST(mips, d, sz); ApplyPrefixD(mips, d, sz); WriteVector(mips, d, sz, vd); @@ -1345,11 +1367,11 @@ namespace MIPSInt u32 tprefixAdd = VFPU_SWIZZLE(3, 2, 1, 0); ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); - // TODO: May mishandle NAN / negative zero / etc. - d[0] = std::min(s[0], t[0]); - d[1] = std::min(s[1], t[1]); - d[2] = std::max(s[2], t[2]); - d[3] = std::max(s[3], t[3]); + // Compared as (w, x) and (z, y), ties keep x and y. + d[0] = vfpu_min(t[0], s[0]); + d[1] = vfpu_min(t[1], s[1]); + d[2] = vfpu_max(s[2], t[2]); + d[3] = vfpu_max(s[3], t[3]); RetainInvalidSwizzleST(mips, d, sz); ApplyPrefixD(mips, d, sz); WriteVector(mips, d, sz, vd); @@ -1371,11 +1393,11 @@ namespace MIPSInt u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2); ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); - // TODO: May mishandle NAN / negative zero / etc. - d[0] = std::max(s[0], t[0]); - d[1] = std::min(s[1], t[1]); - d[2] = std::max(s[2], t[2]); - d[3] = std::min(s[3], t[3]); + // Compared as (x, y) and (z, w), ties keep y and w. + d[0] = vfpu_max(s[0], t[0]); + d[1] = vfpu_min(t[1], s[1]); + d[2] = vfpu_max(s[2], t[2]); + d[3] = vfpu_min(t[3], s[3]); RetainInvalidSwizzleST(mips, d, sz); ApplyPrefixD(mips, d, sz); WriteVector(mips, d, sz, vd); @@ -1397,11 +1419,11 @@ namespace MIPSInt u32 tprefixAdd = VFPU_SWIZZLE(3, 2, 1, 0); ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); - // TODO: May mishandle NAN / negative zero / etc. - d[0] = std::max(s[0], t[0]); - d[1] = std::max(s[1], t[1]); - d[2] = std::min(s[2], t[2]); - d[3] = std::min(s[3], t[3]); + // Compared as (x, w) and (y, z), ties keep w and z. + d[0] = vfpu_max(s[0], t[0]); + d[1] = vfpu_max(s[1], t[1]); + d[2] = vfpu_min(t[2], s[2]); + d[3] = vfpu_min(t[3], s[3]); RetainInvalidSwizzleST(mips, d, sz); ApplyPrefixD(mips, d, sz); WriteVector(mips, d, sz, vd);