mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user