From 5ae9931d88e3008277f8b8973f7770a006053ec1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 23 Feb 2019 19:51:37 -0800 Subject: [PATCH 01/43] 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 6393fff4a9..3af7e96490 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1926,7 +1926,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 012579299e..0d239de236 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1041,33 +1041,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 921ecb4decf9a5e3214038c95e84fe92b011bc3b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 05:28:36 -0800 Subject: [PATCH 02/43] 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 0d239de236..14db6b8e31 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -551,13 +551,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. @@ -580,13 +581,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. @@ -1053,13 +1056,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) { @@ -1068,13 +1072,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"); @@ -1274,26 +1279,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 e493074665..f2630800d8 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 8e25821b1d..884eb128e1 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -119,6 +119,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 c270bd2d85064c4df39b4e2f30fcd4834a07b0db Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 06:49:15 -0800 Subject: [PATCH 03/43] 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 14db6b8e31..1614105f9e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1756,51 +1756,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 eaa112789e91b103110bb56fa014d34116e9fad9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 08:42:14 -0800 Subject: [PATCH 04/43] 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 1614105f9e..fc2f969bd0 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1665,7 +1665,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]; @@ -1691,7 +1690,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]; @@ -1714,8 +1712,7 @@ namespace MIPSInt } - void Int_Vcmov(MIPSOpcode op) - { + void Int_Vcmov(MIPSOpcode op) { int vs = _VS; int vd = _VD; int tf = (op >> 19) & 1; @@ -1726,28 +1723,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 ceeaf3b7ef57dd985632d92639821cf58d452b81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 10:41:02 -0800 Subject: [PATCH 05/43] 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 fc2f969bd0..e42e81e86c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1213,22 +1213,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 16bc3c00867113ec627cb092cf3381e5e8b3cf9b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 11:14:34 -0800 Subject: [PATCH 06/43] 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 | 41 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index e42e81e86c..d67ebd183c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -103,7 +103,7 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) 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++) @@ -120,16 +120,6 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size) if (!constants) { - // Prefix may say "z, z, z, z" but if this is a pair, we force to x. - // TODO: But some ops seem to use const 0 instead? - if (regnum >= n) { - ERROR_LOG_REPORT(CPU, "Invalid VFPU swizzle: %08x: %i / %d at PC = %08x (%s)", data, regnum, n, currentMIPS->pc, MIPSDisasmAt(currentMIPS->pc)); - //for (int i = 0; i < 12; i++) { - // ERROR_LOG(CPU, " vfpuCtrl[%i] = %08x", i, currentMIPS->vfpuCtrl[i]); - //} - regnum = 0; - } - r[i] = origV[regnum]; if (abs) r[i] = fabs(r[i]); @@ -1189,24 +1179,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 335dd394b0d3ae142159826ac0b28d9aa3070343 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 12:18:11 -0800 Subject: [PATCH 07/43] 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 d67ebd183c..f9a55656e8 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -336,25 +336,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; @@ -384,32 +385,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 a38e914960ba49cbdb58393b419147ce9f210920 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 12:18:36 -0800 Subject: [PATCH 08/43] 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 f9a55656e8..552f147ff2 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -484,21 +484,29 @@ namespace MIPSInt PC += 4; } - 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 @@ -519,7 +527,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 08830fd0a265b83d931fc420719235e7d7fc8d7a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 13:49:30 -0800 Subject: [PATCH 09/43] 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 552f147ff2..6768b63418 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -96,14 +96,14 @@ inline float nanclamp(float f, float lower, float upper) } -void ApplyPrefixST(float *r, u32 data, VectorSize size) +void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) { // Possible optimization shortcut: if (data == 0xe4) return; int n = GetNumVectorElements(size); - float origV[4]{}; + float origV[4]{ invalid, invalid, invalid, invalid }; static const float constantArray[8] = {0.f, 1.f, 2.f, 0.5f, 3.f, 1.f/3.f, 0.25f, 1.f/6.f}; for (int i = 0; i < n; i++) @@ -134,14 +134,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) @@ -490,6 +490,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) { @@ -499,10 +500,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) @@ -527,8 +548,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(); @@ -1789,8 +1835,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++) { @@ -1802,9 +1849,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 5ab00685dda91c9784fc73c530ef81cdcd31bcdb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 15:24:39 -0800 Subject: [PATCH 10/43] 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 6768b63418..9a3d5856f5 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -643,19 +643,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_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(0, 0, 0, 0); + 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 ba55b6fe3cebb6a5ca3173be1c4bdb53c3cd3f28 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 15:25:15 -0800 Subject: [PATCH 11/43] interp: Correct prefixes in vsrt ops. --- Core/MIPS/MIPSIntVFPU.cpp | 104 ++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 9a3d5856f5..87a36effd6 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1140,92 +1140,100 @@ 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]); 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]); 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]); 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]); ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; From e0dc8eb1261f7aaaee29552126956b4a461b6c64 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 15:40:58 -0800 Subject: [PATCH 12/43] 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 87a36effd6..52a6e9da1f 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -688,7 +688,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; @@ -711,14 +712,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; @@ -727,12 +728,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 3828a46d53f1a23edf3c675f6e751b64afbb7e4b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 16:44:51 -0800 Subject: [PATCH 13/43] interp: Correct prefixes on vfad and vavg. Including write mask, which didn't work before. --- Core/MIPS/MIPSIntVFPU.cpp | 58 +++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 52a6e9da1f..92b68b6525 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1299,46 +1299,58 @@ 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_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(1, 1, 1, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; } - d = sum; - 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 = VFPU_CONST(1, 1, 1, 1); + if (sz == V_Single) + tprefixAdd |= VFPU_SWIZZLE(0, 0, 0, 0); + else if (sz == V_Pair) + tprefixAdd |= VFPU_SWIZZLE(3, 3, 3, 3); + else if (sz == V_Triple) + tprefixAdd |= VFPU_SWIZZLE(1, 1, 1, 1) | VFPU_ABS(1, 1, 1, 1); + else if (sz == V_Quad) + tprefixAdd |= VFPU_SWIZZLE(2, 2, 2, 2) | VFPU_ABS(1, 1, 1, 1); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; } - d = sum / n; ApplyPrefixD(&d, V_Single); - V(vd) = d; + WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); } From 591064342e9e33795f12ceb3e0c03d3f1d116ae9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Feb 2019 18:19:42 -0800 Subject: [PATCH 14/43] 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 92b68b6525..d3c3137aed 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1381,31 +1381,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 78e50cb7a49c612163521bda053172cba50a3476 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 2 Mar 2019 12:41:58 -0800 Subject: [PATCH 15/43] 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 d3c3137aed..7af1a528ef 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -361,26 +361,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 f2630800d8..e22d80a853 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 94465a29e98ee77f46ba5d89936e7f05ae8d8564 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Mar 2019 10:43:50 -0800 Subject: [PATCH 16/43] 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 7af1a528ef..67ac71a093 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1036,51 +1036,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_CONST(0, 0, 0, 1) | VFPU_SWIZZLE(0, 0, 0, 1); + } else if (sz == V_Triple) { + sprefixRemove = VFPU_SWIZZLE(0, 0, 3, 0); + sprefixAdd = VFPU_CONST(0, 0, 1, 0) | VFPU_SWIZZLE(0, 0, 1, 0); + } else if (sz == V_Pair) { + sprefixRemove = VFPU_SWIZZLE(0, 3, 0, 0); + sprefixAdd = VFPU_CONST(0, 1, 0, 0) | VFPU_SWIZZLE(0, 1, 0, 0); + } else { + sprefixRemove = VFPU_SWIZZLE(3, 0, 0, 0); + sprefixAdd = VFPU_CONST(1, 0, 0, 0) | VFPU_SWIZZLE(1, 0, 0, 0); + } + ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); + float sum = 0.0f; - int n = GetNumVectorElements(sz); - for (int i = 0; i < n; i++) - { - sum += (i == n - 1) ? t[i] : s[i]*t[i]; + for (int i = 0; i < 4; i++) { + sum += s[i] * t[i]; } d = my_isnan(sum) ? fabsf(sum) : sum; - ApplyPrefixD(&d,V_Single); + ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); From a1fe60e28ed9ad0a54c232ceb47d382b22f8a0c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Mar 2019 10:44:59 -0800 Subject: [PATCH 17/43] IR: Use interp for unhandled prefixes. --- Core/MIPS/IR/IRCompVFPU.cpp | 99 +++++++++++++++++++++++----------- Core/MIPS/JitCommon/JitState.h | 31 ++++++----- Core/MIPS/MIPSIntVFPU.cpp | 4 ++ 3 files changed, 92 insertions(+), 42 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 3af7e96490..4c9138fef2 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 { diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 67ac71a093..12beb73b8e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1166,6 +1166,7 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::min(s[0], t[0]); d[1] = std::max(s[1], t[1]); d[2] = std::min(s[2], t[2]); @@ -1191,6 +1192,7 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::min(s[0], t[0]); d[1] = std::min(s[1], t[1]); d[2] = std::max(s[2], t[2]); @@ -1216,6 +1218,7 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::max(s[0], t[0]); d[1] = std::min(s[1], t[1]); d[2] = std::max(s[2], t[2]); @@ -1241,6 +1244,7 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz); // TODO: May mishandle NAN / negative zero / etc. + // TODO: If S swizzle selects a lane that doesn't exist in sz, the result is always 0. d[0] = std::max(s[0], t[0]); d[1] = std::max(s[1], t[1]); d[2] = std::min(s[2], t[2]); From 91891a2069bd22d7e821fb1d45ef6b3bb00c6c88 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 11:48:25 -0700 Subject: [PATCH 18/43] 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 12beb73b8e..c7e7ca2946 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1429,23 +1429,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 884eb128e1..0e6e68d3d8 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 5523dcba77f39d0640f9a07a701c078972348e2b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 12:31:12 -0700 Subject: [PATCH 19/43] 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 e22d80a853..88acda0744 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; @@ -233,24 +233,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); } } } @@ -261,15 +261,16 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) { int row = 0; int side = 0; + int transpose = (reg >> 5) & 1; switch (size) { - case M_2x2: row = (reg>>5)&2; side = 2; break; - case M_3x3: row = (reg>>6)&1; side = 3; break; - case M_4x4: row = (reg>>5)&2; side = 4; break; + case M_1x1: transpose = 0; row = (reg >> 5) & 3; side = 1; break; + case M_2x2: row = (reg >> 5) & 2; side = 2; break; + case M_3x3: row = (reg >> 6) & 1; side = 3; break; + case M_4x4: row = (reg >> 5) & 2; side = 4; break; default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); } - int transpose = (reg>>5)&1; if (currentMIPS->VfpuWriteMask() != 0) { ERROR_LOG_REPORT(CPU, "Write mask used with vfpu matrix instruction."); } @@ -365,6 +366,7 @@ VectorSize GetVecSize(MIPSOpcode op) { VectorSize GetVectorSizeSafe(MatrixSize sz) { switch (sz) { + case M_1x1: return V_Single; case M_2x2: return V_Pair; case M_3x3: return V_Triple; case M_4x4: return V_Quad; @@ -380,7 +382,7 @@ VectorSize GetVectorSize(MatrixSize sz) { MatrixSize GetMatrixSizeSafe(VectorSize sz) { switch (sz) { - case V_Single: return M_Invalid; + case V_Single: return M_1x1; case V_Pair: return M_2x2; case V_Triple: return M_3x3; case V_Quad: return M_4x4; @@ -399,7 +401,7 @@ MatrixSize GetMtxSizeSafe(MIPSOpcode op) { int b = (op >> 15) & 1; a += (b << 1); switch (a) { - case 0: return M_4x4; // This happens in disassembly of junk + case 0: return M_1x1; // This happens in disassembly of junk, but has predictable behavior. case 1: return M_2x2; case 2: return M_3x3; case 3: return M_4x4; @@ -415,6 +417,7 @@ MatrixSize GetMtxSize(MIPSOpcode op) { VectorSize MatrixVectorSizeSafe(MatrixSize sz) { switch (sz) { + case M_1x1: return V_Single; case M_2x2: return V_Pair; case M_3x3: return V_Triple; case M_4x4: return V_Quad; @@ -430,6 +433,7 @@ VectorSize MatrixVectorSize(MatrixSize sz) { int GetMatrixSideSafe(MatrixSize sz) { switch (sz) { + case M_1x1: return 1; case M_2x2: return 2; case M_3x3: return 3; case M_4x4: return 4; @@ -438,7 +442,7 @@ int GetMatrixSideSafe(MatrixSize sz) { } int GetMatrixSide(MatrixSize sz) { - int res = MatrixVectorSizeSafe(sz); + int res = GetMatrixSideSafe(sz); _assert_msg_(JIT, res != 0, "%s: Bad matrix size", __FUNCTION__); return res; } diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 0e6e68d3d8..4333e05401 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -114,6 +114,7 @@ enum VectorSize { }; enum MatrixSize { + M_1x1 = 1, M_2x2 = 2, M_3x3 = 3, M_4x4 = 4, From d708e2ebd980d0d46cd5afa4e535da2cc5c13a81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 13:21:52 -0700 Subject: [PATCH 20/43] interp: Handle prefixes on matrix init ops. --- Core/MIPS/IR/IRCompVFPU.cpp | 4 ++-- Core/MIPS/MIPSIntVFPU.cpp | 35 ++++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 4c9138fef2..734c81b721 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -425,11 +425,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 c7e7ca2946..e1cb4c59eb 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -288,24 +288,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, @@ -315,8 +311,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 @@ -327,7 +322,25 @@ namespace MIPSInt return; } - WriteMatrix(m, sz, vd); + // The S prefix generates constants, but only for the final (possibly transposed) row. + if (currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX] & 0xF0F00) { + float prefixed[16]; + memcpy(prefixed, m, sizeof(prefixed)); + + int off = GetMatrixSide(sz) - 1; + u32 sprefixRemove = VFPU_ANY_SWIZZLE(); + u32 sprefixAdd = VFPU_CONST(1, 1, 1, 1); + switch ((op >> 16) & 0xF) { + case 3: sprefixAdd |= VFPU_SWIZZLE(off == 0, off == 1, off == 2, off == 3); break; + case 6: sprefixAdd |= VFPU_SWIZZLE(0, 0, 0, 0); break; + case 7: sprefixAdd |= VFPU_SWIZZLE(1, 1, 1, 1); break; + } + ApplyPrefixST(&prefixed[off * 4], VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); + WriteMatrix(prefixed, sz, vd); + } else { + // Write mask applies to the final (maybe transposed) row. Sat causes hang. + WriteMatrix(m, sz, vd); + } PC += 4; EatPrefixes(); } From ad7e322c7e7d2db94e31e2d3c05d4a52bebb9f94 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 15:27:53 -0700 Subject: [PATCH 21/43] 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 734c81b721..298fd262c3 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1841,7 +1841,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 e1cb4c59eb..9b4a1d1314 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -659,14 +659,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_CONST(1, 1, 1, 1) | VFPU_SWIZZLE(0, 0, 0, 0); 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 05aaa02171a195defec3b2809e8d0a4f88b7a279 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 16:39:08 -0700 Subject: [PATCH 22/43] 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 298fd262c3..41a0f5127d 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1042,8 +1042,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; @@ -1099,9 +1102,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; @@ -1215,7 +1222,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 9b4a1d1314..f021b531a4 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -414,11 +414,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; @@ -429,29 +426,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; @@ -462,27 +467,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 3bbdc3c7b64a59056b2fe5d93128cbb6259f1e6b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 19:37:30 -0700 Subject: [PATCH 23/43] interp: Handle vtfm/vhtfm prefixes properly. --- Core/MIPS/IR/IRCompVFPU.cpp | 8 +++- Core/MIPS/MIPSIntVFPU.cpp | 75 ++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 41a0f5127d..1de754d03e 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1306,10 +1306,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); @@ -1325,7 +1329,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 f021b531a4..d31d86b17f 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1511,61 +1511,52 @@ 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); + u32 tprefixRemove = VFPU_SWIZZLE(0, n < 2 ? 3 : 0, n < 3 ? 3 : 0, n < 4 ? 3 : 0); + u32 tprefixAdd = VFPU_CONST(0, n < 2, n < 3, n < 4); + if (ins >= n) { + tprefixAdd |= VFPU_SWIZZLE(0, ins == 1 ? 1 : 0, ins == 2 ? 1 : 0, ins == 3 ? 1 : 0); } - else - { - Reporting::ReportMessage("Trying to interpret instruction that can't be interpreted (BADVTFM)"); - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted (BADVTFM)"); - for (int i = 0; i < n; i++) - d[i] = 0.0; + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + + // Really this is the operation all rows probably use (with constant wiring.) + d[ins] = s[ins * 4] * t[0]; + for (int k = 1; k < 4; k++) { + d[ins] += s[ins * 4 + k] * t[k]; } + + // D prefix applies to the last element only. + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << ins; + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (ins + ins); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(d, sz); WriteVector(d, sz, vd); PC += 4; EatPrefixes(); From 26307b5c675eb3f1567da22da998d484b6491943 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 20:25:47 -0700 Subject: [PATCH 24/43] 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 089ace124c..03be08598a 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 871c7e99ea..18ae989b65 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 1de754d03e..ca790a6f59 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1006,13 +1006,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 d31d86b17f..81b16aacc6 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1596,7 +1596,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? @@ -1624,10 +1624,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 10a1e6e9bc..667ca60b7e 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 f9d1432a363631002a590938c7d93acfa15cb57a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 10 Mar 2019 20:28:01 -0700 Subject: [PATCH 25/43] 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 03be08598a..250c2cc0ac 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 18ae989b65..1e0382635f 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 ca790a6f59..2234387ed6 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 81b16aacc6..6df1a8a041 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -173,6 +173,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 667ca60b7e..88ae395642 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 6d20c4517bd7323ffe90320bc7cf831bb87adb8a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Mar 2019 22:44:06 -0700 Subject: [PATCH 26/43] 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 2234387ed6..434c4fb57e 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 6df1a8a041..bc155f1e31 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -777,8 +777,7 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vh2f(MIPSOpcode op) - { + void Int_Vh2f(MIPSOpcode op) { u32 s[4]; float d[4]; int vd = _VD; @@ -795,16 +794,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); @@ -812,32 +809,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 150866e123a38e653e6c1f2038a0abb8867d1914 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 09:28:54 -0700 Subject: [PATCH 27/43] 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 bc155f1e31..4554081ece 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -511,8 +511,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 2ab31d635d642d3753af372d65d253bb8b3d8200 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 10:05:48 -0700 Subject: [PATCH 28/43] 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 4554081ece..4bcca3a7d2 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2068,64 +2068,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 cfcb682dba6ed0c36d4a92ac81e58a5a1ae02c8f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 10:22:14 -0700 Subject: [PATCH 29/43] interp: Correct Vwbn and Vlgb size behavior. --- Core/MIPS/MIPSIntVFPU.cpp | 77 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 4bcca3a7d2..84917eb83c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1994,33 +1994,31 @@ 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? + // TODO: Swizzled outside sz stays zero (no -INF.) ApplySwizzleS(s.f, sz); - if (sz != V_Single) { - ERROR_LOG_REPORT(CPU, "vlgb not implemented for size %d", GetNumVectorElements(sz)); + int exp = (s.u[0] & 0x7F800000) >> 23; + if (exp == 0xFF) { + d.f[0] = s.f[0]; + } else if (exp == 0) { + d.f[0] = -INFINITY; + } else { + d.f[0] = (float)(exp - 127); } - for (int i = 0; i < GetNumVectorElements(sz); ++i) { - int exp = (s.u[i] & 0x7F800000) >> 23; - if (exp == 0xFF) { - d.f[i] = s.f[i]; - } else if (exp == 0) { - d.f[i] = -INFINITY; - } else { - d.f[i] = (float)(exp - 127); - } + + // If sz is greater than V_Single, the rest are copied unchanged. + for (int i = 1; i < GetNumVectorElements(sz); ++i) { + d.u[i] = s.u[i]; } + ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -2030,38 +2028,37 @@ namespace MIPSInt // There has to be a concise way of expressing this in terms of // bit manipulation on the raw floats. void Int_Vwbn(MIPSOpcode op) { + FloatBits d, s; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); - - FloatBits d; - FloatBits s; u8 exp = (u8)((op >> 16) & 0xFF); ReadVector(s.f, sz, vs); - // TODO: Test swizzle, t? + // TODO: Swizzled outside sz stays zero (no exp insertion.) ApplySwizzleS(s.f, sz); - if (sz != V_Single) { - ERROR_LOG_REPORT(CPU, "vwbn not implemented for size %d", GetNumVectorElements(sz)); - } - for (int i = 0; i < GetNumVectorElements(sz); ++i) { - u32 sigbit = s.u[i] & 0x80000000; - u32 prevExp = (s.u[i] & 0x7F800000) >> 23; - u32 mantissa = (s.u[i] & 0x007FFFFF) | 0x00800000; - if (prevExp != 0xFF && prevExp != 0) { - if (exp > prevExp) { - s8 shift = (exp - prevExp) & 0xF; - mantissa = mantissa >> shift; - } else { - s8 shift = (prevExp - exp) & 0xF; - mantissa = mantissa << shift; - } - d.u[i] = sigbit | (mantissa & 0x007FFFFF) | (exp << 23); + u32 sigbit = s.u[0] & 0x80000000; + u32 prevExp = (s.u[0] & 0x7F800000) >> 23; + u32 mantissa = (s.u[0] & 0x007FFFFF) | 0x00800000; + if (prevExp != 0xFF && prevExp != 0) { + if (exp > prevExp) { + s8 shift = (exp - prevExp) & 0xF; + mantissa = mantissa >> shift; } else { - d.u[i] = s.u[i] | (exp << 23); + s8 shift = (prevExp - exp) & 0xF; + mantissa = mantissa << shift; } + d.u[0] = sigbit | (mantissa & 0x007FFFFF) | (exp << 23); + } else { + d.u[0] = s.u[0] | (exp << 23); } + + // If sz is greater than V_Single, the rest are copied unchanged. + for (int i = 1; i < GetNumVectorElements(sz); ++i) { + d.u[i] = s.u[i]; + } + ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -2090,7 +2087,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]; } @@ -2118,7 +2115,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 26d78c7cd1baa781b2efde4740f76e62d664e1e1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Mar 2019 16:17:24 -0700 Subject: [PATCH 30/43] interp: Cleanup prefix/size in vcrsp/vqmul. --- Core/MIPS/IR/IRCompVFPU.cpp | 10 +++---- Core/MIPS/MIPSIntVFPU.cpp | 57 ++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 434c4fb57e..aaeb70be85 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1628,13 +1628,12 @@ namespace MIPSComp { void IRFrontend::Comp_VCrossQuat(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); - // TODO: Does this instruction even look at prefixes at all? - if (js.HasUnknownPrefix()) + if (!js.HasNoPrefix()) DISABLE; - // Vector cross product (n = 3) + // Vector cross product (n = 3, weird prefixes) // d[0 .. 2] = s[0 .. 2] X t[0 .. 2] - // Vector quaternion product (n = 4) + // Vector quaternion product (n = 4, weird prefixes) // d[0 .. 2] = t[0 .. 2] X s[0 .. 2] + s[3] * t[0 .. 2] + t[3] * s[0 .. 2] // d[3] = s[3]*t[3] - s[0 .. 2] dot t[0 .. 3] @@ -1674,13 +1673,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 84917eb83c..e80ce283bb 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1956,38 +1956,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 siwzzle and negate, can be used to have weird constants. + tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + ApplySwizzleS(s, V_Quad); + d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; break; case V_Quad: // vqmul.q d[0] = s[0]*t[3] + s[1]*t[2] - s[2]*t[1] + s[3]*t[0]; d[1] = -s[0]*t[2] + s[1]*t[3] + s[2]*t[0] + s[3]*t[1]; d[2] = s[0]*t[1] - s[1]*t[0] + s[2]*t[3] + s[3]*t[2]; - d[3] = -s[0]*t[0] - s[1]*t[1] - s[2]*t[2] + s[3]*t[3]; + + // T prefix forces siwzzle and negate, can be used to have weird constants. + tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + ApplySwizzleS(s, sz); + d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; break; - default: - Reporting::ReportMessage("CrossQuat instruction with wrong size"); - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + case V_Pair: + // t swizzles invalid so the multiply is always zero. d[0] = 0; - d[1] = 0; + + tprefixAdd = VFPU_SWIZZLE(0, 0, 0, 0) | VFPU_NEGATE(0, 0, 0, 0); + ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); + ApplySwizzleS(s, V_Quad); + // It's possible to populate a value by swizzling s[2]. + d[1] = s[2] * t[2]; break; + + case V_Single: + // t swizzles invalid so the multiply is always zero. + d[0] = 0; + break; + } + + // D prefix applies to the last element only (mask and sat) for pair and larger. + if (sz != V_Single) { + u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); + u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; + ApplyPrefixD(d, sz); + } else { + // Single always seems to write out zero. + currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = 0; } WriteVector(d, sz, vd); PC += 4; From e971c4ded5d36aa3d771f90d182af5e552276b78 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 22:23:52 -0700 Subject: [PATCH 31/43] 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 88acda0744..eba4f88752 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -538,19 +538,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 581dd59a4d663ab9297b166a4dc937f305c5054a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 22:44:45 -0700 Subject: [PATCH 32/43] 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 e80ce283bb..3a9263fb12 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1779,26 +1779,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 e97beac90a6effcd71246509803a975983272235 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 22:59:37 -0700 Subject: [PATCH 33/43] 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 3a9263fb12..563ff5cbf5 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -160,6 +160,23 @@ void ApplyPrefixD(float *v, VectorSize size, bool onlyWriteMask = false) } } +static void RetainInvalidSwizzleST(float *d, VectorSize sz) { + // Somehow it's like a supernan, maybe wires through to zero? + // Doesn't apply to all ops. + int sPrefix = currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX]; + int tPrefix = currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX]; + int n = GetNumVectorElements(sz); + + for (int i = 0; i < n; i++) { + int swizzleS = (sPrefix >> (i + i)) & 3; + int swizzleT = (tPrefix >> (i + i)) & 3; + int constS = (sPrefix >> (12 + i)) & 1; + int constT = (tPrefix >> (12 + i)) & 1; + if ((swizzleS >= n && !constS) || (swizzleT >= n && !constT)) + d[i] = 0.0f; + } +} + void EatPrefixes() { currentMIPS->vfpuCtrl[VFPU_CTRL_SPREFIX] = 0xe4; // passthru @@ -1802,6 +1819,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 8a92383ea7a8954f3231bb60ccbde174c429c564 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 25 Mar 2019 23:11:32 -0700 Subject: [PATCH 34/43] 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 563ff5cbf5..3e99938218 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1827,14 +1827,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); @@ -1845,21 +1843,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); @@ -1870,7 +1868,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 f1a6539c62e675746a912c0c9ef5402ab1e69df0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 26 Mar 2019 21:20:05 -0700 Subject: [PATCH 35/43] 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 3e99938218..2728c62daf 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1735,6 +1735,7 @@ namespace MIPSInt } void Int_Vminmax(MIPSOpcode op) { + FloatBits s, t, d; int vt = _VT; int vs = _VS; int vd = _VD; @@ -1742,9 +1743,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); @@ -1790,6 +1788,7 @@ namespace MIPSInt EatPrefixes(); return; } + RetainInvalidSwizzleST(d.f, sz); ApplyPrefixD(d.f, sz); WriteVector(d.f, sz, vd); PC += 4; @@ -1972,6 +1971,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 d35ea0e81a102905346eb3393fa6821a787dcc1c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 26 Mar 2019 22:06:39 -0700 Subject: [PATCH 36/43] 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 aaeb70be85..1c466f8164 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1940,8 +1940,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 2728c62daf..91e6e548f0 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1045,10 +1045,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]; @@ -1086,7 +1089,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 3842c4f6b5f649ee2127a930d3ba914a460ce07f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 10:23:37 -0700 Subject: [PATCH 37/43] 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 1c466f8164..c44efef1ab 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1894,7 +1894,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 91e6e548f0..0c4cd37c59 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -631,8 +631,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; @@ -653,19 +652,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. @@ -688,7 +689,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 d5a0531086b06ecfc7e721eabf6b45ac070af913 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 10:55:29 -0700 Subject: [PATCH 38/43] 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 c44efef1ab..00b66feb75 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1528,8 +1528,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 0c4cd37c59..7c2e4c76cc 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -861,17 +861,16 @@ namespace MIPSInt EatPrefixes(); } - void Int_Vx2i(MIPSOpcode op) - { - u32 s[4]; - u32 d[4] = {0}; + void Int_Vx2i(MIPSOpcode op) { + u32 s[4], d[4]{}; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); VectorSize oz = sz; ReadVector(reinterpret_cast(s), sz, vs); - // ForbidVPFXS + ApplySwizzleS(reinterpret_cast(s), sz); + // TODO: Similar to colorconv, invalid swizzle seems to reuse last output. switch ((op >> 16) & 3) { case 0: // vuc2i // Quad is the only option. @@ -882,7 +881,7 @@ namespace MIPSInt { u32 value = s[0]; for (int i = 0; i < 4; i++) { - d[i] = (u32)((value & 0xFF) * 0x01010101) >> 1; + d[i] = (u32)((u32)(value & 0xFF) * 0x01010101UL) >> 1; value >>= 8; } oz = V_Quad; @@ -903,8 +902,11 @@ namespace MIPSInt case 2: // vus2i oz = V_Pair; - switch (sz) - { + switch (sz) { + case V_Quad: + case V_Triple: + sz = V_Pair; + // Intentional fallthrough. case V_Pair: oz = V_Quad; // Intentional fallthrough. @@ -924,8 +926,11 @@ namespace MIPSInt case 3: // vs2i oz = V_Pair; - switch (sz) - { + switch (sz) { + case V_Quad: + case V_Triple: + sz = V_Pair; + // Intentional fallthrough. case V_Pair: oz = V_Quad; // Intentional fallthrough. @@ -947,9 +952,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 8a888bad96a9eade941be572667cf2118c575d5d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 11:19:08 -0700 Subject: [PATCH 39/43] 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 7c2e4c76cc..19adbdc32b 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -122,7 +122,7 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) { r[i] = origV[regnum]; if (abs) - r[i] = fabs(r[i]); + ((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF; } else { @@ -130,7 +130,7 @@ void ApplyPrefixST(float *r, u32 data, VectorSize size, float invalid = 0.0f) } if (negate) - r[i] = -r[i]; + ((u32 *)r)[i] = ((u32 *)r)[i] ^ 0x80000000; } } From fa5315de5e8166d874d381bd362957da2e9e5b34 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 11:23:11 -0700 Subject: [PATCH 40/43] 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 00b66feb75..d8a27cf942 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1443,8 +1443,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 19adbdc32b..3ef383793c 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -960,89 +960,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 1ac2625873d8370896d5db19ac5c1f61ee950338 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 11:57:11 -0700 Subject: [PATCH 41/43] 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 d8a27cf942..ac9606913e 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -985,7 +985,15 @@ namespace MIPSComp { if (imm < 128) { ir.Write(IROp::FMovFromGPR, vfpuBase + voffset[imm], rt); } else if ((imm - 128) < VFPU_CTRL_MAX) { - ir.Write(IROp::SetCtrlVFPU, imm - 128, rt); + u32 mask; + if (GetVFPUCtrlMask(imm - 128, &mask)) { + if (mask != 0xFFFFFFFF) { + ir.Write(IROp::AndConst, IRTEMP_0, rt, ir.AddConstant(mask)); + ir.Write(IROp::SetCtrlVFPUReg, imm - 128, IRTEMP_0); + } else { + ir.Write(IROp::SetCtrlVFPU, imm - 128, rt); + } + } if (imm - 128 == VFPU_CTRL_SPREFIX) { js.prefixSFlag = JitState::PREFIX_UNKNOWN; @@ -1031,7 +1039,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 3ef383793c..c9bdef54b6 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -188,7 +188,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; @@ -1632,7 +1632,10 @@ namespace MIPSInt if (imm < 128) { VI(imm) = R(rt); } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc - currentMIPS->vfpuCtrl[imm - 128] = R(rt); + u32 mask; + if (GetVFPUCtrlMask(imm - 128, &mask)) { + currentMIPS->vfpuCtrl[imm - 128] = R(rt) & mask; + } } else { //ERROR _dbg_assert_msg_(CPU,0,"mtv - invalid register"); @@ -1659,7 +1662,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 eba4f88752..6c07bfef5a 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 4333e05401..97d892ed0b 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -212,4 +212,6 @@ inline int TransposeMatrixReg(int matrixReg) { } int GetVectorOverlap(int reg1, VectorSize size1, int reg2, VectorSize size2); +bool GetVFPUCtrlMask(int reg, u32 *mask); + float Float16ToFloat32(unsigned short l); From 914e7e5b1fbecf2d64b0a3777e5d51739a67f7ce Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 12:05:04 -0700 Subject: [PATCH 42/43] 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 ac9606913e..e319cee888 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 c9bdef54b6..b390b4cfe3 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1651,20 +1651,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 f8188c41553588b82b46b1f2694fefeef6043892 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Mar 2019 12:14:15 -0700 Subject: [PATCH 43/43] 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..b51e730208 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); + 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; } }