Files
psxrecomp/runtime/tests/bios_module_fake.c
Alex Vanderveen 60919f5a87 bios: build a retail backend from the player's own dump as a loadable module
Bundled releases link OpenBIOS only; retail-BIOS-derived C never ships.
"Bring your own BIOS" now works without it: when a dump matching a shipped
profile (psx_bios_known_images.h) is selected, the runtime builds that
backend on the player's machine with the overlay toolchain the release
already carries, caches it, and loads it as one more registered backend.

- runtime/include/psx_bios_module.h: the module contract. An overlay-style
  self-contained shared library (no imports from the executable) exporting
  psx_bios_module_{abi,codegen_hash,init,backend}; reaches the runtime only
  through OverlayCallbacks plus a new PsxBiosModuleCallbacks table (traps,
  dirty-RAM/kernel-bless probes, fntrace, log_probe) and pointer aliases of
  the globals the generated dispatcher touches. Its #defines rewrite the
  emitter's own extern lines, so the generated C compiles unchanged.
- runtime/include/psx_bios_module_glue.c.inc: module-side forwarders and the
  four exports, compiled after overlay_dispatch_preamble.c.inc.
- runtime/src/psx_bios_module.c: host side. Cache path
  <exe>/cache/bios/<os-arch>/bm<abi>_<codegen hash>_f<flavor>/<STEM>_<crc>,
  ABI + codegen-hash gated load, callback fill, and a blocking build via
  overlay_toolchain/bios_module_build.py (gcc via compiler.txt or PATH,
  else tcc), then psx_bios_register().
- psx_bios_backend.c: the registry is now a mutable table seeded from the
  CMake-generated psx_bios_builtin_registry by a constructor, with
  psx_bios_register(); every existing walker keeps working.
- main.cpp: validate_bios_for_launch acquires a module (announcing a first
  build) before declaring a mismatch; discovery accepts a buildable dump;
  the launcher verify reports it usable ("ready" / "compiled on first
  launch") with no Generate & rebuild; the BIOS row shows whenever the
  toolchain is present; bundled_only respects module support.
- emitter: the CPS-marker constructor is not emitted in a module build.
- overlay_api.h: OverlayCallbacks gains a struct tag;
  overlay_loader_callbacks() exposes the host table before loader init.
- tools/bios_module_build.py (ships in the toolchain): identity + profile
  SHA-256 gate, staged emitter tree, emit, three-TU compile, ctypes
  self-check, atomic publish. release_stage.py stages psxrecomp-bios, every
  bios/*.toml, the seeds and the builder into overlay_toolchain/.
- Tests: runtime/tests/test_bios_module_host.c against three fake modules
  (good / wrong ABI / wrong hash) on real dlopen; recompiler/tests/
  test_bios_module_build.py builds the bundled OpenBIOS end to end and reads
  the descriptor back; guards extended.

Verified on Twisted Metal 4 (bundled build + staged toolchain, headless):
first launch with --bios SCPH1001.BIN builds and loads the module and boots
on image=SCPH-1001; the second launch loads it from cache.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 19:26:29 -04:00

75 lines
2.5 KiB
C

/* A fake BIOS module for test_bios_module_host.c: exports the four entry
* points psx_bios_module.h requires, with a descriptor whose identity the
* test can assert, and records what init was handed. Built as a shared
* library by runtime/CMakeLists.txt; never shipped.
*
* Which ABI/hash it reports is chosen at compile time so the same source
* builds the good module and the two bad ones the host must reject. */
#include <stdint.h>
#include <string.h>
#include "cpu_state.h"
#include "overlay_api.h"
#include "psx_bios_backend.h"
#include "psx_bios_module.h"
#ifndef FAKE_ABI_TAG
#define FAKE_ABI_TAG PSX_BIOS_MODULE_ABI_TAG
#endif
#ifndef FAKE_CODEGEN_HASH
#define FAKE_CODEGEN_HASH PSX_OVERLAY_CODEGEN_HASH
#endif
#if defined(_WIN32)
# define FAKE_EXPORT __declspec(dllexport)
#else
# define FAKE_EXPORT __attribute__((visibility("default")))
#endif
static int s_dispatch_calls;
static void fake_dispatch(struct CPUState *cpu, uint32_t addr) {
(void)cpu; (void)addr;
s_dispatch_calls++;
}
static void fake_dispatch_call(struct CPUState *cpu, uint32_t addr, uint32_t ra) {
(void)cpu; (void)addr; (void)ra;
s_dispatch_calls++;
}
static const PsxBiosImageInfo s_image = {
0, 0, 0,
0, 0,
524288u, 0xDEADBEEFu, 0u,
"0000000000000000000000000000000000000000000000000000000000000000",
"FAKE-0001",
0,
};
static const PsxBiosBackend s_backend = {
&s_image, fake_dispatch, fake_dispatch_call, 0, 0, 0, 0,
};
/* What init received, for the test to inspect through psx_bios_module_fake_probe. */
static uint32_t s_init_bcbs_size;
static int s_init_ok;
static int *s_seen_depth;
FAKE_EXPORT int psx_bios_module_abi(void) { return FAKE_ABI_TAG; }
FAKE_EXPORT uint32_t psx_bios_module_codegen_hash(void) { return (uint32_t)FAKE_CODEGEN_HASH; }
FAKE_EXPORT int psx_bios_module_init(const OverlayCallbacks *cbs,
const PsxBiosModuleCallbacks *bcbs) {
if (!cbs || !bcbs || bcbs->size < sizeof(PsxBiosModuleCallbacks)) return 0;
s_init_bcbs_size = bcbs->size;
s_seen_depth = bcbs->dispatch_depth;
s_init_ok = 1;
return 1;
}
FAKE_EXPORT const PsxBiosBackend *psx_bios_module_backend(void) { return &s_backend; }
/* Test-only probe: 1 if init ran with a full table and the depth pointer
* aliases the host's (the host passes &g_psx_dispatch_depth). */
FAKE_EXPORT int psx_bios_module_fake_probe(int *host_depth) {
return s_init_ok && s_init_bcbs_size == sizeof(PsxBiosModuleCallbacks) &&
s_seen_depth == host_depth;
}