Skip to content
Open
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
9 changes: 9 additions & 0 deletions ps2xRuntime/src/lib/ps2_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,15 @@ void PS2Memory::write128(uint32_t address, __m128i value)
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);

if (isGsPrivReg(physAddr))
{
// GS privileged registers occupy the low 64 bits of 16-byte EE bus
// slots. SQ stores are used by some games for display register updates;
// the upper lane addresses padding and must not spill into the next slot.
write64(address, static_cast<uint64_t>(_mm_extract_epi64(value, 0)));
return;
}

if (scratch)
{
inRange(physAddr, sizeof(__m128i), PS2_SCRATCHPAD_SIZE, "write128 scratchpad", address);
Expand Down
22 changes: 22 additions & 0 deletions ps2xTest/src/ps2_memory_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,28 @@ void register_ps2_memory_tests()
t.Equals(mem.translateAddress(PS2_SCRATCHPAD_ALIAS_BASE + 0x123u), 0x123u, "0xF000 scratchpad alias should translate to local offset");
});

tc.Run("quadword stores update one GS privileged register slot", [](TestCase &t)
{
PS2Memory mem;
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");

constexpr uint32_t kDispfb1 = PS2_GS_PRIV_REG_BASE + 0x70u;
constexpr uint32_t kDisplay1 = PS2_GS_PRIV_REG_BASE + 0x80u;
constexpr uint64_t kDispfbValue = 0x0123456789ABCDEFull;
constexpr uint64_t kUnusedUpperLane = 0xFEDCBA9876543210ull;
const __m128i value = _mm_set_epi64x(
static_cast<int64_t>(kUnusedUpperLane),
static_cast<int64_t>(kDispfbValue));
const uint64_t display1Before = mem.read64(kDisplay1);

mem.write128(kDispfb1, value);

t.Equals(mem.read64(kDispfb1), kDispfbValue,
"SQ should write the low lane to the GS register");
t.Equals(mem.read64(kDisplay1), display1Before,
"SQ upper lane should not spill into the next GS register slot");
});

tc.Run("EE timer0 count advances from scheduler cycles and can be reset", [](TestCase &t)
{
PS2Memory mem;
Expand Down