diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 089ace124c..83911bcfa0 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -100,7 +100,7 @@ namespace MIPSComp js.prefixTFlag = JitState::PREFIX_KNOWN_DIRTY; break; case 2: // D - js.prefixD = data; + js.prefixD = data & 0x00000FFF; js.prefixDFlag = JitState::PREFIX_KNOWN_DIRTY; break; default: @@ -1333,18 +1333,21 @@ namespace MIPSComp NEON_IF_AVAILABLE(CompNEON_Vmtvc); CONDITIONAL_DISABLE(VFPU_XFER); - int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - fpr.MapRegV(vs); - if (imm - 128 == VFPU_CTRL_CC) { + int vd = _VD; + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { + fpr.MapRegV(vd); + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, 0); - VMOV(fpr.V(vs), gpr.R(MIPS_REG_VFPUCC)); + VMOV(fpr.V(vd), gpr.R(MIPS_REG_VFPUCC)); } else { - ADDI2R(SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + (imm - 128) * 4, SCRATCHREG2); - VLDR(fpr.V(vs), SCRATCHREG1, 0); + ADDI2R(SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + imm * 4, SCRATCHREG2); + VLDR(fpr.V(vd), SCRATCHREG1, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); + } else { + fpr.MapRegV(vd); + MOVI2F(fpr.V(vd), 0.0f, SCRATCHREG1); } } @@ -1353,23 +1356,23 @@ namespace MIPSComp CONDITIONAL_DISABLE(VFPU_XFER); int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + int imm = op & 0x7F; + if (imm < VFPU_CTRL_MAX) { fpr.MapRegV(vs); - if (imm - 128 == VFPU_CTRL_CC) { + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, MAP_DIRTY | MAP_NOINIT); VMOV(gpr.R(MIPS_REG_VFPUCC), fpr.V(vs)); } else { - ADDI2R(SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + (imm - 128) * 4, SCRATCHREG2); + ADDI2R(SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + imm * 4, SCRATCHREG2); VSTR(fpr.V(vs), SCRATCHREG1, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); - if (imm - 128 == VFPU_CTRL_SPREFIX) { + if (imm == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_TPREFIX) { + } else if (imm == VFPU_CTRL_TPREFIX) { js.prefixTFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_DPREFIX) { + } else if (imm == VFPU_CTRL_DPREFIX) { js.prefixDFlag = JitState::PREFIX_UNKNOWN; } } diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 871c7e99ea..b51e730208 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -93,7 +93,7 @@ namespace MIPSComp { js.prefixTFlag = JitState::PREFIX_KNOWN_DIRTY; break; case 2: // D - js.prefixD = data; + js.prefixD = data & 0x00000FFF; js.prefixDFlag = JitState::PREFIX_KNOWN_DIRTY; break; default: @@ -1066,18 +1066,21 @@ namespace MIPSComp { void Arm64Jit::Comp_Vmfvc(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); - int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - fpr.MapRegV(vs); - if (imm - 128 == VFPU_CTRL_CC) { + int vd = _VD; + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { + fpr.MapRegV(vd); + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, 0); - fp.FMOV(fpr.V(vs), gpr.R(MIPS_REG_VFPUCC)); + fp.FMOV(fpr.V(vd), gpr.R(MIPS_REG_VFPUCC)); } else { - ADDI2R(SCRATCH1_64, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + (imm - 128) * 4, SCRATCH2); - fp.LDR(32, INDEX_UNSIGNED, fpr.V(vs), SCRATCH1_64, 0); + ADDI2R(SCRATCH1_64, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + imm * 4, SCRATCH2); + fp.LDR(32, INDEX_UNSIGNED, fpr.V(vd), SCRATCH1_64, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); + } else { + fpr.MapRegV(vd); + MOVI2F(fpr.V(vd), 0.0f, SCRATCH1); } } @@ -1085,23 +1088,23 @@ namespace MIPSComp { CONDITIONAL_DISABLE(VFPU_XFER); int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + int imm = op & 0x7F; + if (imm < VFPU_CTRL_MAX) { fpr.MapRegV(vs); - if (imm - 128 == VFPU_CTRL_CC) { + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, MAP_DIRTY | MAP_NOINIT); fp.FMOV(gpr.R(MIPS_REG_VFPUCC), fpr.V(vs)); } else { - ADDI2R(SCRATCH1_64, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + (imm - 128) * 4, SCRATCH2); + ADDI2R(SCRATCH1_64, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + imm * 4, SCRATCH2); fp.STR(32, INDEX_UNSIGNED, fpr.V(vs), SCRATCH1_64, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); - if (imm - 128 == VFPU_CTRL_SPREFIX) { + if (imm == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_TPREFIX) { + } else if (imm == VFPU_CTRL_TPREFIX) { js.prefixTFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_DPREFIX) { + } else if (imm == VFPU_CTRL_DPREFIX) { js.prefixDFlag = JitState::PREFIX_UNKNOWN; } } diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 6393fff4a9..e319cee888 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -108,8 +108,28 @@ namespace MIPSComp { return true; } + static bool IsPrefixWithinSize(u32 prefix, VectorSize sz) { + int n = GetNumVectorElements(sz); + for (int i = n; i < 4; i++) { + int regnum = (prefix >> (i * 2)) & 3; + int abs = (prefix >> (8 + i)) & 1; + int negate = (prefix >> (16 + i)) & 1; + int constants = (prefix >> (12 + i)) & 1; + if (regnum < n || abs || negate || constants) { + return false; + } + } + + return true; + } + + static bool IsPrefixWithinSize(u32 prefix, MIPSOpcode op) { + return IsPrefixWithinSize(prefix, GetVecSize(op)); + } + void IRFrontend::Comp_VPFX(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); + // This is how prefixes are typically set. int data = op & 0xFFFFF; int regnum = (op >> 24) & 3; switch (regnum) { @@ -122,7 +142,7 @@ namespace MIPSComp { js.prefixTFlag = JitState::PREFIX_KNOWN_DIRTY; break; case 2: // D - js.prefixD = data; + js.prefixD = data & 0x00000FFF; js.prefixDFlag = JitState::PREFIX_KNOWN_DIRTY; break; default: @@ -183,14 +203,10 @@ namespace MIPSComp { // This puts the value into a temp reg, so we won't write the modified value back. vregs[i] = tempReg + i; if (!constants) { - // Prefix may say "z, z, z, z" but if this is a pair, we force to x. - // TODO: But some ops seem to use const 0 instead? if (regnum >= n) { - WARN_LOG(CPU, "JIT: Invalid VFPU swizzle: %08x : %d / %d at PC = %08x (%s)", prefix, regnum, n, GetCompilerPC(), MIPSDisasmAt(GetCompilerPC())); - regnum = 0; - } - - if (abs) { + // Depends on the op, but often zero. + ir.Write(IROp::SetConstF, vregs[i], ir.AddConstantFloat(0.0f)); + } else if (abs) { ir.Write(IROp::FAbs, vregs[i], origV[regnum]); if (negate) ir.Write(IROp::FNeg, vregs[i], vregs[i]); @@ -343,12 +359,13 @@ namespace MIPSComp { void IRFrontend::Comp_VVectorInit(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || js.HasSPrefix()) { DISABLE; } // Vector init // d[N] = CONST[N] + // Note: probably implemented as vmov with prefix hack. VectorSize sz = GetVecSize(op); int type = (op >> 16) & 0xF; @@ -369,12 +386,13 @@ namespace MIPSComp { void IRFrontend::Comp_VIdt(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || js.HasSPrefix()) { DISABLE; } // Vector identity row // d[N] = IDENTITY[N,m] + // Note: probably implemented as vmov with prefix hack. int vd = _VD; VectorSize sz = GetVecSize(op); @@ -408,11 +426,11 @@ namespace MIPSComp { void IRFrontend::Comp_VMatrixInit(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); MatrixSize sz = GetMtxSize(op); - if (sz != M_4x4) { + if (sz != M_4x4 || !js.HasNoPrefix()) { DISABLE; } - // Matrix init (no prefixes) + // Matrix init (weird prefixes) // d[N,M] = CONST[N,M] // Not really about trying here, it will work if enabled. @@ -448,18 +466,22 @@ namespace MIPSComp { void IRFrontend::Comp_VHdp(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || !IsPrefixWithinSize(js.prefixT, op)) { DISABLE; } // Vector homogenous dot product // d[0] = s[0 .. n-2] dot t[0 .. n-2] + t[n-1] - // Note: s[n-1] is ignored / treated as 1. + // Note: s[n-1] is ignored / treated as 1 via prefix override. int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + if (js.prefixS & (0x0101 << (8 + n - 1))) + DISABLE; // TODO: Force read one of them into regs? probably not. u8 sregs[4], tregs[4], dregs[1]; @@ -469,9 +491,7 @@ namespace MIPSComp { ir.Write(IROp::FMul, IRVTEMP_0, sregs[0], tregs[0]); - int n = GetNumVectorElements(sz); for (int i = 1; i < n; i++) { - // sum += s[i]*t[i]; if (i == n - 1) { ir.Write(IROp::FAdd, IRVTEMP_0, IRVTEMP_0, tregs[i]); } else { @@ -488,14 +508,15 @@ namespace MIPSComp { void IRFrontend::Comp_Vhoriz(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix()) { DISABLE; + } // Vector horizontal add // d[0] = s[0] + ... s[n-1] // Vector horizontal average - // d[0] = (s[0] + ... s[n-1]) / n + // d[0] = s[0] / n + ... s[n-1] / n + // Note: Both are implemented as dot products against generated constants. VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); @@ -525,7 +546,7 @@ namespace MIPSComp { void IRFrontend::Comp_VDot(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || !IsPrefixWithinSize(js.prefixT, op)) { DISABLE; } @@ -563,8 +584,9 @@ namespace MIPSComp { void IRFrontend::Comp_VecDo3(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || !IsPrefixWithinSize(js.prefixT, op)) { DISABLE; + } // Vector arithmetic // d[N] = OP(s[N], t[N]) (see below) @@ -576,7 +598,11 @@ namespace MIPSComp { switch ((op >> 23) & 7) { case 0: // d[i] = s[i] + t[i]; break; //vadd case 1: // d[i] = s[i] - t[i]; break; //vsub + break; case 7: // d[i] = s[i] / t[i]; break; //vdiv + if (!js.HasNoPrefix()) { + DISABLE; + } break; default: INVALIDOP; @@ -722,7 +748,7 @@ namespace MIPSComp { void IRFrontend::Comp_VV2Op(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || !IsPrefixWithinSize(js.prefixT, op)) DISABLE; // Vector unary operation @@ -731,8 +757,17 @@ namespace MIPSComp { int vs = _VS; int vd = _VD; + int optype = (op >> 16) & 0x1f; + if (optype >= 16 && !js.HasNoPrefix()) { + DISABLE; + } else if ((optype == 1 || optype == 2) && js.HasSPrefix()) { + DISABLE; + } else if (optype == 5 && js.HasDPrefix()) { + DISABLE; + } + // Pre-processing: Eliminate silly no-op VMOVs, common in Wipeout Pure - if (((op >> 16) & 0x1f) == 0 && vs == vd && js.HasNoPrefix()) { + if (optype == 0 && vs == vd && js.HasNoPrefix()) { return; } @@ -756,7 +791,7 @@ namespace MIPSComp { bool canSIMD = false; // Some can be SIMD'd. - switch ((op >> 16) & 0x1f) { + switch (optype) { case 0: // vmov case 1: // vabs case 2: // vneg @@ -765,7 +800,7 @@ namespace MIPSComp { } if (canSIMD && !usingTemps && IsConsecutive4(sregs) && IsConsecutive4(dregs)) { - switch ((op >> 16) & 0x1f) { + switch (optype) { case 0: // vmov ir.Write(IROp::Vec4Mov, dregs[0], sregs[0]); break; @@ -781,7 +816,7 @@ namespace MIPSComp { } for (int i = 0; i < n; ++i) { - switch ((op >> 16) & 0x1f) { + switch (optype) { case 0: // d[i] = s[i]; break; //vmov // Probably for swizzle. ir.Write(IROp::FMov, tempregs[i], sregs[i]); @@ -848,7 +883,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vi2f(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op)) { DISABLE; } @@ -895,6 +930,9 @@ namespace MIPSComp { void IRFrontend::Comp_Vh2f(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op)) { + DISABLE; + } // Vector expand half to float // d[N*2] = float(lowerhalf(s[N])), d[N*2+1] = float(upperhalf(s[N])) @@ -904,6 +942,9 @@ namespace MIPSComp { void IRFrontend::Comp_Vf2i(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || (js.prefixD & 0xFF) != 0) { + DISABLE; + } // Vector float to integer // d[N] = int(S[N] * mult) @@ -919,7 +960,7 @@ namespace MIPSComp { MIPSGPReg rt = _RT; switch ((op >> 21) & 0x1f) { case 3: //mfv / mfvc - // rt = 0, imm = 255 appears to be used as a CPU interlock by some games. + // rt = 0, imm = 255 appears to be used as a CPU interlock by some games. if (rt != MIPS_REG_ZERO) { if (imm < 128) { //R(rt) = VI(imm); ir.Write(IROp::FMovToGPR, rt, vfpuBase + voffset[imm]); @@ -944,7 +985,15 @@ namespace MIPSComp { if (imm < 128) { ir.Write(IROp::FMovFromGPR, vfpuBase + voffset[imm], rt); } else if ((imm - 128) < VFPU_CTRL_MAX) { - ir.Write(IROp::SetCtrlVFPU, imm - 128, rt); + u32 mask; + if (GetVFPUCtrlMask(imm - 128, &mask)) { + if (mask != 0xFFFFFFFF) { + ir.Write(IROp::AndConst, IRTEMP_0, rt, ir.AddConstant(mask)); + ir.Write(IROp::SetCtrlVFPUReg, imm - 128, IRTEMP_0); + } else { + ir.Write(IROp::SetCtrlVFPU, imm - 128, rt); + } + } if (imm - 128 == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; @@ -969,13 +1018,13 @@ namespace MIPSComp { CONDITIONAL_DISABLE(VFPU_XFER); // Vector Move from vector control reg (no prefixes) - // S[0] = VFPU_CTRL[i] + // D[0] = VFPU_CTRL[i] - int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - ir.Write(IROp::VfpuCtrlToReg, IRTEMP_0, imm - 128); - ir.Write(IROp::FMovFromGPR, vfpuBase + voffset[vs], IRTEMP_0); + int vd = _VD; + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { + ir.Write(IROp::VfpuCtrlToReg, IRTEMP_0, imm); + ir.Write(IROp::FMovFromGPR, vfpuBase + voffset[vd], IRTEMP_0); } else { INVALIDOP; } @@ -989,13 +1038,22 @@ namespace MIPSComp { int vs = _VS; int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - ir.Write(IROp::SetCtrlVFPUFReg, imm - 128, vfpuBase + voffset[vs]); - if (imm - 128 == VFPU_CTRL_SPREFIX) { + if (imm < VFPU_CTRL_MAX) { + u32 mask; + if (GetVFPUCtrlMask(imm, &mask)) { + if (mask != 0xFFFFFFFF) { + ir.Write(IROp::FMovToGPR, IRTEMP_0, vfpuBase + voffset[imm]); + ir.Write(IROp::AndConst, IRTEMP_0, IRTEMP_0, ir.AddConstant(mask)); + ir.Write(IROp::SetCtrlVFPUReg, imm, IRTEMP_0); + } else { + ir.Write(IROp::SetCtrlVFPUFReg, imm, vfpuBase + voffset[vs]); + } + } + if (imm == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_TPREFIX) { + } else if (imm == VFPU_CTRL_TPREFIX) { js.prefixTFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_DPREFIX) { + } else if (imm == VFPU_CTRL_DPREFIX) { js.prefixDFlag = JitState::PREFIX_UNKNOWN; } } else { @@ -1005,8 +1063,11 @@ namespace MIPSComp { void IRFrontend::Comp_Vmmov(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX); + if (!js.HasNoPrefix()) { + DISABLE; + } - // Matrix move (no prefixes) + // Matrix move (weird prefixes) // D[N,M] = S[N,M] int vs = _VS; @@ -1062,9 +1123,13 @@ namespace MIPSComp { void IRFrontend::Comp_Vmscl(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX); + if (!js.HasNoPrefix()) { + DISABLE; + } - // Matrix scale, matrix by scalar (no prefixes) + // Matrix scale, matrix by scalar (weird prefixes) // d[N,M] = s[N,M] * t[0] + // Note: behaves just slightly differently than a series of vscls. int vs = _VS; int vd = _VD; @@ -1100,7 +1165,7 @@ namespace MIPSComp { void IRFrontend::Comp_VScl(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix()) { DISABLE; } @@ -1178,7 +1243,7 @@ namespace MIPSComp { DISABLE; } - // Matrix multiply (wierd prefixes) + // Matrix multiply (weird prefixes) // D[0 .. N, 0 .. M] = S[0 .. N, 0 .. M]' * T[0 .. N, 0 .. M] // Note: Behaves as if it's implemented through a series of vdots. // Important: this is a matrix multiply with a pre-transposed S. @@ -1262,10 +1327,14 @@ namespace MIPSComp { void IRFrontend::Comp_Vtfm(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX); + if (!js.HasNoPrefix()) { + DISABLE; + } - // Vertex transform, vector by matrix (no prefixes) + // Vertex transform, vector by matrix (weird prefixes) // d[N] = s[N*m .. N*m + n-1] dot t[0 .. n-1] // Homogenous means t[n-1] is treated as 1. + // Note: this might be implemented as a series of vdots with special prefixes. VectorSize sz = GetVecSize(op); MatrixSize msz = GetMtxSize(op); @@ -1281,7 +1350,7 @@ namespace MIPSComp { } // Otherwise, n should already be ins + 1. else if (n != ins + 1) { - INVALIDOP; + DISABLE; } u8 sregs[16], dregs[4], tregs[4]; @@ -1364,7 +1433,7 @@ namespace MIPSComp { void IRFrontend::Comp_VCrs(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || js.HasSPrefix() || js.HasTPrefix()) { DISABLE; } @@ -1378,7 +1447,7 @@ namespace MIPSComp { void IRFrontend::Comp_VDet(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || (js.prefixT & 0x000CFCF0) != 0x000E0) { DISABLE; } @@ -1391,8 +1460,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vi2x(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || js.HasSPrefix()) DISABLE; int bits = ((op >> 16) & 2) == 0 ? 8 : 16; // vi2uc/vi2c (0/1), vi2us/vi2s (2/3) @@ -1476,8 +1544,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vx2i(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || js.HasSPrefix()) DISABLE; int bits = ((op >> 16) & 2) == 0 ? 8 : 16; // vuc2i/vc2i (0/1), vus2i/vs2i (2/3) @@ -1576,13 +1643,12 @@ namespace MIPSComp { void IRFrontend::Comp_VCrossQuat(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - // TODO: Does this instruction even look at prefixes at all? - if (js.HasUnknownPrefix()) + if (!js.HasNoPrefix()) DISABLE; - // Vector cross product (n = 3) + // Vector cross product (n = 3, weird prefixes) // d[0 .. 2] = s[0 .. 2] X t[0 .. 2] - // Vector quaternion product (n = 4) + // Vector quaternion product (n = 4, weird prefixes) // d[0 .. 2] = t[0 .. 2] X s[0 .. 2] + s[3] * t[0 .. 2] + t[3] * s[0 .. 2] // d[3] = s[3]*t[3] - s[0 .. 2] dot t[0 .. 3] @@ -1622,19 +1688,21 @@ namespace MIPSComp { ir.Write(IROp::FSub, tempregs[2], temp0, temp1); } else if (sz == V_Quad) { DISABLE; + } else { + DISABLE; } for (int i = 0; i < n; i++) { if (tempregs[i] != dregs[i]) ir.Write(IROp::FMov, dregs[i], tempregs[i]); } - // No D prefix supported } void IRFrontend::Comp_Vcmp(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_COMP); - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || !IsPrefixWithinSize(js.prefixT, op)) { DISABLE; + } // Vector compare // VFPU_CC[N] = COMPARE(s[N], t[N]) @@ -1657,7 +1725,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vcmov(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_COMP); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix()) { DISABLE; } @@ -1803,7 +1871,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vsgn(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || !IsPrefixWithinSize(js.prefixT, op)) { DISABLE; } @@ -1841,7 +1909,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vocp(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix() || (js.prefixS & VFPU_NEGATE(1, 1, 1, 1)) != 0) { DISABLE; } @@ -1887,8 +1955,9 @@ namespace MIPSComp { void IRFrontend::Comp_ColorConv(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix()) { DISABLE; + } // Vector color conversion // d[N] = ConvertTo16(s[N*2]) | (ConvertTo16(s[N*2+1]) << 16) @@ -1898,8 +1967,9 @@ namespace MIPSComp { void IRFrontend::Comp_Vbfy(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix()) { DISABLE; + } // Vector butterfly operation // vbfy2: d[0] = s[0] + s[2], d[1] = s[1] + s[3], d[2] = s[0] - s[2], d[3] = s[1] - s[3] @@ -1926,7 +1996,7 @@ namespace MIPSComp { } int subop = (op >> 16) & 0x1F; - if (subop == 3) { + if (subop == 3 && n == 4) { // vbfy2 ir.Write(IROp::FAdd, tempregs[0], sregs[0], sregs[2]); ir.Write(IROp::FAdd, tempregs[1], sregs[1], sregs[3]); diff --git a/Core/MIPS/JitCommon/JitState.h b/Core/MIPS/JitCommon/JitState.h index ac16bcf7da..6de3cf1962 100644 --- a/Core/MIPS/JitCommon/JitState.h +++ b/Core/MIPS/JitCommon/JitState.h @@ -99,13 +99,23 @@ namespace MIPSComp { prefixDFlag = PREFIX_UNKNOWN; } + bool HasSPrefix() const { + return (prefixSFlag & PREFIX_KNOWN) == 0 || prefixS != 0xE4; + } + + bool HasTPrefix() const { + return (prefixTFlag & PREFIX_KNOWN) == 0 || prefixT != 0xE4; + } + + bool HasDPrefix() const { + return (prefixDFlag & PREFIX_KNOWN) == 0 || prefixD != 0x0; + } + bool MayHavePrefix() const { if (HasUnknownPrefix()) { return true; } else if (prefixS != 0xE4 || prefixT != 0xE4 || prefixD != 0) { return true; - } else if (VfpuWriteMask() != 0) { - return true; } return false; } @@ -118,22 +128,19 @@ namespace MIPSComp { } bool HasNoPrefix() const { - return (prefixDFlag & PREFIX_KNOWN) && (prefixSFlag & PREFIX_KNOWN) && (prefixTFlag & PREFIX_KNOWN) && (prefixS == 0xE4 && prefixT == 0xE4 && prefixD == 0); + return !HasSPrefix() && !HasTPrefix() && !HasDPrefix(); } void EatPrefix() { - if ((prefixSFlag & PREFIX_KNOWN) == 0 || prefixS != 0xE4) { + if (HasSPrefix()) prefixSFlag = PREFIX_KNOWN_DIRTY; - prefixS = 0xE4; - } - if ((prefixTFlag & PREFIX_KNOWN) == 0 || prefixT != 0xE4) { + prefixS = 0xE4; + if (HasTPrefix()) prefixTFlag = PREFIX_KNOWN_DIRTY; - prefixT = 0xE4; - } - if ((prefixDFlag & PREFIX_KNOWN) == 0 || prefixD != 0x0) { + prefixT = 0xE4; + if (HasDPrefix()) prefixDFlag = PREFIX_KNOWN_DIRTY; - prefixD = 0x0; - } + prefixD = 0x0; } u8 VfpuWriteMask() const { diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 012579299e..b390b4cfe3 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -96,14 +96,14 @@ inline float nanclamp(float f, float lower, float upper) } -void ApplyPrefixST(float *r, u32 data, VectorSize size) +void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) { // Possible optimization shortcut: if (data == 0xe4) return; int n = GetNumVectorElements(size); - float origV[4]; + float origV[4]{ invalid, invalid, invalid, invalid }; static const float constantArray[8] = {0.f, 1.f, 2.f, 0.5f, 3.f, 1.f/3.f, 0.25f, 1.f/6.f}; for (int i = 0; i < n; i++) @@ -120,19 +120,9 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) if (!constants) { - // Prefix may say "z, z, z, z" but if this is a pair, we force to x. - // TODO: But some ops seem to use const 0 instead? - if (regnum >= n) { - ERROR_LOG_REPORT(CPU, "Invalid VFPU swizzle: %08x: %i / %d at PC = %08x (%s)", data, regnum, n, currentMIPS->pc, MIPSDisasmAt(currentMIPS->pc)); - //for (int i = 0; i < 12; i++) { - // ERROR_LOG(CPU, " vfpuCtrl[%i] = %08x", i, currentMIPS->vfpuCtrl[i]); - //} - regnum = 0; - } - r[i] = origV[regnum]; if (abs) - r[i] = fabs(r[i]); + ((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF; } else { @@ -140,18 +130,18 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) } if (negate) - r[i] = -r[i]; + ((u32 *)r)[i] = ((u32 *)r)[i] ^ 0x80000000; } } -inline void ApplySwizzleS(float *v, VectorSize size) +inline void ApplySwizzleS(float *v, VectorSize size, float invalid = 0.0f) { - ApplyPrefixST(v, currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX], size); + ApplyPrefixST(v, currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX], size, invalid); } -inline void ApplySwizzleT(float *v, VectorSize size) +inline void ApplySwizzleT(float *v, VectorSize size, float invalid = 0.0f) { - ApplyPrefixST(v, currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX], size); + ApplyPrefixST(v, currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX], size, invalid); } void ApplyPrefixD(float *v, VectorSize size, bool onlyWriteMask = false) @@ -170,6 +160,23 @@ void ApplyPrefixD(float *v, VectorSize size, bool onlyWriteMask = false) } } +static void RetainInvalidSwizzleST(float *d, VectorSize sz) { + // Somehow it's like a supernan, maybe wires through to zero? + // Doesn't apply to all ops. + int sPrefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; + int tPrefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; + int n = GetNumVectorElements(sz); + + for (int i = 0; i < n; i++) { + int swizzleS = (sPrefix >> (i + i)) & 3; + int swizzleT = (tPrefix >> (i + i)) & 3; + int constS = (sPrefix >> (12 + i)) & 1; + int constT = (tPrefix >> (12 + i)) & 1; + if ((swizzleS >= n && !constS) || (swizzleT >= n && !constT)) + d[i] = 0.0f; + } +} + void EatPrefixes() { currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX] = 0xe4; // passthru @@ -181,8 +188,10 @@ namespace MIPSInt { void Int_VPFX(MIPSOpcode op) { - int data = op & 0xFFFFF; + int data = op & 0x000FFFFF; int regnum = (op >> 24) & 3; + if (regnum == VFPU_CTRL_DPREFIX) + data &= 0x00000FFF; currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX + regnum] = data; PC += 4; } @@ -298,24 +307,20 @@ namespace MIPSInt PC += 4; } - void Int_VMatrixInit(MIPSOpcode op) - { - static const float idt[16] = - { + void Int_VMatrixInit(MIPSOpcode op) { + static const float idt[16] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, }; - static const float zero[16] = - { + static const float zero[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }; - static const float one[16] = - { + static const float one[16] = { 1,1,1,1, 1,1,1,1, 1,1,1,1, @@ -325,8 +330,7 @@ namespace MIPSInt MatrixSize sz = GetMtxSize(op); const float *m; - switch ((op >> 16) & 0xF) - { + switch ((op >> 16) & 0xF) { case 3: m=idt; break; //identity // vmidt case 6: m=zero; break; // vmzero case 7: m=one; break; // vmone @@ -337,7 +341,25 @@ namespace MIPSInt return; } - WriteMatrix(m, sz, vd); + // The S prefix generates constants, but only for the final (possibly transposed) row. + if (currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX] & 0xF0F00) { + float prefixed[16]; + memcpy(prefixed, m, sizeof(prefixed)); + + int off = GetMatrixSide(sz) - 1; + u32 sprefixRemove = VFPU_ANY_SWIZZLE(); + u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1); + switch ((op >> 16) & 0xF) { + case 3: sprefixAdd |= VFPU_SWIZZLE(off == 0, off == 1, off == 2, off == 3); break; + case 6: sprefixAdd |= VFPU_SWIZZLE(0, 0, 0, 0); break; + case 7: sprefixAdd |= VFPU_SWIZZLE(1, 1, 1, 1); break; + } + ApplyPrefixST(&prefixed[off * 4], VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); + WriteMatrix(prefixed, sz, vd); + } else { + // Write mask applies to the final (maybe transposed) row. Sat causes hang. + WriteMatrix(m, sz, vd); + } PC += 4; EatPrefixes(); } @@ -346,86 +368,73 @@ namespace MIPSInt { int vd = _VD; VectorSize sz = GetVecSize(op); - int n = GetNumVectorElements(sz); - static const float ones[4] = {1,1,1,1}; - static const float zeros[4] = {0,0,0,0}; - const float *v; - switch ((op >> 16) & 0xF) - { - case 6: v=zeros; break; //vzero - case 7: v=ones; break; //vone + float d[4]; + + int constant = 0; + switch ((op >> 16) & 0xF) { + case 6: constant = 0; break; //vzero + case 7: constant = 1; break; //vone default: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); PC += 4; EatPrefixes(); return; } - float o[4]; - for (int i = 0; i < n; i++) - o[i] = v[i]; - ApplyPrefixD(o, sz); - WriteVector(o, sz, vd); + + // The S prefix generates constants, but negate is still respected. + u32 sprefixRemove = VFPU_ANY_SWIZZLE(); + u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(constant, constant, constant, constant); + ApplyPrefixST(d, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); + + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); EatPrefixes(); PC += 4; } - void Int_Viim(MIPSOpcode op) - { + void Int_Viim(MIPSOpcode op) { int vt = _VT; s32 imm = (s16)(op&0xFFFF); u16 uimm16 = (op&0xFFFF); - //V(vt) = (float)imm; float f[1]; int type = (op >> 23) & 7; - if (type == 6) + if (type == 6) { f[0] = (float)imm; // viim - else if (type == 7) + } else if (type == 7) { f[0] = Float16ToFloat32((u16)uimm16); // vfim - else - { + } else { _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); f[0] = 0; } ApplyPrefixD(f, V_Single); - V(vt) = f[0]; + WriteVector(f, V_Single, vt); PC += 4; EatPrefixes(); } - void Int_Vidt(MIPSOpcode op) - { + void Int_Vidt(MIPSOpcode op) { int vd = _VD; VectorSize sz = GetVecSize(op); float f[4]; - switch (sz) - { - case V_Pair: - f[0] = (vd&1)==0 ? 1.0f : 0.0f; - f[1] = (vd&1)==1 ? 1.0f : 0.0f; - break; - case V_Quad: - f[0] = (vd&3)==0 ? 1.0f : 0.0f; - f[1] = (vd&3)==1 ? 1.0f : 0.0f; - f[2] = (vd&3)==2 ? 1.0f : 0.0f; - f[3] = (vd&3)==3 ? 1.0f : 0.0f; - break; - default: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); - break; - } + + // The S prefix generates constants, but negate is still respected. + int offmask = sz == V_Quad || sz == V_Triple ? 3 : 1; + int off = vd & offmask; + u32 sprefixRemove = VFPU_ANY_SWIZZLE(); + u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(off == 0, off == 1, off == (2 & offmask), off == (3 & offmask)); + ApplyPrefixST(f, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); + ApplyPrefixD(f, sz); WriteVector(f, sz, vd); PC += 4; EatPrefixes(); } + // The test really needs some work. - void Int_Vmmul(MIPSOpcode op) - { - float s[16]; - float t[16]; - float d[16]; + void Int_Vmmul(MIPSOpcode op) { + float s[16]{}, t[16]{}, d[16]; int vd = _VD; int vs = _VS; @@ -436,29 +445,37 @@ namespace MIPSInt ReadMatrix(s, sz, vs); ReadMatrix(t, sz, vt); - for (int a = 0; a < n; a++) - { - for (int b = 0; b < n; b++) - { + for (int a = 0; a < n; a++) { + for (int b = 0; b < n; b++) { float sum = 0.0f; - for (int c = 0; c < n; c++) - { - sum += s[b*4 + c] * t[a*4 + c]; + if (a == n - 1 && b == n - 1) { + // S and T prefixes work on the final (or maybe first, in reverse?) dot. + ApplySwizzleS(&s[b * 4], V_Quad); + ApplySwizzleT(&t[a * 4], V_Quad); + for (int c = 0; c < 4; c++) { + sum += s[b * 4 + c] * t[a * 4 + c]; + } + } else { + for (int c = 0; c < n; c++) { + sum += s[b * 4 + c] * t[a * 4 + c]; + } } - d[a*4 + b] = sum; + d[a * 4 + b] = sum; } } + // The D prefix applies ONLY to the final element, but sat does work. + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(&d[4 * (n - 1)], V_Quad, false); WriteMatrix(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vmscl(MIPSOpcode op) - { - float d[16]; - float s[16]; - float t[1]; + void Int_Vmscl(MIPSOpcode op) { + float s[16]{}, t[4]{}, d[16]; int vd = _VD; int vs = _VS; @@ -469,27 +486,41 @@ namespace MIPSInt ReadMatrix(s, sz, vs); ReadVector(t, V_Single, vt); - for (int a = 0; a < n; a++) - { - for (int b = 0; b < n; b++) - { - d[a*4 + b] = s[a*4 + b] * t[0]; + for (int a = 0; a < n - 1; a++) { + for (int b = 0; b < n; b++) { + d[a * 4 + b] = s[a * 4 + b] * t[0]; } } + // S prefix applies to the last row. + ApplySwizzleS(&s[(n - 1) * 4], V_Quad); + // T prefix applies only for the last row, and is used per element. + // This is like vscl, but instead of zzzz it uses xxxx. + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_SWIZZLE(0, 0, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + for (int b = 0; b < n; b++) { + d[(n - 1) * 4 + b] = s[(n - 1) * 4 + b] * t[b]; + } + + // The D prefix is applied to the last row. + ApplyPrefixD(&d[(n - 1) * 4], V_Quad); WriteMatrix(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vmmov(MIPSOpcode op) - { - float s[16]; + void Int_Vmmov(MIPSOpcode op) { + float s[16]{}; int vd = _VD; int vs = _VS; MatrixSize sz = GetMtxSize(op); ReadMatrix(s, sz, vs); - // This is just for matrices. No prefixes. + // S and D prefixes are applied to the last row. + int off = GetMatrixSide(sz) - 1; + ApplySwizzleS(&s[off * 4], V_Quad); + ApplyPrefixD(&s[off * 4], V_Quad); WriteMatrix(s, sz, vd); PC += 4; EatPrefixes(); @@ -497,25 +528,57 @@ namespace MIPSInt void Int_Vflush(MIPSOpcode op) { - // DEBUG_LOG(CPU,"vflush"); + VERBOSE_LOG(CPU, "vflush"); PC += 4; + // Anything with 0xFC000000 is a nop, but only 0xFFFF0000 retains prefixes. + if ((op & 0xFFFF0000) != 0xFFFF0000) + EatPrefixes(); } - void Int_VV2Op(MIPSOpcode op) - { + void Int_VV2Op(MIPSOpcode op) { float s[4], d[4]; int vd = _VD; int vs = _VS; + int optype = (op >> 16) & 0x1f; VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) - { - switch ((op >> 16) & 0x1f) - { + // Some of these are prefix hacks (affects constants, etc.) + switch (optype) { + case 1: + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, 0, VFPU_ABS(1, 1, 1, 1)), sz); + break; + case 2: + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, 0, VFPU_NEGATE(1, 1, 1, 1)), sz); + break; + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + // Similar to vdiv. Some of the behavior using the invalid constant is iffy. + ApplySwizzleS(&s[n - 1], V_Single, INFINITY); + break; + case 24: + case 26: + // Similar to above, but also ignores negate. + ApplyPrefixST(&s[n - 1], VFPURewritePrefix(VFPU_CTRL_SPREFIX, VFPU_NEGATE(1, 0, 0, 0), 0), V_Single, -INFINITY); + break; + case 28: + // Similar to above, but also ignores negate. + ApplyPrefixST(&s[n - 1], VFPURewritePrefix(VFPU_CTRL_SPREFIX, VFPU_NEGATE(1, 0, 0, 0), 0), V_Single, INFINITY); + break; + default: + ApplySwizzleS(s, sz); + } + for (int i = 0; i < n; i++) { + switch (optype) { case 0: d[i] = s[i]; break; //vmov - case 1: d[i] = fabsf(s[i]); break; //vabs - case 2: d[i] = -s[i]; break; //vneg + case 1: d[i] = s[i]; break; //vabs (prefix) + case 2: d[i] = s[i]; break; //vneg (prefix) // vsat0 changes -0.0 to +0.0, both retain NAN. case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 @@ -536,14 +599,39 @@ namespace MIPSInt break; } } - ApplyPrefixD(d, sz); + // vsat1 is a prefix hack, so 0:1 doesn't apply. Others don't process sat at all. + switch (optype) { + case 5: + ApplyPrefixD(d, sz, true); + break; + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 26: + case 28: + { + // Only the last element gets the mask applied. + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(d, sz); + break; + } + default: + ApplyPrefixD(d, sz); + } WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vocp(MIPSOpcode op) - { + void Int_Vocp(MIPSOpcode op) { float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; @@ -551,42 +639,47 @@ namespace MIPSInt ReadVector(s, sz, vs); // S prefix forces the negate flags. - u32 sprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; - ApplyPrefixST(s, sprefix | 0x000F0000, sz); + u32 sprefixAdd = VFPU_NEGATE(1, 1, 1, 1); + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, 0, sprefixAdd), sz); // T prefix forces constants on and regnum to 1. // That means negate still works, and abs activates a different constant. - u32 tprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; - ApplyPrefixST(t, (tprefix & ~0x000000FF) | 0x00000055 | 0x0000F000, sz); + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_SWIZZLE(1, 1, 1, 1) | VFPU_CONST(1, 1, 1, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); for (int i = 0; i < GetNumVectorElements(sz); i++) { // Always positive NaN. Note that s is always negated from the registers. d[i] = my_isnan(s[i]) ? fabsf(s[i]) : t[i] + s[i]; } + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsocp(MIPSOpcode op) - { + void Int_Vsocp(MIPSOpcode op) { float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); - VectorSize outSize = GetDoubleVectorSize(sz); + VectorSize outSize = GetDoubleVectorSizeSafe(sz); + if (outSize == V_Invalid) + outSize = V_Quad; ReadVector(s, sz, vs); // S prefix forces negate in even/odd and xxyy swizzle. // abs works, and applies to final position (not source.) - u32 sprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; - ApplyPrefixST(s, (sprefix & ~0x000F00FF) | 0x00000050 | 0x00050000, outSize); + u32 sprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_NEGATE(1, 1, 1, 1); + u32 sprefixAdd = VFPU_SWIZZLE(0, 0, 1, 1) | VFPU_NEGATE(1, 0, 1, 0); + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), outSize); // T prefix forces constants on and regnum to 0, 1, 0, 1. // That means negate still works, and abs activates a different constant. - u32 tprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; - ApplyPrefixST(t, (tprefix & ~0x000000FF) | 0x00000011 | 0x0000F000, outSize); + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 1, 0) | VFPU_CONST(1, 1, 1, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), outSize); int n = GetNumVectorElements(sz); // Essentially D prefix saturation is forced. @@ -596,25 +689,36 @@ namespace MIPSInt d[2] = nanclamp(t[2] + s[2], 0.0f, 1.0f); d[3] = nanclamp(t[3] + s[3], 0.0f, 1.0f); } - ApplyPrefixD(d, sz); + ApplyPrefixD(d, sz, true); WriteVector(d, outSize, vd); PC += 4; EatPrefixes(); } - void Int_Vsgn(MIPSOpcode op) - { - float s[4], d[4]; + void Int_Vsgn(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) - { + + // Not sure who would do this, but using abs/neg allows a compare against 3 or -3. + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(0, 0, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + int n = GetNumVectorElements(sz); + if (n < 4) { + // Compare with a swizzled value out of bounds always produces 0. + memcpy(&s[n], &t[n], sizeof(float) * (4 - n)); + } + ApplySwizzleS(s, V_Quad); + + for (int i = 0; i < n; i++) { + float diff = s[i] - t[i]; // To handle NaNs correctly, we do this with integer hackery u32 val; - memcpy(&val, &s[i], sizeof(u32)); + memcpy(&val, &diff, sizeof(u32)); if (val == 0 || val == 0x80000000) d[i] = 0.0f; else if ((val >> 31) == 0) @@ -642,7 +746,8 @@ namespace MIPSInt float mult = (float)(1UL << imm); VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); //TODO: and the mask to kill everything but swizzle + // Negate, abs, and constants apply as you'd expect to the bits. + ApplySwizzleS(s, sz); for (int i = 0; i < GetNumVectorElements(sz); i++) { if (my_isnan(s[i])) { d[i] = 0x7FFFFFFF; @@ -665,14 +770,14 @@ namespace MIPSInt } } } + // Does not apply sat, but does apply mask. ApplyPrefixD(reinterpret_cast(d), sz, true); WriteVector(reinterpret_cast(d), sz, vd); PC += 4; EatPrefixes(); } - void Int_Vi2f(MIPSOpcode op) - { + void Int_Vi2f(MIPSOpcode op) { int s[4]; float d[4]; int vd = _VD; @@ -681,19 +786,19 @@ namespace MIPSInt float mult = 1.0f/(float)(1UL << imm); VectorSize sz = GetVecSize(op); ReadVector(reinterpret_cast(s), sz, vs); - ApplySwizzleS(reinterpret_cast(s), sz); //TODO: and the mask to kill everything but swizzle - for (int i = 0; i < GetNumVectorElements(sz); i++) - { + // Negate, abs, and constants apply as you'd expect to the bits. + ApplySwizzleS(reinterpret_cast(s), sz); + for (int i = 0; i < GetNumVectorElements(sz); i++) { d[i] = (float)s[i] * mult; } - ApplyPrefixD(d, sz); //TODO: and the mask to kill everything but mask + // Sat and mask apply normally. + ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vh2f(MIPSOpcode op) - { + void Int_Vh2f(MIPSOpcode op) { u32 s[4]; float d[4]; int vd = _VD; @@ -710,16 +815,14 @@ namespace MIPSInt d[1] = ExpandHalf(s[0] >> 16); break; case V_Pair: + default: + // All other sizes are treated the same. outsize = V_Quad; d[0] = ExpandHalf(s[0] & 0xFFFF); d[1] = ExpandHalf(s[0] >> 16); d[2] = ExpandHalf(s[1] & 0xFFFF); d[3] = ExpandHalf(s[1] >> 16); break; - default: - _dbg_assert_msg_(CPU, 0, "Trying to interpret Int_Vh2f instruction that can't be interpreted"); - memset(d, 0, sizeof(d)); - break; } ApplyPrefixD(d, outsize); WriteVector(d, outsize, vd); @@ -727,32 +830,30 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vf2h(MIPSOpcode op) - { - float s[4]; + void Int_Vf2h(MIPSOpcode op) { + float s[4]{}; u32 d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); + // Swizzle can cause V_Single to properly write both components. + // TODO: Minor, but negate shouldn't apply to invalid here. Maybe always? + ApplySwizzleS(s, V_Quad); VectorSize outsize = V_Single; switch (sz) { + case V_Single: case V_Pair: outsize = V_Single; d[0] = ShrinkToHalf(s[0]) | ((u32)ShrinkToHalf(s[1]) << 16); break; + case V_Triple: case V_Quad: outsize = V_Pair; d[0] = ShrinkToHalf(s[0]) | ((u32)ShrinkToHalf(s[1]) << 16); d[1] = ShrinkToHalf(s[2]) | ((u32)ShrinkToHalf(s[3]) << 16); break; - default: - _dbg_assert_msg_(CPU, 0, "Trying to interpret Int_Vf2h instruction that can't be interpreted"); - d[0] = 0; - d[1] = 0; - break; } ApplyPrefixD(reinterpret_cast(d), outsize); WriteVector(reinterpret_cast(d), outsize, vd); @@ -760,17 +861,16 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vx2i(MIPSOpcode op) - { - u32 s[4]; - u32 d[4] = {0}; + void Int_Vx2i(MIPSOpcode op) { + u32 s[4], d[4]{}; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); VectorSize oz = sz; ReadVector(reinterpret_cast(s), sz, vs); - // ForbidVPFXS + ApplySwizzleS(reinterpret_cast(s), sz); + // TODO: Similar to colorconv, invalid swizzle seems to reuse last output. switch ((op >> 16) & 3) { case 0: // vuc2i // Quad is the only option. @@ -781,7 +881,7 @@ namespace MIPSInt { u32 value = s[0]; for (int i = 0; i < 4; i++) { - d[i] = (u32)((value & 0xFF) * 0x01010101) >> 1; + d[i] = (u32)((u32)(value & 0xFF) * 0x01010101UL) >> 1; value >>= 8; } oz = V_Quad; @@ -802,8 +902,11 @@ namespace MIPSInt case 2: // vus2i oz = V_Pair; - switch (sz) - { + switch (sz) { + case V_Quad: + case V_Triple: + sz = V_Pair; + // Intentional fallthrough. case V_Pair: oz = V_Quad; // Intentional fallthrough. @@ -823,8 +926,11 @@ namespace MIPSInt case 3: // vs2i oz = V_Pair; - switch (sz) - { + switch (sz) { + case V_Quad: + case V_Triple: + sz = V_Pair; + // Intentional fallthrough. case V_Pair: oz = V_Quad; // Intentional fallthrough. @@ -846,96 +952,93 @@ namespace MIPSInt _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); break; } - - ApplyPrefixD(reinterpret_cast(d),oz, true); // Only write mask - WriteVector(reinterpret_cast(d),oz,vd); + + // Saturation does in fact apply. + ApplyPrefixD(reinterpret_cast(d),oz); + WriteVector(reinterpret_cast(d), oz, vd); PC += 4; EatPrefixes(); } - void Int_Vi2x(MIPSOpcode op) - { - int s[4]; - u32 d[2] = {0}; + void Int_Vi2x(MIPSOpcode op) { + int s[4]{}; + u32 d[2]{}; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); VectorSize oz; ReadVector(reinterpret_cast(s), sz, vs); - ApplySwizzleS(reinterpret_cast(s), sz); //TODO: and the mask to kill everything but swizzle - switch ((op >> 16)&3) - { + // Negate, const, etc. apply as expected. + ApplySwizzleS(reinterpret_cast(s), V_Quad); + + // TODO: Similar to colorconv, invalid swizzle seems to reuse last output. + switch ((op >> 16) & 3) { case 0: //vi2uc - { - for (int i = 0; i < 4; i++) - { - int v = s[i]; - if (v < 0) v = 0; - v >>= 23; - d[0] |= ((u32)v & 0xFF) << (i * 8); - } - oz = V_Single; + for (int i = 0; i < 4; i++) { + int v = s[i]; + if (v < 0) v = 0; + v >>= 23; + d[0] |= ((u32)v & 0xFF) << (i * 8); } + oz = V_Single; break; case 1: //vi2c - { - for (int i = 0; i < 4; i++) - { - u32 v = s[i]; - d[0] |= (v >> 24) << (i * 8); - } - oz = V_Single; + for (int i = 0; i < 4; i++) { + u32 v = s[i]; + d[0] |= (v >> 24) << (i * 8); } + oz = V_Single; break; case 2: //vi2us - { - for (int i = 0; i < GetNumVectorElements(sz) / 2; i++) { - int low = s[i * 2]; - int high = s[i * 2 + 1]; - if (low < 0) low = 0; - if (high < 0) high = 0; - low >>= 15; - high >>= 15; - d[i] = low | (high << 16); - } - switch (sz) { - case V_Quad: oz = V_Pair; break; - case V_Pair: oz = V_Single; break; - default: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); - oz = V_Single; - break; - } + for (int i = 0; i < (GetNumVectorElements(sz) + 1) / 2; i++) { + int low = s[i * 2]; + int high = s[i * 2 + 1]; + if (low < 0) low = 0; + if (high < 0) high = 0; + low >>= 15; + high >>= 15; + d[i] = low | (high << 16); + } + switch (sz) { + case V_Quad: oz = V_Pair; break; + case V_Triple: oz = V_Pair; break; + case V_Pair: oz = V_Single; break; + case V_Single: oz = V_Single; break; + default: + _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); + oz = V_Single; + break; } break; case 3: //vi2s - { - for (int i = 0; i < GetNumVectorElements(sz) / 2; i++) { - u32 low = s[i * 2]; - u32 high = s[i * 2 + 1]; - low >>= 16; - high >>= 16; - d[i] = low | (high << 16); - } - switch (sz) { - case V_Quad: oz = V_Pair; break; - case V_Pair: oz = V_Single; break; - default: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); - oz = V_Single; - break; - } + for (int i = 0; i < (GetNumVectorElements(sz) + 1) / 2; i++) { + u32 low = s[i * 2]; + u32 high = s[i * 2 + 1]; + low >>= 16; + high >>= 16; + d[i] = low | (high << 16); + } + switch (sz) { + case V_Quad: oz = V_Pair; break; + case V_Triple: oz = V_Pair; break; + case V_Pair: oz = V_Single; break; + case V_Single: oz = V_Single; break; + default: + _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); + oz = V_Single; + break; } break; default: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); oz = V_Single; break; } - ApplyPrefixD(reinterpret_cast(d),oz); - WriteVector(reinterpret_cast(d),oz,vd); + // D prefix applies as expected. + ApplyPrefixD(reinterpret_cast(d), oz); + WriteVector(reinterpret_cast(d), oz, vd); PC += 4; EatPrefixes(); } @@ -945,10 +1048,13 @@ namespace MIPSInt int vd = _VD; int vs = _VS; u32 s[4]; + VectorSize isz = GetVecSize(op); VectorSize sz = V_Quad; ReadVector(reinterpret_cast(s), sz, vs); ApplySwizzleS(reinterpret_cast(s), sz); u16 colors[4]; + // TODO: Invalid swizzle values almost seem to use the last value converted in a + // previous execution of these ops. It's a bit odd. for (int i = 0; i < 4; i++) { u32 in = s[i]; @@ -986,296 +1092,362 @@ namespace MIPSInt } u32 ov[2] = {(u32)colors[0] | (colors[1] << 16), (u32)colors[2] | (colors[3] << 16)}; ApplyPrefixD(reinterpret_cast(ov), V_Pair); - WriteVector((const float *)ov, V_Pair, vd); + WriteVector((const float *)ov, isz == V_Single ? V_Single : V_Pair, vd); PC += 4; EatPrefixes(); } - void Int_VDot(MIPSOpcode op) - { - float s[4], t[4]; + void Int_VDot(MIPSOpcode op) { + float s[4]{}, t[4]{}; float d; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); + ApplySwizzleS(s, V_Quad); ReadVector(t, sz, vt); - ApplySwizzleT(t, sz); - float sum = 0.0f; - int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - sum += s[i]*t[i]; + ApplySwizzleT(t, V_Quad); + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; } - d = sum; - ApplyPrefixD(&d,V_Single); + ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); } - void Int_VHdp(MIPSOpcode op) - { - float s[4], t[4]; + void Int_VHdp(MIPSOpcode op) { + float s[4]{}, t[4]{}; float d; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); ReadVector(t, sz, vt); - ApplySwizzleT(t, sz); + ApplySwizzleT(t, V_Quad); + + // S prefix forces constant 1 for the last element (w for quad.) + // Otherwise it is the same as vdot. + u32 sprefixRemove; + u32 sprefixAdd; + if (sz == V_Quad) { + sprefixRemove = VFPU_SWIZZLE(0, 0, 0, 3); + sprefixAdd = VFPU_CONST(0, 0, 0, 1) | VFPU_SWIZZLE(0, 0, 0, 1); + } else if (sz == V_Triple) { + sprefixRemove = VFPU_SWIZZLE(0, 0, 3, 0); + sprefixAdd = VFPU_CONST(0, 0, 1, 0) | VFPU_SWIZZLE(0, 0, 1, 0); + } else if (sz == V_Pair) { + sprefixRemove = VFPU_SWIZZLE(0, 3, 0, 0); + sprefixAdd = VFPU_CONST(0, 1, 0, 0) | VFPU_SWIZZLE(0, 1, 0, 0); + } else { + sprefixRemove = VFPU_SWIZZLE(3, 0, 0, 0); + sprefixAdd = VFPU_CONST(1, 0, 0, 0) | VFPU_SWIZZLE(1, 0, 0, 0); + } + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); + float sum = 0.0f; - int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - sum += (i == n - 1) ? t[i] : s[i]*t[i]; + for (int i = 0; i < 4; i++) { + sum += s[i] * t[i]; } d = my_isnan(sum) ? fabsf(sum) : sum; - ApplyPrefixD(&d,V_Single); + ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); } - void Int_Vbfy(MIPSOpcode op) - { - float s[4]; - float d[4]; + void Int_Vbfy(MIPSOpcode op) { + float s[4]{}, t[4]{}, d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); + ReadVector(t, sz, vs); + int n = GetNumVectorElements(sz); - if (op & 0x10000) - { + if (op & 0x10000) { // vbfy2 - d[0] = s[0] + s[2]; - d[1] = s[1] + s[3]; - d[2] = s[0] - s[2]; - d[3] = s[1] - s[3]; - } - else - { - d[0] = s[0] + s[1]; - d[1] = s[0] - s[1]; - if (n == 4) { - d[2] = s[2] + s[3]; - d[3] = s[2] - s[3]; + // S prefix forces the negate flags (so z and w are negative.) + u32 sprefixAdd = VFPU_NEGATE(0, 0, 1, 1); + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, 0, sprefixAdd), sz); + + // T prefix forces swizzle (zwxy.) + // That means negate still works, but constants are a bit weird. + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_SWIZZLE(2, 3, 0, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + // Other sizes don't seem completely predictable. + if (sz != V_Quad) { + ERROR_LOG_REPORT_ONCE(vbfy2, CPU, "vfby2 with incorrect size"); + } + } else { + // vbfy1 + // S prefix forces the negate flags (so y and w are negative.) + u32 sprefixAdd = VFPU_NEGATE(0, 1, 0, 1); + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, 0, sprefixAdd), sz); + + // T prefix forces swizzle (yxwz.) + // That means negate still works, but constants are a bit weird. + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + if (sz != V_Quad && sz != V_Pair) { + ERROR_LOG_REPORT_ONCE(vbfy2, CPU, "vfby1 with incorrect size"); } } + + d[0] = s[0] + t[0]; + d[1] = s[1] + t[1]; + d[2] = s[2] + t[2]; + d[3] = s[3] + t[3]; + ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsrt1(MIPSOpcode op) - { - float s[4]; - float d[4]; + void Int_Vsrt1(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); ApplySwizzleS(s, sz); - float x = s[0]; - float y = s[1]; - float z = s[2]; - float w = s[3]; - d[0] = std::min(x, y); - d[1] = std::max(x, y); - d[2] = std::min(z, w); - d[3] = std::max(z, w); + ReadVector(t, sz, vs); + + // T is force swizzled to yxwz from S. + u32 tprefixRemove = VFPU_SWIZZLE(3, 3, 3, 3); + u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. + d[0] = std::min(s[0], t[0]); + d[1] = std::max(s[1], t[1]); + d[2] = std::min(s[2], t[2]); + d[3] = std::max(s[3], t[3]); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsrt2(MIPSOpcode op) - { - float s[4]; - float d[4]; + void Int_Vsrt2(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); ApplySwizzleS(s, sz); - float x = s[0]; - float y = s[1]; - float z = s[2]; - float w = s[3]; - d[0] = std::min(x, w); - d[1] = std::min(y, z); - d[2] = std::max(y, z); - d[3] = std::max(x, w); + ReadVector(t, sz, vs); + + // T is force swizzled to wzyx from S. + u32 tprefixRemove = VFPU_SWIZZLE(3, 3, 3, 3); + u32 tprefixAdd = VFPU_SWIZZLE(3, 2, 1, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. + d[0] = std::min(s[0], t[0]); + d[1] = std::min(s[1], t[1]); + d[2] = std::max(s[2], t[2]); + d[3] = std::max(s[3], t[3]); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsrt3(MIPSOpcode op) - { - float s[4]; - float d[4]; + void Int_Vsrt3(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); ApplySwizzleS(s, sz); - float x = s[0]; - float y = s[1]; - float z = s[2]; - float w = s[3]; - d[0] = std::max(x, y); - d[1] = std::min(x, y); - d[2] = std::max(z, w); - d[3] = std::min(z, w); + ReadVector(t, sz, vs); + + // T is force swizzled to yxwz from S. + u32 tprefixRemove = VFPU_SWIZZLE(3, 3, 3, 3); + u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. + d[0] = std::max(s[0], t[0]); + d[1] = std::min(s[1], t[1]); + d[2] = std::max(s[2], t[2]); + d[3] = std::min(s[3], t[3]); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsrt4(MIPSOpcode op) - { - float s[4]; - float d[4]; + void Int_Vsrt4(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); ApplySwizzleS(s, sz); - float x = s[0]; - float y = s[1]; - float z = s[2]; - float w = s[3]; - d[0] = std::max(x, w); - d[1] = std::max(y, z); - d[2] = std::min(y, z); - d[3] = std::min(x, w); + ReadVector(t, sz, vs); + + // T is force swizzled to wzyx from S. + u32 tprefixRemove = VFPU_SWIZZLE(3, 3, 3, 3); + u32 tprefixAdd = VFPU_SWIZZLE(3, 2, 1, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. + d[0] = std::max(s[0], t[0]); + d[1] = std::max(s[1], t[1]); + d[2] = std::min(s[2], t[2]); + d[3] = std::min(s[3], t[3]); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vcrs(MIPSOpcode op) - { + void Int_Vcrs(MIPSOpcode op) { //half a cross product - float s[4], t[4]; - float d[4]; + float s[4]{}, t[4]{}, d[4]; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); - if (sz != V_Triple) - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); - ReadVector(s, sz, vs); ReadVector(t, sz, vt); - // no swizzles allowed - d[0] = s[1] * t[2]; - d[1] = s[2] * t[0]; - d[2] = s[0] * t[1]; + + // S prefix forces swizzle (yzx?.) + // That means negate still works, but constants are a bit weird. + u32 sprefixRemove = VFPU_SWIZZLE(3, 3, 3, 0); + u32 sprefixAdd = VFPU_SWIZZLE(1, 2, 0, 0); + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); + + // T prefix forces swizzle (zxy?.) + u32 tprefixRemove = VFPU_SWIZZLE(3, 3, 3, 0); + u32 tprefixAdd = VFPU_SWIZZLE(2, 0, 1, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + d[0] = s[0] * t[0]; + d[1] = s[1] * t[1]; + d[2] = s[2] * t[2]; + d[3] = s[3] * t[3]; ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vdet(MIPSOpcode op) - { - float s[4], t[4]; - float d[4]; + void Int_Vdet(MIPSOpcode op) { + float s[4]{}, t[4]{}, d[4]; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); - if (sz != V_Pair) - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + // This is normally V_Pair. Unfilled s/t values are treated as zero. ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); + ApplySwizzleS(s, V_Quad); ReadVector(t, sz, vt); - // TODO: The swizzle on t behaves oddly with constants, but sign changes seem to work. - // Also, seems to round in a non-standard way (sometimes toward zero, not always.) - d[0] = s[0] * t[1] - s[1] * t[0]; + + // T prefix forces swizzle for x and y (yx??.) + // That means negate still works, but constants are a bit weird. + // Note: there is no forced negation here. + u32 tprefixRemove = VFPU_SWIZZLE(3, 3, 0, 0); + u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + d[0] = s[0] * t[0] - s[1] * t[1]; + d[0] += s[2] * t[2] + s[3] * t[3]; ApplyPrefixD(d, sz); WriteVector(d, V_Single, vd); PC += 4; EatPrefixes(); } - void Int_Vfad(MIPSOpcode op) - { - float s[4]; + void Int_Vfad(MIPSOpcode op) { + float s[4]{}, t[4]{}; float d; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); - float sum = 0.0f; - int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - sum += s[i]; - } - d = sum; - ApplyPrefixD(&d,V_Single); - V(vd) = d; - PC += 4; - EatPrefixes(); - } + ApplySwizzleS(s, V_Quad); - void Int_Vavg(MIPSOpcode op) - { - float s[4]; - float d; - int vd = _VD; - int vs = _VS; - VectorSize sz = GetVecSize(op); - ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); - float sum = 0.0f; - int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - sum += s[i]; + // T prefix generates constants, but abs can change the constant. + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(1, 1, 1, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; } - d = sum / n; ApplyPrefixD(&d, V_Single); - V(vd) = d; + WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); } - void Int_VScl(MIPSOpcode op) - { - float s[4]; - float d[4]; + void Int_Vavg(MIPSOpcode op) { + float s[4]{}, t[4]{}; + float d; + int vd = _VD; + int vs = _VS; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, V_Quad); + + // T prefix generates constants, but supports negate. + u32 tprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_ABS(1, 1, 1, 1); + u32 tprefixAdd = VFPU_CONST(1, 1, 1, 1); + if (sz == V_Single) + tprefixAdd |= VFPU_SWIZZLE(0, 0, 0, 0); + else if (sz == V_Pair) + tprefixAdd |= VFPU_SWIZZLE(3, 3, 3, 3); + else if (sz == V_Triple) + tprefixAdd |= VFPU_SWIZZLE(1, 1, 1, 1) | VFPU_ABS(1, 1, 1, 1); + else if (sz == V_Quad) + tprefixAdd |= VFPU_SWIZZLE(2, 2, 2, 2) | VFPU_ABS(1, 1, 1, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; + } + ApplyPrefixD(&d, V_Single); + WriteVector(&d, V_Single, vd); + PC += 4; + EatPrefixes(); + } + + void Int_VScl(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); ApplySwizzleS(s, sz); - float scale = V(vt); - if (currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX] != 0xE4) - { - // WARN_LOG(CPU, "Broken T prefix used with VScl: %08x / %08x", currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX], op); - ApplySwizzleT(&scale, V_Single); - } + + // T prefix forces swizzle (zzzz for some reason, so we force V_Quad.) + // That means negate still works, but constants are a bit weird. + t[2] = V(vt); + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_SWIZZLE(2, 2, 2, 2); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - d[i] = s[i] * scale; + for (int i = 0; i < n; i++) { + d[i] = s[i] * t[i]; } ApplyPrefixD(d, sz); WriteVector(d, sz, vd); @@ -1283,31 +1455,33 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vrnds(MIPSOpcode op) - { + void Int_Vrnds(MIPSOpcode op) { int vd = _VD; int seed = VI(vd); + // Swizzles apply a constant value, constants/abs/neg work to vary the seed. + ApplySwizzleS(reinterpret_cast(&seed), V_Single); currentMIPS->rng.Init(seed); PC += 4; EatPrefixes(); } - void Int_VrndX(MIPSOpcode op) - { + void Int_VrndX(MIPSOpcode op) { FloatBits d; int vd = _VD; VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - switch ((op >> 16) & 0x1f) - { + for (int i = 0; i < n; i++) { + switch ((op >> 16) & 0x1f) { case 1: d.u[i] = currentMIPS->rng.R32(); break; // vrndi case 2: d.f[i] = 1.0f + ((float)currentMIPS->rng.R32() / 0xFFFFFFFF); break; // vrndf1 TODO: make more accurate case 3: d.f[i] = 2.0f + 2 * ((float)currentMIPS->rng.R32() / 0xFFFFFFFF); break; // vrndf2 TODO: make more accurate default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); } } + // D prefix is broken and applies to the last element only (mask and sat.) + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -1316,83 +1490,96 @@ namespace MIPSInt // Generates one line of a rotation matrix around one of the three axes void Int_Vrot(MIPSOpcode op) { - // Note: prefixes behave strangely for this. + float d[4]{}; int vd = _VD; int vs = _VS; int imm = (op >> 16) & 0x1f; VectorSize sz = GetVecSize(op); - bool negSin = (imm & 0x10) ? true : false; + bool negSin = (imm & 0x10) != 0; + int sineLane = (imm >> 2) & 3; + int cosineLane = imm & 3; + float sine, cosine; - vfpu_sincos(V(vs), sine, cosine); - if (negSin) - sine = -sine; - float d[4] = {0}; - if (((imm >> 2) & 3) == (imm & 3)) { + if (currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX] == 0x000E4) { + vfpu_sincos(V(vs), sine, cosine); + if (negSin) + sine = -sine; + } else { + // Swizzle on S is a bit odd here, but generally only applies to sine. + float s[4]{}; + ReadVector(s, V_Single, vs); + u32 sprefixRemove = VFPU_NEGATE(1, 0, 0, 0); + u32 sprefixAdd = VFPU_NEGATE(negSin, 0, 0, 0); + // TODO: Minor, but negate shouldn't apply to invalid here. Maybe always? + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Single); + + // Cosine ignores all prefixes, so take the original. + cosine = vfpu_cos(V(vs)); + sine = vfpu_sin(s[0]); + } + + if (sineLane == cosineLane) { for (int i = 0; i < 4; i++) d[i] = sine; + } else { + d[sineLane] = sine; } - d[(imm >> 2) & 3] = sine; - d[imm & 3] = cosine; + d[cosineLane] = cosine; + + // D prefix works, just not for x. + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] &= 0xFFEFC; + ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vtfm(MIPSOpcode op) - { + void Int_Vtfm(MIPSOpcode op) { + float s[16]{}, t[4]{}, d[4]; int vd = _VD; int vs = _VS; int vt = _VT; int ins = (op >> 23) & 7; - VectorSize sz = GetVecSize(op); - MatrixSize msz = GetMtxSize(op); - int n = GetNumVectorElements(sz); + VectorSize sz = (VectorSize)(ins + 1); + MatrixSize msz = (MatrixSize)(ins + 1); + int n = GetNumVectorElements(GetVecSize(op)); - bool homogenous = false; - if (n == ins) - { - n++; - sz = (VectorSize)((int)(sz) + 1); - msz = (MatrixSize)((int)(msz) + 1); - homogenous = true; - } - - float s[16]; + int tn = std::min(n, ins + 1); ReadMatrix(s, msz, vs); - float t[4]; ReadVector(t, sz, vt); - float d[4]; - if (homogenous) - { - for (int i = 0; i < n; i++) - { - d[i] = 0.0f; - for (int k = 0; k < n; k++) - { - d[i] += (k == n-1) ? s[i*4+k] : (s[i*4+k] * t[k]); - } + for (int i = 0; i < ins; i++) { + d[i] = s[i * 4] * t[0]; + for (int k = 1; k < tn; k++) { + d[i] += s[i * 4 + k] * t[k]; + } + if (ins >= n) { + d[i] += s[i * 4 + ins]; } } - else if (n == ins + 1) - { - for (int i = 0; i < n; i++) - { - d[i] = s[i*4] * t[0]; - for (int k = 1; k < n; k++) - { - d[i] += s[i*4+k] * t[k]; - } - } + + // S and T prefixes apply for the final row only. + // The T prefix is used to apply zero/one constants, but abs still changes it. + ApplySwizzleS(&s[ins * 4], V_Quad); + u32 tprefixRemove = VFPU_SWIZZLE(0, n < 2 ? 3 : 0, n < 3 ? 3 : 0, n < 4 ? 3 : 0); + u32 tprefixAdd = VFPU_CONST(0, n < 2, n < 3, n < 4); + if (ins >= n) { + tprefixAdd |= VFPU_SWIZZLE(0, ins == 1 ? 1 : 0, ins == 2 ? 1 : 0, ins == 3 ? 1 : 0); } - else - { - Reporting::ReportMessage("Trying to interpret instruction that can't be interpreted (BADVTFM)"); - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted (BADVTFM)"); - for (int i = 0; i < n; i++) - d[i] = 0.0; + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + // Really this is the operation all rows probably use (with constant wiring.) + d[ins] = s[ins * 4] * t[0]; + for (int k = 1; k < 4; k++) { + d[ins] += s[ins * 4 + k] * t[k]; } + + // D prefix applies to the last element only. + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << ins; + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (ins + ins); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); @@ -1432,7 +1619,7 @@ namespace MIPSInt if (rt != 0) { if (imm < 128) { R(rt) = VI(imm); - } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc + } else if (imm < 128 + VFPU_CTRL_MAX) { //mfvc R(rt) = currentMIPS->vfpuCtrl[imm - 128]; } else { //ERROR - maybe need to make this value too an "interlock" value? @@ -1445,7 +1632,10 @@ namespace MIPSInt if (imm < 128) { VI(imm) = R(rt); } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc - currentMIPS->vfpuCtrl[imm - 128] = R(rt); + u32 mask; + if (GetVFPUCtrlMask(imm - 128, &mask)) { + currentMIPS->vfpuCtrl[imm - 128] = R(rt) & mask; + } } else { //ERROR _dbg_assert_msg_(CPU,0,"mtv - invalid register"); @@ -1460,19 +1650,24 @@ namespace MIPSInt } void Int_Vmfvc(MIPSOpcode op) { - int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - VI(vs) = currentMIPS->vfpuCtrl[imm - 128]; + int vd = _VD; + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { + VI(vd) = currentMIPS->vfpuCtrl[imm]; + } else { + VI(vd) = 0; } PC += 4; } void Int_Vmtvc(MIPSOpcode op) { int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - currentMIPS->vfpuCtrl[imm - 128] = VI(vs); + int imm = op & 0x7F; + if (imm < VFPU_CTRL_MAX) { + u32 mask; + if (GetVFPUCtrlMask(imm, &mask)) { + currentMIPS->vfpuCtrl[imm] = VI(vs) & mask; + } } PC += 4; } @@ -1554,6 +1749,7 @@ namespace MIPSInt } void Int_Vminmax(MIPSOpcode op) { + FloatBits s, t, d; int vt = _VT; int vs = _VS; int vd = _VD; @@ -1561,9 +1757,6 @@ namespace MIPSInt VectorSize sz = GetVecSize(op); int numElements = GetNumVectorElements(sz); - FloatBits s; - FloatBits t; - FloatBits d; ReadVector(s.f, sz, vs); ApplySwizzleS(s.f, sz); ReadVector(t.f, sz, vt); @@ -1609,46 +1802,50 @@ namespace MIPSInt EatPrefixes(); return; } + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } - // This doesn't quite pass all the tests :/ void Int_Vscmp(MIPSOpcode op) { + FloatBits s, t, d; int vt = _VT; int vs = _VS; int vd = _VD; VectorSize sz = GetVecSize(op); - float s[4]; - float t[4]; - float d[4]; - ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); - ReadVector(t, sz, vt); - ApplySwizzleT(t, sz); + ReadVector(s.f, sz, vs); + ApplySwizzleS(s.f, sz); + ReadVector(t.f, sz, vt); + ApplySwizzleT(t.f, sz); int n = GetNumVectorElements(sz); for (int i = 0; i < n ; i++) { - float a = s[i] - t[i]; - d[i] = (float) ((0.0 < a) - (a < 0.0)); + float a = s.f[i] - t.f[i]; + if (my_isnan(a)) { + // NAN/INF are treated as just larger numbers, as in vmin/vmax. + int sMagnitude = s.u[i] & 0x7FFFFFFF; + int tMagnitude = t.u[i] & 0x7FFFFFFF; + int b = (s.i[i] < 0 ? -sMagnitude : sMagnitude) - (t.i[i] < 0 ? -tMagnitude : tMagnitude); + d.f[i] = (float)((0 < b) - (b < 0)); + } else { + d.f[i] = (float)((0.0f < a) - (a < 0.0f)); + } } - ApplyPrefixD(d, sz); - WriteVector(d, sz, vd); + RetainInvalidSwizzleST(d.f, sz); + ApplyPrefixD(d.f, sz); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } void Int_Vsge(MIPSOpcode op) { + float s[4], t[4], d[4]; int vt = _VT; int vs = _VS; int vd = _VD; - int cond = op&15; VectorSize sz = GetVecSize(op); int numElements = GetNumVectorElements(sz); - float s[4]; - float t[4]; - float d[4]; ReadVector(s, sz, vs); ApplySwizzleS(s, sz); ReadVector(t, sz, vt); @@ -1659,22 +1856,21 @@ namespace MIPSInt else d[i] = s[i] >= t[i] ? 1.0f : 0.0f; } - ApplyPrefixD(d, sz); + RetainInvalidSwizzleST(d, sz); + // The clamp cannot matter, so skip it. + ApplyPrefixD(d, sz, true); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } void Int_Vslt(MIPSOpcode op) { + float s[4], t[4], d[4]; int vt = _VT; int vs = _VS; int vd = _VD; - int cond = op&15; VectorSize sz = GetVecSize(op); int numElements = GetNumVectorElements(sz); - float s[4]; - float t[4]; - float d[4]; ReadVector(s, sz, vs); ApplySwizzleS(s, sz); ReadVector(t, sz, vt); @@ -1685,15 +1881,16 @@ namespace MIPSInt else d[i] = s[i] < t[i] ? 1.0f : 0.0f; } - ApplyPrefixD(d, sz); + RetainInvalidSwizzleST(d, sz); + // The clamp cannot matter, so skip it. + ApplyPrefixD(d, sz, true); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vcmov(MIPSOpcode op) - { + void Int_Vcmov(MIPSOpcode op) { int vs = _VS; int vd = _VD; int tf = (op >> 19) & 1; @@ -1704,28 +1901,23 @@ namespace MIPSInt float d[4]; ReadVector(s, sz, vs); ApplySwizzleS(s, sz); - ReadVector(d, sz, vd); //Yes! + // Not only is D read (as T), but the T prefix applies to it. + ReadVector(d, sz, vd); + ApplySwizzleT(d, sz); int CC = currentMIPS->vfpuCtrl[VFPU_CTRL_CC]; - if (imm3 < 6) - { - if (((CC >> imm3) & 1) == !tf) - { + if (imm3 < 6) { + if (((CC >> imm3) & 1) == !tf) { for (int i = 0; i < n; i++) d[i] = s[i]; } - } - else if (imm3 == 6) - { - for (int i = 0; i < n; i++) - { + } else if (imm3 == 6) { + for (int i = 0; i < n; i++) { if (((CC >> i) & 1) == !tf) d[i] = s[i]; } - } - else - { + } else { ERROR_LOG_REPORT(CPU, "Bad Imm3 in cmov: %d", imm3); } ApplyPrefixD(d, sz); @@ -1734,117 +1926,167 @@ namespace MIPSInt EatPrefixes(); } - void Int_VecDo3(MIPSOpcode op) - { + void Int_VecDo3(MIPSOpcode op) { + float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); - float s[4]; - float t[4]; - float d[4]; + + int optype = 0; + switch (op >> 26) { + case 24: //VFPU0 + switch ((op >> 23) & 7) { + case 0: optype = 0; break; + case 1: optype = 1; break; + case 7: optype = 7; break; + default: goto bad; + } + break; + case 25: //VFPU1 + switch ((op >> 23) & 7) { + case 0: optype = 8; break; + default: goto bad; + } + break; + default: + bad: + _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); + break; + } + + int n = GetNumVectorElements(sz); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); ReadVector(t, sz, vt); - ApplySwizzleT(t, sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) - { - switch(op >> 26) - { - case 24: //VFPU0 - switch ((op >> 23)&7) - { - case 0: d[i] = s[i] + t[i]; break; //vadd - case 1: d[i] = s[i] - t[i]; break; //vsub - case 7: d[i] = s[i] / t[i]; break; //vdiv - default: goto bad; - } - break; - case 25: //VFPU1 - switch ((op >> 23)&7) - { - case 0: d[i] = s[i] * t[i]; break; //vmul - default: goto bad; - } - break; - default: -bad: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); - break; + if (optype != 7) { + ApplySwizzleS(s, sz); + ApplySwizzleT(t, sz); + } else { + // The prefix handling of S/T is a bit odd, probably the HW doesn't do it in parallel. + // The X prefix is applied to the last element in sz. + // TODO: This doesn't match exactly for a swizzle past x in some cases... + ApplySwizzleS(&s[n - 1], V_Single, -INFINITY); + ApplySwizzleT(&t[n - 1], V_Single, -INFINITY); + } + + for (int i = 0; i < n; i++) { + switch (optype) { + case 0: d[i] = s[i] + t[i]; break; //vadd + case 1: d[i] = s[i] - t[i]; break; //vsub + case 7: d[i] = s[i] / t[i]; break; //vdiv + case 8: d[i] = s[i] * t[i]; break; //vmul } } - ApplyPrefixD(d, sz); + + // For vdiv only, the D prefix only applies mask (and like S/T, x applied to last.) + if (optype == 7) { + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(d, sz); + } else { + RetainInvalidSwizzleST(d, sz); + ApplyPrefixD(d, sz); + } WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - - void Int_CrossQuat(MIPSOpcode op) - { + void Int_CrossQuat(MIPSOpcode op) { + float s[4]{}, t[4]{}, d[4]; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); - float s[4]; - float t[4]; - float d[4]; + int n = GetNumVectorElements(sz); ReadVector(s, sz, vs); ReadVector(t, sz, vt); - switch (sz) - { + + u32 tprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_NEGATE(1, 1, 1, 1); + u32 tprefixAdd; + + switch (sz) { case V_Triple: // vcrsp.t d[0] = s[1]*t[2] - s[2]*t[1]; d[1] = s[2]*t[0] - s[0]*t[2]; - d[2] = s[0]*t[1] - s[1]*t[0]; + + // T prefix forces siwzzle and negate, can be used to have weird constants. + tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + ApplySwizzleS(s, V_Quad); + d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; break; case V_Quad: // vqmul.q d[0] = s[0]*t[3] + s[1]*t[2] - s[2]*t[1] + s[3]*t[0]; d[1] = -s[0]*t[2] + s[1]*t[3] + s[2]*t[0] + s[3]*t[1]; d[2] = s[0]*t[1] - s[1]*t[0] + s[2]*t[3] + s[3]*t[2]; - d[3] = -s[0]*t[0] - s[1]*t[1] - s[2]*t[2] + s[3]*t[3]; + + // T prefix forces siwzzle and negate, can be used to have weird constants. + tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + ApplySwizzleS(s, sz); + d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; break; - default: - Reporting::ReportMessage("CrossQuat instruction with wrong size"); - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + case V_Pair: + // t swizzles invalid so the multiply is always zero. d[0] = 0; - d[1] = 0; + + tprefixAdd = VFPU_SWIZZLE(0, 0, 0, 0) | VFPU_NEGATE(0, 0, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + ApplySwizzleS(s, V_Quad); + // It's possible to populate a value by swizzling s[2]. + d[1] = s[2] * t[2]; break; + + case V_Single: + // t swizzles invalid so the multiply is always zero. + d[0] = 0; + break; + } + + // D prefix applies to the last element only (mask and sat) for pair and larger. + if (sz != V_Single) { + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(d, sz); + } else { + // Single always seems to write out zero. + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = 0; } WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vlgb(MIPSOpcode op) - { + void Int_Vlgb(MIPSOpcode op) { // Vector log binary (extract exponent) + FloatBits d, s; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); - FloatBits d; - FloatBits s; - ReadVector(s.f, sz, vs); - // TODO: Test swizzle, t? + // TODO: Swizzled outside sz stays zero (no -INF.) ApplySwizzleS(s.f, sz); - if (sz != V_Single) { - ERROR_LOG_REPORT(CPU, "vlgb not implemented for size %d", GetNumVectorElements(sz)); + int exp = (s.u[0] & 0x7F800000) >> 23; + if (exp == 0xFF) { + d.f[0] = s.f[0]; + } else if (exp == 0) { + d.f[0] = -INFINITY; + } else { + d.f[0] = (float)(exp - 127); } - for (int i = 0; i < GetNumVectorElements(sz); ++i) { - int exp = (s.u[i] & 0x7F800000) >> 23; - if (exp == 0xFF) { - d.f[i] = s.f[i]; - } else if (exp == 0) { - d.f[i] = -INFINITY; - } else { - d.f[i] = (float)(exp - 127); - } + + // If sz is greater than V_Single, the rest are copied unchanged. + for (int i = 1; i < GetNumVectorElements(sz); ++i) { + d.u[i] = s.u[i]; } + ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -1854,102 +2096,98 @@ bad: // There has to be a concise way of expressing this in terms of // bit manipulation on the raw floats. void Int_Vwbn(MIPSOpcode op) { + FloatBits d, s; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); - - FloatBits d; - FloatBits s; u8 exp = (u8)((op >> 16) & 0xFF); ReadVector(s.f, sz, vs); - // TODO: Test swizzle, t? + // TODO: Swizzled outside sz stays zero (no exp insertion.) ApplySwizzleS(s.f, sz); - if (sz != V_Single) { - ERROR_LOG_REPORT(CPU, "vwbn not implemented for size %d", GetNumVectorElements(sz)); - } - for (int i = 0; i < GetNumVectorElements(sz); ++i) { - u32 sigbit = s.u[i] & 0x80000000; - u32 prevExp = (s.u[i] & 0x7F800000) >> 23; - u32 mantissa = (s.u[i] & 0x007FFFFF) | 0x00800000; - if (prevExp != 0xFF && prevExp != 0) { - if (exp > prevExp) { - s8 shift = (exp - prevExp) & 0xF; - mantissa = mantissa >> shift; - } else { - s8 shift = (prevExp - exp) & 0xF; - mantissa = mantissa << shift; - } - d.u[i] = sigbit | (mantissa & 0x007FFFFF) | (exp << 23); + u32 sigbit = s.u[0] & 0x80000000; + u32 prevExp = (s.u[0] & 0x7F800000) >> 23; + u32 mantissa = (s.u[0] & 0x007FFFFF) | 0x00800000; + if (prevExp != 0xFF && prevExp != 0) { + if (exp > prevExp) { + s8 shift = (exp - prevExp) & 0xF; + mantissa = mantissa >> shift; } else { - d.u[i] = s.u[i] | (exp << 23); + s8 shift = (prevExp - exp) & 0xF; + mantissa = mantissa << shift; } + d.u[0] = sigbit | (mantissa & 0x007FFFFF) | (exp << 23); + } else { + d.u[0] = s.u[0] | (exp << 23); } + + // If sz is greater than V_Single, the rest are copied unchanged. + for (int i = 1; i < GetNumVectorElements(sz); ++i) { + d.u[i] = s.u[i]; + } + ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsbn(MIPSOpcode op) - { + void Int_Vsbn(MIPSOpcode op) { + FloatBits d, s, t; int vd = _VD; int vs = _VS; int vt = _VT; VectorSize sz = GetVecSize(op); - FloatBits d; - FloatBits s; - u8 exp = (u8)(127 + VI(vt)); - ReadVector(s.f, sz, vs); - // TODO: Test swizzle, t? ApplySwizzleS(s.f, sz); + ReadVector(t.f, sz, vs); + ApplySwizzleT(t.f, sz); + // Swizzle does apply to the value read as an integer. + u8 exp = (u8)(127 + t.i[0]); - if (sz != V_Single) { - ERROR_LOG_REPORT(CPU, "vsbn not implemented for size %d", GetNumVectorElements(sz)); + // Simply replace the exponent bits. + u32 prev = s.u[0] & 0x7F800000; + if (prev != 0 && prev != 0x7F800000) { + d.u[0] = (s.u[0] & ~0x7F800000) | (exp << 23); + } else { + d.u[0] = s.u[0]; } - for (int i = 0; i < GetNumVectorElements(sz); ++i) { - // Simply replace the exponent bits. - u32 prev = s.u[i] & 0x7F800000; - if (prev != 0 && prev != 0x7F800000) { - d.u[i] = (s.u[i] & ~0x7F800000) | (exp << 23); - } else { - d.u[i] = s.u[i]; - } + + // If sz is greater than V_Single, the rest are copied unchanged. + for (int i = 1; i < GetNumVectorElements(sz); ++i) { + d.u[i] = s.u[i]; } + ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } - void Int_Vsbz(MIPSOpcode op) - { + void Int_Vsbz(MIPSOpcode op) { // Vector scale by zero (set exp to 0 to extract mantissa) + FloatBits d, s; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); - FloatBits d; - FloatBits s; - ReadVector(s.f, sz, vs); - // TODO: Test swizzle, t? ApplySwizzleS(s.f, sz); - if (sz != V_Single) { - ERROR_LOG_REPORT(CPU, "vsbz not implemented for size %d", GetNumVectorElements(sz)); + // NAN and denormals pass through. + if (my_isnan(s.f[0]) || (s.u[0] & 0x7F800000) == 0) { + d.u[0] = s.u[0]; + } else { + d.u[0] = (127 << 23) | (s.u[0] & 0x007FFFFF); } - for (int i = 0; i < GetNumVectorElements(sz); ++i) { - // NAN and denormals pass through. - if (my_isnan(s.f[i]) || (s.u[i] & 0x7F800000) == 0) { - d.u[i] = s.u[i]; - } else { - d.u[i] = (127 << 23) | (s.u[i] & 0x007FFFFF); - } + + // If sz is greater than V_Single, the rest are copied unchanged. + for (int i = 1; i < GetNumVectorElements(sz); ++i) { + d.u[i] = s.u[i]; } + ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index e493074665..6c07bfef5a 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -57,16 +57,16 @@ void GetMatrixRegs(u8 regs[16], MatrixSize N, int matrixReg) { int row = 0; int side = 0; + int transpose = (matrixReg >> 5) & 1; switch (N) { - case M_2x2: row = (matrixReg>>5)&2; side = 2; break; - case M_3x3: row = (matrixReg>>6)&1; side = 3; break; - case M_4x4: row = (matrixReg>>5)&2; side = 4; break; + case M_1x1: transpose = 0; row = (matrixReg >> 5) & 3; side = 1; break; + case M_2x2: row = (matrixReg >> 5) & 2; side = 2; break; + case M_3x3: row = (matrixReg >> 6) & 1; side = 3; break; + case M_4x4: row = (matrixReg >> 5) & 2; side = 4; break; default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); } - int transpose = (matrixReg>>5) & 1; - for (int i = 0; i < side; i++) { for (int j = 0; j < side; j++) { int index = mtx * 4; @@ -176,19 +176,27 @@ void ReadVector(float *rd, VectorSize size, int reg) { } void WriteVector(const float *rd, VectorSize size, int reg) { + if (size == V_Single) { + // Optimize the common case. + if (!currentMIPS->VfpuWriteMask(0)) { + V(reg) = rd[0]; + } + return; + } + + const int mtx = (reg>>2)&7; + const int col = reg & 3; + int transpose = (reg>>5)&1; int row = 0; int length = 0; switch (size) { - case V_Single: V(reg) = rd[0]; return; // transpose = 0; row=(reg>>5)&3; length = 1; break; + case V_Single: _dbg_assert_(JIT, 0); return; // transpose = 0; row=(reg>>5)&3; length = 1; break; case V_Pair: row=(reg>>5)&2; length = 2; break; case V_Triple: row=(reg>>6)&1; length = 3; break; case V_Quad: row=(reg>>5)&2; length = 4; break; default: _assert_msg_(JIT, 0, "%s: Bad vector size", __FUNCTION__); } - const int mtx = (reg>>2)&7; - const int col = reg & 3; - int transpose = (reg>>5)&1; if (currentMIPS->VfpuWriteMask() == 0) { if (transpose) { @@ -214,30 +222,35 @@ void WriteVector(const float *rd, VectorSize size, int reg) { } } +u32 VFPURewritePrefix(int ctrl, u32 remove, u32 add) { + u32 prefix = currentMIPS->vfpuCtrl[ctrl]; + return (prefix & ~remove) | add; +} + void ReadMatrix(float *rd, MatrixSize size, int reg) { int mtx = (reg >> 2) & 7; int col = reg & 3; int row = 0; int side = 0; + int transpose = (reg >> 5) & 1; switch (size) { - case M_2x2: row = (reg>>5)&2; side = 2; break; - case M_3x3: row = (reg>>6)&1; side = 3; break; - case M_4x4: row = (reg>>5)&2; side = 4; break; + case M_1x1: transpose = 0; row = (reg >> 5) & 3; side = 1; break; + case M_2x2: row = (reg >> 5) & 2; side = 2; break; + case M_3x3: row = (reg >> 6) & 1; side = 3; break; + case M_4x4: row = (reg >> 5) & 2; side = 4; break; default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); } - int transpose = (reg>>5) & 1; - for (int i = 0; i < side; i++) { for (int j = 0; j < side; j++) { - int index = mtx * 4; + int index = mtx * 4; if (transpose) - index += ((row+i)&3) + ((col+j)&3)*32; - else - index += ((col+j)&3) + ((row+i)&3)*32; - rd[j*4 + i] = V(index); + index += ((row+i)&3) + ((col+j)&3)*32; + else + index += ((col+j)&3) + ((row+i)&3)*32; + rd[j*4 + i] = V(index); } } } @@ -248,15 +261,16 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) { int row = 0; int side = 0; + int transpose = (reg >> 5) & 1; switch (size) { - case M_2x2: row = (reg>>5)&2; side = 2; break; - case M_3x3: row = (reg>>6)&1; side = 3; break; - case M_4x4: row = (reg>>5)&2; side = 4; break; + case M_1x1: transpose = 0; row = (reg >> 5) & 3; side = 1; break; + case M_2x2: row = (reg >> 5) & 2; side = 2; break; + case M_3x3: row = (reg >> 6) & 1; side = 3; break; + case M_4x4: row = (reg >> 5) & 2; side = 4; break; default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); } - int transpose = (reg>>5)&1; if (currentMIPS->VfpuWriteMask() != 0) { ERROR_LOG_REPORT(CPU, "Write mask used with vfpu matrix instruction."); } @@ -352,6 +366,7 @@ VectorSize GetVecSize(MIPSOpcode op) { VectorSize GetVectorSizeSafe(MatrixSize sz) { switch (sz) { + case M_1x1: return V_Single; case M_2x2: return V_Pair; case M_3x3: return V_Triple; case M_4x4: return V_Quad; @@ -367,7 +382,7 @@ VectorSize GetVectorSize(MatrixSize sz) { MatrixSize GetMatrixSizeSafe(VectorSize sz) { switch (sz) { - case V_Single: return M_Invalid; + case V_Single: return M_1x1; case V_Pair: return M_2x2; case V_Triple: return M_3x3; case V_Quad: return M_4x4; @@ -386,7 +401,7 @@ MatrixSize GetMtxSizeSafe(MIPSOpcode op) { int b = (op >> 15) & 1; a += (b << 1); switch (a) { - case 0: return M_4x4; // This happens in disassembly of junk + case 0: return M_1x1; // This happens in disassembly of junk, but has predictable behavior. case 1: return M_2x2; case 2: return M_3x3; case 3: return M_4x4; @@ -402,6 +417,7 @@ MatrixSize GetMtxSize(MIPSOpcode op) { VectorSize MatrixVectorSizeSafe(MatrixSize sz) { switch (sz) { + case M_1x1: return V_Single; case M_2x2: return V_Pair; case M_3x3: return V_Triple; case M_4x4: return V_Quad; @@ -417,6 +433,7 @@ VectorSize MatrixVectorSize(MatrixSize sz) { int GetMatrixSideSafe(MatrixSize sz) { switch (sz) { + case M_1x1: return 1; case M_2x2: return 2; case M_3x3: return 3; case M_4x4: return 4; @@ -425,7 +442,7 @@ int GetMatrixSideSafe(MatrixSize sz) { } int GetMatrixSide(MatrixSize sz) { - int res = MatrixVectorSizeSafe(sz); + int res = GetMatrixSideSafe(sz); _assert_msg_(JIT, res != 0, "%s: Bad matrix size", __FUNCTION__); return res; } @@ -509,6 +526,41 @@ const char *GetMatrixNotation(int reg, MatrixSize size) return hej[yo]; } +bool GetVFPUCtrlMask(int reg, u32 *mask) { + switch (reg) { + case VFPU_CTRL_SPREFIX: + case VFPU_CTRL_TPREFIX: + *mask = 0x000FFFFF; + return true; + case VFPU_CTRL_DPREFIX: + *mask = 0x00000FFF; + return true; + case VFPU_CTRL_CC: + *mask = 0x0000003F; + return true; + case VFPU_CTRL_INF4: + *mask = 0xFFFFFFFF; + return true; + case VFPU_CTRL_RSV5: + case VFPU_CTRL_RSV6: + case VFPU_CTRL_REV: + // Don't change anything, these regs are read only. + return false; + case VFPU_CTRL_RCX0: + case VFPU_CTRL_RCX1: + case VFPU_CTRL_RCX2: + case VFPU_CTRL_RCX3: + case VFPU_CTRL_RCX4: + case VFPU_CTRL_RCX5: + case VFPU_CTRL_RCX6: + case VFPU_CTRL_RCX7: + *mask = 0x3FFFFFFF; + return true; + default: + return false; + } +} + float Float16ToFloat32(unsigned short l) { union float2int { @@ -521,19 +573,17 @@ float Float16ToFloat32(unsigned short l) int exponent = (float16 >> VFPU_SH_FLOAT16_EXP) & VFPU_MASK_FLOAT16_EXP; unsigned int fraction = float16 & VFPU_MASK_FLOAT16_FRAC; - float signf = (sign == 1) ? -1.0f : 1.0f; - float f; if (exponent == VFPU_FLOAT16_EXP_MAX) { - if (fraction == 0) - f = std::numeric_limits::infinity(); //(*info->fprintf_func) (info->stream, "%cInf", signchar); - else - f = std::numeric_limits::quiet_NaN(); //(*info->fprintf_func) (info->stream, "%cNaN", signchar); + float2int.i = sign << 31; + float2int.i |= 255 << 23; + float2int.i |= fraction; + f = float2int.f; } else if (exponent == 0 && fraction == 0) { - f = 0.0f * signf; + f = sign == 1 ? -0.0f : 0.0f; } else { diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 8e25821b1d..97d892ed0b 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -35,6 +35,7 @@ inline int Xpose(int v) { // Some games depend on exact values, but sinf() and cosf() aren't always precise. // Stepping down to [0, 2pi) helps, but we also check common exact-result values. +// TODO: cos(2) and sin(1) should be -0.0, but doing that gives wrong results (possibly from floorf.) inline float vfpu_sin(float angle) { angle -= floorf(angle * 0.25f) * 4.f; @@ -113,12 +114,39 @@ enum VectorSize { }; enum MatrixSize { + M_1x1 = 1, M_2x2 = 2, M_3x3 = 3, M_4x4 = 4, M_Invalid = -1 }; +static u32 VFPU_SWIZZLE(int x, int y, int z, int w) { + return (x << 0) | (y << 2) | (z << 4) | (w << 6); +} + +static u32 VFPU_MASK(int x, int y, int z, int w) { + return (x << 0) | (y << 1) | (z << 2) | (w << 3); +} + +static u32 VFPU_ANY_SWIZZLE() { + return 0x000000FF; +} + +static u32 VFPU_ABS(int x, int y, int z, int w) { + return VFPU_MASK(x, y, z, w) << 8; +} + +static u32 VFPU_CONST(int x, int y, int z, int w) { + return VFPU_MASK(x, y, z, w) << 12; +} + +static u32 VFPU_NEGATE(int x, int y, int z, int w) { + return VFPU_MASK(x, y, z, w) << 16; +} + +u32 VFPURewritePrefix(int ctrl, u32 remove, u32 add); + void ReadMatrix(float *rd, MatrixSize size, int reg); void WriteMatrix(const float *rs, MatrixSize size, int reg); @@ -184,4 +212,6 @@ inline int TransposeMatrixReg(int matrixReg) { } int GetVectorOverlap(int reg1, VectorSize size1, int reg2, VectorSize size2); +bool GetVFPUCtrlMask(int reg, u32 *mask); + float Float16ToFloat32(unsigned short l); diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 10a1e6e9bc..73cd85c22a 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -87,7 +87,7 @@ void Jit::Comp_VPFX(MIPSOpcode op) js.prefixTFlag = JitState::PREFIX_KNOWN_DIRTY; break; case 2: // D - js.prefixD = data; + js.prefixD = data & 0x00000FFF; js.prefixDFlag = JitState::PREFIX_KNOWN_DIRTY; break; } @@ -2528,39 +2528,43 @@ void Jit::Comp_Mftv(MIPSOpcode op) { void Jit::Comp_Vmfvc(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); - int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - fpr.MapRegV(vs, MAP_DIRTY | MAP_NOINIT); - if (imm - 128 == VFPU_CTRL_CC) { + int vd = _VD; + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { + fpr.MapRegV(vd, MAP_DIRTY | MAP_NOINIT); + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, true, false); - MOVD_xmm(fpr.VX(vs), gpr.R(MIPS_REG_VFPUCC)); + MOVD_xmm(fpr.VX(vd), gpr.R(MIPS_REG_VFPUCC)); } else { - MOVSS(fpr.VX(vs), MIPSSTATE_VAR_ELEM32(vfpuCtrl[0], imm - 128)); + MOVSS(fpr.VX(vd), MIPSSTATE_VAR_ELEM32(vfpuCtrl[0], imm)); } fpr.ReleaseSpillLocks(); + } else { + fpr.MapRegV(vd, MAP_DIRTY | MAP_NOINIT); + XORPS(fpr.VX(vd), fpr.V(vd)); + fpr.ReleaseSpillLocks(); } } void Jit::Comp_Vmtvc(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); int vs = _VS; - int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + int imm = op & 0x7F; + if (imm < VFPU_CTRL_MAX) { fpr.MapRegV(vs, 0); - if (imm - 128 == VFPU_CTRL_CC) { + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, false, true); MOVD_xmm(gpr.R(MIPS_REG_VFPUCC), fpr.VX(vs)); } else { - MOVSS(MIPSSTATE_VAR_ELEM32(vfpuCtrl[0], imm - 128), fpr.VX(vs)); + MOVSS(MIPSSTATE_VAR_ELEM32(vfpuCtrl[0], imm), fpr.VX(vs)); } fpr.ReleaseSpillLocks(); - if (imm - 128 == VFPU_CTRL_SPREFIX) { + if (imm == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_TPREFIX) { + } else if (imm == VFPU_CTRL_TPREFIX) { js.prefixTFlag = JitState::PREFIX_UNKNOWN; - } else if (imm - 128 == VFPU_CTRL_DPREFIX) { + } else if (imm == VFPU_CTRL_DPREFIX) { js.prefixDFlag = JitState::PREFIX_UNKNOWN; } }