diff --git a/ps2xRecomp/src/lib/vu_translation_helpers.cpp b/ps2xRecomp/src/lib/vu_translation_helpers.cpp index 509fd07b0..e09cc5ecb 100644 --- a/ps2xRecomp/src/lib/vu_translation_helpers.cpp +++ b/ps2xRecomp/src/lib/vu_translation_helpers.cpp @@ -164,17 +164,16 @@ namespace ps2recomp std::string CodeGenerator::translateVU_VCALLMSR(const Instruction &inst) { - // VCALLMSR calls a VU0 microprogram at address stored in integer register - uint8_t vis_reg_idx = inst.rd; // Source integer register (vis) - - return fmt::format( - "{{ " - " uint16_t instr_index = ctx->vi[{}] & 0x1FF; " // Get instruction index from VI[IS], mask to 9 bits - " uint32_t target_byte_addr = (uint32_t)instr_index << 3; " // Convert to byte address - " ctx->vu0_pc = target_byte_addr; " - " runtime->vu0StartMicroProgram(rdram, ctx, target_byte_addr); " - "}}", - vis_reg_idx); + // VCALLMSR calls a VU0 microprogram at the address held in CMSAR0. CMSAR0 is VU0 control register 27 + // (written by CTC2 $vi27, see VU0_CR_CMSAR0), not one of the 16 VI registers, so it lives in ctx->vu0_cmsar0. + (void)inst; + + return "{ " + " uint16_t instr_index = (uint16_t)(ctx->vu0_cmsar0 & 0x1FF); " // Get instruction index from CMSAR0, mask to 9 bits + " uint32_t target_byte_addr = (uint32_t)instr_index << 3; " // Convert to byte address + " ctx->vu0_pc = target_byte_addr; " + " runtime->vu0StartMicroProgram(rdram, ctx, target_byte_addr); " + "}"; } std::string CodeGenerator::translateVU_VRNEXT(const Instruction &inst) diff --git a/ps2xTest/src/code_generator_tests.cpp b/ps2xTest/src/code_generator_tests.cpp index 463b6cff1..f2a4303b5 100644 --- a/ps2xTest/src/code_generator_tests.cpp +++ b/ps2xTest/src/code_generator_tests.cpp @@ -2035,6 +2035,25 @@ void register_code_generator_tests() "VRXOR should keep the LFSR comment on its own line"); }); + tc.Run("VCALLMSR starts the microprogram from CMSAR0, not a VI array slot", [](TestCase &t) { + CodeGenerator gen({}, {}); + + // VCALLMSR has no register operand; $vi27 in the assembly is CMSAR0 (VU0 control register 27). + Instruction inst{}; + inst.opcode = OPCODE_COP2; + inst.function = VU0_S1_VCALLMSR; + inst.rd = 27; + + const std::string generated = gen.translateVU_VCALLMSR(inst); + printGeneratedCode("VCALLMSR starts the microprogram from CMSAR0, not a VI array slot", generated); + t.IsTrue(generated.find("ctx->vu0_cmsar0") != std::string::npos, + "VCALLMSR should read the start address from CMSAR0"); + t.IsTrue(generated.find("ctx->vi[") == std::string::npos, + "VCALLMSR must not index the 16-entry vi[] array with a control register number"); + t.IsTrue(generated.find("runtime->vu0StartMicroProgram(") != std::string::npos, + "VCALLMSR should still start the VU0 microprogram"); + }); + tc.Run("resolveStubTarget allows leading underscore alias", [](TestCase &t) { t.Equals(PS2Recompiler::resolveStubTarget("_rand"), StubTarget::Stub, "_rand should resolve via rand stub alias");