refactor: refactor VU1 (#191)

* feat: implement fix and changes based on dark cloud report
fix: fix GS AFAIL for RGB/alpha/Z, ZMSK
fix: fix VU1 flags mask and pipeline
fix: small VU1 cache fix
feat: __ct__, __sinit_ are not sillent stubs anymore

* feat: fix song JP pulling

* feat: sound update for lotR

* feat: prevent guest execution to be very slow

* fix: small gs size bug

* feat: refactor VU
fix: fix cliping and other issues on gs
fix: fix wrong vu0 register on recompiler

* fix fix ACC scheduler stall
feat: remove unused test
fix: .fix overflow e underflow on FMAC

* feat: small setting  for windows test
This commit is contained in:
Ranieri
2026-08-05 14:50:24 -03:00
committed by GitHub
parent 61300792a0
commit f49ca4edbc
34 changed files with 5506 additions and 1015 deletions
+126 -22
View File
@@ -7,6 +7,7 @@
#include <fstream>
#include <regex>
#include <sstream>
#include <utility>
using namespace ps2recomp;
@@ -823,32 +824,71 @@ void register_code_generator_tests()
t.IsTrue(ctc1Code.find("ignored") == std::string::npos, "CTC1 FCR31 should not be ignored");
});
tc.Run("VU CReg access uses CFC2/CTC2", [](TestCase &t) {
CodeGenerator gen({}, {});
tc.Run("VU CFC2/CTC2 access VI registers directly", [](TestCase& t)
{
CodeGenerator gen({}, {});
Instruction cfc2{};
cfc2.opcode = OPCODE_COP2;
cfc2.rs = COP2_CFC2;
cfc2.rt = 2;
cfc2.rd = VU0_CR_STATUS;
Instruction cfc2{};
cfc2.opcode = OPCODE_COP2;
cfc2.rs = COP2_CFC2;
cfc2.rt = 2;
cfc2.rd = 11;
std::string cfc2Code = gen.translateInstruction(cfc2);
printGeneratedCode("VU CReg access uses CFC2/CTC2 (CFC2)", cfc2Code);
t.IsTrue(cfc2Code.find("SET_GPR_U32(ctx, 2") != std::string::npos, "CFC2 should write to rt");
t.IsTrue(cfc2Code.find("ctx->vu0_status") != std::string::npos, "CFC2 STATUS should read vu0_status");
t.IsTrue(cfc2Code.find("Unimplemented CFC2 VU CReg") == std::string::npos, "CFC2 should not hit unimplemented CReg path");
std::string cfc2Code = gen.translateInstruction(cfc2);
printGeneratedCode("VU CFC2/CTC2 access VI registers directly (CFC2)", cfc2Code);
Instruction ctc2{};
ctc2.opcode = OPCODE_COP2;
ctc2.rs = COP2_CTC2;
ctc2.rt = 3;
ctc2.rd = VU0_CR_ITOP;
t.IsTrue(cfc2Code.find("SET_GPR_U32(ctx, 2") != std::string::npos, "CFC2 should write to rt");
std::string ctc2Code = gen.translateInstruction(ctc2);
printGeneratedCode("VU CReg access uses CFC2/CTC2 (CTC2)", ctc2Code);
t.IsTrue(ctc2Code.find("ctx->vu0_itop") != std::string::npos, "CTC2 ITOP should write vu0_itop");
t.IsTrue(ctc2Code.find("GPR_U32(ctx, 3) & 0x3FF") != std::string::npos, "CTC2 ITOP should mask to 10 bits");
t.IsTrue(ctc2Code.find("Unimplemented CTC2 VU CReg") == std::string::npos, "CTC2 should not hit unimplemented CReg path");
t.IsTrue(cfc2Code.find("ctx->vi[11]") != std::string::npos, "CFC2 VI11 should read VI11");
t.IsTrue(cfc2Code.find("vu0_cmsar1") == std::string::npos, "CFC2 VI11 must not read CMSAR1");
t.IsTrue(cfc2Code.find("Unimplemented") == std::string::npos, "CFC2 VI11 should be implemented");
Instruction ctc2{};
ctc2.opcode = OPCODE_COP2;
ctc2.rs = COP2_CTC2;
ctc2.rt = 3;
ctc2.rd = 4;
std::string ctc2Code = gen.translateInstruction(ctc2);
printGeneratedCode("VU CFC2/CTC2 access VI registers directly (CTC2)", ctc2Code);
t.IsTrue(ctc2Code.find("ctx->vi[4]") != std::string::npos, "CTC2 VI4 should write VI4");
t.IsTrue(ctc2Code.find("static_cast<uint16_t>(GPR_U32(ctx, 3))") != std::string::npos, "CTC2 VI4 should store the low 16 bits");
t.IsTrue(ctc2Code.find("vu0_i") == std::string::npos, "CTC2 VI4 must not write the I register");
t.IsTrue(ctc2Code.find("Unimplemented") == std::string::npos, "CTC2 VI4 should be implemented");
});
tc.Run("VU special control registers use hardware indices", [](TestCase& t)
{
CodeGenerator gen({}, {});
Instruction cfc2{};
cfc2.opcode = OPCODE_COP2;
cfc2.rs = COP2_CFC2;
cfc2.rt = 2;
cfc2.rd = VU0_CR_STATUS;
std::string cfc2Code = gen.translateInstruction(cfc2);
printGeneratedCode("VU special control registers use hardware indices (STATUS)", cfc2Code);
t.IsTrue(cfc2Code.find("SET_GPR_U32(ctx, 2") != std::string::npos,"CFC2 should write to rt");
t.IsTrue(cfc2Code.find("ctx->vu0_status") != std::string::npos, "CFC2 STATUS should read vu0_status");
t.IsTrue(cfc2Code.find("Unimplemented") == std::string::npos,"CFC2 STATUS should be implemented");
Instruction ctc2{};
ctc2.opcode = OPCODE_COP2;
ctc2.rs = COP2_CTC2;
ctc2.rt = 3;
ctc2.rd = VU0_CR_FBRST;
std::string ctc2Code = gen.translateInstruction(ctc2);
printGeneratedCode("VU special control registers use hardware indices (FBRST)", ctc2Code);
t.IsTrue(ctc2Code.find("ctx->vu0_fbrst") != std::string::npos, "CTC2 register 28 should write FBRST");
t.IsTrue(ctc2Code.find("vu0_itop") == std::string::npos, "CTC2 register 28 must not write ITOP");
t.IsTrue(ctc2Code.find("Unimplemented") == std::string::npos, "CTC2 FBRST should be implemented");
});
tc.Run("scalar logical immediates emit low64 operations", [](TestCase &t) {
@@ -1095,6 +1135,70 @@ void register_code_generator_tests()
t.IsTrue(out.find("ctx->vu0_vf[25]") == std::string::npos, "S1 q/i must not use rs(format) as register index");
});
tc.Run("VU0 destination MADD and MSUB forms preserve ACC", [](TestCase &t) {
Instruction inst{};
inst.rt = 7;
inst.rd = 11;
inst.sa = 3;
inst.function = 0;
inst.vectorInfo.vectorField = 0xE;
CodeGenerator gen({}, {});
const std::vector<std::pair<const char *, std::string>> emitted = {
{"MADD field", gen.translateVU_VMADD_Field(inst)},
{"MADD", gen.translateVU_VMADD(inst)},
{"MADDq", gen.translateVU_VMADDq(inst)},
{"MADDi", gen.translateVU_VMADDi(inst)},
{"MSUB field", gen.translateVU_VMSUB_Field(inst)},
{"MSUB", gen.translateVU_VMSUB(inst)},
{"MSUBq", gen.translateVU_VMSUBq(inst)},
{"MSUBi", gen.translateVU_VMSUBi(inst)},
{"OPMSUB", gen.translateVU_VOPMSUB(inst)},
};
for (const auto &[name, code] : emitted)
{
const std::string message =
std::string(name) + " writes VF and must not overwrite ACC";
t.IsTrue(code.find("ctx->vu0_acc = res") == std::string::npos,
message.c_str());
t.IsTrue(code.find("PS2_VADD(ctx->vu0_acc") != std::string::npos ||
code.find("PS2_VSUB(ctx->vu0_acc") != std::string::npos,
(std::string(name) + " must still read ACC").c_str());
}
const std::string madda = gen.translateVU_VMADDA(inst);
t.IsTrue(madda.find("ctx->vu0_acc =") != std::string::npos,
"MADDA must continue writing ACC");
});
tc.Run("VU0 OPMULA and OPMSUB use cross-product lane permutations", [](TestCase &t) {
Instruction inst{};
inst.rt = 7;
inst.rd = 11;
inst.sa = 3;
inst.vectorInfo.vectorField = 0xE;
CodeGenerator gen({}, {});
const std::string opmula = gen.translateVU_VOPMULA(inst);
const std::string opmsub = gen.translateVU_VOPMSUB(inst);
for (const std::string *code : {&opmula, &opmsub})
{
t.IsTrue(code->find("_MM_SHUFFLE(3,0,2,1)") != std::string::npos,
"OPM source Fs must be permuted to y,z,x");
t.IsTrue(code->find("_MM_SHUFFLE(3,1,0,2)") != std::string::npos,
"OPM source Ft must be permuted to z,x,y");
t.IsTrue(code->find("PS2_VMUL(fs_yzx, ft_zxy)") != std::string::npos,
"OPM product must use the permuted operands");
}
t.IsTrue(opmula.find("ctx->vu0_acc =") != std::string::npos,
"OPMULA must write the permuted product to ACC");
t.IsTrue(opmsub.find("ctx->vu0_acc = res") == std::string::npos,
"OPMSUB must preserve ACC after producing the cross product");
});
tc.Run("VU0 S2 vector ops use rd as source and rt as destination", [](TestCase &t) {
Instruction inst{};
inst.opcode = OPCODE_COP2;