From 2c52b467c6f4ee3dc1f07e7c24d8ebb9b6fb212d Mon Sep 17 00:00:00 2001 From: Alexandros Mandravillis Date: Sun, 23 Aug 2026 19:43:23 +0300 Subject: [PATCH] recompiler: fold PSX main RAM dispatch mirrors (cherry picked from commit d0f3e950a9c0ddc080db7c8b74d7b24fba73435d) --- recompiler/src/bios_address_model.cpp | 8 ++++++++ recompiler/tests/bios_address_model_test.cpp | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/recompiler/src/bios_address_model.cpp b/recompiler/src/bios_address_model.cpp index d2d8cb0a..9c3d9259 100644 --- a/recompiler/src/bios_address_model.cpp +++ b/recompiler/src/bios_address_model.cpp @@ -105,6 +105,12 @@ BiosAddressModel BiosAddressModel::from_config(const BiosConfig& cfg) { uint32_t BiosAddressModel::normalize(uint32_t addr) const { uint32_t phys = addr & 0x1FFFFFFFu; + // The 2 MiB main RAM is mirrored four times across the first 8 MiB of + // physical address space. Dispatch keys must use the same hardware alias + // as ordinary memory reads, or a valid call through 0x80200000+ + // incorrectly becomes an unmapped-PC failure. + if (phys < 0x00800000u) + phys &= 0x001FFFFFu; // Sequential per-copy folds, same shape as the emitted normalize() — // equivalence to first-match-wins is enforced by from_config. for (const BiosAddrCopy& c : copies_) { @@ -217,6 +223,8 @@ std::string BiosAddressModel::emit_normalize_c() const { std::string out; out += "static uint32_t normalize(uint32_t addr) {\n"; out += " uint32_t phys = addr & 0x1FFFFFFFu;\n"; + out += " /* PSX main RAM: 2 MiB mirrored through physical 0x007FFFFF. */\n"; + out += " if (phys < 0x00800000u) phys &= 0x001FFFFFu;\n"; for (const BiosAddrCopy& c : copies_) { if (c.key_is_ram) { out += fmt::format(" /* {}: ROM 0x{:X}+ -> RAM 0x{:X}+ */\n", diff --git a/recompiler/tests/bios_address_model_test.cpp b/recompiler/tests/bios_address_model_test.cpp index 4e11395a..3f8a3cc2 100644 --- a/recompiler/tests/bios_address_model_test.cpp +++ b/recompiler/tests/bios_address_model_test.cpp @@ -160,6 +160,14 @@ int main(int argc, char** argv) { sweep(0xBFC00000u, 0xBFC80000u); // BIOS ROM KSEG1 (mask path) sweep(0x80000000u, 0x80200000u); // RAM KSEG0 (mask path) + // Main RAM aliases cover the first 8 MiB of physical space. Dispatch + // normalization must agree with memory access normalization at every + // mirror, including the exact 0x80200000 boundary seen in indirect calls. + expect_eq(model.normalize(0x00200000u), 0x00000000u, "normalize.ram_mirror", 0x00200000u); + expect_eq(model.normalize(0x007FFFFCu), 0x001FFFFCu, "normalize.ram_mirror", 0x007FFFFCu); + expect_eq(model.normalize(0x80200000u), 0x00000000u, "normalize.ram_mirror", 0x80200000u); + expect_eq(model.normalize(0xA07FFFFCu), 0x001FFFFCu, "normalize.ram_mirror", 0xA07FFFFCu); + // --- kbless / rom-keyed accessors match the SCPH1001 constants -------- if (!model.has_kbless()) { std::fprintf(stderr, "FAIL: no kbless window\n"); g_failures++; } expect_eq(model.kbless_ram_lo(), 0x500u, "kbless_ram_lo", 0);