mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
WriteMMIO_U32 was missing the return after the kernel-mode check, so a user-mode write raised the exception and then went through anyway. The other five MMIO accessors already return here; this one lost it when the GPIO/syscon branches were added. Int_Vrot scanned all four entries of dregs, but GetVectorRegs only fills the first n - so a vrot with vs == 0 (S000) matched a lane that isn't there and took the cosine from it. The IR backend gets this right via IsOverlapSafe, so the two disagreed. The breakpoint checks in MIPSInterpret and RunUntilDowncountZeroWithChecks read instr->flags without checking for null, which MIPSGetInstruction returns for the eight primary opcodes (and many subops) that don't decode. With a memcheck or register breakpoint active, landing on one of those crashed instead of raising ILLEGAL. Also: GetVectorOverlap decoded the second vector with size1 (no callers today), and RegisterFunction left most of its AnalyzedFunction uninitialized, including the size that ends up in knownfuncs.ini. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2420 lines
68 KiB
C++
2420 lines
68 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
// TODO: Test and maybe fix: https://code.google.com/p/jpcsp/source/detail?r=3082#
|
|
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <algorithm>
|
|
|
|
#include "Common/Data/Convert/SmallDataConvert.h"
|
|
#include "Common/Math/math_util.h"
|
|
|
|
#include "Core/Compatibility.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/MemMap.h"
|
|
#include "Core/Reporting.h"
|
|
#include "Core/System.h"
|
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
#include "Core/MIPS/Interpreter.h"
|
|
#include "Core/MIPS/InterpreterVFPU.h"
|
|
#include "Core/MIPS/MIPSTables.h"
|
|
#include "Core/MIPS/MIPSVFPUUtils.h"
|
|
|
|
#define R(i) (mips->r[i])
|
|
#define V(i) (mips->v[voffset[i]])
|
|
#define VI(i) (mips->vi[voffset[i]])
|
|
#define FI(i) (mips->fi[i])
|
|
#define FsI(i) (mips->fs[i])
|
|
#define PC (mips->pc)
|
|
|
|
#define _RS ((op>>21) & 0x1F)
|
|
#define _RT ((op>>16) & 0x1F)
|
|
#define _RD ((op>>11) & 0x1F)
|
|
#define _FS ((op>>11) & 0x1F)
|
|
#define _FT ((op>>16) & 0x1F)
|
|
#define _FD ((op>>6 ) & 0x1F)
|
|
#define _POS ((op>>6 ) & 0x1F)
|
|
#define _SIZE ((op>>11) & 0x1F)
|
|
|
|
#define HI mips->hi
|
|
#define LO mips->lo
|
|
|
|
#ifndef M_LOG2E
|
|
#define M_E 2.71828182845904523536f
|
|
#define M_LOG2E 1.44269504088896340736f
|
|
#define M_LOG10E 0.434294481903251827651f
|
|
#define M_LN2 0.693147180559945309417f
|
|
#define M_LN10 2.30258509299404568402f
|
|
#undef M_PI
|
|
#define M_PI 3.14159265358979323846f
|
|
#ifndef M_PI_2
|
|
#define M_PI_2 1.57079632679489661923f
|
|
#endif
|
|
#define M_PI_4 0.785398163397448309616f
|
|
#define M_1_PI 0.318309886183790671538f
|
|
#define M_2_PI 0.636619772367581343076f
|
|
#define M_2_SQRTPI 1.12837916709551257390f
|
|
#define M_SQRT2 1.41421356237309504880f
|
|
#define M_SQRT1_2 0.707106781186547524401f
|
|
#endif
|
|
|
|
static const bool USE_VFPU_DOT = false;
|
|
static const bool USE_VFPU_SQRT = false;
|
|
|
|
union FloatBits {
|
|
float f[4];
|
|
u32 u[4];
|
|
int i[4];
|
|
};
|
|
|
|
// Preserves NaN in first param, takes sign of equal second param.
|
|
// Technically, std::max may do this but it's undefined.
|
|
inline float nanmax(float f, float cst)
|
|
{
|
|
return f <= cst ? cst : f;
|
|
}
|
|
|
|
// Preserves NaN in first param, takes sign of equal second param.
|
|
inline float nanmin(float f, float cst)
|
|
{
|
|
return f >= cst ? cst : f;
|
|
}
|
|
|
|
// Preserves NaN in first param, takes sign of equal value in others.
|
|
inline float nanclamp(float f, float lower, float upper)
|
|
{
|
|
return nanmin(nanmax(f, lower), upper);
|
|
}
|
|
|
|
static void ApplyPrefixST(MIPSState *mips, float *r, u32 data, VectorSize size, float invalid = 0.0f) {
|
|
// Check for no prefix.
|
|
if (data == 0xe4)
|
|
return;
|
|
|
|
int n = GetNumVectorElements(size);
|
|
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++) {
|
|
origV[i] = r[i];
|
|
}
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
int regnum = (data >> (i*2)) & 3;
|
|
int abs = (data >> (8+i)) & 1;
|
|
int negate = (data >> (16+i)) & 1;
|
|
int constants = (data >> (12+i)) & 1;
|
|
|
|
if (!constants) {
|
|
if (regnum >= n) {
|
|
// We mostly handle this now, but still worth reporting.
|
|
ERROR_LOG_REPORT(Log::CPU, "Invalid VFPU swizzle: %08x: %i / %d at PC = %08x (%s)", data, regnum, n, mips->pc, MIPSDisasmAt(mips->pc).c_str());
|
|
}
|
|
r[i] = origV[regnum];
|
|
if (abs)
|
|
((u32 *)r)[i] = ((u32 *)r)[i] & 0x7FFFFFFF;
|
|
} else {
|
|
r[i] = constantArray[regnum + (abs<<2)];
|
|
}
|
|
|
|
if (negate)
|
|
((u32 *)r)[i] = ((u32 *)r)[i] ^ 0x80000000;
|
|
}
|
|
}
|
|
|
|
inline void ApplySwizzleS(MIPSState *mips, float *v, VectorSize size, float invalid = 0.0f)
|
|
{
|
|
ApplyPrefixST(mips, v, mips->vfpuCtrl[VFPU_CTRL_SPREFIX], size, invalid);
|
|
}
|
|
|
|
inline void ApplySwizzleT(MIPSState *mips, float *v, VectorSize size, float invalid = 0.0f)
|
|
{
|
|
ApplyPrefixST(mips, v, mips->vfpuCtrl[VFPU_CTRL_TPREFIX], size, invalid);
|
|
}
|
|
|
|
void ApplyPrefixD(MIPSState *mips, float *v, VectorSize size, bool onlyWriteMask = false)
|
|
{
|
|
u32 data = mips->vfpuCtrl[VFPU_CTRL_DPREFIX];
|
|
if (!data || onlyWriteMask)
|
|
return;
|
|
int n = GetNumVectorElements(size);
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
int sat = (data >> (i * 2)) & 3;
|
|
if (sat == 1)
|
|
v[i] = vfpu_clamp(v[i], 0.0f, 1.0f);
|
|
else if (sat == 3)
|
|
v[i] = vfpu_clamp(v[i], -1.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
static void RetainInvalidSwizzleST(MIPSState *mips, float *d, VectorSize sz) {
|
|
// Somehow it's like a supernan, maybe wires through to zero?
|
|
// Doesn't apply to all ops.
|
|
int sPrefix = mips->vfpuCtrl[VFPU_CTRL_SPREFIX];
|
|
int tPrefix = mips->vfpuCtrl[VFPU_CTRL_TPREFIX];
|
|
int n = GetNumVectorElements(sz);
|
|
|
|
// TODO: We can probably do some faster check of sPrefix and tPrefix to skip over this loop.
|
|
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(MIPSState *mips)
|
|
{
|
|
mips->vfpuCtrl[VFPU_CTRL_SPREFIX] = 0xe4; // passthru
|
|
mips->vfpuCtrl[VFPU_CTRL_TPREFIX] = 0xe4; // passthru
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = 0;
|
|
}
|
|
|
|
namespace MIPSInt
|
|
{
|
|
void Int_VPFX(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
int data = op & 0x000FFFFF;
|
|
int regnum = (op >> 24) & 3;
|
|
if (regnum == VFPU_CTRL_DPREFIX)
|
|
data &= 0x00000FFF;
|
|
mips->vfpuCtrl[VFPU_CTRL_SPREFIX + regnum] = data;
|
|
PC += 4;
|
|
}
|
|
|
|
void Int_SVQ(MIPSState *mips, MIPSOpcode op) {
|
|
int imm = SignExtend16ToS32(op & 0xFFFC);
|
|
int rs = _RS;
|
|
int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5);
|
|
|
|
u32 addr = R(rs) + imm;
|
|
float *f;
|
|
const float *cf;
|
|
|
|
switch (op >> 26) {
|
|
case 53: //lvl.q/lvr.q
|
|
{
|
|
float d[4];
|
|
ReadVector(mips, d, V_Quad, vt);
|
|
int offset = (addr >> 2) & 3;
|
|
const bool valid = Memory::IsValid4AlignedAddress(addr);
|
|
if ((op & 2) == 0) {
|
|
if (!valid) {
|
|
Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lvl.q");
|
|
}
|
|
// It's an LVL
|
|
for (int i = 0; i < offset + 1; i++) {
|
|
d[3 - i] = valid ? Memory::ReadUnchecked_Float(addr - 4 * i) : 0.0f;
|
|
}
|
|
} else {
|
|
if (!valid) {
|
|
Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lvr.q");
|
|
}
|
|
// It's an LVR
|
|
for (int i = 0; i < (3 - offset) + 1; i++) {
|
|
d[i] = valid ? Memory::ReadUnchecked_Float(addr + 4 * i) : 0.0f;
|
|
}
|
|
}
|
|
WriteVector(mips, d, V_Quad, vt);
|
|
}
|
|
break;
|
|
|
|
case 54: //lv.q
|
|
// A quadword access has to be 16-byte aligned, so a misaligned one is simply
|
|
// rejected - we don't try to carry it out anyway, same as every other path here.
|
|
if ((addr & 0xF) || !Memory::IsValid4AlignedAddress(addr)) {
|
|
Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lv.q");
|
|
const float zero[4]{};
|
|
WriteVector(mips, zero, V_Quad, vt);
|
|
break;
|
|
}
|
|
|
|
#ifndef COMMON_BIG_ENDIAN
|
|
cf = reinterpret_cast<const float *>(Memory::GetPointerUnchecked(addr));
|
|
WriteVector(mips, cf, V_Quad, vt);
|
|
#else
|
|
float lvqd[4];
|
|
|
|
lvqd[0] = Memory::ReadUnchecked_Float(addr);
|
|
lvqd[1] = Memory::ReadUnchecked_Float(addr + 4);
|
|
lvqd[2] = Memory::ReadUnchecked_Float(addr + 8);
|
|
lvqd[3] = Memory::ReadUnchecked_Float(addr + 12);
|
|
|
|
WriteVector(mips, lvqd, V_Quad, vt);
|
|
#endif
|
|
break;
|
|
|
|
case 61: // svl.q/svr.q
|
|
{
|
|
float d[4];
|
|
ReadVector(mips, d, V_Quad, vt);
|
|
int offset = (addr >> 2) & 3;
|
|
if ((op & 2) == 0) {
|
|
if (!Memory::IsValid4AlignedAddress(addr)) {
|
|
Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "svl.q");
|
|
break;
|
|
}
|
|
// It's an SVL
|
|
for (int i = 0; i < offset + 1; i++)
|
|
{
|
|
Memory::WriteUnchecked_Float(d[3 - i], addr - i * 4);
|
|
}
|
|
} else {
|
|
if (!Memory::IsValid4AlignedAddress(addr)) {
|
|
Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "svr.q");
|
|
break;
|
|
}
|
|
// It's an SVR
|
|
for (int i = 0; i < (3 - offset) + 1; i++) {
|
|
Memory::WriteUnchecked_Float(d[i], addr + 4 * i);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 62: //sv.q
|
|
// See lv.q above.
|
|
if ((addr & 0xF) || !Memory::IsValid4AlignedAddress(addr)) {
|
|
Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "sv.q");
|
|
break;
|
|
}
|
|
#ifndef COMMON_BIG_ENDIAN
|
|
f = reinterpret_cast<float *>(Memory::GetPointerWriteUnchecked(addr));
|
|
if (f) {
|
|
ReadVector(mips, f, V_Quad, vt);
|
|
}
|
|
#else
|
|
float svqd[4];
|
|
ReadVector(mips, svqd, V_Quad, vt);
|
|
|
|
Memory::WriteUnchecked_Float(svqd[0], addr);
|
|
Memory::WriteUnchecked_Float(svqd[1], addr + 4);
|
|
Memory::WriteUnchecked_Float(svqd[2], addr + 8);
|
|
Memory::WriteUnchecked_Float(svqd[3], addr + 12);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
_dbg_assert_msg_(false,"Trying to interpret VQ instruction that can't be interpreted");
|
|
break;
|
|
}
|
|
PC += 4;
|
|
}
|
|
|
|
void Int_VMatrixInit(MIPSState *mips, 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] = {
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
};
|
|
static const float one[16] = {
|
|
1,1,1,1,
|
|
1,1,1,1,
|
|
1,1,1,1,
|
|
1,1,1,1,
|
|
};
|
|
int vd = _VD;
|
|
MatrixSize sz = GetMtxSize(op);
|
|
const float *m;
|
|
|
|
switch ((op >> 16) & 0xF) {
|
|
case 3: m=idt; break; //identity // vmidt
|
|
case 6: m=zero; break; // vmzero
|
|
case 7: m=one; break; // vmone
|
|
default:
|
|
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
return;
|
|
}
|
|
|
|
// The S prefix generates constants, but only for the final (possibly transposed) row.
|
|
if (mips->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 = 0;
|
|
switch ((op >> 16) & 0xF) {
|
|
case 3:
|
|
{
|
|
VFPUConst constX = off == 0 ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
VFPUConst constY = off == 1 ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
VFPUConst constZ = off == 2 ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
VFPUConst constW = off == 3 ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW);
|
|
break;
|
|
}
|
|
case 6:
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO);
|
|
break;
|
|
case 7:
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE);
|
|
break;
|
|
default:
|
|
_dbg_assert_msg_( 0, "Unknown matrix init op");
|
|
break;
|
|
}
|
|
ApplyPrefixST(mips, &prefixed[off * 4], VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad);
|
|
WriteMatrix(mips, prefixed, sz, vd);
|
|
} else {
|
|
// Write mask applies to the final (maybe transposed) row. Sat causes hang.
|
|
WriteMatrix(mips, m, sz, vd);
|
|
}
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VVectorInit(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
int vd = _VD;
|
|
VectorSize sz = GetVecSize(op);
|
|
float d[4];
|
|
|
|
VFPUConst constant = VFPUConst::ZERO;
|
|
switch ((op >> 16) & 0xF) {
|
|
case 6: constant = VFPUConst::ZERO; break; //vzero
|
|
case 7: constant = VFPUConst::ONE; break; //vone
|
|
default:
|
|
_dbg_assert_msg_( 0, "Trying to interpret instruction that can't be interpreted");
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
return;
|
|
}
|
|
|
|
// The S prefix generates constants, but negate is still respected.
|
|
u32 sprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 sprefixAdd = VFPU_MAKE_CONSTANTS(constant, constant, constant, constant);
|
|
ApplyPrefixST(mips, d, VFPURewritePrefix(mips,VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz);
|
|
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
|
|
EatPrefixes(mips);
|
|
PC += 4;
|
|
}
|
|
|
|
void Int_Viim(MIPSState *mips, MIPSOpcode op) {
|
|
int vt = _VT;
|
|
s32 imm = SignExtend16ToS32(op & 0xFFFF);
|
|
u16 uimm16 = (op&0xFFFF);
|
|
float f[1];
|
|
int type = (op >> 23) & 7;
|
|
if (type == 6) {
|
|
f[0] = (float)imm; // viim
|
|
} else if (type == 7) {
|
|
f[0] = Float16ToFloat32((u16)uimm16); // vfim
|
|
} else {
|
|
_dbg_assert_msg_( 0, "Invalid Viim opcode type %d", type);
|
|
f[0] = 0;
|
|
}
|
|
|
|
ApplyPrefixD(mips, f, V_Single);
|
|
WriteVector(mips, f, V_Single, vt);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vidt(MIPSState *mips, MIPSOpcode op) {
|
|
int vd = _VD;
|
|
VectorSize sz = GetVecSize(op);
|
|
float f[4];
|
|
|
|
// The S prefix generates constants, but negate is still respected.
|
|
int offmask = sz == V_Quad || sz == V_Triple ? 3 : 1;
|
|
int off = vd & offmask;
|
|
// If it's a pair, the identity starts in a different position.
|
|
VFPUConst constX = off == (0 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
VFPUConst constY = off == (1 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
VFPUConst constZ = off == (2 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
VFPUConst constW = off == (3 & offmask) ? VFPUConst::ONE : VFPUConst::ZERO;
|
|
|
|
u32 sprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 sprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW);
|
|
ApplyPrefixST(mips, f, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), sz);
|
|
|
|
ApplyPrefixD(mips, f, sz);
|
|
WriteVector(mips, f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
// The test really needs some work.
|
|
void Int_Vmmul(MIPSState *mips, MIPSOpcode op) {
|
|
float s[16]{}, t[16]{}, d[16];
|
|
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
MatrixSize sz = GetMtxSize(op);
|
|
int n = GetMatrixSide(sz);
|
|
|
|
ReadMatrix(mips, s, sz, vs);
|
|
ReadMatrix(mips, t, sz, vt);
|
|
|
|
// TODO: Always use the more accurate path in interpreter?
|
|
bool useAccurateDot = USE_VFPU_DOT || PSP_CoreParameter().compat.flags().MoreAccurateVMMUL;
|
|
for (int a = 0; a < n; a++) {
|
|
for (int b = 0; b < n; b++) {
|
|
union { float f; uint32_t u; } sum = { 0.0f };
|
|
if (a == n - 1 && b == n - 1) {
|
|
// S and T prefixes work on the final (or maybe first, in reverse?) dot.
|
|
ApplySwizzleS(mips, &s[b * 4], V_Quad);
|
|
ApplySwizzleT(mips, &t[a * 4], V_Quad);
|
|
}
|
|
|
|
if (useAccurateDot) {
|
|
sum.f = vfpu_dot(&s[b * 4], &t[a * 4]);
|
|
if (my_isnan(sum.f)) {
|
|
sum.u = 0x7f800001;
|
|
} else if ((sum.u & 0x7F800000) == 0) {
|
|
sum.u &= 0xFF800000;
|
|
}
|
|
} else {
|
|
if (a == n - 1 && b == n - 1) {
|
|
for (int c = 0; c < 4; c++) {
|
|
sum.f += s[b * 4 + c] * t[a * 4 + c];
|
|
}
|
|
} else {
|
|
for (int c = 0; c < n; c++) {
|
|
sum.f += s[b * 4 + c] * t[a * 4 + c];
|
|
}
|
|
}
|
|
}
|
|
|
|
d[a * 4 + b] = sum.f;
|
|
}
|
|
}
|
|
|
|
// The D prefix applies ONLY to the final element, but sat does work.
|
|
u32 lastmask = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1);
|
|
u32 lastsat = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2);
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat;
|
|
ApplyPrefixD(mips, &d[4 * (n - 1)], V_Quad, false);
|
|
WriteMatrix(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vmscl(MIPSState *mips, MIPSOpcode op) {
|
|
float s[16]{}, t[4]{}, d[16];
|
|
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
MatrixSize sz = GetMtxSize(op);
|
|
int n = GetMatrixSide(sz);
|
|
|
|
ReadMatrix(mips, s, sz, vs);
|
|
ReadVector(mips, t, V_Single, vt);
|
|
|
|
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(mips, &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.
|
|
int tlane = (vt >> 5) & 3;
|
|
t[tlane] = t[0];
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_SWIZZLE(tlane, tlane, tlane, tlane);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, 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(mips, &d[(n - 1) * 4], V_Quad);
|
|
WriteMatrix(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vmmov(MIPSState *mips, MIPSOpcode op) {
|
|
float s[16]{};
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
MatrixSize sz = GetMtxSize(op);
|
|
ReadMatrix(mips, s, sz, vs);
|
|
// S and D prefixes are applied to the last row.
|
|
int off = GetMatrixSide(sz) - 1;
|
|
ApplySwizzleS(mips, &s[off * 4], V_Quad);
|
|
ApplyPrefixD(mips, &s[off * 4], V_Quad);
|
|
WriteMatrix(mips, s, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vflush(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
VERBOSE_LOG(Log::CPU, "vflush");
|
|
PC += 4;
|
|
// Anything with 0xFC000000 is a nop, but only 0xFFFF0000 retains prefixes.
|
|
if ((op & 0xFFFF0000) != 0xFFFF0000)
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VV2Op(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int optype = (op >> 16) & 0x1f;
|
|
VectorSize sz = GetVecSize(op);
|
|
u32 n = GetNumVectorElements(sz);
|
|
ReadVector(mips, s, sz, vs);
|
|
// Some of these are prefix hacks (affects constants, etc.)
|
|
switch (optype) {
|
|
case 1:
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, 0, VFPU_ABS(1, 1, 1, 1)), sz);
|
|
break;
|
|
case 2:
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, 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(mips, &s[n - 1], V_Single, INFINITY);
|
|
break;
|
|
case 24:
|
|
case 26:
|
|
// Similar to above, but also ignores negate.
|
|
ApplyPrefixST(mips, &s[n - 1], VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, VFPU_NEGATE(1, 0, 0, 0), 0), V_Single, -INFINITY);
|
|
break;
|
|
case 28:
|
|
// Similar to above, but also ignores negate.
|
|
ApplyPrefixST(mips, &s[n - 1], VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, VFPU_NEGATE(1, 0, 0, 0), 0), V_Single, INFINITY);
|
|
break;
|
|
default:
|
|
ApplySwizzleS(mips, s, sz);
|
|
break;
|
|
}
|
|
for (int i = 0; i < (int)n; i++) {
|
|
switch (optype) {
|
|
case 0: d[i] = s[i]; break; //vmov
|
|
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
|
|
case 16: { d[i] = vfpu_rcp(s[i]); } break; //vrcp
|
|
case 17: d[i] = USE_VFPU_SQRT ? vfpu_rsqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq
|
|
|
|
case 18: { d[i] = vfpu_sin(s[i]); } break; //vsin
|
|
case 19: { d[i] = vfpu_cos(s[i]); } break; //vcos
|
|
case 20: { d[i] = vfpu_exp2(s[i]); } break; //vexp2
|
|
case 21: { d[i] = vfpu_log2(s[i]); } break; //vlog2
|
|
case 22: d[i] = USE_VFPU_SQRT ? vfpu_sqrt(s[i]) : fabsf(sqrtf(s[i])); break; //vsqrt
|
|
case 23: { d[i] = vfpu_asin(s[i]); } break; //vasin
|
|
case 24: { d[i] = -vfpu_rcp(s[i]); } break; // vnrcp
|
|
case 26: { d[i] = -vfpu_sin(s[i]); } break; // vnsin
|
|
case 28: { d[i] = vfpu_rexp2(s[i]); } break; // vrexp2
|
|
default:
|
|
_dbg_assert_msg_( false, "Invalid VV2Op op type %d", optype);
|
|
break;
|
|
}
|
|
}
|
|
// vsat1 is a prefix hack, so 0:1 doesn't apply. Others don't process sat at all.
|
|
switch (optype) {
|
|
case 5:
|
|
ApplyPrefixD(mips, 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 = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1);
|
|
u32 lastsat = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2);
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat;
|
|
ApplyPrefixD(mips, d, sz);
|
|
break;
|
|
}
|
|
default:
|
|
ApplyPrefixD(mips, d, sz);
|
|
}
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vocp(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
|
|
// S prefix forces the negate flags.
|
|
u32 sprefixAdd = VFPU_NEGATE(1, 1, 1, 1);
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, 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 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
for (int i = 0; i < GetNumVectorElements(sz); i++) {
|
|
// Always positive NaN. Note that s is always negated from the registers.
|
|
d[i] = my_isnan(s[i]) ? fabsf(s[i]) : t[i] + s[i];
|
|
}
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsocp(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
VectorSize outSize = GetDoubleVectorSizeSafe(sz);
|
|
if (outSize == V_Invalid)
|
|
outSize = V_Quad;
|
|
ReadVector(mips, s, sz, vs);
|
|
|
|
// S prefix forces negate in even/odd and xxyy swizzle.
|
|
// abs works, and applies to final position (not source.)
|
|
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(mips, s, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), outSize);
|
|
|
|
// T prefix forces constants on and regnum to 1, 0, 1, 0.
|
|
// That means negate still works, and abs activates a different constant.
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ZERO, VFPUConst::ONE, VFPUConst::ZERO);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), outSize);
|
|
|
|
// Essentially D prefix saturation is forced.
|
|
d[0] = nanclamp(t[0] + s[0], 0.0f, 1.0f);
|
|
d[1] = nanclamp(t[1] + s[1], 0.0f, 1.0f);
|
|
if (outSize == V_Quad) {
|
|
d[2] = nanclamp(t[2] + s[2], 0.0f, 1.0f);
|
|
d[3] = nanclamp(t[3] + s[3], 0.0f, 1.0f);
|
|
}
|
|
ApplyPrefixD(mips, d, sz, true);
|
|
WriteVector(mips, d, outSize, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsgn(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
|
|
// Not sure who would do this, but using abs/neg allows a compare against 3 or -3.
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
int n = GetNumVectorElements(sz);
|
|
if (n < 4) {
|
|
// Compare with a swizzled value out of bounds always produces 0.
|
|
memcpy(&s[n], &t[n], sizeof(float) * (4 - n));
|
|
}
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
float diff = s[i] - t[i];
|
|
// To handle NaNs correctly, we do this with integer hackery
|
|
u32 val;
|
|
memcpy(&val, &diff, sizeof(u32));
|
|
if (val == 0 || val == 0x80000000)
|
|
d[i] = 0.0f;
|
|
else if ((val >> 31) == 0)
|
|
d[i] = 1.0f;
|
|
else
|
|
d[i] = -1.0f;
|
|
}
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
inline int round_vfpu_n(double param) {
|
|
// return floorf(param);
|
|
return (int)round_ieee_754(param);
|
|
}
|
|
|
|
void Int_Vf2i(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4];
|
|
int d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int imm = (op >> 16) & 0x1f;
|
|
float mult = (float)(1UL << imm);
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
// Negate, abs, and constants apply as you'd expect to the bits.
|
|
ApplySwizzleS(mips, s, sz);
|
|
for (int i = 0; i < GetNumVectorElements(sz); i++) {
|
|
if (my_isnan(s[i])) {
|
|
d[i] = 0x7FFFFFFF;
|
|
continue;
|
|
}
|
|
double sv = s[i] * mult; // (float)0x7fffffff == (float)0x80000000
|
|
// Cap/floor it to 0x7fffffff / 0x80000000
|
|
if (sv > (double)0x7fffffff) {
|
|
d[i] = 0x7fffffff;
|
|
} else if (sv <= (double)(int)0x80000000) {
|
|
d[i] = 0x80000000;
|
|
} else {
|
|
switch ((op >> 21) & 0x1f)
|
|
{
|
|
case 16: d[i] = (int)round_vfpu_n(sv); break; //(floor(sv + 0.5f)); break; //n
|
|
case 17: d[i] = s[i]>=0 ? (int)floor(sv) : (int)ceil(sv); break; //z
|
|
case 18: d[i] = (int)ceil(sv); break; //u
|
|
case 19: d[i] = (int)floor(sv); break; //d
|
|
default: d[i] = 0x7FFFFFFF; break;
|
|
}
|
|
}
|
|
}
|
|
// Does not apply sat, but does apply mask.
|
|
ApplyPrefixD(mips, reinterpret_cast<float *>(d), sz, true);
|
|
WriteVector(mips, reinterpret_cast<float *>(d), sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vi2f(MIPSState *mips, MIPSOpcode op) {
|
|
int s[4];
|
|
float d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int imm = (op >> 16) & 0x1f;
|
|
float mult = 1.0f/(float)(1UL << imm);
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, reinterpret_cast<float *>(s), sz, vs);
|
|
// Negate, abs, and constants apply as you'd expect to the bits.
|
|
ApplySwizzleS(mips, reinterpret_cast<float *>(s), sz);
|
|
for (int i = 0; i < GetNumVectorElements(sz); i++) {
|
|
d[i] = (float)s[i] * mult;
|
|
}
|
|
// Sat and mask apply normally.
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vh2f(MIPSState *mips, MIPSOpcode op) {
|
|
u32 s[4];
|
|
float d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, reinterpret_cast<float *>(s), sz, vs);
|
|
ApplySwizzleS(mips, reinterpret_cast<float *>(s), sz);
|
|
|
|
VectorSize outsize = V_Pair;
|
|
switch (sz) {
|
|
case V_Single:
|
|
outsize = V_Pair;
|
|
d[0] = ExpandHalf(s[0] & 0xFFFF);
|
|
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;
|
|
}
|
|
ApplyPrefixD(mips, d, outsize);
|
|
WriteVector(mips, d, outsize, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vf2h(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{};
|
|
u32 d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
// Swizzle can cause V_Single to properly write both components.
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
// Negate should not actually apply to invalid swizzle.
|
|
RetainInvalidSwizzleST(mips, 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:
|
|
ERROR_LOG_REPORT(Log::CPU, "vf2h with invalid elements");
|
|
break;
|
|
}
|
|
ApplyPrefixD(mips, reinterpret_cast<float *>(d), outsize);
|
|
WriteVector(mips, reinterpret_cast<float *>(d), outsize, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vx2i(MIPSState *mips, MIPSOpcode op) {
|
|
u32 s[4], d[4]{};
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
VectorSize oz = sz;
|
|
ReadVector(mips, reinterpret_cast<float *>(s), sz, vs);
|
|
ApplySwizzleS(mips, reinterpret_cast<float *>(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.
|
|
// This converts 8-bit unsigned to 31-bit signed, swizzling to saturate.
|
|
// Similar to 5-bit to 8-bit color swizzling, but clamping to INT_MAX.
|
|
{
|
|
u32 value = s[0];
|
|
for (int i = 0; i < 4; i++) {
|
|
d[i] = (u32)((u32)(value & 0xFF) * 0x01010101UL) >> 1;
|
|
value >>= 8;
|
|
}
|
|
oz = V_Quad;
|
|
}
|
|
break;
|
|
|
|
case 1: // vc2i
|
|
// Quad is the only option
|
|
// Unlike vuc2i, the source and destination are signed so there is no shift.
|
|
// It lacks the swizzle because of negative values.
|
|
{
|
|
u32 value = s[0];
|
|
d[0] = (value & 0xFF) << 24;
|
|
d[1] = (value & 0xFF00) << 16;
|
|
d[2] = (value & 0xFF0000) << 8;
|
|
d[3] = (value & 0xFF000000);
|
|
oz = V_Quad;
|
|
}
|
|
break;
|
|
|
|
case 2: // vus2i
|
|
// Note: for some reason, this skips swizzle such that 0xFFFF -> 0x7FFF8000 unlike vuc2i.
|
|
oz = V_Pair;
|
|
switch (sz) {
|
|
case V_Quad:
|
|
case V_Triple:
|
|
sz = V_Pair;
|
|
// Intentional fallthrough.
|
|
[[fallthrough]];
|
|
case V_Pair:
|
|
oz = V_Quad;
|
|
// Intentional fallthrough.
|
|
[[fallthrough]];
|
|
case V_Single:
|
|
for (int i = 0; i < GetNumVectorElements(sz); i++) {
|
|
u32 value = s[i];
|
|
d[i * 2] = (value & 0xFFFF) << 15;
|
|
d[i * 2 + 1] = (value & 0xFFFF0000) >> 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG_REPORT(Log::CPU, "vus2i with more than 2 elements");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 3: // vs2i
|
|
oz = V_Pair;
|
|
switch (sz) {
|
|
case V_Quad:
|
|
case V_Triple:
|
|
sz = V_Pair;
|
|
// Intentional fallthrough.
|
|
[[fallthrough]];
|
|
case V_Pair:
|
|
oz = V_Quad;
|
|
// Intentional fallthrough.
|
|
[[fallthrough]];
|
|
case V_Single:
|
|
for (int i = 0; i < GetNumVectorElements(sz); i++) {
|
|
u32 value = s[i];
|
|
d[i * 2] = (value & 0xFFFF) << 16;
|
|
d[i * 2 + 1] = value & 0xFFFF0000;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG_REPORT(Log::CPU, "vs2i with more than 2 elements");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
_dbg_assert_msg_( false, "Trying to interpret instruction that can't be interpreted");
|
|
break;
|
|
}
|
|
|
|
// Saturation does in fact apply.
|
|
ApplyPrefixD(mips, reinterpret_cast<float *>(d),oz);
|
|
WriteVector(mips, reinterpret_cast<float *>(d), oz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vi2x(MIPSState *mips, MIPSOpcode op) {
|
|
int s[4]{};
|
|
u32 d[2]{};
|
|
const int vd = _VD;
|
|
const int vs = _VS;
|
|
const VectorSize sz = GetVecSize(op);
|
|
VectorSize oz;
|
|
ReadVector(mips, reinterpret_cast<float *>(s), sz, vs);
|
|
// Negate, const, etc. apply as expected.
|
|
ApplySwizzleS(mips, reinterpret_cast<float *>(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;
|
|
break;
|
|
|
|
case 1: //vi2c
|
|
for (int i = 0; i < 4; i++) {
|
|
u32 v = s[i];
|
|
d[0] |= (v >> 24) << (i * 8);
|
|
}
|
|
oz = V_Single;
|
|
break;
|
|
|
|
case 2: //vi2us
|
|
{
|
|
int elems = (GetNumVectorElements(sz) + 1) / 2;
|
|
for (int i = 0; i < elems; 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_( false, "Trying to interpret instruction that can't be interpreted");
|
|
oz = V_Single;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 3: //vi2s
|
|
{
|
|
int elems = (GetNumVectorElements(sz) + 1) / 2;
|
|
for (int i = 0; i < elems; 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_(0, "Trying to interpret instruction that can't be interpreted");
|
|
oz = V_Single;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
_dbg_assert_msg_( 0, "Trying to interpret instruction that can't be interpreted");
|
|
oz = V_Single;
|
|
break;
|
|
}
|
|
// D prefix applies as expected.
|
|
ApplyPrefixD(mips, reinterpret_cast<float *>(d), oz);
|
|
WriteVector(mips, reinterpret_cast<float *>(d), oz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_ColorConv(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
u32 s[4];
|
|
VectorSize isz = GetVecSize(op);
|
|
VectorSize sz = V_Quad;
|
|
ReadVector(mips, reinterpret_cast<float *>(s), sz, vs);
|
|
ApplySwizzleS(mips, reinterpret_cast<float *>(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];
|
|
u16 col = 0;
|
|
switch ((op >> 16) & 3)
|
|
{
|
|
case 1: // 4444
|
|
{
|
|
int a = ((in >> 24) & 0xFF) >> 4;
|
|
int b = ((in >> 16) & 0xFF) >> 4;
|
|
int g = ((in >> 8) & 0xFF) >> 4;
|
|
int r = ((in) & 0xFF) >> 4;
|
|
col = (a << 12) | (b << 8) | (g << 4) | (r);
|
|
break;
|
|
}
|
|
case 2: // 5551
|
|
{
|
|
int a = ((in >> 24) & 0xFF) >> 7;
|
|
int b = ((in >> 16) & 0xFF) >> 3;
|
|
int g = ((in >> 8) & 0xFF) >> 3;
|
|
int r = ((in) & 0xFF) >> 3;
|
|
col = (a << 15) | (b << 10) | (g << 5) | (r);
|
|
break;
|
|
}
|
|
case 3: // 565
|
|
{
|
|
int b = ((in >> 16) & 0xFF) >> 3;
|
|
int g = ((in >> 8) & 0xFF) >> 2;
|
|
int r = ((in) & 0xFF) >> 3;
|
|
col = (b << 11) | (g << 5) | (r);
|
|
break;
|
|
}
|
|
}
|
|
colors[i] = col;
|
|
}
|
|
u32 ov[2] = {(u32)colors[0] | (colors[1] << 16), (u32)colors[2] | (colors[3] << 16)};
|
|
ApplyPrefixD(mips, reinterpret_cast<float *>(ov), V_Pair);
|
|
WriteVector(mips, (const float *)ov, isz == V_Single ? V_Single : V_Pair, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VDot(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{};
|
|
union { float f; uint32_t u; } d;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
ReadVector(mips, t, sz, vt);
|
|
ApplySwizzleT(mips, t, V_Quad);
|
|
|
|
if (USE_VFPU_DOT) {
|
|
d.f = vfpu_dot(s, t);
|
|
if (my_isnan(d.f)) {
|
|
d.u = 0x7f800001;
|
|
} else if ((d.u & 0x7F800000) == 0) {
|
|
d.u &= 0xFF800000;
|
|
}
|
|
} else {
|
|
d.f = 0.0f;
|
|
for (int i = 0; i < 4; i++) {
|
|
d.f += s[i] * t[i];
|
|
}
|
|
}
|
|
|
|
ApplyPrefixD(mips, &d.f, V_Single);
|
|
WriteVector(mips, &d.f, V_Single, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VHdp(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{};
|
|
float d;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ReadVector(mips, t, sz, vt);
|
|
ApplySwizzleT(mips, t, V_Quad);
|
|
|
|
// S prefix forces constant 1 for the last element (w for quad.)
|
|
// Otherwise it is the same as vdot.
|
|
u32 sprefixRemove;
|
|
u32 sprefixAdd;
|
|
if (sz == V_Quad) {
|
|
sprefixRemove = VFPU_SWIZZLE(0, 0, 0, 3);
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::NONE, VFPUConst::NONE, VFPUConst::ONE);
|
|
} else if (sz == V_Triple) {
|
|
sprefixRemove = VFPU_SWIZZLE(0, 0, 3, 0);
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::NONE, VFPUConst::ONE, VFPUConst::NONE);
|
|
} else if (sz == V_Pair) {
|
|
sprefixRemove = VFPU_SWIZZLE(0, 3, 0, 0);
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::NONE, VFPUConst::ONE, VFPUConst::NONE, VFPUConst::NONE);
|
|
} else {
|
|
sprefixRemove = VFPU_SWIZZLE(3, 0, 0, 0);
|
|
sprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::NONE, VFPUConst::NONE, VFPUConst::NONE);
|
|
}
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad);
|
|
|
|
float sum = 0.0f;
|
|
if (USE_VFPU_DOT) {
|
|
sum = vfpu_dot(s, t);
|
|
} else {
|
|
for (int i = 0; i < 4; i++) {
|
|
sum += s[i] * t[i];
|
|
}
|
|
}
|
|
d = my_isnan(sum) ? fabsf(sum) : sum;
|
|
ApplyPrefixD(mips, &d, V_Single);
|
|
WriteVector(mips, &d, V_Single, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vbfy(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{}, d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ReadVector(mips, t, sz, vs);
|
|
|
|
if (op & 0x10000) {
|
|
// vbfy2
|
|
// S prefix forces the negate flags (so z and w are negative.)
|
|
u32 sprefixAdd = VFPU_NEGATE(0, 0, 1, 1);
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, 0, sprefixAdd), sz);
|
|
|
|
// T prefix forces swizzle (zwxy.)
|
|
// That means negate still works, but constants are a bit weird.
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_SWIZZLE(2, 3, 0, 1);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
// Other sizes don't seem completely predictable.
|
|
if (sz != V_Quad) {
|
|
ERROR_LOG_REPORT_ONCE(vbfy2, Log::CPU, "vfby2 with incorrect size");
|
|
}
|
|
} else {
|
|
// vbfy1
|
|
// S prefix forces the negate flags (so y and w are negative.)
|
|
u32 sprefixAdd = VFPU_NEGATE(0, 1, 0, 1);
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, 0, sprefixAdd), sz);
|
|
|
|
// T prefix forces swizzle (yxwz.)
|
|
// That means negate still works, but constants are a bit weird.
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
if (sz != V_Quad && sz != V_Pair) {
|
|
ERROR_LOG_REPORT_ONCE(vbfy2, Log::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(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsrt1(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, 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(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
// TODO: May mishandle NAN / negative zero / etc.
|
|
d[0] = std::min(s[0], t[0]);
|
|
d[1] = std::max(s[1], t[1]);
|
|
d[2] = std::min(s[2], t[2]);
|
|
d[3] = std::max(s[3], t[3]);
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsrt2(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, 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(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
// TODO: May mishandle NAN / negative zero / etc.
|
|
d[0] = std::min(s[0], t[0]);
|
|
d[1] = std::min(s[1], t[1]);
|
|
d[2] = std::max(s[2], t[2]);
|
|
d[3] = std::max(s[3], t[3]);
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsrt3(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, 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(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
// TODO: May mishandle NAN / negative zero / etc.
|
|
d[0] = std::max(s[0], t[0]);
|
|
d[1] = std::min(s[1], t[1]);
|
|
d[2] = std::max(s[2], t[2]);
|
|
d[3] = std::min(s[3], t[3]);
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsrt4(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, 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(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
|
|
|
|
// TODO: May mishandle NAN / negative zero / etc.
|
|
d[0] = std::max(s[0], t[0]);
|
|
d[1] = std::max(s[1], t[1]);
|
|
d[2] = std::min(s[2], t[2]);
|
|
d[3] = std::min(s[3], t[3]);
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vcrs(MIPSState *mips, MIPSOpcode op) {
|
|
//half a cross product
|
|
float s[4]{}, t[4]{}, d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ReadVector(mips, t, sz, vt);
|
|
|
|
// 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(mips, s, VFPURewritePrefix(mips, 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(mips, t, VFPURewritePrefix(mips, 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(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vdet(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{}, d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
// This is normally V_Pair. Unfilled s/t values are treated as zero.
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
ReadVector(mips, t, sz, vt);
|
|
|
|
// 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(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
|
|
if (USE_VFPU_DOT) {
|
|
s[1] = -s[1];
|
|
d[0] = vfpu_dot(s, t);
|
|
} else {
|
|
d[0] = s[0] * t[0] - s[1] * t[1];
|
|
d[0] += s[2] * t[2] + s[3] * t[3];
|
|
}
|
|
|
|
ApplyPrefixD(mips, d, V_Single);
|
|
WriteVector(mips, d, V_Single, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vfad(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{};
|
|
float d;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
|
|
// T prefix generates constants, but abs can change the constant.
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
|
|
if (USE_VFPU_DOT) {
|
|
d = vfpu_dot(s, t);
|
|
} else {
|
|
d = 0.0f;
|
|
for (int i = 0; i < 4; i++) {
|
|
d += s[i] * t[i];
|
|
}
|
|
}
|
|
ApplyPrefixD(mips, &d, V_Single);
|
|
WriteVector(mips, &d, V_Single, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vavg(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{};
|
|
float d;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
|
|
// T prefix generates constants, but supports negate.
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_ABS(1, 1, 1, 1);
|
|
u32 tprefixAdd;
|
|
if (sz == V_Single)
|
|
tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO, VFPUConst::ZERO);
|
|
else if (sz == V_Pair)
|
|
tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::HALF, VFPUConst::HALF, VFPUConst::HALF, VFPUConst::HALF);
|
|
else if (sz == V_Triple)
|
|
tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::THIRD, VFPUConst::THIRD, VFPUConst::THIRD, VFPUConst::THIRD);
|
|
else if (sz == V_Quad)
|
|
tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::FOURTH, VFPUConst::FOURTH, VFPUConst::FOURTH, VFPUConst::FOURTH);
|
|
else
|
|
tprefixAdd = 0;
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
|
|
if (USE_VFPU_DOT) {
|
|
d = vfpu_dot(s, t);
|
|
} else {
|
|
d = 0.0f;
|
|
for (int i = 0; i < 4; i++) {
|
|
d += s[i] * t[i];
|
|
}
|
|
}
|
|
ApplyPrefixD(mips, &d, V_Single);
|
|
WriteVector(mips, &d, V_Single, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VScl(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4], d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
|
|
// T prefix forces swizzle (zzzz for some reason, so we force V_Quad.)
|
|
// That means negate still works, but constants are a bit weird.
|
|
int tlane = (vt >> 5) & 3;
|
|
t[tlane] = V(vt);
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE();
|
|
u32 tprefixAdd = VFPU_SWIZZLE(tlane, tlane, tlane, tlane);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
|
|
int n = GetNumVectorElements(sz);
|
|
for (int i = 0; i < n; i++) {
|
|
d[i] = s[i] * t[i];
|
|
}
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vrnds(MIPSState *mips, MIPSOpcode op) {
|
|
int vd = _VD;
|
|
int seed = VI(vd);
|
|
// Swizzles apply a constant value, constants/abs/neg work to vary the seed.
|
|
ApplySwizzleS(mips, reinterpret_cast<float *>(&seed), V_Single);
|
|
vrnd_init(uint32_t(seed), mips->vfpuCtrl + VFPU_CTRL_RCX0);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VrndX(MIPSState *mips, MIPSOpcode op) {
|
|
FloatBits d;
|
|
int vd = _VD;
|
|
VectorSize sz = GetVecSize(op);
|
|
u32 n = GetNumVectorElements(sz);
|
|
// Values are written in backwards order.
|
|
for (int i = n - 1; i >= 0; i--) {
|
|
switch ((op >> 16) & 0x1f) {
|
|
case 1: d.u[i] = vrnd_generate(mips->vfpuCtrl + VFPU_CTRL_RCX0); break; // vrndi
|
|
case 2: d.u[i] = 0x3F800000 | (vrnd_generate(mips->vfpuCtrl + VFPU_CTRL_RCX0) & 0x007FFFFF); break; // vrndf1 (>= 1, < 2)
|
|
case 3: d.u[i] = 0x40000000 | (vrnd_generate(mips->vfpuCtrl + VFPU_CTRL_RCX0) & 0x007FFFFF); break; // vrndf2 (>= 2, < 4)
|
|
default: _dbg_assert_msg_(false,"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 = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1);
|
|
u32 lastsat = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2);
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat;
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
// Generates one line of a rotation matrix around one of the three axes
|
|
void Int_Vrot(MIPSState *mips, MIPSOpcode op) {
|
|
float d[4]{};
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int imm = (op >> 16) & 0x1f;
|
|
VectorSize sz = GetVecSize(op);
|
|
bool negSin = (imm & 0x10) != 0;
|
|
int sineLane = (imm >> 2) & 3;
|
|
int cosineLane = imm & 3;
|
|
|
|
float sine, cosine;
|
|
if (mips->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(mips, s, V_Single, vs);
|
|
u32 sprefixRemove = VFPU_NEGATE(1, 0, 0, 0);
|
|
// We apply negSin later, not here. This handles zero a bit better.
|
|
u32 sprefixAdd = VFPU_NEGATE(0, 0, 0, 0);
|
|
ApplyPrefixST(mips, s, VFPURewritePrefix(mips, VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Single);
|
|
|
|
// Cosine ignores all prefixes, so take the original.
|
|
cosine = vfpu_cos(V(vs));
|
|
sine = vfpu_sin(s[0]);
|
|
|
|
if (negSin)
|
|
sine = -sine;
|
|
RetainInvalidSwizzleST(mips, &sine, V_Single);
|
|
}
|
|
|
|
if (sineLane == cosineLane) {
|
|
for (int i = 0; i < 4; i++)
|
|
d[i] = sine;
|
|
} else {
|
|
d[sineLane] = sine;
|
|
}
|
|
|
|
if (((vd >> 2) & 7) == ((vs >> 2) & 7)) {
|
|
u8 dregs[4]{};
|
|
GetVectorRegs(dregs, sz, vd);
|
|
// Calculate cosine based on sine/zero result.
|
|
// Only the first n entries of dregs are valid, the rest are still zero - which would
|
|
// falsely match vs == 0 (S000).
|
|
int n = GetNumVectorElements(sz);
|
|
bool written = false;
|
|
for (int i = 0; i < n; i++) {
|
|
if (vs == dregs[i]) {
|
|
d[cosineLane] = vfpu_cos(d[i]);
|
|
written = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!written)
|
|
d[cosineLane] = cosine;
|
|
} else {
|
|
d[cosineLane] = cosine;
|
|
}
|
|
|
|
// D prefix works, just not for the cosine lane.
|
|
// The saturation field is two bits per element (see ApplyPrefixD), the mask field one.
|
|
uint32_t dprefixRemove = (3 << (cosineLane * 2)) | (1 << (8 + cosineLane));
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] &= 0xFFFFF ^ dprefixRemove;
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vtfm(MIPSState *mips, MIPSOpcode op) {
|
|
float s[16]{}, t[4]{};
|
|
FloatBits d;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
int ins = (op >> 23) & 3;
|
|
|
|
VectorSize sz = (VectorSize)(ins + 1);
|
|
MatrixSize msz = (MatrixSize)(ins + 1);
|
|
int n = GetNumVectorElements(GetVecSize(op));
|
|
|
|
int tn = std::min(n, ins + 1);
|
|
ReadMatrix(mips, s, msz, vs);
|
|
ReadVector(mips, t, sz, vt);
|
|
|
|
if (USE_VFPU_DOT) {
|
|
float t2[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
if (i < tn) {
|
|
t2[i] = t[i];
|
|
} else if (i == ins) {
|
|
t2[i] = 1.0f;
|
|
} else {
|
|
t2[i] = 0.0f;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < ins; i++) {
|
|
d.f[i] = vfpu_dot(&s[i * 4], t2);
|
|
|
|
if (my_isnan(d.f[i])) {
|
|
d.u[i] = 0x7f800001;
|
|
} else if ((d.u[i] & 0x7F800000) == 0) {
|
|
d.u[i] &= 0xFF800000;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 0; i < ins; i++) {
|
|
d.f[i] = s[i * 4] * t[0];
|
|
for (int k = 1; k < tn; k++) {
|
|
d.f[i] += s[i * 4 + k] * t[k];
|
|
}
|
|
if (ins >= n) {
|
|
d.f[i] += s[i * 4 + ins];
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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(mips, &s[ins * 4], V_Quad);
|
|
VFPUConst constX = VFPUConst::NONE;
|
|
VFPUConst constY = n < 2 ? VFPUConst::ZERO : VFPUConst::NONE;
|
|
VFPUConst constZ = n < 3 ? VFPUConst::ZERO : VFPUConst::NONE;
|
|
VFPUConst constW = n < 4 ? VFPUConst::ZERO : VFPUConst::NONE;
|
|
if (ins >= n) {
|
|
if (ins == 1) {
|
|
constY = VFPUConst::ONE;
|
|
} else if (ins == 2) {
|
|
constZ = VFPUConst::ONE;
|
|
} else if (ins == 3) {
|
|
constW = VFPUConst::ONE;
|
|
}
|
|
}
|
|
u32 tprefixRemove = VFPU_SWIZZLE(0, n < 2 ? 3 : 0, n < 3 ? 3 : 0, n < 4 ? 3 : 0);
|
|
u32 tprefixAdd = VFPU_MAKE_CONSTANTS(constX, constY, constZ, constW);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
|
|
// Really this is the operation all rows probably use (with constant wiring.)
|
|
if (USE_VFPU_DOT) {
|
|
d.f[ins] = vfpu_dot(&s[ins * 4], t);
|
|
|
|
if (my_isnan(d.f[ins])) {
|
|
d.u[ins] = 0x7f800001;
|
|
} else if ((d.u[ins] & 0x7F800000) == 0) {
|
|
d.u[ins] &= 0xFF800000;
|
|
}
|
|
} else {
|
|
d.f[ins] = s[ins * 4] * t[0];
|
|
for (int k = 1; k < 4; k++) {
|
|
d.f[ins] += s[ins * 4 + k] * t[k];
|
|
}
|
|
}
|
|
|
|
// D prefix applies to the last element only.
|
|
u32 lastmask = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << ins;
|
|
u32 lastsat = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (ins + ins);
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat;
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_SV(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
s32 imm = SignExtend16ToS32(op & 0xFFFC);
|
|
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
|
|
int rs = _RS;
|
|
u32 addr = R(rs) + imm;
|
|
|
|
switch (op >> 26)
|
|
{
|
|
case 50: //lv.s
|
|
if (!Memory::IsValid4AlignedAddress(addr)) {
|
|
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lv.s");
|
|
VI(vt) = 0;
|
|
break;
|
|
}
|
|
VI(vt) = Memory::ReadUnchecked_U32(addr);
|
|
break;
|
|
case 58: //sv.s
|
|
if (!Memory::IsValid4AlignedAddress(addr)) {
|
|
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sv.s");
|
|
break;
|
|
}
|
|
Memory::WriteUnchecked_U32(VI(vt), addr);
|
|
break;
|
|
default:
|
|
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
|
|
break;
|
|
}
|
|
PC += 4;
|
|
}
|
|
|
|
|
|
void Int_Mftv(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
int imm = op & 0xFF;
|
|
int 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.
|
|
if (rt != 0) {
|
|
if (imm < 128) {
|
|
R(rt) = VI(imm);
|
|
} else if (imm < 128 + VFPU_CTRL_MAX) { //mfvc
|
|
R(rt) = mips->vfpuCtrl[imm - 128];
|
|
} else {
|
|
//ERROR - maybe need to make this value too an "interlock" value?
|
|
_dbg_assert_msg_(false,"mfv - invalid register");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 7: //mtv
|
|
if (imm < 128) {
|
|
VI(imm) = R(rt);
|
|
} else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc
|
|
u32 mask;
|
|
if (GetVFPUCtrlMask(imm - 128, &mask)) {
|
|
mips->vfpuCtrl[imm - 128] = R(rt) & mask;
|
|
}
|
|
} else {
|
|
//ERROR
|
|
_dbg_assert_msg_(false,"mtv - invalid register");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
|
|
break;
|
|
}
|
|
PC += 4;
|
|
}
|
|
|
|
void Int_Vmfvc(MIPSState *mips, MIPSOpcode op) {
|
|
int vd = _VD;
|
|
int imm = (op >> 8) & 0x7F;
|
|
if (imm < VFPU_CTRL_MAX) {
|
|
VI(vd) = mips->vfpuCtrl[imm];
|
|
} else {
|
|
VI(vd) = 0;
|
|
}
|
|
PC += 4;
|
|
}
|
|
|
|
void Int_Vmtvc(MIPSState *mips, MIPSOpcode op) {
|
|
int vs = _VS;
|
|
int imm = op & 0x7F;
|
|
if (imm < VFPU_CTRL_MAX) {
|
|
u32 mask;
|
|
if (GetVFPUCtrlMask(imm, &mask)) {
|
|
mips->vfpuCtrl[imm] = VI(vs) & mask;
|
|
}
|
|
}
|
|
PC += 4;
|
|
}
|
|
|
|
void Int_Vcst(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
int conNum = (op >> 16) & 0x1f;
|
|
int vd = _VD;
|
|
|
|
VectorSize sz = GetVecSize(op);
|
|
float c = cst_constants[conNum];
|
|
float temp[4] = {c,c,c,c};
|
|
ApplyPrefixD(mips, temp, sz);
|
|
WriteVector(mips, temp, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vcmp(MIPSState *mips, MIPSOpcode op)
|
|
{
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
int cond = op & 0xf;
|
|
VectorSize sz = GetVecSize(op);
|
|
int n = GetNumVectorElements(sz);
|
|
float s[4];
|
|
float t[4];
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, t, sz, vt);
|
|
ApplySwizzleT(mips, t, sz);
|
|
int cc = 0;
|
|
int or_val = 0;
|
|
int and_val = 1;
|
|
int affected_bits = (1 << 4) | (1 << 5); // 4 and 5
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
int c;
|
|
// These set c to 0 or 1, nothing else.
|
|
switch (cond)
|
|
{
|
|
case VC_FL: c = 0; break;
|
|
case VC_EQ: c = s[i] == t[i]; break;
|
|
case VC_LT: c = s[i] < t[i]; break;
|
|
case VC_LE: c = s[i] <= t[i]; break;
|
|
|
|
case VC_TR: c = 1; break;
|
|
case VC_NE: c = s[i] != t[i]; break;
|
|
case VC_GE: c = s[i] >= t[i]; break;
|
|
case VC_GT: c = s[i] > t[i]; break;
|
|
|
|
case VC_EZ: c = s[i] == 0.0f || s[i] == -0.0f; break;
|
|
case VC_EN: c = my_isnan(s[i]); break;
|
|
case VC_EI: c = my_isinf(s[i]); break;
|
|
case VC_ES: c = my_isnanorinf(s[i]); break; // Tekken Dark Resurrection
|
|
|
|
case VC_NZ: c = s[i] != 0; break;
|
|
case VC_NN: c = !my_isnan(s[i]); break;
|
|
case VC_NI: c = !my_isinf(s[i]); break;
|
|
case VC_NS: c = !(my_isnanorinf(s[i])); break; // How about t[i] ?
|
|
|
|
default:
|
|
_dbg_assert_msg_(false,"Unsupported vcmp condition code %d", cond);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
return;
|
|
}
|
|
cc |= (c<<i);
|
|
or_val |= c;
|
|
and_val &= c;
|
|
affected_bits |= 1 << i;
|
|
}
|
|
// Use masking to only change the affected bits
|
|
mips->vfpuCtrl[VFPU_CTRL_CC] =
|
|
(mips->vfpuCtrl[VFPU_CTRL_CC] & ~affected_bits) |
|
|
((cc | (or_val << 4) | (and_val << 5)) & affected_bits);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vminmax(MIPSState *mips, MIPSOpcode op) {
|
|
FloatBits s, t, d;
|
|
int vt = _VT;
|
|
int vs = _VS;
|
|
int vd = _VD;
|
|
int cond = op&15;
|
|
VectorSize sz = GetVecSize(op);
|
|
int numElements = GetNumVectorElements(sz);
|
|
|
|
ReadVector(mips, s.f, sz, vs);
|
|
ApplySwizzleS(mips, s.f, sz);
|
|
ReadVector(mips, t.f, sz, vt);
|
|
ApplySwizzleT(mips, t.f, sz);
|
|
|
|
// If both are zero, take t's sign.
|
|
// Otherwise: -NAN < -INF < real < INF < NAN (higher mantissa is farther from 0.)
|
|
|
|
switch ((op >> 23) & 3) {
|
|
case 2: // vmin
|
|
for (int i = 0; i < numElements; i++) {
|
|
if (my_isnanorinf(s.f[i]) || my_isnanorinf(t.f[i])) {
|
|
// If both are negative, we flip the comparison (not two's compliment.)
|
|
if (s.i[i] < 0 && t.i[i] < 0) {
|
|
// If at least one side is NAN, we take the highest mantissa bits.
|
|
d.i[i] = std::max(t.i[i], s.i[i]);
|
|
} else {
|
|
// Otherwise, we take the lowest value (negative or lowest mantissa.)
|
|
d.i[i] = std::min(t.i[i], s.i[i]);
|
|
}
|
|
} else {
|
|
d.f[i] = std::min(t.f[i], s.f[i]);
|
|
}
|
|
}
|
|
break;
|
|
case 3: // vmax
|
|
for (int i = 0; i < numElements; i++) {
|
|
// This is the same logic as vmin, just reversed.
|
|
if (my_isnanorinf(s.f[i]) || my_isnanorinf(t.f[i])) {
|
|
if (s.i[i] < 0 && t.i[i] < 0) {
|
|
d.i[i] = std::min(t.i[i], s.i[i]);
|
|
} else {
|
|
d.i[i] = std::max(t.i[i], s.i[i]);
|
|
}
|
|
} else {
|
|
d.f[i] = std::max(t.f[i], s.f[i]);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
_dbg_assert_msg_(false,"unknown min/max op %d", cond);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
return;
|
|
}
|
|
RetainInvalidSwizzleST(mips, d.f, sz);
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vscmp(MIPSState *mips, MIPSOpcode op) {
|
|
FloatBits s, t, d;
|
|
int vt = _VT;
|
|
int vs = _VS;
|
|
int vd = _VD;
|
|
VectorSize sz = GetVecSize(op);
|
|
ReadVector(mips, s.f, sz, vs);
|
|
ApplySwizzleS(mips, s.f, sz);
|
|
ReadVector(mips, t.f, sz, vt);
|
|
ApplySwizzleT(mips, t.f, sz);
|
|
int n = GetNumVectorElements(sz);
|
|
for (int i = 0; i < n ; i++) {
|
|
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));
|
|
}
|
|
}
|
|
RetainInvalidSwizzleST(mips, d.f, sz);
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsge(MIPSState *mips, 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);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, t, sz, vt);
|
|
ApplySwizzleT(mips, t, sz);
|
|
for (int i = 0; i < numElements; i++) {
|
|
if ( my_isnan(s[i]) || my_isnan(t[i]) )
|
|
d[i] = 0.0f;
|
|
else
|
|
d[i] = s[i] >= t[i] ? 1.0f : 0.0f;
|
|
}
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
// The clamp cannot matter, so skip it.
|
|
ApplyPrefixD(mips, d, sz, true);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vslt(MIPSState *mips, 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);
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
ReadVector(mips, t, sz, vt);
|
|
ApplySwizzleT(mips, t, sz);
|
|
for (int i = 0; i < numElements; i++) {
|
|
if ( my_isnan(s[i]) || my_isnan(t[i]) )
|
|
d[i] = 0.0f;
|
|
else
|
|
d[i] = s[i] < t[i] ? 1.0f : 0.0f;
|
|
}
|
|
RetainInvalidSwizzleST(mips, d, sz);
|
|
// The clamp cannot matter, so skip it.
|
|
ApplyPrefixD(mips, d, sz, true);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
|
|
void Int_Vcmov(MIPSState *mips, MIPSOpcode op) {
|
|
int vs = _VS;
|
|
int vd = _VD;
|
|
int tf = (op >> 19) & 1;
|
|
int imm3 = (op >> 16) & 7;
|
|
VectorSize sz = GetVecSize(op);
|
|
int n = GetNumVectorElements(sz);
|
|
float s[4];
|
|
float d[4];
|
|
ReadVector(mips, s, sz, vs);
|
|
ApplySwizzleS(mips, s, sz);
|
|
// Not only is D read (as T), but the T prefix applies to it.
|
|
ReadVector(mips, d, sz, vd);
|
|
ApplySwizzleT(mips, d, sz);
|
|
|
|
int CC = mips->vfpuCtrl[VFPU_CTRL_CC];
|
|
|
|
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++) {
|
|
if (((CC >> i) & 1) == !tf)
|
|
d[i] = s[i];
|
|
}
|
|
} else {
|
|
ERROR_LOG_REPORT(Log::CPU, "Bad Imm3 in cmov: %d", imm3);
|
|
}
|
|
ApplyPrefixD(mips, d, sz);
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_VecDo3(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4], t[4];
|
|
FloatBits d;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
|
|
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_( 0, "Trying to interpret instruction that can't be interpreted");
|
|
break;
|
|
}
|
|
|
|
u32 n = GetNumVectorElements(sz);
|
|
ReadVector(mips, s, sz, vs);
|
|
ReadVector(mips, t, sz, vt);
|
|
if (optype != 7) {
|
|
ApplySwizzleS(mips, s, sz);
|
|
ApplySwizzleT(mips, t, sz);
|
|
} else {
|
|
// The prefix handling of S/T is a bit odd, probably the HW doesn't do it in parallel.
|
|
// The X prefix is applied to the last element in sz.
|
|
// TODO: This doesn't match exactly for a swizzle past x in some cases...
|
|
ApplySwizzleS(mips, &s[n - 1], V_Single, -INFINITY);
|
|
ApplySwizzleT(mips, &t[n - 1], V_Single, -INFINITY);
|
|
}
|
|
|
|
for (int i = 0; i < (int)n; i++) {
|
|
switch (optype) {
|
|
case 0: d.f[i] = s[i] + t[i]; break; //vadd
|
|
case 1: d.f[i] = s[i] - t[i]; break; //vsub
|
|
case 7: d.f[i] = s[i] / t[i]; break; //vdiv
|
|
case 8: d.f[i] = s[i] * t[i]; break; //vmul
|
|
}
|
|
|
|
if (USE_VFPU_DOT) {
|
|
if (my_isnan(d.f[i])) {
|
|
d.u[i] = (d.u[i] & 0xff800001) | 1;
|
|
} else if ((d.u[i] & 0x7F800000) == 0) {
|
|
d.u[i] &= 0xFF800000;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For vdiv only, the D prefix only applies mask (and like S/T, x applied to last.)
|
|
if (optype == 7) {
|
|
u32 lastmask = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1);
|
|
u32 lastsat = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2);
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat;
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
} else {
|
|
RetainInvalidSwizzleST(mips, d.f, sz);
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
}
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_CrossQuat(MIPSState *mips, MIPSOpcode op) {
|
|
float s[4]{}, t[4]{}, d[4];
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
u32 n = GetNumVectorElements(sz);
|
|
ReadVector(mips, s, sz, vs);
|
|
ReadVector(mips, t, sz, vt);
|
|
|
|
u32 tprefixRemove = VFPU_ANY_SWIZZLE() | VFPU_NEGATE(1, 1, 1, 1);
|
|
u32 tprefixAdd;
|
|
|
|
switch (sz) {
|
|
case V_Triple: // vcrsp.t
|
|
{
|
|
if (USE_VFPU_DOT) {
|
|
float t0[4] = { 0.0f, t[2], -t[1], 0.0f };
|
|
float t1[4] = { -t[2], 0.0f, t[0], 0.0f };
|
|
d[0] = vfpu_dot(s, t0);
|
|
d[1] = vfpu_dot(s, t1);
|
|
} else {
|
|
d[0] = s[1] * t[2] - s[2] * t[1];
|
|
d[1] = s[2] * t[0] - s[0] * t[2];
|
|
}
|
|
|
|
// T prefix forces swizzle and negate, can be used to have weird constants.
|
|
tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
ApplySwizzleS(mips, s, V_Quad);
|
|
if (USE_VFPU_DOT) {
|
|
// TODO: But flush any infs to 0? This seems sketchy.
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (my_isinf(s[i]))
|
|
s[i] = 0.0f;
|
|
if (my_isinf(t[i]))
|
|
t[i] = 0.0f;
|
|
}
|
|
d[2] = vfpu_dot(s, t);
|
|
} else {
|
|
d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3];
|
|
}
|
|
break;
|
|
}
|
|
|
|
case V_Quad: // vqmul.q
|
|
{
|
|
if (USE_VFPU_DOT) {
|
|
float t0[4] = { t[3], t[2], -t[1], t[0] };
|
|
float t1[4] = { -t[2], t[3], t[0], t[1] };
|
|
float t2[4] = { t[1], -t[0], t[3], t[2] };
|
|
d[0] = vfpu_dot(s, t0);
|
|
d[1] = vfpu_dot(s, t1);
|
|
d[2] = vfpu_dot(s, t2);
|
|
} else {
|
|
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];
|
|
}
|
|
|
|
// T prefix forces swizzle and negate, can be used to have weird constants.
|
|
tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
ApplySwizzleS(mips, s, sz);
|
|
if (USE_VFPU_DOT)
|
|
d[3] = vfpu_dot(s, t);
|
|
else
|
|
d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3];
|
|
break;
|
|
}
|
|
|
|
case V_Pair:
|
|
// t swizzles invalid so the multiply is always zero.
|
|
d[0] = 0;
|
|
|
|
tprefixAdd = VFPU_SWIZZLE(0, 0, 0, 0) | VFPU_NEGATE(0, 0, 0, 0);
|
|
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
|
ApplySwizzleS(mips, 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;
|
|
|
|
default:
|
|
ERROR_LOG_REPORT(Log::CPU, "vcrsp/vqmul with invalid elements");
|
|
break;
|
|
}
|
|
|
|
// D prefix applies to the last element only (mask and sat) for pair and larger.
|
|
if (sz != V_Single) {
|
|
u32 lastmask = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1);
|
|
u32 lastsat = (mips->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2);
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat;
|
|
ApplyPrefixD(mips, d, sz);
|
|
} else {
|
|
// Single always seems to write out zero.
|
|
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] = 0;
|
|
}
|
|
WriteVector(mips, d, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vlgb(MIPSState *mips, MIPSOpcode op) {
|
|
// Vector log binary (extract exponent)
|
|
FloatBits d, s;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
|
|
ReadVector(mips, s.f, sz, vs);
|
|
ApplySwizzleS(mips, s.f, 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);
|
|
}
|
|
|
|
// If sz is greater than V_Single, the rest are copied unchanged.
|
|
for (int i = 1; i < GetNumVectorElements(sz); ++i) {
|
|
d.u[i] = s.u[i];
|
|
}
|
|
|
|
RetainInvalidSwizzleST(mips, d.f, sz);
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vwbn(MIPSState *mips, MIPSOpcode op) {
|
|
FloatBits d, s;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
VectorSize sz = GetVecSize(op);
|
|
u8 exp = (u8)((op >> 16) & 0xFF);
|
|
|
|
ReadVector(mips, s.f, sz, vs);
|
|
ApplySwizzleS(mips, s.f, sz);
|
|
|
|
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 {
|
|
s8 shift = (prevExp - exp) & 0xF;
|
|
mantissa = mantissa << shift;
|
|
}
|
|
d.u[0] = sigbit | (mantissa & 0x007FFFFF) | (exp << 23);
|
|
} else {
|
|
d.u[0] = s.u[0] | (exp << 23);
|
|
}
|
|
|
|
// If sz is greater than V_Single, the rest are copied unchanged.
|
|
for (int i = 1; i < GetNumVectorElements(sz); ++i) {
|
|
d.u[i] = s.u[i];
|
|
}
|
|
|
|
RetainInvalidSwizzleST(mips, d.f, sz);
|
|
ApplyPrefixD(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsbn(MIPSState *mips, MIPSOpcode op) {
|
|
FloatBits d, s, t;
|
|
int vd = _VD;
|
|
int vs = _VS;
|
|
int vt = _VT;
|
|
VectorSize sz = GetVecSize(op);
|
|
|
|
ReadVector(mips, s.f, sz, vs);
|
|
ApplySwizzleS(mips, s.f, sz);
|
|
ReadVector(mips, t.f, sz, vt);
|
|
ApplySwizzleT(mips, t.f, sz);
|
|
// Swizzle does apply to the value read as an integer.
|
|
u8 exp = (u8)(127 + t.i[0]);
|
|
|
|
// 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];
|
|
}
|
|
|
|
// 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(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
|
|
void Int_Vsbz(MIPSState *mips, 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);
|
|
|
|
ReadVector(mips, s.f, sz, vs);
|
|
ApplySwizzleS(mips, s.f, 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);
|
|
}
|
|
|
|
// 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(mips, d.f, sz);
|
|
WriteVector(mips, d.f, sz, vd);
|
|
PC += 4;
|
|
EatPrefixes(mips);
|
|
}
|
|
}
|