diff --git a/pcsx2/VUops.cpp b/pcsx2/VUops.cpp index 0278f95899..3ef9582e30 100644 --- a/pcsx2/VUops.cpp +++ b/pcsx2/VUops.cpp @@ -980,7 +980,9 @@ static __fi void _vuSQRT(VURegs* VU) VU->statusflag &= ~0x30; - if (ft < 0.0) + // Sign bit, not `ft < 0.0`: -0 raises I, and so do the denormals vuDouble + // has already flushed to it. + if (VU->VF[_Ft_].UL[_Ftf_] & 0x80000000) VU->statusflag |= 0x10; VU->q.F = sqrt(fabs(ft)); VU->q.F = vuDouble(VU->q.UL); diff --git a/pcsx2/arm64/iCOP2-arm64.cpp b/pcsx2/arm64/iCOP2-arm64.cpp index 8288a7b583..b68189f085 100644 --- a/pcsx2/arm64/iCOP2-arm64.cpp +++ b/pcsx2/arm64/iCOP2-arm64.cpp @@ -2524,25 +2524,20 @@ void recCOP2_VSQRT() const int ftf = _Ftf_cop2; - // Clear D/I flags + // Clear D/I, then take I from the sign bit: a compare against zero misses + // -0 and reads an unordered result as negative. armAsm->Ldr(RWSCRATCH, armVU0Mem(&VU0.statusflag)); armAsm->Mov(RWARG1, 0x30); armAsm->Bic(RWSCRATCH, RWSCRATCH, RWARG1); // clear D/I bits + armAsm->Ldr(a64::w1, armVU0Mem(&VU0.VF[_Ft_cop2].UL[ftf])); + a64::Label ftPositive; + armAsm->Tbz(a64::w1, 31, &ftPositive); + armAsm->Orr(RWSCRATCH, RWSCRATCH, 0x10); + armAsm->Bind(&ftPositive); armAsm->Str(RWSCRATCH, armVU0Mem(&VU0.statusflag)); // Load ft scalar armAsm->Ldr(RSSCRATCH, armVU0Mem(&VU0.VF[_Ft_cop2].UL[ftf])); - // If ft < 0, set invalid flag (D flag = 0x10) - a64::Label notNeg; - armAsm->Fcmp(RSSCRATCH, 0.0); - armAsm->B(a64::ge, ¬Neg); - { - armAsm->Ldr(RWSCRATCH, armVU0Mem(&VU0.statusflag)); - armAsm->Orr(RWSCRATCH, RWSCRATCH, 0x10); - armAsm->Str(RWSCRATCH, armVU0Mem(&VU0.statusflag)); - } - armAsm->Bind(¬Neg); - // Q = sqrt(|ft|) armAsm->Fabs(RSSCRATCH, RSSCRATCH); armAsm->Fsqrt(RSSCRATCH, RSSCRATCH);