diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 540f551..64a710f 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -1,6 +1,7 @@ #ifndef PS2RECOMP_CODE_GENERATOR_H #define PS2RECOMP_CODE_GENERATOR_H +#include #include #include #include @@ -89,6 +90,12 @@ namespace ps2recomp 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_VMSUB_Field(const Instruction &inst); + std::string translateVU_VADDA_Field(const Instruction &inst); + std::string translateVU_VSUBA_Field(const Instruction &inst); + std::string translateVU_VMADDA_Field(const Instruction &inst); + std::string translateVU_VMSUBA_Field(const Instruction &inst); + std::string translateVU_VMULA_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); @@ -112,10 +119,40 @@ namespace ps2recomp 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_VMAX_Field(const Instruction &inst); std::string translateVU_VMADD(const Instruction &inst); + std::string translateVU_VMADDq(const Instruction &inst); + std::string translateVU_VMADDi(const Instruction &inst); std::string translateVU_VMAX(const Instruction &inst); + std::string translateVU_VMAXi(const Instruction &inst); + std::string translateVU_VADDA(const Instruction &inst); + std::string translateVU_VADDAq(const Instruction &inst); + std::string translateVU_VADDAi(const Instruction &inst); + std::string translateVU_VSUBA(const Instruction &inst); + std::string translateVU_VSUBAq(const Instruction &inst); + std::string translateVU_VSUBAi(const Instruction &inst); + std::string translateVU_VMADDA(const Instruction &inst); + std::string translateVU_VMADDAq(const Instruction &inst); + std::string translateVU_VMADDAi(const Instruction &inst); + std::string translateVU_VMSUBA(const Instruction &inst); + std::string translateVU_VMSUBAq(const Instruction &inst); + std::string translateVU_VMSUBAi(const Instruction &inst); + std::string translateVU_VMULA(const Instruction &inst); + std::string translateVU_VMULAq(const Instruction &inst); + std::string translateVU_VMULAi(const Instruction &inst); + std::string translateVU_VOPMULA(const Instruction &inst); std::string translateVU_VOPMSUB(const Instruction &inst); std::string translateVU_VMINI(const Instruction &inst); + std::string translateVU_VMINIi(const Instruction &inst); + std::string translateVU_VMSUB(const Instruction &inst); + std::string translateVU_VMSUBq(const Instruction &inst); + std::string translateVU_VMSUBi(const Instruction &inst); + std::string translateVU_VITOF(const Instruction &inst, int shift); + std::string translateVU_VFTOI(const Instruction &inst, int shift); + std::string translateVU_VLQI(const Instruction &inst); + std::string translateVU_VSQI(const Instruction &inst); + std::string translateVU_VLQD(const Instruction &inst); + std::string translateVU_VSQD(const Instruction &inst); // Jump Table Generation std::string generateJumpTableSwitch(const Instruction &inst, uint32_t tableAddress, diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 1e740d0..b98c7e6 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -517,7 +517,6 @@ namespace ps2recomp return "// NOP (addiu $zero, ...)"; return fmt::format("SET_GPR_S32(ctx, {}, ADD32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate); - return fmt::format("SET_GPR_S32(ctx, {}, ADD32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate); case OPCODE_SLTI: return fmt::format("SET_GPR_U32(ctx, {}, SLT32(GPR_S32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate); case OPCODE_SLTIU: @@ -1582,10 +1581,93 @@ namespace ps2recomp case COP2_CO + 15: { uint8_t vu_func = inst.function; - if (vu_func >= 0x3C) // Special2 Table + if (vu_func >= 0x3C) { - switch (vu_func) + uint8_t vu_fhi_flo = (uint8_t)((((inst.raw >> 6) & 0x1F) << 2) | (inst.raw & 0x3)); + + switch (vu_fhi_flo) { + case VU0_S2_VADDAx: + case VU0_S2_VADDAy: + case VU0_S2_VADDAz: + case VU0_S2_VADDAw: + return translateVU_VADDA_Field(inst); + case VU0_S2_VSUBAx: + case VU0_S2_VSUBAy: + case VU0_S2_VSUBAz: + case VU0_S2_VSUBAw: + return translateVU_VSUBA_Field(inst); + case VU0_S2_VMADDAx: + case VU0_S2_VMADDAy: + case VU0_S2_VMADDAz: + case VU0_S2_VMADDAw: + return translateVU_VMADDA_Field(inst); + case VU0_S2_VMSUBAx: + case VU0_S2_VMSUBAy: + case VU0_S2_VMSUBAz: + case VU0_S2_VMSUBAw: + return translateVU_VMSUBA_Field(inst); + case VU0_S2_VMULAx: + case VU0_S2_VMULAy: + case VU0_S2_VMULAz: + case VU0_S2_VMULAw: + return translateVU_VMULA_Field(inst); + case VU0_S2_VADDA: + return translateVU_VADDA(inst); + case VU0_S2_VADDAq: + return translateVU_VADDAq(inst); + case VU0_S2_VADDAi: + return translateVU_VADDAi(inst); + case VU0_S2_VMADDA: + return translateVU_VMADDA(inst); + case VU0_S2_VMADDAq: + return translateVU_VMADDAq(inst); + case VU0_S2_VMADDAi: + return translateVU_VMADDAi(inst); + case VU0_S2_VSUBA: + return translateVU_VSUBA(inst); + case VU0_S2_VSUBAq: + return translateVU_VSUBAq(inst); + case VU0_S2_VSUBAi: + return translateVU_VSUBAi(inst); + case VU0_S2_VMSUBA: + return translateVU_VMSUBA(inst); + case VU0_S2_VMSUBAq: + return translateVU_VMSUBAq(inst); + case VU0_S2_VMSUBAi: + return translateVU_VMSUBAi(inst); + case VU0_S2_VMULA: + return translateVU_VMULA(inst); + case VU0_S2_VMULAq: + return translateVU_VMULAq(inst); + case VU0_S2_VMULAi: + return translateVU_VMULAi(inst); + case VU0_S2_VOPMULA: + return translateVU_VOPMULA(inst); + case VU0_S2_VITOF0: + return translateVU_VITOF(inst, 0); + case VU0_S2_VITOF4: + return translateVU_VITOF(inst, 4); + case VU0_S2_VITOF12: + return translateVU_VITOF(inst, 12); + case VU0_S2_VITOF15: + return translateVU_VITOF(inst, 15); + case VU0_S2_VFTOI0: + return translateVU_VFTOI(inst, 0); + case VU0_S2_VFTOI4: + return translateVU_VFTOI(inst, 4); + case VU0_S2_VFTOI12: + return translateVU_VFTOI(inst, 12); + case VU0_S2_VFTOI15: + return translateVU_VFTOI(inst, 15); + case VU0_S2_VLQI: + return translateVU_VLQI(inst); + case VU0_S2_VSQI: + return translateVU_VSQI(inst); + case VU0_S2_VLQD: + return translateVU_VLQD(inst); + case VU0_S2_VSQD: + return translateVU_VSQD(inst); case VU0_S2_VDIV: return translateVU_VDIV(inst); case VU0_S2_VSQRT: @@ -1593,7 +1675,7 @@ namespace ps2recomp case VU0_S2_VRSQRT: return translateVU_VRSQRT(inst); case VU0_S2_VWAITQ: - return fmt::format("// Unhandled VU0 VWAITQ instruction: 0x{:X}", vu_func); + return fmt::format("// VWAITQ (Q already resolved in this runtime)"); case VU0_S2_VMTIR: return translateVU_VMTIR(inst); case VU0_S2_VMFIR: @@ -1602,6 +1684,42 @@ namespace ps2recomp return translateVU_VILWR(inst); case VU0_S2_VISWR: return translateVU_VISWR(inst); + case VU0_S2_VABS: + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = _mm_and_ps(ctx->vu0_vf[{}], _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF))); " + "__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); + } + case VU0_S2_VMOVE: + return fmt::format("ctx->vu0_vf[{}] = ctx->vu0_vf[{}];", inst.rt, inst.rs); + 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); + case VU0_S2_VCLIPw: + { + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + + return fmt::format( + "{{ __m128 fs = ctx->vu0_vf[{}]; " + "__m128 ft = _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {}); " + "__m128 neg_ft = _mm_xor_ps(ft, _mm_castsi128_ps(_mm_set1_epi32(0x80000000))); " + "__m128 gt = _mm_cmpgt_ps(fs, ft); " + "__m128 lt = _mm_cmplt_ps(fs, neg_ft); " + "uint32_t gt_mask = (uint32_t)_mm_movemask_ps(gt); " + "uint32_t lt_mask = (uint32_t)_mm_movemask_ps(lt); " + "uint32_t flags = ((lt_mask & 0x1) << 0) | ((gt_mask & 0x1) << 1) | " + "((lt_mask & 0x2) << 1) | ((gt_mask & 0x2) << 2) | " + "((lt_mask & 0x4) << 2) | ((gt_mask & 0x4) << 3); " + "ctx->vu0_clip_flags = ((ctx->vu0_clip_flags << 6) | (flags & 0x3F)) & 0xFFFFFF; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern); + } + case VU0_S2_VNOP: + return fmt::format("// NOP operation, no action needed for VU0"); case VU0_S2_VRNEXT: return translateVU_VRNEXT(inst); case VU0_S2_VRGET: @@ -1610,95 +1728,107 @@ namespace ps2recomp 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); + return fmt::format("// Unhandled VU0 Special2 fhi_flo: 0x{:X}", vu_fhi_flo); } } - else // Special1 Table + + // Special1 Table (function-based) + switch (vu_func) { - 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); - 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); - } + 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); + case VU0_S1_VMADDx: + case VU0_S1_VMADDy: + case VU0_S1_VMADDz: + case VU0_S1_VMADDw: + return translateVU_VMADD_Field(inst); + case VU0_S1_VMSUBx: + case VU0_S1_VMSUBy: + case VU0_S1_VMSUBz: + case VU0_S1_VMSUBw: + return translateVU_VMSUB_Field(inst); + case VU0_S1_VMAXx: + case VU0_S1_VMAXy: + case VU0_S1_VMAXz: + case VU0_S1_VMAXw: + return translateVU_VMAX_Field(inst); + case VU0_S1_VMINIx: + case VU0_S1_VMINIy: + case VU0_S1_VMINIz: + case VU0_S1_VMINIw: + return translateVU_VMINI_Field(inst); + case VU0_S1_VMAXi: + return translateVU_VMAXi(inst); + case VU0_S1_VMINIi: + return translateVU_VMINIi(inst); + case VU0_S1_VMADD: + return translateVU_VMADD(inst); + case VU0_S1_VMADDq: + return translateVU_VMADDq(inst); + case VU0_S1_VMADDi: + return translateVU_VMADDi(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); + case VU0_S1_VMSUB: + return translateVU_VMSUB(inst); + case VU0_S1_VMSUBq: + return translateVU_VMSUBq(inst); + case VU0_S1_VMSUBi: + return translateVU_VMSUBi(inst); + default: + return fmt::format("// Unhandled VU0 Special1 function: 0x{:X}", vu_func); + } } - } default: return fmt::format("// Unhandled COP2 format: 0x{:X}", format); } @@ -1707,19 +1837,25 @@ namespace ps2recomp std::string CodeGenerator::translateVU_VADD_Field(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); + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + return fmt::format("{{ __m128 res = PS2_VADD(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_VSUB_Field(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); + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + return fmt::format("{{ __m128 res = PS2_VSUB(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_VMUL_Field(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); + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + return fmt::format("{{ __m128 res = PS2_VMUL(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_VADD(const Instruction &inst) @@ -2064,6 +2200,24 @@ namespace ps2recomp inst.rd, inst.rd); } + std::string CodeGenerator::translateVU_VMSUB_Field(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + uint8_t field = inst.function & 0x3; // Extract field from function code + + 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_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, 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; @@ -2080,6 +2234,22 @@ namespace ps2recomp inst.rd, inst.rd); } + std::string CodeGenerator::translateVU_VMAX_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_max_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; @@ -2094,6 +2264,34 @@ namespace ps2recomp inst.rd, inst.rd); } + std::string CodeGenerator::translateVU_VMADDq(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q)); " + "__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, + (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_VMADDi(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); " + "__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, + (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; @@ -2106,6 +2304,18 @@ namespace ps2recomp inst.rd, inst.rd); } + std::string CodeGenerator::translateVU_VMAXi(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = _mm_max_ps(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); " + "__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.rd, inst.rd); + } + std::string CodeGenerator::translateVU_VOPMSUB(const Instruction &inst) { uint8_t dest_mask = inst.vectorInfo.vectorField; @@ -2132,6 +2342,307 @@ namespace ps2recomp inst.rd, inst.rd); } + std::string CodeGenerator::translateVU_VMINIi(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 res = _mm_min_ps(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); " + "__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.rd, inst.rd); + } + + std::string CodeGenerator::translateVU_VMSUB(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_VMSUBq(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q)); " + "__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, + (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_VMSUBi(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); " + "__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, + (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_VADDA_Field(const Instruction &inst) + { + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + + return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); " + "ctx->vu0_acc = res; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern); + } + + std::string CodeGenerator::translateVU_VSUBA_Field(const Instruction &inst) + { + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + + return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); " + "ctx->vu0_acc = res; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern); + } + + std::string CodeGenerator::translateVU_VMADDA_Field(const Instruction &inst) + { + uint8_t field = inst.function & 0x3; + 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); " + "ctx->vu0_acc = res; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern); + } + + std::string CodeGenerator::translateVU_VMSUBA_Field(const Instruction &inst) + { + uint8_t field = inst.function & 0x3; + 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_VSUB(ctx->vu0_acc, mul_res); " + "ctx->vu0_acc = res; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern); + } + + std::string CodeGenerator::translateVU_VMULA_Field(const Instruction &inst) + { + uint8_t field = inst.function & 0x3; + std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); + + return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); " + "ctx->vu0_acc = res; }}", + inst.rs, inst.rt, inst.rt, shuffle_pattern); + } + + std::string CodeGenerator::translateVU_VADDA(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", + inst.rs, inst.rt); + } + + std::string CodeGenerator::translateVU_VADDAq(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VADD(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q));", + inst.rs); + } + + std::string CodeGenerator::translateVU_VADDAi(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VADD(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i));", + inst.rs); + } + + std::string CodeGenerator::translateVU_VSUBA(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", + inst.rs, inst.rt); + } + + std::string CodeGenerator::translateVU_VSUBAq(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VSUB(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q));", + inst.rs); + } + + std::string CodeGenerator::translateVU_VSUBAi(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VSUB(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i));", + inst.rs); + } + + std::string CodeGenerator::translateVU_VMADDA(const Instruction &inst) + { + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); " + "ctx->vu0_acc = PS2_VADD(ctx->vu0_acc, mul_res); }}", + inst.rs, inst.rt); + } + + std::string CodeGenerator::translateVU_VMADDAq(const Instruction &inst) + { + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q)); " + "ctx->vu0_acc = PS2_VADD(ctx->vu0_acc, mul_res); }}", + inst.rs); + } + + std::string CodeGenerator::translateVU_VMADDAi(const Instruction &inst) + { + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); " + "ctx->vu0_acc = PS2_VADD(ctx->vu0_acc, mul_res); }}", + inst.rs); + } + + std::string CodeGenerator::translateVU_VMSUBA(const Instruction &inst) + { + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); " + "ctx->vu0_acc = PS2_VSUB(ctx->vu0_acc, mul_res); }}", + inst.rs, inst.rt); + } + + std::string CodeGenerator::translateVU_VMSUBAq(const Instruction &inst) + { + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q)); " + "ctx->vu0_acc = PS2_VSUB(ctx->vu0_acc, mul_res); }}", + inst.rs); + } + + std::string CodeGenerator::translateVU_VMSUBAi(const Instruction &inst) + { + return fmt::format("{{ __m128 mul_res = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i)); " + "ctx->vu0_acc = PS2_VSUB(ctx->vu0_acc, mul_res); }}", + inst.rs); + } + + std::string CodeGenerator::translateVU_VMULA(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", + inst.rs, inst.rt); + } + + std::string CodeGenerator::translateVU_VMULAq(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_q));", + inst.rs); + } + + std::string CodeGenerator::translateVU_VMULAi(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VMUL(ctx->vu0_vf[{}], _mm_set1_ps(ctx->vu0_i));", + inst.rs); + } + + std::string CodeGenerator::translateVU_VOPMULA(const Instruction &inst) + { + return fmt::format("ctx->vu0_acc = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]);", + inst.rs, inst.rt); + } + + std::string CodeGenerator::translateVU_VITOF(const Instruction &inst, int shift) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + float scale = (shift == 0) ? 1.0f : (1.0f / static_cast(1 << shift)); + + return fmt::format("{{ __m128i src = _mm_castps_si128(ctx->vu0_vf[{}]); " + "__m128 res = _mm_cvtepi32_ps(src); " + "res = _mm_mul_ps(res, _mm_set1_ps({}f)); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", + inst.rs, scale, + (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, + (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + inst.rt, inst.rt); + } + + std::string CodeGenerator::translateVU_VFTOI(const Instruction &inst, int shift) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + float scale = (shift == 0) ? 1.0f : static_cast(1 << shift); + + return fmt::format("{{ __m128 src = ctx->vu0_vf[{}]; " + "src = _mm_mul_ps(src, _mm_set1_ps({}f)); " + "__m128i res_i = _mm_cvttps_epi32(src); " + "__m128 res = _mm_castsi128_ps(res_i); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", + inst.rs, scale, + (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, + (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + inst.rt, inst.rt); + } + + std::string CodeGenerator::translateVU_VLQI(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ uint32_t addr = ((uint32_t)(ctx->vi[{}] & 0x3FF)) << 4; " + "__m128 res = _mm_castsi128_ps(READ128(addr)); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " + "ctx->vi[{}] = (ctx->vi[{}] + 1) & 0x3FF; }}", + 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, + inst.rs, inst.rs); + } + + std::string CodeGenerator::translateVU_VSQI(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ uint32_t addr = ((uint32_t)(ctx->vi[{}] & 0x3FF)) << 4; " + "__m128i old_val = READ128(addr); " + "__m128 res = _mm_blendv_ps(_mm_castsi128_ps(old_val), ctx->vu0_vf[{}], _mm_castsi128_ps(_mm_set_epi32({}, {}, {}, {}))); " + "WRITE128(addr, _mm_castps_si128(res)); " + "ctx->vi[{}] = (ctx->vi[{}] + 1) & 0x3FF; }}", + 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.rs, inst.rs); + } + + std::string CodeGenerator::translateVU_VLQD(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ ctx->vi[{}] = (ctx->vi[{}] - 1) & 0x3FF; " + "uint32_t addr = ((uint32_t)(ctx->vi[{}] & 0x3FF)) << 4; " + "__m128 res = _mm_castsi128_ps(READ128(addr)); " + "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " + "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", + inst.rs, inst.rs, + 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); + } + + std::string CodeGenerator::translateVU_VSQD(const Instruction &inst) + { + uint8_t dest_mask = inst.vectorInfo.vectorField; + return fmt::format("{{ ctx->vi[{}] = (ctx->vi[{}] - 1) & 0x3FF; " + "uint32_t addr = ((uint32_t)(ctx->vi[{}] & 0x3FF)) << 4; " + "__m128i old_val = READ128(addr); " + "__m128 res = _mm_blendv_ps(_mm_castsi128_ps(old_val), ctx->vu0_vf[{}], _mm_castsi128_ps(_mm_set_epi32({}, {}, {}, {}))); " + "WRITE128(addr, _mm_castps_si128(res)); }}", + inst.rs, inst.rs, + inst.rs, + inst.rt, + (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, + (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0); + } + std::string CodeGenerator::translateVU_VRGET(const Instruction &inst) { uint8_t dest_mask = inst.vectorInfo.vectorField; diff --git a/ps2xRecomp/src/lib/r5900_decoder.cpp b/ps2xRecomp/src/lib/r5900_decoder.cpp index f68b576..5444fe7 100644 --- a/ps2xRecomp/src/lib/r5900_decoder.cpp +++ b/ps2xRecomp/src/lib/r5900_decoder.cpp @@ -806,8 +806,15 @@ namespace ps2recomp case COP2_CO + 15: { // Refine based on specific VU function uint8_t vu_func = inst.function; + bool is_special2 = vu_func >= 0x3C; + uint8_t vu_fhi_flo = 0; - if (vu_func == VU0_S2_VDIV || vu_func == VU0_S2_VSQRT || vu_func == VU0_S2_VRSQRT) + if (is_special2) + { + vu_fhi_flo = static_cast((((inst.raw >> 6) & 0x1F) << 2) | (inst.raw & 0x3)); + } + + if (is_special2 && (vu_fhi_flo == VU0_S2_VDIV || vu_fhi_flo == VU0_S2_VSQRT || vu_fhi_flo == VU0_S2_VRSQRT)) { inst.vectorInfo.fsf = (inst.raw >> 10) & 0x3; // Extract bits 10-11 inst.vectorInfo.ftf = (inst.raw >> 8) & 0x3; // Extract bits 8-9 @@ -818,9 +825,9 @@ namespace ps2recomp 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 + if (is_special2) // Special2 Table { - switch (vu_func) + switch (vu_fhi_flo) { case VU0_S2_VDIV: case VU0_S2_VSQRT: @@ -857,6 +864,10 @@ namespace ps2recomp inst.modificationInfo.modifiesControl = true; /* Modifies R */ inst.modificationInfo.modifiesVFR = false; break; // Writes R + case VU0_S2_VWAITQ: + inst.modificationInfo.modifiesVFR = false; + inst.modificationInfo.modifiesControl = false; + break; case VU0_S2_VABS: case VU0_S2_VMOVE: case VU0_S2_VMR32: @@ -871,45 +882,6 @@ namespace ps2recomp 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; } } diff --git a/ps2xTest/src/code_generator_tests.cpp b/ps2xTest/src/code_generator_tests.cpp index 4a72cc5..82b590c 100644 --- a/ps2xTest/src/code_generator_tests.cpp +++ b/ps2xTest/src/code_generator_tests.cpp @@ -2,6 +2,10 @@ #include "ps2recomp/code_generator.h" #include "ps2recomp/instructions.h" #include "ps2recomp/types.h" +#include +#include +#include +#include using namespace ps2recomp; @@ -30,6 +34,34 @@ static Instruction makeNop(uint32_t address) return inst; } +static std::string readFileFromCandidates(const std::vector &candidates) +{ + for (const auto &path : candidates) + { + std::ifstream file(path); + if (file) + { + std::ostringstream ss; + ss << file.rdbuf(); + return ss.str(); + } + } + return {}; +} + +static std::vector parseEnumValues(const std::string &text, const std::string &prefix) +{ + std::vector values; + std::regex re("\\b(" + prefix + "[A-Za-z0-9_]+)\\b\\s*=\\s*0x([0-9A-Fa-f]+)"); + for (auto it = std::sregex_iterator(text.begin(), text.end(), re); it != std::sregex_iterator(); ++it) + { + const auto &match = *it; + uint32_t value = static_cast(std::stoul(match[2].str(), nullptr, 16)); + values.push_back(value); + } + return values; +} + void register_code_generator_tests() { MiniTest::Case("CodeGenerator", [](TestCase &tc) @@ -220,5 +252,60 @@ void register_code_generator_tests() "definition should use sanitized name"); t.IsTrue(generated.find("ps2___is_pointer(rdram, ctx, runtime); return;") != std::string::npos, "call should use sanitized name"); - }); }); + }); + + tc.Run("VU0 macro mappings cover all S1/S2 enums", [](TestCase &t) { + const std::vector candidates = { + "ps2xRecomp/include/ps2recomp/instructions.h", + "../ps2xRecomp/include/ps2recomp/instructions.h", + "../../ps2xRecomp/include/ps2recomp/instructions.h" + }; + + std::string text = readFileFromCandidates(candidates); + t.IsTrue(!text.empty(), "instructions.h should be readable from the test working directory"); + + std::vector s1 = parseEnumValues(text, "VU0_S1_"); + std::vector s2 = parseEnumValues(text, "VU0_S2_"); + t.IsTrue(!s1.empty(), "VU0_S1 enum list should not be empty"); + t.IsTrue(!s2.empty(), "VU0_S2 enum list should not be empty"); + + CodeGenerator gen({}); + + for (uint32_t value : s1) + { + Instruction inst; + inst.opcode = OPCODE_COP2; + inst.rs = COP2_CO; // format + inst.rt = 2; + inst.rd = 3; + inst.function = value; + inst.vectorInfo.vectorField = 0xF; + + std::string out = gen.translateInstruction(inst); + std::ostringstream msg; + msg << "VU0 S1 0x" << std::hex << value << " should be mapped"; + t.IsTrue(out.find("Unhandled VU0 Special1") == std::string::npos, msg.str().c_str()); + } + + for (uint32_t value : s2) + { + Instruction inst; + inst.opcode = OPCODE_COP2; + inst.rs = COP2_CO; // format + inst.rt = 2; + inst.rd = 3; + inst.function = 0x3C; // force Special2 path + inst.vectorInfo.vectorField = 0xF; + + uint32_t upper = (value >> 2) & 0x1F; + uint32_t lower = value & 0x3; + inst.raw = (upper << 6) | lower; + + std::string out = gen.translateInstruction(inst); + std::ostringstream msg; + msg << "VU0 S2 0x" << std::hex << value << " should be mapped"; + t.IsTrue(out.find("Unhandled VU0 Special2") == std::string::npos, msg.str().c_str()); + } + }); + }); }