From 70f672cfd8626e460dbe6e85d83b5b70d70b1de0 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 24 Apr 2025 12:04:56 -0300 Subject: [PATCH] feat: better decode and code gen (remove some old instruction) --- ps2xRecomp/include/ps2recomp/code_generator.h | 64 +- ps2xRecomp/include/ps2recomp/instructions.h | 874 +++-- ps2xRecomp/include/ps2recomp/types.h | 31 +- ps2xRecomp/src/code_generator.cpp | 3069 +++++++---------- ps2xRecomp/src/r5900_decoder.cpp | 697 ++-- ps2xRuntime/include/ps2_runtime.h | 2 +- 6 files changed, 2182 insertions(+), 2555 deletions(-) diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 2fe861c..7dc5114 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -28,48 +28,66 @@ namespace ps2recomp std::string translateVUInstruction(const Instruction &inst); std::string translateFPUInstruction(const Instruction &inst); std::string translateCOP0Instruction(const Instruction &inst); + std::string translateRegimmInstruction(const Instruction &inst); std::string translateSpecialInstruction(const Instruction &inst); - // MMI instruction translations + // MMI Translation functions + std::string translateMMI0Instruction(const Instruction &inst); + std::string translateMMI1Instruction(const Instruction &inst); + std::string translateMMI2Instruction(const Instruction &inst); + std::string translateMMI3Instruction(const Instruction &inst); + std::string translatePMFHLInstruction(const Instruction &inst); + std::string translatePMTHLInstruction(const Instruction &inst); + + // Instruction Helpers std::string translateQFSRV(const Instruction &inst); - std::string translatePCPYLD(const Instruction &inst); - std::string translatePCPYUD(const Instruction &inst); std::string translatePMADDW(const Instruction &inst); + std::string translatePDIVW(const Instruction &inst); + std::string translatePCPYLD(const Instruction &inst); std::string translatePMADDH(const Instruction &inst); std::string translatePHMADH(const Instruction &inst); - std::string translatePMULTH(const Instruction &inst); - std::string translatePDIVW(const Instruction &inst); - std::string translatePDIVBW(const Instruction &inst); std::string translatePEXEH(const Instruction &inst); std::string translatePREVH(const Instruction &inst); + std::string translatePMULTH(const Instruction &inst); + std::string translatePDIVBW(const Instruction &inst); std::string translatePEXEW(const Instruction &inst); std::string translatePROT3W(const Instruction &inst); std::string translatePMULTUW(const Instruction &inst); std::string translatePDIVUW(const Instruction &inst); + std::string translatePCPYUD(const Instruction &inst); 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); - std::string translateEI(const Instruction &inst); - std::string translateDI(const Instruction &inst); - std::string translateDSLL(const Instruction &inst); - std::string translateDSRL(const Instruction &inst); - std::string translateDSRA(const Instruction &inst); - std::string translateDSLLV(const Instruction &inst); - std::string translateDSRLV(const Instruction &inst); - std::string translateDSRAV(const Instruction &inst); - std::string translateDSLL32(const Instruction &inst); - std::string translateDSRL32(const Instruction &inst); - std::string translateDSRA32(const Instruction &inst); - std::string translateDADD(const Instruction &inst); - std::string translateDADDU(const Instruction &inst); - std::string translateDSUB(const Instruction &inst); - std::string translateDSUBU(const Instruction &inst); + // VU instruction translations + std::string translateVU_VADD_Field(const Instruction &inst); + std::string translateVU_VSUB_Field(const Instruction &inst); + std::string translateVU_VMUL_Field(const Instruction &inst); + std::string translateVU_VADD(const Instruction &inst); + std::string translateVU_VSUB(const Instruction &inst); + std::string translateVU_VMUL(const Instruction &inst); + std::string translateVU_VDIV(const Instruction &inst); + std::string translateVU_VSQRT(const Instruction &inst); + std::string translateVU_VRSQRT(const Instruction &inst); + std::string translateVU_VMTIR(const Instruction &inst); + std::string translateVU_VMFIR(const Instruction &inst); + std::string translateVU_VILWR(const Instruction &inst); + std::string translateVU_VISWR(const Instruction &inst); + std::string translateVU_VIADD(const Instruction &inst); + std::string translateVU_VISUB(const Instruction &inst); + std::string translateVU_VIADDI(const Instruction &inst); + std::string translateVU_VIAND(const Instruction &inst); + std::string translateVU_VIOR(const Instruction &inst); + std::string translateVU_VCALLMS(const Instruction &inst); + std::string translateVU_VCALLMSR(const Instruction &inst); + std::string translateVU_VRNEXT(const Instruction &inst); + std::string translateVU_VRGET(const Instruction &inst); + std::string translateVU_VRINIT(const Instruction &inst); + std::string translateVU_VRXOR(const Instruction &inst); + // std::string generateJumpTableSwitch(const Instruction &inst, uint32_t tableAddress, const std::vector &entries); diff --git a/ps2xRecomp/include/ps2recomp/instructions.h b/ps2xRecomp/include/ps2recomp/instructions.h index b2dc146..b685024 100644 --- a/ps2xRecomp/include/ps2recomp/instructions.h +++ b/ps2xRecomp/include/ps2recomp/instructions.h @@ -8,183 +8,221 @@ namespace ps2recomp // Basic MIPS opcodes (shared with R4300i) enum MipsOpcodes { - OPCODE_SPECIAL = 0x00, - OPCODE_REGIMM = 0x01, - OPCODE_J = 0x02, - OPCODE_JAL = 0x03, - OPCODE_BEQ = 0x04, - OPCODE_BNE = 0x05, - OPCODE_BLEZ = 0x06, - OPCODE_BGTZ = 0x07, - OPCODE_ADDI = 0x08, - OPCODE_ADDIU = 0x09, - OPCODE_SLTI = 0x0A, - OPCODE_SLTIU = 0x0B, - OPCODE_ANDI = 0x0C, - OPCODE_ORI = 0x0D, - OPCODE_XORI = 0x0E, - OPCODE_LUI = 0x0F, - OPCODE_COP0 = 0x10, - OPCODE_COP1 = 0x11, - OPCODE_COP2 = 0x12, // VU0 macro instructions + OPCODE_SPECIAL = 0x00, // Special instructions group (see SpecialFunctions) + OPCODE_REGIMM = 0x01, // RegImm instructions group (see RegimmFunctions) + OPCODE_J = 0x02, // Jump + OPCODE_JAL = 0x03, // Jump and Link + OPCODE_BEQ = 0x04, // Branch on Equal + OPCODE_BNE = 0x05, // Branch on Not Equal + OPCODE_BLEZ = 0x06, // Branch on Less Than or Equal to Zero + OPCODE_BGTZ = 0x07, // Branch on Greater Than Zero + + OPCODE_ADDI = 0x08, // Add Immediate Word + OPCODE_ADDIU = 0x09, // Add Immediate Unsigned Word + OPCODE_SLTI = 0x0A, // Set on Less Than Immediate + OPCODE_SLTIU = 0x0B, // Set on Less Than Immediate Unsigned + OPCODE_ANDI = 0x0C, // AND Immediate + OPCODE_ORI = 0x0D, // OR Immediate + OPCODE_XORI = 0x0E, // XOR Immediate + OPCODE_LUI = 0x0F, // Load Upper Immediate + + OPCODE_COP0 = 0x10, // Coprocessor 0 instructions (see Cop0Format) + OPCODE_COP1 = 0x11, // Coprocessor 1 (FPU) instructions (see Cop1Format) + OPCODE_COP2 = 0x12, // Coprocessor 2 (VU0 Macro) instructions (see Cop2Format) OPCODE_COP3 = 0x13, // Unused on PS2 - OPCODE_BEQL = 0x14, - OPCODE_BNEL = 0x15, - OPCODE_BLEZL = 0x16, - OPCODE_BGTZL = 0x17, - OPCODE_DADDI = 0x18, - OPCODE_DADDIU = 0x19, - OPCODE_LDL = 0x1A, - OPCODE_LDR = 0x1B, - OPCODE_MMI = 0x1C, // PS2 specific multimedia instructions - OPCODE_LQ = 0x1E, // PS2 specific 128-bit load - OPCODE_SQ = 0x1F, // PS2 specific 128-bit store - OPCODE_LB = 0x20, - OPCODE_LH = 0x21, - OPCODE_LWL = 0x22, - OPCODE_LW = 0x23, - OPCODE_LBU = 0x24, - OPCODE_LHU = 0x25, - OPCODE_LWR = 0x26, - OPCODE_LWU = 0x27, - OPCODE_SB = 0x28, - OPCODE_SH = 0x29, - OPCODE_SWL = 0x2A, - OPCODE_SW = 0x2B, - OPCODE_SDL = 0x2C, - OPCODE_SDR = 0x2D, - OPCODE_SWR = 0x2E, - OPCODE_CACHE = 0x2F, - OPCODE_LL = 0x30, - OPCODE_LWC1 = 0x31, - OPCODE_LWC2 = 0x32, - OPCODE_PREF = 0x33, - OPCODE_LLD = 0x34, - OPCODE_LDC1 = 0x35, - OPCODE_LDC2 = 0x36, - OPCODE_LD = 0x37, - OPCODE_SC = 0x38, - OPCODE_SWC1 = 0x39, - OPCODE_SWC2 = 0x3A, - OPCODE_SCD = 0x3C, - OPCODE_SDC1 = 0x3D, - OPCODE_SDC2 = 0x3E, - OPCODE_SD = 0x3F, - OPCODE_LQC2 = 0x36, - OPCODE_SQC2 = 0x3E + + OPCODE_BEQL = 0x14, // Branch on Equal Likely + OPCODE_BNEL = 0x15, // Branch on Not Equal Likely + OPCODE_BLEZL = 0x16, // Branch on Less Than or Equal to Zero Likely + OPCODE_BGTZL = 0x17, // Branch on Greater Than Zero Likely + + OPCODE_DADDI = 0x18, // Doubleword Add Immediate + OPCODE_DADDIU = 0x19, // Doubleword Add Immediate Unsigned + OPCODE_LDL = 0x1A, // Load Doubleword Left + OPCODE_LDR = 0x1B, // Load Doubleword Right + OPCODE_MMI = 0x1C, // PS2 specific MMI instructions group (see MMIFunctions) + + OPCODE_LQ = 0x1E, // PS2 specific 128-bit load + OPCODE_SQ = 0x1F, // PS2 specific 128-bit store + + OPCODE_LB = 0x20, // Load Byte + OPCODE_LH = 0x21, // Load Halfword + OPCODE_LWL = 0x22, // Load Word Left + OPCODE_LW = 0x23, // Load Word + OPCODE_LBU = 0x24, // Load Byte Unsigned + OPCODE_LHU = 0x25, // Load Halfword Unsigned + OPCODE_LWR = 0x26, // Load Word Right + OPCODE_LWU = 0x27, // Load Word Unsigned + + OPCODE_SB = 0x28, // Store Byte + OPCODE_SH = 0x29, // Store Halfword + OPCODE_SWL = 0x2A, // Store Word Left + OPCODE_SW = 0x2B, // Store Word + OPCODE_SDL = 0x2C, // Store Doubleword Left + OPCODE_SDR = 0x2D, // Store Doubleword Right + OPCODE_SWR = 0x2E, // Store Word Right + OPCODE_CACHE = 0x2F, // Cache Operation + + OPCODE_LL = 0x30, // LL - Load Linked Word, maybe omitted/unused + OPCODE_LWC1 = 0x31, // Load Word to Coprocessor 1 (FPU) + OPCODE_LWC2 = 0x32, // Load Word to Coprocessor 2, maybe omitted/unused + OPCODE_PREF = 0x33, // Prefetch + OPCODE_LLD = 0x34, // Load Linked Doubleword, maybe omitted/unused + OPCODE_LDC1 = 0x35, // Load Doubleword to Coprocessor 1, FPU is single-precision + OPCODE_LDC2 = 0x36, // Load Quadword to Coprocessor 2 (VU0) - Overrides standard MIPS LDC2 + OPCODE_LD = 0x37, // Load Doubleword + + OPCODE_SC = 0x38, // Store Conditional Word, maybe omitted/unused? + OPCODE_SWC1 = 0x39, // Store Word from Coprocessor 1 (FPU) + OPCODE_SWC2 = 0x3A, // SWC2 - Store Word from Coprocessor 2, maybe omitted/unused + OPCODE_SCD = 0x3C, // SCD - Store Conditional Doubleword, maybe omitted/unused? + OPCODE_SDC1 = 0x3D, // SDC1 - Store Doubleword from Coprocessor 1, FPU is single-precision + OPCODE_SDC2 = 0x3E, // PS2 specific Store Quadword from Coprocessor 2 (VU0) - Overrides standard MIPS SDC2 + OPCODE_SD = 0x3F, // Store Doubleword + + //OPCODE_LQC2 = 0x36, + //OPCODE_SQC2 = 0x3E }; - // SPECIAL function codes + // SPECIAL Function (bits 5-0) for OPCODE_SPECIAL enum SpecialFunctions { - SPECIAL_SLL = 0x00, - SPECIAL_SRL = 0x02, - SPECIAL_SRA = 0x03, - SPECIAL_SLLV = 0x04, - SPECIAL_SRLV = 0x06, - SPECIAL_SRAV = 0x07, - SPECIAL_JR = 0x08, - SPECIAL_JALR = 0x09, - SPECIAL_MOVZ = 0x0A, - SPECIAL_MOVN = 0x0B, - SPECIAL_SYSCALL = 0x0C, - SPECIAL_BREAK = 0x0D, - SPECIAL_SYNC = 0x0F, - SPECIAL_MFHI = 0x10, - SPECIAL_MTHI = 0x11, - SPECIAL_MFLO = 0x12, - SPECIAL_MTLO = 0x13, - SPECIAL_DSLLV = 0x14, - SPECIAL_DSRLV = 0x16, - SPECIAL_DSRAV = 0x17, - SPECIAL_MULT = 0x18, - SPECIAL_MULTU = 0x19, - SPECIAL_DIV = 0x1A, - SPECIAL_DIVU = 0x1B, - SPECIAL_ADD = 0x20, - SPECIAL_ADDU = 0x21, - SPECIAL_SUB = 0x22, - SPECIAL_SUBU = 0x23, - SPECIAL_AND = 0x24, - SPECIAL_OR = 0x25, - SPECIAL_XOR = 0x26, - SPECIAL_NOR = 0x27, - SPECIAL_MFSA = 0x28, - SPECIAL_MTSA = 0x29, - SPECIAL_SLT = 0x2A, - SPECIAL_SLTU = 0x2B, - SPECIAL_DADD = 0x2C, - SPECIAL_DADDU = 0x2D, - SPECIAL_DSUB = 0x2E, - SPECIAL_DSUBU = 0x2F, - SPECIAL_TGE = 0x30, - SPECIAL_TGEU = 0x31, - SPECIAL_TLT = 0x32, - SPECIAL_TLTU = 0x33, - SPECIAL_TEQ = 0x34, - SPECIAL_TNE = 0x36, - SPECIAL_DSLL = 0x38, - SPECIAL_DSRL = 0x3A, - SPECIAL_DSRA = 0x3B, - SPECIAL_DSLL32 = 0x3C, - SPECIAL_DSRL32 = 0x3E, - SPECIAL_DSRA32 = 0x3F + SPECIAL_SLL = 0x00, // Shift Word Left Logical + + SPECIAL_SRL = 0x02, // Shift Word Right Logical + SPECIAL_SRA = 0x03, // Shift Word Right Arithmetic + SPECIAL_SLLV = 0x04, // Shift Word Left Logical Variable + + SPECIAL_SRLV = 0x06, // Shift Word Right Logical Variable + SPECIAL_SRAV = 0x07, // Shift Word Right Arithmetic Variable + + SPECIAL_JR = 0x08, // Jump Register + SPECIAL_JALR = 0x09, // Jump and Link Register + SPECIAL_MOVZ = 0x0A, // Move Conditional on Zero + SPECIAL_MOVN = 0x0B, // Move Conditional on Not Zero + SPECIAL_SYSCALL = 0x0C, // System Call + SPECIAL_BREAK = 0x0D, // Breakpoint + + SPECIAL_SYNC = 0x0F, // Synchronize Shared Memory + + SPECIAL_MFHI = 0x10, // Move From HI Register + SPECIAL_MTHI = 0x11, // Move To HI Register + SPECIAL_MFLO = 0x12, // Move From LO Register + SPECIAL_MTLO = 0x13, // Move To LO Register + SPECIAL_DSLLV = 0x14, // Doubleword Shift Left Logical Variable + + SPECIAL_DSRLV = 0x16, // Doubleword Shift Right Logical Variable + SPECIAL_DSRAV = 0x17, // Doubleword Shift Right Arithmetic Variable + + SPECIAL_MULT = 0x18, // Multiply Word + SPECIAL_MULTU = 0x19, // Multiply Unsigned Word + SPECIAL_DIV = 0x1A, // Divide Word + SPECIAL_DIVU = 0x1B, // Divide Unsigned Word + + SPECIAL_ADD = 0x20, // Add Word + SPECIAL_ADDU = 0x21, // Add Unsigned Word + SPECIAL_SUB = 0x22, // Subtract Word + SPECIAL_SUBU = 0x23, // Subtract Unsigned Word + SPECIAL_AND = 0x24, // AND + SPECIAL_OR = 0x25, // OR + SPECIAL_XOR = 0x26, // XOR + SPECIAL_NOR = 0x27, // NOR + + SPECIAL_MFSA = 0x28, // Move From SA Register (PS2 specific) + SPECIAL_MTSA = 0x29, // Move To SA Register (PS2 specific) + SPECIAL_SLT = 0x2A, // Set on Less Than + SPECIAL_SLTU = 0x2B, // Set on Less Than Unsigned + SPECIAL_DADD = 0x2C, // Doubleword Add + SPECIAL_DADDU = 0x2D, // Doubleword Add Unsigned + SPECIAL_DSUB = 0x2E, // Doubleword Subtract + SPECIAL_DSUBU = 0x2F, // Doubleword Subtract Unsigned + + SPECIAL_TGE = 0x30, // Trap if Greater or Equal + SPECIAL_TGEU = 0x31, // Trap if Greater or Equal Unsigned + SPECIAL_TLT = 0x32, // Trap if Less Than + SPECIAL_TLTU = 0x33, // Trap if Less Than Unsigned + SPECIAL_TEQ = 0x34, // Trap if Equal + + SPECIAL_TNE = 0x36, // Trap if Not Equal + + SPECIAL_DSLL = 0x38, // Doubleword Shift Left Logical + + SPECIAL_DSRL = 0x3A, // Doubleword Shift Right Logical + SPECIAL_DSRA = 0x3B, // Doubleword Shift Right Arithmetic + SPECIAL_DSLL32 = 0x3C, // Doubleword Shift Left Logical +32 + + SPECIAL_DSRL32 = 0x3E, // Doubleword Shift Right Logical +32 + SPECIAL_DSRA32 = 0x3F, // Doubleword Shift Right Arithmetic +32 }; - // REGIMM function codes + // REGIMM RT Field (bits 20-16) for OPCODE_REGIMM enum RegimmFunctions { - REGIMM_BLTZ = 0x00, - REGIMM_BGEZ = 0x01, - REGIMM_BLTZL = 0x02, - REGIMM_BGEZL = 0x03, - REGIMM_TGEI = 0x08, - REGIMM_TGEIU = 0x09, - REGIMM_TLTI = 0x0A, - REGIMM_TLTIU = 0x0B, - REGIMM_TEQI = 0x0C, - REGIMM_TNEI = 0x0E, - REGIMM_BLTZAL = 0x10, - REGIMM_BGEZAL = 0x11, - REGIMM_BLTZALL = 0x12, - REGIMM_BGEZALL = 0x13, - REGIMM_MTSAB = 0x18, - REGIMM_MTSAH = 0x19 + REGIMM_BLTZ = 0x00, // Branch on Less Than Zero + REGIMM_BGEZ = 0x01, // Branch on Greater Than or Equal to Zero + REGIMM_BLTZL = 0x02, // Branch on Less Than Zero Likely + REGIMM_BGEZL = 0x03, // Branch on Greater Than or Equal to Zero Likely + + REGIMM_TGEI = 0x08, // Trap if Greater or Equal Immediate + REGIMM_TGEIU = 0x09, // Trap if Greater or Equal Immediate Unsigned + REGIMM_TLTI = 0x0A, // Trap if Less Than Immediate + REGIMM_TLTIU = 0x0B, // Trap if Less Than Immediate Unsigned + REGIMM_TEQI = 0x0C, // Trap if Equal Immediate + + REGIMM_TNEI = 0x0E, // Trap if Not Equal Immediate + + REGIMM_BLTZAL = 0x10, // Branch on Less Than Zero and Link + REGIMM_BGEZAL = 0x11, // Branch on Greater Than or Equal to Zero and Link + REGIMM_BLTZALL = 0x12, // Branch on Less Than Zero and Link Likely + REGIMM_BGEZALL = 0x13, // Branch on Greater Than or Equal to Zero and Link Likely + + REGIMM_MTSAB = 0x18, // Move To SA Register Byte (PS2 specific) + REGIMM_MTSAH = 0x19, // Move To SA Register Halfword (PS2 specific) }; - // PS2-specific MMI function codes + // MMI Function Field (bits 5-0) for OPCODE_MMI (0x1C) enum MMIFunctions { - MMI_MADD = 0x00, - MMI_MADDU = 0x01, - MMI_PLZCW = 0x04, - MMI_MMI0 = 0x08, - MMI_MMI2 = 0x09, - MMI_MFHI1 = 0x10, - MMI_MTHI1 = 0x11, - MMI_MFLO1 = 0x12, - MMI_MTLO1 = 0x13, - MMI_MULT1 = 0x18, - MMI_MULTU1 = 0x19, - MMI_DIV1 = 0x1A, - MMI_DIVU1 = 0x1B, - MMI_MADD1 = 0x20, - MMI_MADDU1 = 0x21, - MMI_MMI1 = 0x28, - MMI_MMI3 = 0x29, - MMI_PMFHL = 0x30, - MMI_PMTHL = 0x31, - MMI_PSLLH = 0x34, - MMI_PSRLH = 0x36, - MMI_PSRAH = 0x37, - MMI_PSLLW = 0x3C, - MMI_PSRLW = 0x3E, - MMI_PSRAW = 0x3F, - MMI_MSUB = 0x02, - MMI_MSUBU = 0x03 + MMI_MADD = 0x00, // Multiply Add Word (to LO/HI) + MMI_MADDU = 0x01, // Multiply Add Unsigned Word (to LO/HI) + MMI_MSUB = 0x02, // Multiply Subtract Word (Not listed in provided doc table, but valid MMI) + MMI_MSUBU = 0x03, // Multiply Subtract Unsigned Word (Not listed in provided doc table, but valid MMI) + MMI_PLZCW = 0x04, // Parallel Leading Zero/One Count Word + + MMI_MMI0 = 0x08, // Group MMI0 instructions (see MMI0Functions) + MMI_MMI2 = 0x09, // Group MMI2 instructions (see MMI2Functions) + + MMI_MFHI1 = 0x10, // Move From HI1 Register + MMI_MTHI1 = 0x11, // Move To HI1 Register + MMI_MFLO1 = 0x12, // Move From LO1 Register + MMI_MTLO1 = 0x13, // Move To LO1 Register + + MMI_MULT1 = 0x18, // Multiply Word to LO1/HI1 + MMI_MULTU1 = 0x19, // Multiply Unsigned Word to LO1/HI1 + MMI_DIV1 = 0x1A, // Divide Word using LO1/HI1 + MMI_DIVU1 = 0x1B, // Divide Unsigned Word using LO1/HI1 + + MMI_MADD1 = 0x20, // Multiply Add Word to LO1/HI1 + MMI_MADDU1 = 0x21, // Multiply Add Unsigned Word to LO1/HI1 + + MMI_MMI1 = 0x28, // Group MMI1 instructions (see MMI1Functions) + MMI_MMI3 = 0x29, // Group MMI3 instructions (see MMI3Functions) + + MMI_PMFHL = 0x30, // Parallel Move From HI/LO Register (see PMFHLFunctions for 'sa' field) + MMI_PMTHL = 0x31, // Parallel Move To HI/LO Register (see PMFHLFunctions for 'sa' field) + + MMI_PSLLH = 0x34, // Parallel Shift Left Logical Halfword + + MMI_PSRLH = 0x36, // Parallel Shift Right Logical Halfword + MMI_PSRAH = 0x37, // Parallel Shift Right Arithmetic Halfword + + MMI_PSLLW = 0x3C, // Parallel Shift Left Logical Word + + MMI_PSRLW = 0x3E, // Parallel Shift Right Logical Word + MMI_PSRAW = 0x3F // Parallel Shift Right Arithmetic Word }; - // PS2-specific MMI0 function codes + // PS2-specific MMI0 Sub-Function Field (bits 0-5 within MMI_MMI0 group) enum MMI0Functions { MMI0_PADDW = 0x00, @@ -214,7 +252,7 @@ namespace ps2recomp MMI0_PPAC5 = 0x1F }; - // PS2-specific MMI1 function codes + // PS2-specific MMI1 Sub-Function Field (bits 0-5 within MMI_MMI1 group) enum MMI1Functions { MMI1_PABSW = 0x01, @@ -237,7 +275,7 @@ namespace ps2recomp MMI1_QFSRV = 0x1B }; - // PS2-specific MMI2 function codes + // PS2-specific MMI2 Sub-Function Field (bits 0-5 within MMI_MMI2 group) enum MMI2Functions { MMI2_PMADDW = 0x00, @@ -248,14 +286,12 @@ namespace ps2recomp MMI2_PMFLO = 0x09, MMI2_PINTH = 0x0A, MMI2_PMULTW = 0x0C, - MMI2_PDIVW = 0x0D, + MMI2_PDIVW = 0x0D, MMI2_PCPYLD = 0x0E, MMI2_PAND = 0x12, MMI2_PXOR = 0x13, MMI2_PMADDH = 0x14, MMI2_PHMADH = 0x15, - MMI2_PAND_ = 0x16, - MMI2_PXOR_ = 0x17, MMI2_PMSUBH = 0x18, MMI2_PHMSBH = 0x19, MMI2_PEXEH = 0x1A, @@ -266,11 +302,13 @@ namespace ps2recomp MMI2_PROT3W = 0x1F }; - // PS2-specific MMI3 function codes + // PS2-specific MMI3 Sub-Function Field (bits 0-5 within MMI_MMI3 group) enum MMI3Functions { MMI3_PMADDUW = 0x00, MMI3_PSRAVW = 0x03, + MMI3_PMTHI = 0x08, // Move To HI register + MMI3_PMTLO = 0x09, // Move To LO register MMI3_PINTEH = 0x0A, MMI3_PMULTUW = 0x0C, MMI3_PDIVUW = 0x0D, @@ -279,195 +317,382 @@ namespace ps2recomp MMI3_PNOR = 0x13, MMI3_PEXCH = 0x1A, MMI3_PCPYH = 0x1B, - MMI3_PEXCW = 0x1E, - MMI3_PMTHI = 0x08, // Move To HI register - MMI3_PMTLO = 0x09 // Move To LO register + MMI3_PEXCW = 0x1E }; - // COP0 (rs field) + // PMFHL/PMTHL Sub-Function Field ('sa' field, bits 10-6) + enum PMFHLFunctions + { + PMFHL_LW = 0x00, // Load Word / Lower Word + PMFHL_UW = 0x01, // Upper Word + PMFHL_SLW = 0x02, // Signed Lower Word + PMFHL_LH = 0x03, // Load Halfword / Lower Halfword + PMFHL_SH = 0x04 // Store Halfword / Signed Upper Halfword? (Doc name varies) + }; + + // COP0 Format Field ('fmt' or 'rs' field, bits 25-21) for OPCODE_COP0 enum COP0Format { - COP0_MF = 0x00, // Move From COP0 - COP0_MT = 0x04, // Move To COP0 - COP0_CO = 0x10 // COP0 operation + COP0_MF = 0x00, // Move From COP0 - MFC0 (fmt=00000) + COP0_MT = 0x04, // Move To COP0 - MTC0 (fmt=00100) + COP0_BC = 0x08, // BC0 group (fmt=01000) (see Cop0BranchCondition) + COP0_CO = 0x10 // COP0 Operation group (fmt=10000) (see Cop0CoFunctions) }; - // COP0 CO (COProcessor) function codes + // COP0 CO Function Field (bits 5-0) for COP0_CO Format enum Cop0CoFunctions { COP0_CO_TLBR = 0x01, // TLB Read COP0_CO_TLBWI = 0x02, // TLB Write Indexed + COP0_CO_TLBWR = 0x06, // TLB Write Random - COP0_CO_TLBP = 0x08, // TLB Probe - COP0_CO_ERET = 0x18, // Return from Exception - COP0_CO_EI = 0x38, // Enable Interrupts - COP0_CO_DI = 0x39 // Disable Interrupts + + COP0_CO_TLBP = 0x08, // TLB Probe + + COP0_CO_ERET = 0x18, // Return from Exception + + COP0_CO_EI = 0x38, // Enable Interrupts + COP0_CO_DI = 0x39 // Disable Interrupts }; + // COP0 BC Condition Field ('fmt' or 'rt' field, bits 20-16) for COP0_BC Format + enum Cop0BranchCondition + { + COP0_BC_BCF = 0x00, // Branch on Condition False + COP0_BC_BCT = 0x01, // Branch on Condition True + COP0_BC_BCFL = 0x02, // Branch on Condition False Likely + COP0_BC_BCTL = 0x03, // Branch on Condition True Likely + }; + + // COP0 Register Numbers (used with MFC0/MTC0) enum COP0Reg { - COP0_REG_INDEX = 0, // Index into TLB array - COP0_REG_RANDOM = 1, // Randomly generated index into TLB array - COP0_REG_ENTRYLO0 = 2, // Low half of TLB entry for even-numbered virtual pages - COP0_REG_ENTRYLO1 = 3, // Low half of TLB entry for odd-numbered virtual pages - COP0_REG_CONTEXT = 4, // TLB miss handler pointer - COP0_REG_PAGEMASK = 5, // TLB page size mask - COP0_REG_WIRED = 6, // Controls which TLB entries are affected by random replacement - COP0_REG_BADVADDR = 8, // Virtual address of most recent address-related exception - COP0_REG_COUNT = 9, // Timer count - COP0_REG_ENTRYHI = 10, // High half of TLB entry - COP0_REG_COMPARE = 11, // Timer compare value - COP0_REG_STATUS = 12, // Processor status and control - COP0_REG_CAUSE = 13, // Cause of last exception - COP0_REG_EPC = 14, // Exception program counter - COP0_REG_PRID = 15, // Processor identification and revision - COP0_REG_CONFIG = 16, // Configuration register + COP0_REG_INDEX = 0, // Index into TLB array + COP0_REG_RANDOM = 1, // Randomly generated index into TLB array + COP0_REG_ENTRYLO0 = 2, // Low half of TLB entry for even-numbered virtual pages + COP0_REG_ENTRYLO1 = 3, // Low half of TLB entry for odd-numbered virtual pages + COP0_REG_CONTEXT = 4, // Context + COP0_REG_PAGEMASK = 5, // TLB page size mask + COP0_REG_WIRED = 6, // Controls which TLB entries are affected by random replacement + + COP0_REG_BADVADDR = 8, // Virtual address of most recent address-related exception + COP0_REG_COUNT = 9, // Timer count + COP0_REG_ENTRYHI = 10, // High half of TLB entry + COP0_REG_COMPARE = 11, // Timer compare value + COP0_REG_STATUS = 12, // Processor status and control + COP0_REG_CAUSE = 13, // Cause of last exception + COP0_REG_EPC = 14, // Exception program counter + COP0_REG_PRID = 15, // Processor identification and revision + COP0_REG_CONFIG = 16, // Configuration register + COP0_REG_BADPADDR = 23, // Bad physical address COP0_REG_DEBUG = 24, // Debug register COP0_REG_PERF = 25, // Performance counter - COP0_REG_TAGLO = 28, // Low bits of cache tag - COP0_REG_TAGHI = 29, // High bits of cache tag - COP0_REG_ERROREPC = 30 // Error exception program counter + + COP0_REG_TAGLO = 28, // Cache TagLo (I-Cache & D-Cache) + COP0_REG_TAGHI = 29, // High bits of cache tag ( Cache TagHi ) + COP0_REG_ERROREPC = 30 // Error exception program counter }; - // COP1 (FPU) function codes + // COP1 (FPU) Format Field ('fmt' or 'rs' field, bits 25-21) for OPCODE_COP1 enum Cop1Format { COP1_MF = 0x00, // Move From FPU Register COP1_CF = 0x02, // Move From FPU Control Register COP1_MT = 0x04, // Move To FPU Register COP1_CT = 0x06, // Move To FPU Control Register - COP1_BC = 0x08, // Branch on FPU Condition - COP1_S = 0x10, // Single Precision Operation - 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_BC = 0x08, // Branch on FPU Condition (see Cop1BranchCondition) + + COP1_D = 0x11, // Standard MIPS Double format; EE FPU is single-precision, (unused on PS2 FPU?) + COP1_L = 0x15, // Long (64-bit integer) Operation (unused on PS2 FPU?) + + COP1_S = 0x10, // Single Precision Operation (see Cop1FunctionsS) + COP1_W = 0x14 // Word (integer) Operation (see Cop1FunctionsW) }; - enum Cop1Functions + // COP1 Function Field (bits 5-0) for COP1_S Format (Single-Precision) + enum Cop1FunctionsS { - COP1_FUNC_ADD = 0x00, - COP1_FUNC_SUB = 0x01, - COP1_FUNC_MUL = 0x02, - COP1_FUNC_DIV = 0x03, - COP1_FUNC_SQRT = 0x04, - COP1_FUNC_ABS = 0x05, - COP1_FUNC_MOV = 0x06, - COP1_FUNC_NEG = 0x07, - COP1_FUNC_ROUND_L = 0x08, - COP1_FUNC_TRUNC_L = 0x09, - COP1_FUNC_CEIL_L = 0x0A, - COP1_FUNC_FLOOR_L = 0x0B, - COP1_FUNC_ROUND_W = 0x0C, - COP1_FUNC_TRUNC_W = 0x0D, - COP1_FUNC_CEIL_W = 0x0E, - COP1_FUNC_FLOOR_W = 0x0F, - COP1_FUNC_CVT_S = 0x20, // Convert to Single - COP1_FUNC_CVT_D = 0x21, // Convert to Double - COP1_FUNC_CVT_W = 0x24, // Convert to Word - COP1_FUNC_CVT_L = 0x25, // Convert to Long - // Comparisons (lowest 5 bits matter for condition, C bit set in FCR31) - COP1_FUNC_C_F = 0x30, // False - COP1_FUNC_C_UN = 0x31, // Unordered - COP1_FUNC_C_EQ = 0x32, // Equal - COP1_FUNC_C_UEQ = 0x33, // Unordered or Equal - COP1_FUNC_C_OLT = 0x34, // Ordered Less Than - COP1_FUNC_C_ULT = 0x35, // Unordered or Less Than - COP1_FUNC_C_OLE = 0x36, // Ordered Less Than or Equal - COP1_FUNC_C_ULE = 0x37, // Unordered or Less Than or Equal - COP1_FUNC_C_SF = 0x38, // Signaling False - COP1_FUNC_C_NGLE = 0x39, // Not Greater or Less or Equal (Unordered) - COP1_FUNC_C_SEQ = 0x3A, // Signaling Equal - COP1_FUNC_C_NGL = 0x3B, // Not Greater or Less (Unordered or Equal) - COP1_FUNC_C_LT = 0x3C, // Signaling Less Than - COP1_FUNC_C_NGE = 0x3D, // Not Greater or Equal (Unordered or Less Than) - COP1_FUNC_C_LE = 0x3E, // Signaling Less Than or Equal - COP1_FUNC_C_NGT = 0x3F, // Not Greater Than (Unordered or Less Than or Equal) + COP1_S_ADD = 0x00, // ADD.S + COP1_S_SUB = 0x01, // SUB.S + COP1_S_MUL = 0x02, // MUL.S + COP1_S_DIV = 0x03, // DIV.S + COP1_S_SQRT = 0x04, // SQRT.S + COP1_S_ABS = 0x05, // ABS.S + COP1_S_MOV = 0x06, // MOV.S + COP1_S_NEG = 0x07, // NEG.S + + COP1_S_ROUND_L = 0x08, // ROUND.L.S - Requires 64-bit int dest, likely unused + COP1_S_TRUNC_L = 0x09, // TRUNC.L.S - Requires 64-bit int dest, likely unused + COP1_S_CEIL_L = 0x0A, // CEIL.L.S - Requires 64-bit int dest, likely unused + COP1_S_FLOOR_L = 0x0B, // FLOOR.L.S - Requires 64-bit int dest, likely unused + COP1_S_ROUND_W = 0x0C, // ROUND.W.S + COP1_S_TRUNC_W = 0x0D, // TRUNC.W.S + COP1_S_CEIL_W = 0x0E, // CEIL.W.S + COP1_S_FLOOR_W = 0x0F, // FLOOR.W.S + + // --- Additional Arithmetic/Functions (from FPU.S table) --- + COP1_S_RSQRT = 0x16, // RSQRT.S (Reciprocal Square Root) + COP1_S_ADDA = 0x18, // ADDA.S (Add Accumulator) + COP1_S_SUBA = 0x19, // SUBA.S (Subtract Accumulator) + COP1_S_MULA = 0x1A, // MULA.S (Multiply Accumulator) + COP1_S_MADD = 0x1C, // MADD.S (Multiply Add) + COP1_S_MSUB = 0x1D, // MSUB.S (Multiply Subtract) + COP1_S_MADDA = 0x1E, // MADDA.S (Multiply Add Accumulator) + COP1_S_MSUBA = 0x1F, // MSUBA.S (Multiply Subtract Accumulator) + + // --- Conversions (from FPU.S table) --- + COP1_S_CVT_D = 0x21, // CVT.D.S - Requires double-precision FPU + COP1_S_CVT_W = 0x24, // CVT.W.S (Convert to Word) + COP1_S_CVT_L = 0x25, // CVT.L.S - Requires 64-bit int dest, likely unused + + // --- Min/Max (from FPU.S table) --- + COP1_S_MAX = 0x28, // MAX.S + COP1_S_MIN = 0x29, // MIN.S + + COP1_S_C_F = 0x30, // C.F.S (False) + COP1_S_C_UN = 0x31, // C.UN.S (Unordered) + COP1_S_C_EQ = 0x32, // C.EQ.S (Equal) + COP1_S_C_UEQ = 0x33, // C.UEQ.S (Unordered or Equal) + COP1_S_C_OLT = 0x34, // C.OLT.S (Ordered Less Than) + COP1_S_C_ULT = 0x35, // C.ULT.S (Unordered or Less Than) + COP1_S_C_OLE = 0x36, // C.OLE.S (Ordered Less Than or Equal) + COP1_S_C_ULE = 0x37, // C.ULE.S (Unordered or Less Than or Equal) + COP1_S_C_SF = 0x38, // C.SF.S (Signaling False) + COP1_S_C_NGLE = 0x39, // C.NGLE.S (Not Greater or Less or Equal - Unordered) + COP1_S_C_SEQ = 0x3A, // C.SEQ.S (Signaling Equal) + COP1_S_C_NGL = 0x3B, // C.NGL.S (Not Greater or Less - Unordered or Equal) + COP1_S_C_LT = 0x3C, // C.LT.S (Signaling Less Than) + COP1_S_C_NGE = 0x3D, // C.NGE.S (Not Greater or Equal - Unordered or Less Than) + COP1_S_C_LE = 0x3E, // C.LE.S (Signaling Less Than or Equal) + COP1_S_C_NGT = 0x3F, // C.NGT.S (Not Greater Than - Unordered or Less Than or Equal) }; - // COP2 (VU0 macro) function codes - enum Cop2Functions + // COP1 Function Field (bits 5-0) for COP1_W Format (Word/Integer source) + enum Cop1FunctionsW { - COP2_QMFC2 = 0x00, // Move From Coprocessor 2 (128-bit) - COP2_CFC2 = 0x02, // Move Control From Coprocessor 2 - COP2_QMTC2 = 0x04, // Move To Coprocessor 2 (128-bit) - COP2_CTC2 = 0x06, // Move Control To Coprocessor 2 - COP2_BC2 = 0x08, // Branch On Coprocessor 2 Condition - COP2_CO = 0x10, // COProcessor instructions (VU0 macro) - COP2_BCF = 0x00, // Branch on VU0 false - COP2_BCT = 0x01, // Branch on VU0 true - COP2_BCFL = 0x02, // Branch on VU0 false likely - COP2_BCTL = 0x03, // Branch on VU0 true likely - COP2_BCEF = 0x4, // Branch on VU0 equal flag false (new) - COP2_BCET = 0x5, // Branch on VU0 equal flag true (new) - COP2_BCEFL = 0x6, // Branch on VU0 equal flag false likely (new) - COP2_BCETL = 0x7, // Branch on VU0 equal flag true likely (new) - COP2_MFC2 = 0x02, - COP2_MTC2 = 0x0A, - COP2_BC2F = 0x11, // Branch on VU0 condition false - COP2_VU0OPS = 0x12, // VU0 Special Operations - COP2_MTVUCF = 0x13, // Move To VU0 control/flag register - COP2_CTCVU = 0x18, // Control To VU0 with specific functionality - COP2_VMTIR = 0x1C, // Move To VU0 I Register - COP2_VCLIP = 0x1E, // VU0 Clipping operation - COP2_VLDQ = 0x1F // VU0 Load/Store Quad with Decrement + COP1_W_CVT_S = 0x20, // CVT.S.W (Convert Word Integer to Single Float) + // 0x21 --- (Standard MIPS: CVT.D.W - requires double-precision) + // 0x24 --- (Standard MIPS: CVT.W.W - Nop?) + // 0x25 --- (Standard MIPS: CVT.L.W - requires 64-bit int dest) }; - // VU0 macro instruction function codes (subset - there are many more) - enum VU0MacroFunctions + // COP1 BC Condition Field ('fmt' or 'rt' field, bits 20-16) for COP1_BC Format + enum Cop1BranchCondition { - VU0_VADD = 0x00, - VU0_VSUB = 0x01, - VU0_VMUL = 0x02, - VU0_VDIV = 0x03, - VU0_VSQRT = 0x04, - VU0_VRSQRT = 0x05, - VU0_VMULQ = 0x06, - VU0_VIADD = 0x10, - VU0_VISUB = 0x11, - VU0_VIADDI = 0x12, - VU0_VIAND = 0x13, - VU0_VIOR = 0x14, - VU0_VILWR = 0x15, - VU0_VISWR = 0x16, - VU0_VCALLMS = 0x20, - VU0_VCALLMSR = 0x21, - VU0_VRGET = 0x3F, // Get VU0 R register - VU0_VSUB_XYZ = 0x38 // Subtract with component selection + COP1_BC_BCF = 0x00, // Branch on FPU Condition False (BC1F) + COP1_BC_BCT = 0x01, // Branch on FPU Condition True (BC1T) + COP1_BC_BCFL = 0x02, // Branch on FPU Condition False Likely (BC1FL) + COP1_BC_BCTL = 0x03, // Branch on FPU Condition True Likely (BC1TL) }; - enum VU0SpecialFormats + // COP2 (VU0 Macro) Format Field ('fmt' or 'rs' field, bits 25-21) for OPCODE_COP2 + enum Cop2Format { - VU0_BC2F = 0x11, // Branch on VU0 condition false - VU0_MTVUCF = 0x13, // Move To VU0 control/flag register - VU0_CTCVU = 0x18, // Control To VU0 with specific functionality - VU0_VMTIR = 0x1C, // Move To VU0 I Register - VU0_VCLIP = 0x1E, // VU0 Clipping operation - VU0_VLDQ = 0x1F // VU0 Load/Store Quad with Decrement + // Note: Standard MIPS MFC2/MTC2 use fmt 0x00/0x04, EE uses QMFC2/QMTC2 + COP2_QMFC2 = 0x01, // QMFC2 (fmt=00001) - Load Quadword From Coprocessor 2 + COP2_CFC2 = 0x02, // CFC2 (fmt=00010) - Load Control Word From Coprocessor 2 + COP2_QMTC2 = 0x05, // QMTC2 (fmt=00101) - Store Quadword To Coprocessor 2 + COP2_CTC2 = 0x06, // CTC2 (fmt=00110) - Store Control Word To Coprocessor 2 + COP2_BC = 0x08, // BC2 group (fmt=01000) (see Cop2BranchCondition) + COP2_CO = 0x10, // VU0 Macro Operation group (fmt=1xxxx) }; + // COP2 BC Condition Field ('fmt' or 'rt' field, bits 20-16) for COP2_BC Format + enum Cop2BranchCondition + { + COP2_BC_BCF = 0x00, // Branch on VU0 Condition False (BC2F) + COP2_BC_BCT = 0x01, // Branch on VU0 Condition True (BC2T) + COP2_BC_BCFL = 0x02, // Branch on VU0 Condition False Likely (BC2FL) + COP2_BC_BCTL = 0x03, // Branch on VU0 Condition True Likely (BC2TL) + }; + + // VU0 Macro Function Field (bits 5-0) for COP2_CO Format (Special1 Table) + enum VU0MacroSpecial1Functions : uint8_t + { + VU0_S1_VADDx = 0x00, + VU0_S1_VADDy = 0x01, + VU0_S1_VADDz = 0x02, + VU0_S1_VADDw = 0x03, + VU0_S1_VSUBx = 0x04, + VU0_S1_VSUBy = 0x05, + VU0_S1_VSUBz = 0x06, + VU0_S1_VSUBw = 0x07, + VU0_S1_VMADDx = 0x08, + VU0_S1_VMADDy = 0x09, + VU0_S1_VMADDz = 0x0A, + VU0_S1_VMADDw = 0x0B, + VU0_S1_VMSUBx = 0x0C, + VU0_S1_VMSUBy = 0x0D, + VU0_S1_VMSUBz = 0x0E, + VU0_S1_VMSUBw = 0x0F, + VU0_S1_VMAXx = 0x10, + VU0_S1_VMAXy = 0x11, + VU0_S1_VMAXz = 0x12, + VU0_S1_VMAXw = 0x13, + VU0_S1_VMINIx = 0x14, + VU0_S1_VMINIy = 0x15, + VU0_S1_VMINIz = 0x16, + VU0_S1_VMINIw = 0x17, + VU0_S1_VMULx = 0x18, + VU0_S1_VMULy = 0x19, + VU0_S1_VMULz = 0x1A, + VU0_S1_VMULw = 0x1B, + VU0_S1_VMULq = 0x1C, + VU0_S1_VMAXi = 0x1D, + VU0_S1_VMULi = 0x1E, + VU0_S1_VMINIi = 0x1F, + VU0_S1_VADDq = 0x20, + VU0_S1_VMADDq = 0x21, + VU0_S1_VADDi = 0x22, + VU0_S1_VMADDi = 0x23, + VU0_S1_VSUBq = 0x24, + VU0_S1_VMSUBq = 0x25, + VU0_S1_VSUBi = 0x26, + VU0_S1_VMSUBi = 0x27, + VU0_S1_VADD = 0x28, + VU0_S1_VMADD = 0x29, + VU0_S1_VMUL = 0x2A, + VU0_S1_VMAX = 0x2B, + VU0_S1_VSUB = 0x2C, + VU0_S1_VMSUB = 0x2D, + VU0_S1_VOPMSUB = 0x2E, + VU0_S1_VMINI = 0x2F, + VU0_S1_VIADD = 0x30, + VU0_S1_VISUB = 0x31, + VU0_S1_VIADDI = 0x32, + VU0_S1_VIAND = 0x34, + VU0_S1_VIOR = 0x35, + VU0_S1_VCALLMS = 0x38, + VU0_S1_VCALLMSR = 0x39, + }; + + // VU0 Macro Function Field (bits 5-0) for COP2_CO Format (Special2 Table) + enum VU0MacroSpecial2Functions : uint8_t + { + VU0_S2_VADDAx = 0x00, + VU0_S2_VADDAy = 0x01, + VU0_S2_VADDAz = 0x02, + VU0_S2_VADDAw = 0x03, + VU0_S2_VSUBAx = 0x04, + VU0_S2_VSUBAy = 0x05, + VU0_S2_VSUBAz = 0x06, + VU0_S2_VSUBAw = 0x07, + VU0_S2_VMADDAx = 0x08, + VU0_S2_VMADDAy = 0x09, + VU0_S2_VMADDAz = 0x0A, + VU0_S2_VMADDAw = 0x0B, + VU0_S2_VMSUBAx = 0x0C, + VU0_S2_VMSUBAy = 0x0D, + VU0_S2_VMSUBAz = 0x0E, + VU0_S2_VMSUBAw = 0x0F, + VU0_S2_VITOF0 = 0x10, + VU0_S2_VITOF4 = 0x11, + VU0_S2_VITOF12 = 0x12, + VU0_S2_VITOF15 = 0x13, + VU0_S2_VFTOI0 = 0x14, + VU0_S2_VFTOI4 = 0x15, + VU0_S2_VFTOI12 = 0x16, + VU0_S2_VFTOI15 = 0x17, + VU0_S2_VMULAx = 0x18, + VU0_S2_VMULAy = 0x19, + VU0_S2_VMULAz = 0x1A, + VU0_S2_VMULAw = 0x1B, + VU0_S2_VMULAq = 0x1C, + VU0_S2_VABS = 0x1D, + VU0_S2_VMULAi = 0x1E, + VU0_S2_VCLIPw = 0x1F, + VU0_S2_VADDAq = 0x20, + VU0_S2_VMADDAq = 0x21, + VU0_S2_VADDAi = 0x22, + VU0_S2_VMADDAi = 0x23, + VU0_S2_VSUBAq = 0x24, + VU0_S2_VMSUBAq = 0x25, + VU0_S2_VSUBAi = 0x26, + VU0_S2_VMSUBAi = 0x27, + VU0_S2_VADDA = 0x28, + VU0_S2_VMADDA = 0x29, + VU0_S2_VMULA = 0x2A, + VU0_S2_VSUBA = 0x2C, + VU0_S2_VMSUBA = 0x2D, + VU0_S2_VOPMULA = 0x2E, + VU0_S2_VNOP = 0x2F, + VU0_S2_VMOVE = 0x30, + VU0_S2_VMR32 = 0x31, + VU0_S2_VLQI = 0x34, + VU0_S2_VSQI = 0x35, + VU0_S2_VLQD = 0x36, + VU0_S2_VSQD = 0x37, + VU0_S2_VDIV = 0x38, + VU0_S2_VSQRT = 0x39, + VU0_S2_VRSQRT = 0x3A, + VU0_S2_VWAITQ = 0x3B, + VU0_S2_VMTIR = 0x3C, + VU0_S2_VMFIR = 0x3D, + VU0_S2_VILWR = 0x3E, + VU0_S2_VISWR = 0x3F, + VU0_S2_VRNEXT = 0x40, + VU0_S2_VRGET = 0x41, + VU0_S2_VRINIT = 0x42, + VU0_S2_VRXOR = 0x43 + }; + + // // VU0 macro instruction function codes (subset - there are many more) + // enum VU0MacroFunctions + // { + // VU0_VADD = 0x00, + // VU0_VSUB = 0x01, + // VU0_VMUL = 0x02, + // VU0_VDIV = 0x03, + // VU0_VSQRT = 0x04, + // VU0_VRSQRT = 0x05, + // VU0_VMULQ = 0x06, + // VU0_VIADD = 0x10, + // VU0_VISUB = 0x11, + // VU0_VIADDI = 0x12, + // VU0_VIAND = 0x13, + // VU0_VIOR = 0x14, + // VU0_VILWR = 0x15, + // VU0_VISWR = 0x16, + // VU0_VCALLMS = 0x20, + // VU0_VCALLMSR = 0x21, + // VU0_VRGET = 0x3F, // Get VU0 R register + // VU0_VSUB_XYZ = 0x38 // Subtract with component selection + // }; + + // enum VU0SpecialFormats + // { + // VU0_BC2F = 0x11, // Branch on VU0 condition false + // VU0_MTVUCF = 0x13, // Move To VU0 control/flag register + // VU0_CTCVU = 0x18, // Control To VU0 with specific functionality + // VU0_VMTIR = 0x1C, // Move To VU0 I Register + // VU0_VCLIP = 0x1E, // VU0 Clipping operation + // VU0_VLDQ = 0x1F // VU0 Load/Store Quad with Decrement + // }; + + // VU0 Control Register Numbers (used with CFC2/CTC2) enum VU0ControlRegisters { VU0_CR_STATUS = 0, // Status/Control register VU0_CR_MAC = 1, // MAC flags register - VU0_CR_CLIP = 5, // Clipping flags register + VU0_CR_CLIP = 5, // Clipping flags register //TODO maybe this is a 2 instead of 5 + VU0_CR_R = 3, // R register (Random number) + VU0_CR_I = 4, // I register (Immediate) VU0_CR_CMSAR0 = 13, // VU0 microprogram start address register VU0_CR_FBRST = 18 // VIF/VU0/VU1 reset register }; 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 = 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 + 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 @@ -494,16 +719,6 @@ namespace ps2recomp VU0_FMT_LQSQ_COP0 = 0x1B // Load/store quad VU0 format }; - // PMFHL functions (sa field) - enum PMFHLFunctions - { - PMFHL_LW = 0x00, - PMFHL_UW = 0x01, - PMFHL_SLW = 0x02, - PMFHL_LH = 0x03, - PMFHL_SH = 0x04 - }; - // Instruction decoding helper macros #define OPCODE(inst) ((inst >> 26) & 0x3F) #define RS(inst) ((inst >> 21) & 0x1F) @@ -512,8 +727,31 @@ namespace ps2recomp #define SA(inst) ((inst >> 6) & 0x1F) #define FUNCTION(inst) ((inst) & 0x3F) #define IMMEDIATE(inst) ((inst) & 0xFFFF) -#define SIMMEDIATE(inst) ((int16_t)((inst) & 0xFFFF)) +#define SIMMEDIATE(inst) ((int32_t)(int16_t)((inst) & 0xFFFF)) #define TARGET(inst) ((inst) & 0x3FFFFFF) +#define COP_FUNCT(inst) ((uint8_t)((inst) & 0x3F)) // Function field for COPz CO instructions +#define FPU_FMT(inst) ((uint8_t)(((inst) >> 21) & 0x1F)) // Format field for FPU instructions +#define FT(inst) RT(inst) // Target FPU register +#define FS(inst) RD(inst) // Source FPU register +#define FD(inst) SA(inst) // Destination FPU register +#define VU_I(inst) ((inst >> 25) & 0x1) // VU Upper I bit +#define VU_E(inst) ((inst >> 24) & 0x1) // VU Upper E bit +#define VU_M(inst) ((inst >> 23) & 0x1) // VU Upper M bit (VU0 only) +#define VU_D(inst) ((inst >> 22) & 0x1) // VU Upper D bit +#define VU_T(inst) ((inst >> 21) & 0x1) // VU Upper T bit +#define VU_DEST(inst) ((uint8_t)(((inst) >> 16) & 0xF)) // VU Destination mask +#define VU_FSF(inst) ((uint8_t)(((inst) >> 10) & 0x3)) // VU Source Field F +#define VU_FTF(inst) ((uint8_t)(((inst) >> 8) & 0x3)) // VU Target Field F +#define VU_FD(inst) ((uint8_t)(((inst) >> 11) & 0x1F)) // VU Destination Vector Register +#define VU_FS(inst) ((uint8_t)(((inst) >> 6) & 0x1F)) // VU Source Vector Register +#define VU_FT(inst) ((uint8_t)((inst) & 0x1F)) // VU Target Vector Register +#define VU_IS(inst) RT(inst) // VU Source Integer Register +#define VU_IT(inst) RD(inst) // VU Target Integer Register +#define VU_ID(inst) SA(inst) // VU Destination Integer Register +#define VU_IMM5(inst) ((uint8_t)((inst >> 6) & 0x1F)) // VU 5-bit immediate +#define VU_IMM11(inst) ((uint16_t)((inst) & 0x7FF)) // VU 11-bit immediate +#define VU_SIMM11(inst) ((int16_t)(((inst & 0x7FF) ^ 0x400) - 0x400)) // VU 11-bit signed immediate +#define VU_IMM15(inst) ((uint16_t)((inst) & 0x7FFF)) // VU 15-bit immediate } // namespace ps2recomp diff --git a/ps2xRecomp/include/ps2recomp/types.h b/ps2xRecomp/include/ps2recomp/types.h index 6747404..402396e 100644 --- a/ps2xRecomp/include/ps2recomp/types.h +++ b/ps2xRecomp/include/ps2recomp/types.h @@ -15,14 +15,15 @@ namespace ps2recomp { uint32_t address; uint32_t opcode; - uint32_t rs; // Source register - uint32_t rt; // Target register - uint32_t rd; // Destination register - uint32_t sa; // Shift amount - uint32_t function; // Function code for R-type instructions - uint32_t immediate; // Immediate value for I-type instructions - uint32_t target; // Jump target for J-type instructions - uint32_t raw; // Raw instruction value + uint32_t rs; // Source register + uint32_t rt; // Target register + uint32_t rd; // Destination register + uint32_t sa; // Shift amount + uint32_t function; // Function code for R-type instructions + uint32_t immediate; // Immediate value for I-type instructions + uint32_t simmediate; // Sign-extended immediate value (extended to 32 bits) + uint32_t target; // Jump target for J-type instructions + uint32_t raw; // Raw instruction value // Instruction type flags bool isMMI; // Is MMI instruction (PS2 specific) @@ -49,6 +50,8 @@ namespace ps2recomp bool usesPReg; // Uses P register bool modifiesMAC; // Modifies MAC flags uint8_t vectorField; // xyzw field mask + uint8_t fsf; // Field select for FS reg (bits 10-11) + uint8_t ftf; // Source Field select for FT reg (bits 8-9) } vectorInfo; struct @@ -56,9 +59,21 @@ namespace ps2recomp bool modifiesGPR; // Modifies general purpose register bool modifiesFPR; // Modifies floating point register bool modifiesVFR; // Modifies vector float register + bool modifiesVIR; // Modifies vector integer register + bool modifiesVIC; // Modifies vector integer control register bool modifiesMemory; // Modifies memory bool modifiesControl; // Modifies control register } modificationInfo; + + Instruction() : address(0), opcode(0), rs(0), rt(0), rd(0), sa(0), function(0), + immediate(0), simmediate(0), target(0), raw(0), + isMMI(false), isVU(false), isBranch(false), isJump(false), isCall(false), + isReturn(false), hasDelaySlot(false), isMultimedia(false), isStore(false), isLoad(false), + mmiType(0), mmiFunction(0), pmfhlVariation(0), vuFunction(0) + { + vectorInfo = {}; + modificationInfo = {}; + } }; // Function information diff --git a/ps2xRecomp/src/code_generator.cpp b/ps2xRecomp/src/code_generator.cpp index 05d7498..45a916c 100644 --- a/ps2xRecomp/src/code_generator.cpp +++ b/ps2xRecomp/src/code_generator.cpp @@ -15,197 +15,206 @@ namespace ps2recomp std::string CodeGenerator::handleBranchDelaySlots(const Instruction &branchInst, const Instruction &delaySlot) { std::stringstream ss; + bool hasValidDelaySlot = (delaySlot.raw != 0); + std::string delaySlotCode = hasValidDelaySlot ? translateInstruction(delaySlot) : ""; + uint8_t rs_reg = branchInst.rs; + uint8_t rt_reg = branchInst.rt; + uint8_t rd_reg = branchInst.rd; if (branchInst.opcode == OPCODE_J || branchInst.opcode == OPCODE_JAL) { - // J/JAL instruction if (branchInst.opcode == OPCODE_JAL) { - // For JAL, set the return address - ss << " ctx->r[31] = 0x" << std::hex << (branchInst.address + 8) << ";\n" + ss << " SET_GPR_U32(ctx, 31, 0x" << std::hex << (branchInst.address + 8) << ");\n" << std::dec; } - - // Execute delay slot - ss << " " << translateInstruction(delaySlot) << "\n"; - - // Jump to target + if (hasValidDelaySlot) + { + ss << " " << delaySlotCode << "\n"; + } uint32_t target = (branchInst.address & 0xF0000000) | (branchInst.target << 2); Symbol *sym = findSymbolByAddress(target); if (sym && sym->isFunction) { - ss << " " << sym->name << "(rdram, ctx);\n"; - ss << " return;\n"; + ss << " " << sym->name << "(rdram, ctx); return;\n"; } else { - ss << " // Jump to unknown target: 0x" << std::hex << target << std::dec << "\n"; - ss << " return;\n"; + ss << " ctx->pc = 0x" << std::hex << target << "; return;\n" + << std::dec; } } else if (branchInst.opcode == OPCODE_SPECIAL && (branchInst.function == SPECIAL_JR || branchInst.function == SPECIAL_JALR)) { - // JR/JALR instruction - if (branchInst.function == SPECIAL_JALR) + uint8_t link_reg = (branchInst.function == SPECIAL_JALR) ? ((rd_reg == 0) ? 31 : rd_reg) : 0; + if (link_reg != 0) { - // For JALR, set the return address - ss << " ctx->r[" << branchInst.rd << "] = 0x" << std::hex << (branchInst.address + 8) << ";\n" + ss << " SET_GPR_U32(ctx, " << (int)link_reg << ", 0x" << std::hex << (branchInst.address + 8) << ");\n" << std::dec; } - - // Execute delay slot - ss << " " << translateInstruction(delaySlot) << "\n"; - - // Jump to address in register - if (branchInst.rs == 31 && branchInst.function == SPECIAL_JR) + if (hasValidDelaySlot) + { + ss << " " << delaySlotCode << "\n"; + } + if (rs_reg == 31 && branchInst.function == SPECIAL_JR) { - // JR $ra - likely a return ss << " return;\n"; } else { - ss << " LOOKUP_FUNC(ctx->r[" << branchInst.rs << "])(rdram, ctx);\n"; - ss << " return;\n"; + ss << " ctx->pc = GPR_U32(ctx, " << (int)rs_reg << "); return;\n"; } } else if (branchInst.isBranch) { - std::string conditionStr; + std::string conditionStr = "false"; + std::string linkCode = ""; - // Generate condition based on branch type switch (branchInst.opcode) { case OPCODE_BEQ: - conditionStr = fmt::format("ctx->r[{}] == ctx->r[{}]", branchInst.rs, branchInst.rt); + conditionStr = fmt::format("GPR_U32(ctx, {}) == GPR_U32(ctx, {})", rs_reg, rt_reg); break; - case OPCODE_BNE: - conditionStr = fmt::format("ctx->r[{}] != ctx->r[{}]", branchInst.rs, branchInst.rt); + conditionStr = fmt::format("GPR_U32(ctx, {}) != GPR_U32(ctx, {})", rs_reg, rt_reg); break; - case OPCODE_BLEZ: - conditionStr = fmt::format("(int32_t)ctx->r[{}] <= 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) <= 0", rs_reg); break; - case OPCODE_BGTZ: - conditionStr = fmt::format("(int32_t)ctx->r[{}] > 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) > 0", rs_reg); break; - case OPCODE_BEQL: - conditionStr = fmt::format("ctx->r[{}] == ctx->r[{}]", branchInst.rs, branchInst.rt); + conditionStr = fmt::format("GPR_U32(ctx, {}) == GPR_U32(ctx, {})", rs_reg, rt_reg); break; - case OPCODE_BNEL: - conditionStr = fmt::format("ctx->r[{}] != ctx->r[{}]", branchInst.rs, branchInst.rt); + conditionStr = fmt::format("GPR_U32(ctx, {}) != GPR_U32(ctx, {})", rs_reg, rt_reg); break; - case OPCODE_BLEZL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] <= 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) <= 0", rs_reg); break; - case OPCODE_BGTZL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] > 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) > 0", rs_reg); break; - case OPCODE_REGIMM: - switch (branchInst.rt) + switch (rt_reg) { case REGIMM_BLTZ: - conditionStr = fmt::format("(int32_t)ctx->r[{}] < 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) < 0", rs_reg); break; - case REGIMM_BGEZ: - conditionStr = fmt::format("(int32_t)ctx->r[{}] >= 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) >= 0", rs_reg); break; - case REGIMM_BLTZL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] < 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) < 0", rs_reg); break; - case REGIMM_BGEZL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] >= 0", branchInst.rs); + conditionStr = fmt::format("GPR_S32(ctx, {}) >= 0", rs_reg); break; - case REGIMM_BLTZAL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] < 0", branchInst.rs); - ss << " ctx->r[31] = 0x" << std::hex << (branchInst.address + 8) << ";\n" - << std::dec; + conditionStr = fmt::format("GPR_S32(ctx, {}) < 0", rs_reg); + linkCode = fmt::format("SET_GPR_U32(ctx, 31, 0x{:X});", branchInst.address + 8); break; - case REGIMM_BGEZAL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] >= 0", branchInst.rs); - ss << " ctx->r[31] = 0x" << std::hex << (branchInst.address + 8) << ";\n" - << std::dec; + conditionStr = fmt::format("GPR_S32(ctx, {}) >= 0", rs_reg); + linkCode = fmt::format("SET_GPR_U32(ctx, 31, 0x{:X});", branchInst.address + 8); break; - case REGIMM_BLTZALL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] < 0", branchInst.rs); - ss << " ctx->r[31] = 0x" << std::hex << (branchInst.address + 8) << ";\n" - << std::dec; + conditionStr = fmt::format("GPR_S32(ctx, {}) < 0", rs_reg); + linkCode = fmt::format("SET_GPR_U32(ctx, 31, 0x{:X});", branchInst.address + 8); break; - case REGIMM_BGEZALL: - conditionStr = fmt::format("(int32_t)ctx->r[{}] >= 0", branchInst.rs); - ss << " ctx->r[31] = 0x" << std::hex << (branchInst.address + 8) << ";\n" - << std::dec; - break; - - default: - conditionStr = "false"; + conditionStr = fmt::format("GPR_S32(ctx, {}) >= 0", rs_reg); + linkCode = fmt::format("SET_GPR_U32(ctx, 31, 0x{:X});", branchInst.address + 8); break; } break; - - default: - conditionStr = "false"; + case OPCODE_COP1: + if (branchInst.rs == COP1_BC) + { + uint8_t bc_cond = branchInst.rt; + if (bc_cond == COP1_BC_BCF || bc_cond == COP1_BC_BCFL) + { + conditionStr = "!(ctx->fcr31 & 0x800000)"; + } + else + { + conditionStr = "(ctx->fcr31 & 0x800000)"; + } + } + break; + case OPCODE_COP2: + if (branchInst.rs == COP2_BC) + { + uint8_t bc_cond = branchInst.rt; + if (bc_cond == COP2_BC_BCF || bc_cond == COP2_BC_BCFL) + { + conditionStr = "!(ctx->vu0_status & 0x1)"; + } + else + { + conditionStr = "(ctx->vu0_status & 0x1)"; + } + } break; } - int32_t offset = static_cast(branchInst.immediate) << 2; + int32_t offset = branchInst.simmediate << 2; uint32_t target = branchInst.address + 4 + offset; Symbol *sym = findSymbolByAddress(target); - std::string targetLabel; + std::string targetAction; if (sym && sym->isFunction) { - targetLabel = sym->name; + targetAction = fmt::format("{}(rdram, ctx); return;", sym->name); } else { - targetLabel = fmt::format("func_{:08X}", target); + targetAction = fmt::format("ctx->pc = 0x{:X}; return;", target); } - bool isLikely = (branchInst.opcode == OPCODE_BEQL || - branchInst.opcode == OPCODE_BNEL || - branchInst.opcode == OPCODE_BLEZL || - branchInst.opcode == OPCODE_BGTZL || - branchInst.rt == REGIMM_BLTZL || - branchInst.rt == REGIMM_BGEZL || - branchInst.rt == REGIMM_BLTZALL || - branchInst.rt == REGIMM_BGEZALL); + bool isLikely = (branchInst.opcode == OPCODE_BEQL || branchInst.opcode == OPCODE_BNEL || + branchInst.opcode == OPCODE_BLEZL || branchInst.opcode == OPCODE_BGTZL || + (branchInst.opcode == OPCODE_REGIMM && (branchInst.rt == REGIMM_BLTZL || branchInst.rt == REGIMM_BGEZL || branchInst.rt == REGIMM_BLTZALL || branchInst.rt == REGIMM_BGEZALL)) || + (branchInst.opcode == OPCODE_COP1 && branchInst.rs == COP1_BC && (branchInst.rt == COP1_BC_BCFL || branchInst.rt == COP1_BC_BCTL)) || + (branchInst.opcode == OPCODE_COP2 && branchInst.rs == COP2_BC && (branchInst.rt == COP2_BC_BCFL || branchInst.rt == COP2_BC_BCTL))); + + if (linkCode != "") + { + ss << " " << linkCode << "\n"; + } if (isLikely) { - // Likely branches only execute the delay slot if the branch is taken ss << " if (" << conditionStr << ") {\n"; - ss << " " << translateInstruction(delaySlot) << "\n"; - ss << " " << targetLabel << "(rdram, ctx);\n"; - ss << " return;\n"; + if (hasValidDelaySlot) + { + ss << " " << delaySlotCode << "\n"; + } + ss << " " << targetAction << "\n"; ss << " }\n"; } else { - // Regular branches always execute the delay slot - ss << " " << translateInstruction(delaySlot) << "\n"; + if (hasValidDelaySlot) + { + ss << " " << delaySlotCode << "\n"; + } ss << " if (" << conditionStr << ") {\n"; - ss << " " << targetLabel << "(rdram, ctx);\n"; - ss << " return;\n"; + ss << " " << targetAction << "\n"; ss << " }\n"; } } - + else + { + ss << " " << translateInstruction(branchInst) << "\n"; + if (hasValidDelaySlot) + { + ss << " " << delaySlotCode << "\n"; + } + } return ss.str(); } @@ -265,16 +274,16 @@ namespace ps2recomp ss << "#define PS2_VMULQ(a, q) _mm_mul_ps((__m128)(a), _mm_set1_ps(q))\n\n"; ss << "// Memory access helpers\n"; - ss << "#define READ8(addr) (*(uint8_t*)((rdram) + ((addr) & 0x1FFFFFF)))\n"; - ss << "#define READ16(addr) (*(uint16_t*)((rdram) + ((addr) & 0x1FFFFFF)))\n"; - ss << "#define READ32(addr) (*(uint32_t*)((rdram) + ((addr) & 0x1FFFFFF)))\n"; - ss << "#define READ64(addr) (*(uint64_t*)((rdram) + ((addr) & 0x1FFFFFF)))\n"; - ss << "#define READ128(addr) (*((__m128i*)((rdram) + ((addr) & 0x1FFFFFF))))\n"; - ss << "#define WRITE8(addr, val) (*(uint8_t*)((rdram) + ((addr) & 0x1FFFFFF)) = (val))\n"; - ss << "#define WRITE16(addr, val) (*(uint16_t*)((rdram) + ((addr) & 0x1FFFFFF)) = (val))\n"; - ss << "#define WRITE32(addr, val) (*(uint32_t*)((rdram) + ((addr) & 0x1FFFFFF)) = (val))\n"; - ss << "#define WRITE64(addr, val) (*(uint64_t*)((rdram) + ((addr) & 0x1FFFFFF)) = (val))\n"; - ss << "#define WRITE128(addr, val) (*((__m128i*)((rdram) + ((addr) & 0x1FFFFFF))) = (val))\n\n"; + ss << "#define READ8(addr) (*(uint8_t*)((rdram) + ((addr) & PS2_RAM_MASK)))\n"; + ss << "#define READ16(addr) (*(uint16_t*)((rdram) + ((addr) & PS2_RAM_MASK)))\n"; + ss << "#define READ32(addr) (*(uint32_t*)((rdram) + ((addr) & PS2_RAM_MASK)))\n"; + ss << "#define READ64(addr) (*(uint64_t*)((rdram) + ((addr) & PS2_RAM_MASK)))\n"; + ss << "#define READ128(addr) (*((__m128i*)((rdram) + ((addr) & PS2_RAM_MASK))))\n"; + ss << "#define WRITE8(addr, val) (*(uint8_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))\n"; + ss << "#define WRITE16(addr, val) (*(uint16_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))\n"; + ss << "#define WRITE32(addr, val) (*(uint32_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))\n"; + ss << "#define WRITE64(addr, val) (*(uint64_t*)((rdram) + ((addr) & PS2_RAM_MASK)) = (val))\n"; + ss << "#define WRITE128(addr, val) (*((__m128i*)((rdram) + ((addr) & PS2_RAM_MASK))) = (val))\n\n"; ss << "// Function lookup for indirect calls\n"; ss << "#define LOOKUP_FUNC(addr) runtime->lookupFunction(addr)\n\n"; @@ -399,6 +408,47 @@ namespace ps2recomp ss << "#define PS2_VCALLMS(addr) // VU0 microprogram calls not supported directly\n"; ss << "#define PS2_VCALLMSR(reg) // VU0 microprogram calls not supported directly\n"; + ss << "#define GPR_U32(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0U : ctx_ptr->r[reg_idx].m128i_u32[0])"; + ss << "#define GPR_S32(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0 : ctx_ptr->r[reg_idx].m128i_i32[0])"; + ss << "#define GPR_U64(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0ULL : ctx_ptr->r[reg_idx].m128i_u64[0])"; + ss << "#define GPR_S64(ctx_ptr, reg_idx) ((reg_idx == 0) ? 0LL : ctx_ptr->r[reg_idx].m128i_i64[0])"; + ss << "#define GPR_VEC(ctx_ptr, reg_idx) ((reg_idx == 0) ? _mm_setzero_si128() : ctx_ptr->r[reg_idx])"; + + ss << "#define SET_GPR_U32(ctx_ptr, reg_idx, val) \\\n"; + ss << " do \\\n"; + ss << " { \\\n"; + ss << " if (reg_idx != 0) \\\n"; + ss << " ctx_ptr->r[reg_idx] = _mm_set_epi32(0, 0, 0, (val)); \\\n"; + ss << " } while (0)\n"; + + ss << "#define SET_GPR_S32(ctx_ptr, reg_idx, val) \\\n"; + ss << " do \\\n"; + ss << " { \\\n"; + ss << " if (reg_idx != 0) \\\n"; + ss << " ctx_ptr->r[reg_idx] = _mm_set_epi32(0, 0, 0, (val)); \\\n"; + ss << " } while (0)\n"; + + ss << "#define SET_GPR_U64(ctx_ptr, reg_idx, val) \\\n"; + ss << " do \\\n"; + ss << " { \\\n"; + ss << " if (reg_idx != 0) \\\n"; + ss << " ctx_ptr->r[reg_idx] = _mm_set_epi64x(0, (val)); \\\n"; + ss << " } while (0)\n"; + + ss << "#define SET_GPR_S64(ctx_ptr, reg_idx, val) \\\n"; + ss << " do \\\n"; + ss << " { \\\n"; + ss << " if (reg_idx != 0) \\\n"; + ss << " ctx_ptr->r[reg_idx] = _mm_set_epi64x(0, (val)); \\\n"; + ss << " } while (0)\n"; + + ss << "#define SET_GPR_VEC(ctx_ptr, reg_idx, val) \\\n"; + ss << " do \\\n"; + ss << " { \\\n"; + ss << " if (reg_idx != 0) \\\n"; + ss << " ctx_ptr->r[reg_idx] = (val); \\\n"; + ss << " } while (0)\n"; + ss << "#endif // PS2_RUNTIME_MACROS_H\n"; return ss.str(); @@ -450,207 +500,77 @@ namespace ps2recomp { return translateMMIInstruction(inst); } - else if (inst.isVU) - { - return translateVUInstruction(inst); - } switch (inst.opcode) { case OPCODE_SPECIAL: return translateSpecialInstruction(inst); - + case OPCODE_REGIMM: + return translateRegimmInstruction(inst); + case OPCODE_COP0: + return translateCOP0Instruction(inst); + case OPCODE_COP1: + return translateFPUInstruction(inst); + case OPCODE_COP2: + return translateVUInstruction(inst); case OPCODE_ADDI: case OPCODE_ADDIU: if (inst.rt == 0) - { return "// NOP (addiu $zero, ...)"; - } - if (inst.modificationInfo.modifiesGPR) - { - return fmt::format("ctx->r[{}] = ADD32(ctx->r[{}], 0x{:X}); // Modifies GPR", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - } - return fmt::format("ctx->r[{}] = ADD32(ctx->r[{}], 0x{:X});", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_S32(ctx, {}, ADD32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate); case OPCODE_SLTI: - return fmt::format("ctx->r[{}] = (int32_t)ctx->r[{}] < (int32_t)0x{:X} ? 1 : 0;", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_U32(ctx, {}, SLT32(GPR_S32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate); case OPCODE_SLTIU: - return fmt::format("ctx->r[{}] = ctx->r[{}] < (uint32_t)0x{:X} ? 1 : 0;", - inst.rt, inst.rs, (uint32_t)static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_U32(ctx, {}, SLTU32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.immediate); case OPCODE_ANDI: - return fmt::format("ctx->r[{}] = AND32(ctx->r[{}], 0x{:X});", - inst.rt, inst.rs, inst.immediate); - + return fmt::format("SET_GPR_U32(ctx, {}, AND32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.immediate); case OPCODE_ORI: - return fmt::format("ctx->r[{}] = OR32(ctx->r[{}], 0x{:X});", - inst.rt, inst.rs, inst.immediate); - + return fmt::format("SET_GPR_U32(ctx, {}, OR32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.immediate); case OPCODE_XORI: - return fmt::format("ctx->r[{}] = XOR32(ctx->r[{}], 0x{:X});", - inst.rt, inst.rs, inst.immediate); - + return fmt::format("SET_GPR_U32(ctx, {}, XOR32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.immediate); case OPCODE_LUI: - return fmt::format("ctx->r[{}] = 0x{:X} << 16;", - inst.rt, inst.immediate); - + return fmt::format("SET_GPR_U32(ctx, {}, ((uint32_t){} << 16));", inst.rt, inst.immediate); case OPCODE_LB: - return fmt::format("ctx->r[{}] = (int32_t)(int8_t)READ8(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_S32(ctx, {}, (int8_t)READ8(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); case OPCODE_LH: - return fmt::format("ctx->r[{}] = (int32_t)(int16_t)READ16(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_S32(ctx, {}, (int16_t)READ16(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); case OPCODE_LW: - return fmt::format("ctx->r[{}] = READ32(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_U32(ctx, {}, READ32(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); case OPCODE_LBU: - return fmt::format("ctx->r[{}] = (uint32_t)READ8(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - + return fmt::format("SET_GPR_U32(ctx, {}, (uint8_t)READ8(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); case OPCODE_LHU: - return fmt::format("ctx->r[{}] = (uint32_t)READ16(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - - case OPCODE_SB: - return fmt::format("WRITE8(ADD32(ctx->r[{}], 0x{:X}), (uint8_t)ctx->r[{}]);", - inst.rs, static_cast(static_cast(inst.immediate)), inst.rt); - - case OPCODE_SH: - return fmt::format("WRITE16(ADD32(ctx->r[{}], 0x{:X}), (uint16_t)ctx->r[{}]);", - inst.rs, static_cast(static_cast(inst.immediate)), inst.rt); - - case OPCODE_SW: - return fmt::format("WRITE32(ADD32(ctx->r[{}], 0x{:X}), ctx->r[{}]);", - inst.rs, static_cast(static_cast(inst.immediate)), inst.rt); - - // 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, static_cast(static_cast(inst.immediate))); - } - return fmt::format("ctx->r[{}] = (__m128i)READ128(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(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, static_cast(static_cast(inst.immediate)), inst.rt); - } - return fmt::format("WRITE128(ADD32(ctx->r[{}], 0x{:X}), (__m128i)ctx->r[{}]);", - inst.rs, static_cast(static_cast(inst.immediate)), inst.rt); - - case OPCODE_LD: - return fmt::format("{{ uint64_t val = READ64(ADD32(ctx->r[{}], 0x{:X})); ctx->r[{}].m128i_u32[0] = (uint32_t)val; ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rs, static_cast(static_cast(inst.immediate)), inst.rt, inst.rt); - - case OPCODE_SD: - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; WRITE64(ADD32(ctx->r[{}], 0x{:X}), val); }}", - inst.rt, inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - - case OPCODE_SWC1: - return fmt::format("{{ float val = ctx->f[{}]; WRITE32(ADD32(ctx->r[{}], 0x{:X}), *(uint32_t*)&val); }}", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - - case OPCODE_LQC2: - return fmt::format("ctx->vu0_vf[{}] = (__m128)READ128(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(inst.immediate))); - - case OPCODE_SQC2: - return fmt::format("WRITE128(ADD32(ctx->r[{}], 0x{:X}), (__m128i)ctx->vu0_vf[{}]);", - inst.rs, static_cast(static_cast(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, static_cast(static_cast(inst.immediate)), inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, (uint16_t)READ16(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); case OPCODE_LWU: - return fmt::format("ctx->r[{}] = (uint32_t)READ32(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(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)"; - - // REGIMM special format (opcode=0x01) - case OPCODE_REGIMM: - switch (inst.rt) - { - case REGIMM_BLTZ: - case REGIMM_BGEZ: - case REGIMM_BLTZL: - case REGIMM_BGEZL: - case REGIMM_BLTZAL: - case REGIMM_BGEZAL: - case REGIMM_BLTZALL: - case REGIMM_BGEZALL: - { - uint32_t target = inst.address + 4 + (static_cast(static_cast(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 = (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;", - inst.rs, inst.immediate); - - case REGIMM_TGEI: - return fmt::format("if ((int32_t)ctx->r[{}] >= (int32_t)0x{:X}) {{ /* Trap */ }}", - inst.rs, static_cast(static_cast(inst.immediate))); - - case REGIMM_TGEIU: - return fmt::format("if (ctx->r[{}] >= (uint32_t)0x{:X}) {{ /* Trap */ }}", - inst.rs, (uint32_t)static_cast(static_cast(inst.immediate))); - - case REGIMM_TLTI: - return fmt::format("if ((int32_t)ctx->r[{}] < (int32_t)0x{:X}) {{ /* Trap */ }}", - inst.rs, static_cast(static_cast(inst.immediate))); - - case REGIMM_TLTIU: - return fmt::format("if (ctx->r[{}] < (uint32_t)0x{:X}) {{ /* Trap */ }}", - inst.rs, (uint32_t)static_cast(static_cast(inst.immediate))); - - case REGIMM_TEQI: - return fmt::format("if ((int32_t)ctx->r[{}] == (int32_t)0x{:X}) {{ /* Trap */ }}", - inst.rs, static_cast(static_cast(inst.immediate))); - - case REGIMM_TNEI: - return fmt::format("if ((int32_t)ctx->r[{}] != (int32_t)0x{:X}) {{ /* Trap */ }}", - inst.rs, static_cast(static_cast(inst.immediate))); - - default: - return fmt::format("// Unhandled REGIMM instruction: 0x{:X}", inst.rt); - } - - // MIPS-IV special format opcodes + return fmt::format("SET_GPR_U32(ctx, {}, READ32(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); + case OPCODE_SB: + return fmt::format("WRITE8(ADD32(GPR_U32(ctx, {}), {}), (uint8_t)GPR_U32(ctx, {}));", inst.rs, inst.simmediate, inst.rt); + case OPCODE_SH: + return fmt::format("WRITE16(ADD32(GPR_U32(ctx, {}), {}), (uint16_t)GPR_U32(ctx, {}));", inst.rs, inst.simmediate, inst.rt); + case OPCODE_SW: + return fmt::format("WRITE32(ADD32(GPR_U32(ctx, {}), {}), GPR_U32(ctx, {}));", inst.rs, inst.simmediate, inst.rt); + case OPCODE_LQ: + return fmt::format("SET_GPR_VEC(ctx, {}, READ128(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); + case OPCODE_SQ: + return fmt::format("WRITE128(ADD32(GPR_U32(ctx, {}), {}), GPR_VEC(ctx, {}));", inst.rs, inst.simmediate, inst.rt); + case OPCODE_LD: + return fmt::format("SET_GPR_U64(ctx, {}, READ64(ADD32(GPR_U32(ctx, {}), {})));", inst.rt, inst.rs, inst.simmediate); + case OPCODE_SD: + return fmt::format("WRITE64(ADD32(GPR_U32(ctx, {}), {}), GPR_U64(ctx, {}));", inst.rs, inst.simmediate, inst.rt); + case OPCODE_LWC1: + return fmt::format("{{ uint32_t val = READ32(ADD32(GPR_U32(ctx, {}), {})); ctx->f[{}] = *(float*)&val; }}", inst.rs, inst.simmediate, inst.rt); + case OPCODE_SWC1: + return fmt::format("{{ float val = ctx->f[{}]; WRITE32(ADD32(GPR_U32(ctx, {}), {}), *(uint32_t*)&val); }}", inst.rt, inst.rs, inst.simmediate); + case OPCODE_LDC2: // was OPCODE_LQC2 need to check + return fmt::format("ctx->vu0_vf[{}] = (__m128)READ128(ADD32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate); + case OPCODE_SDC2: // was OPCODE_SQC2 need to check + return fmt::format("WRITE128(ADD32(GPR_U32(ctx, {}), {}), (__m128i)ctx->vu0_vf[{}]);", inst.rs, inst.simmediate, inst.rt); + case OPCODE_DADDI: + case OPCODE_DADDIU: + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) + (uint64_t)(int64_t){});", inst.rt, inst.rs, inst.simmediate); + case OPCODE_J: + return fmt::format("// JAL 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_BEQ: case OPCODE_BNE: case OPCODE_BLEZ: @@ -659,21 +579,7 @@ namespace ps2recomp case OPCODE_BNEL: case OPCODE_BLEZL: case OPCODE_BGTZL: - // Already handled in branch delay slot logic return fmt::format("// Likely branch instruction at 0x{:X} - Handled by branch logic", inst.address); - - case OPCODE_DADDI: - return fmt::format("{{ int64_t a = ((int64_t)ctx->r[{}].m128i_i32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "int64_t res = a + (int64_t)(int16_t)0x{:X}; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)res; ctx->r[{}].m128i_u32[1] = (uint32_t)(res >> 32); }}", - inst.rs, inst.rs, inst.immediate, inst.rt, inst.rt); - - case OPCODE_DADDIU: - return fmt::format("{{ uint64_t a = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "uint64_t res = a + (uint64_t)(int16_t)0x{:X}; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)res; ctx->r[{}].m128i_u32[1] = (uint32_t)(res >> 32); }}", - inst.rs, inst.rs, inst.immediate, inst.rt, inst.rt); - case OPCODE_LDL: case OPCODE_LDR: case OPCODE_SDL: @@ -682,8 +588,11 @@ namespace ps2recomp case OPCODE_LWR: case OPCODE_SWL: case OPCODE_SWR: - return fmt::format("// Unaligned load/store instruction 0x{:X} not implemented", inst.opcode); - + return fmt::format("//Unhandled Unaligned load/store instruction 0x{:X} not implemented", inst.opcode); + case OPCODE_CACHE: + return "// CACHE instruction (ignored)"; + case OPCODE_PREF: + return "// PREF instruction (ignored)"; default: return fmt::format("// Unhandled opcode: 0x{:X}", inst.opcode); } @@ -695,196 +604,152 @@ namespace ps2recomp { case SPECIAL_SLL: if (inst.rd == 0 && inst.rt == 0 && inst.sa == 0) - { return "// NOP"; - } - return fmt::format("ctx->r[{}] = SLL32(ctx->r[{}], {});", - inst.rd, inst.rt, inst.sa); - + if (inst.rd == 0) + return ""; + return fmt::format("SET_GPR_U32(ctx, {}, SLL32(GPR_U32(ctx, {}), {}));", inst.rd, inst.rt, inst.sa); case SPECIAL_SRL: - return fmt::format("ctx->r[{}] = SRL32(ctx->r[{}], {});", - inst.rd, inst.rt, inst.sa); - + return fmt::format("SET_GPR_U32(ctx, {}, SRL32(GPR_U32(ctx, {}), {}));", inst.rd, inst.rt, inst.sa); case SPECIAL_SRA: - return fmt::format("ctx->r[{}] = SRA32(ctx->r[{}], {});", - inst.rd, inst.rt, inst.sa); - + return fmt::format("SET_GPR_S32(ctx, {}, SRA32(GPR_S32(ctx, {}), {}));", inst.rd, inst.rt, inst.sa); case SPECIAL_SLLV: - return fmt::format("ctx->r[{}] = SLL32(ctx->r[{}], ctx->r[{}] & 0x1F);", - inst.rd, inst.rt, inst.rs); - + return fmt::format("SET_GPR_U32(ctx, {}, SLL32(GPR_U32(ctx, {}), GPR_U32(ctx, {}) & 0x1F));", inst.rd, inst.rt, inst.rs); case SPECIAL_SRLV: - return fmt::format("ctx->r[{}] = SRL32(ctx->r[{}], ctx->r[{}] & 0x1F);", - inst.rd, inst.rt, inst.rs); - + return fmt::format("SET_GPR_U32(ctx, {}, SRL32(GPR_U32(ctx, {}), GPR_U32(ctx, {}) & 0x1F));", inst.rd, inst.rt, inst.rs); case SPECIAL_SRAV: - return fmt::format("ctx->r[{}] = SRA32(ctx->r[{}], ctx->r[{}] & 0x1F);", - inst.rd, inst.rt, inst.rs); - + return fmt::format("SET_GPR_S32(ctx, {}, SRA32(GPR_S32(ctx, {}), GPR_U32(ctx, {}) & 0x1F));", inst.rd, inst.rt, inst.rs); case SPECIAL_JR: - // Handled by branch delay slots return fmt::format("// JR ${} - Handled by branch logic", inst.rs); - case SPECIAL_JALR: - // Handled by branch delay slots return fmt::format("// JALR ${}, ${} - Handled by branch logic", inst.rd, inst.rs); - case SPECIAL_SYSCALL: - return fmt::format("// SYSCALL 0x{:X}", (inst.raw & 0x03FFFFC0) >> 6); - + return fmt::format("runtime->handleSyscall(rdram, ctx);"); case SPECIAL_BREAK: - return fmt::format("// BREAK 0x{:X}", (inst.raw & 0x03FFFFC0) >> 6); - + return fmt::format("runtime->handleBreak(rdram, ctx);"); case SPECIAL_SYNC: - return translateSYNC(inst); - + return "// SYNC instruction - memory barrier\n// In recompiled code, we don't need explicit memory barriers"; case SPECIAL_MFHI: - return fmt::format("ctx->r[{}] = ctx->hi;", inst.rd); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->hi);", inst.rd); case SPECIAL_MTHI: - return fmt::format("ctx->hi = ctx->r[{}];", inst.rs); - + return fmt::format("ctx->hi = GPR_U32(ctx, {});", inst.rs); case SPECIAL_MFLO: - return fmt::format("ctx->r[{}] = ctx->lo;", inst.rd); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->lo);", inst.rd); case SPECIAL_MTLO: - return fmt::format("ctx->lo = ctx->r[{}];", inst.rs); - - case SPECIAL_DSLLV: - return translateDSLLV(inst); - - case SPECIAL_DSRLV: - return translateDSRLV(inst); - - case SPECIAL_DSRAV: - return translateDSRAV(inst); - + return fmt::format("ctx->lo = GPR_U32(ctx, {});", inst.rs); case SPECIAL_MULT: - return fmt::format("{{ int64_t result = (int64_t)(int32_t)ctx->r[{}] * (int64_t)(int32_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - + return fmt::format("{{ int64_t result = (int64_t)GPR_S32(ctx, {}) * (int64_t)GPR_S32(ctx, {}); ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", inst.rs, inst.rt); case SPECIAL_MULTU: - return fmt::format("{{ uint64_t result = (uint64_t)ctx->r[{}] * (uint64_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - + return fmt::format("{{ uint64_t result = (uint64_t)GPR_U32(ctx, {}) * (uint64_t)GPR_U32(ctx, {}); ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", inst.rs, inst.rt); case SPECIAL_DIV: - return fmt::format("{{ if (ctx->r[{}] != 0) {{ ctx->lo = (uint32_t)((int32_t)ctx->r[{}] / (int32_t)ctx->r[{}]); ctx->hi = (uint32_t)((int32_t)ctx->r[{}] % (int32_t)ctx->r[{}]); }} }}", - inst.rt, inst.rs, inst.rt, inst.rs, inst.rt); - + return fmt::format("{{ int32_t divisor = GPR_S32(ctx, {}); if (divisor != 0) {{ ctx->lo = (uint32_t)(GPR_S32(ctx, {}) / divisor); ctx->hi = (uint32_t)(GPR_S32(ctx, {}) % divisor); }} else {{ ctx->lo = (GPR_S32(ctx,{}) < 0) ? 1 : -1; ctx->hi = GPR_S32(ctx,{}); }} }}", inst.rt, inst.rs, inst.rt, inst.rs, inst.rt); case SPECIAL_DIVU: - return fmt::format("{{ if (ctx->r[{}] != 0) {{ ctx->lo = ctx->r[{}] / ctx->r[{}]; ctx->hi = ctx->r[{}] % ctx->r[{}]; }} }}", - inst.rt, inst.rs, inst.rt, inst.rs, inst.rt); - + return fmt::format("{{ uint32_t divisor = GPR_U32(ctx, {}); if (divisor != 0) {{ ctx->lo = GPR_U32(ctx, {}) / divisor; ctx->hi = GPR_U32(ctx, {}) % divisor; }} else {{ ctx->lo = 0xFFFFFFFF; ctx->hi = GPR_U32(ctx,{}); }} }}", inst.rt, inst.rs, inst.rt, inst.rs, inst.rt); case SPECIAL_ADD: + return fmt::format("SET_GPR_S32(ctx, {}, ADD32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_ADDU: - return fmt::format("ctx->r[{}] = ADD32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ADD32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_SUB: + return fmt::format("SET_GPR_S32(ctx, {}, SUB32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_SUBU: - return fmt::format("ctx->r[{}] = SUB32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, SUB32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_AND: - return fmt::format("ctx->r[{}] = AND32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, AND32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_OR: - return fmt::format("ctx->r[{}] = OR32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, OR32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_XOR: - return fmt::format("ctx->r[{}] = XOR32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, XOR32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_NOR: - return fmt::format("ctx->r[{}] = NOR32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, NOR32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_SLT: - return fmt::format("ctx->r[{}] = SLT32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, SLT32(GPR_S32(ctx, {}), GPR_S32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_SLTU: - return fmt::format("ctx->r[{}] = SLTU32(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - // PS2-specific instructions + return fmt::format("SET_GPR_U32(ctx, {}, SLTU32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_MOVZ: - return fmt::format("if (ctx->r[{}] == 0) ctx->r[{}] = ctx->r[{}];", - inst.rt, inst.rd, inst.rs); - + return fmt::format("if (GPR_U32(ctx, {}) == 0) SET_GPR_U32(ctx, {}, GPR_U32(ctx, {}));", inst.rt, inst.rd, inst.rs); case SPECIAL_MOVN: - return fmt::format("if (ctx->r[{}] != 0) ctx->r[{}] = ctx->r[{}];", - inst.rt, inst.rd, inst.rs); - + return fmt::format("if (GPR_U32(ctx, {}) != 0) SET_GPR_U32(ctx, {}, GPR_U32(ctx, {}));", inst.rt, inst.rd, inst.rs); case SPECIAL_MFSA: - return fmt::format("ctx->r[{}] = ctx->sa;", inst.rd); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->sa);", inst.rd); case SPECIAL_MTSA: - return fmt::format("ctx->sa = ctx->r[{}] & 0x0F;", inst.rs); - - // Doubleword operations + return fmt::format("ctx->sa = GPR_U32(ctx, {}) & 0x1F;", inst.rs); case SPECIAL_DADD: - return translateDADD(inst); - case SPECIAL_DADDU: - return translateDADDU(inst); - + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) + GPR_U64(ctx, {}));", inst.rd, inst.rs, inst.rt); case SPECIAL_DSUB: - return translateDSUB(inst); - case SPECIAL_DSUBU: - return translateDSUBU(inst); - + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) - GPR_U64(ctx, {}));", inst.rd, inst.rs, inst.rt); case SPECIAL_DSLL: - return translateDSLL(inst); - + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) << {});", inst.rd, inst.rt, inst.sa); case SPECIAL_DSRL: - return translateDSRL(inst); - + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) >> {});", inst.rd, inst.rt, inst.sa); case SPECIAL_DSRA: - return translateDSRA(inst); - + return fmt::format("SET_GPR_S64(ctx, {}, GPR_S64(ctx, {}) >> {});", inst.rd, inst.rt, inst.sa); + case SPECIAL_DSLLV: + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) << (GPR_U32(ctx, {}) & 0x3F));", inst.rd, inst.rt, inst.rs); + case SPECIAL_DSRLV: + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) >> (GPR_U32(ctx, {}) & 0x3F));", inst.rd, inst.rt, inst.rs); + case SPECIAL_DSRAV: + return fmt::format("SET_GPR_S64(ctx, {}, GPR_S64(ctx, {}) >> (GPR_U32(ctx, {}) & 0x3F));", inst.rd, inst.rt, inst.rs); case SPECIAL_DSLL32: - return translateDSLL32(inst); - + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) << (32 + {}));", inst.rd, inst.rt, inst.sa); case SPECIAL_DSRL32: - return translateDSRL32(inst); - + return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) >> (32 + {}));", inst.rd, inst.rt, inst.sa); case SPECIAL_DSRA32: - return translateDSRA32(inst); - - // Trap instructions + return fmt::format("SET_GPR_S64(ctx, {}, GPR_S64(ctx, {}) >> (32 + {}));", inst.rd, inst.rt, inst.sa); case SPECIAL_TGE: - return fmt::format("if ((int32_t)ctx->r[{}] >= (int32_t)ctx->r[{}]) {{ /* Trap */ }}", - inst.rs, inst.rt); - + return fmt::format("if (GPR_S32(ctx, {}) >= GPR_S32(ctx, {})) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.rt); case SPECIAL_TGEU: - return fmt::format("if (ctx->r[{}] >= ctx->r[{}]) {{ /* Trap */ }}", - inst.rs, inst.rt); - + return fmt::format("if (GPR_U32(ctx, {}) >= GPR_U32(ctx, {})) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.rt); case SPECIAL_TLT: - return fmt::format("if ((int32_t)ctx->r[{}] < (int32_t)ctx->r[{}]) {{ /* Trap */ }}", - inst.rs, inst.rt); - + return fmt::format("if (GPR_S32(ctx, {}) < GPR_S32(ctx, {})) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.rt); case SPECIAL_TLTU: - return fmt::format("if (ctx->r[{}] < ctx->r[{}]) {{ /* Trap */ }}", - inst.rs, inst.rt); - + return fmt::format("if (GPR_U32(ctx, {}) < GPR_U32(ctx, {})) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.rt); case SPECIAL_TEQ: - return fmt::format("if (ctx->r[{}] == ctx->r[{}]) {{ /* Trap */ }}", - inst.rs, inst.rt); - + return fmt::format("if (GPR_U32(ctx, {}) == GPR_U32(ctx, {})) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.rt); case SPECIAL_TNE: - return fmt::format("if (ctx->r[{}] != ctx->r[{}]) {{ /* Trap */ }}", - inst.rs, inst.rt); - + return fmt::format("if (GPR_U32(ctx, {}) != GPR_U32(ctx, {})) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.rt); default: return fmt::format("// Unhandled SPECIAL instruction: 0x{:X}", inst.function); } } + std::string CodeGenerator::translateRegimmInstruction(const Instruction &inst) + { + switch (inst.rt) + { + case REGIMM_BLTZ: + case REGIMM_BGEZ: + case REGIMM_BLTZL: + case REGIMM_BGEZL: + case REGIMM_BLTZAL: + case REGIMM_BGEZAL: + case REGIMM_BLTZALL: + case REGIMM_BGEZALL: + { + uint32_t target = inst.address + 4 + (inst.simmediate << 2); + return fmt::format("// REGIMM branch instruction to 0x{:X} - Handled by branch logic", target); + } + case REGIMM_MTSAB: + return fmt::format("ctx->sa = (GPR_U32(ctx, {}) + {}) & 0xF;", inst.rs, inst.simmediate); + case REGIMM_MTSAH: + return fmt::format("ctx->sa = ((GPR_U32(ctx, {}) + {}) & 0x7) << 1;", inst.rs, inst.simmediate); + case REGIMM_TGEI: + return fmt::format("if (GPR_S32(ctx, {}) >= {}) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.simmediate); + case REGIMM_TGEIU: + return fmt::format("if (GPR_U32(ctx, {}) >= (uint32_t){}) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.simmediate); + case REGIMM_TLTI: + return fmt::format("if (GPR_S32(ctx, {}) < {}) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.simmediate); + case REGIMM_TLTIU: + return fmt::format("if (GPR_U32(ctx, {}) < (uint32_t){}) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.simmediate); + case REGIMM_TEQI: + return fmt::format("if (GPR_S32(ctx, {}) == {}) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.simmediate); + case REGIMM_TNEI: + return fmt::format("if (GPR_S32(ctx, {}) != {}) {{ runtime->handleTrap(rdram, ctx); }}", inst.rs, inst.simmediate); + default: + return fmt::format("// Unhandled REGIMM instruction: 0x{:X}", inst.rt); + } + } + std::string CodeGenerator::translateCOP0Instruction(const Instruction &inst) { uint32_t format = inst.rs; // Format field @@ -893,1612 +758,1114 @@ namespace ps2recomp switch (format) { - case COP0_MF: // MFC0 - Move From COP0 + case COP0_MF: switch (rd) { case COP0_REG_INDEX: - return fmt::format("ctx->r[{}] = ctx->cop0_index;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_index);", rt); case COP0_REG_RANDOM: - return fmt::format("ctx->r[{}] = ctx->cop0_random;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_random);", rt); case COP0_REG_ENTRYLO0: - return fmt::format("ctx->r[{}] = ctx->cop0_entrylo0;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_entrylo0);", rt); case COP0_REG_ENTRYLO1: - return fmt::format("ctx->r[{}] = ctx->cop0_entrylo1;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_entrylo1);", rt); case COP0_REG_CONTEXT: - return fmt::format("ctx->r[{}] = ctx->cop0_context;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_context);", rt); case COP0_REG_PAGEMASK: - return fmt::format("ctx->r[{}] = ctx->cop0_pagemask;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_pagemask);", rt); case COP0_REG_WIRED: - return fmt::format("ctx->r[{}] = ctx->cop0_wired;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_wired);", rt); case COP0_REG_BADVADDR: - return fmt::format("ctx->r[{}] = ctx->cop0_badvaddr;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_badvaddr);", rt); case COP0_REG_COUNT: - return fmt::format("ctx->r[{}] = ctx->cop0_count;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_count);", rt); case COP0_REG_ENTRYHI: - return fmt::format("ctx->r[{}] = ctx->cop0_entryhi;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_entryhi);", rt); case COP0_REG_COMPARE: - return fmt::format("ctx->r[{}] = ctx->cop0_compare;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_compare);", rt); case COP0_REG_STATUS: - return fmt::format("ctx->r[{}] = ctx->cop0_status;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_status);", rt); case COP0_REG_CAUSE: - return fmt::format("ctx->r[{}] = ctx->cop0_cause;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_cause);", rt); case COP0_REG_EPC: - return fmt::format("ctx->r[{}] = ctx->cop0_epc;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_epc);", rt); case COP0_REG_PRID: - return fmt::format("ctx->r[{}] = ctx->cop0_prid;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_prid);", rt); case COP0_REG_CONFIG: - return fmt::format("ctx->r[{}] = ctx->cop0_config;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_config);", rt); case COP0_REG_BADPADDR: - return fmt::format("ctx->r[{}] = ctx->cop0_badpaddr;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_badpaddr);", rt); case COP0_REG_DEBUG: - return fmt::format("ctx->r[{}] = ctx->cop0_debug;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_debug);", rt); case COP0_REG_PERF: - return fmt::format("ctx->r[{}] = ctx->cop0_perf;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_perf);", rt); case COP0_REG_TAGLO: - return fmt::format("ctx->r[{}] = ctx->cop0_taglo;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_taglo);", rt); case COP0_REG_TAGHI: - return fmt::format("ctx->r[{}] = ctx->cop0_taghi;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_taghi);", rt); case COP0_REG_ERROREPC: - return fmt::format("ctx->r[{}] = ctx->cop0_errorepc;", rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->cop0_errorepc);", rt); default: - return fmt::format("ctx->r[{}] = 0; // Unimplemented COP0 register {}", rt, rd); + return fmt::format("SET_GPR_U32(ctx, {}, 0); // Unimplemented COP0 register {}", rt, rd); } - - case COP0_MT: // MTC0 - Move To COP0 + case COP0_MT: switch (rd) { case COP0_REG_INDEX: - return fmt::format("ctx->cop0_index = ctx->r[{}] & 0x3F;", rt); // 6-bit field - + return fmt::format("ctx->cop0_index = GPR_U32(ctx, {}) & 0x3F;", rt); case COP0_REG_RANDOM: - return fmt::format("// MTC0 to RANDOM register ignored (read-only)"); - + return "// MTC0 to RANDOM register ignored (read-only)"; case COP0_REG_ENTRYLO0: - return fmt::format("ctx->cop0_entrylo0 = ctx->r[{}] & 0x3FFFFFFF;", rt); - + return fmt::format("ctx->cop0_entrylo0 = GPR_U32(ctx, {}) & 0x3FFFFFFF;", rt); case COP0_REG_ENTRYLO1: - return fmt::format("ctx->cop0_entrylo1 = ctx->r[{}] & 0x3FFFFFFF;", rt); - + return fmt::format("ctx->cop0_entrylo1 = GPR_U32(ctx, {}) & 0x3FFFFFFF;", rt); case COP0_REG_CONTEXT: - return fmt::format("ctx->cop0_context = (ctx->cop0_context & 0x7) | (ctx->r[{}] & ~0x7);", rt); - + return fmt::format("ctx->cop0_context = (ctx->cop0_context & 0xFF800000) | (GPR_U32(ctx, {}) & 0x7FFFFF);", rt); case COP0_REG_PAGEMASK: - return fmt::format("ctx->cop0_pagemask = ctx->r[{}] & 0x1FFE000;", rt); - + return fmt::format("ctx->cop0_pagemask = GPR_U32(ctx, {}) & 0x01FFE000;", rt); case COP0_REG_WIRED: - return fmt::format("ctx->cop0_wired = ctx->r[{}] & 0x3F; ctx->cop0_random = 47;", rt); - + return fmt::format("ctx->cop0_wired = GPR_U32(ctx, {}) & 0x3F; ctx->cop0_random = 47;", rt); case COP0_REG_BADVADDR: - return fmt::format("// MTC0 to BADVADDR register ignored (read-only)"); - + return "// MTC0 to BADVADDR register ignored (read-only)"; case COP0_REG_COUNT: - return fmt::format("ctx->cop0_count = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_count = GPR_U32(ctx, {});", rt); case COP0_REG_ENTRYHI: - return fmt::format("ctx->cop0_entryhi = ctx->r[{}] & 0xFFFFE0FF;", rt); - + return fmt::format("ctx->cop0_entryhi = GPR_U32(ctx, {}) & 0xC00000FF;", rt); case COP0_REG_COMPARE: - return fmt::format("ctx->cop0_compare = ctx->r[{}]; ctx->cop0_cause &= ~0x8000; // Clear timer interrupt", rt); - + return fmt::format("ctx->cop0_compare = GPR_U32(ctx, {}); ctx->cop0_cause &= ~0x8000;", rt); case COP0_REG_STATUS: - return fmt::format("ctx->cop0_status = ctx->r[{}] & 0xFF57FFFF;", rt); - + return fmt::format("ctx->cop0_status = GPR_U32(ctx, {}) & 0xFF57FFFF;", rt); case COP0_REG_CAUSE: - return fmt::format("ctx->cop0_cause = (ctx->cop0_cause & ~0x300) | (ctx->r[{}] & 0x300);", rt); - + return fmt::format("ctx->cop0_cause = (ctx->cop0_cause & ~0x00000300) | (GPR_U32(ctx, {}) & 0x00000300);", rt); case COP0_REG_EPC: - return fmt::format("ctx->cop0_epc = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_epc = GPR_U32(ctx, {});", rt); case COP0_REG_PRID: - return fmt::format("// MTC0 to PRID register ignored (read-only)"); - + return "// MTC0 to PRID register ignored (read-only)"; case COP0_REG_CONFIG: - return fmt::format("ctx->cop0_config = (ctx->cop0_config & ~0x7) | (ctx->r[{}] & 0x7);", rt); - + return fmt::format("ctx->cop0_config = (ctx->cop0_config & ~0x7) | (GPR_U32(ctx, {}) & 0x7);", rt); case COP0_REG_BADPADDR: - return fmt::format("// MTC0 to BADPADDR register ignored (read-only)"); - + return "// MTC0 to BADPADDR register ignored (read-only)"; case COP0_REG_DEBUG: - return fmt::format("ctx->cop0_debug = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_debug = GPR_U32(ctx, {});", rt); case COP0_REG_PERF: - return fmt::format("ctx->cop0_perf = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_perf = GPR_U32(ctx, {});", rt); case COP0_REG_TAGLO: - return fmt::format("ctx->cop0_taglo = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_taglo = GPR_U32(ctx, {});", rt); case COP0_REG_TAGHI: - return fmt::format("ctx->cop0_taghi = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_taghi = GPR_U32(ctx, {});", rt); case COP0_REG_ERROREPC: - return fmt::format("ctx->cop0_errorepc = ctx->r[{}];", rt); - + return fmt::format("ctx->cop0_errorepc = GPR_U32(ctx, {});", rt); default: return fmt::format("// Unimplemented MTC0 to COP0 {}", rd); } - - case COP0_CO: // COP0 co-processor operations + case COP0_BC: + return fmt::format("// BC0 (Condition: 0x{:X}) - Handled by branch logic", rt); + case COP0_CO: { - uint32_t function = inst.function; - + uint8_t function = FUNCTION(inst.raw); switch (function) { case COP0_CO_TLBR: return fmt::format("runtime->handleTLBR(rdram, ctx);"); - case COP0_CO_TLBWI: return fmt::format("runtime->handleTLBWI(rdram, ctx);"); - case COP0_CO_TLBWR: return fmt::format("runtime->handleTLBWR(rdram, ctx);"); - case COP0_CO_TLBP: return fmt::format("runtime->handleTLBP(rdram, ctx);"); - case COP0_CO_ERET: - return fmt::format("// ERET instruction - Return from Exception\n" - " if (ctx->cop0_status & 0x4) {{\n" - " ctx->pc = ctx->cop0_errorepc;\n" - " ctx->cop0_status &= ~0x4; // Clear ERL\n" - " }} else {{\n" - " ctx->pc = ctx->cop0_epc;\n" - " ctx->cop0_status &= ~0x2; // Clear EXL\n" - " }}\n" - " return;"); - + return fmt::format( + "if (ctx->cop0_status & 0x4) {{ \\\n" // Check ERL bit (bit 2) + " ctx->pc = ctx->cop0_errorepc; \\\n" + " ctx->cop0_status &= ~0x4; \\\n" // Clear ERL bit + "}} else {{ \\\n" // If ERL is not set, use EPC and clear EXL (bit 1) + " ctx->pc = ctx->cop0_epc; \\\n" // Note: If neither ERL/EXL set, behavior is undefined; using EPC is common. + " ctx->cop0_status &= ~0x2; \\\n" // Clear EXL bit + "}} \\\n" + "runtime->clearLLBit(ctx); \\\n" // Essential: Clear Load-Linked bit + "return;" // Stop execution in this recompiled block + ); case COP0_CO_EI: - return translateEI(inst); - + return fmt::format("ctx->cop0_status |= 0x1; // Enable interrupts"); case COP0_CO_DI: - return translateDI(inst); - + return fmt::format("ctx->cop0_status &= ~0x1; // Disable interrupts"); default: return fmt::format("// Unhandled COP0 CO-OP: 0x{:X}", function); } - break; } - default: return fmt::format("// Unhandled COP0 instruction format: 0x{:X}", format); } } + std::string CodeGenerator::translateFPUInstruction(const Instruction &inst) + { + uint8_t format = inst.rs; // Format field + uint32_t ft = inst.rt; // FPU source register + uint32_t fs = inst.rd; // FPU source register + uint32_t fd = inst.sa; // FPU destination register + uint32_t function = inst.function; + + switch (format) + { + case COP1_MF: + return fmt::format("SET_GPR_U32(ctx, {}, *(uint32_t*)&ctx->f[{}]);", ft, fs); + case COP1_MT: + return fmt::format("*(uint32_t*)&ctx->f[{}] = GPR_U32(ctx, {});", fs, ft); + case COP1_CF: + if (fs == 31) + return fmt::format("SET_GPR_U32(ctx, {}, ctx->fcr31);", ft); // FCR31 contains status/control + if (fs == 0) + return fmt::format("SET_GPR_U32(ctx, {}, 0x00000000);", ft); // FCR0 is the FPU implementation register + return fmt::format("SET_GPR_U32(ctx, {}, 0); // Unimplemented FCR{}", ft, fs); + case COP1_CT: + if (fs == 31) + return fmt::format("ctx->fcr31 = GPR_U32(ctx, {}) & 0x0183FFFF;", ft); + else + return fmt::format("// CTC1 to FCR{} ignored", fs); + return ""; + case COP1_BC: + return "// FPU branch instruction - handled elsewhere"; + case COP1_S: + switch (function) + { + case COP1_S_ADD: + return fmt::format("ctx->f[{}] = FPU_ADD_S(ctx->f[{}], ctx->f[{}]);", fd, fs, ft); + case COP1_S_SUB: + return fmt::format("ctx->f[{}] = FPU_SUB_S(ctx->f[{}], ctx->f[{}]);", fd, fs, ft); + case COP1_S_MUL: + return fmt::format("ctx->f[{}] = FPU_MUL_S(ctx->f[{}], ctx->f[{}]);", fd, fs, ft); + case COP1_S_DIV: + return fmt::format("ctx->f[{}] = FPU_DIV_S(ctx->f[{}], ctx->f[{}]);", fd, fs, ft); + case COP1_S_SQRT: + return fmt::format("ctx->f[{}] = FPU_SQRT_S(ctx->f[{}]);", fd, fs); + case COP1_S_ABS: + return fmt::format("ctx->f[{}] = FPU_ABS_S(ctx->f[{}]);", fd, fs); + case COP1_S_MOV: + return fmt::format("ctx->f[{}] = FPU_MOV_S(ctx->f[{}]);", fd, fs); + case COP1_S_NEG: + return fmt::format("ctx->f[{}] = FPU_NEG_S(ctx->f[{}]);", fd, fs); + case COP1_S_ROUND_W: + return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_ROUND_W_S(ctx->f[{}]);", fd, fs); + case COP1_S_TRUNC_W: + return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_TRUNC_W_S(ctx->f[{}]);", fd, fs); + case COP1_S_CEIL_W: + return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_CEIL_W_S(ctx->f[{}]);", fd, fs); + case COP1_S_FLOOR_W: + return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_FLOOR_W_S(ctx->f[{}]);", fd, fs); + case COP1_S_CVT_W: + return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_CVT_W_S(ctx->f[{}]);", fd, fs); + case COP1_S_RSQRT: + return fmt::format("ctx->f[{}] = 1.0f / sqrtf(ctx->f[{}]);", fd, fs); + case COP1_S_ADDA: + return fmt::format("ctx->f[31] = FPU_ADD_S(ctx->f[{}], ctx->f[{}]);", fs, ft); + case COP1_S_SUBA: + return fmt::format("ctx->f[31] = FPU_SUB_S(ctx->f[{}], ctx->f[{}]);", fs, ft); + case COP1_S_MULA: + return fmt::format("ctx->f[31] = FPU_MUL_S(ctx->f[{}], ctx->f[{}]);", fs, ft); + case COP1_S_MADD: + return fmt::format("ctx->f[{}] = FPU_ADD_S(ctx->f[31], FPU_MUL_S(ctx->f[{}], ctx->f[{}]));", fd, fs, ft); + case COP1_S_MSUB: + return fmt::format("ctx->f[{}] = FPU_SUB_S(ctx->f[31], FPU_MUL_S(ctx->f[{}], ctx->f[{}]));", fd, fs, ft); + case COP1_S_MADDA: + return fmt::format("ctx->f[31] = FPU_ADD_S(ctx->f[31], FPU_MUL_S(ctx->f[{}], ctx->f[{}]));", fs, ft); + case COP1_S_MSUBA: + return fmt::format("ctx->f[31] = FPU_SUB_S(ctx->f[31], FPU_MUL_S(ctx->f[{}], ctx->f[{}]));", fs, ft); + case COP1_S_MAX: + return fmt::format("ctx->f[{}] = std::max(ctx->f[{}], ctx->f[{}]);", fd, fs, ft); + case COP1_S_MIN: + return fmt::format("ctx->f[{}] = std::min(ctx->f[{}], ctx->f[{}]);", fd, fs, ft); + case COP1_S_C_F: + return fmt::format("ctx->fcr31 &= ~0x800000;"); + case COP1_S_C_UN: + return fmt::format("ctx->fcr31 = (FPU_C_UN_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_EQ: + return fmt::format("ctx->fcr31 = (FPU_C_EQ_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_UEQ: + return fmt::format("ctx->fcr31 = (FPU_C_UEQ_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_OLT: + return fmt::format("ctx->fcr31 = (FPU_C_OLT_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_ULT: + return fmt::format("ctx->fcr31 = (FPU_C_ULT_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_OLE: + return fmt::format("ctx->fcr31 = (FPU_C_OLE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_ULE: + return fmt::format("ctx->fcr31 = (FPU_C_ULE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_SF: + return fmt::format("ctx->fcr31 &= ~0x800000;"); + case COP1_S_C_NGLE: + return fmt::format("ctx->fcr31 = (FPU_C_NGLE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_SEQ: + return fmt::format("ctx->fcr31 = (FPU_C_SEQ_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_NGL: + return fmt::format("ctx->fcr31 = (FPU_C_NGL_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_LT: + return fmt::format("ctx->fcr31 = (FPU_C_LT_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_NGE: + return fmt::format("ctx->fcr31 = (FPU_C_NGE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_LE: + return fmt::format("ctx->fcr31 = (FPU_C_LE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + case COP1_S_C_NGT: + return fmt::format("ctx->fcr31 = (FPU_C_NGT_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", fs, ft); + default: + return fmt::format("// Unhandled FPU.S instruction: function 0x{:X}", function); + } + case COP1_W: + switch (function) + { + case COP1_W_CVT_S: + return fmt::format("ctx->f[{}] = FPU_CVT_S_W(*(int32_t*)&ctx->f[{}]);", fd, fs); + default: + return fmt::format("// Unhandled FPU.W instruction: function 0x{:X}", function); + } + default: + return fmt::format("// Unhandled FPU instruction: format 0x{:X}, function 0x{:X}", format, function); + } + } + std::string CodeGenerator::translateMMIInstruction(const Instruction &inst) { uint32_t function = inst.function; - + uint8_t rs = inst.rs; + uint8_t rt = inst.rt; + uint8_t rd = inst.rd; + uint8_t sa = inst.sa; switch (function) { - case MMI_MADD: - return fmt::format("{{ int64_t result = (int64_t)(((int64_t)ctx->hi << 32) | ctx->lo) + (int64_t)(int32_t)ctx->r[{}] * (int64_t)(int32_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - - case MMI_MADDU: - return fmt::format("{{ uint64_t result = (uint64_t)(((uint64_t)ctx->hi << 32) | ctx->lo) + (uint64_t)ctx->r[{}] * (uint64_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - - case MMI_PLZCW: - return fmt::format("ctx->r[{}] = __builtin_clz(ctx->r[{}]);", - inst.rd, inst.rs); - case MMI_MFHI1: - return fmt::format("ctx->r[{}] = ctx->hi;", inst.rd); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->hi1);", rd); case MMI_MTHI1: - return fmt::format("ctx->hi = ctx->r[{}];", inst.rs); - + return fmt::format("ctx->hi1 = GPR_U32(ctx, {});", rs); case MMI_MFLO1: - return fmt::format("ctx->r[{}] = ctx->lo;", inst.rd); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->lo1);", rd); case MMI_MTLO1: - return fmt::format("ctx->lo = ctx->r[{}];", inst.rs); - + return fmt::format("ctx->lo1 = GPR_U32(ctx, {});", rs); case MMI_MULT1: - return fmt::format("{{ int64_t result = (int64_t)(int32_t)ctx->r[{}] * (int64_t)(int32_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - + return fmt::format("{{ int64_t result = (int64_t)GPR_S32(ctx, {}) * (int64_t)GPR_S32(ctx, {}); ctx->lo1 = (uint32_t)result; ctx->hi1 = (uint32_t)(result >> 32); }}", rs, rt); case MMI_MULTU1: - return fmt::format("{{ uint64_t result = (uint64_t)ctx->r[{}] * (uint64_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - + return fmt::format("{{ uint64_t result = (uint64_t)GPR_U32(ctx, {}) * (uint64_t)GPR_U32(ctx, {}); ctx->lo1 = (uint32_t)result; ctx->hi1 = (uint32_t)(result >> 32); }}", rs, rt); case MMI_DIV1: - return fmt::format("{{ if (ctx->r[{}] != 0) {{ ctx->lo = (uint32_t)((int32_t)ctx->r[{}] / (int32_t)ctx->r[{}]); ctx->hi = (uint32_t)((int32_t)ctx->r[{}] % (int32_t)ctx->r[{}]); }} }}", - inst.rt, inst.rs, inst.rt, inst.rs, inst.rt); - + return fmt::format("{{ int32_t divisor = GPR_S32(ctx, {}); if (divisor != 0) {{ ctx->lo1 = (uint32_t)(GPR_S32(ctx, {}) / divisor); ctx->hi1 = (uint32_t)(GPR_S32(ctx, {}) % divisor); }} else {{ ctx->lo1= (GPR_S32(ctx,{}) < 0) ? 1 : -1; ctx->hi1=GPR_S32(ctx,{}); }} }}", rt, rs, rt, rs, rt); case MMI_DIVU1: - return fmt::format("{{ if (ctx->r[{}] != 0) {{ ctx->lo = ctx->r[{}] / ctx->r[{}]; ctx->hi = ctx->r[{}] % ctx->r[{}]; }} }}", - inst.rt, inst.rs, inst.rt, inst.rs, inst.rt); - + return fmt::format("{{ uint32_t divisor = GPR_U32(ctx, {}); if (divisor != 0) {{ ctx->lo1 = GPR_U32(ctx, {}) / divisor; ctx->hi1 = GPR_U32(ctx, {}) % divisor; }} else {{ ctx->lo1=0xFFFFFFFF; ctx->hi1=GPR_U32(ctx,{}); }} }}", rt, rs, rt, rs, rt); + case MMI_MADD: + return fmt::format("{{ int64_t acc = ((int64_t)ctx->hi << 32) | ctx->lo; int64_t prod = (int64_t)GPR_S32(ctx, {}) * (int64_t)GPR_S32(ctx, {}); int64_t result = acc + prod; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", rs, rt); + case MMI_MADDU: + return fmt::format("{{ uint64_t acc = ((uint64_t)ctx->hi << 32) | ctx->lo; uint64_t prod = (uint64_t)GPR_U32(ctx, {}) * (uint64_t)GPR_U32(ctx, {}); uint64_t result = acc + prod; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", rs, rt); + case MMI_MSUB: + return fmt::format("{{ int64_t acc = ((int64_t)ctx->hi << 32) | ctx->lo; int64_t prod = (int64_t)GPR_S32(ctx, {}) * (int64_t)GPR_S32(ctx, {}); int64_t result = acc - prod; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", rs, rt); + case MMI_MSUBU: + return fmt::format("{{ uint64_t acc = ((uint64_t)ctx->hi << 32) | ctx->lo; uint64_t prod = (uint64_t)GPR_U32(ctx, {}) * (uint64_t)GPR_U32(ctx, {}); uint64_t result = acc - prod; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", rs, rt); case MMI_MADD1: - return fmt::format("{{ int64_t result = (int64_t)(((int64_t)ctx->hi << 32) | ctx->lo) + (int64_t)(int32_t)ctx->r[{}] * (int64_t)(int32_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - + return fmt::format("{{ int64_t acc = ((int64_t)ctx->hi1 << 32) | ctx->lo1; int64_t prod = (int64_t)GPR_S32(ctx, {}) * (int64_t)GPR_S32(ctx, {}); int64_t result = acc + prod; ctx->lo1 = (uint32_t)result; ctx->hi1 = (uint32_t)(result >> 32); }}", rs, rt); case MMI_MADDU1: - return fmt::format("{{ uint64_t result = (uint64_t)(((uint64_t)ctx->hi << 32) | ctx->lo) + (uint64_t)ctx->r[{}] * (uint64_t)ctx->r[{}]; ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); }}", - inst.rs, inst.rt); - - // MMI0 functions (PADDW, PSUBW, etc.) + return fmt::format("{{ uint64_t acc = ((uint64_t)ctx->hi1 << 32) | ctx->lo1; uint64_t prod = (uint64_t)GPR_U32(ctx, {}) * (uint64_t)GPR_U32(ctx, {}); uint64_t result = acc + prod; ctx->lo1 = (uint32_t)result; ctx->hi1 = (uint32_t)(result >> 32); }}", rs, rt); + case MMI_PLZCW: + return fmt::format("{{ uint32_t val = GPR_U32(ctx, {}); SET_GPR_U32(ctx, {}, val == 0 ? 32 : __builtin_clz(val)); }}", rs, rd); + case MMI_PSLLH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_slli_epi16(GPR_VEC(ctx, {}), {}));", rd, rt, sa); + case MMI_PSRLH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_srli_epi16(GPR_VEC(ctx, {}), {}));", rd, rt, sa); + case MMI_PSRAH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_srai_epi16(GPR_VEC(ctx, {}), {}));", rd, rt, sa); + case MMI_PSLLW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_slli_epi32(GPR_VEC(ctx, {}), {}));", rd, rt, sa); + case MMI_PSRLW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_srli_epi32(GPR_VEC(ctx, {}), {}));", rd, rt, sa); + case MMI_PSRAW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_srai_epi32(GPR_VEC(ctx, {}), {}));", rd, rt, sa); case MMI_MMI0: - 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); - - case MMI0_PSUBW: - return fmt::format("ctx->r[{}] = PS2_PSUBW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PCGTW: - return fmt::format("ctx->r[{}] = PS2_PCGTW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PMAXW: - return fmt::format("ctx->r[{}] = PS2_PMAXW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PADDH: - return fmt::format("ctx->r[{}] = PS2_PADDH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PSUBH: - return fmt::format("ctx->r[{}] = PS2_PSUBH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PCGTH: - return fmt::format("ctx->r[{}] = PS2_PCGTH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PMAXH: - return fmt::format("ctx->r[{}] = PS2_PMAXH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PADDB: - return fmt::format("ctx->r[{}] = PS2_PADDB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PSUBB: - return fmt::format("ctx->r[{}] = PS2_PSUBB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PCGTB: - return fmt::format("ctx->r[{}] = PS2_PCGTB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PEXTLW: - return fmt::format("ctx->r[{}] = PS2_PEXTLW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PPACW: - return fmt::format("ctx->r[{}] = PS2_PPACW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PEXTLH: - return fmt::format("ctx->r[{}] = PS2_PEXTLH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PPACH: - return fmt::format("ctx->r[{}] = PS2_PPACH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PEXTLB: - return fmt::format("ctx->r[{}] = PS2_PEXTLB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI0_PPACB: - 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); - } - break; - - // MMI1 functions (PABSW, PCEQW, etc.) + return translateMMI0Instruction(inst); case MMI_MMI1: - switch (inst.sa) - { - case MMI1_PADDUW: - return fmt::format("ctx->r[{}] = PS2_PADDW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PSUBUW: - return fmt::format("ctx->r[{}] = PS2_PSUBW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PEXTUW: - return fmt::format("ctx->r[{}] = PS2_PEXTUW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PADDUH: - return fmt::format("ctx->r[{}] = PS2_PADDH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PSUBUH: - return fmt::format("ctx->r[{}] = PS2_PSUBH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PEXTUH: - return fmt::format("ctx->r[{}] = PS2_PEXTUH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - case MMI1_PABSW: - return fmt::format("ctx->r[{}] = PS2_PABSW(ctx->r[{}]);", - inst.rd, inst.rs); - - case MMI1_PABSH: - return fmt::format("ctx->r[{}] = PS2_PABSH(ctx->r[{}]);", - inst.rd, inst.rs); - - case MMI1_PCEQW: - return fmt::format("ctx->r[{}] = PS2_PCEQW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PCEQH: - return fmt::format("ctx->r[{}] = PS2_PCEQH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PCEQB: - return fmt::format("ctx->r[{}] = PS2_PCEQB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PMINW: - return fmt::format("ctx->r[{}] = PS2_PMINW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PMINH: - return fmt::format("ctx->r[{}] = PS2_PMINH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PADDUB: - return fmt::format("ctx->r[{}] = PS2_PADDB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PSUBUB: - return fmt::format("ctx->r[{}] = PS2_PSUBB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_PEXTUB: - return fmt::format("ctx->r[{}] = PS2_PEXTUB(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI1_QFSRV: - return translateQFSRV(inst); - - default: - return fmt::format("// Unhandled MMI1 function: 0x{:X}", inst.sa); - } - break; - - // MMI2 functions (PMADDW, PSLLVW, etc.) + return translateMMI1Instruction(inst); case MMI_MMI2: - switch (inst.sa) - { - case MMI2_PMADDW: - return translatePMADDW(inst); - - case MMI2_PSLLVW: - return fmt::format("ctx->r[{}] = PS2_PSLLVW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI2_PSRLVW: - return fmt::format("ctx->r[{}] = PS2_PSRLVW(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI2_PINTH: - return fmt::format("ctx->r[{}] = PS2_PINTH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI2_PAND: - return fmt::format("ctx->r[{}] = PS2_PAND(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI2_PXOR: - return fmt::format("ctx->r[{}] = PS2_PXOR(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI2_PMULTW: - return fmt::format("// PS2_PMULTW - Packed Multiply Word"); - - case MMI2_PDIVW: - return translatePDIVW(inst); - - case MMI2_PCPYLD: - return translatePCPYLD(inst); - - case MMI2_PMADDH: - return translatePMADDH(inst); - - 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); - - case MMI2_PREVH: - return translatePREVH(inst); - - case MMI2_PMULTH: - return translatePMULTH(inst); - - case MMI2_PDIVBW: - return translatePDIVBW(inst); - - case MMI2_PEXEW: - return translatePEXEW(inst); - - case MMI2_PROT3W: - return translatePROT3W(inst); - - default: - return fmt::format("// Unhandled MMI2 function: 0x{:X}", inst.sa); - } - break; - - // MMI3 functions (PMADDUW, PSRAVW, etc.) + return translateMMI2Instruction(inst); case MMI_MMI3: - switch (inst.sa) - { - case MMI3_POR: - return fmt::format("ctx->r[{}] = PS2_POR(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI3_PNOR: - return fmt::format("ctx->r[{}] = PS2_PNOR(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI3_PMADDUW: - 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[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI3_PINTEH: - return fmt::format("ctx->r[{}] = PS2_PINTEH(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rs, inst.rt); - - case MMI3_PMULTUW: - return translatePMULTUW(inst); - - case MMI3_PDIVUW: - return translatePDIVUW(inst); - - case MMI3_PCPYUD: - return translatePCPYUD(inst); - - case MMI3_PEXCH: - return translatePEXCH(inst); - - case MMI3_PCPYH: - return translatePCPYH(inst); - - 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); - } - break; - - // MMI_PMFHL functions + return translateMMI3Instruction(inst); case MMI_PMFHL: - switch (inst.pmfhlVariation) - { - case PMFHL_LW: - return fmt::format("ctx->r[{}] = PS2_PMFHL_LW(ctx->hi, ctx->lo);", - inst.rd); - - case PMFHL_UW: - return fmt::format("ctx->r[{}] = PS2_PMFHL_UW(ctx->hi, ctx->lo);", - inst.rd); - - case PMFHL_SLW: - return fmt::format("ctx->r[{}] = PS2_PMFHL_SLW(ctx->hi, ctx->lo);", - inst.rd); - - case PMFHL_LH: - return fmt::format("ctx->r[{}] = PS2_PMFHL_LH(ctx->hi, ctx->lo);", - inst.rd); - - case PMFHL_SH: - return fmt::format("ctx->r[{}] = PS2_PMFHL_SH(ctx->hi, ctx->lo);", - inst.rd); - - default: - return fmt::format("// Unknown PMFHL variation: 0x{:X}", inst.pmfhlVariation); - } - break; - + return translatePMFHLInstruction(inst); + case MMI_PMTHL: + return translatePMTHLInstruction(inst); default: - return fmt::format("// Unhandled MMI instruction: 0x{:X}", function); + return fmt::format("// Unhandled MMI instruction: function 0x{:X}", function); + } + } + + std::string CodeGenerator::translateMMI0Instruction(const Instruction &inst) + { + uint8_t subfunc = inst.sa; + uint8_t rs = inst.rs; + uint8_t rt = inst.rt; + uint8_t rd = inst.rd; + switch (subfunc) + { + case MMI0_PADDW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PADDW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PSUBW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSUBW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PCGTW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCGTW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PMAXW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMAXW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PADDH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PADDH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PSUBH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSUBH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PCGTH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCGTH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PMAXH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMAXH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PADDB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PADDB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PSUBB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSUBB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PCGTB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCGTB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PADDSW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_adds_epi32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PSUBSW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_subs_epi32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PEXTLW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTLW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PPACW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PPACW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PADDSH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_adds_epi16(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PSUBSH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_subs_epi16(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PEXTLH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTLH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PPACH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PPACH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PADDSB: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_adds_epi8(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PSUBSB: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_subs_epi8(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PEXTLB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTLB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PPACB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PPACB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI0_PEXT5: + return fmt::format("// Unhandled PEXT5 instruction: function 0x{:X}", subfunc); + case MMI0_PPAC5: + return fmt::format("// Unhandled PPAC5 instruction: function 0x{:X}", subfunc); + default: + return fmt::format("// Unhandled MMI0 instruction: function 0x{:X}", subfunc); + } + } + + std::string CodeGenerator::translateMMI1Instruction(const Instruction &inst) + { + uint8_t subfunc = inst.sa; + uint8_t rs = inst.rs; + uint8_t rt = inst.rt; + uint8_t rd = inst.rd; + switch (subfunc) + { + case MMI1_PABSW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PABSW(GPR_VEC(ctx, {})));", rd, rs); + case MMI1_PCEQW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCEQW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PMINW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMINW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PADSBH: + return fmt::format("// Unhandled PADSBH instruction: function 0x{:X}", subfunc); + case MMI1_PABSH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PABSH(GPR_VEC(ctx, {})));", rd, rs); + case MMI1_PCEQH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCEQH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PMINH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMINH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PCEQB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCEQB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PADDUW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_add_epi32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PSUBUW: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_sub_epi32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PEXTUW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTUW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PADDUH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_add_epi16(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PSUBUH: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_sub_epi16(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PEXTUH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTUH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PADDUB: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_adds_epu8(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PSUBUB: + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_subs_epu8(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_PEXTUB: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTUB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI1_QFSRV: + return translateQFSRV(inst); + default: + return fmt::format("// Unhandled MMI1 instruction: function 0x{:X}", subfunc); + } + } + + std::string CodeGenerator::translateMMI2Instruction(const Instruction &inst) + { + uint8_t subfunc = inst.sa; + uint8_t rs = inst.rs; + uint8_t rt = inst.rt; + uint8_t rd = inst.rd; + switch (subfunc) + { + case MMI2_PMADDW: + return translatePMADDW(inst); + case MMI2_PSLLVW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSLLVW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI2_PSRLVW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSRLVW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI2_PMSUBW: + return fmt::format("// Unhandled PMSUBW instruction: function 0x{:X}", subfunc); + case MMI2_PMFHI: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->hi);", rd); + case MMI2_PMFLO: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->lo);", rd); + case MMI2_PINTH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PINTH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI2_PMULTW: + return fmt::format("// Unhandled PMULTW instruction: function 0x{:X}", subfunc); + case MMI2_PDIVW: + return translatePDIVW(inst); + case MMI2_PCPYLD: + return translatePCPYLD(inst); + case MMI2_PAND: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PAND(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI2_PXOR: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PXOR(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI2_PMADDH: + return translatePMADDH(inst); + case MMI2_PHMADH: + return translatePHMADH(inst); + case MMI2_PMSUBH: + return fmt::format("// Unhandled PMSUBH instruction: function 0x{:X}", subfunc); + case MMI2_PHMSBH: + return fmt::format("// Unhandled PHMSBH instruction: function 0x{:X}", subfunc); + case MMI2_PEXEH: + return translatePEXEH(inst); + case MMI2_PREVH: + return translatePREVH(inst); + case MMI2_PMULTH: + return translatePMULTH(inst); + case MMI2_PDIVBW: + return translatePDIVBW(inst); + case MMI2_PEXEW: + return translatePEXEW(inst); + case MMI2_PROT3W: + return translatePROT3W(inst); + default: + return fmt::format("// Unhandled MMI2 instruction: function 0x{:X}", subfunc); + } + } + + std::string CodeGenerator::translateMMI3Instruction(const Instruction &inst) + { + uint8_t subfunc = inst.sa; + uint8_t rs = inst.rs; + uint8_t rt = inst.rt; + uint8_t rd = inst.rd; + switch (subfunc) + { + case MMI3_PMADDUW: + return fmt::format("Unhandled PMADDUW instruction: function 0x{:X}", subfunc); + case MMI3_PSRAVW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSRAVW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI3_PMTHI: + return translatePMTHI(inst); + case MMI3_PMTLO: + return translatePMTLO(inst); + case MMI3_PINTEH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PINTEH(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI3_PMULTUW: + return translatePMULTUW(inst); + case MMI3_PDIVUW: + return translatePDIVUW(inst); + case MMI3_PCPYUD: + return translatePCPYUD(inst); + case MMI3_POR: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_POR(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI3_PNOR: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PNOR(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt); + case MMI3_PEXCH: + return translatePEXCH(inst); + case MMI3_PCPYH: + return translatePCPYH(inst); + case MMI3_PEXCW: + return translatePEXCW(inst); + default: + return fmt::format("// Unhandled MMI3 instruction: function 0x{:X}", subfunc); + } + } + + std::string CodeGenerator::translatePMFHLInstruction(const Instruction &inst) + { + uint8_t subfunc = inst.sa; + switch (subfunc) + { + case PMFHL_LW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMFHL_LW(ctx->hi, ctx->lo));", inst.rd); + case PMFHL_UW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMFHL_UW(ctx->hi, ctx->lo));", inst.rd); + case PMFHL_SLW: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMFHL_SLW(ctx->hi, ctx->lo));", inst.rd); + case PMFHL_LH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMFHL_LH(ctx->hi, ctx->lo));", inst.rd); + case PMFHL_SH: + return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PMFHL_SH(ctx->hi, ctx->lo));", inst.rd); + default: + return fmt::format("// Unhandled PMFHL instruction: function 0x{:X}", subfunc); + } + } + + std::string CodeGenerator::translatePMTHLInstruction(const Instruction &inst) + { + uint8_t subfunc = inst.sa; + switch (subfunc) + { + case PMFHL_LW: + return fmt::format("{{ __m128i val = GPR_VEC(ctx, {}); ctx->lo = _mm_extract_epi32(val, 0); ctx->hi = _mm_extract_epi32(val, 1); }}", inst.rs); + default: + return fmt::format("// Unhandled PMTHL instruction: function 0x{:X}", subfunc); } } std::string CodeGenerator::translateVUInstruction(const Instruction &inst) { - uint32_t rs = inst.rs; + uint8_t format = inst.rs; // Use parsed rs field for COP2 format + uint8_t rt = inst.rt; + uint8_t rd = inst.rd; - switch (rs) + switch (format) { case COP2_QMFC2: - return fmt::format("ctx->r[{}] = (__m128i)ctx->vu0_vf[{}];", - inst.rt, inst.rd); - + return fmt::format("SET_GPR_VEC(ctx, {}, (__m128i)ctx->vu0_vf[{}]);", rt, rd); case COP2_CFC2: - switch (inst.rd) + { + switch (rd) // Control register number is in rd { case VU0_CR_STATUS: - return fmt::format("ctx->r[{}] = ctx->vu0_status;", inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_status);", rt); case VU0_CR_MAC: - return fmt::format("ctx->r[{}] = ctx->vu0_mac_flags;", inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_mac_flags);", rt); case VU0_CR_CLIP: - return fmt::format("ctx->r[{}] = ctx->vu0_clip_flags;", inst.rt); - - case VU0_CR_CMSAR0: - return fmt::format("ctx->r[{}] = ctx->vu0_cmsar0;", inst.rt); - - case VU0_CR_FBRST: - return fmt::format("ctx->r[{}] = ctx->vu0_fbrst;", inst.rt); - + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags);", rt); + case VU0_CR_R: + return fmt::format("SET_GPR_VEC(ctx, {}, (__m128i)ctx->vu0_r);", rt); + case VU0_CR_I: + return fmt::format("SET_GPR_U32(ctx, {}, *(uint32_t*)&ctx->vu0_i);", rt); default: - return fmt::format("// Unhandled CFC2 VU control register: {}", inst.rd); + return fmt::format("// Unhandled CFC2 VU CReg: {}", rd); } - + } case COP2_QMTC2: - return fmt::format("ctx->vu0_vf[{}] = (__m128)ctx->r[{}];", - inst.rd, inst.rt); - + return fmt::format("ctx->vu0_vf[{}] = (__m128)GPR_VEC(ctx, {});", rd, rt); case COP2_CTC2: - if (inst.rd == 0) - { - return fmt::format("ctx->vu0_status = ctx->r[{}] & 0xFFFF;", inst.rt); - } - else - { - return fmt::format("// Unhandled CTC2 VU control register: {}", inst.rd); - } - - 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, static_cast(static_cast(inst.immediate)), inst.rt); - } - else // Load operation - { - return fmt::format("ctx->vu0_vf[{}] = (__m128)READ128(ADD32(ctx->r[{}], 0x{:X}));", - inst.rt, inst.rs, static_cast(static_cast(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) - { - case COP2_BCF: // BC2F - Branch on VU0 false - return fmt::format("if ((ctx->vu0_status & 0x1) == 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCT: // BC2T - Branch on VU0 true - return fmt::format("if ((ctx->vu0_status & 0x1) != 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCFL: // BC2FL - Branch on VU0 false likely - return fmt::format("if ((ctx->vu0_status & 0x1) == 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCTL: // BC2TL - Branch on VU0 true likely - return fmt::format("if ((ctx->vu0_status & 0x1) != 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCEF: // BC2EF - Branch on VU0 equal flag false (0x5) - return fmt::format("if ((ctx->vu0_status & 0x2) == 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCET: // BC2ET - Branch on VU0 equal flag true (0x6) - return fmt::format("if ((ctx->vu0_status & 0x2) != 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCEFL: // BC2EFL - Branch on VU0 equal flag false likely (0x6) - return fmt::format("if ((ctx->vu0_status & 0x2) == 0) {{ /* branch logic handled elsewhere */ }}"); - - case COP2_BCETL: // BC2ETL - Branch on VU0 equal flag true likely (0x7) - return fmt::format("if ((ctx->vu0_status & 0x2) != 0) {{ /* branch logic handled elsewhere */ }}"); - - default: - return fmt::format("// Unhandled BC2 instruction: rt=0x{:X}", inst.rt); - } - - case COP2_MTVUCF: - return fmt::format("ctx->vu0_cf[{}] = ctx->r[{}];", inst.rd, 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]; " - "float z = ctx->vu0_vf[{}].m128_f32[2]; " - "float w = ctx->vu0_vf[{}].m128_f32[3]; " - "// Calculate VU0 clipping flags and store in ctx->vu0_clip_flags \n" - "ctx->vu0_clip_flags = 0; " - "if (w < -x) ctx->vu0_clip_flags |= 0x01; " - "if (w < x) ctx->vu0_clip_flags |= 0x02; " - "if (w < -y) ctx->vu0_clip_flags |= 0x04; " - "if (w < y) ctx->vu0_clip_flags |= 0x08; " - "if (w < -z) ctx->vu0_clip_flags |= 0x10; " - "if (w < z) ctx->vu0_clip_flags |= 0x20; }}", - inst.rt, inst.rt, inst.rt, inst.rt); - - case COP2_VLDQ: - if (inst.function & 0x10) - { - return fmt::format("{{ uint32_t addr = (ctx->r[{}].m128_i32[0] - 16) & 0x3FF; " - "WRITE128(addr, (__m128i)ctx->vu0_vf[{}]); " - "ctx->r[{}].m128_i32[0] = addr; }}", - inst.rs, inst.rt, inst.rs); - } - else - { - return fmt::format("{{ uint32_t addr = (ctx->r[{}].m128_i32[0] - 16) & 0x3FF; " - "ctx->vu0_vf[{}] = (__m128)READ128(addr); " - "ctx->r[{}].m128_i32[0] = addr; }}", - inst.rs, inst.rt, inst.rs); - } - - case COP2_CO: - switch (inst.function) - { - case VU0_VADD: - return fmt::format("ctx->vu0_vf[{}] = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", - inst.rd, inst.rs, inst.rt); - - case VU0_VSUB: - return fmt::format("ctx->vu0_vf[{}] = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", - inst.rd, inst.rs, inst.rt); - - case VU0_VMUL: - return fmt::format("ctx->vu0_vf[{}] = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", - inst.rd, inst.rs, inst.rt); - - case VU0_VDIV: - return fmt::format("{{ float q = 0.0f; " - "if (ctx->vu0_vf[{}].m128_f32[{}] != 0.0f) {{ " - "q = ctx->vu0_vf[{}].m128_f32[{}] / ctx->vu0_vf[{}].m128_f32[{}]; }} " - "ctx->vu0_q = q; }}", - inst.rt, inst.rd, inst.rs, inst.rd, inst.rt, inst.rd); - - case VU0_VSQRT: - return fmt::format("{{ float fsrc = ctx->vu0_vf[{}].m128_f32[{}]; " - "ctx->vu0_q = sqrtf(fsrc); }}", - inst.rt, inst.rd); - - case VU0_VRSQRT: - return fmt::format("{{ float fsrc = ctx->vu0_vf[{}].m128_f32[{}]; " - "if (fsrc != 0.0f) {{ ctx->vu0_q = 1.0f / sqrtf(fsrc); }} " - "else {{ ctx->vu0_q = INFINITY; }} }}", - inst.rt, inst.rd); - - case VU0_VIADDI: - return fmt::format("ctx->vu0_i += {};", - static_cast(static_cast(inst.immediate))); - - case VU0_VIAND: - return fmt::format("ctx->vu0_i &= _mm_cvtss_f32(_mm_castsi128_ps(_mm_cvtsi32_si128(ctx->vu0_vf[{}].m128_i32[{}])));", - inst.rt, inst.rd); - - case VU0_VIOR: - return fmt::format("ctx->vu0_i |= _mm_cvtss_f32(_mm_castsi128_ps(_mm_cvtsi32_si128(ctx->vu0_vf[{}].m128_i32[{}])));", - inst.rt, inst.rd); - - case VU0_VILWR: - return fmt::format("{{ uint32_t addr = (_mm_extract_epi32(ctx->vu0_vf[{}], {}) + ctx->vu0_i) & 0xFFF; " - "ctx->vu0_vf[{}].m128_i32[{}] = *(int32_t*)(rdram + addr); }}", - inst.rt, inst.rd, inst.rt, inst.rd); - - case VU0_VISWR: - return fmt::format("{{ uint32_t addr = (_mm_extract_epi32(ctx->vu0_vf[{}], {}) + ctx->vu0_i) & 0xFFF; " - "*(int32_t*)(rdram + addr) = ctx->vu0_vf[{}].m128_i32[{}]; }}", - inst.rt, inst.rd, inst.rt, inst.rd); - - case VU0_VCALLMS: - return fmt::format("// Calls VU0 microprogram at address {} - not implemented in recompiled code", - inst.immediate); - - case VU0_VRGET: - return fmt::format("ctx->vu0_vf[{}] = ctx->vu0_r; // Get VU0 R register", - inst.rd); - - case VU0_VMULQ: - return fmt::format("ctx->vu0_vf[{}] = PS2_VMULQ(ctx->vu0_vf[{}], ctx->vu0_q);", - inst.rd, inst.rs); - - case VU0_VIADD: - return fmt::format("ctx->vu0_i = _mm_extract_ps(ctx->vu0_vf[{}], {}) + " - "_mm_extract_ps(ctx->vu0_vf[{}], {});", - inst.rs, inst.rd, inst.rt, inst.rd); - - case VU0_VISUB: - return fmt::format("ctx->vu0_i = _mm_extract_ps(ctx->vu0_vf[{}], {}) - " - "_mm_extract_ps(ctx->vu0_vf[{}], {});", - inst.rs, inst.rd, inst.rt, inst.rd); - - default: - return fmt::format("// Unhandled VU0 macro instruction: 0x{:X}", inst.function); - } - break; - - case COP2_CTCVU: - switch (inst.rd) + switch (rd) // Control register number is in rd { case VU0_CR_STATUS: - return fmt::format("ctx->vu0_control = ctx->r[{}] & 0xFFFF; // Set VU0 status/control register", inst.rt); - + return fmt::format("ctx->vu0_status = GPR_U32(ctx, {}) & 0xFFFF;", rt); case VU0_CR_MAC: - return fmt::format("ctx->vu0_mac_flags = ctx->r[{}]; // Set VU0 MAC flags register", inst.rt); - + return fmt::format("ctx->vu0_mac_flags = GPR_U32(ctx, {});", rt); case VU0_CR_CLIP: - return fmt::format("ctx->vu0_clip_flags = ctx->r[{}]; // Set VU0 clipping flags register", inst.rt); - - case VU0_CR_CMSAR0: - return fmt::format("ctx->vu0_cmsar0 = ctx->r[{}]; // Set VU0 microprogram start address", - inst.rt); - - case VU0_CR_FBRST: - return fmt::format("// VU0 FBRST register - handles VU/VIF resets\n" - " // Bit 0: Reset VIF0, Bit 1: Reset VIF1\n" - " // Bit 8: Reset VU0, Bit 9: Reset VU1\n" - " ctx->vu0_fbrst = ctx->r[{}] & 0x0303;", - inst.rt); - + return fmt::format("ctx->vu0_clip_flags = GPR_U32(ctx, {});", rt); + case VU0_CR_R: + return fmt::format("ctx->vu0_r = (__m128)GPR_VEC(ctx, {});", rt); + case VU0_CR_I: + return fmt::format("ctx->vu0_i = *(float*)&GPR_U32(ctx, {});", rt); default: - return fmt::format("// Unhandled CTCVU VU control register: {}", inst.rd); + return fmt::format("// Unhandled CTC2 VU CReg: {}", rd); } - - case COP2_VU0OPS: - switch (inst.function) + } + case COP2_BC: + return fmt::format("// BC2 (Condition: 0x{:X}) - Handled by branch logic", rt); + case COP2_CO: + 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: + { + uint8_t vu_func = inst.function; + if (vu_func >= 0x3C) // Special2 Table { - case VU0OPS_QMFC2_NI: // 0x00 - Non-incrementing QMFC2 - return fmt::format("ctx->r[{}] = (__m128i)ctx->vu0_vf[{}]; // Non-incrementing QMFC2", - inst.rt, inst.rd); - - case VU0OPS_QMFC2_I: // 0x01 - Incrementing QMFC2 - return fmt::format("{{ ctx->r[{}] = (__m128i)ctx->vu0_vf[{}]; " - "ctx->vu0_vf[{}] = (__m128)_mm_setzero_si128(); }} // Incrementing QMFC2", - inst.rt, inst.rd, inst.rd); - - case VU0OPS_QMTC2_NI: // 0x02 - Non-incrementing QMTC2 - return fmt::format("ctx->vu0_vf[{}] = (__m128)ctx->r[{}]; // Non-incrementing QMTC2", - inst.rd, inst.rt); - - case VU0OPS_QMTC2_I: // 0x03 - Incrementing QMTC2 - return fmt::format("{{ ctx->vu0_vf[{}] = (__m128)ctx->r[{}]; " - "ctx->r[{}] = _mm_setzero_si128(); }} // Incrementing QMTC2", - inst.rd, inst.rt, inst.rt); - - case VU0OPS_VMFIR: // 0x04 - Move From Integer Register - return fmt::format("{{ int val = ctx->r[{}].m128i_i32[0]; " - "ctx->vu0_vf[{}] = (__m128)_mm_set1_epi32(val); }}", - inst.rt, inst.rd); - - case VU0OPS_VXITOP: // 0x08 - Execute Interrupt on VU0 - return fmt::format("// VXITOP - VU0 Interrupt operation not implemented"); - - case VU0OPS_VWAITQ: // 0x3C - Wait for Q register operations to complete TODO check this better - 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); + switch (vu_func) + { + case VU0_S2_VDIV: + return translateVU_VDIV(inst); + case VU0_S2_VSQRT: + return translateVU_VSQRT(inst); + case VU0_S2_VRSQRT: + return translateVU_VRSQRT(inst); + case VU0_S2_VWAITQ: + return fmt::format("// Unhandled VU0 VWAITQ instruction: 0x{:X}", vu_func); + case VU0_S2_VMTIR: + return translateVU_VMTIR(inst); + case VU0_S2_VMFIR: + return translateVU_VMFIR(inst); + case VU0_S2_VILWR: + return translateVU_VILWR(inst); + case VU0_S2_VISWR: + return translateVU_VISWR(inst); + case VU0_S2_VRNEXT: + return translateVU_VRNEXT(inst); + case VU0_S2_VRGET: + return translateVU_VRGET(inst); + case VU0_S2_VRINIT: + return translateVU_VRINIT(inst); + case VU0_S2_VRXOR: + return translateVU_VRXOR(inst); + case VU0_S2_VABS: + return fmt::format("ctx->vu0_vf[{}] = _mm_andnot_ps(_mm_set1_ps(-0.0f), ctx->vu0_vf[{}]);", inst.rt, inst.rs); // FT, FS + case VU0_S2_VNOP: + return fmt::format("// NOP operation, no action needed for VU0"); // No operation + case VU0_S2_VMOVE: + return fmt::format("ctx->vu0_vf[{}] = ctx->vu0_vf[{}];", inst.rt, inst.rs); // FT, FS + case VU0_S2_VMR32: + return fmt::format("ctx->vu0_vf[{}] = _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,1));", inst.rt, inst.rs, inst.rs); // FT, FS + default: + return fmt::format("// Unhandled VU0 Special2 function: 0x{:X}", vu_func); + } } - + else // Special1 Table + { + switch (vu_func) + { + case VU0_S1_VADDx: + case VU0_S1_VADDy: + case VU0_S1_VADDz: + case VU0_S1_VADDw: + return translateVU_VADD_Field(inst); + case VU0_S1_VSUBx: + case VU0_S1_VSUBy: + case VU0_S1_VSUBz: + case VU0_S1_VSUBw: + return translateVU_VSUB_Field(inst); + case VU0_S1_VMULx: + case VU0_S1_VMULy: + case VU0_S1_VMULz: + case VU0_S1_VMULw: + return translateVU_VMUL_Field(inst); + case VU0_S1_VADD: + return translateVU_VADD(inst); + case VU0_S1_VSUB: + return translateVU_VSUB(inst); + case VU0_S1_VMUL: + return translateVU_VMUL(inst); + case VU0_S1_VIADD: + return translateVU_VIADD(inst); + case VU0_S1_VISUB: + return translateVU_VISUB(inst); + case VU0_S1_VIADDI: + return translateVU_VIADDI(inst); + case VU0_S1_VIAND: + return translateVU_VIAND(inst); + case VU0_S1_VIOR: + return translateVU_VIOR(inst); + case VU0_S1_VCALLMS: + return translateVU_VCALLMS(inst); + case VU0_S1_VCALLMSR: + return translateVU_VCALLMSR(inst); + case VU0_S1_VADDq: + return fmt::format("ctx->vu0_vf[{}] = PS2_VADD(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q));", inst.rd, inst.rs); + case VU0_S1_VSUBq: + return fmt::format("ctx->vu0_vf[{}] = PS2_VSUB(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q));", inst.rd, inst.rs); + case VU0_S1_VMULq: + return fmt::format("ctx->vu0_vf[{}] = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q));", inst.rd, inst.rs); + case VU0_S1_VADDi: + return fmt::format("ctx->vu0_vf[{}] = PS2_VADD(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i));", inst.rd, inst.rs); + case VU0_S1_VSUBi: + return fmt::format("ctx->vu0_vf[{}] = PS2_VSUB(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i));", inst.rd, inst.rs); + case VU0_S1_VMULi: + return fmt::format("ctx->vu0_vf[{}] = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i));", inst.rd, inst.rs); + default: + return fmt::format("// Unhandled VU0 Special1 function: 0x{:X}", vu_func); + } + } + } default: - return fmt::format("// Unhandled VU instruction format: 0x{:X}", rs); + return fmt::format("// Unhandled COP2 format: 0x{:X}", format); } } - std::string CodeGenerator::translateFPUInstruction(const Instruction &inst) + std::string CodeGenerator::translateVU_VADD_Field(const Instruction &inst) { - uint32_t rs = inst.rs; // Format field - uint32_t ft = inst.rt; // FPU source register - uint32_t fs = inst.rd; // FPU source register - uint32_t fd = inst.sa; // FPU destination register - uint32_t function = inst.function; - - // For MFC1/MTC1/CFC1/CTC1, the GPR is in rt and the FPR is in rd(fs) - if (rs == COP1_MF) - { - return fmt::format("ctx->r[{}] = *(uint32_t*)&ctx->f[{}];", - ft, fs); - } - else if (rs == COP1_MT) - { - return fmt::format("*(uint32_t*)&ctx->f[{}] = ctx->r[{}];", - fs, ft); - } - else if (rs == COP1_CF) - { - // CFC1 - Move Control From FPU - if (fs == 31) // FCR31 contains status/control - { - return fmt::format("ctx->r[{}] = ctx->fcr31;", ft); - } - else if (fs == 0) // FCR0 is the FPU implementation register - { - return fmt::format("ctx->r[{}] = 0x00000000; // Emulated FPU implementation", ft); - } - else - { - return fmt::format("ctx->r[{}] = 0; // Unimplemented FCR{}", ft, fs); - } - } - else if (rs == COP1_CT) - { - // CTC1 - Move Control To FPU - if (fs == 31) // FCR31 contains status/control - { - return fmt::format("ctx->fcr31 = ctx->r[{}] & 0x0183FFFF;", ft); // Apply bit mask for valid bits - } - else - { - return fmt::format("// CTC1 to FCR{} ignored", fs); - } - } - else if (rs == COP1_BC) - { - // FPU Branch instructions - handled by delay slot code - return fmt::format("// FPU branch instruction - handled elsewhere"); - } - else if (rs == COP1_S) - { - // Single precision operations - switch (function) - { - case 0x00: // ADD.S - return fmt::format("ctx->f[{}] = FPU_ADD_S(ctx->f[{}], ctx->f[{}]);", - fd, fs, ft); - - case 0x01: // SUB.S - return fmt::format("ctx->f[{}] = FPU_SUB_S(ctx->f[{}], ctx->f[{}]);", - fd, fs, ft); - - case 0x02: // MUL.S - return fmt::format("ctx->f[{}] = FPU_MUL_S(ctx->f[{}], ctx->f[{}]);", - fd, fs, ft); - - case 0x03: // DIV.S - return fmt::format("ctx->f[{}] = FPU_DIV_S(ctx->f[{}], ctx->f[{}]);", - fd, fs, ft); - - case 0x04: // SQRT.S - return fmt::format("ctx->f[{}] = FPU_SQRT_S(ctx->f[{}]);", - fd, fs); - - case 0x05: // ABS.S - return fmt::format("ctx->f[{}] = FPU_ABS_S(ctx->f[{}]);", - fd, fs); - - case 0x06: // MOV.S - return fmt::format("ctx->f[{}] = FPU_MOV_S(ctx->f[{}]);", - fd, fs); - - case 0x07: // NEG.S - return fmt::format("ctx->f[{}] = FPU_NEG_S(ctx->f[{}]);", - fd, fs); - - case 0x08: // ROUND.L.S - return fmt::format("*(int64_t*)&ctx->f[{}] = FPU_ROUND_L_S(ctx->f[{}]);", - fd, fs); - - case 0x09: // TRUNC.L.S - return fmt::format("*(int64_t*)&ctx->f[{}] = FPU_TRUNC_L_S(ctx->f[{}]);", - fd, fs); - - case 0x0A: // CEIL.L.S - return fmt::format("*(int64_t*)&ctx->f[{}] = FPU_CEIL_L_S(ctx->f[{}]);", - fd, fs); - - case 0x0B: // FLOOR.L.S - return fmt::format("*(int64_t*)&ctx->f[{}] = FPU_FLOOR_L_S(ctx->f[{}]);", - fd, fs); - - case 0x0C: // ROUND.W.S - return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_ROUND_W_S(ctx->f[{}]);", - fd, fs); - - case 0x0D: // TRUNC.W.S - return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_TRUNC_W_S(ctx->f[{}]);", - fd, fs); - - case 0x0E: // CEIL.W.S - return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_CEIL_W_S(ctx->f[{}]);", - fd, fs); - - case 0x0F: // FLOOR.W.S - return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_FLOOR_W_S(ctx->f[{}]);", - fd, fs); - - case 0x21: // CVT.D.S - Convert Single to Double (not commonly used on PS2) - return fmt::format("// CVT.D.S not implemented (PS2 rarely uses double precision)"); - - case 0x24: // CVT.W.S - Convert Single to Word - return fmt::format("*(int32_t*)&ctx->f[{}] = FPU_CVT_W_S(ctx->f[{}]);", - fd, fs); - - case 0x25: // CVT.L.S - Convert Single to Long - return fmt::format("*(int64_t*)&ctx->f[{}] = FPU_CVT_L_S(ctx->f[{}]);", - fd, fs); - - case 0x30: // C.F.S - Compare False - return fmt::format("ctx->fcr31 = (ctx->fcr31 & ~0x800000); // Clear condition bit", - fs, ft); - - case 0x31: // C.UN.S - Compare Unordered - return fmt::format("ctx->fcr31 = (FPU_C_UN_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - case 0x32: // C.EQ.S - Compare Equal - return fmt::format("ctx->fcr31 = (FPU_C_EQ_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - case 0x33: // C.UEQ.S - Compare Unordered or Equal - return fmt::format("ctx->fcr31 = (FPU_C_UEQ_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - case 0x34: // C.OLT.S - Compare Ordered Less Than - return fmt::format("ctx->fcr31 = (FPU_C_OLT_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - case 0x35: // C.ULT.S - Compare Unordered or Less Than - return fmt::format("ctx->fcr31 = (FPU_C_ULT_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - case 0x36: // C.OLE.S - Compare Ordered Less Than or Equal - return fmt::format("ctx->fcr31 = (FPU_C_OLE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - case 0x37: // C.ULE.S - Compare Unordered or Less Than or Equal - return fmt::format("ctx->fcr31 = (FPU_C_ULE_S(ctx->f[{}], ctx->f[{}])) ? (ctx->fcr31 | 0x800000) : (ctx->fcr31 & ~0x800000);", - fs, ft); - - default: - return fmt::format("// Unhandled FPU.S instruction: function 0x{:X}", function); - } - } - else if (rs == COP1_W) - { - // Word format operations - switch (function) - { - case 0x20: // CVT.S.W - Convert Word to Single - return fmt::format("ctx->f[{}] = FPU_CVT_S_W(*(int32_t*)&ctx->f[{}]);", - fd, fs); - - default: - return fmt::format("// Unhandled FPU.W instruction: function 0x{:X}", function); - } - } - - return fmt::format("// Unhandled FPU instruction: format 0x{:X}, function 0x{:X}", rs, function); + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, inst.rt, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rd, inst.rd); } - std::string CodeGenerator::translateQFSRV(const Instruction &inst) + std::string CodeGenerator::translateVU_VSUB_Field(const Instruction &inst) { - return fmt::format("{{ uint32_t shift = ctx->sa & 0x1F; " - "ctx->r[{}] = _mm_or_si128(_mm_srl_epi32(ctx->r[{}], _mm_cvtsi32_si128(shift)), " - "_mm_sll_epi32(ctx->r[{}], _mm_cvtsi32_si128(32 - shift))); }}", - inst.rd, inst.rs, inst.rt); + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, inst.rt, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rd, inst.rd); } - std::string CodeGenerator::translatePCPYLD(const Instruction &inst) + std::string CodeGenerator::translateVU_VMUL_Field(const Instruction &inst) { - return fmt::format("ctx->r[{}] = _mm_shuffle_epi32(_mm_shuffle_epi32(" - "ctx->r[{}], _MM_SHUFFLE(1, 0, 1, 0)), " - "_mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(1, 0, 1, 0)), " - "_MM_SHUFFLE(3, 2, 1, 0));", - inst.rd, inst.rt, inst.rs); + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, inst.rt, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rd, inst.rd); + } + + std::string CodeGenerator::translateVU_VADD(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, inst.rt, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rd, inst.rd); + } + + std::string CodeGenerator::translateVU_VSUB(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, inst.rt, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rd, inst.rd); + } + + std::string CodeGenerator::translateVU_VMUL(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, inst.rt, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rd, inst.rd); } std::string CodeGenerator::translatePMADDW(const Instruction &inst) { - return fmt::format("{{ __m128i product = _mm_mullo_epi32(ctx->r[{}], ctx->r[{}]); " - "__m128i acc = _mm_set_epi32(0, ctx->hi, 0, ctx->lo); " - "__m128i result = _mm_add_epi32(product, acc); " - "ctx->r[{}] = result; " - "ctx->lo = _mm_extract_epi32(result, 0); " - "ctx->hi = _mm_extract_epi32(result, 1); }}", + return fmt::format("{{ __m128i p01 = _mm_mul_epu32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})); \n" // [p1, p0] 64b each + " __m128i p23 = _mm_mul_epu32(_mm_srli_si128(GPR_VEC(ctx, {}), 8), _mm_srli_si128(GPR_VEC(ctx, {}), 8)); \n" // [p3, p2] 64b each + " uint64_t acc = ((uint64_t)ctx->hi << 32) | ctx->lo; \n" + " acc += _mm_cvtsi128_si64(p01); \n" // Add product 0 + " acc += _mm_cvtsi128_si64(_mm_srli_si128(p01, 8)); \n" // Add product 1 + " acc += _mm_cvtsi128_si64(p23); \n" // Add product 2 + " acc += _mm_cvtsi128_si64(_mm_srli_si128(p23, 8)); \n" // Add product 3 + " ctx->lo = (uint32_t)acc; ctx->hi = (uint32_t)(acc >> 32); \n" + " SET_GPR_U64(ctx, {}, acc); }}", // Store 64-bit acc result in rd + inst.rs, inst.rt, inst.rs, inst.rt, inst.rd); + } + + std::string CodeGenerator::translatePDIVW(const Instruction &inst) + { + // Only divides the first word element rs[0] / rt[0] + return fmt::format("{{ int32_t rs0 = GPR_S32(ctx, {}); int32_t rt0 = GPR_S32(ctx, {}); \n" + " if (rt0 != 0) {{ ctx->lo = (uint32_t)(rs0 / rt0); ctx->hi = (uint32_t)(rs0 % rt0); }} \n" + " else {{ ctx->lo = (rs0 < 0) ? 1 : -1; ctx->hi = rs0; }} \n" // Div by zero behavior + " SET_GPR_U32(ctx, {}, ctx->lo); }}", // Store quotient in rd[0] inst.rs, inst.rt, inst.rd); } - std::string CodeGenerator::translatePEXEH(const Instruction &inst) + std::string CodeGenerator::translatePCPYLD(const Instruction &inst) { - return fmt::format("ctx->r[{}] = _mm_shufflelo_epi16(_mm_shufflehi_epi16(" - "ctx->r[{}], _MM_SHUFFLE(2, 3, 0, 1)), _MM_SHUFFLE(2, 3, 0, 1));", - inst.rd, inst.rs); - } - - std::string CodeGenerator::translatePEXEW(const Instruction &inst) - { - return fmt::format("ctx->r[{}] = _mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(2, 3, 0, 1));", - inst.rd, inst.rs); - } - - std::string CodeGenerator::translatePROT3W(const Instruction &inst) - { - return fmt::format("ctx->r[{}] = _mm_shuffle_epi32(ctx->r[{}], _MM_SHUFFLE(0, 3, 2, 1));", - inst.rd, inst.rs); + // Copies lower 64 of rs to lower 64 of rd, lower 64 of rt to upper 64 of rd + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_unpacklo_epi64(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", + inst.rd, inst.rs, inst.rt); // Order matters for unpack } std::string CodeGenerator::translatePMADDH(const Instruction &inst) { - return fmt::format("{{ __m128i product = _mm_mullo_epi16(ctx->r[{}], ctx->r[{}]); " - "// Convert 16-bit 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" - "// Add to accumulator\n" - "__m128i acc = _mm_set_epi32(0, ctx->hi, 0, ctx->lo);\n" - "__m128i result = _mm_add_epi32(_mm_add_epi32(prod_lo, prod_hi), acc);\n" - "ctx->r[{}] = result;\n" - "ctx->lo = _mm_extract_epi32(result, 0);\n" - "ctx->hi = _mm_extract_epi32(result, 1); }}", + // Parallel multiply add halfword -> results to HI/LO and rd + return fmt::format("{{ __m128i prod = _mm_madd_epi16(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})); \n" // Packed multiply and add adjacent pairs + " int32_t p0 = _mm_cvtsi128_si32(prod); \n" + " int32_t p1 = _mm_cvtsi128_si32(_mm_srli_si128(prod, 4)); \n" + " int32_t p2 = _mm_cvtsi128_si32(_mm_srli_si128(prod, 8)); \n" + " int32_t p3 = _mm_cvtsi128_si32(_mm_srli_si128(prod, 12)); \n" + " int64_t acc = ((int64_t)ctx->hi << 32) | ctx->lo; \n" + " acc += (int64_t)p0 + (int64_t)p1 + (int64_t)p2 + (int64_t)p3; \n" + " ctx->lo = (uint32_t)acc; ctx->hi = (uint32_t)(acc >> 32); \n" + " SET_GPR_U64(ctx, {}, acc); }}", inst.rs, inst.rt, inst.rd); } std::string CodeGenerator::translatePHMADH(const Instruction &inst) { - 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 add to accumulator\n" - "__m128i result = _mm_add_epi32(\n" - " _mm_unpacklo_epi16(sum, _mm_srai_epi16(sum, 15)),\n" - " _mm_set_epi32(0, ctx->hi, 0, ctx->lo));\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); + // Parallel Horizontal Multiply Add Halfword -> results to HI/LO and rd + return fmt::format("{{ __m128i evens = _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(2,0,2,0)); \n" // Select even halfwords + " __m128i odds = _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(3,1,3,1)); \n" // Select odd halfwords + " __m128i prod_ev = _mm_mullo_epi16(evens, _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(2,0,2,0))); \n" + " __m128i prod_od = _mm_mullo_epi16(odds, _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(3,1,3,1))); \n" + " __m128i sum_pairs = _mm_add_epi16(prod_ev, prod_od); \n" // Add rs[0]*rt[0] + rs[1]*rt[1], etc. + " int32_t h0 = _mm_extract_epi16(sum_pairs, 0) + _mm_extract_epi16(sum_pairs, 1); \n" // Horizontal add within low 32b + " int32_t h1 = _mm_extract_epi16(sum_pairs, 2) + _mm_extract_epi16(sum_pairs, 3); \n" // Horizontal add within next 32b + " int32_t h2 = _mm_extract_epi16(sum_pairs, 4) + _mm_extract_epi16(sum_pairs, 5); \n" + " int32_t h3 = _mm_extract_epi16(sum_pairs, 6) + _mm_extract_epi16(sum_pairs, 7); \n" + " int64_t acc = ((int64_t)ctx->hi << 32) | ctx->lo; \n" + " acc += (int64_t)h0 + (int64_t)h1 + (int64_t)h2 + (int64_t)h3; \n" + " ctx->lo = (uint32_t)acc; ctx->hi = (uint32_t)(acc >> 32); \n" + " SET_GPR_U64(ctx, {}, acc); }}", + inst.rs, inst.rt, inst.rs, inst.rt, inst.rd); } - std::string CodeGenerator::translatePMULTH(const Instruction &inst) + std::string CodeGenerator::translatePEXEH(const Instruction &inst) { - return fmt::format("{{ __m128i product = _mm_mullo_epi16(ctx->r[{}], ctx->r[{}]);\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" - "__m128i result = _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); - } - - std::string CodeGenerator::translatePDIVW(const Instruction &inst) - { - return fmt::format("{{ // Extract 32-bit integers\n" - "int32_t rs0 = _mm_extract_epi32(ctx->r[{}], 0);\n" - "int32_t rs1 = _mm_extract_epi32(ctx->r[{}], 1);\n" - "int32_t rs2 = _mm_extract_epi32(ctx->r[{}], 2);\n" - "int32_t rs3 = _mm_extract_epi32(ctx->r[{}], 3);\n" - "int32_t rt0 = _mm_extract_epi32(ctx->r[{}], 0);\n" - "int32_t rt1 = _mm_extract_epi32(ctx->r[{}], 1);\n" - "int32_t rt2 = _mm_extract_epi32(ctx->r[{}], 2);\n" - "int32_t rt3 = _mm_extract_epi32(ctx->r[{}], 3);\n" - "// Perform division, handling div by zero\n" - "int32_t lo = (rt0 != 0) ? rs0 / rt0 : 0;\n" - "int32_t hi = (rt0 != 0) ? rs0 % rt0 : 0;\n" - "// Store results\n" - "ctx->lo = lo;\n" - "ctx->hi = hi;\n" - "// Create result vector\n" - "ctx->r[{}] = _mm_set_epi32(0, 0, hi, lo); }}", - inst.rs, inst.rs, inst.rs, inst.rs, - inst.rt, inst.rt, inst.rt, inst.rt, inst.rd); - } - - std::string CodeGenerator::translatePDIVBW(const Instruction &inst) - { - return fmt::format("{{ // Get the first element of rt as the divisor\n" - "int32_t divisor = _mm_extract_epi32(ctx->r[{}], 0);\n" - "// Perform division on all elements of rs if divisor is not zero\n" - "if (divisor != 0) {{\n" - " int32_t rs0 = _mm_extract_epi32(ctx->r[{}], 0);\n" - " int32_t rs1 = _mm_extract_epi32(ctx->r[{}], 1);\n" - " int32_t rs2 = _mm_extract_epi32(ctx->r[{}], 2);\n" - " int32_t rs3 = _mm_extract_epi32(ctx->r[{}], 3);\n" - " // Store quotient in lo and remainder in hi\n" - " ctx->lo = rs0 / divisor;\n" - " ctx->hi = rs0 % divisor;\n" - " // Create result vector\n" - " ctx->r[{}] = _mm_set_epi32(0, 0, ctx->hi, ctx->lo);\n" - "}} }}", - inst.rt, inst.rs, inst.rs, inst.rs, inst.rs, inst.rd); - } - - std::string CodeGenerator::translatePCPYUD(const Instruction &inst) - { - return fmt::format("ctx->r[{}] = _mm_unpackhi_epi64(ctx->r[{}], ctx->r[{}]);", - inst.rd, inst.rt, inst.rs); + // Swaps halfwords 1<->3 and 5<->7 within the 128-bit register + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_shufflelo_epi16(_mm_shufflehi_epi16(GPR_VEC(ctx, {}), _MM_SHUFFLE(2,3,0,1)), _MM_SHUFFLE(2,3,0,1)));", + inst.rd, inst.rs); } std::string CodeGenerator::translatePREVH(const Instruction &inst) { - return fmt::format("ctx->r[{}] = _mm_shufflelo_epi16(_mm_shufflehi_epi16(" - "ctx->r[{}], _MM_SHUFFLE(2, 3, 0, 1)), _MM_SHUFFLE(2, 3, 0, 1));", + // Reverses the order of the 8 halfwords + return fmt::format("{{ __m128i mask = _mm_set_epi8(0,1, 2,3, 4,5, 6,7, 8,9, 10,11, 12,13, 14,15); " + "SET_GPR_VEC(ctx, {}, _mm_shuffle_epi8(GPR_VEC(ctx, {}), mask)); }}", inst.rd, inst.rs); } - std::string CodeGenerator::translateDSLLV(const Instruction &inst) + std::string CodeGenerator::translatePMULTH(const Instruction &inst) { - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "val = val << (ctx->r[{}] & 0x3F); " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.rs, inst.rd, inst.rd); + // Parallel multiply halfword, results sum to HI/LO and rd + return fmt::format("{{ __m128i prod = _mm_madd_epi16(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})); \n" + " int32_t p0 = _mm_cvtsi128_si32(prod); \n" + " int32_t p1 = _mm_cvtsi128_si32(_mm_srli_si128(prod, 4)); \n" + " int32_t p2 = _mm_cvtsi128_si32(_mm_srli_si128(prod, 8)); \n" + " int32_t p3 = _mm_cvtsi128_si32(_mm_srli_si128(prod, 12)); \n" + " int64_t result = (int64_t)p0 + (int64_t)p1 + (int64_t)p2 + (int64_t)p3; \n" + " ctx->lo = (uint32_t)result; ctx->hi = (uint32_t)(result >> 32); \n" + " SET_GPR_U64(ctx, {}, result); }}", + inst.rs, inst.rt, inst.rd); } - std::string CodeGenerator::translateDSRLV(const Instruction &inst) + std::string CodeGenerator::translatePDIVBW(const Instruction &inst) { - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "val = val >> (ctx->r[{}] & 0x3F); " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.rs, inst.rd, inst.rd); + // Divide each element of rs by the first element of rt + return fmt::format("{{ int32_t div = GPR_S32(ctx, {}); \n" + " int32_t r0 = GPR_S32(ctx, {}); int32_t r1 = GPR_S32(ctx, {}); \n" + " int32_t r2 = GPR_S32(ctx, {}); int32_t r3 = GPR_S32(ctx, {}); \n" + " int32_t q0=0, q1=0, q2=0, q3=0; \n" + " if (div != 0) {{ \n" + " q0 = r0 / div; ctx->lo = q0; ctx->hi = r0 % div; \n" // HI/LO only from first element + " q1 = r1 / div; q2 = r2 / div; q3 = r3 / div; \n" + " }} else {{ ctx->lo = (r0 < 0) ? 1 : -1; ctx->hi = r0; }} \n" + " SET_GPR_VEC(ctx, {}, _mm_set_epi32(q3, q2, q1, q0)); }}", + inst.rt, inst.rs + 0, inst.rs + 1, inst.rs + 2, inst.rs + 3, // TODO check if GPR_S32 allows offset indexing + inst.rd); } - std::string CodeGenerator::translateDSRAV(const Instruction &inst) + std::string CodeGenerator::translatePEXEW(const Instruction &inst) { - return fmt::format("{{ int64_t val = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "val = val >> (ctx->r[{}] & 0x3F); " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.rs, inst.rd, inst.rd); + // Swaps words 0<->2 and 1<->3 + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(1,0,3,2)));", + inst.rd, inst.rs); } - std::string CodeGenerator::translateDSLL(const Instruction &inst) + std::string CodeGenerator::translatePROT3W(const Instruction &inst) { - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "val = val << {}; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.sa, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSRL(const Instruction &inst) - { - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "val = val >> {}; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.sa, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSRA(const Instruction &inst) - { - return fmt::format("{{ int64_t val = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "val = val >> {}; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.sa, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSLL32(const Instruction &inst) - { - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "val = val << (32 + {}); " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(val >> 32); }}", - inst.rt, inst.rt, inst.sa, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSRL32(const Instruction &inst) - { - return fmt::format("{{ uint64_t val = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "val = val >> (32 + {}); " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = 0; }}", - inst.rt, inst.rt, inst.sa, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSRA32(const Instruction &inst) - { - return fmt::format("{{ int64_t val = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "val = val >> (32 + {}); " - "ctx->r[{}].m128i_u32[0] = (uint32_t)val; " - "ctx->r[{}].m128i_u32[1] = (val < 0) ? 0xFFFFFFFF : 0; }}", - inst.rt, inst.rt, inst.sa, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDADD(const Instruction &inst) - { - return fmt::format("{{ int64_t a = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "int64_t b = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "int64_t res = a + b; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)res; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(res >> 32); }}", - inst.rs, inst.rs, inst.rt, inst.rt, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDADDU(const Instruction &inst) - { - return fmt::format("{{ uint64_t a = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "uint64_t b = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "uint64_t res = a + b; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)res; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(res >> 32); }}", - inst.rs, inst.rs, inst.rt, inst.rt, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSUB(const Instruction &inst) - { - return fmt::format("{{ int64_t a = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "int64_t b = ((int64_t)(int32_t)ctx->r[{}].m128i_i32[1] << 32) | (uint32_t)ctx->r[{}].m128i_u32[0]; " - "int64_t res = a - b; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)res; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(res >> 32); }}", - inst.rs, inst.rs, inst.rt, inst.rt, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateDSUBU(const Instruction &inst) - { - return fmt::format("{{ uint64_t a = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "uint64_t b = ((uint64_t)ctx->r[{}].m128i_u32[1] << 32) | ctx->r[{}].m128i_u32[0]; " - "uint64_t res = a - b; " - "ctx->r[{}].m128i_u32[0] = (uint32_t)res; " - "ctx->r[{}].m128i_u32[1] = (uint32_t)(res >> 32); }}", - inst.rs, inst.rs, inst.rt, inst.rt, inst.rd, inst.rd); - } - - std::string CodeGenerator::translateSYNC(const Instruction &inst) - { - return "// SYNC instruction - memory barrier\n" - " // In recompiled code, we don't need explicit memory barriers"; - } - - std::string CodeGenerator::translateEI(const Instruction &inst) - { - return "ctx->cop0_status |= 0x1; // Enable interrupts"; - } - - std::string CodeGenerator::translateDI(const Instruction &inst) - { - return "ctx->cop0_status &= ~0x1; // Disable interrupts"; + // Rotates words left by 3: [d,c,b,a] -> [a,d,c,b] + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(0,3,2,1)));", + inst.rd, inst.rs); } std::string CodeGenerator::translatePMULTUW(const Instruction &inst) { - return fmt::format("{{ // Packed multiply of unsigned 32-bit integers\n" - " __m128i a = ctx->r[{}];\n" - " __m128i b = ctx->r[{}];\n" - " // Extract 32-bit integers\n" - " uint32_t a0 = _mm_extract_epi32(a, 0);\n" - " uint32_t a1 = _mm_extract_epi32(a, 1);\n" - " uint32_t a2 = _mm_extract_epi32(a, 2);\n" - " uint32_t a3 = _mm_extract_epi32(a, 3);\n" - " uint32_t b0 = _mm_extract_epi32(b, 0);\n" - " uint32_t b1 = _mm_extract_epi32(b, 1);\n" - " uint32_t b2 = _mm_extract_epi32(b, 2);\n" - " uint32_t b3 = _mm_extract_epi32(b, 3);\n" - " // Compute products\n" - " uint64_t product0 = (uint64_t)a0 * (uint64_t)b0;\n" - " uint64_t product1 = (uint64_t)a1 * (uint64_t)b1;\n" - " uint64_t product2 = (uint64_t)a2 * (uint64_t)b2;\n" - " uint64_t product3 = (uint64_t)a3 * (uint64_t)b3;\n" - " // Store results\n" - " ctx->lo = (uint32_t)product0;\n" - " ctx->hi = (uint32_t)(product0 >> 32);\n" - " // Create result vector with the multiplication results\n" - " ctx->r[{}] = _mm_set_epi32(\n" - " (uint32_t)product3, (uint32_t)product2,\n" - " (uint32_t)product1, (uint32_t)product0); }}", - inst.rs, inst.rt, inst.rd); + // Parallel multiply unsigned word -> results to HI/LO and rd (lower 32 bits) + return fmt::format("{{ __m128i p01 = _mm_mul_epu32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})); \n" + " __m128i p23 = _mm_mul_epu32(_mm_srli_si128(GPR_VEC(ctx, {}), 8), _mm_srli_si128(GPR_VEC(ctx, {}), 8)); \n" + " uint64_t res0 = _mm_cvtsi128_si64(p01); uint64_t res1 = _mm_cvtsi128_si64(_mm_srli_si128(p01, 8)); \n" + " uint64_t res2 = _mm_cvtsi128_si64(p23); uint64_t res3 = _mm_cvtsi128_si64(_mm_srli_si128(p23, 8)); \n" + " ctx->lo = (uint32_t)res0; ctx->hi = (uint32_t)(res0 >> 32); \n" // HI/LO from first product only + " SET_GPR_VEC(ctx, {}, _mm_set_epi32((uint32_t)res3, (uint32_t)res2, (uint32_t)res1, (uint32_t)res0)); }}", + inst.rs, inst.rt, inst.rs, inst.rt, inst.rd); } std::string CodeGenerator::translatePDIVUW(const Instruction &inst) { - return fmt::format("{{ // Packed division of unsigned 32-bit integers\n" - " __m128i a = ctx->r[{}];\n" - " __m128i b = ctx->r[{}];\n" - " // Extract 32-bit integers\n" - " uint32_t a0 = _mm_extract_epi32(a, 0);\n" - " uint32_t b0 = _mm_extract_epi32(b, 0);\n" - " // PS2 PDIVUW only operates on the first word\n" - " uint32_t quotient = 0;\n" - " uint32_t remainder = 0;\n" - " if (b0 != 0) {{ // Check to avoid division by zero\n" - " quotient = a0 / b0;\n" - " remainder = a0 % b0;\n" - " }}\n" - " // Store results\n" - " ctx->lo = quotient;\n" - " ctx->hi = remainder;\n" - " // Create result vector with zeros except in lowest word\n" - " ctx->r[{}] = _mm_set_epi32(0, 0, 0, quotient); }}", + // Parallel divide unsigned word (only first element) -> results to HI/LO and rd (quotient) + return fmt::format("{{ uint32_t rs0 = GPR_U32(ctx, {}); uint32_t rt0 = GPR_U32(ctx, {}); \n" + " if (rt0 != 0) {{ ctx->lo = rs0 / rt0; ctx->hi = rs0 % rt0; }} \n" + " else {{ ctx->lo = 0xFFFFFFFF; ctx->hi = rs0; }} \n" // Div by zero behavior + " SET_GPR_U32(ctx, {}, ctx->lo); }}", inst.rs, inst.rt, inst.rd); } + std::string CodeGenerator::translatePCPYUD(const Instruction &inst) + { + // Copies upper 64 of rs to lower 64 of rd, upper 64 of rt to upper 64 of rd + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_unpackhi_epi64(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", + inst.rd, inst.rs, inst.rt); // Order matters + } + std::string CodeGenerator::translatePEXCH(const Instruction &inst) { - return fmt::format("{{ // Parallel Exchange Center Halfword\n" - " // For each 64-bit half, exchange the middle two 16-bit values\n" - " // [A3|A2|A1|A0] => [A3|A1|A2|A0] for each 64-bit half\n" - " __m128i src = ctx->r[{}];\n" - " // Create a shuffle mask for _mm_shuffle_epi8 that exchanges the center halfwords\n" - " // For low 64 bits: [0,1, 4,5, 2,3, 6,7] becomes [0,1, 4,5, 6,7, 2,3]\n" - " // For high 64 bits: [8,9, 12,13, 10,11, 14,15] becomes [8,9, 12,13, 14,15, 10,11]\n" - " __m128i shuffleMask = _mm_set_epi8(\n" - " 15, 14, 13, 12, 11, 10, 9, 8, // High 64 bits: keep order\n" - " 7, 6, 3, 2, 5, 4, 1, 0); // Low 64 bits: swap 2-3 and 6-7\n" - " __m128i shuffled = _mm_shuffle_epi8(src, shuffleMask);\n" - " // Now we need to swap the byte pairs within each 64-bit half\n" - " // Using a 16-bit shuffle for each 64-bit half\n" - " ctx->r[{}] = _mm_shufflelo_epi16(_mm_shufflehi_epi16(shuffled, \n" - " _MM_SHUFFLE(3, 1, 2, 0)), _MM_SHUFFLE(3, 1, 2, 0)); }}", - inst.rs, inst.rd); + // Parallel Exchange Center Halfword (same as MMI2 PEXEH) + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_shufflelo_epi16(_mm_shufflehi_epi16(GPR_VEC(ctx, {}), _MM_SHUFFLE(2,3,0,1)), _MM_SHUFFLE(2,3,0,1)));", + inst.rd, inst.rs); } std::string CodeGenerator::translatePCPYH(const Instruction &inst) { - return fmt::format("{{ // Parallel Copy Halfword\n" - " // Copy the lowest 16-bit value in each 64-bit half to all halfwords in that half\n" - " // [A3|A2|A1|A0] => [A0|A0|A0|A0] for the low 64 bits\n" - " // [B3|B2|B1|B0] => [B0|B0|B0|B0] for the high 64 bits\n" - " __m128i src = ctx->r[{}];\n" - " // Extract the lowest 16-bit value from each 64-bit half\n" - " uint16_t lowHalf = (uint16_t)_mm_extract_epi16(src, 0);\n" - " uint16_t highHalf = (uint16_t)_mm_extract_epi16(src, 4);\n" - " // Create a vector with these values repeated\n" - " ctx->r[{}] = _mm_set_epi16(\n" - " highHalf, highHalf, highHalf, highHalf,\n" - " lowHalf, lowHalf, lowHalf, lowHalf); }}", + // Parallel Copy Halfword (Broadcast lower 16 bits of each 64-bit half) + return fmt::format("{{ __m128i src = GPR_VEC(ctx, {}); uint16_t l = _mm_extract_epi16(src, 0); uint16_t h = _mm_extract_epi16(src, 4); \n" + " SET_GPR_VEC(ctx, {}, _mm_set_epi16(h,h,h,h, l,l,l,l)); }}", inst.rs, inst.rd); } std::string CodeGenerator::translatePEXCW(const Instruction &inst) { - return fmt::format("{{ // Parallel Exchange Center Word\n" - " // Exchange the two 32-bit words in each 64-bit half\n" - " // [A1|A0|B1|B0] => [A0|A1|B0|B1]\n" - " __m128i src = ctx->r[{}];\n" - " // Exchange words in each 64-bit half using shuffle\n" - " ctx->r[{}] = _mm_shuffle_epi32(src, _MM_SHUFFLE(2, 3, 0, 1)); }}", - inst.rs, inst.rd); + // Parallel Exchange Center Word (Swaps words 0<>2, 1<>3) + return fmt::format("SET_GPR_VEC(ctx, {}, _mm_shuffle_epi32(GPR_VEC(ctx, {}), _MM_SHUFFLE(1,0,3,2)));", + inst.rd, inst.rs); } std::string CodeGenerator::translatePMTHI(const Instruction &inst) { - return fmt::format("ctx->hi = _mm_extract_epi32(ctx->r[{}], 0);", inst.rs); + return fmt::format("ctx->hi = GPR_U32(ctx, {});", inst.rs); // PMTHI uses standard HI/LO } std::string CodeGenerator::translatePMTLO(const Instruction &inst) { - return fmt::format("ctx->lo = _mm_extract_epi32(ctx->r[{}], 0);", inst.rs); + return fmt::format("ctx->lo = GPR_U32(ctx, {});", inst.rs); // PMTLO uses standard HI/LO } - std::string CodeGenerator::generateJumpTableSwitch(const Instruction &inst, uint32_t tableAddress, - const std::vector &entries) + std::string CodeGenerator::translateVU_VDIV(const Instruction &inst) { - std::stringstream ss; + uint8_t fsf = inst.vectorInfo.fsf; + uint8_t ftf = inst.vectorInfo.ftf; + uint8_t fs_reg = inst.rs; + uint8_t ft_reg = inst.rt; - uint32_t indexReg = inst.rs; + return fmt::format("{{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); ctx->vu0_q = (ft != 0.0f) ? (fs / ft) : 0.0f; }}", fs_reg, fs_reg, fsf, ft_reg, ft_reg, ftf); + } - ss << "switch (ctx->r[" << indexReg << "]) {\n"; + std::string CodeGenerator::translateVU_VSQRT(const Instruction &inst) + { + uint8_t ftf = inst.vectorInfo.ftf; + uint8_t ft_reg = inst.rt; + return fmt::format("{{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); ctx->vu0_q = sqrtf(std::max(0.0f, ft)); }}", ft_reg, ft_reg, ftf); + } - for (const auto &entry : entries) - { - ss << " case " << entry.index << ": {\n"; + std::string CodeGenerator::translateVU_VRSQRT(const Instruction &inst) + { + uint8_t ftf = inst.vectorInfo.ftf; + uint8_t ft_reg = inst.rt; + return fmt::format("{{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); ctx->vu0_q = (ft > 0.0f) ? (1.0f / sqrtf(ft)) : 0.0f; }}", ft_reg, ft_reg, ftf); + } - Symbol *sym = findSymbolByAddress(entry.target); - if (sym && sym->isFunction) - { - ss << " " << sym->name << "(rdram, ctx);\n"; - } - else - { - ss << " func_" << std::hex << entry.target << std::dec << "(rdram, ctx);\n"; - } + std::string CodeGenerator::translateVU_VMTIR(const Instruction &inst) + { + return fmt::format("ctx->vu0_i = (float)ctx->vi[{}];", inst.rt); // rt = IT + } - ss << " return;\n"; - ss << " }\n"; - } + std::string CodeGenerator::translateVU_VMFIR(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; // Use parsed field + return fmt::format("{{ float val = (float)ctx->vi[{}]; __m128 res = _mm_set1_ps(val); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rs, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, inst.rt, inst.rt); // rs=IS, rt=FT + } - ss << " default:\n"; - ss << " // Unknown jump table target\n"; - ss << " return;\n"; - ss << "}\n"; + std::string CodeGenerator::translateVU_VILWR(const Instruction &inst) + { + uint8_t field_idx = inst.vectorInfo.ftf; // Use parsed ftf field + return fmt::format("{{ uint32_t addr = (uint32_t)(_mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))) + ctx->vu0_i) & 0x3FFC; ctx->vi[{}] = READ32(addr); }}", inst.rs, inst.rs, field_idx, inst.rt); // rs=IS, rt=IT + } - return ss.str(); + std::string CodeGenerator::translateVU_VISWR(const Instruction &inst) + { + uint8_t field_idx = inst.vectorInfo.ftf; // Use parsed ftf field + return fmt::format("{{ uint32_t addr = (uint32_t)(_mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))) + ctx->vu0_i) & 0x3FFC; WRITE32(addr, ctx->vi[{}]); }}", inst.rs, inst.rs, field_idx, inst.rt); // rs=IS, rt=IT + } + + std::string CodeGenerator::translateVU_VIADD(const Instruction &inst) + { + return fmt::format("ctx->vi[{}] = ctx->vi[{}] + ctx->vi[{}];", inst.rd, inst.rs, inst.rt); // rd=ID, rs=IS, rt=IT + } + + std::string CodeGenerator::translateVU_VISUB(const Instruction &inst) + { + return fmt::format("ctx->vi[{}] = ctx->vi[{}] - ctx->vi[{}];", inst.rd, inst.rs, inst.rt); // rd=ID, rs=IS, rt=IT + } + + std::string CodeGenerator::translateVU_VIADDI(const Instruction &inst) + { + return fmt::format("ctx->vi[{}] = ctx->vi[{}] + {};", inst.rt, inst.rs, inst.sa); // rt=IT, rs=IS, sa=Imm5 + } + + std::string CodeGenerator::translateVU_VIAND(const Instruction &inst) + { + return fmt::format("ctx->vi[{}] = ctx->vi[{}] & ctx->vi[{}];", inst.rd, inst.rs, inst.rt); // rd=ID, rs=IS, rt=IT + } + + std::string CodeGenerator::translateVU_VIOR(const Instruction &inst) + { + return fmt::format("ctx->vi[{}] = ctx->vi[{}] | ctx->vi[{}];", inst.rd, inst.rs, inst.rt); // rd=ID, rs=IS, rt=IT + } + + std::string CodeGenerator::translateVU_VCALLMS(const Instruction &inst) + { + return fmt::format("// Calls VU0 microprogram at address {} - not implemented in recompiled code", inst.immediate); + } + + std::string CodeGenerator::translateVU_VCALLMSR(const Instruction &inst) + { + return fmt::format("// Calls VU0 microprogram at address {} - not implemented in recompiled code", inst.immediate); + } + + std::string CodeGenerator::translateVU_VRNEXT(const Instruction &inst) + { + return fmt::format("// Unhandled VU0 VRNEXT instruction: 0x{:X}", inst.function); + } + + std::string CodeGenerator::translateVU_VRGET(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + uint8_t ft_reg = inst.rt; + return fmt::format("{{ __m128 res = ctx->vu0_r; __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, ft_reg, ft_reg); + } + + std::string CodeGenerator::translateVU_VRINIT(const Instruction &inst) + { + return fmt::format("// Unhandled VU0 VRINIT instruction: 0x{:X}", inst.function); + } + + std::string CodeGenerator::translateVU_VRXOR(const Instruction &inst) + { + return fmt::format("// Unhandled VU0 VRXOR instruction: 0x{:X}", inst.function); + } + + std::string CodeGenerator::translateQFSRV(const Instruction &inst) + { + uint8_t rd = inst.rd; + uint8_t rs = inst.rs; + uint8_t rt = inst.rt; + // PS2 MMI QFSRV uses the lower 7 bits of the SA register. + return fmt::format( + "{{ \n" + " __m128i val_rt = GPR_VEC(ctx, {});\n" // Get rt (higher bits of the 256-bit value) + " __m128i val_rs = GPR_VEC(ctx, {});\n" // Get rs (lower bits of the 256-bit value) + " uint32_t shift_amount = ctx->sa & 0x7F; \n" // Get shift amount (0-127) from SA reg + + // Perform the shift using 64-bit parts for easier SSE2 implementation + " uint64_t rt_hi = _mm_cvtsi128_si64(_mm_srli_si128(val_rt, 8));\n" + " uint64_t rt_lo = _mm_cvtsi128_si64(val_rt);\n" + " uint64_t rs_hi = _mm_cvtsi128_si64(_mm_srli_si128(val_rs, 8));\n" + " uint64_t rs_lo = _mm_cvtsi128_si64(val_rs);\n" + + " __m128i result; \n" + " if (shift_amount == 0) {{ \n" + " result = val_rs; \n" // No shift, result is just rs + " }} else if (shift_amount < 64) {{ \n" + " uint64_t res_lo = (rs_lo >> shift_amount) | (rs_hi << (64 - shift_amount)); \n" + " uint64_t res_hi = (rs_hi >> shift_amount) | (rt_lo << (64 - shift_amount)); \n" + " result = _mm_set_epi64x(res_hi, res_lo); \n" + " }} else if (shift_amount == 64) {{ \n" + " result = _mm_set_epi64x(rt_lo, rs_hi); \n" // Shift exactly 64 bits + " }} else if (shift_amount < 128) {{ \n" // shift_amount > 64 + " uint32_t sub_shift = shift_amount - 64; \n" + " uint64_t res_lo = (rs_hi >> sub_shift) | (rt_lo << (64 - sub_shift)); \n" + " uint64_t res_hi = (rt_lo >> sub_shift) | (rt_hi << (64 - sub_shift)); \n" + " result = _mm_set_epi64x(res_hi, res_lo); \n" + " }} else {{ // shift_amount >= 128 \n" + " uint32_t sub_shift = shift_amount - 128; \n" + " uint64_t res_lo = (rt_lo >> sub_shift) | (rt_hi << (64 - sub_shift)); \n" // Shift rt into result + " uint64_t res_hi = (rt_hi >> sub_shift); \n" // Shift hi part of rt + " result = _mm_set_epi64x(res_hi, res_lo); \n" + " }} \n" + " SET_GPR_VEC(ctx, {}, result); \n" + "}}", + rt, rs, rd); } std::string CodeGenerator::generateFunctionRegistration(const std::vector &functions, @@ -2685,6 +2052,9 @@ namespace ps2recomp std::vector> systemCallFunctions; std::vector> libraryFunctions; + uint32_t libBaseAddr = 0x00110000; + uint32_t libOffset = 0; + for (const auto &function : functions) { if (!function.isRecompiled) @@ -2702,11 +2072,7 @@ namespace ps2recomp if (isLibCall) { - // we need to generate a unique address for each library function because are created at runtime - static uint32_t libBaseAddr = 0x00110000; - static uint32_t libOffset = 0; uint32_t libAddr = libBaseAddr + (libOffset++ * 4); - libraryFunctions.push_back({libAddr, libraryStubs.at(function.name)}); continue; } @@ -2754,6 +2120,41 @@ namespace ps2recomp return ss.str(); } + std::string CodeGenerator::generateJumpTableSwitch(const Instruction &inst, uint32_t tableAddress, + const std::vector &entries) + { + std::stringstream ss; + + uint32_t indexReg = inst.rs; + + ss << "switch (ctx->r[" << indexReg << "]) {\n"; + + for (const auto &entry : entries) + { + ss << " case " << entry.index << ": {\n"; + + Symbol *sym = findSymbolByAddress(entry.target); + if (sym && sym->isFunction) + { + ss << " " << sym->name << "(rdram, ctx);\n"; + } + else + { + ss << " func_" << std::hex << entry.target << std::dec << "(rdram, ctx);\n"; + } + + ss << " return;\n"; + ss << " }\n"; + } + + ss << " default:\n"; + ss << " // Unknown jump table target\n"; + ss << " return;\n"; + ss << "}\n"; + + return ss.str(); + } + Symbol *CodeGenerator::findSymbolByAddress(uint32_t address) { for (auto &symbol : m_symbols) diff --git a/ps2xRecomp/src/r5900_decoder.cpp b/ps2xRecomp/src/r5900_decoder.cpp index d0d6f9e..2bdf931 100644 --- a/ps2xRecomp/src/r5900_decoder.cpp +++ b/ps2xRecomp/src/r5900_decoder.cpp @@ -25,6 +25,7 @@ namespace ps2recomp inst.sa = SA(rawInstruction); inst.function = FUNCTION(rawInstruction); inst.immediate = IMMEDIATE(rawInstruction); + inst.simmediate = SIMMEDIATE(rawInstruction); inst.target = TARGET(rawInstruction); inst.isMMI = false; @@ -53,6 +54,8 @@ namespace ps2recomp 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; @@ -68,18 +71,10 @@ namespace ps2recomp case OPCODE_J: decodeJType(inst); - inst.isJump = true; - inst.hasDelaySlot = true; - inst.modificationInfo.modifiesControl = true; // PC break; case OPCODE_JAL: decodeJType(inst); - inst.isJump = true; - inst.isCall = true; - inst.hasDelaySlot = true; - inst.modificationInfo.modifiesGPR = true; // $ra (r[31]) - inst.modificationInfo.modifiesControl = true; // PC break; case OPCODE_BEQ: @@ -96,6 +91,103 @@ namespace ps2recomp inst.modificationInfo.modifiesControl = true; // PC potentially break; + case OPCODE_DADDI: + case OPCODE_DADDIU: + decodeIType(inst); + if (inst.rt != 0) + inst.modificationInfo.modifiesGPR = true; + break; + + case OPCODE_MMI: + decodeMMI(inst); + break; + + case OPCODE_LQ: + decodeIType(inst); + inst.isLoad = true; + inst.isMultimedia = true; // 128-bit load + break; + + case OPCODE_SQ: + decodeIType(inst); + inst.isStore = true; + inst.isMultimedia = true; // 128-bit store + inst.modificationInfo.modifiesMemory = true; + break; + + case OPCODE_LB: + case OPCODE_LH: + case OPCODE_LW: + case OPCODE_LBU: + case OPCODE_LHU: + case OPCODE_LWU: + case OPCODE_LD: + inst.isLoad = true; + if (inst.rt != 0) + inst.modificationInfo.modifiesGPR = true; + break; + + case OPCODE_LWL: + case OPCODE_LWR: + case OPCODE_LDL: + case OPCODE_LDR: + inst.isLoad = true; + if (inst.rt != 0) + inst.modificationInfo.modifiesGPR = true; + inst.modificationInfo.modifiesMemory = true; + break; + + case OPCODE_LWC1: + inst.isLoad = true; + inst.modificationInfo.modifiesFPR = true; + break; + + case OPCODE_LDC1: // Not present/used on EE FPU + case OPCODE_LWC2: // Maybe unused + case OPCODE_LDC2: // VU Load + inst.isLoad = true; + inst.isVU = true; + inst.modificationInfo.modifiesVFR = true; + break; + + case OPCODE_SB: + case OPCODE_SH: + case OPCODE_SW: + case OPCODE_SD: + inst.isStore = true; + inst.modificationInfo.modifiesMemory = true; + break; + + case OPCODE_SWL: + case OPCODE_SWR: + case OPCODE_SDL: + case OPCODE_SDR: + inst.isStore = true; + inst.modificationInfo.modifiesMemory = true; + break; + + case OPCODE_SWC1: + inst.isStore = true; + inst.modificationInfo.modifiesMemory = true; + break; + + case OPCODE_SDC1: // Not present/used on EE FPU + case OPCODE_SWC2: // Potentially unused + case OPCODE_SDC2: + inst.isStore = true; + inst.isVU = true; + inst.modificationInfo.modifiesMemory = true; + break; + + case OPCODE_SC: + case OPCODE_SCD: + inst.isStore = true; + inst.modificationInfo.modifiesMemory = true; + if (inst.rt != 0) + inst.modificationInfo.modifiesGPR = true; // Writes success/fail to rt + inst.modificationInfo.modifiesControl = true; // Reads/Clears LLBit + break; + case OPCODE_ADDI: case OPCODE_ADDIU: case OPCODE_SLTI: @@ -109,144 +201,25 @@ namespace ps2recomp inst.modificationInfo.modifiesGPR = true; break; - case OPCODE_DADDI: - case OPCODE_DADDIU: - decodeIType(inst); - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - break; - - case OPCODE_MMI: - 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 - if (inst.rt != 0) - 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: - case OPCODE_LH: - case OPCODE_LWL: - case OPCODE_LW: - case OPCODE_LBU: - case OPCODE_LHU: - case OPCODE_LWR: - case OPCODE_LWU: - case OPCODE_LD: - case OPCODE_LDL: - case OPCODE_LDR: - case OPCODE_LL: - case OPCODE_LWC1: - case OPCODE_LDC1: - case OPCODE_LWC2: - case OPCODE_LDC2: // VU Load - decodeIType(inst); - inst.isLoad = true; - if (inst.opcode == OPCODE_LWC1 || inst.opcode == OPCODE_LDC1) - inst.modificationInfo.modifiesFPR = true; - else if (inst.opcode == OPCODE_LQC2) - inst.modificationInfo.modifiesVFR = true; - else if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; // Standard GPR loads - break; - - case OPCODE_SB: - case OPCODE_SH: - case OPCODE_SWL: - case OPCODE_SW: - case OPCODE_SWR: - case OPCODE_SD: - case OPCODE_SDL: - case OPCODE_SDR: - case OPCODE_SC: - case OPCODE_SWC1: - case OPCODE_SDC1: - case OPCODE_SWC2: - case OPCODE_SDC2: - case OPCODE_SCD: // VU Store - decodeIType(inst); - inst.isStore = true; - if (inst.opcode == OPCODE_SC || inst.opcode == OPCODE_SCD) - { - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - } - break; - case OPCODE_CACHE: decodeIType(inst); inst.modificationInfo.modifiesControl = true; // Cache state break; + case OPCODE_PREF: decodeIType(inst); break; case OPCODE_COP0: decodeCOP0(inst); - inst.modificationInfo.modifiesControl = true; - if (inst.rs == COP0_MF && inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; break; case OPCODE_COP1: decodeCOP1(inst); - // Can modify FPRs, FCRs, GPRs (MFC1/CFC1), or PC (BC1) - if (inst.isBranch) - inst.modificationInfo.modifiesControl = true; // PC - if (inst.rs == COP1_MF || inst.rs == COP1_CF) - { - // MFC1, CFC1 - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - } - else if (inst.rs == COP1_S || inst.rs == COP1_W || inst.rs == COP1_L) - { - inst.modificationInfo.modifiesFPR = true; - } - if (inst.rs == COP1_CT || inst.function >= COP1_FUNC_C_F) - { // CTC1 or Compare - inst.modificationInfo.modifiesControl = true; // FCR31 - } break; case OPCODE_COP2: decodeCOP2(inst); - inst.isVU = true; - inst.isMultimedia = true; - // Can modify VFRs, VU Flags, GPRs (QMFC2/CFC2), PC (BC2), memory (VISWR) - if (inst.isBranch) - inst.modificationInfo.modifiesControl = true; // PC - if (inst.rs == COP2_QMFC2 || inst.rs == COP2_CFC2) - { - // QMFC2, CFC2 - if (inst.rt != 0) - inst.modificationInfo.modifiesGPR = true; - } - else if (inst.rs == COP2_QMTC2 || inst.rs == COP2_CTC2) - { - // Modifies VU state based on GPR - } - else - { - inst.modificationInfo.modifiesVFR = true; // Default assumption - inst.modificationInfo.modifiesControl = true; // Flags, Q, P, I etc. - } - if (inst.isStore) - inst.modificationInfo.modifiesMemory = true; break; default: @@ -255,6 +228,13 @@ namespace ps2recomp break; } + // Generic multimedia flag if MMI or VU + if (inst.isMMI || inst.isVU) + { + inst.isMultimedia = true; + inst.vectorInfo.isVector = inst.isVU; // Only VU ops are truly vector + } + return inst; } @@ -277,9 +257,12 @@ namespace ps2recomp 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]) } } @@ -420,13 +403,8 @@ namespace ps2recomp case SPECIAL_SYNC: inst.modificationInfo.modifiesGPR = false; - // Potentially modifies memory/cache visibility inst.modificationInfo.modifiesControl = true; break; - - default: - // Other R-type instructions - break; } } @@ -493,82 +471,69 @@ namespace ps2recomp // The function field is actually determined by the lowest 6 bits (as in R-type) uint32_t mmiFunction = inst.function; - // Categorize the MMI instruction type based on the rs field 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) { + // Thouse comments has the same value case MMI_MADD: case MMI_MADDU: + case MMI_MSUB: + case MMI_MSUBU: case MMI_MADD1: case MMI_MADDU1: - // Multiply-add operations - inst.modificationInfo.modifiesControl = true; // Modifies HI/LO or HI1/LO1 - break; - - case MMI_PLZCW: - // Count leading zeros/ones - break; - - case MMI_MFHI1: - case MMI_MFLO1: - // Modifies rd GPR (already set) - break; - - case MMI_MTHI1: - case MMI_MTLO1: - // Secondary HI/LO register operations - inst.modificationInfo.modifiesGPR = false; - inst.modificationInfo.modifiesControl = true; - break; - case MMI_MULT1: case MMI_MULTU1: case MMI_DIV1: case MMI_DIVU1: - // Secondary multiply/divide operations - break; - - case MMI_MMI0: - // First set of multimedia instructions - decodeMMI0(inst); - break; - - case MMI_MMI1: - // Second set of multimedia instructions - decodeMMI1(inst); - break; - - case MMI_MMI2: - // Third set of multimedia instructions - decodeMMI2(inst); - break; - - case MMI_MMI3: - // Fourth set of multimedia instructions - decodeMMI3(inst); - break; - - case MMI_PMFHL: - // PMFHL variations based on sa field - decodePMFHL(inst); - break; - - case MMI_PMTHL: - // PMTHL operations - inst.modificationInfo.modifiesGPR = false; + // case MMI2_PMADDW: + case MMI2_PMSUBW: + case MMI2_PMULTW: + case MMI2_PDIVW: + case MMI2_PDIVBW: + case MMI2_PMADDH: + case MMI2_PHMADH: + // case MMI2_PMSUBH: + // case MMI2_PHMSBH: + case MMI2_PMULTH: + // case MMI3_PMADDUW: + // case MMI3_PMULTUW: + // case MMI3_PDIVUW: + inst.modificationInfo.modifiesGPR = false; // Writes to HI/LO or HI1/LO1 inst.modificationInfo.modifiesControl = true; break; - - case MMI_PSLLH: - case MMI_PSRLH: - case MMI_PSRAH: - case MMI_PSLLW: - case MMI_PSRLW: - case MMI_PSRAW: - // SIMD shift operations + case MMI_MTHI1: + case MMI_MTLO1: + 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; @@ -812,207 +777,197 @@ namespace ps2recomp void R5900Decoder::decodeCOP0(Instruction &inst) const { // COP0 (System Control) instructions - uint32_t rs = inst.rs; // Actually the cop0 format field - - if (rs == COP0_MF) + uint8_t format = inst.rs; + inst.modificationInfo.modifiesControl = true; // Assume COP0 always modifies some control state + if (format == COP0_MF && inst.rt != 0) { - // Move From COP0 register + inst.modificationInfo.modifiesGPR = true; } - else if (rs == COP0_MT) + else if (format == COP0_CO && inst.function == COP0_CO_ERET) { - // Move To COP0 register - } - else if (rs == COP0_CO) + inst.isReturn = true; + inst.hasDelaySlot = false; + } // ERET is special + else if (format == COP0_BC) { - // COProcessor operations - uint32_t function = inst.function; - - if (function == COP0_CO_ERET) - { - inst.isReturn = true; - inst.hasDelaySlot = true; - } - else if (function == COP0_CO_TLBR || - function == COP0_CO_TLBWI || - function == COP0_CO_TLBWR || - function == COP0_CO_TLBP) - { - // TLB operations - } - else if (function == COP0_CO_EI || function == COP0_CO_DI) - { - // Enable/Disable Interrupts - } + inst.isBranch = true; + inst.hasDelaySlot = true; } } void R5900Decoder::decodeCOP1(Instruction &inst) const { - // COP1 (FPU) instructions - uint32_t rs = inst.rs; // The FPU format field - - if (rs == COP1_MF) + uint8_t format = inst.rs; + if (format == COP1_MF || format == COP1_CF) { - // Move From FPU register + if (inst.rt != 0) + inst.modificationInfo.modifiesGPR = true; } - else if (rs == COP1_CF) + else if (format == COP1_BC) { - // Move From FPU Control register + inst.isBranch = true; + inst.hasDelaySlot = true; + inst.modificationInfo.modifiesControl = true; } - else if (rs == COP1_MT) + else if (format == COP1_S || format == COP1_W || format == COP1_L) { - // Move To FPU register + inst.modificationInfo.modifiesFPR = true; } - else if (rs == COP1_CT) + if (format == COP1_CT || (format == COP1_S && inst.function >= COP1_S_C_F)) { - // Move To FPU Control register - } - else if (rs == COP1_BC) - { - // FPU Branch on Condition - uint32_t rt = inst.rt; // The condition code - if (rt == COP1_BC_BCF || rt == COP1_BC_BCT) - { - inst.isBranch = true; - inst.hasDelaySlot = true; - } - } - else if (rs == COP1_S || rs == COP1_W) - { - // FPU operations (single precision or word) - uint32_t function = inst.function; - // Decode specific FPU operation based on function field - } + inst.modificationInfo.modifiesControl = true; + } // FCR31 } void R5900Decoder::decodeCOP2(Instruction &inst) const { + uint8_t format = inst.rs; inst.isVU = true; inst.isMultimedia = true; - uint32_t rs = inst.rs; // The format field - - switch (rs) + switch (format) { - 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 + case COP2_QMFC2: + case COP2_CFC2: + if (inst.rt != 0) + inst.modificationInfo.modifiesGPR = true; 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; - } - } - break; - - case COP2_CO: // VU0 vector operations - { - 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; - } - } - 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 + 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; - case COP2_VU0OPS: // Additional VU0 operations - { - uint32_t function = inst.function; - - switch (function) + if (vu_func == VU0_S2_VDIV || vu_func == VU0_S2_VSQRT || vu_func == VU0_S2_VRSQRT) { - 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; + inst.vectorInfo.fsf = (inst.raw >> 10) & 0x3; // Extract bits 10-11 + inst.vectorInfo.ftf = (inst.raw >> 8) & 0x3; // Extract bits 8-9 } - } - break; - default: - // Unknown COP2 format + 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 (vu_func >= 0x3C) // Special2 Table + { + switch (vu_func) + { + 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_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; + } + } + else // Special1 Table + { + if (vu_func >= VU0_S1_VIADD && vu_func <= VU0_S1_VIOR) + { + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesVIR = true; + } // Integer ops + if (vu_func == VU0_S1_VIADDI) + { + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesVIR = true; + } + if (vu_func == VU0_S2_VMFIR) + { + inst.modificationInfo.modifiesVIR = false; + } // Only reads VIR + if (vu_func == VU0_S2_VMTIR) + { + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesVIC = true; + } // Modifies I reg + if (vu_func == VU0_S2_VILWR) + { + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesVIR = true; + inst.isLoad = true; + } + if (vu_func == VU0_S2_VISWR) + { + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesVIR = false; + inst.isStore = true; + inst.modificationInfo.modifiesMemory = true; + } + if (vu_func == VU0_S2_VDIV || vu_func == VU0_S2_VSQRT || vu_func == VU0_S2_VRSQRT) + { + inst.vectorInfo.usesQReg = true; + } + } break; } + } } void R5900Decoder::decodePMFHL(Instruction &inst) const @@ -1094,8 +1049,7 @@ namespace ps2recomp return 0; } - // Calculate branch target: PC + 4 + (sign-extended immediate << 2) - int32_t offset = static_cast(inst.immediate) << 2; + int32_t offset = inst.simmediate << 2; return inst.address + 4 + offset; } @@ -1106,6 +1060,13 @@ namespace ps2recomp 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 @@ -1113,12 +1074,6 @@ namespace ps2recomp uint32_t pc_upper = (inst.address + 4) & 0xF0000000; return pc_upper | (inst.target << 2); } - else 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; - } return 0; } diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 526cfe7..4aa2ea2 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -11,7 +11,7 @@ #include constexpr uint32_t PS2_RAM_SIZE = 32 * 1024 * 1024; // 32MB -constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1; // Mask for 32MB alignment +constexpr uint32_t PS2_RAM_MASK = 0x1FFFFFF; // Mask for 32MB alignment constexpr uint32_t PS2_RAM_BASE = 0x00000000; // Physical base of RDRAM constexpr uint32_t PS2_SCRATCHPAD_BASE = 0x70000000; constexpr uint32_t PS2_SCRATCHPAD_SIZE = 16 * 1024; // 16KB