From 23bb92bd4a1d9edb2fcc9b6af30b8589bc973efe Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 16 Apr 2025 01:45:13 -0300 Subject: [PATCH] refactor: added more instructions decoder --- ps2xRecomp/include/ps2recomp/code_generator.h | 2 + ps2xRecomp/include/ps2recomp/instructions.h | 2 + ps2xRecomp/include/ps2recomp/types.h | 47 ++- ps2xRecomp/src/code_generator.cpp | 110 +++++- ps2xRecomp/src/r5900_decoder.cpp | 359 ++++++++++++++++-- 5 files changed, 463 insertions(+), 57 deletions(-) diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 3f71c33..40f9b87 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -47,6 +47,8 @@ namespace ps2recomp std::string translatePEXCH(const Instruction &inst); std::string translatePCPYH(const Instruction &inst); std::string translatePEXCW(const Instruction &inst); + std::string translatePMTHI(const Instruction &inst); + std::string translatePMTLO(const Instruction &inst); // SPECIAL instructions std::string translateSYNC(const Instruction &inst); diff --git a/ps2xRecomp/include/ps2recomp/instructions.h b/ps2xRecomp/include/ps2recomp/instructions.h index 2ce43d3..3730e7d 100644 --- a/ps2xRecomp/include/ps2recomp/instructions.h +++ b/ps2xRecomp/include/ps2recomp/instructions.h @@ -280,6 +280,8 @@ namespace ps2recomp MMI3_PEXCH = 0x1A, MMI3_PCPYH = 0x1B, MMI3_PEXCW = 0x1E, + MMI3_PMTHI = 0x08, // Move To HI register + MMI3_PMTLO = 0x09 // Move To LO register }; // COP0 (rs field) diff --git a/ps2xRecomp/include/ps2recomp/types.h b/ps2xRecomp/include/ps2recomp/types.h index c14c843..9166878 100644 --- a/ps2xRecomp/include/ps2recomp/types.h +++ b/ps2xRecomp/include/ps2recomp/types.h @@ -23,17 +23,42 @@ namespace ps2recomp uint32_t immediate; // Immediate value for I-type instructions uint32_t target; // Jump target for J-type instructions uint32_t raw; // Raw instruction value - bool isMMI; // Is MMI instruction (PS2 specific) - bool isVU; // Is VU instruction (PS2 specific) - bool isBranch; // Is branch instruction - bool isJump; // Is jump instruction - bool isCall; // Is function call - bool isReturn; // Is return instruction - bool hasDelaySlot; // Has delay slot - bool isMultimedia; // PS2-specific multimedia operations - bool isStore; - bool isLoad; - uint8_t pmfhlVariation; + + // Instruction type flags + bool isMMI; // Is MMI instruction (PS2 specific) + bool isVU; // Is VU instruction (PS2 specific) + bool isBranch; // Is branch instruction + bool isJump; // Is jump instruction + bool isCall; // Is function call + bool isReturn; // Is return instruction + bool hasDelaySlot; // Has delay slot + bool isMultimedia; // PS2-specific multimedia operations + bool isStore; // Is store instruction + bool isLoad; // Is load instruction + + // Additional PS2-specific fields + uint8_t mmiType; // 0=MMI0, 1=MMI1, 2=MMI2, 3=MMI3 + uint8_t mmiFunction; // Function within MMI type + uint8_t pmfhlVariation; // For PMFHL instructions + uint8_t vuFunction; // For VU instructions + + struct + { + bool isVector; // Uses vector operations + bool usesQReg; // Uses Q register + bool usesPReg; // Uses P register + bool modifiesMAC; // Modifies MAC flags + uint8_t vectorField; // xyzw field mask + } vectorInfo; + + struct + { + bool modifiesGPR; // Modifies general purpose register + bool modifiesFPR; // Modifies floating point register + bool modifiesVFR; // Modifies vector float register + bool modifiesMemory; // Modifies memory + bool modifiesControl; // Modifies control register + } modificationInfo; }; // Function information diff --git a/ps2xRecomp/src/code_generator.cpp b/ps2xRecomp/src/code_generator.cpp index 98b0c73..98e8d15 100644 --- a/ps2xRecomp/src/code_generator.cpp +++ b/ps2xRecomp/src/code_generator.cpp @@ -465,6 +465,11 @@ namespace ps2recomp { return "// NOP (addiu $zero, ...)"; } + if (inst.modificationInfo.modifiesGPR) + { + return fmt::format("ctx->r[{}] = ADD32(ctx->r[{}], 0x{:X}); // Modifies GPR", + inst.rt, inst.rs, (int16_t)inst.immediate); + } return fmt::format("ctx->r[{}] = ADD32(ctx->r[{}], 0x{:X});", inst.rt, inst.rs, (int16_t)inst.immediate); @@ -526,10 +531,20 @@ namespace ps2recomp // PS2-specific 128-bit load/store case OPCODE_LQ: + if (inst.vectorInfo.isVector) + { + return fmt::format("ctx->r[{}] = (__m128i)READ128(ADD32(ctx->r[{}], 0x{:X})); // Vector load", + inst.rt, inst.rs, (int16_t)inst.immediate); + } return fmt::format("ctx->r[{}] = (__m128i)READ128(ADD32(ctx->r[{}], 0x{:X}));", inst.rt, inst.rs, (int16_t)inst.immediate); case OPCODE_SQ: + if (inst.vectorInfo.isVector) + { + return fmt::format("WRITE128(ADD32(ctx->r[{}], 0x{:X}), (__m128i)ctx->r[{}]); // Vector store", + inst.rs, (int16_t)inst.immediate, inst.rt); + } return fmt::format("WRITE128(ADD32(ctx->r[{}], 0x{:X}), (__m128i)ctx->r[{}]);", inst.rs, (int16_t)inst.immediate, inst.rt); @@ -553,6 +568,14 @@ namespace ps2recomp return fmt::format("WRITE128(ADD32(ctx->r[{}], 0x{:X}), (__m128i)ctx->vu0_vf[{}]);", inst.rs, (int16_t)inst.immediate, inst.rt); + case OPCODE_J: + return fmt::format("// J 0x{:X} - Handled by branch logic", + (inst.address & 0xF0000000) | (inst.target << 2)); + + case OPCODE_JAL: + return fmt::format("// JAL 0x{:X} - Handled by branch logic", + (inst.address & 0xF0000000) | (inst.target << 2)); + case OPCODE_LWC1: return fmt::format("{{ uint32_t val = READ32(ADD32(ctx->r[{}], 0x{:X})); ctx->f[{}] = *(float*)&val; }}", inst.rs, (int16_t)inst.immediate, inst.rt); @@ -579,20 +602,17 @@ namespace ps2recomp switch (inst.rt) { case REGIMM_BLTZ: - return fmt::format("// BLTZ r{}, 0x{:X} - Handled by branch logic", - inst.rs, inst.address + 4 + ((int16_t)inst.immediate << 2)); - case REGIMM_BGEZ: - return fmt::format("// BGEZ r{}, 0x{:X} - Handled by branch logic", - inst.rs, inst.address + 4 + ((int16_t)inst.immediate << 2)); - + case REGIMM_BLTZL: + case REGIMM_BGEZL: case REGIMM_BLTZAL: - return fmt::format("// BLTZAL r{}, 0x{:X} - Handled by branch logic", - inst.rs, inst.address + 4 + ((int16_t)inst.immediate << 2)); - case REGIMM_BGEZAL: - return fmt::format("// BGEZAL r{}, 0x{:X} - Handled by branch logic", - inst.rs, inst.address + 4 + ((int16_t)inst.immediate << 2)); + case REGIMM_BLTZALL: + case REGIMM_BGEZALL: + { + uint32_t target = inst.address + 4 + ((int16_t)inst.immediate << 2); + return fmt::format("// REGIMM branch instruction to 0x{:X} - Handled by branch logic", target); + } case REGIMM_MTSAB: return fmt::format("ctx->sa = (ctx->r[{}] + 0x{:X}) & 0x0F;", @@ -631,6 +651,10 @@ namespace ps2recomp } // MIPS-IV special format opcodes + case OPCODE_BEQ: + case OPCODE_BNE: + case OPCODE_BLEZ: + case OPCODE_BGTZ: case OPCODE_BEQL: case OPCODE_BNEL: case OPCODE_BLEZL: @@ -1128,6 +1152,15 @@ namespace ps2recomp switch (inst.sa) { case MMI0_PADDW: + if (inst.vectorInfo.isVector && inst.vectorInfo.vectorField != 0xF) + { + return fmt::format("{{ __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->r[{}] = _mm_blendv_epi8(ctx->r[{}], PS2_PADDW(ctx->r[{}], ctx->r[{}]), mask); }}", + (inst.vectorInfo.vectorField & 0x8) ? 0xFFFFFFFF : 0, + (inst.vectorInfo.vectorField & 0x4) ? 0xFFFFFFFF : 0, + (inst.vectorInfo.vectorField & 0x2) ? 0xFFFFFFFF : 0, + (inst.vectorInfo.vectorField & 0x1) ? 0xFFFFFFFF : 0, + inst.rd, inst.rd, inst.rs, inst.rt); + } return fmt::format("ctx->r[{}] = PS2_PADDW(ctx->r[{}], ctx->r[{}]);", inst.rd, inst.rs, inst.rt); @@ -1192,7 +1225,8 @@ namespace ps2recomp inst.rd, inst.rs, inst.rt); case MMI0_PPACB: - return fmt::format("// PS2_PPACB not implemented"); + return fmt::format("ctx->r[{}] = PS2_PPACB(ctx->r[{}], ctx->r[{}]);", + inst.rd, inst.rs, inst.rt); default: return fmt::format("// Unhandled MMI0 function: 0x{:X}", inst.sa); @@ -1316,6 +1350,36 @@ namespace ps2recomp case MMI2_PHMADH: return translatePHMADH(inst); + case MMI2_PMSUBH: + return fmt::format("{{ __m128i product = _mm_mullo_epi16(ctx->r[{}], ctx->r[{}]); " + "// Convert products to 32-bit\n" + "__m128i prod_lo = _mm_unpacklo_epi16(product, _mm_srai_epi16(product, 15));\n" + "__m128i prod_hi = _mm_unpackhi_epi16(product, _mm_srai_epi16(product, 15));\n" + "// Subtract from accumulator\n" + "__m128i acc = _mm_set_epi32(0, ctx->hi, 0, ctx->lo);\n" + "__m128i result = _mm_sub_epi32(acc, _mm_add_epi32(prod_lo, prod_hi));\n" + "ctx->r[{}] = result;\n" + "ctx->lo = _mm_extract_epi32(result, 0);\n" + "ctx->hi = _mm_extract_epi32(result, 1); }}", + inst.rs, inst.rt, inst.rd); + + case MMI2_PHMSBH: + return fmt::format("{{ // Multiply horizontally adjacent halfwords\n" + "__m128i rtEven = _mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(2, 0, 2, 0));\n" + "__m128i rtOdd = _mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(3, 1, 3, 1));\n" + "__m128i rsEven = _mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(2, 0, 2, 0));\n" + "__m128i rsOdd = _mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(3, 1, 3, 1));\n" + "__m128i prod1 = _mm_mullo_epi16(rtEven, rsEven);\n" + "__m128i prod2 = _mm_mullo_epi16(rtOdd, rsOdd);\n" + "__m128i sum = _mm_add_epi16(prod1, prod2);\n" + "// Convert to 32-bit and subtract from accumulator\n" + "__m128i acc = _mm_set_epi32(0, ctx->hi, 0, ctx->lo);\n" + "__m128i result = _mm_sub_epi32(acc, _mm_unpacklo_epi16(sum, _mm_srai_epi16(sum, 15)));\n" + "ctx->r[{}] = result;\n" + "ctx->lo = _mm_extract_epi32(result, 0);\n" + "ctx->hi = _mm_extract_epi32(result, 1); }}", + inst.rt, inst.rt, inst.rs, inst.rs, inst.rd); + case MMI2_PEXEH: return translatePEXEH(inst); @@ -1352,7 +1416,11 @@ namespace ps2recomp inst.rd, inst.rs, inst.rt); case MMI3_PMADDUW: - return fmt::format("// PS2_PMADDUW - Packed Multiply-Add Unsigned Word"); + return fmt::format("{{ uint64_t result = (uint64_t)(((uint64_t)ctx->hi << 32) | ctx->lo) + " + "(uint64_t)_mm_extract_epi32(ctx->r[{}], 0) * (uint64_t)_mm_extract_epi32(ctx->r[{}], 0); " + "ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); " + "ctx->r[{}] = _mm_set_epi32(0, 0, ctx->hi, ctx->lo); }}", + inst.rs, inst.rt, inst.rd); case MMI3_PSRAVW: return fmt::format("ctx->r[{}] = PS2_PSRAVW(ctx->r[{}], ctx->r[{}]);", @@ -1380,6 +1448,12 @@ namespace ps2recomp case MMI3_PEXCW: return translatePEXCW(inst); + case MMI3_PMTHI: + return translatePMTHI(inst); + + case MMI3_PMTLO: + return translatePMTLO(inst); + default: return fmt::format("// Unhandled MMI3 function: 0x{:X}", inst.sa); } @@ -2258,6 +2332,16 @@ namespace ps2recomp inst.rs, inst.rd); } + std::string CodeGenerator::translatePMTHI(const Instruction &inst) + { + return fmt::format("ctx->hi = _mm_extract_epi32(ctx->r[{}], 0);", inst.rs); + } + + std::string CodeGenerator::translatePMTLO(const Instruction &inst) + { + return fmt::format("ctx->lo = _mm_extract_epi32(ctx->r[{}], 0);", inst.rs); + } + std::string CodeGenerator::generateJumpTableSwitch(const Instruction &inst, uint32_t tableAddress, const std::vector &entries) { diff --git a/ps2xRecomp/src/r5900_decoder.cpp b/ps2xRecomp/src/r5900_decoder.cpp index d24c468..7757e80 100644 --- a/ps2xRecomp/src/r5900_decoder.cpp +++ b/ps2xRecomp/src/r5900_decoder.cpp @@ -34,6 +34,26 @@ namespace ps2recomp inst.isReturn = false; inst.hasDelaySlot = false; inst.isMultimedia = false; + inst.isLoad = false; + inst.isStore = false; + + // Initialize the enhanced fields + inst.mmiType = 0; + inst.mmiFunction = 0; + inst.pmfhlVariation = 0; + inst.vuFunction = 0; + + inst.vectorInfo.isVector = false; + inst.vectorInfo.usesQReg = false; + inst.vectorInfo.usesPReg = false; + inst.vectorInfo.modifiesMAC = false; + inst.vectorInfo.vectorField = 0xF; // All fields (xyzw) + + inst.modificationInfo.modifiesGPR = false; + inst.modificationInfo.modifiesFPR = false; + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesMemory = false; + inst.modificationInfo.modifiesControl = false; switch (inst.opcode) { @@ -86,18 +106,21 @@ namespace ps2recomp decodeMMI(inst); inst.isMMI = true; inst.isMultimedia = true; + inst.modificationInfo.modifiesGPR = true; break; case OPCODE_LQ: decodeIType(inst); inst.isLoad = true; inst.isMultimedia = true; // 128-bit load + inst.modificationInfo.modifiesGPR = true; break; case OPCODE_SQ: decodeIType(inst); inst.isStore = true; inst.isMultimedia = true; // 128-bit store + inst.modificationInfo.modifiesMemory = true; break; case OPCODE_LB: @@ -462,71 +485,341 @@ namespace ps2recomp void R5900Decoder::decodeCOP2(Instruction &inst) const { - // COP2 (VU0 macro mode) instructions inst.isVU = true; inst.isMultimedia = true; - uint32_t rs = inst.rs; // The VU0 format field + uint32_t rs = inst.rs; // The format field - if (rs == COP2_MFC2) + switch (rs) { - // Move From COP2 register + case COP2_QMFC2: // Move From COP2 (128-bit) + case COP2_CFC2: // Move Control From COP2 + case COP2_QMTC2: // Move To COP2 (128-bit) + case COP2_CTC2: // Move Control To COP2 + // Register transfer operations + break; + + case COP2_BC2: // Branch on COP2 condition + { + uint32_t rt = inst.rt; // The condition code + + if (rt == COP2_BCF || rt == COP2_BCT || + rt == COP2_BCFL || rt == COP2_BCTL || + rt == COP2_BCEF || rt == COP2_BCET || + rt == COP2_BCEFL || rt == COP2_BCETL) + { + inst.isBranch = true; + inst.hasDelaySlot = true; + } } - else if (rs == COP2_CFC2) + break; + + case COP2_CO: // VU0 vector operations { - // Move From COP2 Control register + uint32_t function = inst.function; + + switch (function) + { + case VU0_VADD: + case VU0_VSUB: + case VU0_VMUL: + // Basic vector math operations + break; + + case VU0_VDIV: + case VU0_VSQRT: + case VU0_VRSQRT: + // Division and square root operations + break; + + case VU0_VMULQ: + // Multiply by Q register + break; + + case VU0_VIADD: + case VU0_VISUB: + case VU0_VIADDI: + // Integer operations + break; + + case VU0_VIAND: + case VU0_VIOR: + // Logical operations + break; + + case VU0_VILWR: + case VU0_VISWR: + // Load/Store operations + inst.isLoad = (function == VU0_VILWR); + inst.isStore = (function == VU0_VISWR); + break; + + case VU0_VCALLMS: + case VU0_VCALLMSR: + // VU0 microprogram calls + inst.isCall = true; + break; + + case VU0_VRGET: + // Get random number from R register + break; + + default: + // Other VU0 operations + break; + } } - else if (rs == COP2_MTC2) + break; + + case COP2_CTCVU: // Control Transfer to VU0 + case COP2_MTVUCF: // Move To VU Control/Flag register + case COP2_VMTIR: // Move To VU0 I Register + // Special register operations + break; + + case COP2_VU0OPS: // Additional VU0 operations { - // Move To COP2 register + uint32_t function = inst.function; + + switch (function) + { + case VU0OPS_QMFC2_NI: // Non-incrementing QMFC2 + case VU0OPS_QMFC2_I: // Incrementing QMFC2 + case VU0OPS_QMTC2_NI: // Non-incrementing QMTC2 + case VU0OPS_QMTC2_I: // Incrementing QMTC2 + // Extended register transfer operations + break; + + case VU0OPS_VMFIR: // Move From Integer Register + // Move operations + break; + + case VU0OPS_VXITOP: // Execute Interrupt on VU0 + break; + + case VU0OPS_VWAITQ: // Wait for Q register operations + break; + + default: + // Unknown VU0OPS + break; + } } - else if (rs == COP2_CTC2) - { - // Move To COP2 Control register - } - else if (rs == COP2_BCF || rs == COP2_BCT) - { - // VU0 Branch on Condition - inst.isBranch = true; - inst.hasDelaySlot = true; - } - else - { - // VU0 vector operations - // These would need detailed decoding based on function field + break; + + default: + // Unknown COP2 format + break; } } void R5900Decoder::decodeMMI0(Instruction &inst) const { - // Decode MMI0 subfunctions (based on function field) - uint32_t subFunction = inst.function & 0x3F; + uint32_t sa = inst.sa; - // The implementation would set appropriate flags or properties based on the specific MMI0 operation + switch (sa) + { + case MMI0_PADDW: + case MMI0_PSUBW: + case MMI0_PCGTW: + case MMI0_PMAXW: + case MMI0_PADDH: + case MMI0_PSUBH: + case MMI0_PCGTH: + case MMI0_PMAXH: + case MMI0_PADDB: + case MMI0_PSUBB: + case MMI0_PCGTB: + // Arithmetic and comparison operations + break; + + case MMI0_PADDSW: + case MMI0_PSUBSW: + case MMI0_PADDSH: + case MMI0_PSUBSH: + case MMI0_PADDSB: + case MMI0_PSUBSB: + // Saturated arithmetic operations + break; + + case MMI0_PEXTLW: + case MMI0_PPACW: + case MMI0_PEXTLH: + case MMI0_PPACH: + case MMI0_PEXTLB: + case MMI0_PPACB: + case MMI0_PEXT5: + case MMI0_PPAC5: + // Data packing/unpacking operations + break; + + default: + // Unknown MMI0 operation + break; + } } void R5900Decoder::decodeMMI1(Instruction &inst) const { - // Decode MMI1 subfunctions (based on function field) uint32_t subFunction = inst.function & 0x3F; - // The implementation would set appropriate flags or properties based on the specific MMI1 operation + uint32_t sa = inst.sa; + + switch (sa) + { + case MMI1_PABSW: + case MMI1_PABSH: + // Absolute value operations + break; + + case MMI1_PCEQW: + case MMI1_PCEQH: + case MMI1_PCEQB: + // Equality comparison operations + break; + + case MMI1_PMINW: + case MMI1_PMINH: + // Minimum value operations + break; + + case MMI1_PADDUW: + case MMI1_PSUBUW: + case MMI1_PEXTUW: + case MMI1_PADDUH: + case MMI1_PSUBUH: + case MMI1_PEXTUH: + case MMI1_PADDUB: + case MMI1_PSUBUB: + case MMI1_PEXTUB: + // Unsigned arithmetic and extension operations + break; + + case MMI1_QFSRV: + // Quadword funnel shift right variable + inst.isMultimedia = true; + break; + + default: + // Unknown MMI1 operation + break; + } } void R5900Decoder::decodeMMI2(Instruction &inst) const { - // Decode MMI2 subfunctions (based on function field) - uint32_t subFunction = inst.function & 0x3F; + uint32_t sa = inst.sa; - // The implementation would set appropriate flags or properties based on the specific MMI2 operation + switch (sa) + { + case MMI2_PMADDW: + case MMI2_PMSUBW: + case MMI2_PMADDH: + case MMI2_PHMADH: + case MMI2_PMSUBH: + case MMI2_PHMSBH: + case MMI2_PMULTH: + // Multiply/multiply-add operations + inst.isMultimedia = true; + break; + + case MMI2_PSLLVW: + case MMI2_PSRLVW: + // Variable shift operations + break; + + case MMI2_PMFHI: + case MMI2_PMFLO: + // Move from HI/LO registers + break; + + case MMI2_PINTH: + // Interleave half words + break; + + case MMI2_PMULTW: + case MMI2_PDIVW: + case MMI2_PDIVBW: + // Multiply/divide operations + inst.isMultimedia = true; + break; + + case MMI2_PCPYLD: + // Copy lower doubleword + break; + + case MMI2_PAND: + case MMI2_PXOR: + // Logical operations + break; + + case MMI2_PEXEH: + case MMI2_PREVH: + case MMI2_PEXEW: + case MMI2_PROT3W: + // Data permutation operations + break; + + default: + // Unknown MMI2 operation + break; + } } void R5900Decoder::decodeMMI3(Instruction &inst) const { - // Decode MMI3 subfunctions (based on function field) - uint32_t subFunction = inst.function & 0x3F; + uint32_t sa = inst.sa; - // The implementation would set appropriate flags or properties based on the specific MMI3 operation + switch (sa) + { + case MMI3_PMADDUW: + // Unsigned multiply-add + inst.isMultimedia = true; + break; + + case MMI3_PSRAVW: + // Packed shift right arithmetic variable word + break; + + case MMI3_PMTHI: + inst.mmiFunction = MMI3_PMTHI; + inst.modificationInfo.modifiesControl = true; // HI register is modified + break; + + case MMI3_PMTLO: + inst.mmiFunction = MMI3_PMTLO; + inst.modificationInfo.modifiesControl = true; // LO register is modified + break; + + case MMI3_PINTEH: + // Interleave even halfwords + break; + + case MMI3_PMULTUW: + case MMI3_PDIVUW: + // Unsigned multiply/divide operations + inst.isMultimedia = true; + break; + + case MMI3_PCPYUD: + // Copy upper doubleword + break; + + case MMI3_POR: + case MMI3_PNOR: + // Logical operations + break; + + case MMI3_PEXCH: + case MMI3_PCPYH: + case MMI3_PEXCW: + // Data permutation operations + break; + + default: + // Unknown MMI3 operation + break; + } } void R5900Decoder::decodePMFHL(Instruction &inst) const