diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 70128b4c62..fafd6668bf 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -986,7 +986,55 @@ namespace MIPSComp { // d[N] = int(S[N] * mult) // Note: saturates on overflow. - DISABLE; + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + int imm = (op >> 16) & 0x1f; + + u8 sregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixD(dregs, sz, _VD); + + // Same values as FCR31. + uint8_t rmode = (op >> 21) & 3; + if (((op >> 21) & 0x1C) != 0x10) + INVALIDOP; + + u8 tempregs[4]; + for (int i = 0; i < n; ++i) { + if (!IsOverlapSafe(dregs[i], n, sregs)) { + tempregs[i] = IRVTEMP_PFX_T + i; // Need IRVTEMP_0 for the scaling factor + } else { + tempregs[i] = dregs[i]; + } + } + if (imm != 0) { + for (int i = 0; i < n; i++) + ir.Write(IROp::FCvtScaledWS, dregs[i], sregs[i], (uint8_t)(imm | (rmode << 6))); + } else { + for (int i = 0; i < n; i++) { + switch (rmode) { + case 0: // vf2in + ir.Write(IROp::FRound, dregs[i], sregs[i]); + break; + + case 1: // vf2iz + ir.Write(IROp::FTrunc, dregs[i], sregs[i]); + break; + + case 2: // vf2iu + ir.Write(IROp::FCeil, dregs[i], sregs[i]); + break; + + case 3: // vf2id + ir.Write(IROp::FFloor, dregs[i], sregs[i]); + break; + + default: + INVALIDOP; + } + } + } } void IRFrontend::Comp_Mftv(MIPSOpcode op) { diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 116908c656..ed7bd403b0 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -105,6 +105,7 @@ static const IRMeta irMeta[] = { { IROp::FFloor, "FFloor", "FF" }, { IROp::FCvtWS, "FCvtWS", "FF" }, { IROp::FCvtSW, "FCvtSW", "FF" }, + { IROp::FCvtScaledWS, "FCvtScaledWS", "FFI" }, { IROp::FCmp, "FCmp", "mFF" }, { IROp::FSat0_1, "FSat(0 - 1)", "FF" }, { IROp::FSatMinus1_1, "FSat(-1 - 1)", "FF" }, diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index d4eb47f49a..2e5a1afce8 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -127,6 +127,7 @@ enum class IROp : u8 { FCvtWS, FCvtSW, + FCvtScaledWS, FMovFromGPR, FMovToGPR, @@ -304,9 +305,6 @@ enum : IRReg { IRVTEMP_PFX_D = 232 - 32, IRVTEMP_0 = 236 - 32, - // 16 float temps for vector S and T prefixes and things like that. - // IRVTEMP_0 = 208 - 64, // -64 to be relative to v[0] - // Hacky way to get to other state IRREG_VFPU_CTRL_BASE = 208, IRREG_VFPU_CC = 211, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 561adf6f25..22b012787f 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -834,7 +834,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; break; } else { - mips->fs[inst->dest] = (int)floorf(value + 0.5f); + mips->fs[inst->dest] = (int)round_ieee_754(value); } break; } @@ -931,6 +931,32 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { } break; //cvt.w.s } + case IROp::FCvtScaledWS: + { + float src = mips->f[inst->src1]; + if (my_isnan(src)) { + // TODO: True for negatives too? + mips->fs[inst->dest] = 2147483647L; + break; + } + + float mult = (float)(1UL << (inst->src2 & 0x1F)); + double sv = src * mult; // (float)0x7fffffff == (float)0x80000000 + // Cap/floor it to 0x7fffffff / 0x80000000 + if (sv > (double)0x7fffffff) { + mips->fs[inst->dest] = 0x7fffffff; + } else if (sv <= (double)(int)0x80000000) { + mips->fs[inst->dest] = 0x80000000; + } else { + switch (inst->src2 >> 6) { + case 0: mips->fs[inst->dest] = (int)round_ieee_754(sv); break; + case 1: mips->fs[inst->dest] = src >= 0 ? (int)floor(sv) : (int)ceil(sv); break; + case 2: mips->fs[inst->dest] = (int)ceil(sv); break; + case 3: mips->fs[inst->dest] = (int)floor(sv); break; + } + } + break; + } case IROp::ZeroFpCond: mips->fpcond = 0; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 52ea66c5d4..247b3b6865 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -667,6 +667,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::FCeil: case IROp::FFloor: case IROp::FCvtSW: + case IROp::FCvtScaledWS: case IROp::FSin: case IROp::FCos: case IROp::FSqrt: diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 84c7faa832..38d23f900c 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -190,6 +190,7 @@ void RiscVJit::CompIR_FCvt(IRInst inst) { switch (inst.op) { case IROp::FCvtWS: + case IROp::FCvtScaledWS: case IROp::FCvtSW: CompIR_Generic(inst); break; diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index b94369c4ca..6edea8d25e 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -265,6 +265,7 @@ void RiscVJit::CompileIRInst(IRInst inst) { case IROp::FCvtWS: case IROp::FCvtSW: + case IROp::FCvtScaledWS: CompIR_FCvt(inst); break;