mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
bios: add the SCPH-5500 and SCPH-5501 profiles
The v3.0 Japanese (SCPH-5500, 09/09/96) and US (SCPH-5501, 11/18/96) retail BIOS images join SCPH-1001, SCPH-101 and SCPH-5552, so every v3.0 region (J, A, E) has a profile. Both images match SCPH-1001 v2.2 byte for byte in the kernel, ROM [0x80, 0x18000). SCPH-5501's reset vector is identical too; SCPH-5500's differs in 24 bytes. The shell differs in both. So each profile is SCPH1001.toml with its own identity (name, id, rom, sha256 pin, stem): the copy windows and install slots are kernel properties and carry over unchanged. The shell runs through the dirty-RAM interpreter, as for SCPH5552. Each image gets its own seed list, derived by the same rule as phase2_ghidra_seeds_SCPH5552.json: the SCPH-1001 corpus filtered to seeds whose 64-byte ROM window is identical in both images (534 kept, 569 dropped), plus the two architectural vectors and 6 A0/B0/C0 kernel-table targets. The files carry only addresses, labels and rationales, plus the SHA-256 of both ROMs. - psx_bios_known_images.h: CRC-32 FF3EEB8C (SCPH-5500) and 8D8CB7E4 (SCPH-5501), both 512 KiB. A build that does not link these backends builds one from the player's own dump as a loadable module, like SCPH-5552, and the netplay lobby can name the image. - main_cli.cpp: map SCPH5500 / SCPH5501 dumps to their profiles. - build_cli.py: package both profiles with the CLI framework. - docs/BIOS_SELECTION.md: list them among the module-built images.
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# bios/SCPH5500.toml — BIOS build profile for the retail Sony SCPH-5500 image
|
||||
# (Japan, v3.0 09/09/96).
|
||||
#
|
||||
# Compared byte for byte with the SCPH-1001 v2.2 image (bios/SCPH1001.toml):
|
||||
# ROM [0x00000,0x00080) reset vector 24 bytes differ
|
||||
# ROM [0x00080,0x18000) kernel identical
|
||||
# ROM [0x18000,0x80000) shell differs
|
||||
# So every kernel property below (copy windows, install slots) carries over
|
||||
# unchanged. Because the reset vector differs, this image has its own seed
|
||||
# list, recompiler/seeds/phase2_ghidra_seeds_SCPH5500.json: the SCPH-1001
|
||||
# corpus filtered to seeds whose 64-byte window is identical in both ROMs,
|
||||
# plus the architectural vectors and this ROM's own A0/B0/C0 table targets
|
||||
# (the same rule as phase2_ghidra_seeds_SCPH5552.json). The shell has no seeds
|
||||
# of its own; it runs through the dirty-RAM interpreter (build with
|
||||
# -DPSX_SHELLWIN_INTERP=ON), as for SCPH5552.
|
||||
#
|
||||
# One profile per BIOS image. Consumed by `psxrecomp-bios --config`, by
|
||||
# tools/regen_bios.sh and by tools/bios_emitter_fingerprint.sh, as for
|
||||
# SCPH1001.toml. The .BIN image itself stays untracked (bios/* gitignore rule).
|
||||
|
||||
[program]
|
||||
name = "Sony SCPH-5500 BIOS (Japan, v3.0 09/09/96)"
|
||||
id = "SCPH-5500"
|
||||
rom = "bios/SCPH5500.BIN"
|
||||
load_address = "0xBFC00000"
|
||||
entry_pc = "0xBFC00000"
|
||||
text_size = "0x80000"
|
||||
|
||||
# Declared identity. The emitter refuses to generate from any other image
|
||||
# (main_bios.cpp's declared-identity gate). SCPH-5500 v3.0: CRC32 FF3EEB8C,
|
||||
# 512 KiB.
|
||||
[program.image]
|
||||
sha256 = "9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"
|
||||
license = "proprietary"
|
||||
redistributable = false
|
||||
|
||||
[recompiler]
|
||||
seeds = "recompiler/seeds/phase2_ghidra_seeds_SCPH5500.json"
|
||||
out_dir = "generated"
|
||||
out_stem = "SCPH5500"
|
||||
strict = true
|
||||
|
||||
# The BIOS's boot-time bulk copies of CODE out of ROM into RAM. Single source
|
||||
# of truth for every relocation window in the recompiler AND in the C it
|
||||
# emits (the generated dispatch's normalize(), the kernel-bless guard, the
|
||||
# game/shell-overlap tests). Each entry is a CLAIM that the copy is
|
||||
# byte-verbatim; the runtime's kernel-bless memcmp enforces it for the
|
||||
# bless window. All bounds are [lo, hi) with hi EXCLUSIVE.
|
||||
#
|
||||
# dispatch_key = "ram": functions in the window are keyed by RAM address
|
||||
# (normalize folds ROM -> RAM).
|
||||
# dispatch_key = "rom": functions keep their ROM key (normalize folds the
|
||||
# RAM alias back to ROM).
|
||||
[recompiler.address_model]
|
||||
normalize_mask = "0x1FFFFFFF"
|
||||
|
||||
[[recompiler.address_model.copy]]
|
||||
name = "Kernel Part 2"
|
||||
rom_lo = "0x1FC10000"
|
||||
rom_hi = "0x1FC18000"
|
||||
ram_lo = "0x00000500"
|
||||
runtime_base = "0x00000500" # KUSEG — the kernel executes uncached-region code cached at 0x500
|
||||
dispatch_key = "ram"
|
||||
kernel_bless = true
|
||||
|
||||
[[recompiler.address_model.copy]]
|
||||
name = "Shell"
|
||||
rom_lo = "0x1FC18000"
|
||||
rom_hi = "0x1FC43000"
|
||||
ram_lo = "0x00030000"
|
||||
runtime_base = "0x80030000" # KSEG0 — LoadRunShell jumps to 0x80030000
|
||||
dispatch_key = "rom"
|
||||
kernel_bless = false
|
||||
# NOTE (do not "fix" without evidence): runtime/src/bios_hle.c documents the
|
||||
# actual LoadRunShell copy as 0x67FF0 bytes (ROM 0xBFC18000 -> RAM 0x30000..
|
||||
# 0x97FF0). Every pre-profile window constant covered only 0x2B000 of that,
|
||||
# and dirty_ram_interp.c tracks live RAM in [0x5B000,0x8F000) as an open
|
||||
# class-B bug. Widening this window is a REAL BEHAVIOR CHANGE requiring its
|
||||
# own evidence-backed commit — it is not a parameterization cleanup.
|
||||
|
||||
# Kernel-RAM RANGES the BIOS and the game's Psy-Q libapi patchers overwrite at
|
||||
# runtime (CLAUDE.md rule 18; docs/dynamic_handler_install.md). The emitter
|
||||
# plants a compare-against-ROM hook at each range start so the guest's patched
|
||||
# instructions execute; the runtime excludes the range from the kernel-bless
|
||||
# memcmp and resumes native at the range end, so only the patched words
|
||||
# interpret.
|
||||
#
|
||||
# `resume` says how the compiled body picks up again: "jalr" for the classic
|
||||
# 4-word lui/addiu/jalr/nop stub (the call returns to ram_addr+0x10),
|
||||
# "fallthrough" when the patched words ARE the function, "none" when the patch
|
||||
# jumps out and never comes back.
|
||||
#
|
||||
# Measured on a live boot of Breath of Fire III (SLPS-00990, Psy-Q SDK 3.70)
|
||||
# against the ROM source, 30 s in, identical at t+8 s — the patchers are
|
||||
# _patch_gte / _patch_card / _patch_card2 / _patch_pad. Every SDK title runs
|
||||
# these, so the ranges are a property of the BIOS revision and libapi release,
|
||||
# not of one game. Re-measure per SDK version before trusting the numbers:
|
||||
# the schema tolerates declaring the union.
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00000C88" # exception handler: register-save prologue
|
||||
len = "0x30" # shifted two words + an inserted mfc0 v0,Cause
|
||||
resume = "fallthrough" # the patched words are the function itself
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00000CF0" # SIO data-byte handler dispatch slot
|
||||
len = "0x10" # lui/addiu/jalr/nop over ROM zeros
|
||||
resume = "jalr" # the stub's call returns to 0x0D00
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00004964" # pad driver clear routine, 11 words NOP'd
|
||||
len = "0x2C" # (RemovePatchPad / ChgclrPAD family)
|
||||
resume = "fallthrough"
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00004D98" # _patch_card2 payload: jalr into boot EXE
|
||||
len = "0x14" # 0x8017EB6C; the ra landing word is NOP'd too,
|
||||
resume = "fallthrough" # so the body resumes past it, not at +0x10
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00006444" # _patch_card payload: the kernel's card
|
||||
len = "0x10" # handler replaced by jr into boot EXE
|
||||
resume = "none" # 0x8017EAA0 — a jr never returns here
|
||||
|
||||
# NOT declared: RAM 0x0500, one word of kernel_trampoline_0 cleared to zero
|
||||
# (ROM `lui t2, 0`). It differs on every boot, but what writes it is not yet
|
||||
# identified, and a range declared on a guess would tell the bless verifier to
|
||||
# stop checking a word it should be checking. Rule 14: unknown is acceptable,
|
||||
# guessing is not. Undeclared means that body keeps interpreting, exactly as
|
||||
# it does today.
|
||||
|
||||
# Per-image anchors couriered into the generated C (psx_bios_image) for the
|
||||
# runtime HLE tier. Values are FACTS about this image, not tunables: the
|
||||
# shell entry is where LoadRunShell (ROM 0xBFC06FF0) jumps after its copy;
|
||||
# deliver_event_ret is the instruction after the kernel DeliverEvent loop's
|
||||
# `jalr v0` (kernel RAM 0x1718), the $ra a delivered event callback returns
|
||||
# through. Omit a key on a BIOS that has no such anchor — the runtime then
|
||||
# treats that HLE feature as structurally unavailable instead of firing on a
|
||||
# wrong address.
|
||||
[recompiler.runtime_exports]
|
||||
shell_entry_phys = "0x00030000"
|
||||
deliver_event_ret = "0x80001720"
|
||||
@@ -0,0 +1,141 @@
|
||||
# bios/SCPH5501.toml — BIOS build profile for the retail Sony SCPH-5501 image
|
||||
# (USA, v3.0 11/18/96).
|
||||
#
|
||||
# Compared byte for byte with the SCPH-1001 v2.2 image (bios/SCPH1001.toml):
|
||||
# ROM [0x00000,0x00080) reset vector identical
|
||||
# ROM [0x00080,0x18000) kernel identical
|
||||
# ROM [0x18000,0x80000) shell differs
|
||||
# So every kernel property below (copy windows, install slots) carries over
|
||||
# unchanged. The seed list, recompiler/seeds/phase2_ghidra_seeds_SCPH5501.json,
|
||||
# is the SCPH-1001 corpus filtered to seeds whose 64-byte window is identical
|
||||
# in both ROMs, plus the architectural vectors and this ROM's own A0/B0/C0
|
||||
# table targets (the same rule as phase2_ghidra_seeds_SCPH5552.json). The shell
|
||||
# has no seeds of its own; it runs through the dirty-RAM interpreter (build
|
||||
# with -DPSX_SHELLWIN_INTERP=ON), as for SCPH5552.
|
||||
#
|
||||
# One profile per BIOS image. Consumed by `psxrecomp-bios --config`, by
|
||||
# tools/regen_bios.sh and by tools/bios_emitter_fingerprint.sh, as for
|
||||
# SCPH1001.toml. The .BIN image itself stays untracked (bios/* gitignore rule).
|
||||
|
||||
[program]
|
||||
name = "Sony SCPH-5501 BIOS (USA, v3.0 11/18/96)"
|
||||
id = "SCPH-5501"
|
||||
rom = "bios/SCPH5501.BIN"
|
||||
load_address = "0xBFC00000"
|
||||
entry_pc = "0xBFC00000"
|
||||
text_size = "0x80000"
|
||||
|
||||
# Declared identity. The emitter refuses to generate from any other image
|
||||
# (main_bios.cpp's declared-identity gate). SCPH-5501 v3.0: CRC32 8D8CB7E4,
|
||||
# 512 KiB.
|
||||
[program.image]
|
||||
sha256 = "11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"
|
||||
license = "proprietary"
|
||||
redistributable = false
|
||||
|
||||
[recompiler]
|
||||
seeds = "recompiler/seeds/phase2_ghidra_seeds_SCPH5501.json"
|
||||
out_dir = "generated"
|
||||
out_stem = "SCPH5501"
|
||||
strict = true
|
||||
|
||||
# The BIOS's boot-time bulk copies of CODE out of ROM into RAM. Single source
|
||||
# of truth for every relocation window in the recompiler AND in the C it
|
||||
# emits (the generated dispatch's normalize(), the kernel-bless guard, the
|
||||
# game/shell-overlap tests). Each entry is a CLAIM that the copy is
|
||||
# byte-verbatim; the runtime's kernel-bless memcmp enforces it for the
|
||||
# bless window. All bounds are [lo, hi) with hi EXCLUSIVE.
|
||||
#
|
||||
# dispatch_key = "ram": functions in the window are keyed by RAM address
|
||||
# (normalize folds ROM -> RAM).
|
||||
# dispatch_key = "rom": functions keep their ROM key (normalize folds the
|
||||
# RAM alias back to ROM).
|
||||
[recompiler.address_model]
|
||||
normalize_mask = "0x1FFFFFFF"
|
||||
|
||||
[[recompiler.address_model.copy]]
|
||||
name = "Kernel Part 2"
|
||||
rom_lo = "0x1FC10000"
|
||||
rom_hi = "0x1FC18000"
|
||||
ram_lo = "0x00000500"
|
||||
runtime_base = "0x00000500" # KUSEG — the kernel executes uncached-region code cached at 0x500
|
||||
dispatch_key = "ram"
|
||||
kernel_bless = true
|
||||
|
||||
[[recompiler.address_model.copy]]
|
||||
name = "Shell"
|
||||
rom_lo = "0x1FC18000"
|
||||
rom_hi = "0x1FC43000"
|
||||
ram_lo = "0x00030000"
|
||||
runtime_base = "0x80030000" # KSEG0 — LoadRunShell jumps to 0x80030000
|
||||
dispatch_key = "rom"
|
||||
kernel_bless = false
|
||||
# NOTE (do not "fix" without evidence): runtime/src/bios_hle.c documents the
|
||||
# actual LoadRunShell copy as 0x67FF0 bytes (ROM 0xBFC18000 -> RAM 0x30000..
|
||||
# 0x97FF0). Every pre-profile window constant covered only 0x2B000 of that,
|
||||
# and dirty_ram_interp.c tracks live RAM in [0x5B000,0x8F000) as an open
|
||||
# class-B bug. Widening this window is a REAL BEHAVIOR CHANGE requiring its
|
||||
# own evidence-backed commit — it is not a parameterization cleanup.
|
||||
|
||||
# Kernel-RAM RANGES the BIOS and the game's Psy-Q libapi patchers overwrite at
|
||||
# runtime (CLAUDE.md rule 18; docs/dynamic_handler_install.md). The emitter
|
||||
# plants a compare-against-ROM hook at each range start so the guest's patched
|
||||
# instructions execute; the runtime excludes the range from the kernel-bless
|
||||
# memcmp and resumes native at the range end, so only the patched words
|
||||
# interpret.
|
||||
#
|
||||
# `resume` says how the compiled body picks up again: "jalr" for the classic
|
||||
# 4-word lui/addiu/jalr/nop stub (the call returns to ram_addr+0x10),
|
||||
# "fallthrough" when the patched words ARE the function, "none" when the patch
|
||||
# jumps out and never comes back.
|
||||
#
|
||||
# Measured on a live boot of Breath of Fire III (SLPS-00990, Psy-Q SDK 3.70)
|
||||
# against the ROM source, 30 s in, identical at t+8 s — the patchers are
|
||||
# _patch_gte / _patch_card / _patch_card2 / _patch_pad. Every SDK title runs
|
||||
# these, so the ranges are a property of the BIOS revision and libapi release,
|
||||
# not of one game. Re-measure per SDK version before trusting the numbers:
|
||||
# the schema tolerates declaring the union.
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00000C88" # exception handler: register-save prologue
|
||||
len = "0x30" # shifted two words + an inserted mfc0 v0,Cause
|
||||
resume = "fallthrough" # the patched words are the function itself
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00000CF0" # SIO data-byte handler dispatch slot
|
||||
len = "0x10" # lui/addiu/jalr/nop over ROM zeros
|
||||
resume = "jalr" # the stub's call returns to 0x0D00
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00004964" # pad driver clear routine, 11 words NOP'd
|
||||
len = "0x2C" # (RemovePatchPad / ChgclrPAD family)
|
||||
resume = "fallthrough"
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00004D98" # _patch_card2 payload: jalr into boot EXE
|
||||
len = "0x14" # 0x8017EB6C; the ra landing word is NOP'd too,
|
||||
resume = "fallthrough" # so the body resumes past it, not at +0x10
|
||||
|
||||
[[recompiler.install_slots]]
|
||||
ram_addr = "0x00006444" # _patch_card payload: the kernel's card
|
||||
len = "0x10" # handler replaced by jr into boot EXE
|
||||
resume = "none" # 0x8017EAA0 — a jr never returns here
|
||||
|
||||
# NOT declared: RAM 0x0500, one word of kernel_trampoline_0 cleared to zero
|
||||
# (ROM `lui t2, 0`). It differs on every boot, but what writes it is not yet
|
||||
# identified, and a range declared on a guess would tell the bless verifier to
|
||||
# stop checking a word it should be checking. Rule 14: unknown is acceptable,
|
||||
# guessing is not. Undeclared means that body keeps interpreting, exactly as
|
||||
# it does today.
|
||||
|
||||
# Per-image anchors couriered into the generated C (psx_bios_image) for the
|
||||
# runtime HLE tier. Values are FACTS about this image, not tunables: the
|
||||
# shell entry is where LoadRunShell (ROM 0xBFC06FF0) jumps after its copy;
|
||||
# deliver_event_ret is the instruction after the kernel DeliverEvent loop's
|
||||
# `jalr v0` (kernel RAM 0x1718), the $ra a delivered event callback returns
|
||||
# through. Omit a key on a BIOS that has no such anchor — the runtime then
|
||||
# treats that HLE feature as structurally unavailable instead of firing on a
|
||||
# wrong address.
|
||||
[recompiler.runtime_exports]
|
||||
shell_entry_phys = "0x00030000"
|
||||
deliver_event_ret = "0x80001720"
|
||||
@@ -25,7 +25,8 @@ for it). Regenerate with `tools/regen_bios.sh --config bios/<stem>.toml` and
|
||||
commit the output whenever the emitter, seeds or profile change.
|
||||
|
||||
**Other known images: the backend is built on the player's machine.** For a
|
||||
retail image a build does not link (today SCPH-5552), the release ships
|
||||
retail image a build does not link (today SCPH-5500, SCPH-5501 and SCPH-5552),
|
||||
the release ships
|
||||
`overlay_toolchain/` and compiles game code from the player's own disc at
|
||||
runtime; the same mechanism builds a BIOS backend from the player's own dump
|
||||
(`runtime/include/psx_bios_module.h`, `runtime/src/psx_bios_module.c`,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,8 @@ void usage(const char* program) {
|
||||
"--output <directory> [--name <title>]\n\n"
|
||||
"The output contains generated game/BIOS C, game.toml, CMakeLists.txt,\n"
|
||||
"and build scripts. No compiler toolchain is bundled.\n"
|
||||
"Supported BIOS: OpenBIOS, SCPH1001, SCPH101, SCPH5552 (any 512 KiB PS1 BIOS dump).\n",
|
||||
"Supported BIOS: OpenBIOS, SCPH1001, SCPH101, SCPH5500, SCPH5501, SCPH5552\n"
|
||||
"(any 512 KiB PS1 BIOS dump).\n",
|
||||
program);
|
||||
}
|
||||
|
||||
@@ -155,6 +156,8 @@ const BiosProfile& select_bios_profile(const fs::path& bios_path) {
|
||||
{"OPENBIOS", {"OpenBIOS.toml", "OpenBIOS"}},
|
||||
{"SCPH1001", {"SCPH1001.toml", "SCPH1001"}},
|
||||
{"SCPH101", {"SCPH101.toml", "SCPH101"}},
|
||||
{"SCPH5500", {"SCPH5500.toml", "SCPH5500"}},
|
||||
{"SCPH5501", {"SCPH5501.toml", "SCPH5501"}},
|
||||
{"SCPH5552", {"SCPH5552.toml", "SCPH5552"}},
|
||||
};
|
||||
const std::string token = PSXRecompV4::bios_model_token(bios_path);
|
||||
@@ -163,7 +166,8 @@ const BiosProfile& select_bios_profile(const fs::path& bios_path) {
|
||||
}
|
||||
throw std::runtime_error(
|
||||
"unsupported BIOS filename '" + bios_path.filename().string() +
|
||||
"' (expected openbios.bin, SCPH1001.BIN, SCPH101.BIN, or SCPH5552.BIN)");
|
||||
"' (expected openbios.bin, SCPH1001.BIN, SCPH101.BIN, SCPH5500.BIN, "
|
||||
"SCPH5501.BIN, or SCPH5552.BIN)");
|
||||
}
|
||||
|
||||
fs::path find_bios_profile(const fs::path& framework,
|
||||
|
||||
@@ -62,7 +62,8 @@ def main() -> int:
|
||||
|
||||
for profile in (
|
||||
"OpenBIOS.toml", "SCPH1001.toml",
|
||||
"SCPH101.toml", "SCPH5552.toml"):
|
||||
"SCPH101.toml", "SCPH5500.toml", "SCPH5501.toml",
|
||||
"SCPH5552.toml"):
|
||||
require(package, f'"{profile}"',
|
||||
f"CLI package omits BIOS profile {profile}")
|
||||
require(package, 'shutil.copy2(ROOT / ".gitignore", framework)',
|
||||
|
||||
@@ -35,6 +35,8 @@ typedef struct PsxKnownBiosImage {
|
||||
* identity the recompiler emits into generated/<stem>_dispatch.c. */
|
||||
static const PsxKnownBiosImage psx_known_bios_images[] = {
|
||||
{ "SCPH1001", "SCPH-1001", 0x37157331u, 524288u },
|
||||
{ "SCPH5500", "SCPH-5500", 0xFF3EEB8Cu, 524288u },
|
||||
{ "SCPH5501", "SCPH-5501", 0x8D8CB7E4u, 524288u },
|
||||
{ "SCPH5552", "SCPH-5552", 0xD786F0B9u, 524288u },
|
||||
};
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ def main() -> int:
|
||||
"OpenBIOS.toml": ROOT / "bios" / "OpenBIOS.toml",
|
||||
"SCPH1001.toml": ROOT / "bios" / "SCPH1001.toml",
|
||||
"SCPH101.toml": ROOT / "bios" / "SCPH101.toml",
|
||||
"SCPH5500.toml": ROOT / "bios" / "SCPH5500.toml",
|
||||
"SCPH5501.toml": ROOT / "bios" / "SCPH5501.toml",
|
||||
"SCPH5552.toml": ROOT / "bios" / "SCPH5552.toml",
|
||||
}
|
||||
(framework / "bios").mkdir()
|
||||
|
||||
Reference in New Issue
Block a user