diff --git a/ps2xRecomp/include/ps2recomp/instructions.h b/ps2xRecomp/include/ps2recomp/instructions.h index 3a41e8c..b2dc146 100644 --- a/ps2xRecomp/include/ps2recomp/instructions.h +++ b/ps2xRecomp/include/ps2recomp/instructions.h @@ -281,7 +281,7 @@ namespace ps2recomp MMI3_PCPYH = 0x1B, MMI3_PEXCW = 0x1E, MMI3_PMTHI = 0x08, // Move To HI register - MMI3_PMTLO = 0x09 // Move To LO register + MMI3_PMTLO = 0x09 // Move To LO register }; // COP0 (rs field) @@ -342,8 +342,8 @@ namespace ps2recomp COP1_W = 0x14, // Word (integer) Operation COP1_BC_BCF = 0x00, COP1_BC_BCT = 0x01, - COP1_L = 0x15, // Long (64-bit integer) Operation - COP1_D = 0x11, // Double Precision Operation (unused on PS2 FPU?) + COP1_L = 0x15, // Long (64-bit integer) Operation + COP1_D = 0x11, // Double Precision Operation (unused on PS2 FPU?) }; enum Cop1Functions @@ -386,7 +386,6 @@ namespace ps2recomp COP1_FUNC_C_LE = 0x3E, // Signaling Less Than or Equal COP1_FUNC_C_NGT = 0x3F, // Not Greater Than (Unordered or Less Than or Equal) }; - // COP2 (VU0 macro) function codes enum Cop2Functions @@ -435,7 +434,8 @@ namespace ps2recomp VU0_VISWR = 0x16, VU0_VCALLMS = 0x20, VU0_VCALLMSR = 0x21, - VU0_VRGET = 0x3F // Get VU0 R register + VU0_VRGET = 0x3F, // Get VU0 R register + VU0_VSUB_XYZ = 0x38 // Subtract with component selection }; enum VU0SpecialFormats @@ -459,13 +459,39 @@ namespace ps2recomp enum VU0OPSFunctions { - VU0OPS_QMFC2_NI = 0x00, // Non-incrementing QMFC2 - VU0OPS_QMFC2_I = 0x01, // Incrementing QMFC2 - VU0OPS_QMTC2_NI = 0x02, // Non-incrementing QMTC2 - VU0OPS_QMTC2_I = 0x03, // Incrementing QMTC2 - VU0OPS_VMFIR = 0x04, // Move From Integer Register - VU0OPS_VXITOP = 0x08, // Execute Interrupt on VU0 - VU0OPS_VWAITQ = 0x3C // Wait for Q register operations to complete + VU0OPS_QMFC2_NI = 0x00, // Non-incrementing QMFC2 + VU0OPS_QMFC2_I = 0x01, // Incrementing QMFC2 + VU0OPS_QMTC2_NI = 0x02, // Non-incrementing QMTC2 + VU0OPS_QMTC2_I = 0x03, // Incrementing QMTC2 + VU0OPS_VMFIR = 0x04, // Move From Integer Register + VU0OPS_VXITOP = 0x08, // Execute Interrupt on VU0 + VU0OPS_VWAITQ = 0x3B, // Wait for Q register operations to complete + VU0OPS_VMFHL = 0x1C, // Move from High / Low(similar to PMFHL) + VU0OPS_VMTIR = 0x3C // Move to VU0 I Register with function code + }; + + enum VU0OPSFunctionsExtra + { + VU0_VCALLMS_DIRECT = 0x00, + VU0_VCALLMS_REG = 0x01 + }; + + enum VU0SpecialMasks + { + VU0_FIELD_MASK = 0xF, // Mask for vector field selection + VU0_FIELD_SHIFT = 21, // Shift amount for field selection + VU0_STORE_BIT = 0x80, // Bit indicating store operation + VU0_SUBOP_MASK = 0x1F, // Mask for sub-operation code + VU0_SUBOP_SHIFT = 6, // Shift amount for sub-operation + VU0_SQC0 = 0x8 // SQC0 instruction code + }; + + enum VU0FormatsExtra + { + VU0_FMT_MACRO_MOVE = 0x1, // Move between VU registers format + VU0_FMT_VIF_STATUS = 0x5, // VIF status operations + VU0_FMT_VCALLMS = 0x14, // VU0 microprogram call format + VU0_FMT_LQSQ_COP0 = 0x1B // Load/store quad VU0 format }; // PMFHL functions (sa field) diff --git a/ps2xRecomp/src/code_generator.cpp b/ps2xRecomp/src/code_generator.cpp index b0faa03..68b3e39 100644 --- a/ps2xRecomp/src/code_generator.cpp +++ b/ps2xRecomp/src/code_generator.cpp @@ -586,18 +586,18 @@ namespace ps2recomp inst.rt, inst.rs, (int16_t)inst.immediate); // Special case for R5900 + case OPCODE_COP0: + return translateCOP0Instruction(inst); + + case OPCODE_COP1: + return translateFPUInstruction(inst); + case OPCODE_CACHE: return "// CACHE instruction (ignored)"; case OPCODE_PREF: return "// PREF instruction (ignored)"; - case OPCODE_COP1: - return translateFPUInstruction(inst); - - case OPCODE_COP0: - return translateCOP0Instruction(inst); - // REGIMM special format (opcode=0x01) case OPCODE_REGIMM: switch (inst.rt) @@ -616,8 +616,7 @@ namespace ps2recomp } case REGIMM_MTSAB: - return fmt::format("ctx->sa = (ctx->r[{}] + 0x{:X}) & 0x0F;", - inst.rs, inst.immediate); + return fmt::format("ctx->sa = (ADD32(ctx->r[{}], (int32_t)(int16_t)0x{:X})) & 0x0F;", inst.rs, inst.immediate); case REGIMM_MTSAH: return fmt::format("ctx->sa = ((ctx->r[{}] + 0x{:X}) & 0x07) << 1;", @@ -1037,7 +1036,7 @@ namespace ps2recomp return fmt::format("ctx->cop0_errorepc = ctx->r[{}];", rt); default: - return fmt::format("// MTC0 to register {} ignored", rd); + return fmt::format("// Unimplemented MTC0 to COP0 {}", rd); } case COP0_CO: // COP0 co-processor operations @@ -1047,24 +1046,16 @@ namespace ps2recomp switch (function) { case COP0_CO_TLBR: - return fmt::format("// TLBR instruction - TLB Read\n" - " // Reads TLB entry specified by Index register\n" - " // Not fully implemented in recompiled code"); + return fmt::format("runtime->handleTLBR(rdram, ctx);"); case COP0_CO_TLBWI: - return fmt::format("// TLBWI instruction - TLB Write Indexed\n" - " // Writes TLB entry specified by Index register\n" - " // Not fully implemented in recompiled code"); + return fmt::format("runtime->handleTLBWI(rdram, ctx);"); case COP0_CO_TLBWR: - return fmt::format("// TLBWR instruction - TLB Write Random\n" - " // Writes TLB entry specified by Random register\n" - " // Not fully implemented in recompiled code"); + return fmt::format("runtime->handleTLBWR(rdram, ctx);"); case COP0_CO_TLBP: - return fmt::format("// TLBP instruction - TLB Probe\n" - " // Searches TLB for matching entry to EntryHi, sets Index\n" - " // Not fully implemented in recompiled code"); + return fmt::format("runtime->handleTLBP(rdram, ctx);"); case COP0_CO_ERET: return fmt::format("// ERET instruction - Return from Exception\n" @@ -1543,6 +1534,138 @@ namespace ps2recomp case COP2_BC2: return fmt::format("// VU branch instruction not implemented"); + case VU0_FMT_LQSQ_COP0: + if (inst.function & VU0_SQC0) // Store operation + { + return fmt::format("WRITE128(ADD32(ctx->r[{}], 0x{:X}), (__m128i)ctx->vu0_vf[{}]);", + inst.rs, (int16_t)inst.immediate, inst.rt); + } + else // Load operation + { + return fmt::format("ctx->vu0_vf[{}] = (__m128)READ128(ADD32(ctx->r[{}], 0x{:X}));", + inst.rt, inst.rs, (int16_t)inst.immediate); + } + + case VU0_FMT_VIF_STATUS: + { + int field = (inst.function >> VU0_FIELD_SHIFT) & VU0_FIELD_MASK; // Field selector + int dest = inst.rd; // Destination register + + if (inst.function & VU0_STORE_BIT) // Store to status + { + return fmt::format("ctx->vu0_status &= ~(0xFF << {}); ctx->vu0_status |= (_mm_extract_epi32(ctx->vu0_vf[{}], 0) & 0xFF) << {};", + field * 8, dest, field * 8); + } + else // Load from status + { + return fmt::format("ctx->vu0_vf[{}] = (__m128)_mm_set1_epi32((ctx->vu0_status >> {}) & 0xFF);", + dest, field * 8); + } + } + + case VU0_FMT_MACRO_MOVE: + { + uint32_t subop = (inst.function >> VU0_SUBOP_SHIFT) & VU0_SUBOP_MASK; + + switch (subop) + { + case VU0OPS_QMFC2_NI: // VMOVE - Move between VF registers + return fmt::format("ctx->vu0_vf[{}] = ctx->vu0_vf[{}];", + inst.rd, inst.rs); + + case VU0OPS_QMFC2_I: // VRNEXT - Get next random value + return fmt::format("{{ uint32_t random_val = (ctx->vu0_status * 0x41C64E6D + 12345) & 0xFFFFFFFF;\n" + " ctx->vu0_status = random_val;\n" + " ctx->vu0_vf[{}] = (__m128)_mm_set1_epi32(random_val); }}", + inst.rd); + + case VU0OPS_QMTC2_NI: // VRGET - Get random state + return fmt::format("ctx->vu0_vf[{}] = (__m128)_mm_set1_epi32(ctx->vu0_status);", + inst.rd); + + default: + return fmt::format("// Unhandled VU0 format 0x1 subop: 0x{:X}", subop); + } + } + + case VU0_FMT_VCALLMS: + { + uint32_t subop = inst.function & 0x3F; + + switch (subop) + { + case VU0_VCALLMS_DIRECT: // VCALLMS - Call VU0 microprogram + return fmt::format("{{ uint32_t mpg_addr = 0x{:X};\n" + " ctx->vu0_cmsar0 = mpg_addr;\n" + " ctx->vu0_status |= 0x1; // Set execution bit }}", + inst.immediate * 8); + + case VU0_VCALLMS_REG: // VCALLMSR - Call VU0 microprogram (register) + return fmt::format("{{ uint32_t mpg_addr = ctx->r[{}].m128i_u32[0] & 0xFFF;\n" + " ctx->vu0_cmsar0 = mpg_addr;\n" + " ctx->vu0_status |= 0x1; // Set execution bit }}", + inst.rs); + + default: + return fmt::format("// Unhandled VU0 format 0x14 subop: 0x{:X}", subop); + } + } + + case VU0_VSUB_XYZ: + { + int destMask = (inst.function >> 21) & 0xF; + + return fmt::format("{{ __m128 result = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);\n" + " __m128 destMask = _mm_set_ps(\n" + " {}f, {}f, {}f, {}f);\n" + " // Blend the result with the destination register based on mask\n" + " ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], result, destMask); }}", + inst.rs, inst.rt, + (destMask & 0x8) ? 0xFFFFFFFF : 0, + (destMask & 0x4) ? 0xFFFFFFFF : 0, + (destMask & 0x2) ? 0xFFFFFFFF : 0, + (destMask & 0x1) ? 0xFFFFFFFF : 0, + inst.rd, inst.rd); + } + + case VU0OPS_VMFHL: // 0x1C - Move from High/Low (similar to PMFHL) + { + int subop = (inst.function >> 16) & 0x1F; + switch (subop) + { + case 0: // LW variant - Extracts low word from accumulator + return fmt::format("{{ \n" + " float w = _mm_cvtss_f32(_mm_castsi128_ps(_mm_cvtsi32_si128(ctx->lo)));\n" + " float z = 0.0f;\n" + " float y = 0.0f;\n" + " float x = 0.0f;\n" + " ctx->vu0_vf[{}] = _mm_set_ps(w, z, y, x); }}", + inst.rd); + + case 1: // UW variant - Extracts upper word from accumulator + return fmt::format("{{ \n" + " float w = _mm_cvtss_f32(_mm_castsi128_ps(_mm_cvtsi32_si128(ctx->hi)));\n" + " float z = 0.0f;\n" + " float y = 0.0f;\n" + " float x = 0.0f;\n" + " ctx->vu0_vf[{}] = _mm_set_ps(w, z, y, x); }}", + inst.rd); + + case 2: // SLW variant - Saturated conversion of accumulator + return fmt::format("{{ \n" + " int64_t acc = ((int64_t)ctx->hi << 32) | ctx->lo;\n" + " float result = (float)acc;\n" + " // Clamp to prevent overflow\n" + " if (result > FLT_MAX) result = FLT_MAX;\n" + " if (result < -FLT_MAX) result = -FLT_MAX;\n" + " ctx->vu0_vf[{}] = _mm_set_ps(result, 0.0f, 0.0f, 0.0f); }}", + inst.rd); + + default: + return fmt::format("// Unhandled VU0OPS_VMFHL suboperation: 0x{:X}", subop); + } + } + case COP2_BC2F: switch (inst.rt) { @@ -1577,9 +1700,6 @@ namespace ps2recomp case COP2_MTVUCF: return fmt::format("ctx->vu0_cf[{}] = ctx->r[{}];", inst.rd, inst.rt); - case COP2_VMTIR: - return fmt::format("ctx->vu0_i = (float)ctx->r[{}].m128_i32[0];", inst.rt); - case COP2_VCLIP: // VU0 Clipping operation (0x1E) return fmt::format("{{ float x = ctx->vu0_vf[{}].m128_f32[0]; " "float y = ctx->vu0_vf[{}].m128_f32[1]; " @@ -1753,6 +1873,9 @@ namespace ps2recomp return fmt::format("// VWAITQ - Wait for Q register operations to complete\n" " // need proper implementation for this\n"); + case VU0OPS_VMTIR: + return fmt::format("ctx->vu0_i = (float)ctx->r[{}].m128_i32[0];", inst.rt); + default: return fmt::format("// Unhandled VU0OPS function: 0x{:X}", inst.function); }