From 71fbb2130b5ef0767468d6b027984cecbf423077 Mon Sep 17 00:00:00 2001 From: rain Date: Tue, 13 Oct 2026 10:34:34 -0400 Subject: [PATCH 1/3] bios module: map mod guest-function pair through the callback table (ABI 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dispatch template emitted since mod-guest-functions landed declares extern g_psx_mod_guest_functions / psx_mod_dispatch_guest_function in every BIOS dispatcher, but the module name-mapping block and PsxBiosModuleCallbacks never carried them, so a freshly built retail backend (e.g. SCPH1001) failed RTLD_NOW with an undefined symbol — both in the builder self-check and in the real dlopen. Add the two mappings, table entries, glue dummy/forwarder, and host wiring; bump the ABI so stale caches cannot load. Verified: SCPH-1001 module builds, self-checks, and boots SLPS-00756 on Linux/gcc. --- overlay_toolchain/include/psx_bios_module.h | 10 +++++++++- overlay_toolchain/include/psx_bios_module_glue.c.inc | 9 +++++++++ runtime/include/psx_bios_module.h | 10 +++++++++- runtime/include/psx_bios_module_glue.c.inc | 9 +++++++++ runtime/src/psx_bios_module.c | 4 ++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/overlay_toolchain/include/psx_bios_module.h b/overlay_toolchain/include/psx_bios_module.h index 5170dfbe..a092e8eb 100644 --- a/overlay_toolchain/include/psx_bios_module.h +++ b/overlay_toolchain/include/psx_bios_module.h @@ -72,6 +72,8 @@ extern uint64_t *g_psx_bm_bail_flattened_p; # define g_psx_bail_flattened (*g_psx_bm_bail_flattened_p) # define g_psx_ram_mask (*g_psx_bm_ram_mask_p) # define s_frame_count (*g_psx_bm_frame_count_p) +# define g_psx_mod_guest_functions (*g_psx_bm_mod_guest_functions_p) +# define psx_mod_dispatch_guest_function g_psx_bm_mod_dispatch_guest #endif #ifdef __cplusplus @@ -82,7 +84,7 @@ struct CPUState; struct OverlayCallbacks; struct PsxBiosBackend; -#define PSX_BIOS_MODULE_ABI_VERSION 1 +#define PSX_BIOS_MODULE_ABI_VERSION 2 /* Combined tag: ABI version (low 16) | codegen flavor (high 16), like * PSX_OVERLAY_ABI_TAG. The flavor comes from the same -DPSX_OVERLAY_FLAVOR @@ -130,6 +132,12 @@ typedef struct PsxBiosModuleCallbacks { uint64_t *bail_flattened; /* traps.c */ uint32_t *ram_mask; /* psx_ram_geometry.c (live 2/8 MiB) */ uint64_t *frame_count; /* debug_server.c s_frame_count (miss records) */ + /* Mod-defined guest function table (mod_runtime.cpp). The generated + * dispatch consults the registration count at the top of every + * iteration. Added in ABI 2 alongside the emitter's mod-guest-function + * template. */ + uint32_t *mod_guest_functions; /* registration count */ + int (*mod_dispatch_guest_function)(struct CPUState *cpu, uint32_t addr); } PsxBiosModuleCallbacks; /* ---- Host side (runtime/src/psx_bios_module.c) ----------------------------- */ diff --git a/overlay_toolchain/include/psx_bios_module_glue.c.inc b/overlay_toolchain/include/psx_bios_module_glue.c.inc index e43e57cd..3cfbdb26 100644 --- a/overlay_toolchain/include/psx_bios_module_glue.c.inc +++ b/overlay_toolchain/include/psx_bios_module_glue.c.inc @@ -34,6 +34,7 @@ static uint32_t s_func_addr_dummy; static uint64_t s_flattened_dummy; static uint32_t s_ram_mask_dummy = 0x1FFFFFu; static uint64_t s_frame_count_dummy; +static uint32_t s_mod_guest_count_dummy; /* The pointers the generated C declares (via the psx_bios_module.h name * mappings): `extern int g_psx_dispatch_depth;` became @@ -45,6 +46,7 @@ uint32_t *g_psx_bm_current_func_addr_p = &s_func_addr_dummy; uint64_t *g_psx_bm_bail_flattened_p = &s_flattened_dummy; uint32_t *g_psx_bm_ram_mask_p = &s_ram_mask_dummy; uint64_t *g_psx_bm_frame_count_p = &s_frame_count_dummy; +uint32_t *g_psx_bm_mod_guest_functions_p = &s_mod_guest_count_dummy; /* ---- Forwarders for what OverlayCallbacks does not carry ----------------- */ void psx_arith_overflow(CPUState *cpu) { @@ -97,6 +99,12 @@ void psx_segment_miss_record_kind(uint32_t addr, uint32_t home, uint32_t ra, g_bcbs.segment_miss_record_kind(addr, home, ra, sp, frame, kind); } +int g_psx_bm_mod_dispatch_guest(CPUState *cpu, uint32_t addr) { + overlay_flush_cycles(); + return g_bcbs.mod_dispatch_guest_function + ? g_bcbs.mod_dispatch_guest_function(cpu, addr) : 0; +} + /* ---- Exports ------------------------------------------------------------- */ PSX_OVERLAY_EXPORT int psx_bios_module_abi(void) { return PSX_BIOS_MODULE_ABI_TAG; @@ -120,6 +128,7 @@ PSX_OVERLAY_EXPORT int psx_bios_module_init(const OverlayCallbacks *cbs, if (g_bcbs.bail_flattened) g_psx_bm_bail_flattened_p = g_bcbs.bail_flattened; if (g_bcbs.ram_mask) g_psx_bm_ram_mask_p = g_bcbs.ram_mask; if (g_bcbs.frame_count) g_psx_bm_frame_count_p = g_bcbs.frame_count; + if (g_bcbs.mod_guest_functions) g_psx_bm_mod_guest_functions_p = g_bcbs.mod_guest_functions; return 1; } PSX_OVERLAY_EXPORT const PsxBiosBackend *psx_bios_module_backend(void) { diff --git a/runtime/include/psx_bios_module.h b/runtime/include/psx_bios_module.h index 5170dfbe..a092e8eb 100644 --- a/runtime/include/psx_bios_module.h +++ b/runtime/include/psx_bios_module.h @@ -72,6 +72,8 @@ extern uint64_t *g_psx_bm_bail_flattened_p; # define g_psx_bail_flattened (*g_psx_bm_bail_flattened_p) # define g_psx_ram_mask (*g_psx_bm_ram_mask_p) # define s_frame_count (*g_psx_bm_frame_count_p) +# define g_psx_mod_guest_functions (*g_psx_bm_mod_guest_functions_p) +# define psx_mod_dispatch_guest_function g_psx_bm_mod_dispatch_guest #endif #ifdef __cplusplus @@ -82,7 +84,7 @@ struct CPUState; struct OverlayCallbacks; struct PsxBiosBackend; -#define PSX_BIOS_MODULE_ABI_VERSION 1 +#define PSX_BIOS_MODULE_ABI_VERSION 2 /* Combined tag: ABI version (low 16) | codegen flavor (high 16), like * PSX_OVERLAY_ABI_TAG. The flavor comes from the same -DPSX_OVERLAY_FLAVOR @@ -130,6 +132,12 @@ typedef struct PsxBiosModuleCallbacks { uint64_t *bail_flattened; /* traps.c */ uint32_t *ram_mask; /* psx_ram_geometry.c (live 2/8 MiB) */ uint64_t *frame_count; /* debug_server.c s_frame_count (miss records) */ + /* Mod-defined guest function table (mod_runtime.cpp). The generated + * dispatch consults the registration count at the top of every + * iteration. Added in ABI 2 alongside the emitter's mod-guest-function + * template. */ + uint32_t *mod_guest_functions; /* registration count */ + int (*mod_dispatch_guest_function)(struct CPUState *cpu, uint32_t addr); } PsxBiosModuleCallbacks; /* ---- Host side (runtime/src/psx_bios_module.c) ----------------------------- */ diff --git a/runtime/include/psx_bios_module_glue.c.inc b/runtime/include/psx_bios_module_glue.c.inc index e43e57cd..3cfbdb26 100644 --- a/runtime/include/psx_bios_module_glue.c.inc +++ b/runtime/include/psx_bios_module_glue.c.inc @@ -34,6 +34,7 @@ static uint32_t s_func_addr_dummy; static uint64_t s_flattened_dummy; static uint32_t s_ram_mask_dummy = 0x1FFFFFu; static uint64_t s_frame_count_dummy; +static uint32_t s_mod_guest_count_dummy; /* The pointers the generated C declares (via the psx_bios_module.h name * mappings): `extern int g_psx_dispatch_depth;` became @@ -45,6 +46,7 @@ uint32_t *g_psx_bm_current_func_addr_p = &s_func_addr_dummy; uint64_t *g_psx_bm_bail_flattened_p = &s_flattened_dummy; uint32_t *g_psx_bm_ram_mask_p = &s_ram_mask_dummy; uint64_t *g_psx_bm_frame_count_p = &s_frame_count_dummy; +uint32_t *g_psx_bm_mod_guest_functions_p = &s_mod_guest_count_dummy; /* ---- Forwarders for what OverlayCallbacks does not carry ----------------- */ void psx_arith_overflow(CPUState *cpu) { @@ -97,6 +99,12 @@ void psx_segment_miss_record_kind(uint32_t addr, uint32_t home, uint32_t ra, g_bcbs.segment_miss_record_kind(addr, home, ra, sp, frame, kind); } +int g_psx_bm_mod_dispatch_guest(CPUState *cpu, uint32_t addr) { + overlay_flush_cycles(); + return g_bcbs.mod_dispatch_guest_function + ? g_bcbs.mod_dispatch_guest_function(cpu, addr) : 0; +} + /* ---- Exports ------------------------------------------------------------- */ PSX_OVERLAY_EXPORT int psx_bios_module_abi(void) { return PSX_BIOS_MODULE_ABI_TAG; @@ -120,6 +128,7 @@ PSX_OVERLAY_EXPORT int psx_bios_module_init(const OverlayCallbacks *cbs, if (g_bcbs.bail_flattened) g_psx_bm_bail_flattened_p = g_bcbs.bail_flattened; if (g_bcbs.ram_mask) g_psx_bm_ram_mask_p = g_bcbs.ram_mask; if (g_bcbs.frame_count) g_psx_bm_frame_count_p = g_bcbs.frame_count; + if (g_bcbs.mod_guest_functions) g_psx_bm_mod_guest_functions_p = g_bcbs.mod_guest_functions; return 1; } PSX_OVERLAY_EXPORT const PsxBiosBackend *psx_bios_module_backend(void) { diff --git a/runtime/src/psx_bios_module.c b/runtime/src/psx_bios_module.c index acd0990c..f771a816 100644 --- a/runtime/src/psx_bios_module.c +++ b/runtime/src/psx_bios_module.c @@ -206,6 +206,8 @@ void psx_bios_module_fill_callbacks(PsxBiosModuleCallbacks *out) { extern uint64_t g_psx_bail_flattened; extern int psx_game_address_in_text(uint32_t addr); extern uint64_t s_frame_count; + extern uint32_t g_psx_mod_guest_functions; + extern int psx_mod_dispatch_guest_function(struct CPUState *cpu, uint32_t address); memset(out, 0, sizeof(*out)); out->size = (uint32_t)sizeof(*out); out->arith_overflow = psx_arith_overflow; @@ -229,6 +231,8 @@ void psx_bios_module_fill_callbacks(PsxBiosModuleCallbacks *out) { out->bail_flattened = &g_psx_bail_flattened; out->ram_mask = &g_psx_ram_mask; out->frame_count = &s_frame_count; + out->mod_guest_functions = &g_psx_mod_guest_functions; + out->mod_dispatch_guest_function = psx_mod_dispatch_guest_function; } /* ---- Loading -------------------------------------------------------------- */ From d3bf6f5383179cda04629e39d812f6ad78d537d5 Mon Sep 17 00:00:00 2001 From: rain Date: Tue, 13 Oct 2026 13:58:29 -0400 Subject: [PATCH 2/3] cdrom: treat out-of-range XA MBF SetLoc as stream sentinel (keep head position) Xing CinePlayer STR players (e.g. SLPS-00756 XINGLOGO.STR boot movie) arm a real extent seek and then issue SetLoc with a sentinel MBF whose digits are not valid BCD (0xC0,0x00,0x00 / 0xC9,0x03,0x00) immediately before ReadS. Decoding the sentinel as a position parked the drive past the lead-out, so every auto-read sector came back as zero-filled buffers: the movie's XA ADPCM never started and the consumer spun forever at the opening. A SetLoc whose MSF digits contain A-F nibbles is now a no-op (the head stays at the previously armed extent), which matches the observed guest expectation: ReadS streams the interleaved video+audio sectors from the header-following position and the STR plays. Valid BCD seeks are unchanged. --- runtime/src/cdrom.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/runtime/src/cdrom.c b/runtime/src/cdrom.c index a0ed6d2d..d323a1ef 100644 --- a/runtime/src/cdrom.c +++ b/runtime/src/cdrom.c @@ -1026,6 +1026,28 @@ static int msf_to_lba(int m, int s, int f) { return (m * 60 + s) * 75 + f - 150; } +/* CD-ROM XA MBF addressing with SetMode bit6 enabled: Xing CinePlayer + * (XINGLOGO.STR boot movie, SLPS-00756) seeks 0xC0,0x00,0x00 immediately + * after the real extent seek (5:45:52 = the movie's ADPCM extent) and then + * issues ReadS. The STR header (sector 10709) carries the extents as plain + * LBA pairs, so 0xC00000 is not an addressable MBF: it is the stream + * sentinel meaning "run the auto-read past the lead-out until XA EOB". + * Such an out-of-range XA MBF must NOT move the head: the data the game + * waits for is delivered from the previously armed seek. Valid BCD digits + * keep the standard MM:SS:FF decode. Returns SEEK_KEEP_MBF to signal the + * no-op, or the LBA otherwise. */ +#define SEEK_KEEP_MBF (-999999) +static int seek_params_to_lba(const uint8_t *p) { + int nib_invalid = ((p[0] >> 4) > 9) || ((p[0] & 0xF) > 9) || + ((p[1] >> 4) > 9) || ((p[1] & 0xF) > 9) || + ((p[2] >> 4) > 9) || ((p[2] & 0xF) > 9); + /* SetMode arrives AFTER these two SetLocs, so the sentinel cannot be + * gated on the XA bit: any A-F nibble is not a valid BCD MSF digit at + * all, and no conforming seek sends one. */ + if (nib_invalid) return SEEK_KEEP_MBF; + return msf_to_lba(bcd_to_bin(p[0]), bcd_to_bin(p[1]), bcd_to_bin(p[2])); +} + static CdTimingRecord *cd_timing_lookup(uint64_t seq) { if (seq == UINT64_MAX || seq >= s_cd_timing_total || s_cd_timing_total - seq > CD_TIMING_CAP) return NULL; @@ -2137,16 +2159,19 @@ static void exec_command(uint8_t cmd) { case 0x02: /* SetLoc */ if (param_count >= 3) { - int current_lba = (last_sector_lba >= 0) - ? last_sector_lba - : msf_to_lba(seek_min, seek_sec, seek_sect); - seek_min = bcd_to_bin(param_fifo[0]); - seek_sec = bcd_to_bin(param_fifo[1]); - seek_sect = bcd_to_bin(param_fifo[2]); - s_setloc_lba = msf_to_lba(seek_min, seek_sec, seek_sect); - setloc_pending = 1; - setloc_seek_far = (abs(current_lba - s_setloc_lba) > 16) ? 1 : 0; - warm_route_on_setloc(s_setloc_lba); + int new_lba = seek_params_to_lba(param_fifo); + if (new_lba != SEEK_KEEP_MBF) { + int current_lba = (last_sector_lba >= 0) + ? last_sector_lba + : msf_to_lba(seek_min, seek_sec, seek_sect); + seek_min = bcd_to_bin(param_fifo[0]); + seek_sec = bcd_to_bin(param_fifo[1]); + seek_sect = bcd_to_bin(param_fifo[2]); + s_setloc_lba = new_lba; + setloc_pending = 1; + setloc_seek_far = (abs(current_lba - s_setloc_lba) > 16) ? 1 : 0; + warm_route_on_setloc(s_setloc_lba); + } } response_push(stat_reg); set_irq(CDIRQ_ACK); From df1150df7509a6ceec3504fd4437ac6813eeb6a1 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Wed, 14 Oct 2026 11:23:53 -0700 Subject: [PATCH 3/3] Use faithful SetLoc INT5 errors and validate BIOS callback ABI by default --- runtime/src/cdrom.c | 54 ++++++++++---------------- runtime/tests/bios_module_fake.c | 5 +++ runtime/tests/test_bios_module_host.c | 21 +++++++++- runtime/tests/test_cdrom_sector_fifo.c | 22 +++++++++++ 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/runtime/src/cdrom.c b/runtime/src/cdrom.c index 3c81fb5b..ef040e99 100644 --- a/runtime/src/cdrom.c +++ b/runtime/src/cdrom.c @@ -1023,28 +1023,6 @@ static int msf_to_lba(int m, int s, int f) { return (m * 60 + s) * 75 + f - 150; } -/* CD-ROM XA MBF addressing with SetMode bit6 enabled: Xing CinePlayer - * (XINGLOGO.STR boot movie, SLPS-00756) seeks 0xC0,0x00,0x00 immediately - * after the real extent seek (5:45:52 = the movie's ADPCM extent) and then - * issues ReadS. The STR header (sector 10709) carries the extents as plain - * LBA pairs, so 0xC00000 is not an addressable MBF: it is the stream - * sentinel meaning "run the auto-read past the lead-out until XA EOB". - * Such an out-of-range XA MBF must NOT move the head: the data the game - * waits for is delivered from the previously armed seek. Valid BCD digits - * keep the standard MM:SS:FF decode. Returns SEEK_KEEP_MBF to signal the - * no-op, or the LBA otherwise. */ -#define SEEK_KEEP_MBF (-999999) -static int seek_params_to_lba(const uint8_t *p) { - int nib_invalid = ((p[0] >> 4) > 9) || ((p[0] & 0xF) > 9) || - ((p[1] >> 4) > 9) || ((p[1] & 0xF) > 9) || - ((p[2] >> 4) > 9) || ((p[2] & 0xF) > 9); - /* SetMode arrives AFTER these two SetLocs, so the sentinel cannot be - * gated on the XA bit: any A-F nibble is not a valid BCD MSF digit at - * all, and no conforming seek sends one. */ - if (nib_invalid) return SEEK_KEEP_MBF; - return msf_to_lba(bcd_to_bin(p[0]), bcd_to_bin(p[1]), bcd_to_bin(p[2])); -} - static CdTimingRecord *cd_timing_lookup(uint64_t seq) { if (seq == UINT64_MAX || seq >= s_cd_timing_total || s_cd_timing_total - seq > CD_TIMING_CAP) return NULL; @@ -2140,19 +2118,27 @@ static void exec_command(uint8_t cmd) { case 0x02: /* SetLoc */ if (param_count >= 3) { - int new_lba = seek_params_to_lba(param_fifo); - if (new_lba != SEEK_KEEP_MBF) { - int current_lba = (last_sector_lba >= 0) - ? last_sector_lba - : msf_to_lba(seek_min, seek_sec, seek_sect); - seek_min = bcd_to_bin(param_fifo[0]); - seek_sec = bcd_to_bin(param_fifo[1]); - seek_sect = bcd_to_bin(param_fifo[2]); - s_setloc_lba = new_lba; - setloc_pending = 1; - setloc_seek_far = (abs(current_lba - s_setloc_lba) > 16) ? 1 : 0; - warm_route_on_setloc(s_setloc_lba); + /* Invalid BCD/ranges report INT5(10h) and preserve the existing + * target, matching the controller and Beetle Command_Setloc. + * They are not an XA successful-stream sentinel. */ + if ((param_fifo[0] & 0x0Fu) > 9u || param_fifo[0] > 0x99u || + (param_fifo[1] & 0x0Fu) > 9u || param_fifo[1] > 0x59u || + (param_fifo[2] & 0x0Fu) > 9u || param_fifo[2] > 0x74u) { + response_push(stat_reg | 1u); + response_push(0x10u); + set_irq(CDIRQ_ERROR); + break; } + int current_lba = (last_sector_lba >= 0) + ? last_sector_lba + : msf_to_lba(seek_min, seek_sec, seek_sect); + seek_min = bcd_to_bin(param_fifo[0]); + seek_sec = bcd_to_bin(param_fifo[1]); + seek_sect = bcd_to_bin(param_fifo[2]); + s_setloc_lba = msf_to_lba(seek_min, seek_sec, seek_sect); + setloc_pending = 1; + setloc_seek_far = (abs(current_lba - s_setloc_lba) > 16) ? 1 : 0; + warm_route_on_setloc(s_setloc_lba); } response_push(stat_reg); set_irq(CDIRQ_ACK); diff --git a/runtime/tests/bios_module_fake.c b/runtime/tests/bios_module_fake.c index 1ded92f2..ff9c10a3 100644 --- a/runtime/tests/bios_module_fake.c +++ b/runtime/tests/bios_module_fake.c @@ -27,9 +27,13 @@ #endif static int s_dispatch_calls; +static PsxBiosModuleCallbacks s_callbacks; static void fake_dispatch(struct CPUState *cpu, uint32_t addr) { (void)cpu; (void)addr; s_dispatch_calls++; + if (s_callbacks.mod_guest_functions && *s_callbacks.mod_guest_functions && + s_callbacks.mod_dispatch_guest_function) + s_callbacks.mod_dispatch_guest_function(cpu, addr); } static void fake_dispatch_call(struct CPUState *cpu, uint32_t addr, uint32_t ra) { (void)cpu; (void)addr; (void)ra; @@ -59,6 +63,7 @@ FAKE_EXPORT uint32_t psx_bios_module_codegen_hash(void) { return (uint32_t)FAKE_ FAKE_EXPORT int psx_bios_module_init(const OverlayCallbacks *cbs, const PsxBiosModuleCallbacks *bcbs) { if (!cbs || !bcbs || bcbs->size < sizeof(PsxBiosModuleCallbacks)) return 0; + s_callbacks = *bcbs; s_init_bcbs_size = bcbs->size; s_seen_depth = bcbs->dispatch_depth; s_init_ok = 1; diff --git a/runtime/tests/test_bios_module_host.c b/runtime/tests/test_bios_module_host.c index 65fb5a39..35ff96ce 100644 --- a/runtime/tests/test_bios_module_host.c +++ b/runtime/tests/test_bios_module_host.c @@ -40,6 +40,11 @@ void debug_server_trace_dispatch(uint32_t f) { (void)f; } void debug_server_log_probe(uint32_t pc, CPUState *cpu) { (void)pc; (void)cpu; } void psx_segment_miss_record_kind(uint32_t a, uint32_t h, uint32_t r, uint32_t s, uint32_t f, uint32_t k) { (void)a; (void)h; (void)r; (void)s; (void)f; (void)k; } uint64_t s_frame_count = 0; +uint32_t g_psx_mod_guest_functions = 1; +int psx_mod_dispatch_guest_function(struct CPUState *cpu, uint32_t address) { + cpu->gpr[2] = address; + return 1; +} int (*g_psx_bios_hle_hook)(struct CPUState *cpu, uint32_t phys) = 0; uint64_t g_dispatch_static_hits = 0; uint32_t g_debug_current_func_addr = 0; @@ -104,6 +109,16 @@ int main(void) { CHECK(psx_bios_registry[1] == b, "registered after the builtin"); CHECK(psx_bios_has_selectable(), "a non-bundled backend is now selectable"); CHECK(psx_bios_find("FAKE-0001") == b, "find() by id"); + { + CPUState cpu = {0}; + b->dispatch(&cpu, 0x80010000u); + CHECK(cpu.gpr[2] == 0x80010000u, "loaded module calls new mod callback"); + g_psx_mod_guest_functions = 0; + cpu.gpr[2] = 0; + b->dispatch(&cpu, 0x80020000u); + CHECK(cpu.gpr[2] == 0, "loaded module observes live registration count"); + g_psx_mod_guest_functions = 1; + } CHECK(psx_bios_activate(b) == 1, "activate"); CHECK(psx_bios_active == b, "active pointer"); CHECK(psx_bios_image.image_crc32 == 0xDEADBEEFu, "published image copied"); @@ -115,6 +130,10 @@ int main(void) { CHECK(probe.dispatch_depth == &g_psx_dispatch_depth, "depth pointer aliases host"); CHECK(probe.hle_hook == &g_psx_bios_hle_hook, "hle hook pointer aliases host"); CHECK(probe.ram_mask == &g_psx_ram_mask, "ram mask pointer aliases host"); + CHECK(probe.mod_guest_functions == &g_psx_mod_guest_functions, + "mod count aliases host"); + CHECK(probe.mod_dispatch_guest_function == psx_mod_dispatch_guest_function, + "mod callback forwards to host"); } } @@ -131,7 +150,7 @@ int main(void) { char path[512]; CHECK(psx_bios_module_cache_path("/x", "SCPH1001", 0x37157331u, path, sizeof(path)), "cache path fits"); - CHECK(strstr(path, "/x/cache/bios/test-arch/bm1_") == path, "cache path root: %s", path); + CHECK(strstr(path, "/x/cache/bios/test-arch/bm2_") == path, "cache path root: %s", path); CHECK(strstr(path, "/SCPH1001_37157331.") != 0, "cache path stem+crc: %s", path); } diff --git a/runtime/tests/test_cdrom_sector_fifo.c b/runtime/tests/test_cdrom_sector_fifo.c index 05a1e821..ec9de719 100644 --- a/runtime/tests/test_cdrom_sector_fifo.c +++ b/runtime/tests/test_cdrom_sector_fifo.c @@ -147,7 +147,29 @@ static void snapshot_between_request_and_dma(void) { drain(100, 505); free(wire); free(after); } +static void invalid_setloc_preserves_target(void) { + const uint8_t invalid[][3] = { + {0xC0,0,0}, {0x0A,0,0}, {0,0x60,0}, {0,0x0A,0}, + {0,0,0x75}, {0,0,0x0A}, {0xFF,0xFF,0xFF} + }; + for (unsigned n=0;n