From 7dc775e54f0a58c299a13fb763c12308b8121418 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Mar 2019 10:44:59 -0800 Subject: [PATCH 01/57] IR: Use interp for unhandled prefix cases. The interpreter is not changed yet, so in theory this shouldn't change behavior. --- Core/MIPS/IR/IRCompVFPU.cpp | 99 +++++++++++++++++++++++----------- Core/MIPS/JitCommon/JitState.h | 31 ++++++----- 2 files changed, 88 insertions(+), 42 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 6393fff4a9..8261a3a3ac 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -108,6 +108,25 @@ 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); int data = op & 0xFFFFF; @@ -183,14 +202,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 +358,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 +385,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); @@ -448,18 +465,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 +490,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 +507,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 +545,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 +583,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 +597,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 +747,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 +756,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 +790,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 +799,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 +815,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 +882,7 @@ namespace MIPSComp { void IRFrontend::Comp_Vi2f(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - if (js.HasUnknownPrefix()) { + if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op)) { DISABLE; } @@ -904,6 +938,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) @@ -1100,7 +1137,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; } @@ -1364,7 +1401,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 +1415,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; } @@ -1633,8 +1670,9 @@ namespace MIPSComp { 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 +1695,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; } @@ -1898,8 +1936,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] 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 { From cfab70f14991bd04106124a23c586c38f46a3cb9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 11:19:08 -0700 Subject: [PATCH 02/57] interp: Handle abs/neg on nan values better. `fabsf` was altering other bits. --- Core/MIPS/MIPSIntVFPU.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 012579299e..91fe7e0d41 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -132,7 +132,7 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) r[i] = origV[regnum]; if (abs) - r[i] = fabs(r[i]); + ((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF; } else { @@ -140,7 +140,7 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) } if (negate) - r[i] = -r[i]; + ((u32 *)r)[i] = ((u32 *)r)[i] ^ 0x80000000; } } From fef8e71b8f709e435a2536105a44285fb3b5a673 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 12:31:12 -0700 Subject: [PATCH 03/57] interp: Correct behavior for matrixes with size=1. Games don't (shouldn't?) actually use 1x1 matrices, but they seem to have predictable behavior in matrix ops. --- Core/MIPS/MIPSVFPUUtils.cpp | 48 ++++++++++++++++++++----------------- Core/MIPS/MIPSVFPUUtils.h | 1 + 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index e493074665..f867ffd20f 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; @@ -220,24 +220,24 @@ void ReadMatrix(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; - 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 +248,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 +353,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 +369,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 +388,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 +404,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 +420,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 +429,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; } diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 8e25821b1d..c76870e8ad 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -113,6 +113,7 @@ enum VectorSize { }; enum MatrixSize { + M_1x1 = 1, M_2x2 = 2, M_3x3 = 3, M_4x4 = 4, From 30223cb17f28d0c3ec0d284b3445144680844ca5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 08:42:14 -0800 Subject: [PATCH 04/57] interp: Apply T prefix to D in vcmov. --- Core/MIPS/MIPSIntVFPU.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 91fe7e0d41..8f9403b7ce 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1643,7 +1643,6 @@ namespace MIPSInt int vt = _VT; int vs = _VS; int vd = _VD; - int cond = op&15; VectorSize sz = GetVecSize(op); int numElements = GetNumVectorElements(sz); float s[4]; @@ -1669,7 +1668,6 @@ namespace MIPSInt int vt = _VT; int vs = _VS; int vd = _VD; - int cond = op&15; VectorSize sz = GetVecSize(op); int numElements = GetNumVectorElements(sz); float s[4]; @@ -1692,8 +1690,7 @@ namespace MIPSInt } - void Int_Vcmov(MIPSOpcode op) - { + void Int_Vcmov(MIPSOpcode op) { int vs = _VS; int vd = _VD; int tf = (op >> 19) & 1; @@ -1704,28 +1701,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); From 9d1d4473e8ab12a52f1982bb43676bc5de3a1b8a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 15:40:58 -0800 Subject: [PATCH 05/57] interp: Confirm vi2f/vf2i prefix handling. --- Core/MIPS/MIPSIntVFPU.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 8f9403b7ce..717367d1b2 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -642,7 +642,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 +666,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,12 +682,13 @@ 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(); From d5273f589ac8348377cd371d7e73b2e9b2470e23 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 20:28:01 -0700 Subject: [PATCH 06/57] interp: Mask value in vpfxd. The actual register ends up with only the lower 12 bits, which makes sense since those are the only ones that do anything. --- Core/MIPS/ARM/ArmCompVFPU.cpp | 2 +- Core/MIPS/ARM64/Arm64CompVFPU.cpp | 2 +- Core/MIPS/IR/IRCompVFPU.cpp | 3 ++- Core/MIPS/MIPSIntVFPU.cpp | 2 ++ Core/MIPS/x86/CompVFPU.cpp | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 089ace124c..5b5c6baba3 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: diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 871c7e99ea..68428e69d2 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: diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 8261a3a3ac..a0c9aecd9c 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -129,6 +129,7 @@ namespace MIPSComp { 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) { @@ -141,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: diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 717367d1b2..3d982fb705 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -183,6 +183,8 @@ namespace MIPSInt { int data = op & 0xFFFFF; int regnum = (op >> 24) & 3; + if (regnum == VFPU_CTRL_DPREFIX) + data &= 0x00000FFF; currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX + regnum] = data; PC += 4; } diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 10a1e6e9bc..8f5d25d294 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; } From ec1dae57eb76be2f15460592e42c91b96b13c1b8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 23 Feb 2019 19:51:37 -0800 Subject: [PATCH 07/57] interp: Fix vbfy prefix handling. --- Core/MIPS/IR/IRCompVFPU.cpp | 2 +- Core/MIPS/MIPSIntVFPU.cpp | 56 ++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index a0c9aecd9c..f0dee9fd7b 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1966,7 +1966,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/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 3d982fb705..a441f2d8ec 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1045,33 +1045,51 @@ namespace MIPSInt 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 sprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; + ApplyPrefixST(s, sprefix | 0x000C0000, sz); + + // T prefix forces swizzle (zwxy.) + // That means negate still works, but constants are a bit weird. + u32 tprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; + ApplyPrefixST(t, (tprefix & ~0x000000FF) | 0x0000004E, 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 sprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; + ApplyPrefixST(s, sprefix | 0x000A0000, sz); + + // T prefix forces swizzle (yxwz.) + // That means negate still works, but constants are a bit weird. + u32 tprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; + ApplyPrefixST(t, (tprefix & ~0x000000FF) | 0x000000B1, 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; From 0be321315129c500a9296eb2a96d807018e13ed4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 05:28:36 -0800 Subject: [PATCH 08/57] interp: Correct vscl prefix handling. --- Core/MIPS/MIPSIntVFPU.cpp | 62 ++++++++++++++++++++----------------- Core/MIPS/MIPSVFPUUtils.cpp | 5 +++ Core/MIPS/MIPSVFPUUtils.h | 26 ++++++++++++++++ 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index a441f2d8ec..2e8ab3ca15 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -553,13 +553,14 @@ 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. @@ -582,13 +583,15 @@ namespace MIPSInt // 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. @@ -1057,13 +1060,14 @@ namespace MIPSInt if (op & 0x10000) { // vbfy2 // S prefix forces the negate flags (so z and w are negative.) - u32 sprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; - ApplyPrefixST(s, sprefix | 0x000C0000, sz); + 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 tprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; - ApplyPrefixST(t, (tprefix & ~0x000000FF) | 0x0000004E, sz); + 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) { @@ -1072,13 +1076,14 @@ namespace MIPSInt } else { // vbfy1 // S prefix forces the negate flags (so y and w are negative.) - u32 sprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; - ApplyPrefixST(s, sprefix | 0x000A0000, sz); + 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 tprefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; - ApplyPrefixST(t, (tprefix & ~0x000000FF) | 0x000000B1, sz); + 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"); @@ -1278,26 +1283,25 @@ namespace MIPSInt EatPrefixes(); } - void Int_VScl(MIPSOpcode op) - { - float s[4]; - float d[4]; + 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); diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index f867ffd20f..7be0b6210a 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -214,6 +214,11 @@ 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; diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index c76870e8ad..e8aad28672 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -120,6 +120,32 @@ enum MatrixSize { 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); From 1936e8c4d12787e53dc233253aa5bd9774b9bca4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 12:18:11 -0800 Subject: [PATCH 09/57] interp: Generate constants using prefixes. This way they properly respect negate in the S prefix. --- Core/MIPS/MIPSIntVFPU.cpp | 57 +++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 2e8ab3ca15..e83eb1d813 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -348,25 +348,26 @@ 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; @@ -396,32 +397,24 @@ namespace MIPSInt 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) { From b86a6af36486c2ff722f63381639a4ed79ff0a0d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 2 Mar 2019 12:41:58 -0800 Subject: [PATCH 10/57] interp: Properly apply mask on single lane ops. When using something like vadd.s, we should still be applying the mask. Mainly should only matter if masks are set in a conditional, or if games nop out instructions. --- Core/MIPS/MIPSIntVFPU.cpp | 13 +++++-------- Core/MIPS/MIPSVFPUUtils.cpp | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index e83eb1d813..a9fa18cc48 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -373,26 +373,23 @@ namespace MIPSInt 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(); } diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 7be0b6210a..88acda0744 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -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) { From 8fd8dce1852ecde44af5c6af614f1bdfdd9abcf7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 31 Mar 2019 10:33:26 -0700 Subject: [PATCH 11/57] interp: Use a helper to generate prefix consts. This makes more logical sense that using the VFPU_SWIZZLE and VFPU_ABS macros to select the constant, although that's how the bits work. --- Core/MIPS/MIPSIntVFPU.cpp | 22 ++++++++++++++-------- Core/MIPS/MIPSVFPUUtils.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index a9fa18cc48..476202c1fb 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -350,10 +350,10 @@ namespace MIPSInt VectorSize sz = GetVecSize(op); float d[4]; - int constant = 0; + VFPUConst constant = VFPUConst::ZERO; switch ((op >> 16) & 0xF) { - case 6: constant = 0; break; //vzero - case 7: constant = 1; break; //vone + case 6: constant = VFPUConst::ZERO; break; //vzero + case 7: constant = VFPUConst::ONE; break; //vone default: _dbg_assert_msg_(CPU, 0, "Trying to interpret instruction that can't be interpreted"); PC += 4; @@ -363,7 +363,7 @@ namespace MIPSInt // 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); + u32 sprefixAdd = VFPU_MAKE_CONSTANTS(constant, constant, constant, constant); ApplyPrefixST(d, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); ApplyPrefixD(d, sz); @@ -402,8 +402,14 @@ namespace MIPSInt // The S prefix generates constants, but negate is still respected. int offmask = sz == V_Quad || sz == V_Triple ? 3 : 1; int off = vd & offmask; + // If it's a pair, the identity starts in a different position. + VFPUConst constX = off == (0 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constY = off == (1 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constZ = off == (2 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constW = off == (3 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO; + 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)); + u32 sprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW); ApplyPrefixST(f, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz); ApplyPrefixD(f, sz); @@ -549,7 +555,7 @@ namespace MIPSInt // T prefix forces constants on and regnum to 1. // That means negate still works, and abs activates a different constant. u32 tprefixRemove = VFPU_ANY_SWIZZLE(); - u32 tprefixAdd = VFPU_SWIZZLE(1, 1, 1, 1) | VFPU_CONST(1, 1, 1, 1); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); for (int i = 0; i < GetNumVectorElements(sz); i++) { @@ -577,10 +583,10 @@ namespace MIPSInt 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. + // T prefix forces constants on and regnum to 1, 0, 1, 0. // That means negate still works, and abs activates a different constant. u32 tprefixRemove = VFPU_ANY_SWIZZLE(); - u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 1, 0) | VFPU_CONST(1, 1, 1, 1); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ZERO, VFPUConst::ONE, VFPUConst::ZERO); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), outSize); int n = GetNumVectorElements(sz); diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index e8aad28672..22356fc4c1 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -144,6 +144,36 @@ static u32 VFPU_NEGATE(int x, int y, int z, int w) { return VFPU_MASK(x, y, z, w) << 16; } +enum class VFPUConst { + NONE = -1, + ZERO, + ONE, + TWO, + HALF, + THREE, + THIRD, + FOURTH, + SIXTH, +}; + +static u32 VFPU_MAKE_CONSTANTS(VFPUConst x, VFPUConst y, VFPUConst z, VFPUConst w) { + u32 result = 0; + if (x != VFPUConst::NONE) { + // This sets the constant flag and the swizzle/abs flags for the right constant. + result |= (((int)x & 3) << 0) | (((int)x & 4) << 6) | (1 << 12); + } + if (y != VFPUConst::NONE) { + result |= (((int)y & 3) << 2) | (((int)y & 4) << 7) | (1 << 13); + } + if (z != VFPUConst::NONE) { + result |= (((int)z & 3) << 4) | (((int)z & 4) << 8) | (1 << 14); + } + if (w != VFPUConst::NONE) { + result |= (((int)w & 3) << 6) | (((int)w & 4) << 9) | (1 << 15); + } + return result; +} + u32 VFPURewritePrefix(int ctrl, u32 remove, u32 add); void ReadMatrix(float *rd, MatrixSize size, int reg); From 26b1368f7b0acb031a2a5c1c12f392bd6aa43fdb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 11:48:25 -0700 Subject: [PATCH 12/57] interp: Handle vrot prefixes mostly correctly. Still some issues with 1/2 results and negate on swizzle. --- Core/MIPS/MIPSIntVFPU.cpp | 40 ++++++++++++++++++++++++++++++--------- Core/MIPS/MIPSVFPUUtils.h | 1 + 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 476202c1fb..5979293d00 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1338,23 +1338,45 @@ 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(); diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 22356fc4c1..8fca2a0d7a 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; From d40ac043d473038696d1da1ec1a8d782b9ed8cb9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 16:39:08 -0700 Subject: [PATCH 13/57] interp: Handle prefixes for Vmmov/Vmmul/Vmscl. I doubt any actual code uses this, but we have some tricky VFPU bugs left, so just trying for maximum accuracy in the interpreter. --- Core/MIPS/IR/IRCompVFPU.cpp | 13 +++++-- Core/MIPS/MIPSIntVFPU.cpp | 73 +++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index f0dee9fd7b..c87f45bd14 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1043,8 +1043,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; @@ -1100,9 +1103,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; @@ -1216,7 +1223,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. diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 5979293d00..e6570a38ce 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -419,11 +419,8 @@ namespace MIPSInt } // 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; @@ -434,29 +431,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; @@ -467,27 +472,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(); From db28c612729f06fa9b4fd949340b23ad3f175a2e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 09:28:54 -0700 Subject: [PATCH 14/57] interp: Handle flush prefixes slightly better. --- Core/MIPS/MIPSIntVFPU.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index e6570a38ce..1d997202be 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -514,8 +514,11 @@ 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) From 2a5d4e577d1ecd338da7b8e578b500b6aced3ffd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 22:44:45 -0700 Subject: [PATCH 15/57] interp: Handle NAN more correctly in vscmp. --- Core/MIPS/MIPSIntVFPU.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 1d997202be..0369605124 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1681,26 +1681,31 @@ namespace MIPSInt 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); + ApplyPrefixD(d.f, sz); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } From af3ed69144fb520c65624061127019ebbd896700 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 11:57:11 -0700 Subject: [PATCH 16/57] interp: Mask moves to vfpu ctrl. These bits of the registers can't be written. --- Core/MIPS/IR/IRCompVFPU.cpp | 21 +++++++++++++++++++-- Core/MIPS/MIPSIntVFPU.cpp | 12 +++++++++--- Core/MIPS/MIPSVFPUUtils.cpp | 35 +++++++++++++++++++++++++++++++++++ Core/MIPS/MIPSVFPUUtils.h | 2 ++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index c87f45bd14..f9081133f1 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -982,7 +982,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; @@ -1028,7 +1036,16 @@ 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]); + u32 mask; + if (GetVFPUCtrlMask(imm - 128, &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 - 128, IRTEMP_0); + } else { + ir.Write(IROp::SetCtrlVFPUFReg, imm - 128, vfpuBase + voffset[vs]); + } + } if (imm - 128 == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; } else if (imm - 128 == VFPU_CTRL_TPREFIX) { diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 0369605124..6039d75523 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -181,7 +181,7 @@ 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; @@ -1511,7 +1511,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"); @@ -1538,7 +1541,10 @@ namespace MIPSInt int vs = _VS; int imm = op & 0xFF; if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - currentMIPS->vfpuCtrl[imm - 128] = VI(vs); + u32 mask; + if (GetVFPUCtrlMask(imm - 128, &mask)) { + currentMIPS->vfpuCtrl[imm - 128] = VI(vs) & mask; + } } PC += 4; } diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 88acda0744..af1328c4d3 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -526,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 { diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 8fca2a0d7a..34863a222b 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -242,4 +242,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); From dfc2449f35dcd48588506d4579b2b4828799a0ac Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 06:49:15 -0800 Subject: [PATCH 17/57] interp: Match actual vdiv prefix handling. --- Core/MIPS/MIPSIntVFPU.cpp | 79 +++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 6039d75523..c42db78547 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1803,51 +1803,64 @@ 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. + ApplySwizzleS(&s[n - 1], V_Single); + ApplySwizzleT(&t[n - 1], V_Single); + } + + 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); + + // The D prefix (even mask) is ignored by vdiv only (vmul, etc. do apply it.) + if (optype != 7) + ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); } - void Int_CrossQuat(MIPSOpcode op) { From aceb0a824461b5b88f2de15a5b75052eba96a8f3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 18:19:42 -0800 Subject: [PATCH 18/57] interp: Correct vrnd prefix handling. We don't match random values perfectly anyway, but at least we should vary at the right times. --- Core/MIPS/MIPSIntVFPU.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index c42db78547..be4628d5bd 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1327,31 +1327,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; From b2e024025f0038abdf943377fa1059a59bf612dd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Mar 2019 22:44:06 -0700 Subject: [PATCH 19/57] interp: Handle wrong sizes of vf2h/vh2f. Probably not ever used, but they have consistent behavior. --- Core/MIPS/IR/IRCompVFPU.cpp | 3 +++ Core/MIPS/MIPSIntVFPU.cpp | 25 ++++++++++--------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index f9081133f1..16ba587bfb 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -930,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])) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index be4628d5bd..ab30fcb8e9 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -717,8 +717,7 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vh2f(MIPSOpcode op) - { + void Int_Vh2f(MIPSOpcode op) { u32 s[4]; float d[4]; int vd = _VD; @@ -735,16 +734,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); @@ -752,32 +749,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); From 5749ae09d0447d2c84c8ec5bd3bee09b23c49875 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 20:25:47 -0700 Subject: [PATCH 20/57] interp: Correct vmfvc register behavior. The target and source registers were completely wrong. --- Core/MIPS/ARM/ArmCompVFPU.cpp | 10 +++++----- Core/MIPS/ARM64/Arm64CompVFPU.cpp | 10 +++++----- Core/MIPS/IR/IRCompVFPU.cpp | 8 ++++---- Core/MIPS/MIPSIntVFPU.cpp | 8 ++++---- Core/MIPS/x86/CompVFPU.cpp | 10 +++++----- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 5b5c6baba3..250c2cc0ac 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -1333,16 +1333,16 @@ namespace MIPSComp NEON_IF_AVAILABLE(CompNEON_Vmtvc); CONDITIONAL_DISABLE(VFPU_XFER); - int vs = _VS; - int imm = op & 0xFF; + int vd = _VD; + int imm = (op >> 8) & 0xFF; if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - fpr.MapRegV(vs); + fpr.MapRegV(vd); if (imm - 128 == 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); + VLDR(fpr.V(vd), SCRATCHREG1, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); } diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 68428e69d2..1e0382635f 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -1066,16 +1066,16 @@ namespace MIPSComp { void Arm64Jit::Comp_Vmfvc(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); - int vs = _VS; - int imm = op & 0xFF; + int vd = _VD; + int imm = (op >> 8) & 0xFF; if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - fpr.MapRegV(vs); + fpr.MapRegV(vd); if (imm - 128 == 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); + fp.LDR(32, INDEX_UNSIGNED, fpr.V(vd), SCRATCH1_64, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); } diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 16ba587bfb..c13ab20c72 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1018,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; + int vd = _VD; + int imm = (op >> 8) & 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); + ir.Write(IROp::FMovFromGPR, vfpuBase + voffset[vd], IRTEMP_0); } else { INVALIDOP; } diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index ab30fcb8e9..fbc52a018a 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1495,7 +1495,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? @@ -1526,10 +1526,10 @@ namespace MIPSInt } void Int_Vmfvc(MIPSOpcode op) { - int vs = _VS; - int imm = op & 0xFF; + int vd = _VD; + int imm = (op >> 8) & 0xFF; if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - VI(vs) = currentMIPS->vfpuCtrl[imm - 128]; + VI(vd) = currentMIPS->vfpuCtrl[imm - 128]; } PC += 4; } diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 8f5d25d294..88ae395642 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -2528,15 +2528,15 @@ void Jit::Comp_Mftv(MIPSOpcode op) { void Jit::Comp_Vmfvc(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); - int vs = _VS; - int imm = op & 0xFF; + int vd = _VD; + int imm = (op >> 8) & 0xFF; if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - fpr.MapRegV(vs, MAP_DIRTY | MAP_NOINIT); + fpr.MapRegV(vd, MAP_DIRTY | MAP_NOINIT); if (imm - 128 == 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 - 128)); } fpr.ReleaseSpillLocks(); } From c7e83cd4fa121a7c4dc31faa48c9c94a4ba3f098 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 22:23:52 -0700 Subject: [PATCH 21/57] interp: Correct vfim for -inf and similar. Was dropping the sign bit before for inf and nan. --- Core/MIPS/MIPSVFPUUtils.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index af1328c4d3..6c07bfef5a 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -573,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 { From aa998b815ceefbdadb3a6eeaf49dbfa1ec157755 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 22:59:37 -0700 Subject: [PATCH 22/57] interp: Force vscmp result of invalid to zero. Some other ops do this, but mostly only that do plus or minus. --- Core/MIPS/MIPSIntVFPU.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index fbc52a018a..65659e0603 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -170,6 +170,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 @@ -1707,6 +1724,7 @@ namespace MIPSInt d.f[i] = (float)((0.0f < a) - (a < 0.0f)); } } + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; From 5736b1be2a760970b082d5106faeb4e16ac62d9f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 31 Mar 2019 13:06:12 -0700 Subject: [PATCH 23/57] interp: Correct some negative invalid zero cases. In these cases, the input value wires to +0. Also, transposed the values in a comment (oops.) --- Core/MIPS/MIPSIntVFPU.cpp | 11 ++++++++--- Core/MIPS/MIPSVFPUUtils.h | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 65659e0603..b9bb5e2d2e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -774,8 +774,9 @@ namespace MIPSInt VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); // 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); + // Negate should not actually apply to invalid swizzle. + RetainInvalidSwizzleST(s, V_Quad); VectorSize outsize = V_Single; switch (sz) { @@ -1393,13 +1394,17 @@ namespace MIPSInt 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? + // We apply negSin later, not here. This handles zero a bit better. + u32 sprefixAdd = VFPU_NEGATE(0, 0, 0, 0); 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 (negSin) + sine = -sine; + RetainInvalidSwizzleST(&sine, V_Single); } if (sineLane == cosineLane) { diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 34863a222b..5a9c59ae70 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -35,7 +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.) +// TODO: cos(1) and sin(2) 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; From 68cdcba6c5cbb0a075a5a4e0e95e34ae639a7f2c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 26 Mar 2019 22:06:39 -0700 Subject: [PATCH 24/57] interp: Don't write lane 2 on single colorconv. Not that it's valid to use the op with that size anyway. --- Core/MIPS/IR/IRCompVFPU.cpp | 3 ++- Core/MIPS/MIPSIntVFPU.cpp | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index c13ab20c72..7240b5e8bb 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1953,8 +1953,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) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index b9bb5e2d2e..8de28d7e87 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -983,10 +983,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]; @@ -1024,7 +1027,7 @@ 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(); } From b75690787e3d7b1f0b5ee0fe525455b622f6f4ce Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 10:55:29 -0700 Subject: [PATCH 25/57] interp: Correct swizzle on vx2i ops. --- Core/MIPS/IR/IRCompVFPU.cpp | 3 +-- Core/MIPS/MIPSIntVFPU.cpp | 32 +++++++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 7240b5e8bb..9939822e99 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1541,8 +1541,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) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 8de28d7e87..573275e123 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -798,17 +798,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. @@ -819,7 +818,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; @@ -840,8 +839,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. @@ -861,8 +863,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. @@ -884,9 +889,10 @@ 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(); } From 4a2f8a74dc4a6a76e97d8f9e01a6c64d1475b41b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 11:23:11 -0700 Subject: [PATCH 26/57] interp: Correct size handling for vi2x ops. --- Core/MIPS/IR/IRCompVFPU.cpp | 3 +- Core/MIPS/MIPSIntVFPU.cpp | 116 +++++++++++++++++------------------- 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 9939822e99..c964628c72 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1456,8 +1456,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) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 573275e123..d6e0230b67 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -897,89 +897,85 @@ namespace MIPSInt 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(); } From 175ceef58326213d83239de6fd49450989a2b392 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 10:23:37 -0700 Subject: [PATCH 27/57] interp: Cleanup vsocp size handling. --- Core/MIPS/IR/IRCompVFPU.cpp | 2 +- Core/MIPS/MIPSIntVFPU.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index c964628c72..eb73cb27fa 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1905,7 +1905,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; } diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index d6e0230b67..0b8be5894d 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -579,8 +579,7 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vocp(MIPSOpcode op) - { + void Int_Vocp(MIPSOpcode op) { float s[4], t[4], d[4]; int vd = _VD; int vs = _VS; @@ -601,19 +600,21 @@ namespace MIPSInt // 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. @@ -636,7 +637,7 @@ 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(); From f2be0cb0836081e44eab3e245c728bb8d5c9add4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 10:05:48 -0700 Subject: [PATCH 28/57] interp: Correct prefixes for vsbn/vsbz. --- Core/MIPS/MIPSIntVFPU.cpp | 61 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 0b8be5894d..90c4a2b71c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2000,64 +2000,61 @@ namespace MIPSInt 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 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 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; From cc58d0d3a33edc8e79542c6d11e5b42ce6988fb4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 15:25:15 -0800 Subject: [PATCH 29/57] interp: Correct prefixes in vsrt ops. --- Core/MIPS/MIPSIntVFPU.cpp | 108 +++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index b9bb5e2d2e..e5fedcc170 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1132,92 +1132,104 @@ namespace MIPSInt 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. + 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]); + RetainInvalidSwizzleST(d, sz); 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. + 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]); + RetainInvalidSwizzleST(d, sz); 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. + 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]); + RetainInvalidSwizzleST(d, sz); 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. + 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]); + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; From 01aebe54b9e856496e32e0fba2ef29f56bb35699 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 10:41:02 -0800 Subject: [PATCH 30/57] interp: Correct vdet prefix handling. --- Core/MIPS/MIPSIntVFPU.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index e5fedcc170..c893d760a4 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1260,22 +1260,26 @@ namespace MIPSInt 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; From fa7ac7bc6468ea198bd32197018846d2c4733ca4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 12:18:36 -0800 Subject: [PATCH 31/57] interp: Correct simple vmov variant prefixing. --- Core/MIPS/MIPSIntVFPU.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index c893d760a4..beea0d0018 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -538,21 +538,29 @@ namespace MIPSInt 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); 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; + default: + ApplySwizzleS(s, sz); + } + for (int i = 0; i < GetNumVectorElements(sz); 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 @@ -573,7 +581,8 @@ namespace MIPSInt break; } } - ApplyPrefixD(d, sz); + // vsat1 is a prefix hack, so 0:1 doesn't apply. + ApplyPrefixD(d, sz, optype == 5); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); From dfd8094f219ced04f8b2c1052dab640c42b74051 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 11:14:34 -0800 Subject: [PATCH 32/57] interp: Implement vcrs prefixes and sizes. It only makes sense as triple, but it can be used as quad/single/etc. and has consistent and sane results. --- Core/MIPS/MIPSIntVFPU.cpp | 55 ++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index beea0d0018..1009bb8433 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -96,46 +96,34 @@ 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) { // Possible optimization shortcut: if (data == 0xe4) return; int n = GetNumVectorElements(size); - float origV[4]; + float origV[4]{}; 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++) - { + for (int i = 0; i < n; i++) { origV[i] = r[i]; } - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { int regnum = (data >> (i*2)) & 3; int abs = (data >> (8+i)) & 1; int negate = (data >> (16+i)) & 1; int constants = (data >> (12+i)) & 1; - 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 (!constants) { if (regnum >= n) { + // We mostly handle this now, but still worth reporting. 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) ((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF; - } - else - { + } else { r[i] = constantArray[regnum + (abs<<2)]; } @@ -1245,24 +1233,31 @@ namespace MIPSInt 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; From dffa238611c3a3377dbad36827e9241f00075cfc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 23:11:32 -0700 Subject: [PATCH 33/57] interp: Handle invalid swizzle in vsge/vslt. --- Core/MIPS/MIPSIntVFPU.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 1009bb8433..f20da21024 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1757,14 +1757,12 @@ namespace MIPSInt } void Int_Vsge(MIPSOpcode op) { + float s[4], t[4], d[4]; int vt = _VT; int vs = _VS; int vd = _VD; 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); @@ -1775,21 +1773,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; 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); @@ -1800,7 +1798,9 @@ 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(); From b881a689c41791dfc8a0979fe50647d2cfb3eb39 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 12:05:04 -0700 Subject: [PATCH 34/57] interp: Ignore high bit in vmfvc/vmtvc. Both 0 and 128 read/write the S prefix, for example. --- Core/MIPS/IR/IRCompVFPU.cpp | 22 +++++++++++----------- Core/MIPS/MIPSIntVFPU.cpp | 16 +++++++++------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index c13ab20c72..5ace3cae59 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -960,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]); @@ -1021,9 +1021,9 @@ namespace MIPSComp { // D[0] = VFPU_CTRL[i] int vd = _VD; - int imm = (op >> 8) & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - ir.Write(IROp::VfpuCtrlToReg, IRTEMP_0, imm - 128); + 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; @@ -1038,22 +1038,22 @@ namespace MIPSComp { int vs = _VS; int imm = op & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + if (imm < VFPU_CTRL_MAX) { u32 mask; - if (GetVFPUCtrlMask(imm - 128, &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 - 128, IRTEMP_0); + ir.Write(IROp::SetCtrlVFPUReg, imm, IRTEMP_0); } else { - ir.Write(IROp::SetCtrlVFPUFReg, imm - 128, vfpuBase + voffset[vs]); + ir.Write(IROp::SetCtrlVFPUFReg, imm, vfpuBase + voffset[vs]); } } - 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; } } else { diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index b9bb5e2d2e..309acc39d1 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1549,20 +1549,22 @@ namespace MIPSInt void Int_Vmfvc(MIPSOpcode op) { int vd = _VD; - int imm = (op >> 8) & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { - VI(vd) = currentMIPS->vfpuCtrl[imm - 128]; + 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) { + int imm = op & 0x7F; + if (imm < VFPU_CTRL_MAX) { u32 mask; - if (GetVFPUCtrlMask(imm - 128, &mask)) { - currentMIPS->vfpuCtrl[imm - 128] = VI(vs) & mask; + if (GetVFPUCtrlMask(imm, &mask)) { + currentMIPS->vfpuCtrl[imm] = VI(vs) & mask; } } PC += 4; From a5214d0b1ad5219f15b52b465e7426b86c27d678 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 12:14:15 -0700 Subject: [PATCH 35/57] Jit: Ignore high bit in vmfvc/vmtvc. --- Core/MIPS/ARM/ArmCompVFPU.cpp | 25 ++++++++++++++----------- Core/MIPS/ARM64/Arm64CompVFPU.cpp | 25 ++++++++++++++----------- Core/MIPS/x86/CompVFPU.cpp | 26 +++++++++++++++----------- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 250c2cc0ac..83911bcfa0 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -1334,17 +1334,20 @@ namespace MIPSComp CONDITIONAL_DISABLE(VFPU_XFER); int vd = _VD; - int imm = (op >> 8) & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { fpr.MapRegV(vd); - if (imm - 128 == VFPU_CTRL_CC) { + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, 0); VMOV(fpr.V(vd), gpr.R(MIPS_REG_VFPUCC)); } else { - ADDI2R(SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + (imm - 128) * 4, SCRATCHREG2); + 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 1e0382635f..cf67f4d84f 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -1067,17 +1067,20 @@ namespace MIPSComp { CONDITIONAL_DISABLE(VFPU_XFER); int vd = _VD; - int imm = (op >> 8) & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { fpr.MapRegV(vd); - if (imm - 128 == VFPU_CTRL_CC) { + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, 0); fp.FMOV(fpr.V(vd), gpr.R(MIPS_REG_VFPUCC)); } 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.LDR(32, INDEX_UNSIGNED, fpr.V(vd), SCRATCH1_64, 0); } fpr.ReleaseSpillLocksAndDiscardTemps(); + } else { + fpr.MapRegV(vd); + fp.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/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 88ae395642..73cd85c22a 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -2529,38 +2529,42 @@ void Jit::Comp_Mftv(MIPSOpcode op) { void Jit::Comp_Vmfvc(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_XFER); int vd = _VD; - int imm = (op >> 8) & 0xFF; - if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + int imm = (op >> 8) & 0x7F; + if (imm < VFPU_CTRL_MAX) { fpr.MapRegV(vd, MAP_DIRTY | MAP_NOINIT); - if (imm - 128 == VFPU_CTRL_CC) { + if (imm == VFPU_CTRL_CC) { gpr.MapReg(MIPS_REG_VFPUCC, true, false); MOVD_xmm(fpr.VX(vd), gpr.R(MIPS_REG_VFPUCC)); } else { - MOVSS(fpr.VX(vd), 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; } } From 85ff32eed1bd6e663a0b82656a0b6f278aa8b687 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 15:24:39 -0800 Subject: [PATCH 36/57] interp: Handle vsgn prefixing. One could compare against 3 using this, it just generates zeros to compare with. --- Core/MIPS/MIPSIntVFPU.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 309acc39d1..35d0b26be1 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -642,19 +642,24 @@ namespace MIPSInt 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_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); + + for (int i = 0; i < GetNumVectorElements(sz); 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) From 59905de7195b85928452a21c41babf0ee6b9581e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 15:27:53 -0700 Subject: [PATCH 37/57] interp: Correct vsgn out of swizzle bounds. --- Core/MIPS/IR/IRCompVFPU.cpp | 2 +- Core/MIPS/MIPSIntVFPU.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 5ace3cae59..1ed6d6b8bf 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1869,7 +1869,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; } diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 35d0b26be1..eb49329d5c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -648,14 +648,20 @@ namespace MIPSInt int vs = _VS; VectorSize sz = GetVecSize(op); ReadVector(s, sz, vs); - ApplySwizzleS(s, sz); // 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_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) { + 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; From b24f84d1a2da65bdabdb5352b84d9d75d8712005 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 13:21:52 -0700 Subject: [PATCH 38/57] interp: Handle prefixes on matrix init ops. --- Core/MIPS/IR/IRCompVFPU.cpp | 4 ++-- Core/MIPS/MIPSIntVFPU.cpp | 47 ++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 1ed6d6b8bf..84004f4641 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -426,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. diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index eb49329d5c..0de5b4d327 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -317,24 +317,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, @@ -344,8 +340,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 @@ -356,7 +351,37 @@ 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; + switch ((op >> 16) & 0xF) { + case 3: + { + VFPUConst constX = off == 0 ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constY = off == 1 ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constZ = off == 2 ? VFPUConst::ONE : VFPUConst::ZERO; + VFPUConst constW = off == 3 ? VFPUConst::ONE : VFPUConst::ZERO; + sprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW); + break; + } + case 6: + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); + break; + case 7: + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); + 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(); } From 6f87987e7bffc08001625093e869cbdce52662d3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Mar 2019 10:43:50 -0800 Subject: [PATCH 39/57] interp: Correct prefixes on vdot/vhdp. --- Core/MIPS/MIPSIntVFPU.cpp | 56 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 0de5b4d327..2619ea7b76 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1065,51 +1065,63 @@ namespace MIPSInt 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_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::NONE, VFPUConst::NONE, VFPUConst::ONE); + } else if (sz == V_Triple) { + sprefixRemove = VFPU_SWIZZLE(0, 0, 3, 0); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::NONE, VFPUConst::ONE, VFPUConst::NONE); + } else if (sz == V_Pair) { + sprefixRemove = VFPU_SWIZZLE(0, 3, 0, 0); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::ONE, VFPUConst::NONE, VFPUConst::NONE); + } else { + sprefixRemove = VFPU_SWIZZLE(3, 0, 0, 0); + sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::NONE, VFPUConst::NONE, VFPUConst::NONE); + } + 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(); From 89dbfd7d5b615ff63211463a23f9b4e99cfa5160 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 13:49:30 -0800 Subject: [PATCH 40/57] interp: Better prefixes for vdiv/similar ops. Turns out it does work, just uses the wrong slot like S/T after all. These other ops must go through the a similar process. --- Core/MIPS/MIPSIntVFPU.cpp | 79 ++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 1561be689a..f8e363ff1d 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -96,13 +96,13 @@ 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++) { @@ -132,14 +132,14 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) { } } -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) @@ -557,6 +557,7 @@ namespace MIPSInt int vs = _VS; int optype = (op >> 16) & 0x1f; VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); ReadVector(s, sz, vs); // Some of these are prefix hacks (affects constants, etc.) switch (optype) { @@ -566,10 +567,30 @@ namespace MIPSInt 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 < GetNumVectorElements(sz); i++) { + for (int i = 0; i < n; i++) { switch (optype) { case 0: d[i] = s[i]; break; //vmov case 1: d[i] = s[i]; break; //vabs (prefix) @@ -594,8 +615,33 @@ namespace MIPSInt break; } } - // vsat1 is a prefix hack, so 0:1 doesn't apply. - ApplyPrefixD(d, sz, optype == 5); + // 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(); @@ -1937,8 +1983,9 @@ namespace MIPSInt } 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. - ApplySwizzleS(&s[n - 1], V_Single); - ApplySwizzleT(&t[n - 1], V_Single); + // 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++) { @@ -1950,9 +1997,15 @@ namespace MIPSInt } } - // The D prefix (even mask) is ignored by vdiv only (vmul, etc. do apply it.) - if (optype != 7) + // 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 { + ApplyPrefixD(d, sz); + } WriteVector(d, sz, vd); PC += 4; EatPrefixes(); From e8c060bb5f5ebea73228f12abbab40ee71194f35 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 10:22:14 -0700 Subject: [PATCH 41/57] interp: Correct vwbn and vlgb size behavior. --- Core/MIPS/MIPSIntVFPU.cpp | 79 ++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index f8e363ff1d..424b646225 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2049,74 +2049,69 @@ namespace MIPSInt 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? 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]; } + + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } - // 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? 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]; + } + + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -2145,7 +2140,7 @@ namespace MIPSInt d.u[0] = s.u[0]; } - // If sz is greater than V_Single, the rest are unchanged. + // 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]; } @@ -2173,7 +2168,7 @@ namespace MIPSInt d.u[0] = (127 << 23) | (s.u[0] & 0x007FFFFF); } - // If sz is greater than V_Single, the rest are unchanged. + // 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]; } From 58573cd4b4d7b95f675dbbde559f9708311b82e5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 26 Mar 2019 21:20:05 -0700 Subject: [PATCH 42/57] interp: Handle invalid swizzle for vmin/vmax. --- Core/MIPS/MIPSIntVFPU.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 424b646225..44cf2816b3 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1767,6 +1767,7 @@ namespace MIPSInt } void Int_Vminmax(MIPSOpcode op) { + FloatBits s, t, d; int vt = _VT; int vs = _VS; int vd = _VD; @@ -1774,9 +1775,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); @@ -1822,6 +1820,7 @@ namespace MIPSInt EatPrefixes(); return; } + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -2004,6 +2003,7 @@ namespace MIPSInt currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; ApplyPrefixD(d, sz); } else { + RetainInvalidSwizzleST(d, sz); ApplyPrefixD(d, sz); } WriteVector(d, sz, vd); From 5414c12a152f2a23f54fc0d655e82707bb07b022 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 16:17:24 -0700 Subject: [PATCH 43/57] interp: Cleanup prefix/size in vcrsp/vqmul. --- Core/MIPS/IR/IRCompVFPU.cpp | 11 +++---- Core/MIPS/MIPSIntVFPU.cpp | 57 ++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index b07753a4d0..c447b68248 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1639,15 +1639,15 @@ 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] + // Note: Behaves as if it's implemented through a series of vdots. VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); @@ -1685,13 +1685,14 @@ 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) { diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 44cf2816b3..b4fc2fea5c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2011,38 +2011,69 @@ namespace MIPSInt 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 swizzle 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 swizzle 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; From 442d6450bb13ec6282395161075c54849bfc7fe7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 16:44:51 -0800 Subject: [PATCH 44/57] interp: Correct prefixes on vfad and vavg. Including write mask, which didn't work before. --- Core/MIPS/MIPSIntVFPU.cpp | 60 ++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index b4fc2fea5c..9b782a45ad 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1390,46 +1390,60 @@ namespace MIPSInt 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]; + ApplySwizzleS(s, V_Quad); + + // T prefix generates constants, but abs can change the constant. + u32 tprefixRemove = VFPU_ANY_SWIZZLE(); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); + 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; - ApplyPrefixD(&d,V_Single); - V(vd) = d; + ApplyPrefixD(&d, V_Single); + WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); } - void Int_Vavg(MIPSOpcode op) - { - float s[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, sz); - float sum = 0.0f; - int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - sum += s[i]; + ApplySwizzleS(s, V_Quad); + + // T prefix generates constants, but supports negate. + u32 tprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_ABS(1, 1, 1, 1); + u32 tprefixAdd; + if (sz == V_Single) + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO); + else if (sz == V_Pair) + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::HALF, VFPUConst::HALF, VFPUConst::HALF, VFPUConst::HALF); + else if (sz == V_Triple) + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::THIRD, VFPUConst::THIRD, VFPUConst::THIRD, VFPUConst::THIRD); + else if (sz == V_Quad) + tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::FOURTH, VFPUConst::FOURTH, VFPUConst::FOURTH, VFPUConst::FOURTH); + else + tprefixAdd = 0; + 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(); } From ec7cffa847fc48756888772569cf33ea45265bef Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 19:37:30 -0700 Subject: [PATCH 45/57] interp: Handle vtfm/vhtfm prefixes properly. --- Core/MIPS/IR/IRCompVFPU.cpp | 8 +++- Core/MIPS/MIPSIntVFPU.cpp | 81 ++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index c447b68248..a6ad66a383 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1327,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); @@ -1346,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]; diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 9b782a45ad..1314b1239e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1557,61 +1557,58 @@ namespace MIPSInt 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); + VFPUConst constX = VFPUConst::NONE; + VFPUConst constY = n < 2 ? VFPUConst::ZERO : VFPUConst::NONE; + VFPUConst constZ = n < 3 ? VFPUConst::ZERO : VFPUConst::NONE; + VFPUConst constW = n < 4 ? VFPUConst::ZERO : VFPUConst::NONE; + if (ins >= n) { + constY = ins == 1 ? VFPUConst::ONE : VFPUConst::ZERO; + constZ = ins == 2 ? VFPUConst::ONE : VFPUConst::ZERO; + constW = ins == 3 ? VFPUConst::ONE : VFPUConst::ZERO; } - 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; + u32 tprefixRemove = VFPU_SWIZZLE(0, n < 2 ? 3 : 0, n < 3 ? 3 : 0, n < 4 ? 3 : 0); + u32 tprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW); + 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(); From 00d088ab58b78ce5f42fe1c99821651a51c6145b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Apr 2019 15:09:23 -0700 Subject: [PATCH 46/57] GPU: Improve non dual source stencil replace. If we're replacing with a constant FF, we can make it work more often. Fixes #11249. --- GPU/Common/GPUStateUtils.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index e26498ae91..f16f69cd7e 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1171,7 +1171,13 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) { break; } } else if (!IsStencilTestOutputDisabled()) { - switch (ReplaceAlphaWithStencilType()) { + StencilValueType stencilValue = ReplaceAlphaWithStencilType(); + if (stencilValue == STENCIL_VALUE_UNIFORM && constantAlpha == 0x00) { + stencilValue = STENCIL_VALUE_ZERO; + } else if (stencilValue == STENCIL_VALUE_UNIFORM && constantAlpha == 0xFF) { + stencilValue = STENCIL_VALUE_ONE; + } + switch (stencilValue) { case STENCIL_VALUE_KEEP: blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ONE); break; From d79d829a1d54523aeeeebaec72d9aaa53f2ee81f Mon Sep 17 00:00:00 2001 From: psyberpunk Date: Wed, 10 Apr 2019 08:51:47 -0500 Subject: [PATCH 47/57] Update controller data base ps3 clone controller Update controller data base ps3 clone controller --- assets/gamecontrollerdb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index a4a6c7e528..13a4acd5cb 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -80,6 +80,7 @@ d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,righ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X, # Linux +050000004c0500006802000000000000,Sony PLAYSTATION(R)3 Controller,platform:Linux,a:b14,b:b13,x:b15,y:b12,back:b0,guide:b16,start:b3,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpdown:b6,dpleft:b7,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a12,righttrigger:a13, 03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, From f038573d4885b490b22b5a1722df19dbb24bf960 Mon Sep 17 00:00:00 2001 From: ITSTAKE Date: Sat, 13 Apr 2019 19:31:10 +0900 Subject: [PATCH 48/57] Added IO Timing Method option. --- libretro/libretro.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 3c656fe55b..1dce6fdb5e 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -188,6 +188,7 @@ static RetroOption ppsspp_separate_io_thread("ppsspp_separate_io_thread", static RetroOption ppsspp_unsafe_func_replacements("ppsspp_unsafe_func_replacements", "Unsafe FuncReplacements", true); static RetroOption ppsspp_sound_speedhack("ppsspp_sound_speedhack", "Sound Speedhack", false); static RetroOption ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false); +static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods:IOTIMING_FAST }, { "Host", IOTimingMethods:IOTIMING_HOST }, { "Realistic", IOTimingMethods:IOTIMING_REALISTIC } }); void retro_set_environment(retro_environment_t cb) { std::vector vars; @@ -216,6 +217,7 @@ void retro_set_environment(retro_environment_t cb) { vars.push_back(ppsspp_unsafe_func_replacements.GetOptions()); vars.push_back(ppsspp_sound_speedhack.GetOptions()); vars.push_back(ppsspp_cheats.GetOptions()); + vars.push_back(ppsspp_io_timing_method.GetOptions()); vars.push_back({}); environ_cb = cb; @@ -284,6 +286,7 @@ static void check_variables(CoreParameter &coreParam) { ppsspp_rendering_mode.Update(&g_Config.iRenderingMode); ppsspp_force_max_fps.Update(&g_Config.iForceMaxEmulatedFPS); ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore); + ppsspp_io_timing_method.Update((IOTimingMethod *)&g_Config.iIOTimingMethod); ppsspp_language.Update(&g_Config.iLanguage); if (g_Config.iLanguage < 0) { From bc4a203fcfa6df5501bffb9231a85689534e7828 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2019 14:51:35 -0700 Subject: [PATCH 49/57] Power: Correct hz on update and improve resched. It only reschedules when the PLL changes, which changes in steps. This also reads back much more accurate Mhz for each of PLL, CPU, and bus. --- Core/CoreTiming.cpp | 33 ++++--- Core/CoreTiming.h | 6 +- Core/HLE/sceDisplay.cpp | 4 +- Core/HLE/scePower.cpp | 195 ++++++++++++++++++++++++++-------------- 4 files changed, 147 insertions(+), 91 deletions(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 0e80ddc3a7..9d9123139f 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -92,43 +92,40 @@ static std::mutex externalEventLock; std::vector mhzChangeCallbacks; void FireMhzChange() { - for (auto it = mhzChangeCallbacks.begin(), end = mhzChangeCallbacks.end(); it != end; ++it) { - MHzChangeCallback cb = *it; + for (MHzChangeCallback cb : mhzChangeCallbacks) { cb(); } } -void SetClockFrequencyMHz(int cpuMhz) -{ +void SetClockFrequencyHz(int cpuHz) { // When the mhz changes, we keep track of what "time" it was before hand. // This way, time always moves forward, even if mhz is changed. lastGlobalTimeUs = GetGlobalTimeUs(); lastGlobalTimeTicks = GetTicks(); - CPU_HZ = cpuMhz * 1000000; + CPU_HZ = cpuHz; // TODO: Rescale times of scheduled events? FireMhzChange(); } -int GetClockFrequencyMHz() -{ - return CPU_HZ / 1000000; +int GetClockFrequencyHz() { + return CPU_HZ; } -u64 GetGlobalTimeUsScaled() -{ - s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; - int freq = GetClockFrequencyMHz(); - s64 usSinceLast = ticksSinceLast / freq; - return lastGlobalTimeUs + usSinceLast; +u64 GetGlobalTimeUsScaled() { + return GetGlobalTimeUs(); } -u64 GetGlobalTimeUs() -{ +u64 GetGlobalTimeUs() { s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; - int freq = GetClockFrequencyMHz(); - s64 usSinceLast = ticksSinceLast / freq; + int freq = GetClockFrequencyHz(); + s64 usSinceLast = ticksSinceLast * 1000000 / freq; + if (ticksSinceLast > UINT_MAX) { + // Adjust the calculated value to avoid overflow errors. + lastGlobalTimeUs += usSinceLast; + lastGlobalTimeTicks = GetTicks(); + } return lastGlobalTimeUs + usSinceLast; } diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index ee7a26a714..c72ff2ba44 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -67,7 +67,7 @@ inline s64 usToCycles(u64 us) { } inline s64 cyclesToUs(s64 cycles) { - return cycles / (CPU_HZ / 1000000); + return (cycles * CPU_HZ) / 1000000; } namespace CoreTiming @@ -121,8 +121,8 @@ namespace CoreTiming void DoState(PointerWrap &p); - void SetClockFrequencyMHz(int cpuMhz); - int GetClockFrequencyMHz(); + void SetClockFrequencyHz(int cpuHz); + int GetClockFrequencyHz(); extern int slicelength; }; // end of namespace diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 57f510f278..d1a2de1110 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -1069,10 +1069,10 @@ static u32 sceDisplayGetVcount() { } static u32 __DisplayGetCurrentHcount() { - const static int ticksPerVblank333 = 333 * 1000000 / 60 / hCountPerVblank; const int ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; + const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank; // Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right. - return 1 + (ticksIntoFrame / (CoreTiming::GetClockFrequencyMHz() * ticksPerVblank333 / 333)); + return 1 + (ticksIntoFrame / ticksPerVblank); } static u32 __DisplayGetAccumulatedHcount() { diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 8204a51c58..35ff5c4f0e 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -57,10 +57,57 @@ static int powerCbSlots[numberOfCBPowerSlots]; static std::vector volatileWaitingThreads; // Should this belong here, or in CoreTiming? -static int RealpllFreq = 222; -static int RealbusFreq = 111; -static int pllFreq = 222; -static int busFreq = 111; +static int RealpllFreq = 222000000; +static int RealbusFreq = 111000000; +static int pllFreq = 222000000; +static int busFreq = 111000000; + +// The CPU mhz can only be a multiple of the PLL divided by 511. +int PowerCpuMhzToHz(int desired, int pllHz) { + double maxfreq = desired * 1000000.0; + + double step = (double)pllHz / 511.0; + // These values seem to be locked. + if (pllHz >= 333000000 && desired == 333) { + return 333000000; + } else if (pllHz >= 222000000 && desired == 222) { + return 222000000; + } + + double freq = 0; + while (freq + step < maxfreq) { + freq += step; + } + + // We match the PSP's HLE funcs better when we have the same float error, it seems. + return (float)(freq / 1000000.0f) * 1000000; +} + +int PowerPllMhzToHz(int mhz) { + // These seem to be the only steps it has. + if (mhz <= 190) + return 190285721; + if (mhz <= 222) + return 222000000; + if (mhz <= 266) + return 266399994; + if (mhz <= 333) + return 333000000; + return mhz * 1000000; +} + +int PowerBusMhzToHz(int mhz) { + // These seem to be the only steps it has. + if (mhz <= 95) + return 95142860; + if (mhz <= 111) + return 111000000; + if (mhz <= 133) + return 133199997; + if (mhz <= 166) + return 166500000; + return mhz * 1000000; +} void __PowerInit() { memset(powerCbSlots, 0, sizeof(powerCbSlots)); @@ -68,15 +115,15 @@ void __PowerInit() { volatileWaitingThreads.clear(); if (g_Config.iLockedCPUSpeed > 0) { - CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); - pllFreq = g_Config.iLockedCPUSpeed; - busFreq = g_Config.iLockedCPUSpeed / 2; + pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed); + busFreq = PowerBusMhzToHz(pllFreq / 2000000); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq)); } else { - pllFreq = 222; - busFreq = 111; + pllFreq = PowerPllMhzToHz(222); + busFreq = PowerBusMhzToHz(111); } - RealpllFreq = 222; - RealbusFreq = 111; + RealpllFreq = PowerPllMhzToHz(222); + RealbusFreq = PowerBusMhzToHz(111); } void __PowerDoState(PointerWrap &p) { @@ -87,17 +134,20 @@ void __PowerDoState(PointerWrap &p) { if (s >= 2) { p.Do(RealpllFreq); p.Do(RealbusFreq); - } - else { - RealpllFreq = 222; - RealbusFreq = 111; + + if (RealpllFreq < 1000000) + RealpllFreq = PowerPllMhzToHz(RealpllFreq); + if (RealbusFreq < 1000000) + RealbusFreq = PowerBusMhzToHz(RealbusFreq); + } else { + RealpllFreq = PowerPllMhzToHz(222); + RealbusFreq = PowerBusMhzToHz(111); } if (g_Config.iLockedCPUSpeed > 0) { - CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); - pllFreq = g_Config.iLockedCPUSpeed; - busFreq = g_Config.iLockedCPUSpeed / 2; - } - else { + pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed); + busFreq = PowerBusMhzToHz(pllFreq / 2000000); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq)); + } else { pllFreq = RealpllFreq; busFreq = RealbusFreq; } @@ -372,104 +422,113 @@ static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { + // 190 might (probably) be a typo for 19, but it's what the actual PSP validates against. + if (pllfreq < 19 || pllfreq < cpufreq || pllfreq > 333) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid pll frequency"); + } if (cpufreq == 0 || cpufreq > 333) { - WARN_LOG(HLE,"scePowerSetClockFrequency(%i,%i,%i): invalid frequency", pllfreq, cpufreq, busfreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid cpu frequency"); + } + if (busfreq == 0 || busfreq > 166) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid bus frequency"); } // TODO: More restrictions. if (g_Config.iLockedCPUSpeed > 0) { INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i): locked by user config at %i, %i, %i", pllfreq, cpufreq, busfreq, g_Config.iLockedCPUSpeed, g_Config.iLockedCPUSpeed, busFreq); - } - else { + } else { INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i)", pllfreq, cpufreq, busfreq); } - if (RealpllFreq != pllfreq || RealbusFreq != busfreq) { - RealpllFreq = pllfreq; - RealbusFreq = busfreq; + // Only reschedules when the stepped PLL frequency changes. + // It seems like the busfreq parameter has no effect (but can cause errors.) + if (RealpllFreq != PowerPllMhzToHz(pllfreq)) { + RealpllFreq = PowerPllMhzToHz(pllfreq); + RealbusFreq = PowerBusMhzToHz(RealpllFreq / 2000000); if (g_Config.iLockedCPUSpeed <= 0) { - pllFreq = pllfreq; - busFreq = busfreq; - CoreTiming::SetClockFrequencyMHz(cpufreq); + pllFreq = RealpllFreq; + busFreq = RealbusFreq; + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); } return hleDelayResult(0, "scepower set clockFrequency", 150000); } if (g_Config.iLockedCPUSpeed <= 0) - CoreTiming::SetClockFrequencyMHz(cpufreq); + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); return 0; } static u32 scePowerSetCpuClockFrequency(u32 cpufreq) { - if(g_Config.iLockedCPUSpeed > 0) { - DEBUG_LOG(HLE,"scePowerSetCpuClockFrequency(%i): locked by user config at %i", cpufreq, g_Config.iLockedCPUSpeed); + if (cpufreq == 0 || cpufreq > 333) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency"); } - else { - if (cpufreq == 0 || cpufreq > 333) { - WARN_LOG(HLE,"scePowerSetCpuClockFrequency(%i): invalid frequency", cpufreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; - } - CoreTiming::SetClockFrequencyMHz(cpufreq); - INFO_LOG(HLE,"scePowerSetCpuClockFrequency(%i)", cpufreq); + if (g_Config.iLockedCPUSpeed > 0) { + return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed); } - return 0; + CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); + return hleLogSuccessI(SCEMISC, 0); } static u32 scePowerSetBusClockFrequency(u32 busfreq) { - if(g_Config.iLockedCPUSpeed > 0) { - DEBUG_LOG(HLE,"scePowerSetBusClockFrequency(%i): locked by user config at %i", busfreq, busFreq); + if (busfreq == 0 || busfreq > 111) { + return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency"); } - else { - if (busfreq == 0 || busfreq > 111) { - WARN_LOG(HLE,"scePowerSetBusClockFrequency(%i): invalid frequency", busfreq); - return SCE_KERNEL_ERROR_INVALID_VALUE; - } - // TODO: It seems related to other frequencies, though. - busFreq = busfreq; - INFO_LOG_REPORT_ONCE(SetBusClockFrequency,HLE,"scePowerSetBusClockFrequency(%i)", busfreq); + if (g_Config.iLockedCPUSpeed > 0) { + return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed / 2); } - return 0; + + // The value passed is validated, but then doesn't seem to matter for the result. + // However, this sets a different hz than scePowerSetClockFrequency would have. + + if (pllFreq <= 190) + busFreq = 94956673; + else if (pllFreq <= 222) + busFreq = 111000000; + else if (pllFreq <= 266) + busFreq = 132939331; + else if (pllFreq <= 333) + busFreq = 165848343; + else + busFreq = pllFreq / 2; + + return hleLogSuccessI(SCEMISC, 0); } static u32 scePowerGetCpuClockFrequencyInt() { - int cpuFreq = CoreTiming::GetClockFrequencyMHz(); - DEBUG_LOG(HLE,"%i=scePowerGetCpuClockFrequencyInt()", cpuFreq); - return cpuFreq; + int cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000; + return hleLogSuccessI(SCEMISC, cpuFreq); } static u32 scePowerGetPllClockFrequencyInt() { - INFO_LOG(HLE,"%i=scePowerGetPllClockFrequencyInt()", pllFreq); - return pllFreq; + return hleLogSuccessInfoI(SCEMISC, pllFreq / 1000000); } static u32 scePowerGetBusClockFrequencyInt() { - INFO_LOG(HLE,"%i=scePowerGetBusClockFrequencyInt()", busFreq); - return busFreq; + return hleLogSuccessInfoI(SCEMISC, busFreq / 1000000); } static float scePowerGetCpuClockFrequencyFloat() { - int cpuFreq = CoreTiming::GetClockFrequencyMHz(); - DEBUG_LOG(HLE, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq); - return (float) cpuFreq; + float cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000.0f; + DEBUG_LOG(SCEMISC, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq); + return cpuFreq; } static float scePowerGetPllClockFrequencyFloat() { - INFO_LOG(HLE, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq); - return (float) pllFreq; + INFO_LOG(SCEMISC, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq / 1000000.0f); + return (float) pllFreq / 1000000.0f; } static float scePowerGetBusClockFrequencyFloat() { - INFO_LOG(HLE, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq); - return (float) busFreq; + INFO_LOG(SCEMISC, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq / 1000000.0f); + return (float) busFreq / 1000000.0f; } static int scePowerTick() { - DEBUG_LOG(HLE, "scePowerTick()"); + DEBUG_LOG(SCEMISC, "scePowerTick()"); // Don't think we need to do anything. return 0; } static u32 IsPSPNonFat() { - DEBUG_LOG(HLE, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel); + DEBUG_LOG(SCEMISC, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel); return g_Config.iPSPModel; } From ad299ca92dcb9f5dca572508a8d8c1d64cb01b7e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2019 15:06:32 -0700 Subject: [PATCH 50/57] Power: Match reschedule timing better. --- Core/HLE/scePower.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 35ff5c4f0e..b4fe3b0925 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -441,6 +441,8 @@ static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { // Only reschedules when the stepped PLL frequency changes. // It seems like the busfreq parameter has no effect (but can cause errors.) if (RealpllFreq != PowerPllMhzToHz(pllfreq)) { + int oldPll = RealpllFreq / 1000000; + RealpllFreq = PowerPllMhzToHz(pllfreq); RealbusFreq = PowerBusMhzToHz(RealpllFreq / 2000000); if (g_Config.iLockedCPUSpeed <= 0) { @@ -448,7 +450,16 @@ static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { busFreq = RealbusFreq; CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); } - return hleDelayResult(0, "scepower set clockFrequency", 150000); + + // The delay depends on the source and destination frequency, most are 150ms. + int newPll = RealpllFreq / 1000000; + int usec = 150000; + if ((newPll == 190 && oldPll == 222) || (newPll == 222 && oldPll == 190)) + usec = 15700; + else if ((newPll == 266 && oldPll == 333) || (newPll == 333 && oldPll == 266)) + usec = 16600; + + return hleDelayResult(0, "scepower set clockFrequency", usec); } if (g_Config.iLockedCPUSpeed <= 0) CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq)); From b9352354c9b83c63214ce6ccdf359c05a031784e Mon Sep 17 00:00:00 2001 From: M4xw Date: Mon, 15 Apr 2019 12:07:57 +0200 Subject: [PATCH 51/57] Masked PSP Memory support for the AArch64 Dynarec --- Core/MIPS/ARM64/Arm64Asm.cpp | 3 +++ Core/MIPS/ARM64/Arm64CompALU.cpp | 2 +- Core/MIPS/ARM64/Arm64CompFPU.cpp | 10 +++++++++- Core/MIPS/ARM64/Arm64CompLoadStore.cpp | 23 ++++++++++++++++++----- Core/MIPS/ARM64/Arm64CompVFPU.cpp | 18 +++++++++++++++++- Core/MIPS/ARM64/Arm64RegCache.cpp | 3 +++ 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 1abf148d7c..755e35f57c 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -247,6 +247,9 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { } LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc)); +#ifdef MASKED_PSP_MEMORY + ANDI2R(SCRATCH1, SCRATCH1, 0x3FFFFFFF); +#endif LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64); LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8) ANDI2R(SCRATCH1, SCRATCH1, 0x00FFFFFF); diff --git a/Core/MIPS/ARM64/Arm64CompALU.cpp b/Core/MIPS/ARM64/Arm64CompALU.cpp index aa3ffb577e..f97ba0dee7 100644 --- a/Core/MIPS/ARM64/Arm64CompALU.cpp +++ b/Core/MIPS/ARM64/Arm64CompALU.cpp @@ -43,7 +43,7 @@ using namespace MIPSAnalyst; // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +//#define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } diff --git a/Core/MIPS/ARM64/Arm64CompFPU.cpp b/Core/MIPS/ARM64/Arm64CompFPU.cpp index 98121615b1..efe6887f23 100644 --- a/Core/MIPS/ARM64/Arm64CompFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompFPU.cpp @@ -51,7 +51,7 @@ // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } @@ -102,7 +102,11 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op) fpr.SpillLock(ft); fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); @@ -129,7 +133,11 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op) fpr.MapReg(ft); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index e322d4a9a7..a4d647d98b 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -41,7 +41,7 @@ // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +//#define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { Comp_Generic(op); return; } @@ -56,6 +56,9 @@ namespace MIPSComp { } else { MOV(SCRATCH1, gpr.R(rs)); } +#ifdef MASKED_PSP_MEMORY + ANDI2R(SCRATCH1, SCRATCH1, 0x3FFFFFFF); +#endif } std::vector Arm64Jit::SetScratch1ForSafeAddress(MIPSGPReg rs, s16 offset, ARM64Reg tempReg) { @@ -135,12 +138,17 @@ namespace MIPSComp { std::vector skips; if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = iaddr & 0x3FFFFFFF; +#else + u32 addr = iaddr; +#endif // Need to initialize since this only loads part of the register. // But rs no longer matters (even if rs == rt) since we have the address. gpr.MapReg(rt, load ? MAP_DIRTY : 0); - gpr.SetRegImm(SCRATCH1, iaddr & ~3); + gpr.SetRegImm(SCRATCH1, addr & ~3); - u8 shift = (iaddr & 3) * 8; + u8 shift = (addr & 3) * 8; switch (o) { case 34: // lwl @@ -347,7 +355,12 @@ namespace MIPSComp { } if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) { - if (offset == 0) { +#ifdef MASKED_PSP_MEMORY + u32 addr = iaddr & 0x3FFFFFFF; +#else + u32 addr = iaddr; +#endif + if (addr == iaddr && offset == 0) { // It was already safe. Let's shove it into a reg and use it directly. if (targetReg == INVALID_REG) { load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs); @@ -360,7 +373,7 @@ namespace MIPSComp { gpr.MapReg(rt, load ? MAP_NOINIT : 0); targetReg = gpr.R(rt); } - gpr.SetRegImm(SCRATCH1, iaddr); + gpr.SetRegImm(SCRATCH1, addr); addrReg = SCRATCH1; } } else { diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index cf67f4d84f..22b618aaea 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -37,7 +37,7 @@ // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { fpr.ReleaseSpillLocksAndDiscardTemps(); Comp_Generic(op); return; } +// #define CONDITIONAL_DISABLE(flag) { fpr.ReleaseSpillLocksAndDiscardTemps(); Comp_Generic(op); return; } #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } #define DISABLE { fpr.ReleaseSpillLocksAndDiscardTemps(); Comp_Generic(op); return; } @@ -222,7 +222,11 @@ namespace MIPSComp { // CC might be set by slow path below, so load regs first. fpr.MapRegV(vt, MAP_DIRTY | MAP_NOINIT); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); @@ -251,7 +255,11 @@ namespace MIPSComp { // CC might be set by slow path below, so load regs first. fpr.MapRegV(vt); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = offset + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1, addr); } else { gpr.MapReg(rs); @@ -293,7 +301,11 @@ namespace MIPSComp { fpr.MapRegsAndSpillLockV(vregs, V_Quad, MAP_DIRTY | MAP_NOINIT); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (imm + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = imm + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1_64, addr + (uintptr_t)Memory::base); } else { gpr.MapReg(rs); @@ -326,7 +338,11 @@ namespace MIPSComp { fpr.MapRegsAndSpillLockV(vregs, V_Quad, 0); if (gpr.IsImm(rs)) { +#ifdef MASKED_PSP_MEMORY + u32 addr = (imm + gpr.GetImm(rs)) & 0x3FFFFFFF; +#else u32 addr = imm + gpr.GetImm(rs); +#endif gpr.SetRegImm(SCRATCH1_64, addr + (uintptr_t)Memory::base); } else { gpr.MapReg(rs); diff --git a/Core/MIPS/ARM64/Arm64RegCache.cpp b/Core/MIPS/ARM64/Arm64RegCache.cpp index dd2c83c302..b089efa309 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.cpp +++ b/Core/MIPS/ARM64/Arm64RegCache.cpp @@ -440,6 +440,9 @@ Arm64Gen::ARM64Reg Arm64RegCache::MapRegAsPointer(MIPSGPReg reg) { if (!jo_->enablePointerify) { // Convert to a pointer by adding the base and clearing off the top bits. // If SP, we can probably avoid the top bit clear, let's play with that later. +#ifdef MASKED_PSP_MEMORY + emit_->ANDI2R(EncodeRegTo64(a), EncodeRegTo64(a), 0x3FFFFFFF); +#endif emit_->ADD(EncodeRegTo64(a), EncodeRegTo64(a), MEMBASEREG); mr[reg].loc = ML_ARMREG_AS_PTR; } else if (!ar[a].pointerified) { From 5ee7d73be837a7b4fafffb944deefbe3495e8a49 Mon Sep 17 00:00:00 2001 From: ITSTAKE Date: Mon, 15 Apr 2019 14:07:19 +0000 Subject: [PATCH 52/57] Fix typo --- libretro/libretro.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 1dce6fdb5e..efed32cceb 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -188,7 +188,7 @@ static RetroOption ppsspp_separate_io_thread("ppsspp_separate_io_thread", static RetroOption ppsspp_unsafe_func_replacements("ppsspp_unsafe_func_replacements", "Unsafe FuncReplacements", true); static RetroOption ppsspp_sound_speedhack("ppsspp_sound_speedhack", "Sound Speedhack", false); static RetroOption ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false); -static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods:IOTIMING_FAST }, { "Host", IOTimingMethods:IOTIMING_HOST }, { "Realistic", IOTimingMethods:IOTIMING_REALISTIC } }); +static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods:IOTIMING_FAST }, { "Host", IOTimingMethods:IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods:IOTIMING_REALISTIC } }); void retro_set_environment(retro_environment_t cb) { std::vector vars; @@ -286,7 +286,7 @@ static void check_variables(CoreParameter &coreParam) { ppsspp_rendering_mode.Update(&g_Config.iRenderingMode); ppsspp_force_max_fps.Update(&g_Config.iForceMaxEmulatedFPS); ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore); - ppsspp_io_timing_method.Update((IOTimingMethod *)&g_Config.iIOTimingMethod); + ppsspp_io_timing_method.Update((IOTimingMethods *)&g_Config.iIOTimingMethod); ppsspp_language.Update(&g_Config.iLanguage); if (g_Config.iLanguage < 0) { From 40f15427983cb812d4a2a6b9b50ea61205ee8083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Apr 2019 21:21:52 +0200 Subject: [PATCH 53/57] Update ffmpeg and lang submodules --- assets/lang | 2 +- ffmpeg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/lang b/assets/lang index 86c62b9a23..152c6893e9 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 86c62b9a23936d556ea0899673f7b078a2f12224 +Subproject commit 152c6893e9a30cc45f363cc87a8f48dc5d63cddb diff --git a/ffmpeg b/ffmpeg index 7472c903ec..4e8e6a1e7a 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit 7472c903ec771c00af9925097e8d37075e9a379f +Subproject commit 4e8e6a1e7a9ecb4736b4c96f8b3ef1f0e267080b From 8d4ab57b46263f838394f06409dc19e0aaf0b018 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 16 Apr 2019 17:36:29 -0700 Subject: [PATCH 54/57] Oops, got these backwards. Surprising this didn't break as much as expected. Mostly broke the 60 FPS limit hack. --- Core/CoreTiming.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index c72ff2ba44..4c5cb013c8 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -67,7 +67,7 @@ inline s64 usToCycles(u64 us) { } inline s64 cyclesToUs(s64 cycles) { - return (cycles * CPU_HZ) / 1000000; + return (cycles * 1000000) / CPU_HZ; } namespace CoreTiming From f7aed9a754b423d4cda29b7e188ae867fc4a8421 Mon Sep 17 00:00:00 2001 From: ITSTAKE Date: Sat, 20 Apr 2019 18:02:09 +0900 Subject: [PATCH 55/57] Fix typo again --- libretro/libretro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index efed32cceb..33c7091460 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -188,7 +188,7 @@ static RetroOption ppsspp_separate_io_thread("ppsspp_separate_io_thread", static RetroOption ppsspp_unsafe_func_replacements("ppsspp_unsafe_func_replacements", "Unsafe FuncReplacements", true); static RetroOption ppsspp_sound_speedhack("ppsspp_sound_speedhack", "Sound Speedhack", false); static RetroOption ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false); -static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods:IOTIMING_FAST }, { "Host", IOTimingMethods:IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods:IOTIMING_REALISTIC } }); +static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods::IOTIMING_FAST }, { "Host", IOTimingMethods::IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods::IOTIMING_REALISTIC } }); void retro_set_environment(retro_environment_t cb) { std::vector vars; From f6fe917615681e2d9d391c8138e4aee896494ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 19 Apr 2019 22:50:48 +0200 Subject: [PATCH 56/57] Update gradle stuff again --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 7b7c94e172..6ac36dd8dc 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.0' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 32bb5e0c8d..cd3f2c2a38 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jan 15 23:30:05 CET 2019 +#Fri Apr 19 22:49:10 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip From 00bf914ce47b8e746ae24328d38cd6e6e067c1b0 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 23 Apr 2019 17:35:24 +0200 Subject: [PATCH 57/57] Band-Aid for libretro Savestate load Proper would be using PPSSPP's Queue. This will need frontend extensions to do it sanely. Fixes #11429 (mitigate for now) --- libretro/libretro.cpp | 46 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 4b6da2b3ed..a267c8a225 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -453,6 +453,7 @@ void EmuThreadStop() { // Need to keep eating frames to allow the EmuThread to exit correctly. continue; } + emuThread.join(); emuThread = std::thread(); ctx->ThreadEnd(); @@ -462,8 +463,11 @@ void EmuThreadPause() { if (emuThreadState != EmuThreadState::RUNNING) { return; } + emuThreadState = EmuThreadState::PAUSE_REQUESTED; - ctx->ThreadFrame(); + + ctx->ThreadFrame(); // Eat 1 frame + while (emuThreadState != EmuThreadState::PAUSED) { sleep_ms(1); } @@ -675,6 +679,11 @@ void retro_run(void) { retro_input(); if (useEmuThread) { + if(emuThreadState == EmuThreadState::PAUSED || emuThreadState == EmuThreadState::PAUSE_REQUESTED) { + ctx->SwapBuffers(); + return; + } + if (emuThreadState != EmuThreadState::RUNNING) { EmuThreadStart(); } @@ -705,19 +714,48 @@ struct SaveStart { } // namespace SaveState size_t retro_serialize_size(void) { + // TODO: Libretro API extension to use the savestate queue + if (useEmuThread) { + EmuThreadPause(); + } + SaveState::SaveStart state; - return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF; + return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF; // We don't unpause intentionally } bool retro_serialize(void *data, size_t size) { + // TODO: Libretro API extension to use the savestate queue + if (useEmuThread) { + EmuThreadPause(); // Does nothing if already paused + } + SaveState::SaveStart state; assert(CChunkFileReader::MeasurePtr(state) <= size); - return CChunkFileReader::SavePtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + bool retVal = CChunkFileReader::SavePtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + + if (useEmuThread) { + EmuThreadStart(); + sleep_ms(4); + } + + return retVal; } bool retro_unserialize(const void *data, size_t size) { + // TODO: Libretro API extension to use the savestate queue + if (useEmuThread) { + EmuThreadPause(); // Does nothing if already paused + } + SaveState::SaveStart state; - return CChunkFileReader::LoadPtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + bool retVal = CChunkFileReader::LoadPtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; + + if (useEmuThread) { + EmuThreadStart(); + sleep_ms(4); + } + + return retVal; } void *retro_get_memory_data(unsigned id) {