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
7 changes: 7 additions & 0 deletions ps2xRuntime/include/ps2_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ struct alignas(16) R5900Context
// Initialize VU0 registers
vu0_q = 1.0f; // Q register usually initialized to 1.0

// vf0 is hardwired on real VU0 hardware to (x,y,z,w) = (0,0,0,1) and reads
// back that constant regardless of writes; it is the vector-unit analogue of
// GPR r0. memset above leaves it (0,0,0,0), so w reads 0 instead of 1. Pin it
// here so every context this runtime constructs starts hardware-correct.
// (_mm_set_ps arguments are in (w, z, y, x) order, so this is x=0,y=0,z=0,w=1.)
vu0_vf[0] = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f);

// Reset COP0 registers
cop0_random = 47; // Start at maximum value
// cop0_status = 0x400000; // BEV set, ERL clear, kernel mode
Expand Down
4 changes: 4 additions & 0 deletions ps2xRuntime/src/lib/ps2_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@ PS2Runtime::PS2Runtime()
// R0 is always zero in MIPS
m_cpuContext.r[0] = _mm_set1_epi32(0);

// vf0 is hardwired to (0,0,0,1) on VU0 hardware; the memset above cleared
// the constructor's pin, so repin it here, mirroring r0.
m_cpuContext.vu0_vf[0] = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f);

// Stack pointer (SP) and global pointer (GP) will be set by the loaded ELF

m_loadedModules.clear();
Expand Down
23 changes: 23 additions & 0 deletions ps2xTest/src/ps2_runtime_expansion_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2244,5 +2244,28 @@ void register_ps2_runtime_expansion_tests()
std::memcpy(&directCmd, rdram.data() + kBaseAddr + 12u, sizeof(directCmd));
t.Equals(directCmd, 0x50000001u, "sceVif1PkCloseDirectCode should store a 1-QW DIRECT length");
});

tc.Run("R5900Context constructor pins vf0 to hardware (0,0,0,1)", [](TestCase &t)
{
R5900Context ctx; // default constructor is under test
alignas(16) float vf0[4]{};
_mm_storeu_ps(vf0, ctx.vu0_vf[0]); // vf0[0..3] = x,y,z,w
t.Equals(vf0[0], 0.0f, "vf0.x must be 0");
t.Equals(vf0[1], 0.0f, "vf0.y must be 0");
t.Equals(vf0[2], 0.0f, "vf0.z must be 0");
t.Equals(vf0[3], 1.0f, "vf0.w must be 1 (VU0 hardware homogeneous constant)");
});

tc.Run("PS2Runtime's own CPU context has vf0 pinned to hardware (0,0,0,1)", [](TestCase &t)
{
PS2Runtime runtime; // runtime constructor's memset/repin is under test
const R5900Context &ctx = runtime.cpu();
alignas(16) float vf0[4]{};
_mm_storeu_ps(vf0, ctx.vu0_vf[0]); // vf0[0..3] = x,y,z,w
t.Equals(vf0[0], 0.0f, "runtime vf0.x must be 0");
t.Equals(vf0[1], 0.0f, "runtime vf0.y must be 0");
t.Equals(vf0[2], 0.0f, "runtime vf0.z must be 0");
t.Equals(vf0[3], 1.0f, "runtime vf0.w must be 1 (VU0 hardware homogeneous constant)");
});
});
}