diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 7dc5114..d9f8add 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -86,8 +86,14 @@ namespace ps2recomp std::string translateVU_VRGET(const Instruction &inst); std::string translateVU_VRINIT(const Instruction &inst); std::string translateVU_VRXOR(const Instruction &inst); + std::string translateVU_VMADD_Field(const Instruction &inst); + std::string translateVU_VMINI_Field(const Instruction &inst); + std::string translateVU_VMADD(const Instruction &inst); + std::string translateVU_VMAX(const Instruction &inst); + std::string translateVU_VOPMSUB(const Instruction &inst); + std::string translateVU_VMINI(const Instruction &inst); - // + // Jump Table Generation 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 b685024..30b767d 100644 --- a/ps2xRecomp/include/ps2recomp/instructions.h +++ b/ps2xRecomp/include/ps2recomp/instructions.h @@ -80,8 +80,8 @@ namespace ps2recomp 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 + // OPCODE_LQC2 = 0x36, + // OPCODE_SQC2 = 0x3E }; // SPECIAL Function (bits 5-0) for OPCODE_SPECIAL @@ -286,7 +286,7 @@ 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, @@ -673,15 +673,35 @@ namespace ps2recomp // 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 //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 - }; + VU0_CR_STATUS = 0, // Status/Control register + VU0_CR_MAC = 1, // MAC flags register + VU0_CR_CLIP = 5, // Clipping flags register + VU0_CR_R = 3, // R register (Random number) + VU0_CR_I = 4, // I register (Immediate) + // Add missing registers + VU0_CR_VPU_STAT = 2, // VPU-STAT register + VU0_CR_TPC = 6, // T (program counter) register + VU0_CR_CMSAR0 = 7, // Call/return address 0 + VU0_CR_FBRST = 8, // VIF/VU reset register + VU0_CR_VPU_STAT2 = 9, // VPU-STAT register 2 + VU0_CR_TPC2 = 10, // T (program counter) register 2 + VU0_CR_CMSAR1 = 11, // Call/return address 1 + VU0_CR_FBRST2 = 12, // VIF/VU reset register 2 + VU0_CR_VPU_STAT3 = 13, // VPU-STAT register 3 + VU0_CR_CMSAR2 = 14, // Call/return address 2 + VU0_CR_FBRST3 = 15, // VIF/VU reset register 3 + VU0_CR_VPU_STAT4 = 16, // VPU-STAT register 4 + VU0_CR_CMSAR3 = 17, // Call/return address 3 + VU0_CR_FBRST4 = 18, // VIF/VU reset register 4 + VU0_CR_ACC = 20, // Accumulator register + VU0_CR_INFO = 21, // Information register + VU0_CR_CLIP2 = 22, // Clipping flags register 2 + VU0_CR_P = 26, // P register + VU0_CR_XITOP = 27, // XITOP register + VU0_CR_ITOP = 28, // ITOP register + VU0_CR_TOP = 29 // TOP register + }; enum VU0OPSFunctions { VU0OPS_QMFC2_NI = 0x00, // Non-incrementing QMFC2 diff --git a/ps2xRecomp/src/code_generator.cpp b/ps2xRecomp/src/code_generator.cpp index 45a916c..cd0d73d 100644 --- a/ps2xRecomp/src/code_generator.cpp +++ b/ps2xRecomp/src/code_generator.cpp @@ -580,15 +580,78 @@ namespace ps2recomp case OPCODE_BLEZL: case OPCODE_BGTZL: return fmt::format("// Likely branch instruction at 0x{:X} - Handled by branch logic", inst.address); + case OPCODE_LDL: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = (addr & 7) << 3; " + "uint64_t mask = 0xFFFFFFFFFFFFFFFFULL << shift; " + "uint64_t aligned_data = READ64(addr & ~7ULL); " + "SET_GPR_U64(ctx, {}, (GPR_U64(ctx, {}) & ~mask) | (aligned_data & mask)); }}", + inst.rs, inst.simmediate, inst.rt, inst.rt); + case OPCODE_LDR: - case OPCODE_SDL: - case OPCODE_SDR: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = ((~addr) & 7) << 3; " + "uint64_t mask = 0xFFFFFFFFFFFFFFFFULL >> shift; " + "uint64_t aligned_data = READ64(addr & ~7ULL); " + "SET_GPR_U64(ctx, {}, (GPR_U64(ctx, {}) & ~mask) | (aligned_data & mask)); }}", + inst.rs, inst.simmediate, inst.rt, inst.rt); + case OPCODE_LWL: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = (addr & 3) << 3; " + "uint32_t mask = 0xFFFFFFFF << shift; " + "uint32_t aligned_data = READ32(addr & ~3); " + "SET_GPR_U32(ctx, {}, (GPR_U32(ctx, {}) & ~mask) | (aligned_data & mask)); }}", + inst.rs, inst.simmediate, inst.rt, inst.rt); + case OPCODE_LWR: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = ((~addr) & 3) << 3; " + "uint32_t mask = 0xFFFFFFFF >> shift; " + "uint32_t aligned_data = READ32(addr & ~3); " + "SET_GPR_U32(ctx, {}, (GPR_U32(ctx, {}) & ~mask) | (aligned_data & mask)); }}", + inst.rs, inst.simmediate, inst.rt, inst.rt); + case OPCODE_SWL: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = (addr & 3) << 3; " + "uint32_t mask = 0xFFFFFFFF << shift; " + "uint32_t aligned_addr = addr & ~3; " + "uint32_t old_data = READ32(aligned_addr); " + "uint32_t new_data = (old_data & ~mask) | (GPR_U32(ctx, {}) & mask); " + "WRITE32(aligned_addr, new_data); }}", + inst.rs, inst.simmediate, inst.rt); + case OPCODE_SWR: - return fmt::format("//Unhandled Unaligned load/store instruction 0x{:X} not implemented", inst.opcode); + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = ((~addr) & 3) << 3; " + "uint32_t mask = 0xFFFFFFFF >> shift; " + "uint32_t aligned_addr = addr & ~3; " + "uint32_t old_data = READ32(aligned_addr); " + "uint32_t new_data = (old_data & ~mask) | (GPR_U32(ctx, {}) & mask); " + "WRITE32(aligned_addr, new_data); }}", + inst.rs, inst.simmediate, inst.rt); + + case OPCODE_SDL: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = (addr & 7) << 3; " + "uint64_t mask = 0xFFFFFFFFFFFFFFFFULL << shift; " + "uint64_t aligned_addr = addr & ~7ULL; " + "uint64_t old_data = READ64(aligned_addr); " + "uint64_t new_data = (old_data & ~mask) | (GPR_U64(ctx, {}) & mask); " + "WRITE64(aligned_addr, new_data); }}", + inst.rs, inst.simmediate, inst.rt); + + case OPCODE_SDR: + return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); " + "uint32_t shift = ((~addr) & 7) << 3; " + "uint64_t mask = 0xFFFFFFFFFFFFFFFFULL >> shift; " + "uint64_t aligned_addr = addr & ~7ULL; " + "uint64_t old_data = READ64(aligned_addr); " + "uint64_t new_data = (old_data & ~mask) | (GPR_U64(ctx, {}) & mask); " + "WRITE64(aligned_addr, new_data); }}", + inst.rs, inst.simmediate, inst.rt); case OPCODE_CACHE: return "// CACHE instruction (ignored)"; case OPCODE_PREF: @@ -645,7 +708,20 @@ namespace ps2recomp case SPECIAL_DIVU: 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); + return fmt::format( + "if (runtime->check_overflow) {{ " + " int32_t rs_val = GPR_S32(ctx, {}); " + " int32_t rt_val = GPR_S32(ctx, {}); " + " int64_t result = (int64_t)rs_val + (int64_t)rt_val; " + " if (result > INT32_MAX || result < INT32_MIN) {{ " + " runtime->SignalException(ctx, EXCEPTION_INTEGER_OVERFLOW); " + " }} else {{ " + " SET_GPR_S32(ctx, {}, (int32_t)result); " + " }} " + "}} else {{ " + " SET_GPR_S32(ctx, {}, ADD32(GPR_S32(ctx, {}), GPR_S32(ctx, {}))); " + "}}", + inst.rs, inst.rt, inst.rd, inst.rd, inst.rs, inst.rt); case SPECIAL_ADDU: return fmt::format("SET_GPR_U32(ctx, {}, ADD32(GPR_U32(ctx, {}), GPR_U32(ctx, {})));", inst.rd, inst.rs, inst.rt); case SPECIAL_SUB: @@ -1349,14 +1425,56 @@ namespace ps2recomp return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_status);", rt); case VU0_CR_MAC: return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_mac_flags);", rt); - case VU0_CR_CLIP: - return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags);", rt); + case VU0_CR_VPU_STAT: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat);", 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); + case VU0_CR_CLIP: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags);", rt); + case VU0_CR_TPC: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_tpc);", rt); + case VU0_CR_CMSAR0: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar0);", rt); + case VU0_CR_FBRST: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst);", rt); + case VU0_CR_VPU_STAT2: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat2);", rt); + case VU0_CR_TPC2: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_tpc2);", rt); + case VU0_CR_CMSAR1: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar1);", rt); + case VU0_CR_FBRST2: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst2);", rt); + case VU0_CR_VPU_STAT3: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat3);", rt); + case VU0_CR_CMSAR2: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar2);", rt); + case VU0_CR_FBRST3: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst3);", rt); + case VU0_CR_VPU_STAT4: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_vpu_stat4);", rt); + case VU0_CR_CMSAR3: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_cmsar3);", rt); + case VU0_CR_FBRST4: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_fbrst4);", rt); + case VU0_CR_ACC: + return fmt::format("SET_GPR_VEC(ctx, {}, (__m128i)ctx->vu0_acc);", rt); + case VU0_CR_INFO: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_info);", rt); + case VU0_CR_CLIP2: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_clip_flags2);", rt); + case VU0_CR_P: + return fmt::format("SET_GPR_U32(ctx, {}, *(uint32_t*)&ctx->vu0_p);", rt); + case VU0_CR_XITOP: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_xitop);", rt); + case VU0_CR_ITOP: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_itop);", rt); + case VU0_CR_TOP: + return fmt::format("SET_GPR_U32(ctx, {}, ctx->vu0_top);", rt); default: - return fmt::format("// Unhandled CFC2 VU CReg: {}", rd); + return fmt::format("// Unimplemented CFC2 VU CReg: {}", rt); } } case COP2_QMTC2: @@ -1369,14 +1487,56 @@ namespace ps2recomp return fmt::format("ctx->vu0_status = GPR_U32(ctx, {}) & 0xFFFF;", rt); case VU0_CR_MAC: return fmt::format("ctx->vu0_mac_flags = GPR_U32(ctx, {});", rt); + case VU0_CR_VPU_STAT: + return fmt::format("ctx->vu0_vpu_stat = GPR_U32(ctx, {});", rt); case VU0_CR_CLIP: 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); + case VU0_CR_TPC: + return fmt::format("ctx->vu0_tpc = GPR_U32(ctx, {});", rt); + case VU0_CR_CMSAR0: + return fmt::format("ctx->vu0_cmsar0 = GPR_U32(ctx, {});", rt); + case VU0_CR_FBRST: + return fmt::format("ctx->vu0_fbrst = GPR_U32(ctx, {});", rt); + case VU0_CR_VPU_STAT2: + return fmt::format("ctx->vu0_vpu_stat2 = GPR_U32(ctx, {});", rt); + case VU0_CR_TPC2: + return fmt::format("ctx->vu0_tpc2 = GPR_U32(ctx, {});", rt); + case VU0_CR_CMSAR1: + return fmt::format("ctx->vu0_cmsar1 = GPR_U32(ctx, {});", rt); + case VU0_CR_FBRST2: + return fmt::format("ctx->vu0_fbrst2 = GPR_U32(ctx, {});", rt); + case VU0_CR_VPU_STAT3: + return fmt::format("ctx->vu0_vpu_stat3 = GPR_U32(ctx, {});", rt); + case VU0_CR_CMSAR2: + return fmt::format("ctx->vu0_cmsar2 = GPR_U32(ctx, {});", rt); + case VU0_CR_FBRST3: + return fmt::format("ctx->vu0_fbrst3 = GPR_U32(ctx, {});", rt); + case VU0_CR_VPU_STAT4: + return fmt::format("ctx->vu0_vpu_stat4 = GPR_U32(ctx, {});", rt); + case VU0_CR_CMSAR3: + return fmt::format("ctx->vu0_cmsar3 = GPR_U32(ctx, {});", rt); + case VU0_CR_FBRST4: + return fmt::format("ctx->vu0_fbrst4 = GPR_U32(ctx, {});", rt); + case VU0_CR_ACC: + return fmt::format("ctx->vu0_acc = (__m128)GPR_VEC(ctx, {});", rt); + case VU0_CR_INFO: + return fmt::format("ctx->vu0_info = GPR_U32(ctx, {});", rt); + case VU0_CR_CLIP2: + return fmt::format("ctx->vu0_clip_flags2 = GPR_U32(ctx, {});", rt); + case VU0_CR_P: + return fmt::format("ctx->vu0_p = *(float*)&GPR_U32(ctx, {});", rt); + case VU0_CR_XITOP: + return fmt::format("ctx->vu0_xitop = GPR_U32(ctx, {}) & 0x3FF;", rt); + case VU0_CR_ITOP: + return fmt::format("ctx->vu0_itop = GPR_U32(ctx, {}) & 0x3FF;", rt); + case VU0_CR_TOP: + return fmt::format("ctx->vu0_top = GPR_U32(ctx, {}) & 0x3FF;", rt); default: - return fmt::format("// Unhandled CTC2 VU CReg: {}", rd); + return fmt::format("// Unimplemented CTC2 VU CReg: {}", rd); } } case COP2_BC: @@ -1490,6 +1650,27 @@ namespace ps2recomp 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); + case VU0_S1_VMADDx: + case VU0_S1_VMADDy: + case VU0_S1_VMADDz: + case VU0_S1_VMADDw: + return translateVU_VMADD_Field(inst); + case VU0_S1_VMAXx: + return fmt::format("ctx->vu0_vf[{}] = _mm_max_ps(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,0)));", inst.rd, inst.rs, inst.rt, inst.rt); + case VU0_S1_VMAXz: + return fmt::format("ctx->vu0_vf[{}] = _mm_max_ps(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(2,2,2,2)));", inst.rd, inst.rs, inst.rt, inst.rt); + case VU0_S1_VMINIx: + case VU0_S1_VMINIy: + case VU0_S1_VMINIw: + return translateVU_VMINI_Field(inst); + case VU0_S1_VMADD: + return translateVU_VMADD(inst); + case VU0_S1_VMAX: + return translateVU_VMAX(inst); + case VU0_S1_VOPMSUB: + return translateVU_VOPMSUB(inst); + case VU0_S1_VMINI: + return translateVU_VMINI(inst); default: return fmt::format("// Unhandled VU0 Special1 function: 0x{:X}", vu_func); } @@ -1808,6 +1989,93 @@ namespace ps2recomp return fmt::format("// Unhandled VU0 VRNEXT instruction: 0x{:X}", inst.function); } + std::string CodeGenerator::translateVU_VMADD_Field(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + uint8_t field = inst.function & 0x3; // Extract field from function code + + // Pre-construct the shuffle pattern to avoid format string issues + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); " + "__m128 res = PS2_VADD(ctx->vu0_acc, mul_res); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " + "ctx->vu0_acc = res; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern, + (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_VMINI_Field(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + uint8_t field = inst.function & 0x3; + + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + + return fmt::format("{{ __m128 res = _mm_min_ps(ctx->vu0_vf[{}], _mm_shuffle_ps(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, inst.rt, shuffle_pattern, + (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_VMADD(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); " + "__m128 res = PS2_VADD(ctx->vu0_acc, mul_res); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " + "ctx->vu0_acc = res; }}", + 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_VMAX(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = _mm_max_ps(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_VOPMSUB(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); " + "__m128 res = PS2_VSUB(ctx->vu0_acc, mul_res); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " + "ctx->vu0_acc = res; }}", + 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_VMINI(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = _mm_min_ps(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_VRGET(const Instruction &inst) { uint8_t dest_mask = inst.vectorInfo.vectorField; diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 4aa2ea2..bc059f2 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 = 0x1FFFFFF; // 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 @@ -50,6 +50,11 @@ constexpr uint32_t PS2_GS_PRIV_REG_SIZE = 0x2000; #define PS2_FIO_S_IFDIR 0x1000 #define PS2_FIO_S_IFREG 0x2000 +enum PS2Exception +{ + EXCEPTION_INTEGER_OVERFLOW = 0x0C, // From MIPS spec +}; + // PS2 CPU context (R5900) struct R5900Context { @@ -383,9 +388,15 @@ public: void registerFunction(uint32_t address, RecompiledFunction func); RecompiledFunction lookupFunction(uint32_t address); + void SignalException(R5900Context* ctx, PS2Exception exception); + +private: + void HandleIntegerOverflow(R5900Context* ctx); + private: PS2Memory m_memory; R5900Context m_cpuContext; + bool check_overflow = false; std::unordered_map m_functionTable; diff --git a/ps2xRuntime/src/ps2_runtime.cpp b/ps2xRuntime/src/ps2_runtime.cpp index df0dad6..e18f9bb 100644 --- a/ps2xRuntime/src/ps2_runtime.cpp +++ b/ps2xRuntime/src/ps2_runtime.cpp @@ -171,6 +171,29 @@ PS2Runtime::RecompiledFunction PS2Runtime::lookupFunction(uint32_t address) return defaultFunction; } +void PS2Runtime::SignalException(R5900Context *ctx, PS2Exception exception) +{ + if (exception == EXCEPTION_INTEGER_OVERFLOW) + { + // PS2 behavior: jump to exception handler + HandleIntegerOverflow(ctx); + } +} + +void PS2Runtime::HandleIntegerOverflow(R5900Context *ctx) +{ + std::cerr << "Integer overflow exception at PC: 0x" << std::hex << ctx->pc << std::dec << std::endl; + + // Set the EPC (Exception Program Counter) to the current PC + m_cpuContext.cop0_epc = ctx->pc; + + // Set the cause register to indicate an integer overflow + m_cpuContext.cop0_cause |= (EXCEPTION_INTEGER_OVERFLOW << 2); + + // Jump to the exception handler (usually at 0x80000000) + m_cpuContext.pc = 0x80000000; // Default PS2 exception handler address +} + void PS2Runtime::run() { RecompiledFunction entryPoint = lookupFunction(m_cpuContext.pc);