mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-10-01 10:26:07 -04:00
4961710407
* Add missing VU0 Special1 macro translations * Fix VU0 macro decode and field ops Align Special2 decoding with fhi_flo, implement VCLIPw/VABS, and fix field variants (VADDx/VSUBx/VMULx). Keep C++17 compatibility by replacing unordered_set::contains and remove dead ADDIU return. Update Special2 metadata for VWAITQ. * vu0: complete macro mappings and add coverage test * review: clean up vu macro handling * fix: restore C++20 contains in code generator * fix: correct VU0 Special2 instruction encoding in decoder test --------- Co-authored-by: Antonio Guastella <antonioguastella@energine.it> Co-authored-by: Ranieri <ran-junior@hotmail.com>
1001 lines
30 KiB
C++
1001 lines
30 KiB
C++
#include "ps2recomp/r5900_decoder.h"
|
|
#include "rabbitizer.h"
|
|
#include <iostream>
|
|
|
|
namespace ps2recomp
|
|
{
|
|
|
|
R5900Decoder::R5900Decoder()
|
|
{
|
|
}
|
|
|
|
R5900Decoder::~R5900Decoder()
|
|
{
|
|
}
|
|
|
|
Instruction R5900Decoder::decodeInstruction(uint32_t address, uint32_t rawInstruction) const
|
|
{
|
|
Instruction inst;
|
|
|
|
inst.address = address;
|
|
inst.raw = rawInstruction;
|
|
inst.opcode = OPCODE(rawInstruction);
|
|
inst.rs = RS(rawInstruction);
|
|
inst.rt = RT(rawInstruction);
|
|
inst.rd = RD(rawInstruction);
|
|
inst.sa = SA(rawInstruction);
|
|
inst.function = FUNCTION(rawInstruction);
|
|
inst.immediate = IMMEDIATE(rawInstruction);
|
|
inst.simmediate = SIMMEDIATE(rawInstruction);
|
|
inst.target = TARGET(rawInstruction);
|
|
|
|
inst.isMMI = false;
|
|
inst.isVU = false;
|
|
inst.isBranch = false;
|
|
inst.isJump = false;
|
|
inst.isCall = false;
|
|
inst.isReturn = false;
|
|
inst.hasDelaySlot = false;
|
|
inst.isMultimedia = false;
|
|
inst.isLoad = false;
|
|
inst.isStore = false;
|
|
inst.isMmio = false;
|
|
|
|
// Initialize the enhanced fields
|
|
inst.mmiType = 0;
|
|
inst.mmiFunction = 0;
|
|
inst.pmfhlVariation = 0;
|
|
inst.vuFunction = 0;
|
|
inst.mmioAddress = 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.modifiesVIR = false;
|
|
inst.modificationInfo.modifiesVIC = false;
|
|
inst.modificationInfo.modifiesMemory = false;
|
|
inst.modificationInfo.modifiesControl = false;
|
|
|
|
RabbitizerInstruction rabbitizerInst;
|
|
RabbitizerInstructionR5900_init(&rabbitizerInst, rawInstruction, address);
|
|
RabbitizerInstructionR5900_processUniqueId(&rabbitizerInst);
|
|
|
|
const RabbitizerInstrDescriptor *desc = rabbitizerInst.descriptor;
|
|
|
|
inst.isBranch = RabbitizerInstrDescriptor_isBranch(desc);
|
|
inst.isJump = RabbitizerInstrDescriptor_isJump(desc);
|
|
inst.isCall = RabbitizerInstruction_isFunctionCall(&rabbitizerInst);
|
|
inst.isReturn = RabbitizerInstruction_isReturn(&rabbitizerInst) ||
|
|
rabbitizerInst.uniqueId == RABBITIZER_INSTR_ID_cpu_eret;
|
|
inst.hasDelaySlot = RabbitizerInstruction_hasDelaySlot(&rabbitizerInst);
|
|
|
|
inst.isLoad = RabbitizerInstrDescriptor_doesLoad(desc);
|
|
inst.isStore = RabbitizerInstrDescriptor_doesStore(desc);
|
|
|
|
inst.isMMI = inst.opcode == OPCODE_MMI;
|
|
inst.isVU = inst.opcode == OPCODE_COP2 ||
|
|
inst.opcode == OPCODE_LWC2 ||
|
|
inst.opcode == OPCODE_LDC2 ||
|
|
inst.opcode == OPCODE_SWC2 ||
|
|
inst.opcode == OPCODE_SDC2;
|
|
|
|
bool modifiesGpr = false;
|
|
if (RabbitizerInstrDescriptor_modifiesRs(desc) && inst.rs != 0)
|
|
{
|
|
modifiesGpr = true;
|
|
}
|
|
if (RabbitizerInstrDescriptor_modifiesRt(desc) && inst.rt != 0)
|
|
{
|
|
modifiesGpr = true;
|
|
}
|
|
if (RabbitizerInstrDescriptor_modifiesRd(desc) && inst.rd != 0)
|
|
{
|
|
modifiesGpr = true;
|
|
}
|
|
if (RabbitizerInstrDescriptor_doesLink(desc))
|
|
{
|
|
modifiesGpr = true;
|
|
}
|
|
inst.modificationInfo.modifiesGPR = modifiesGpr;
|
|
|
|
inst.modificationInfo.modifiesFPR = RabbitizerInstrDescriptor_modifiesFs(desc) ||
|
|
RabbitizerInstrDescriptor_modifiesFt(desc) ||
|
|
RabbitizerInstrDescriptor_modifiesFd(desc);
|
|
|
|
inst.modificationInfo.modifiesMemory = RabbitizerInstrDescriptor_doesStore(desc);
|
|
inst.modificationInfo.modifiesControl = RabbitizerInstrDescriptor_isBranch(desc) ||
|
|
RabbitizerInstrDescriptor_isJump(desc) ||
|
|
RabbitizerInstrDescriptor_isTrap(desc) ||
|
|
RabbitizerInstrDescriptor_modifiesHI(desc) ||
|
|
RabbitizerInstrDescriptor_modifiesLO(desc);
|
|
|
|
if (rabbitizerInst.uniqueId == RABBITIZER_INSTR_ID_cpu_eret)
|
|
{
|
|
inst.isReturn = true;
|
|
inst.hasDelaySlot = false;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
}
|
|
|
|
if (inst.opcode == OPCODE_LL || inst.opcode == OPCODE_LLD)
|
|
{
|
|
inst.isLoad = true;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
if (inst.rt != 0)
|
|
{
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
}
|
|
}
|
|
|
|
if (inst.opcode == OPCODE_SC || inst.opcode == OPCODE_SCD)
|
|
{
|
|
inst.isStore = true;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
if (inst.rt != 0)
|
|
{
|
|
inst.modificationInfo.modifiesGPR = true; // success flag to rt
|
|
}
|
|
}
|
|
|
|
if (inst.opcode == OPCODE_COP2)
|
|
{
|
|
decodeCOP2(inst);
|
|
}
|
|
else if (inst.opcode == OPCODE_LWC2 || inst.opcode == OPCODE_LDC2)
|
|
{
|
|
inst.isVU = true;
|
|
inst.modificationInfo.modifiesVFR = true;
|
|
}
|
|
else if (inst.opcode == OPCODE_SWC2 || inst.opcode == OPCODE_SDC2)
|
|
{
|
|
inst.isVU = true;
|
|
inst.modificationInfo.modifiesMemory = true;
|
|
}
|
|
|
|
if (inst.opcode == OPCODE_LQ || inst.opcode == OPCODE_SQ)
|
|
{
|
|
inst.isMultimedia = true;
|
|
}
|
|
|
|
if (inst.isMMI || inst.isVU)
|
|
{
|
|
inst.isMultimedia = true;
|
|
inst.vectorInfo.isVector = inst.isVU; // Only VU ops are truly vector
|
|
}
|
|
|
|
RabbitizerInstructionR5900_destroy(&rabbitizerInst);
|
|
|
|
return inst;
|
|
}
|
|
|
|
void R5900Decoder::decodeRType(Instruction &inst) const
|
|
{
|
|
// R-type instructions already have all fields set correctly
|
|
if (inst.rd != 0)
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
}
|
|
|
|
void R5900Decoder::decodeIType(Instruction &inst) const
|
|
{
|
|
// I-type instructions already have all fields set correctly
|
|
if (!inst.isStore && !inst.isBranch && inst.rt != 0)
|
|
{
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodeJType(Instruction &inst) const
|
|
{
|
|
// J-type instructions already have all fields set correctly
|
|
inst.isJump = true;
|
|
inst.hasDelaySlot = true;
|
|
inst.modificationInfo.modifiesControl = true; // PC
|
|
if (inst.opcode == OPCODE_JAL)
|
|
{
|
|
inst.isCall = true;
|
|
inst.modificationInfo.modifiesGPR = true; // $ra (r[31])
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodeSpecial(Instruction &inst) const
|
|
{
|
|
if (inst.rd != 0)
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
|
|
switch (inst.function)
|
|
{
|
|
case SPECIAL_JR:
|
|
inst.isJump = true;
|
|
inst.hasDelaySlot = true;
|
|
inst.modificationInfo.modifiesGPR = false; // Doesn't modify GPR itself
|
|
inst.modificationInfo.modifiesControl = true; // PC
|
|
if (inst.rs == 31)
|
|
{
|
|
// jr $ra is typically a return
|
|
inst.isReturn = true;
|
|
}
|
|
break;
|
|
|
|
case SPECIAL_JALR:
|
|
inst.isJump = true;
|
|
inst.isCall = true;
|
|
inst.hasDelaySlot = true;
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false; // JALR $zero, $rs is like JR $rs
|
|
else
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
inst.modificationInfo.modifiesControl = true; // PC
|
|
break;
|
|
|
|
case SPECIAL_SYSCALL:
|
|
case SPECIAL_BREAK:
|
|
// Special handling for syscall/break
|
|
inst.modificationInfo.modifiesGPR = false; // No GPR change
|
|
inst.modificationInfo.modifiesControl = true; // Changes control flow, potentially registers via handler
|
|
break;
|
|
|
|
case SPECIAL_MFHI:
|
|
case SPECIAL_MFLO:
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
case SPECIAL_MTHI:
|
|
case SPECIAL_MTLO:
|
|
// HI/LO register operations
|
|
inst.modificationInfo.modifiesGPR = false; // Doesn't modify rd
|
|
inst.modificationInfo.modifiesControl = true; // HI/LO
|
|
break;
|
|
|
|
case SPECIAL_MULT:
|
|
case SPECIAL_MULTU:
|
|
case SPECIAL_DIV:
|
|
case SPECIAL_DIVU:
|
|
// Multiplication and division operations
|
|
inst.modificationInfo.modifiesGPR = false; // Doesn't modify rd
|
|
inst.modificationInfo.modifiesControl = true; // HI/LO
|
|
break;
|
|
|
|
case SPECIAL_ADD:
|
|
case SPECIAL_ADDU:
|
|
case SPECIAL_SUB:
|
|
case SPECIAL_SUBU:
|
|
case SPECIAL_AND:
|
|
case SPECIAL_OR:
|
|
case SPECIAL_XOR:
|
|
case SPECIAL_NOR:
|
|
// ALU operations
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
|
|
case SPECIAL_SLL:
|
|
case SPECIAL_SRL:
|
|
case SPECIAL_SRA:
|
|
case SPECIAL_SLLV:
|
|
case SPECIAL_SRLV:
|
|
case SPECIAL_SRAV:
|
|
// Shift operations
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
|
|
// 64-bit specific operations
|
|
case SPECIAL_DADD:
|
|
case SPECIAL_DADDU:
|
|
case SPECIAL_DSUB:
|
|
case SPECIAL_DSUBU:
|
|
case SPECIAL_DSLL:
|
|
case SPECIAL_DSRL:
|
|
case SPECIAL_DSRA:
|
|
case SPECIAL_DSLL32:
|
|
case SPECIAL_DSRL32:
|
|
case SPECIAL_DSRA32:
|
|
case SPECIAL_DSLLV:
|
|
case SPECIAL_DSRLV:
|
|
case SPECIAL_DSRAV:
|
|
// 64-bit operations
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
|
|
case SPECIAL_MOVZ:
|
|
case SPECIAL_MOVN:
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
|
|
// Set on Less Than
|
|
case SPECIAL_SLT:
|
|
case SPECIAL_SLTU:
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
|
|
case SPECIAL_TGE:
|
|
case SPECIAL_TGEU:
|
|
case SPECIAL_TLT:
|
|
case SPECIAL_TLTU:
|
|
case SPECIAL_TEQ:
|
|
case SPECIAL_TNE:
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
inst.modificationInfo.modifiesControl = true; // Control flow change via trap
|
|
break;
|
|
|
|
// PS2 Specific (MFSA/MTSA)
|
|
case SPECIAL_MFSA: // Modifies rd GPR
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
break;
|
|
case SPECIAL_MTSA: // Modifies SA control register
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
inst.modificationInfo.modifiesControl = true; // SA reg
|
|
break;
|
|
|
|
case SPECIAL_SYNC:
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodeRegimm(Instruction &inst) const
|
|
{
|
|
uint32_t rt = inst.rt;
|
|
|
|
switch (rt)
|
|
{
|
|
case REGIMM_BLTZ:
|
|
case REGIMM_BGEZ:
|
|
case REGIMM_BLTZL:
|
|
case REGIMM_BGEZL:
|
|
// Branches
|
|
inst.isBranch = true;
|
|
inst.hasDelaySlot = true;
|
|
inst.modificationInfo.modifiesControl = true; // PC potentially
|
|
break;
|
|
|
|
case REGIMM_BLTZAL:
|
|
case REGIMM_BGEZAL:
|
|
case REGIMM_BLTZALL:
|
|
case REGIMM_BGEZALL:
|
|
// Branch and Link
|
|
inst.isBranch = true;
|
|
inst.isCall = true;
|
|
inst.hasDelaySlot = true;
|
|
inst.modificationInfo.modifiesGPR = true; // $ra (r[31])
|
|
inst.modificationInfo.modifiesControl = true; // PC
|
|
break;
|
|
|
|
case REGIMM_TGEI:
|
|
case REGIMM_TGEIU:
|
|
case REGIMM_TLTI:
|
|
case REGIMM_TLTIU:
|
|
case REGIMM_TEQI:
|
|
case REGIMM_TNEI:
|
|
// Trap Instructions
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break;
|
|
|
|
case REGIMM_MTSAB:
|
|
case REGIMM_MTSAH:
|
|
// PS2 specific MTSAB/MTSAH instructions (for QMFC2/QMTC2)
|
|
inst.isMultimedia = true;
|
|
inst.modificationInfo.modifiesControl = true; // SA register
|
|
break;
|
|
|
|
default:
|
|
// Unknown REGIMM instructions
|
|
std::cerr << "Unknown REGIMM instruction: " << std::hex << inst.raw << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodeMMI(Instruction &inst) const
|
|
{
|
|
inst.isMMI = true;
|
|
inst.isMultimedia = true;
|
|
inst.modificationInfo.modifiesGPR = true; // Most MMI ops write to rd GPR
|
|
if (inst.rd == 0)
|
|
inst.modificationInfo.modifiesGPR = false; // Except if rd is $zero
|
|
|
|
// The function field is actually determined by the lowest 6 bits (as in R-type)
|
|
uint32_t mmiFunction = inst.function;
|
|
|
|
uint32_t rs = inst.rs;
|
|
|
|
if (mmiFunction == MMI_MMI0)
|
|
{
|
|
decodeMMI0(inst);
|
|
return;
|
|
}
|
|
if (mmiFunction == MMI_MMI1)
|
|
{
|
|
decodeMMI1(inst);
|
|
return;
|
|
}
|
|
if (mmiFunction == MMI_MMI2)
|
|
{
|
|
decodeMMI2(inst);
|
|
return;
|
|
}
|
|
if (mmiFunction == MMI_MMI3)
|
|
{
|
|
decodeMMI3(inst);
|
|
return;
|
|
}
|
|
|
|
switch (mmiFunction)
|
|
{
|
|
case MMI_PLZCW:
|
|
// Parallel leading zero/one count word
|
|
break;
|
|
case MMI_MFHI1:
|
|
case MMI_MFLO1:
|
|
// Move from HI1/LO1 -> rd
|
|
if (inst.rd == 0)
|
|
{
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
}
|
|
inst.modificationInfo.modifiesControl = false;
|
|
break;
|
|
case MMI_MTHI1:
|
|
case MMI_MTLO1:
|
|
// Move to HI1/LO1
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break;
|
|
case MMI_PSLLH:
|
|
case MMI_PSRLH:
|
|
case MMI_PSRAH:
|
|
case MMI_PSLLW:
|
|
case MMI_PSRLW:
|
|
case MMI_PSRAW:
|
|
// Packed shift instructions; defaults already mark GPR modify (unless rd==0)
|
|
break;
|
|
|
|
// HI/LO or HI1/LO1 producers/consumers
|
|
case MMI_MADD:
|
|
case MMI_MADDU:
|
|
case MMI_MSUB:
|
|
case MMI_MSUBU:
|
|
case MMI_MADD1:
|
|
case MMI_MADDU1:
|
|
case MMI_MULT1:
|
|
case MMI_MULTU1:
|
|
case MMI_DIV1:
|
|
case MMI_DIVU1:
|
|
inst.modificationInfo.modifiesGPR = false; // Writes to HI/LO or HI1/LO1
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break;
|
|
case MMI_PMTHL:
|
|
case MMI3_PMTHI:
|
|
case MMI3_PMTLO:
|
|
inst.modificationInfo.modifiesGPR = false; // Writes to HI/LO or HI1/LO1
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break;
|
|
case MMI_PMFHL:
|
|
decodePMFHL(inst);
|
|
break;
|
|
default:
|
|
// Unknown or unsupported MMI function
|
|
std::cerr << "Unknown MMI function: " << std::hex << mmiFunction << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodeMMI0(Instruction &inst) const
|
|
{
|
|
inst.mmiType = 0;
|
|
inst.mmiFunction = inst.sa;
|
|
|
|
uint32_t sa = inst.sa;
|
|
|
|
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
|
|
{
|
|
inst.mmiType = 1;
|
|
inst.mmiFunction = inst.sa;
|
|
|
|
uint32_t subFunction = inst.function & 0x3F;
|
|
|
|
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
|
|
{
|
|
inst.mmiType = 2;
|
|
inst.mmiFunction = inst.sa;
|
|
|
|
if (inst.sa == MMI2_PMADDW || inst.sa == MMI2_PMSUBW ||
|
|
inst.sa == MMI2_PMULTW || inst.sa == MMI2_PDIVW || inst.sa == MMI2_PDIVBW ||
|
|
inst.sa == MMI2_PMADDH || inst.sa == MMI2_PHMADH ||
|
|
inst.sa == MMI2_PMSUBH || inst.sa == MMI2_PHMSBH || inst.sa == MMI2_PMULTH)
|
|
{
|
|
inst.modificationInfo.modifiesControl = true; // HI/LO
|
|
}
|
|
|
|
uint32_t sa = inst.sa;
|
|
|
|
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
|
|
{
|
|
inst.mmiType = 3;
|
|
inst.mmiFunction = inst.sa;
|
|
|
|
uint32_t sa = inst.sa;
|
|
|
|
switch (sa)
|
|
{
|
|
case MMI3_PMADDUW:
|
|
// Unsigned multiply-add
|
|
inst.isMultimedia = true;
|
|
inst.modificationInfo.modifiesControl = true; // HI/LO
|
|
break;
|
|
|
|
case MMI3_PSRAVW:
|
|
// Packed shift right arithmetic variable word
|
|
break;
|
|
|
|
case MMI3_PMTHI:
|
|
inst.mmiFunction = MMI3_PMTHI;
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
inst.modificationInfo.modifiesControl = true; // HI register is modified
|
|
break;
|
|
|
|
case MMI3_PMTLO:
|
|
inst.mmiFunction = MMI3_PMTLO;
|
|
inst.modificationInfo.modifiesGPR = false;
|
|
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;
|
|
inst.modificationInfo.modifiesControl = true; // HI/LO
|
|
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::decodeCOP0(Instruction &inst) const
|
|
{
|
|
// COP0 (System Control) instructions
|
|
uint8_t format = inst.rs;
|
|
inst.modificationInfo.modifiesControl = true; // Assume COP0 always modifies some control state
|
|
if (format == COP0_MF && inst.rt != 0)
|
|
{
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
}
|
|
else if (format == COP0_CO && inst.function == COP0_CO_ERET)
|
|
{
|
|
inst.isReturn = true;
|
|
inst.hasDelaySlot = false;
|
|
} // ERET is special
|
|
else if (format == COP0_BC)
|
|
{
|
|
inst.isBranch = true;
|
|
inst.hasDelaySlot = true;
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodeCOP1(Instruction &inst) const
|
|
{
|
|
uint8_t format = inst.rs;
|
|
if (format == COP1_MF || format == COP1_CF)
|
|
{
|
|
if (inst.rt != 0)
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
}
|
|
else if (format == COP1_BC)
|
|
{
|
|
inst.isBranch = true;
|
|
inst.hasDelaySlot = true;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
}
|
|
else if (format == COP1_S || format == COP1_W || format == COP1_L)
|
|
{
|
|
inst.modificationInfo.modifiesFPR = true;
|
|
}
|
|
if (format == COP1_CT || (format == COP1_S && inst.function >= COP1_S_C_F))
|
|
{
|
|
inst.modificationInfo.modifiesControl = true;
|
|
} // FCR31
|
|
}
|
|
|
|
void R5900Decoder::decodeCOP2(Instruction &inst) const
|
|
{
|
|
uint8_t format = inst.rs;
|
|
inst.isVU = true;
|
|
inst.isMultimedia = true;
|
|
|
|
switch (format)
|
|
{
|
|
case COP2_QMFC2:
|
|
case COP2_CFC2:
|
|
if (inst.rt != 0)
|
|
inst.modificationInfo.modifiesGPR = true;
|
|
break;
|
|
case COP2_QMTC2:
|
|
inst.modificationInfo.modifiesVFR = true;
|
|
break; // Modifies VU state
|
|
case COP2_CTC2:
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break; // Modifies VU control state
|
|
case COP2_BC:
|
|
inst.isBranch = true;
|
|
inst.hasDelaySlot = true;
|
|
inst.modificationInfo.modifiesControl = true;
|
|
break;
|
|
case COP2_CO: // VU Macro instructions (format >= 0x10)
|
|
case COP2_CO + 1:
|
|
case COP2_CO + 2:
|
|
case COP2_CO + 3:
|
|
case COP2_CO + 4:
|
|
case COP2_CO + 5:
|
|
case COP2_CO + 6:
|
|
case COP2_CO + 7:
|
|
case COP2_CO + 8:
|
|
case COP2_CO + 9:
|
|
case COP2_CO + 10:
|
|
case COP2_CO + 11:
|
|
case COP2_CO + 12:
|
|
case COP2_CO + 13:
|
|
case COP2_CO + 14:
|
|
case COP2_CO + 15:
|
|
{ // Refine based on specific VU function
|
|
uint8_t vu_func = inst.function;
|
|
bool is_special2 = vu_func >= 0x3C;
|
|
uint8_t vu_fhi_flo = 0;
|
|
|
|
if (is_special2)
|
|
{
|
|
vu_fhi_flo = static_cast<uint8_t>((((inst.raw >> 6) & 0x1F) << 2) | (inst.raw & 0x3));
|
|
}
|
|
|
|
if (is_special2 && (vu_fhi_flo == VU0_S2_VDIV || vu_fhi_flo == VU0_S2_VSQRT || vu_fhi_flo == VU0_S2_VRSQRT))
|
|
{
|
|
inst.vectorInfo.fsf = (inst.raw >> 10) & 0x3; // Extract bits 10-11
|
|
inst.vectorInfo.ftf = (inst.raw >> 8) & 0x3; // Extract bits 8-9
|
|
}
|
|
|
|
inst.vectorInfo.vectorField = (inst.raw >> 21) & 0xF;
|
|
|
|
inst.modificationInfo.modifiesVFR = true; // Default: Modifies Vector Float Reg
|
|
inst.modificationInfo.modifiesControl = true; // Default: Modifies Flags/Special Regs (Q, P, I, MAC, Clip...)
|
|
|
|
if (is_special2) // Special2 Table
|
|
{
|
|
switch (vu_fhi_flo)
|
|
{
|
|
case VU0_S2_VDIV:
|
|
case VU0_S2_VSQRT:
|
|
case VU0_S2_VRSQRT:
|
|
inst.vectorInfo.usesQReg = true;
|
|
break;
|
|
case VU0_S2_VMTIR:
|
|
inst.modificationInfo.modifiesVFR = false;
|
|
inst.modificationInfo.modifiesVIC = true;
|
|
break;
|
|
case VU0_S2_VMFIR:
|
|
inst.modificationInfo.modifiesVIR = false;
|
|
break; // Reads VI, writes VF
|
|
case VU0_S2_VILWR:
|
|
inst.modificationInfo.modifiesVFR = false;
|
|
inst.modificationInfo.modifiesVIR = true;
|
|
inst.isLoad = true;
|
|
break;
|
|
case VU0_S2_VISWR:
|
|
inst.modificationInfo.modifiesVFR = false;
|
|
inst.modificationInfo.modifiesVIR = false;
|
|
inst.isStore = true;
|
|
inst.modificationInfo.modifiesMemory = true;
|
|
break;
|
|
|
|
case VU0_S2_VRINIT:
|
|
case VU0_S2_VRXOR:
|
|
inst.modificationInfo.modifiesVFR = false; /* Modifies R */
|
|
break; // Modifies R
|
|
case VU0_S2_VRGET:
|
|
inst.modificationInfo.modifiesControl = false; /* Reads R, writes VF */
|
|
break;
|
|
case VU0_S2_VRNEXT:
|
|
inst.modificationInfo.modifiesControl = true; /* Modifies R */
|
|
inst.modificationInfo.modifiesVFR = false;
|
|
break; // Writes R
|
|
case VU0_S2_VWAITQ:
|
|
inst.modificationInfo.modifiesVFR = false;
|
|
inst.modificationInfo.modifiesControl = false;
|
|
break;
|
|
case VU0_S2_VABS:
|
|
case VU0_S2_VMOVE:
|
|
case VU0_S2_VMR32:
|
|
inst.modificationInfo.modifiesControl = false;
|
|
break; // Only VF
|
|
case VU0_S2_VNOP:
|
|
inst.modificationInfo.modifiesVFR = false;
|
|
inst.modificationInfo.modifiesControl = false;
|
|
break;
|
|
case VU0_S2_VCLIPw:
|
|
inst.modificationInfo.modifiesVFR = false; /* Modifies Clip flags */
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void R5900Decoder::decodePMFHL(Instruction &inst) const
|
|
{
|
|
uint32_t saField = inst.sa;
|
|
|
|
switch (saField)
|
|
{
|
|
case PMFHL_LW:
|
|
inst.pmfhlVariation = PMFHL_LW;
|
|
break;
|
|
case PMFHL_UW:
|
|
inst.pmfhlVariation = PMFHL_UW;
|
|
break;
|
|
case PMFHL_SLW:
|
|
inst.pmfhlVariation = PMFHL_SLW;
|
|
break;
|
|
case PMFHL_LH:
|
|
inst.pmfhlVariation = PMFHL_LH;
|
|
break;
|
|
case PMFHL_SH:
|
|
inst.pmfhlVariation = PMFHL_SH;
|
|
break;
|
|
default:
|
|
inst.pmfhlVariation = 0xFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool R5900Decoder::isBranchInstruction(const Instruction &inst) const
|
|
{
|
|
return inst.isBranch;
|
|
}
|
|
|
|
bool R5900Decoder::isJumpInstruction(const Instruction &inst) const
|
|
{
|
|
return inst.isJump;
|
|
}
|
|
|
|
bool R5900Decoder::isCallInstruction(const Instruction &inst) const
|
|
{
|
|
return inst.isCall;
|
|
}
|
|
|
|
bool R5900Decoder::isReturnInstruction(const Instruction &inst) const
|
|
{
|
|
return inst.isReturn;
|
|
}
|
|
|
|
bool R5900Decoder::isMMIInstruction(const Instruction &inst) const
|
|
{
|
|
return inst.isMMI;
|
|
}
|
|
|
|
bool R5900Decoder::isVUInstruction(const Instruction &inst) const
|
|
{
|
|
return inst.isVU;
|
|
}
|
|
|
|
bool R5900Decoder::isStore(const Instruction &inst) const
|
|
{
|
|
return inst.isStore;
|
|
}
|
|
|
|
bool R5900Decoder::isLoad(const Instruction &inst) const
|
|
{
|
|
return inst.isLoad;
|
|
}
|
|
|
|
bool R5900Decoder::hasDelaySlot(const Instruction &inst) const
|
|
{
|
|
return inst.hasDelaySlot;
|
|
}
|
|
|
|
uint32_t R5900Decoder::getBranchTarget(const Instruction &inst) const
|
|
{
|
|
if (!inst.isBranch)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int32_t offset = inst.simmediate << 2;
|
|
return inst.address + 4 + offset;
|
|
}
|
|
|
|
uint32_t R5900Decoder::getJumpTarget(const Instruction &inst) const
|
|
{
|
|
if (!inst.isJump)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (inst.opcode == OPCODE_SPECIAL &&
|
|
(inst.function == SPECIAL_JR || inst.function == SPECIAL_JALR))
|
|
{
|
|
// JR/JALR: target is in the rs register (can't be determined statically)
|
|
return 0;
|
|
}
|
|
|
|
if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL)
|
|
{
|
|
// J/JAL: target is in the lower 26 bits, shifted left by 2
|
|
// and combined with the upper 4 bits of PC + 4
|
|
uint32_t pc_upper = (inst.address + 4) & 0xF0000000;
|
|
return pc_upper | (inst.target << 2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace ps2recomp
|