From cb3a848391434d2fbfbdd2b1f7e38218cd964750 Mon Sep 17 00:00:00 2001 From: Yan Moura Date: Mon, 19 Oct 2026 15:20:12 -0300 Subject: [PATCH] Fix SQRT.S operand selection in the FPU translator On the R5900, SQRT.S fd, ft takes its source from the ft field (the fs field is 0 in the encoding). The translator read fs, so a real "sqrt.s $f2, $f2" (word 0x46020084) became FPU_SQRT_S(f[0]). Read ft instead. FPU_SQRT_S already ignores the sign of its input, as the hardware does. Add a code generator test for the encoding. RSQRT.S had the same kind of bug and was fixed in #272. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HhodgessnwK5k3pipLwFp3 --- ps2xRecomp/src/lib/fpu_translator.cpp | 3 ++- ps2xTest/src/code_generator_tests.cpp | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ps2xRecomp/src/lib/fpu_translator.cpp b/ps2xRecomp/src/lib/fpu_translator.cpp index 435db3aa9..00e455c81 100644 --- a/ps2xRecomp/src/lib/fpu_translator.cpp +++ b/ps2xRecomp/src/lib/fpu_translator.cpp @@ -58,7 +58,8 @@ namespace ps2recomp "ctx->f[{}] = FPU_DIV_S(ctx->f[{}], ctx->f[{}]); }}", ft, fd, fs, ft); case COP1_S_SQRT: - return fmt::format("ctx->f[{}] = FPU_SQRT_S(ctx->f[{}]);", fd, fs); + // SQRT.S fd, ft reads the ft field; fs is 0 in the encoding. + return fmt::format("ctx->f[{}] = FPU_SQRT_S(ctx->f[{}]);", fd, ft); case COP1_S_ABS: return fmt::format("ctx->f[{}] = FPU_ABS_S(ctx->f[{}]);", fd, fs); case COP1_S_MOV: diff --git a/ps2xTest/src/code_generator_tests.cpp b/ps2xTest/src/code_generator_tests.cpp index 463b6cff1..6bc6b01f1 100644 --- a/ps2xTest/src/code_generator_tests.cpp +++ b/ps2xTest/src/code_generator_tests.cpp @@ -1020,6 +1020,26 @@ void register_code_generator_tests() t.IsTrue(ctc1Code.find("ignored") == std::string::npos, "CTC1 FCR31 should not be ignored"); }); + tc.Run("SQRT.S reads the ft operand", [](TestCase &t) { + CodeGenerator gen({}, {}); + + // sqrt.s $f2, $f2 (0x46020084): fs field is 0, the source is ft + Instruction sqrtS{}; + sqrtS.raw = 0x46020084; + sqrtS.opcode = OPCODE_COP1; + sqrtS.rs = COP1_S; + sqrtS.rt = 2; + sqrtS.rd = 0; + sqrtS.sa = 2; + sqrtS.function = COP1_S_SQRT; + + std::string sqrtCode = gen.translateInstruction(sqrtS); + printGeneratedCode("SQRT.S reads the ft operand", sqrtCode); + t.IsTrue(sqrtCode.find("ctx->f[2] = FPU_SQRT_S(ctx->f[2])") != std::string::npos, + "SQRT.S should take the square root of ft"); + t.IsTrue(sqrtCode.find("ctx->f[0]") == std::string::npos, "SQRT.S must not read fs"); + }); + tc.Run("VU CFC2/CTC2 access VI registers directly", [](TestCase& t) { CodeGenerator gen({}, {});