Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions ps2xRecomp/src/lib/vu_translation_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,32 @@ namespace ps2recomp
uint8_t fs_reg = inst.rd;
uint8_t ft_reg = inst.rt;

return fmt::format("{{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); ctx->vu0_q = (ft != 0.0f) ? (fs / ft) : 0.0f; }}", fs_reg, fs_reg, fsf, ft_reg, ft_reg, ftf);
return fmt::format("{{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{0}], ctx->vu0_vf[{0}], _MM_SHUFFLE(0,0,0,{1}))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{2}], ctx->vu0_vf[{2}], _MM_SHUFFLE(0,0,0,{3}))); "
"ctx->vu0_q = PS2_VU_DIV_Q(fs, ft); }}",
fs_reg, fsf, ft_reg, ftf);
}

std::string CodeGenerator::translateVU_VSQRT(const Instruction &inst)
{
uint8_t ftf = inst.vectorInfo.ftf;
uint8_t ft_reg = inst.rt;
return fmt::format("{{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); ctx->vu0_q = sqrtf(std::max(0.0f, ft)); }}", ft_reg, ft_reg, ftf);
return fmt::format("{{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{0}], ctx->vu0_vf[{0}], _MM_SHUFFLE(0,0,0,{1}))); "
"ctx->vu0_q = PS2_VU_SQRT_Q(ft); }}",
ft_reg, ftf);
}

std::string CodeGenerator::translateVU_VRSQRT(const Instruction &inst)
{
uint8_t fsf = inst.vectorInfo.fsf;
uint8_t ftf = inst.vectorInfo.ftf;
uint8_t fs_reg = inst.rd;
uint8_t ft_reg = inst.rt;
return fmt::format("{{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); ctx->vu0_q = (ft > 0.0f) ? (1.0f / sqrtf(ft)) : 0.0f; }}", ft_reg, ft_reg, ftf);

return fmt::format("{{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{0}], ctx->vu0_vf[{0}], _MM_SHUFFLE(0,0,0,{1}))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{2}], ctx->vu0_vf[{2}], _MM_SHUFFLE(0,0,0,{3}))); "
"ctx->vu0_q = PS2_VU_RSQRT_Q(fs, ft); }}",
fs_reg, fsf, ft_reg, ftf);
}

std::string CodeGenerator::translateVU_VMTIR(const Instruction &inst)
Expand Down
26 changes: 26 additions & 0 deletions ps2xRuntime/include/ps2_runtime_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cmath>
#include <cstring>
#include <bit>
#include <limits>
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(USE_SSE2NEON)
Expand Down Expand Up @@ -730,6 +731,31 @@ inline __m128i ps2_qfsrv(__m128i rs, __m128i rt, uint32_t sa)
#define PS2_PROT3W(rs) _mm_shuffle_epi32(rs, _MM_SHUFFLE(0, 3, 2, 1))

// Additional VU0 operations
static inline float Ps2VuDivQ(float fs, float ft)
{
if (ft != 0.0f)
{
return fs / ft;
}
return (std::signbit(fs) != std::signbit(ft))
? -std::numeric_limits<float>::max()
: std::numeric_limits<float>::max();
}

static inline float Ps2VuSqrtQ(float ft)
{
return std::sqrt(std::fabs(ft));
}

static inline float Ps2VuRsqrtQ(float fs, float ft)
{
return Ps2VuDivQ(fs, Ps2VuSqrtQ(ft));
}

#define PS2_VU_DIV_Q(fs, ft) Ps2VuDivQ((fs), (ft))
#define PS2_VU_SQRT_Q(ft) Ps2VuSqrtQ((ft))
#define PS2_VU_RSQRT_Q(fs, ft) Ps2VuRsqrtQ((fs), (ft))

#define PS2_VSQRT(x) sqrtf(x)
#define PS2_VRSQRT(x) (1.0f / sqrtf(x))

Expand Down
73 changes: 73 additions & 0 deletions ps2xTest/src/code_generator_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@ static Instruction makeJr(uint32_t address, uint8_t rs)
return inst;
}

static Instruction makeVu0Special2(uint8_t vuFunc, uint8_t fsReg, uint8_t ftReg, uint8_t fsf, uint8_t ftf)
{
Instruction inst{};
inst.opcode = OPCODE_COP2;
inst.rs = COP2_CO;
inst.rd = fsReg;
inst.rt = ftReg;
// Special2 ops decode as function >= 0x3C with the op rebuilt from raw bits 10-6 and 1-0;
// this is the inverse of that decode and must stay in step with it.
inst.function = 0x3C;
inst.raw = ((static_cast<uint32_t>(vuFunc) >> 2) << 6) | (vuFunc & 0x3);
inst.vectorInfo.fsf = fsf;
inst.vectorInfo.ftf = ftf;
return inst;
}

static void printGeneratedCode(const std::string& name, const std::string& code)
{
#ifdef PRINT_GENERATED_CODE
Expand Down Expand Up @@ -1076,6 +1092,63 @@ void register_code_generator_tests()
t.IsTrue(out.find("ctx->vu0_vf[27]") == std::string::npos, "S1 must not use rs(format) as register index");
});

tc.Run("VDIV emits exactly the statements for a saturating divide of fs by ft", [](TestCase &t) {
CodeGenerator gen({}, {});

t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VDIV, 11, 7, 1, 2)),
"{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[11], ctx->vu0_vf[11], _MM_SHUFFLE(0,0,0,1))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[7], ctx->vu0_vf[7], _MM_SHUFFLE(0,0,0,2))); "
"ctx->vu0_q = PS2_VU_DIV_Q(fs, ft); }",
"VDIV should emit exactly this text for fs=11/fsf=1, ft=7/ftf=2");
t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VDIV, 24, 5, 3, 0)),
"{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[24], ctx->vu0_vf[24], _MM_SHUFFLE(0,0,0,3))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[5], ctx->vu0_vf[5], _MM_SHUFFLE(0,0,0,0))); "
"ctx->vu0_q = PS2_VU_DIV_Q(fs, ft); }",
"VDIV should emit exactly this text for fs=24/fsf=3, ft=5/ftf=0");
t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VDIV, 6, 19, 2, 3)),
"{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[6], ctx->vu0_vf[6], _MM_SHUFFLE(0,0,0,2))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[19], ctx->vu0_vf[19], _MM_SHUFFLE(0,0,0,3))); "
"ctx->vu0_q = PS2_VU_DIV_Q(fs, ft); }",
"VDIV should emit exactly this text for fs=6/fsf=2, ft=19/ftf=3");
});

tc.Run("VSQRT emits exactly the statements for the magnitude square root of ft", [](TestCase &t) {
CodeGenerator gen({}, {});

t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VSQRT, 11, 7, 1, 2)),
"{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[7], ctx->vu0_vf[7], _MM_SHUFFLE(0,0,0,2))); "
"ctx->vu0_q = PS2_VU_SQRT_Q(ft); }",
"VSQRT should emit exactly this text for ft=7/ftf=2, ignoring fs=11/fsf=1");
t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VSQRT, 24, 5, 3, 0)),
"{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[5], ctx->vu0_vf[5], _MM_SHUFFLE(0,0,0,0))); "
"ctx->vu0_q = PS2_VU_SQRT_Q(ft); }",
"VSQRT should emit exactly this text for ft=5/ftf=0, ignoring fs=24/fsf=3");
t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VSQRT, 6, 19, 2, 3)),
"{ float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[19], ctx->vu0_vf[19], _MM_SHUFFLE(0,0,0,3))); "
"ctx->vu0_q = PS2_VU_SQRT_Q(ft); }",
"VSQRT should emit exactly this text for ft=19/ftf=3, ignoring fs=6/fsf=2");
});

tc.Run("VRSQRT emits exactly the statements that read fs and ft from their own register and selector", [](TestCase &t) {
CodeGenerator gen({}, {});

t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VRSQRT, 11, 7, 1, 2)),
"{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[11], ctx->vu0_vf[11], _MM_SHUFFLE(0,0,0,1))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[7], ctx->vu0_vf[7], _MM_SHUFFLE(0,0,0,2))); "
"ctx->vu0_q = PS2_VU_RSQRT_Q(fs, ft); }",
"VRSQRT should emit exactly this text for fs=11/fsf=1, ft=7/ftf=2");
t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VRSQRT, 24, 5, 3, 0)),
"{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[24], ctx->vu0_vf[24], _MM_SHUFFLE(0,0,0,3))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[5], ctx->vu0_vf[5], _MM_SHUFFLE(0,0,0,0))); "
"ctx->vu0_q = PS2_VU_RSQRT_Q(fs, ft); }",
"VRSQRT should emit exactly this text for fs=24/fsf=3, ft=5/ftf=0");
t.Equals(gen.translateInstruction(makeVu0Special2(VU0_S2_VRSQRT, 6, 19, 2, 3)),
"{ float fs = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[6], ctx->vu0_vf[6], _MM_SHUFFLE(0,0,0,2))); "
"float ft = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[19], ctx->vu0_vf[19], _MM_SHUFFLE(0,0,0,3))); "
"ctx->vu0_q = PS2_VU_RSQRT_Q(fs, ft); }",
"VRSQRT should emit exactly this text for fs=6/fsf=2, ft=19/ftf=3");
});

tc.Run("VU0 S1 q/i forms keep mask and use sa as destination", [](TestCase &t) {
Instruction inst{};
inst.opcode = OPCODE_COP2;
Expand Down
Loading