runtime: add opt-in 8 MB main RAM mod

Adapt the 8 MB RAM work from the rbengine branch into a master-targeted change. The builtin package is disabled by default, activation requests expanded RAM before memory init, and runtime/state/digest/dispatch paths respect the live RAM map.
This commit is contained in:
Matthew Stanley
2026-08-27 21:33:40 -07:00
parent d62f4b4420
commit fa951645b4
17 changed files with 314 additions and 33 deletions
@@ -0,0 +1,17 @@
id = "psx.enhancement.8mb-ram"
version = "1.0.0"
name = "8 MB Main RAM"
author = "psxrecomp"
description = "Enables an opt-in 8 MB main RAM map for patches that require expanded PS1 memory. Disabled by default."
[[feature]]
id = "8mb-ram"
name = "8 MB Main RAM"
description = "Use expanded 8 MB main RAM instead of stock 2 MB mirroring. Both netplay peers must use the same setting."
category = "enhancement"
default_enabled = false
hidden = true
[[plugin]]
feature = "8mb-ram"
id = "psx.8mb-ram"
+6 -4
View File
@@ -2137,6 +2137,7 @@ void FullFunctionEmitter::emit_dispatch(
out += "extern int dirty_ram_dispatch(CPUState* cpu, uint32_t addr, uint32_t stop_addr);\n";
out += "extern int dirty_ram_is_dirty(uint32_t phys);\n";
out += "extern int psx_kernel_bless_dispatchable(uint32_t phys);\n";
out += "extern uint32_t psx_ram_canon_code_addr(uint32_t addr);\n";
out += "extern void fntrace_record(CPUState* cpu, uint32_t target);\n";
out += "extern uint64_t g_dispatch_static_hits;\n";
out += "\n";
@@ -2189,7 +2190,8 @@ void FullFunctionEmitter::emit_dispatch(
out += " * physical 0x30000-0x5AFFF. If the target belongs to the\n";
out += " * active game text range, route it through the game/dirty-RAM\n";
out += " * path before normalizing it to shell ROM. */\n";
out += " uint32_t game_phys = addr & 0x1FFFFFFFu;\n";
out += " uint32_t game_addr = psx_ram_canon_code_addr(addr);\n";
out += " uint32_t game_phys = game_addr & 0x1FFFFFFFu;\n";
out += " /* Class-A shell-window collision fix: post-game-start the BIOS shell\n";
out += " * copy at RAM 0x30000-0x5AFFF is DEAD (overwritten by the game EXE,\n";
out += " * its runtime-loaded overlays, or CD streaming), so normalize()->shell\n";
@@ -2233,13 +2235,13 @@ void FullFunctionEmitter::emit_dispatch(
out += fmt::format(
" game_phys >= 0x{:08X}u && game_phys <= 0x{:08X}u &&\n",
addr_model().rom_keyed_ram_lo(), addr_model().rom_keyed_ram_hi_incl());
out += " psx_game_address_in_text(addr) && dirty_ram_text_native_ok(game_phys);\n";
out += " psx_game_address_in_text(game_addr) && dirty_ram_text_native_ok(game_phys);\n";
} else {
out += " int game_text_in_shell_window = 0;\n";
}
out += " if (!found && (game_shell_overlap || game_text_in_shell_window ||\n";
out += " (psx_game_address_in_text(addr) && dirty_ram_is_dirty(game_phys)))) {\n";
out += " found = dirty_ram_dispatch(cpu, addr, stop_addr);\n";
out += " (psx_game_address_in_text(game_addr) && dirty_ram_is_dirty(game_phys)))) {\n";
out += " found = dirty_ram_dispatch(cpu, game_addr, stop_addr);\n";
out += " }\n";
out += "#endif\n";
out += " uint32_t phys = normalize(addr);\n";
+4 -1
View File
@@ -1482,7 +1482,8 @@ int main(int argc, char** argv) {
uint32_t game_text_start = exe->load_address() & 0x1FFFFFFFu;
uint32_t game_text_end = game_text_start + exe->code_size();
ds << "int psx_game_address_in_text(uint32_t addr) {\n";
ds << " uint32_t phys = addr & 0x1FFFFFFFu;\n";
ds << " extern uint32_t psx_ram_canon_code_addr(uint32_t);\n";
ds << " uint32_t phys = psx_ram_canon_code_addr(addr) & 0x1FFFFFFFu;\n";
ds << fmt::format(" return phys >= 0x{:08X}u && phys < 0x{:08X}u;\n",
game_text_start, game_text_end);
ds << "}\n\n";
@@ -1639,6 +1640,8 @@ int main(int argc, char** argv) {
ds << " * code to the interpreter. Compare the 29-bit physical address instead;\n";
ds << " * the table is sorted by the same masked key. */\n";
ds << "static const PsxGameDispatchEntry* psx_game_find_entry(uint32_t addr) {\n";
ds << " extern uint32_t psx_ram_canon_code_addr(uint32_t);\n";
ds << " addr = psx_ram_canon_code_addr(addr);\n";
ds << " const uint32_t want = addr & 0x1FFFFFFFu;\n";
if (indexed_lookup) {
ds << fmt::format(" const uint32_t offset = want - 0x{:08X}u;\n", lookup_lo);
+5
View File
@@ -146,6 +146,11 @@ if(BUILD_TESTING)
COMMAND psx_memory_geometry_${_ram_geometry}_test)
endforeach()
add_executable(psx_ram_runtime_map_test
tests/test_psx_ram_runtime_map.c)
target_include_directories(psx_ram_runtime_map_test PRIVATE include)
add_test(NAME psx_ram_runtime_map_test COMMAND psx_ram_runtime_map_test)
add_executable(host_keymap_test
tests/test_host_keymap.c
src/host_keymap.c)
+3 -2
View File
@@ -2,6 +2,7 @@
#define PSXRECOMP_MOD_MEMORY_H
#include <stdint.h>
#include "psx_ram.h"
#ifdef __cplusplus
extern "C" {
@@ -34,7 +35,7 @@ static inline int psx_mod_gpu_dma_aperture_offset_for(
}
/*
* Preserve the retail DMAC's 2 MiB folding unless the address is inside the
* Fold DMA addresses through live main RAM unless the address is inside the
* portion of the enhancement aperture that has actually been allocated.
*/
static inline uint32_t psx_mod_gpu_dma_resolve_address_for(
@@ -43,7 +44,7 @@ static inline uint32_t psx_mod_gpu_dma_resolve_address_for(
if (psx_mod_gpu_dma_aperture_offset_for(
canonical, 4u, used, (uint32_t *)0))
return canonical;
return canonical & 0x001FFFFCu;
return psx_ram_map_read(canonical) & ~3u;
}
uint32_t psx_mod_gpu_dma_memory_alloc(uint32_t size, uint32_t alignment);
+7
View File
@@ -84,6 +84,13 @@ uint32_t psx_mod_alloc_guest_memory(uint32_t size, uint32_t alignment);
*/
uint32_t psx_mod_alloc_gpu_dma_memory(uint32_t size, uint32_t alignment);
/*
* Opt into expanded 8 MiB main RAM for this launch. Default runtime behavior
* remains stock 2 MiB mirroring unless a trusted activation plugin requests
* this before memory_init().
*/
int psx_mod_set_main_ram_8mb(int enabled);
/* Current per-side widescreen reveal in native game pixels (zero at 4:3). */
int32_t psx_mod_widescreen_x_margin(void);
+3 -2
View File
@@ -34,6 +34,7 @@
#endif
#include "cpu_state.h" /* CPUState (guard-safe: cpu_state.h includes us last) */
#include "psx_cycles.h" /* inline psx_advance_cycles */
#include "psx_ram.h"
#ifdef __cplusplus
extern "C" {
@@ -250,7 +251,7 @@ static inline uint32_t psx_cyc_load_word(CPUState* cpu, uint32_t addr,
cpu->ld_which_t = (uint8_t)rt;
}
uint32_t value;
memcpy(&value, g_psx_ram + (phys & 0x1FFFFFu), sizeof(value));
memcpy(&value, g_psx_ram + psx_ram_map_read(phys), sizeof(value));
return value;
}
return psx_cyc_load_word_slow(cpu, addr, rt, reg_mask);
@@ -280,7 +281,7 @@ static inline uint16_t psx_cyc_load_half(CPUState* cpu, uint32_t addr,
cpu->ld_which_t = (uint8_t)rt;
}
uint16_t value;
memcpy(&value, g_psx_ram + (phys & 0x1FFFFFu), sizeof(value));
memcpy(&value, g_psx_ram + psx_ram_map_read(phys), sizeof(value));
return value;
}
return psx_cyc_load_half_slow(cpu, addr, rt, reg_mask);
+75
View File
@@ -0,0 +1,75 @@
#ifndef PSX_RAM_H
#define PSX_RAM_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Retail DRAM is 2 MiB, mirrored 4x across the 8 MiB decode window. The
* opt-in 8 MB RAM enhancement keeps an 8 MiB host backing and switches the
* live map to unique high pages only after a trusted package requests it.
*/
#define PSX_RAM_2MB 0x00200000u
#define PSX_RAM_8MB 0x00800000u
#define PSX_RAM_CAPACITY PSX_RAM_8MB
#define PSX_RAM_WINDOW 0x00800000u
extern uint32_t g_psx_ram_size;
extern uint32_t g_psx_ram_mask;
uint32_t memory_get_ram_bytes(void);
int psx_ram_8mb_active(void);
void psx_ram_reset_size_request(void);
#define PSX_RAM_HIGH_PAGE0 (PSX_RAM_2MB >> 12)
#define PSX_RAM_HIGH_PAGES ((PSX_RAM_8MB - PSX_RAM_2MB) >> 12)
#define PSX_RAM_HIGH_BITWORDS ((PSX_RAM_HIGH_PAGES + 31u) / 32u)
extern uint32_t g_psx_ram_high_unique[PSX_RAM_HIGH_BITWORDS];
static inline int psx_ram_high_page_unique(uint32_t page) {
uint32_t i, bit;
if (page < PSX_RAM_HIGH_PAGE0 || page >= (PSX_RAM_8MB >> 12))
return 1;
i = page - PSX_RAM_HIGH_PAGE0;
bit = 1u << (i & 31u);
return (g_psx_ram_high_unique[i >> 5] & bit) != 0;
}
static inline uint32_t psx_ram_map_read(uint32_t phys) {
phys &= 0x1FFFFFFFu;
if (phys >= PSX_RAM_WINDOW)
return phys;
if (g_psx_ram_size <= PSX_RAM_2MB)
return phys & (PSX_RAM_2MB - 1u);
if (phys < PSX_RAM_2MB)
return phys;
if (psx_ram_high_page_unique(phys >> 12))
return phys;
return phys & (PSX_RAM_2MB - 1u);
}
static inline uint32_t psx_ram_map_write(uint32_t phys) {
return psx_ram_map_read(phys);
}
static inline uint32_t psx_ram_canon_code_addr_inline(uint32_t addr) {
uint32_t seg = addr & 0xE0000000u;
uint32_t phys = addr & 0x1FFFFFFFu;
if (phys < PSX_RAM_WINDOW && phys >= PSX_RAM_2MB &&
!psx_ram_high_page_unique(phys >> 12))
phys &= (PSX_RAM_2MB - 1u);
return seg | phys;
}
void psx_ram_register_unique(uint32_t addr, uint32_t len);
uint32_t psx_ram_canon_code_addr(uint32_t addr);
void psx_ram_resync_high_after_restore(void);
#ifdef __cplusplus
}
#endif
#endif /* PSX_RAM_H */
+1
View File
@@ -415,6 +415,7 @@ set(PSXRECOMP_RUNTIME_SOURCES
${PSXRECOMP_ROOT}/runtime/src/mod_builtin_speed.c
${PSXRECOMP_ROOT}/runtime/src/mod_builtin_pgxp.c
${PSXRECOMP_ROOT}/runtime/src/mod_builtin_bezel.c
${PSXRECOMP_ROOT}/runtime/src/mod_builtin_ram.c
${PSXRECOMP_ROOT}/runtime/src/mod_packages.cpp
${PSXRECOMP_ROOT}/runtime/src/mod_runtime.cpp
${PSXRECOMP_ROOT}/runtime/src/mod_texture_banks.c
+9 -7
View File
@@ -9,6 +9,7 @@
#include "interrupts.h"
#include "psx_cycles.h"
#include "psx_icache.h" /* g_psx_icache_tv — fetch-cost tags in BS_SEC_ICACHE */
#include "psx_ram.h"
#include "pst_wire.h"
#include <stdint.h>
#include <stdio.h>
@@ -38,7 +39,6 @@ static double boot_state_mono_ms(void) {
/* Compress payloads at/above this size (RAM/VRAM/SPU/dirty dominate I/O). */
#define BOOT_STATE_ZLIB_MIN 256u
#define RAM_SIZE (2u * 1024u * 1024u)
#define SPAD_SIZE (1024u)
#define VRAM_W 1024
#define VRAM_H 512
@@ -46,6 +46,7 @@ static double boot_state_mono_ms(void) {
/* ---- core accessors (existing runtime modules) ---- */
extern uint8_t* memory_get_ram_ptr(void);
extern uint32_t memory_get_ram_bytes(void);
extern uint8_t* memory_get_scratchpad_ptr(void);
extern uint32_t i_stat;
extern uint32_t i_mask;
@@ -377,7 +378,7 @@ static int boot_state_save_to(BsOut* o, const CPUState* cpu,
ok = write_header_le(o, &h);
if (ok) ok = write_cpu_section(o, cpu);
if (ok) ok = write_section(o, BS_SEC_RAM, memory_get_ram_ptr(), RAM_SIZE);
if (ok) ok = write_section(o, BS_SEC_RAM, memory_get_ram_ptr(), memory_get_ram_bytes());
if (ok) ok = write_section(o, BS_SEC_SPAD, memory_get_scratchpad_ptr(), SPAD_SIZE);
if (ok && psx_mod_memory_snapshot_bytes())
ok = write_module_section(o, BS_SEC_MODMEM, psx_mod_memory_snapshot_bytes,
@@ -490,8 +491,8 @@ static int boot_state_save_buffer_ex(const CPUState* cpu, uint32_t bios_checksum
*out_len = 0;
memset(&o, 0, sizeof o);
o.no_zlib = no_zlib ? 1 : 0;
/* Compressed MotK ~1.3–1.5 MiB; raw ~3.5–4 MiB (RAM+VRAM+SPU). */
o.cap = no_zlib ? (5u * 1024u * 1024u) : (2u * 1024u * 1024u);
/* Compressed MotK ~1.3-1.5 MiB; raw ~3.5-4 MiB (2 MB RAM) or ~9.5 MiB (8 MB). */
o.cap = no_zlib ? (12u * 1024u * 1024u) : (2u * 1024u * 1024u);
o.data = (uint8_t*)malloc(o.cap);
if (!o.data) return 0;
if (!boot_state_save_to(&o, cpu, bios_checksum, entry_pc)) {
@@ -544,11 +545,12 @@ static int apply_section(uint32_t tag, const uint8_t* p, uint32_t len,
return 1;
}
case BS_SEC_RAM:
if (len != RAM_SIZE) return 0;
memcpy(memory_get_ram_ptr(), p, RAM_SIZE);
if (len != memory_get_ram_bytes()) return 0;
memcpy(memory_get_ram_ptr(), p, memory_get_ram_bytes());
{
extern void psx_kernel_bless_note_range(uint32_t phys, uint32_t l);
psx_kernel_bless_note_range(0, RAM_SIZE);
psx_kernel_bless_note_range(0, memory_get_ram_bytes());
psx_ram_resync_high_after_restore();
}
return 1;
case BS_SEC_SPAD:
+4
View File
@@ -30,6 +30,7 @@
#include "psx_cycles.h"
#include "psx_icache.h"
#include "psx_instr_cost.h" /* psx_instr_base_cycles — single-source cycle cost */
#include "psx_ram.h"
#include "gpu.h" /* psx_ws_is_backdrop_site / psx_ws_backdrop_x (interp hook) */
#include "ws_backdrop_detect.h" /* shared backdrop-window detector (auto_backdrop) */
#include "lockstep.h"
@@ -2382,6 +2383,9 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_
int dirty_ram_dispatch(CPUState* cpu, uint32_t addr, uint32_t stop_addr) {
extern int g_psx_dispatch_depth;
extern void psx_fatal_halt(const char *reason);
addr = psx_ram_canon_code_addr(addr);
if (stop_addr != 0u)
stop_addr = psx_ram_canon_code_addr(stop_addr);
#ifndef PSX_NO_DEBUG_TOOLS
/* A0/B0/C0 kernel-vector stubs are runtime-written, so calls to them
* land HERE, not in the static dispatcher — which meant the bioscall
+118 -14
View File
@@ -25,13 +25,14 @@
#include "dirty_ram_interp.h"
#include "guest_tty.h"
#include "psx_cycles.h"
#include "psx_ram.h"
#include "starvation_ring.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RAM_SIZE (2 * 1024 * 1024)
#define RAM_SIZE PSX_RAM_CAPACITY
#define SCRATCHPAD_SIZE 1024
#define BIOS_ROM_SIZE (512 * 1024)
#define MOD_MEMORY_BASE 0x1F000000u
@@ -44,6 +45,66 @@ static uint8_t mod_memory[MOD_MEMORY_SIZE];
static uint32_t mod_memory_used;
static uint8_t mod_gpu_dma_memory[PSX_MOD_GPU_DMA_APERTURE_SIZE];
static uint32_t mod_gpu_dma_memory_used;
static int s_ram_8mb_requested;
uint32_t g_psx_ram_size = PSX_RAM_2MB;
uint32_t g_psx_ram_mask = PSX_RAM_2MB - 1u;
static uint32_t s_ram_high_registered[PSX_RAM_HIGH_BITWORDS];
uint32_t g_psx_ram_high_unique[PSX_RAM_HIGH_BITWORDS];
static inline void psx_ram_high_page_mark(uint32_t page) {
uint32_t i, bit;
if (page < PSX_RAM_HIGH_PAGE0 || page >= (PSX_RAM_8MB >> 12))
return;
i = page - PSX_RAM_HIGH_PAGE0;
bit = 1u << (i & 31u);
g_psx_ram_high_unique[i >> 5] |= bit;
s_ram_high_registered[i >> 5] |= bit;
}
static void psx_ram_apply_registered_bitmap(void) {
memcpy(g_psx_ram_high_unique, s_ram_high_registered,
sizeof(g_psx_ram_high_unique));
}
void psx_ram_register_unique(uint32_t addr, uint32_t len) {
uint32_t phys, end, page, last;
if (len == 0u)
return;
phys = addr & 0x1FFFFFFFu;
if (phys >= PSX_RAM_WINDOW)
return;
end = phys + len;
if (end < phys || end > PSX_RAM_WINDOW)
end = PSX_RAM_WINDOW;
if (end <= PSX_RAM_2MB)
return;
if (phys < PSX_RAM_2MB)
phys = PSX_RAM_2MB;
page = phys >> 12;
last = (end - 1u) >> 12;
for (; page <= last; page++)
psx_ram_high_page_mark(page);
}
uint32_t psx_ram_canon_code_addr(uint32_t addr) {
return psx_ram_canon_code_addr_inline(addr);
}
void psx_ram_resync_high_after_restore(void) {
uint32_t page;
psx_ram_apply_registered_bitmap();
if (g_psx_ram_size <= PSX_RAM_2MB)
return;
for (page = PSX_RAM_HIGH_PAGE0; page < (PSX_RAM_8MB >> 12); page++) {
uint32_t dst = page << 12;
uint32_t src = dst & (PSX_RAM_2MB - 1u);
if (psx_ram_high_page_unique(page))
continue;
if (memcmp(ram + dst, ram + src, 4096u) != 0)
psx_ram_high_page_mark(page);
}
}
uint32_t psx_mod_memory_snapshot_bytes(void) {
return mod_memory_used || mod_gpu_dma_memory_used
@@ -139,22 +200,63 @@ uint8_t *g_psx_ram = ram;
/* PSX_LOAD_DELAY gate (default on). −1 = unread; 0/1 after first resolve. */
int g_psx_load_delay = -1;
/* Physical address translation for guest accesses. The 2 MB main RAM is
* mirrored 4x across the first 8 MB of each segment (mem-ctrl RAM_SIZE
* register, default 0x0B88) and games rely on it — Kula World's crt0
* parks $sp in the 4th mirror (0x807FFFF8). Fold the mirrors here so the
* RAM bounds checks below see canonical offsets; without this, mirror
* writes fell into the open-bus no-op and mirror reads returned 0 (the
* guest's stack silently vanished and $ra came back as 0). */
/* Physical address translation for guest accesses. Retail 2 MB DRAM is
* mirrored across the first 8 MB of each segment; the opt-in 8 MB mode maps
* registered high pages uniquely and keeps unregistered high pages aliased. */
static inline uint32_t psx_phys_addr(uint32_t addr) {
uint32_t phys = addr & 0x1FFFFFFFu;
if (phys < 0x00800000u) phys &= (uint32_t)(RAM_SIZE - 1);
if (phys < PSX_RAM_WINDOW) phys = psx_ram_map_read(phys);
return phys;
}
static inline uint32_t psx_phys_addr_store(uint32_t addr) {
uint32_t phys = addr & 0x1FFFFFFFu;
if (phys < PSX_RAM_WINDOW) phys = psx_ram_map_write(phys);
return phys;
}
/* Expose RAM pointer for oracle comparison (find_first_divergence). */
uint8_t *memory_get_ram_ptr(void) { return ram; }
uint8_t *memory_get_scratchpad_ptr(void) { return scratchpad; }
uint32_t memory_get_ram_bytes(void) { return g_psx_ram_size; }
int psx_ram_8mb_active(void) { return g_psx_ram_size > PSX_RAM_2MB; }
void psx_ram_reset_size_request(void) {
s_ram_8mb_requested = 0;
memset(s_ram_high_registered, 0, sizeof(s_ram_high_registered));
memset(g_psx_ram_high_unique, 0, sizeof(g_psx_ram_high_unique));
}
int psx_mod_set_main_ram_8mb(int enabled) {
s_ram_8mb_requested = enabled ? 1 : 0;
memset(s_ram_high_registered, 0, sizeof(s_ram_high_registered));
memset(g_psx_ram_high_unique, 0, sizeof(g_psx_ram_high_unique));
if (enabled)
psx_ram_register_unique(PSX_RAM_2MB, PSX_RAM_8MB - PSX_RAM_2MB);
return 1;
}
static int psx_ram_any_high_registered(void) {
uint32_t i;
for (i = 0; i < PSX_RAM_HIGH_BITWORDS; i++) {
if (s_ram_high_registered[i] != 0u)
return 1;
}
return 0;
}
static void psx_ram_apply_size_request(void) {
if (s_ram_8mb_requested) {
g_psx_ram_size = PSX_RAM_8MB;
g_psx_ram_mask = PSX_RAM_8MB - 1u;
if (!psx_ram_any_high_registered())
psx_ram_register_unique(PSX_RAM_2MB, PSX_RAM_8MB - PSX_RAM_2MB);
} else {
g_psx_ram_size = PSX_RAM_2MB;
g_psx_ram_mask = PSX_RAM_2MB - 1u;
memset(g_psx_ram_high_unique, 0, sizeof(g_psx_ram_high_unique));
}
}
void memory_clear_low_boot_scratch(void) {
memset(ram, 0, 0x10u);
@@ -771,6 +873,7 @@ uint32_t dirty_ram_get_bitmap_word_count(void) {
}
void dirty_ram_set_bitmap_words(const uint32_t* words, uint32_t count) {
memset(dirty_ram_bitmap, 0, sizeof(dirty_ram_bitmap));
if (count > DIRTY_RAM_BITMAP_WORDS) count = DIRTY_RAM_BITMAP_WORDS;
for (uint32_t i = 0; i < count; i++)
dirty_ram_bitmap[i] = words[i];
@@ -1103,6 +1206,7 @@ static uint32_t s_bios_checksum = 0;
uint32_t memory_get_bios_checksum(void) { return s_bios_checksum; }
void memory_init(const char* bios_path) {
psx_ram_apply_size_request();
memset(ram, 0, sizeof(ram));
memset(scratchpad, 0, sizeof(scratchpad));
/* Rematch re-enters without process exit — wipe sticky I/O regs that
@@ -1712,7 +1816,7 @@ static void psx_write_word_raw(uint32_t addr, uint32_t val) {
* We have no cache model, so silently discard RAM/scratchpad writes. */
if (sr_ptr && (*sr_ptr & 0x10000u)) return;
uint32_t phys = psx_phys_addr(addr);
uint32_t phys = psx_phys_addr_store(addr);
/* The generated BIOS mirrors its exception trampoline to 0x80000000 during
* boot. On hardware this mirror copy is not visible in RAM; only the real
@@ -1906,7 +2010,7 @@ static void psx_write_half_raw(uint32_t addr, uint16_t val) {
/* KSEG2 guard — see psx_read_word_raw. */
if (addr >= 0xC0000000u) { g_kseg2_ignored_writes++; return; }
uint32_t phys = psx_phys_addr(addr);
uint32_t phys = psx_phys_addr_store(addr);
if (phys < RAM_SIZE) {
debug_server_trace_write_check(phys, (uint32_t)read_ram_half(phys), (uint32_t)val, 2);
@@ -2166,10 +2270,10 @@ static inline int psx_cyc_main_ram_fast_addr(uint32_t addr, uint32_t width,
return 0;
uint32_t phys = addr & 0x1FFFFFFFu;
if (phys >= 0x00800000u) return 0;
phys &= (uint32_t)(RAM_SIZE - 1);
phys = psx_ram_map_read(phys);
/* Aligned guest loads cannot cross this boundary, but fail closed for a
* malformed/unaligned caller instead of introducing a host OOB read. */
if (phys > (uint32_t)RAM_SIZE - width) return 0;
if (phys > g_psx_ram_size - width) return 0;
*phys_out = phys;
return 1;
}
@@ -2241,7 +2345,7 @@ static void psx_write_byte_raw(uint32_t addr, uint8_t val) {
/* KSEG2 guard — see psx_read_word_raw. */
if (addr >= 0xC0000000u) { g_kseg2_ignored_writes++; return; }
uint32_t phys = psx_phys_addr(addr);
uint32_t phys = psx_phys_addr_store(addr);
if (phys < RAM_SIZE) {
debug_server_trace_write_check(phys, (uint32_t)ram[phys], (uint32_t)val, 1);
+16
View File
@@ -0,0 +1,16 @@
/*
* Framework-owned opt-in 8 MB main RAM.
*
* Retail PS1 DRAM is 2 MiB mirrored four times across 0x00000000-0x007FFFFF.
* Some enhancement patches use the upper part of that window as real RAM.
*/
#include "mod_plugins.h"
static void builtin_8mb_ram_activate(void) {
(void)psx_mod_set_main_ram_8mb(1);
}
PSX_MOD_CONSTRUCTOR(psx_register_builtin_ram_plugins) {
(void)psx_mod_register_activation_plugin(
"psx.8mb-ram", builtin_8mb_ram_activate);
}
+2
View File
@@ -5,6 +5,7 @@
#include "mod_packages.h"
#include "mod_plugins.h"
#include "gpu.h"
#include "psx_ram.h"
#include "psx_sha256.h"
#if defined(RECOMP_LAUNCHER)
@@ -1337,6 +1338,7 @@ extern "C" int psx_mod_read_disc_file(const char* path, void* buffer,
extern "C" void mod_runtime_activate_plugins(void) {
using namespace PSXRecompV4;
RuntimeMods& s = state();
psx_ram_reset_size_request();
if (!s.initialized || !s.plan.ok) return;
for (const ModResolution::Plugin& plugin : s.plan.plugins) {
s.current_plugin = &plugin;
+2 -3
View File
@@ -12,6 +12,7 @@
#include <string.h>
extern uint8_t* memory_get_ram_ptr(void);
extern uint32_t memory_get_ram_bytes(void);
extern uint32_t i_stat;
extern uint32_t i_mask;
extern void timers_get_snapshot(uint16_t counter[3], uint32_t mode[3],
@@ -55,8 +56,6 @@ static uint32_t digest_module(uint32_t (*bytes_fn)(void), void (*write_fn)(uint8
return crc32_compute(buf, n);
}
#define NP_RAM_SIZE (2u * 1024u * 1024u)
uint32_t netplay_cdrom_digest(void)
{
static uint8_t *buf;
@@ -159,7 +158,7 @@ void netplay_core_digest_parts(const CPUState* cpu, NetplayCoreParts* out)
ram = memory_get_ram_ptr();
if (ram)
crc_ram = crc32_update(crc_ram, ram, NP_RAM_SIZE);
crc_ram = crc32_update(crc_ram, ram, memory_get_ram_bytes());
wc = dirty_ram_get_bitmap_word_count();
for (i = 0; i < wc; i++) {
+1
View File
@@ -60,6 +60,7 @@ extern "C" uint32_t psx_mod_memory_alloc(uint32_t, uint32_t) { return 0; }
extern "C" uint32_t psx_mod_gpu_dma_memory_alloc(uint32_t, uint32_t) {
return 0;
}
extern "C" void psx_ram_reset_size_request(void) {}
extern "C" int psx_ws_x_margin(void) { return 0; }
/* Stand-in for the GPU's display geometry. psx_mod_display_width/height must
+41
View File
@@ -0,0 +1,41 @@
#include "psx_ram.h"
#include <stdint.h>
#include <stdio.h>
uint32_t g_psx_ram_size = PSX_RAM_2MB;
uint32_t g_psx_ram_mask = PSX_RAM_2MB - 1u;
uint32_t g_psx_ram_high_unique[PSX_RAM_HIGH_BITWORDS];
static int check(int cond, const char *label) {
if (!cond) {
fprintf(stderr, "FAIL: %s\n", label);
return 0;
}
return 1;
}
int main(void) {
const uint32_t high = 0x00201000u;
const uint32_t high_page = high >> 12;
uint32_t i = high_page - PSX_RAM_HIGH_PAGE0;
int ok = 1;
ok &= check(psx_ram_map_read(high) == 0x00001000u,
"retail maps high mirror to low RAM");
ok &= check(psx_ram_canon_code_addr_inline(0x80201000u) == 0x80001000u,
"retail folds high code PC to low mirror");
g_psx_ram_size = PSX_RAM_8MB;
g_psx_ram_mask = PSX_RAM_8MB - 1u;
ok &= check(psx_ram_map_read(high) == 0x00001000u,
"unregistered 8 MB high page still mirrors low RAM");
g_psx_ram_high_unique[i >> 5] |= 1u << (i & 31u);
ok &= check(psx_ram_map_read(high) == high,
"registered 8 MB high page maps uniquely");
ok &= check(psx_ram_canon_code_addr_inline(0x80201000u) == 0x80201000u,
"registered high code PC remains unique");
return ok ? 0 : 1;
}