mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
feat(hle): implement sceVu0 macro-mode matrix/vector math library (#183)
Fill the remaining TODO_NAMED bodies in Kernel/Stubs/VU.cpp so the out-of-line libvu0 macro-mode routines write their documented results to the caller's output operand instead of leaving guest memory untouched (the TODO_NAMED stub throws, or returns -1 once its per-name warning budget is spent, and never writes the destination). Implements the matrix/geometry/clip/lighting entry points: CameraMatrix, InversMatrix, MulMatrix, TransMatrix, RotMatrix, NormalLightMatrix, LightColorMatrix, DropShadowMatrix, ViewScreenMatrix, RotTransPers/RotTransPersN, ClipScreen/ClipScreen3/ClipAll, MulVector, ScaleVectorXYZ, DivVector/DivVectorXYZ, InterVector/InterVectorXYZ, ClampVector, ecossin. Pure input->output arithmetic over guest memory through the existing getMemPtr/getConstMemPtr and getRegU32/ctx->f[] accessors already used by the implemented sceVu0 functions in the same file. No new runtime state, no signature changes, no VU.h changes. The clip and view/shadow routines provide the functional contract callers rely on (nonzero => offscreen; projection by formula shape) rather than a bit-exact reproduction of the COP2 sticky clip-flag register. Adds ps2xTest/src/ps2_vu_tests.cpp covering each routine against its documented formula.
This commit is contained in:
committed by
GitHub
parent
f3687c5ae6
commit
6970f3a2c4
@@ -1,13 +1,8 @@
|
||||
#include "Common.h"
|
||||
#include "VU.h"
|
||||
//TODO use glm
|
||||
|
||||
namespace ps2_stubs
|
||||
{
|
||||
void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -77,6 +72,7 @@ namespace ps2_stubs
|
||||
return true;
|
||||
}
|
||||
|
||||
// Row-major matrix product: out = lhs * rhs (lhs is the left factor).
|
||||
void mulVuMatrix(const float (&lhs)[16], const float (&rhs)[16], float (&out)[16])
|
||||
{
|
||||
std::fill(std::begin(out), std::end(out), 0.0f);
|
||||
@@ -100,6 +96,94 @@ namespace ps2_stubs
|
||||
out[10] = 1.0f;
|
||||
out[15] = 1.0f;
|
||||
}
|
||||
|
||||
// Rigid-transform inverse under the file's row-vector convention
|
||||
// (translation in row 3, ApplyMatrix computes v*M): transpose the 3x3
|
||||
// rotation block, zero its 4th column, and set the translation row to
|
||||
// -t*R^T -- each lane dots t with a ROW of R: out[12+col] =
|
||||
// -(t . R[col]). This makes M * rigidInverse(M) == I. Copy [15].
|
||||
void rigidInverse(const float (&in)[16], float (&out)[16])
|
||||
{
|
||||
for (int row = 0; row < 3; ++row)
|
||||
{
|
||||
for (int col = 0; col < 3; ++col)
|
||||
out[4 * row + col] = in[4 * col + row];
|
||||
out[4 * row + 3] = 0.0f;
|
||||
}
|
||||
const float tx = in[12], ty = in[13], tz = in[14];
|
||||
for (int col = 0; col < 3; ++col)
|
||||
out[12 + col] = -((tx * in[4 * col]) + (ty * in[4 * col + 1]) + (tz * in[4 * col + 2]));
|
||||
out[15] = in[15];
|
||||
}
|
||||
|
||||
void axisRotateMatrix(const float (&in)[16], float angle, int axis, float (&out)[16])
|
||||
{
|
||||
float rot[16]{};
|
||||
makeIdentityMatrix(rot);
|
||||
const float cs = std::cos(angle);
|
||||
const float sn = std::sin(angle);
|
||||
if (axis == 2)
|
||||
{
|
||||
rot[0] = cs;
|
||||
rot[1] = sn;
|
||||
rot[4] = -sn;
|
||||
rot[5] = cs;
|
||||
}
|
||||
else if (axis == 1)
|
||||
{
|
||||
rot[0] = cs;
|
||||
rot[2] = -sn;
|
||||
rot[8] = sn;
|
||||
rot[10] = cs;
|
||||
}
|
||||
else
|
||||
{
|
||||
rot[5] = cs;
|
||||
rot[6] = sn;
|
||||
rot[9] = -sn;
|
||||
rot[10] = cs;
|
||||
}
|
||||
mulVuMatrix(in, rot, out);
|
||||
}
|
||||
|
||||
// Applies the matrix to the vertex, perspective-divides xyz by w
|
||||
// (w==0 maps to a zero divide rather than Inf/NaN), then converts
|
||||
// x/y to 12.4 fixed point (x16) unconditionally. z/w take the same
|
||||
// x16 conversion when fullFtoi4 is set; otherwise they are plain
|
||||
// integer-truncated (FTOI0) after the divide instead.
|
||||
void rotTransPersOne(const float (&m)[16], const float (&v)[4], bool fullFtoi4, int32_t (&out)[4])
|
||||
{
|
||||
float t[4];
|
||||
t[0] = (m[0] * v[0]) + (m[4] * v[1]) + (m[8] * v[2]) + (m[12] * v[3]);
|
||||
t[1] = (m[1] * v[0]) + (m[5] * v[1]) + (m[9] * v[2]) + (m[13] * v[3]);
|
||||
t[2] = (m[2] * v[0]) + (m[6] * v[1]) + (m[10] * v[2]) + (m[14] * v[3]);
|
||||
t[3] = (m[3] * v[0]) + (m[7] * v[1]) + (m[11] * v[2]) + (m[15] * v[3]);
|
||||
const float q = (t[3] != 0.0f) ? (1.0f / t[3]) : 0.0f;
|
||||
t[0] *= q;
|
||||
t[1] *= q;
|
||||
t[2] *= q;
|
||||
out[0] = static_cast<int32_t>(t[0] * 16.0f);
|
||||
out[1] = static_cast<int32_t>(t[1] * 16.0f);
|
||||
out[2] = fullFtoi4 ? static_cast<int32_t>(t[2] * 16.0f) : static_cast<int32_t>(t[2]);
|
||||
out[3] = fullFtoi4 ? static_cast<int32_t>(t[3] * 16.0f) : static_cast<int32_t>(t[3]);
|
||||
}
|
||||
|
||||
// Guard-band proxy for the COP2 sticky clip flags: nonzero => the
|
||||
// vertex is offscreen. Not the hardware per-plane flag layout.
|
||||
constexpr float kScreenClipGuard = 4096.0f;
|
||||
int32_t screenClipCode(const float (&v)[4])
|
||||
{
|
||||
int32_t code = 0;
|
||||
if (v[0] > kScreenClipGuard)
|
||||
code |= 0x1;
|
||||
if (v[0] < -kScreenClipGuard)
|
||||
code |= 0x2;
|
||||
if (v[1] > kScreenClipGuard)
|
||||
code |= 0x4;
|
||||
if (v[1] < -kScreenClipGuard)
|
||||
code |= 0x8;
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -147,27 +231,139 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t eyeAddr = getRegU32(ctx, 5);
|
||||
const uint32_t fwdAddr = getRegU32(ctx, 6);
|
||||
const uint32_t upAddr = getRegU32(ctx, 7);
|
||||
float eye[4]{}, fwd[4]{}, up[4]{};
|
||||
if (readVuVec4f(rdram, eyeAddr, eye) && readVuVec4f(rdram, fwdAddr, fwd) && readVuVec4f(rdram, upAddr, up))
|
||||
{
|
||||
auto cross = [](const float (&l)[4], const float (&r)[4], float (&o)[4])
|
||||
{
|
||||
o[0] = (l[1] * r[2]) - (l[2] * r[1]);
|
||||
o[1] = (l[2] * r[0]) - (l[0] * r[2]);
|
||||
o[2] = (l[0] * r[1]) - (l[1] * r[0]);
|
||||
o[3] = 0.0f;
|
||||
};
|
||||
auto normalize = [](const float (&s)[4], float (&o)[4])
|
||||
{
|
||||
const float len = std::sqrt((s[0] * s[0]) + (s[1] * s[1]) + (s[2] * s[2]) + (s[3] * s[3]));
|
||||
const float inv = (len > 1.0e-6f) ? (1.0f / len) : 0.0f;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
o[i] = s[i] * inv;
|
||||
};
|
||||
float rawCross[4]{}, row0[4]{}, row1[4]{}, row2[4]{};
|
||||
cross(up, fwd, rawCross);
|
||||
normalize(rawCross, row0);
|
||||
normalize(fwd, row2);
|
||||
cross(row2, row0, row1);
|
||||
|
||||
float m[16]{};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
m[i] = row0[i];
|
||||
m[4 + i] = row1[i];
|
||||
m[8 + i] = row2[i];
|
||||
}
|
||||
m[12] = eye[0];
|
||||
m[13] = eye[1];
|
||||
m[14] = eye[2];
|
||||
m[15] = 1.0f;
|
||||
|
||||
float out[16]{};
|
||||
rigidInverse(m, out);
|
||||
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const float lo = ctx ? ctx->f[12] : 0.0f;
|
||||
const float hi = ctx ? ctx->f[13] : 0.0f;
|
||||
float src[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, srcAddr, src))
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
float v = src[i];
|
||||
v = (v < lo) ? lo : v;
|
||||
v = (v > hi) ? hi : v;
|
||||
out[i] = v;
|
||||
}
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime);
|
||||
const uint32_t loAddr = getRegU32(ctx, 4);
|
||||
const uint32_t hiAddr = getRegU32(ctx, 5);
|
||||
const uint32_t matAddr = getRegU32(ctx, 6);
|
||||
uint32_t vAddr = getRegU32(ctx, 7);
|
||||
const int32_t count = static_cast<int32_t>(getRegU32(ctx, 8));
|
||||
float lo[4]{}, hi[4]{}, m[16]{};
|
||||
int32_t result = 0;
|
||||
if (readVuVec4f(rdram, loAddr, lo) && readVuVec4f(rdram, hiAddr, hi) && readVuMatrix4f(rdram, matAddr, m))
|
||||
{
|
||||
result = 1;
|
||||
for (int32_t i = 0; i < count; ++i)
|
||||
{
|
||||
float v[4]{};
|
||||
if (!readVuVec4f(rdram, vAddr, v))
|
||||
break;
|
||||
const float tx = (m[0] * v[0]) + (m[4] * v[1]) + (m[8] * v[2]) + (m[12] * v[3]);
|
||||
const float ty = (m[1] * v[0]) + (m[5] * v[1]) + (m[9] * v[2]) + (m[13] * v[3]);
|
||||
const float tw = (m[3] * v[0]) + (m[7] * v[1]) + (m[11] * v[2]) + (m[15] * v[3]);
|
||||
const bool outsideX = (tx < lo[0] * tw) || (tx > hi[0] * tw);
|
||||
const bool outsideY = (ty < lo[1] * tw) || (ty > hi[1] * tw);
|
||||
if (!outsideX && !outsideY)
|
||||
{
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
vAddr += 16u;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, result);
|
||||
}
|
||||
|
||||
void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime);
|
||||
const uint32_t vAddr = getRegU32(ctx, 4);
|
||||
float v[4]{};
|
||||
int32_t code = 0;
|
||||
if (readVuVec4f(rdram, vAddr, v))
|
||||
{
|
||||
code = screenClipCode(v);
|
||||
}
|
||||
setReturnS32(ctx, code);
|
||||
}
|
||||
|
||||
void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime);
|
||||
const uint32_t v0Addr = getRegU32(ctx, 4);
|
||||
const uint32_t v1Addr = getRegU32(ctx, 5);
|
||||
const uint32_t v2Addr = getRegU32(ctx, 6);
|
||||
float v0[4]{}, v1[4]{}, v2[4]{};
|
||||
int32_t code = 0;
|
||||
if (readVuVec4f(rdram, v0Addr, v0))
|
||||
{
|
||||
code |= screenClipCode(v0);
|
||||
}
|
||||
if (readVuVec4f(rdram, v1Addr, v1))
|
||||
{
|
||||
code |= screenClipCode(v1);
|
||||
}
|
||||
if (readVuVec4f(rdram, v2Addr, v2))
|
||||
{
|
||||
code |= screenClipCode(v2);
|
||||
}
|
||||
setReturnS32(ctx, code);
|
||||
}
|
||||
|
||||
void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -211,17 +407,108 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const float divisor = ctx ? ctx->f[12] : 1.0f;
|
||||
float src[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, srcAddr, src))
|
||||
{
|
||||
const float q = (divisor != 0.0f) ? (1.0f / divisor) : 0.0f;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
out[i] = src[i] * q;
|
||||
}
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const float divisor = ctx ? ctx->f[12] : 1.0f;
|
||||
float src[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, srcAddr, src))
|
||||
{
|
||||
const float q = (divisor != 0.0f) ? (1.0f / divisor) : 0.0f;
|
||||
out[0] = src[0] * q;
|
||||
out[1] = src[1] * q;
|
||||
out[2] = src[2] * q;
|
||||
out[3] = src[3];
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime);
|
||||
// Two documented drop-shadow modes reconstructed from behavior; not
|
||||
// claimed bit-exact to a specific SDK build.
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t nAddr = getRegU32(ctx, 5);
|
||||
const uint32_t mode = getRegU32(ctx, 6);
|
||||
const float lx = ctx ? ctx->f[12] : 0.0f;
|
||||
const float ly = ctx ? ctx->f[13] : 0.0f;
|
||||
const float lz = ctx ? ctx->f[14] : 0.0f;
|
||||
float n[4]{};
|
||||
if (readVuVec4f(rdram, nAddr, n))
|
||||
{
|
||||
const float nx = n[0], ny = n[1], nz = n[2];
|
||||
const float d = (lx * nx) + (ly * ny) + (lz * nz);
|
||||
float out[16]{};
|
||||
if (mode != 0)
|
||||
{
|
||||
const float k = 1.0f - d;
|
||||
out[0] = (lx * nx) + k;
|
||||
out[1] = lx * ny;
|
||||
out[2] = lx * nz;
|
||||
out[3] = lx;
|
||||
out[4] = ly * nx;
|
||||
out[5] = (ly * ny) + k;
|
||||
out[6] = ly * nz;
|
||||
out[7] = ly;
|
||||
out[8] = lz * nx;
|
||||
out[9] = lz * ny;
|
||||
out[10] = (lz * nz) + k;
|
||||
out[11] = lz;
|
||||
out[12] = -nx;
|
||||
out[13] = -ny;
|
||||
out[14] = -nz;
|
||||
out[15] = -d;
|
||||
}
|
||||
else
|
||||
{
|
||||
const float k = (d != 0.0f) ? (-1.0f / d) : 0.0f;
|
||||
out[0] = k * ((lx * nx) - d);
|
||||
out[1] = k * (lx * ny);
|
||||
out[2] = k * (lx * nz);
|
||||
out[3] = 0.0f;
|
||||
out[4] = k * (ly * nx);
|
||||
out[5] = k * ((ly * ny) - d);
|
||||
out[6] = k * (ly * nz);
|
||||
out[7] = 0.0f;
|
||||
out[8] = k * (lz * nx);
|
||||
out[9] = k * (lz * ny);
|
||||
out[10] = k * ((lz * nz) - d);
|
||||
out[11] = 0.0f;
|
||||
out[12] = k * -nx;
|
||||
out[13] = k * -ny;
|
||||
out[14] = k * -nz;
|
||||
out[15] = 1.0f;
|
||||
}
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const float angle = ctx ? ctx->f[12] : 0.0f;
|
||||
float out[4] = {std::cos(angle), std::sin(angle), 0.0f, 0.0f};
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -280,17 +567,53 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t aAddr = getRegU32(ctx, 5);
|
||||
const uint32_t bAddr = getRegU32(ctx, 6);
|
||||
const float t = ctx ? ctx->f[12] : 0.0f;
|
||||
float a[4]{}, b[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, aAddr, a) && readVuVec4f(rdram, bAddr, b))
|
||||
{
|
||||
const float invT = 1.0f - t;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
out[i] = (a[i] * t) + (b[i] * invT);
|
||||
}
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t aAddr = getRegU32(ctx, 5);
|
||||
const uint32_t bAddr = getRegU32(ctx, 6);
|
||||
const float t = ctx ? ctx->f[12] : 0.0f;
|
||||
float a[4]{}, b[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, aAddr, a) && readVuVec4f(rdram, bAddr, b))
|
||||
{
|
||||
const float invT = 1.0f - t;
|
||||
out[0] = (a[0] * t) + (b[0] * invT);
|
||||
out[1] = (a[1] * t) + (b[1] * invT);
|
||||
out[2] = (a[2] * t) + (b[2] * invT);
|
||||
out[3] = a[3];
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
float in[16]{}, out[16]{};
|
||||
if (readVuMatrix4f(rdram, srcAddr, in))
|
||||
{
|
||||
rigidInverse(in, out);
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -346,17 +669,60 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t c0Addr = getRegU32(ctx, 5);
|
||||
const uint32_t c1Addr = getRegU32(ctx, 6);
|
||||
const uint32_t c2Addr = getRegU32(ctx, 7);
|
||||
const uint32_t c3Addr = getRegU32(ctx, 8);
|
||||
float c0[4]{}, c1[4]{}, c2[4]{}, c3[4]{};
|
||||
if (readVuVec4f(rdram, c0Addr, c0) && readVuVec4f(rdram, c1Addr, c1) &&
|
||||
readVuVec4f(rdram, c2Addr, c2) && readVuVec4f(rdram, c3Addr, c3))
|
||||
{
|
||||
float out[16]{};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
out[i] = c0[i];
|
||||
out[4 + i] = c1[i];
|
||||
out[8 + i] = c2[i];
|
||||
out[12 + i] = c3[i];
|
||||
}
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t m0Addr = getRegU32(ctx, 5);
|
||||
const uint32_t m1Addr = getRegU32(ctx, 6);
|
||||
float m0[16]{}, m1[16]{}, out[16]{};
|
||||
if (readVuMatrix4f(rdram, m0Addr, m0) && readVuMatrix4f(rdram, m1Addr, m1))
|
||||
{
|
||||
// out = m0 * m1 (first source . second source), matching the
|
||||
// file's mulVuMatrix(lhs,rhs)=lhs.rhs convention and the RotMatrix
|
||||
// / ViewScreenMatrix siblings (first operand on the left).
|
||||
mulVuMatrix(m0, m1, out);
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t lhsAddr = getRegU32(ctx, 5);
|
||||
const uint32_t rhsAddr = getRegU32(ctx, 6);
|
||||
float lhs[4]{}, rhs[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs))
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
out[i] = lhs[i] * rhs[i];
|
||||
}
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -382,7 +748,42 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime);
|
||||
// Rows = normalize(-light); the 4x4 is transposed so directions occupy
|
||||
// columns (one ApplyMatrix then yields per-light N.L).
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t l0Addr = getRegU32(ctx, 5);
|
||||
const uint32_t l1Addr = getRegU32(ctx, 6);
|
||||
const uint32_t l2Addr = getRegU32(ctx, 7);
|
||||
float l0[4]{}, l1[4]{}, l2[4]{};
|
||||
if (readVuVec4f(rdram, l0Addr, l0) && readVuVec4f(rdram, l1Addr, l1) && readVuVec4f(rdram, l2Addr, l2))
|
||||
{
|
||||
auto negNormalize = [](const float (&s)[4], float (&o)[4])
|
||||
{
|
||||
const float len = std::sqrt((s[0] * s[0]) + (s[1] * s[1]) + (s[2] * s[2]) + (s[3] * s[3]));
|
||||
const float inv = (len > 1.0e-6f) ? (1.0f / len) : 0.0f;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
o[i] = -s[i] * inv;
|
||||
};
|
||||
float r0[4]{}, r1[4]{}, r2[4]{};
|
||||
negNormalize(l0, r0);
|
||||
negNormalize(l1, r1);
|
||||
negNormalize(l2, r2);
|
||||
float m[16]{};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
m[i] = r0[i];
|
||||
m[4 + i] = r1[i];
|
||||
m[8 + i] = r2[i];
|
||||
m[12 + i] = 0.0f;
|
||||
}
|
||||
m[15] = 1.0f;
|
||||
float out[16]{};
|
||||
for (int row = 0; row < 4; ++row)
|
||||
for (int col = 0; col < 4; ++col)
|
||||
out[4 * row + col] = m[4 * col + row];
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -404,7 +805,19 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const uint32_t rotAddr = getRegU32(ctx, 6);
|
||||
float src[16]{}, rotVec[4]{};
|
||||
if (readVuMatrix4f(rdram, srcAddr, src) && readVuVec4f(rdram, rotAddr, rotVec))
|
||||
{
|
||||
float afterZ[16]{}, afterY[16]{}, afterX[16]{};
|
||||
axisRotateMatrix(src, rotVec[2], 2, afterZ);
|
||||
axisRotateMatrix(afterZ, rotVec[1], 1, afterY);
|
||||
axisRotateMatrix(afterY, rotVec[0], 0, afterX);
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, afterX);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -472,12 +885,44 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t matAddr = getRegU32(ctx, 5);
|
||||
const uint32_t vAddr = getRegU32(ctx, 6);
|
||||
const bool fullFtoi4 = (getRegU32(ctx, 7) != 0);
|
||||
float m[16]{}, v[4]{};
|
||||
if (readVuMatrix4f(rdram, matAddr, m) && readVuVec4f(rdram, vAddr, v))
|
||||
{
|
||||
int32_t out[4]{};
|
||||
rotTransPersOne(m, v, fullFtoi4, out);
|
||||
(void)writeVuVec4i(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t matAddr = getRegU32(ctx, 5);
|
||||
uint32_t vAddr = getRegU32(ctx, 6);
|
||||
const int32_t count = static_cast<int32_t>(getRegU32(ctx, 7));
|
||||
const bool fullFtoi4 = (getRegU32(ctx, 8) != 0);
|
||||
float m[16]{};
|
||||
if (readVuMatrix4f(rdram, matAddr, m))
|
||||
{
|
||||
uint32_t outAddr = dstAddr;
|
||||
for (int32_t i = 0; i < count; ++i)
|
||||
{
|
||||
float v[4]{};
|
||||
if (!readVuVec4f(rdram, vAddr, v))
|
||||
break;
|
||||
int32_t out[4]{};
|
||||
rotTransPersOne(m, v, fullFtoi4, out);
|
||||
(void)writeVuVec4i(rdram, outAddr, out);
|
||||
vAddr += 16u;
|
||||
outAddr += 16u;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -509,7 +954,19 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const float scale = ctx ? ctx->f[12] : 0.0f;
|
||||
float src[4]{}, out[4]{};
|
||||
if (readVuVec4f(rdram, srcAddr, src))
|
||||
{
|
||||
out[0] = src[0] * scale;
|
||||
out[1] = src[1] * scale;
|
||||
out[2] = src[2] * scale;
|
||||
out[3] = src[3];
|
||||
(void)writeVuVec4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -531,7 +988,20 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const uint32_t vAddr = getRegU32(ctx, 6);
|
||||
float src[16]{}, v[4]{};
|
||||
if (readVuMatrix4f(rdram, srcAddr, src) && readVuVec4f(rdram, vAddr, v))
|
||||
{
|
||||
float out[16]{};
|
||||
std::memcpy(out, src, sizeof(out));
|
||||
out[12] = src[12] + v[0];
|
||||
out[13] = src[13] + v[1];
|
||||
out[14] = src[14] + v[2];
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -579,6 +1049,55 @@ namespace ps2_stubs
|
||||
|
||||
void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime);
|
||||
// Near/far params handled by formula shape; SDK parameter names
|
||||
// not pinned. Args follow the out-of-line libvu0 register convention
|
||||
// used throughout this file: eight scalar floats in f12..f19 (p0..p7)
|
||||
// and the ninth (p8) as the first stack-passed argument. That
|
||||
// eight-FP-arg-register layout is the n32/EABI convention the EE
|
||||
// toolchain emits (an o32 layout would carry only two FP args in
|
||||
// f12/f14 and spill p2..p7 too), and under it no GPR home/save area is
|
||||
// reserved, so the ninth float is at 0(sp) -- read below. A target ABI
|
||||
// that reserves a home area would shift only that read by its size.
|
||||
const uint32_t dstAddr = getRegU32(ctx, 4);
|
||||
const float p0 = ctx ? ctx->f[12] : 0.0f;
|
||||
const float p1 = ctx ? ctx->f[13] : 0.0f;
|
||||
const float p2 = ctx ? ctx->f[14] : 0.0f;
|
||||
const float p3 = ctx ? ctx->f[15] : 0.0f;
|
||||
const float p4 = ctx ? ctx->f[16] : 0.0f;
|
||||
const float p5 = ctx ? ctx->f[17] : 0.0f;
|
||||
const float p6 = ctx ? ctx->f[18] : 0.0f;
|
||||
const float p7 = ctx ? ctx->f[19] : 0.0f;
|
||||
float p8 = 0.0f;
|
||||
if (const uint8_t *sp = getConstMemPtr(rdram, getRegU32(ctx, 29)))
|
||||
{
|
||||
std::memcpy(&p8, sp, sizeof(p8));
|
||||
}
|
||||
|
||||
const float denom = p8 - p7;
|
||||
const float zScale = (denom != 0.0f) ? ((p8 * p7 * (p6 - p5)) / denom) : 0.0f;
|
||||
const float zOffset = (denom != 0.0f) ? (((p5 * p8) - (p6 * p7)) / denom) : 0.0f;
|
||||
|
||||
float scaleMat[16]{};
|
||||
makeIdentityMatrix(scaleMat);
|
||||
scaleMat[0] = p0;
|
||||
scaleMat[5] = p0;
|
||||
scaleMat[10] = 0.0f;
|
||||
scaleMat[11] = 1.0f;
|
||||
scaleMat[14] = 1.0f;
|
||||
scaleMat[15] = 0.0f;
|
||||
|
||||
float projMat[16]{};
|
||||
makeIdentityMatrix(projMat);
|
||||
projMat[0] = p1;
|
||||
projMat[5] = p2;
|
||||
projMat[10] = zScale;
|
||||
projMat[12] = p3;
|
||||
projMat[13] = p4;
|
||||
projMat[14] = zOffset;
|
||||
|
||||
float out[16]{};
|
||||
mulVuMatrix(scaleMat, projMat, out);
|
||||
(void)writeVuMatrix4f(rdram, dstAddr, out);
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ add_library(ps2_test_lib STATIC
|
||||
src/ps2_runtime_interrupt_tests.cpp
|
||||
src/ps2_memory_tests.cpp
|
||||
src/ps2_vu1_tests.cpp
|
||||
src/ps2_vu_tests.cpp
|
||||
src/ps2_gs_tests.cpp
|
||||
src/ps2_iop_tests.cpp
|
||||
src/ps2_sif_rpc_tests.cpp
|
||||
|
||||
@@ -11,6 +11,7 @@ void register_ps2_runtime_kernel_tests();
|
||||
void register_ps2_runtime_interrupt_tests();
|
||||
void register_ps2_memory_tests();
|
||||
void register_ps2_vu1_tests();
|
||||
void register_ps2_vu_tests();
|
||||
void register_ps2_gs_tests();
|
||||
void register_ps2_iop_tests();
|
||||
void register_ps2_sif_rpc_tests();
|
||||
@@ -32,6 +33,7 @@ int main()
|
||||
register_ps2_runtime_interrupt_tests();
|
||||
register_ps2_memory_tests();
|
||||
register_ps2_vu1_tests();
|
||||
register_ps2_vu_tests();
|
||||
register_ps2_gs_tests();
|
||||
register_ps2_iop_tests();
|
||||
register_ps2_sif_rpc_tests();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user