From 1f8a5876e672210eaca763b12f0faabe435d43ef Mon Sep 17 00:00:00 2001 From: GTTeancum Date: Tue, 1 Sep 2026 17:57:39 -0400 Subject: [PATCH] Handle GS privileged register SQ writes --- ps2xRuntime/src/lib/ps2_memory.cpp | 9 +++++++++ ps2xTest/src/ps2_memory_tests.cpp | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index 7cb2ba463..4cd11d76d 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -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(_mm_extract_epi64(value, 0))); + return; + } + if (scratch) { inRange(physAddr, sizeof(__m128i), PS2_SCRATCHPAD_SIZE, "write128 scratchpad", address); diff --git a/ps2xTest/src/ps2_memory_tests.cpp b/ps2xTest/src/ps2_memory_tests.cpp index 7c3e8ea5d..f8e2ac375 100644 --- a/ps2xTest/src/ps2_memory_tests.cpp +++ b/ps2xTest/src/ps2_memory_tests.cpp @@ -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(kUnusedUpperLane), + static_cast(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;