Files
BFM-decomp/docs/psyq-worklist.md
T
Drew T 02f060f607 feat(phase-31): S79 #5 — the libpad 4.2.1 + libapi 4.2 band and the apicard region LINKED from real objects: 13 stubs + 4 TUs + the reorder island gone; main 16 stubs, fleet 38
800c3 (0x8005CE18-0x8005FC68, one contiguous run of 33 interleaved Sony objects) is now four
stub rows — libapi1 (21 BIOS trampolines + COUNTER), libpad1 (PADENTRY + PADMAIN 760), libapi2
(L02/L03), libpad2 (PADCMD PADIF PADPORTD PADSEQD WAITRC2) — fed by two WINDOWED psyq_integrate
calls from the raw .run/obj42/{libapi42,libpad421} dirs (integrate tiles each stub with one
library; every boundary checked against .text SECTION sizes). The apicard region's three
"game code" rows were libapi 4.2's C objects to the byte: 800c2 = FIRST.o (firstfile + the
"no jump table wall" stub func_80062144), 800c2_2 = PAD.o, 800c2_3 = PATCH.o + CHCLRPAD.o ->
apicard5/6/7; make_apicard_used.py sources libapi from 4.2 (the EXE's real libapi; libcard
stays 4.0) into .run/obj42/apicard_used, 26 objects / 7 blocks, no game code left in
0x80061F38-0x80062888. src/800c3.c (129 hand-matched "C", 62 verbatim bodies, 19 stubs incl.
the four §332 %lo-in-a-delay-slot "walls"), src/800c2.c, src/800c2_2.c, src/800c2_3.c removed;
REORDER_TUS is empty (mechanism kept). Cookbook §490.

Two stale instruments fixed: exclude_audit let a pinned WALL outrank LINKED (PopMatrix/
PushMatrix had sat as walls since S68 while living in libgte3, linked since Phase 8) — LINKED
dominates now, config/wave_exclude.txt 13 -> 3; frontier_classify carried a hard-coded 49-name
LINKED set (R51) and reported 337 "stubs" — derived from the Makefile now.

Verified: main 143dbb89f34491258bbc27810d0a12ec8b43a8dd WITH all SDK dirs and WITHOUT them from
a fresh extract; make tools-health OK; R22 fleet extract-all 212/212 + check-all 213/213.
Metrics: main REAL 839->773, LINKED 1,150->1,256, VERBATIM 29->3, stubs 29->16, byte-identical
2,075/2,091 = 99.2%; game-code weighted 93.3% (38,748/41,534), remainder 2,786 = the open-stub
sum; fleet stubs 51->38 (frontier_classify: 39 rows incl. the data word). Verbatim manifest
33 -> 6. Docs: worklist rows + "S79 task #5", SETUP (fresh-clone obj42 commands, Makefile
blocks, exclude_audit), decision-log "S79 addendum 2", accelerators "S79 (2)", CURRENT_PHASE
S79 FINAL refreshed (census, metrics, the task #6 brief).
2026-09-04 17:56:31 -06:00

275 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PsyQ library integration worklist (Phase 8)
> Byte-confirmed footprint map of the remaining PsyQ 4.0 libraries linked by `SLUS_007.26`.
> Generated by `psyq_identify.py` over the **original** EXE (`extracted/retail/SLUS_007.26`,
> text `0x80010000..0x800629DC`); raw per-object placements in `.run/survey/<lib>.txt` (regenerable:
> `for L in …; do python3 tools/psyq_identify.py .run/obj40/$L --vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26; done`). Replaces the Phase-7 ~350-fn
> *estimate* (R14). **Placement = candidate footprint; per-object byte-identity is confirmed at
> integration time by `psyq_link_region.py --verify` before wiring (the real gate, G3/P9).**
>
> **DONE (Gen1/Phase 7):** libcd (18 objs) + libgs (31 objs) linked byte-identical.
## Footprint summary (remaining libraries)
| Order | Library | Placed / total | Region(s) | ~banked B | Difficulty notes |
|---|---|---|---|---|---|
| 1 | **libgpu** | 3 / 12 | 800b2 (`0x80058890`) | ~14.9k | ✅ **DONE (T4 + S78 #4): EXT+PRIM+SYS linked** (raw `.run/obj40/libgpu`, no curated dir). SYS.o was EXCLUDED Phase 8→P31 S78 as scattered-`.bss` (§9.1); linked since S78 #4 via the link-prepare `.bss` split (cookbook §489) as `libgpu2`. |
| 2 | **libetc** | 5 / 7 | 800 tail (`0x8004239C`) | ~3.3k | VSYNC/INTR/INTR_VB/INTR_DMA/VMODE **contiguous**, ends exactly at libcd1 (`0x80043088`). Clean. |
| 3 | **libmcrd** | 2 / 2 | 800b2 (`0x8005FC68`,`0x80062888`) | ~9.0k | LIBMCRD.o (2186 ins, huge, holds the 55 `LIBMCRD_OBJ_*`) + USERFUNC.o — **2 blocks**. `_card_*` h_norm dups collapse inside LIBMCRD. |
| 4 | **libc2** | 17 / 46 | 800b2 (`0x8005C2C8`–`0x5CD98` + STRCAT `0x80061E90`) | ~3.1k | C stdlib (BZERO/MEMCPY/STRCMP/PRINTF/PRNT…); contiguous run + 1 outlier. **PRNT.o (418 ins) has an internal jtbl** — verify NOLOAD `.rodata` placement (the `PRNT_OBJ_24C` rodata note). |
| 5+6 | **libapi+libcard** | 26 (800c2) + 23 (band) | 800c2 (`0x80061F38`–`0x80062888`) + the band (`0x8005CE18`–`0x8005E188`) | ~2.6k | ✅ **DONE (S79 #5): apicard region 26 objs / 7 blocks tiling the whole range** (`tools/make_apicard_used.py`, libapi **4.2** from `tools/psyq/lib421` + libcard 4.0; FIRST/PAD/PATCH/CHCLRPAD were the "game code" rows 800c2/800c2_2/800c2_3) **+ the band's 23 libapi 4.2 objects (libapi1/libapi2)** from the raw `.run/obj42/libapi42` dir, windowed. |
| 9 | **libpad 4.2.1** | 7 / 11 | the band (`0x8005D0D8`–`0x8005FC68`) | ~11.5k | ✅ **DONE (S79 #13 found it, #5 wired it): PADENTRY PADMAIN PADCMD PADIF PADPORTD PADSEQD WAITRC2 as libpad1/libpad2** from the raw `.run/obj42/libpad421` dir (SCE's 1998-02-26 J421PD patch, `tools/psyq/lib421`). Absent: PADGUN/PADPORTM/PADSEQM/GUNHOOK (no gun, no multitap). |
| 7+8 | **libspu+libsnd** | 63 / (38+32) | 800 sound (`0x8003A444`–`0x8004239C`) | ~24k | ✅ **DONE (combined region; S78 #3/#4): 63 objs / 12 blocks.** The two libs interleave, so linked as ONE region (`tools/make_snd_used.py` dedups + excludes 3 addresses; `gen_lib_subsegs.py` + window). **Excluded:** S_R/S_W `0x3C438`, S_GRMDT* `0x3D424` (cross-object commons — these objects have NO `.bss`, so the S78 split cannot apply), S_IH/UT_RON `0x3D94C` (false-positive, inside SSSTART). VM_F `0x3FA64` (237 ins) rejoined in S78 #4 (`snd12`, `.bss` split at `_svm_sreg_buf`). SSGM.o `0x1BD80` deferred (isolated in matched-C, 8 ins). |
| 9 | **libgte** | 58 / 381 | 800b (`0x8004787C`–`0x5082C`) + **libgs gaps** | ~48k | ✅ **DONE (T11): 53 objs / 22 blocks linked** in 800b (subsegs via `gen_lib_subsegs.py`; integrate window 0x4787C..0x51804). **5 libgs-gap objects DEFERRED** (MTX_05/07/11/REG03/REG11 → gsgap1/2/4/5 stay stubs; gsgap2≠MTX_07 exactly so needs sub-split). |
**SKIP — zero footprint (recorded, not linked by the EXE):** `libmath` 0/48, `libc` 0/56, `libsn` 0/51 (2 tiny ambiguous). BFM links **libc2**, not libc; no libmath/libsn. (Also unbuilt/no-footprint: libcomb, libds, libgun, libsio, libtap, libpress — never converted, no symbols.)
## Region map (where each library lives, relative to the current splat subsegs)
```
800 (0x800123F0–0x80043088) game code + libsnd(SSGM @1BD80; 3D454–42374) + libspu(3A444–422E8) + libetc(4239C–43088, tail)
libcd (0x80043088–0x8004787C) DONE
800b (0x8004787C–0x80051804) libgte(4787C–5082C, multi-block) + game-code tail
libgs (0x8005080C–0x80057928) DONE; gaps gsgap1/2/4/5 are actually libgte (MTX_05/07/11, REG03/11; wired S78 #3 as libgte27-30); gsgap3=GS_001 linked S78 #4 as libgs8 (six-piece `.bss` split)
800b2 (0x80057928–0x800629DC) game code + libgpu(58890–5Bxxx) + libc2(5C2C8–5CD98,+STRCAT 61E90) + libapi(5CE18–626B8 scattered) + libmcrd(5FC68 + 62888) + libcard(61F38–62808)
```
## Integration order rationale (ascending difficulty)
- **1–2 (libgpu, libetc):** clean single contiguous blocks, minimal interleaving → re-validate the §9 mechanism end-to-end on a fresh library before the hard ones.
- **3–4 (libmcrd, libc2):** few blocks, contained in 800b2; libc2 introduces the PRNT internal-jtbl wrinkle.
- **5–6 (libcard, libapi):** short syscall stubs + cross-library aliases + scatter in 800b2 → need windowed placement + curated `_used` dirs.
- **7–8 (libspu, libsnd):** the interleaved sound pair in the 800 subseg (which still holds the 43 REAL game-code matches lower down) → `split_src_region.py` trims, windowed placement, alias disambiguation.
- **9 (libgte):** largest, multi-region (800b + 4 libgs gaps), game-code-interleaved blocks → last.
## Per-library integration recipe (each = one byte-gated task, §9.3/§9.5)
> **Phase 9:** all these tools now REQUIRE `--vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26`
> (the EXE's values; `psyq_integrate` also `--symbols config/symbols.us.txt`). For a Gen2 binary, pass
> its own `--vram-base`/`--exe`/`--symbols` (cookbook §9.7, SETUP §6.7).
1. `psyq_link_region.py .run/obj40/<lib> <lo> <hi> --vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26 [--emit <p>]` → confirm per-object byte-identity ("per-object .text byte-identical: True") + the block/gap structure (G1: derive blocks empirically, don't trust this table's addresses).
2. Curate `.run/obj40/<lib>_used` (drop cross-library aliases / keep the variant whose `.data`/`.rdata` also matches) when aliases exist (make_libgs.sh pattern).
3. Resegment `config/splat.us.exe.yaml` (one `c` subseg per contiguous block; non-library gaps stay stubs); `split_src_region.py trim` the affected game-code `.c`; `make extract`.
4. Makefile: add `LIB<X>_ELF/OBJDIR/SYMS` + a gated `psyq_integrate.py --vram-base $(main_VRAM_BASE) --exe $(main_EXE) --symbols $(main_SYMBOLS) … <stubs> [lo hi]` call + `-T $(LIB<X>_SYMS)` on the `ld` line.
5. Add recovered SDK names to `config/symbols.us.txt` (mirror to Ghidra, R15/G6); update progress.py's LINKED config.
6. **Dual byte-gate:** `make clean && extract && build && check` → `143dbb89…` WITH `.run/obj40/<lib>` present AND with it absent (stub fallback). `make report` LINKED up. Commit (R20).
**Cross-cutting:** windowed placement is mandatory for the short-object / interleaved libraries (the libgs GS_106 precedent); cross-library address collisions (C112 libapi/libcard; UT_RON/S_IH; S_R/S_W) mean total distinct objects < the 199 summed placements — byte-verify decides ownership. Any object that won't link byte-identical after §9.1/§9.4 stays a documented honest stub (does not block exit, P9).
**⚠️ Boundary gotcha (found at libc2/T6):** the game-code subseg boundary AFTER a library block must be the last object's **`.text` SECTION size** (8-aligned), NOT its instruction count × 4. `psyq_identify` reports the instruction count, which omits the object's trailing 8-byte alignment padding (e.g. libc2 SETJMP.o: 30 ins = 0x78, but `.text` = 0x80 → block ends 8 bytes later). A too-low boundary overlaps the object's padded tail and the relink inserts +N padding, shifting the whole downstream image (pervasive 1-byte reloc diffs + grown file). Verify the boundary = `last_obj.vram + readelf .text size` for any library whose last object isn't followed object-to-object. Also: when a library block sits at a subseg's START (no `< lo` prefix to keep), `rm` the old `.c` so splat regenerates it under the new boundaries (split_src_region.trim would leave it header-only).
## Excluded / deferred objects (§9.1 scattered-`.bss`) — honest stubs, byte-identical via asm
The standard mechanism places each object's `.bss` as ONE NOLOAD section at a single base. An object whose
`.bss` commons the **original linker scattered** to non-contiguous addresses (referenced as `.bss`+offset by a
single section symbol) cannot be reproduced by one base → excluded, kept as an INCLUDE_ASM stub (still
byte-identical). Diagnose: `psyq_link_region.py <dir>` shows N words differ in the object, and its `.bss`
references resolve to >1 base in the EXE. Curate the library's `_used` dir to drop these.
| Object | Library | Evidence | Status |
|---|---|---|---|
| `GS_001.o` | libgs | scattered `.bss` (Phase 7) — SIX bases | **LINKED S78 #4 (`libgs8`)**: `psyq_bss_split` cuts the section into six single-base pieces at symbol starts (PSDBASEX, CLIP2, PSDBASEY, POSITION, GsDRAWENV); the S77 probe's "5 interleaved bases, NOT splittable" grouped by base instead of by run (cookbook §489) |
| `SYS.o` (3109 ins) | libgpu | `.bss`+0x150 → `0x800c551c` but base recovered `0x80078830`; commons scattered 0x80078xxx/0x800c5xxx | **LINKED S78 #4 (`libgpu2`)**: `.bss` [0,0x144) @0x80078830 + `.bss2` = `_que` @0x800C5510 |
| `MTX_05/07/11`,`REG03`,`REG11` | libgte | sit in libgs gaps gsgap1/2/4/5; gsgap2(48B)≠MTX_07(36B) so the gap stub needs a sub-split | **deferred (T11)**; small GTE fns; link byte-identical, just need the gsgap region resegmented (low priority) |
| `0x3C438`,`0x3D424`,`0x3D94C` (S_R/S_GRMDT/S_IH) | libspu/snd | cross-object commons referenced at a minority address (S_R/S_GRMDT — these objects have NO `.bss` of their own, so the S78 split does not apply) + a false placement inside SSSTART (S_IH) | **excluded (sound region)**; stay stubs (24+4+24 ins). VM_F `0x3FA64` (237 ins) left this row in S78 #4 → `snd12` |
| `SSGM.o` | libsnd | isolated @0x1BD80, inside the matched-C region (near func_8001Bxxx) | **deferred**; 8 ins; would need a 1-object carve amid matched C |
| libapi 800c3 cluster (~22 objs) | libapi | C57..L10/L02/L03 @0x5CE18.. in the 800c3 region (separate from the 800c2 apicard region) | **LINKED S79 #5** (`libapi1`/`libapi2`, from libapi **4.2** — the EXE's real libapi, cookbook §490); the row's "lowest value" verdict missed that the same region held libpad 4.2.1's 2,780 ins |
*~~If scattered-`.bss` proves prevalent across libgte/libspu/libsnd, escalate to a Max general fix (split each object's `.bss` into per-common NOLOAD sections at their EXE-resolved addresses); otherwise excluding the few affected objects is the GS_001-precedent decision.~~ **Done in P31 S78 #4 — exactly that fix, twenty-three phases later: `tools/psyq_bss_split.py` runs inside the link-prepare step of `psyq_link` / `psyq_link_region` / `psyq_integrate` and tiles any such section into per-base NOBITS pieces from the bytes (cookbook §489). All three excluded objects link byte-identical; 235 placed objects across the 9 curated dirs, 0 refusals (R39 negative control).***
---
## The RESIDENT has **no linkable PsyQ footprint** — it is custom engine code (Phase 12 T1, byte-verified)
> **Pre-Phase-12 assumption (now corrected):** because the resident detects as **PsyQ 4.7.0** (DetectPsyQ + a `DsMix`/libsnd signature hit), it was expected to link embedded 4.7 libsnd/libgte/libspu (the Phase-11 close-out + R24 "link the resident's 4.7 libs" opener). **The bytes refuted this.**
**Phase 12 T1 survey** ran `psyq_identify` over the 4.7 objects (`tools/psyq/conv47/…/lib/`, already ELF — `ar x` into `.run/obj47/<lib>/`, no psyq-obj-parser needed) **and** the 4.0 objects, against the resident (`MAIN.CD/FILE_010/1.1`, `--vram-base 0x800CEDF8`), over the code window **and** the whole file. Result — **NIL footprint:**
| Lib (4.7) | placed | | Lib (4.0) | placed |
|---|---|---|---|---|
| libsnd | **1/226** (`ut_rev_2.o`, 4 ins) | | libsnd | **2/163** (`VM_DON.o` 5 ins, `VM_DOFF.o` 4 ins) |
| libspu | 0/134 | | libspu | 0/129 |
| libgte | 0/509 | | libgte | 0/381 |
| libgpu | 0/61 | | | |
| libcd/libmath/libds | 1 each (≤8 ins, two alias the same DsMix addr) | | | |
All hits are the **§9.5 short-object coincidental class** (≤8 ins, masked patterns match by luck). **Tool sanity (the negative is real):** 4.0 libsnd vs the **EXE** snd region = **35/163** placed — the tool works and is version-sensitive; the resident genuinely embeds **no stock PsyQ object of either version**.
**Ghidra corroboration (G1, sampled):** `DsMix` decompiles to `{ FUN_800d1bf8(); return 1; }` — a custom 2-line wrapper that **ignores its `vol` arg** (NOT the stock libsnd `DsMix`; the R13 tag in `symbols.resident.txt` is refuted). Other sampled functions are game logic (global accessors, engine init calling EXE REAL matches, entity-heading math calling the EXE's libgte `RATAN`). The resident's code makes **61 distinct EXE-range `jal` calls** (vs 37 internal) — it **calls** the EXE's resident SDK rather than embedding it.
**Conclusion / architecture:** the PsyQ SDK lives in the **EXE** (959 LINKED); the **resident is ~143 functions of custom engine code** that calls the EXE's SDK + engine via fixed addresses (no RAM-wasting SDK duplication in an always-loaded blob). The DetectPsyQ "4.7.0" was a single coincidental DsMix-region signature, not a linked footprint. **Phase 12 matches the resident engine by hand (REAL), not by linking (LINKED stays 0).** R24's per-binary-provenance principle holds, but for the resident the practical consequence is "nothing to link." *(Regenerate: `for L in libsnd libspu libgte libgpu; do d=.run/obj47/$L; mkdir -p $d; (cd $d && ar x ../../tools/psyq/conv47/psyq-4_7-converted/lib/$L.a); python3 tools/psyq_identify.py $d 0x800CEDFC 0x800D3408 --vram-base 0x800CEDF8 --exe extracted/retail/MAIN.CD.dir/FILE_010.dir/1.1; done`)*
---
## S78 (2026-09-04): the `800c3` "wall" band is **LIBPAD 4.2.1 + LIBAPI 4.2**, and the LINKED residue is now printed by the build
**How it was identified (a free oracle nobody had used).** `ghidra_psx_ldr` ships per-version PsyQ
signature sets — `~/ghidra_12.1_PUBLIC/Ghidra/Extensions/ghidra_psx_ldr/data/psyq/<ver>/<LIB>.LIB.json`,
one masked-byte signature + function labels per OBJECT, for 2.6 → 4.7. Matched against the retail EXE
bytes (cookbook §487), the **4.2** set places these byte-exact:
| object | vram | ins | functions (labels) |
|---|---|---|---|
| LIBAPI `COUNTER.OBJ` | 0x8005CF68 | 92 | SetRCnt · GetRCnt · StartRCnt · StopRCnt · ResetRCnt |
| LIBAPI `C114.OBJ` | 0x8005CE48 | 8 | `_96_remove` — **the 4.2 `Ps` stamp** (SETUP §5.1 "libnum 0") |
| LIBPAD `PADENTRY.OBJ` | 0x8005D0D8 | 300 | PadChkVsync · PadStartCom · PadStopCom · PadChkMtap · PadGetState · PadInfoMode · PadInfoAct · PadInfoComb · PadSetActAlign · PadSetMainMode · PadSetAct |
| LIBPAD `PADMAIN.OBJ` (4.2.1) | 0x8005D588 | ~756 | PadEnableCom · `_padSetVsyncParam` · `_padChkVsync` · `_padStartCom` · `_padStopCom` · `_padInitSioMode` · `_padSioRW` (+ `_padSioRW2`/`_padClrIntSio0`/`_padWaitRXready`, order-inferred: the 4.2 sig drifts +4/+12 here — the EXE holds the 4.2.1 revision) |
| LIBAPI `L02.OBJ`/`L03.OBJ` | 0x8005E168 | 4+4 | SysEnqIntRP · SysDeqIntRP |
| LIBPAD `PADCMD.OBJ` | 0x8005E188 | 600 | `_padSetAct` · `_padSetCmd` · `_padSendAtLoadInfo` · `_padRecvAtLoadInfo` · `_padGetActSize` · `_padLoadActInfo` · `_padSetActAlign` · `_padSetMainMode` · `_padCmdParaMode` |
| LIBPAD `PADIF.OBJ` | 0x8005EAE8 | ~376 | statics only (no public labels): `func_8005EAE8/EB28/EC00/ECC0/ED4C` |
| LIBPAD `PADPORTD.OBJ` | 0x8005F0C8 | 408 | PadInitDirect |
| LIBPAD `PADSEQD.OBJ` | 0x8005F728 | 288 | `_padInitDirSeq` · `_dirFailAuto` |
| LIBPAD `WAITRC2.OBJ` (4.3 sig) | 0x8005FBA8 | 48 | setRC2wait · chkRC2wait |
| LIBAPI `FIRST.OBJ` | 0x80061FA8 | 168 | firstfile (the 4.2 C wrapper; 4.0's `A66.o` trampoline at 0x80062248 is `firstfile2` in 4.2 naming) |
| LIBAPI `PAD.OBJ` | 0x80062388 | 192 | SetInitPadFlag · ReadInitPadFlag · PAD_init · InitPAD · StartPAD · StopPAD |
| LIBAPI `PATCH.OBJ` | 0x800626C8 | 40 | EnablePAD · DisablePAD · `_patch_pad` |
| LIBAPI `CHCLRPAD.OBJ` | 0x80062768 | 28 | `_remove_ChgclrPAD` |
The 4.2.1x `Ps` stamp at `0x80072954` ("libnum 12") stands directly in front of libpad's `.data` — the
callback tables `D_8007295C..D_800729D8` the band's functions index. So **all 12 of main's open stubs
in `800c3`** (incl. the four §332 "%lo-in-a-delay-slot" walls: `_padInitSioMode`, `_padStartCom`,
`func_8005ED4C`, `func_8005F450`) and the 8 SDK-C-REORDER verbatims are **Sony library code assembled
in reorder mode** — §332b's mechanism, with its provenance. Names applied to `config/symbols.us.txt` +
Ghidra (46; `firstfile` after retiring Ghidra's 4.0-sig `firstfile` at 0x80062248 → `firstfile2`).
**Which archives hold it.** Placed with `psyq_identify` after converting each to ELF: PsyQ **4.0**
(`lib40/`, no LIBPAD at all), **4.6** (`lib46/`, S78 fetch), **4.7** (`conv47/`): only
`PDMAIINI.o` (4.6/4.7, 68 ins @0x8005D8B4 = `_padStartCom`+`_padStopCom`) is byte-identical in the
band; 4.6/4.7 libpad otherwise differs (PADMAIN 884 ins in 4.4+, PADCMD 768). The loader's **4.3**
signature set matches PADENTRY/PADCMD/PADPORTD/WAITRC2 but not PADSEQD (292 vs 288). **A 4.2.1 or
4.3 LIBPAD.LIB is the archive that would LINK the whole band** (task #13); until then the band is
matched as C under the `REORDER_TUS` island with the real names.
**The LINKED residue, now a build output.** `psyq_integrate --yaml` (S78) wires only the objects
inside each library's stub subsegs and PRINTS the rest as `~~ N located object(s) / M ins OUTSIDE the
stub subsegs` — the completion contract's "SDK residue empties" line, read straight off `make build`.
At S78 close it reads, for libgte: **13 objects / 1,264 ins** — `MSC01/02/05/09` (800b, 276 ins,
100% of that "game code" subseg), `SMP_00` (800b_2), `SMP_05` (800b_3), `FGO_01–06` (800b_5, 804 ins,
100%), `PATCHGTE` (800b_6, 40 ins, 100%). Plus, outside libgte's window: `SYS.o` (800c, 3,109),
`2D_BG0/1` (800b_7, 1,022), `VM_NO1` (sgap_7, 305), `VM_NOWON` (sgap_8, 300), `VM_F` (sgap_6, 237),
libapi's 800c3 trampolines (176), `FIRST.o` (168). Task #3/#4 wire these.
**Why the build was RED at HEAD (found S78).** The S77 `psyq_identify` fix (§485) started locating
those in-gap objects, and `psyq_integrate.contiguous_blocks()` then merged libgte's 22 stub blocks
into 3 (`3 object blocks but 22 stubs`) — every `make build BINARY=main` with `.run/obj40/libgte`
present died at the link. It passed the S77 gates because the gate worktrees have no `.run/obj40` and
take the stub fallback. Two fixes, both in `psyq_integrate.py`: (1) `--yaml`: stub↔objects by SUBSEG
RANGE with an exact-tiling check, residue printed; (2) a library object's DEFINED symbol whose
recovered address the curated file names differently is `--redefine-sym`'d to the curated name (R15)
— `A66.o` `firstfile`→`firstfile2`, and it also exposed `TOC.o` `CdGetToc`@0x800430B8 mis-curated as
`DecDCToutCallback` (an xdedup-vs-VS mislabel; libcd 4.0's linked object is the stronger oracle).
### S78 task #3 — thirteen "game code" subsegs were library objects: wired as LINKED (exact tiles, 0 tokens)
| new block | was | objects | ins | note |
|---|---|---|---|---|
| `libgte23` | 800b | MSC01, MSC02, MSC05, MSC09 | 276 | GTE macro wrappers (SetRGBfifo…) that had been hand-matched as REAL |
| `libgte24` | 800b_2 | SMP_00 | 132 | |
| `libgte9` (re-derived) | 800b_3 + libgte9 + 800b_4 | SMP_05 (`NormalClip`) | 12 | SMP_06 `NormalClipS` is its nested sub-pattern; the "3-nop NOTCODE-PAD" was the object's alignment |
| `libgte25` | 800b_5 | FGO_01…FGO_06 | 804 | |
| `libgte26` | 800b_6 | PATCHGTE | 40 | `.sbss` 0x10, single base |
| `libgte27/28/29` | gsgap1/2/4 | MTX_05 / MTX_07 / MTX_11 | 20/12/12 | the Phase-8 "deferred (T11)" libgs-gap objects |
| `libgte30` | gsgap5 | REG03 + REG11 | 76 | |
| `libgs7` | 800b_7 | 2D_BG0 + 2D_BG1 | 1022 | §485's headline find |
| `snd10` | sgap_7 | VM_NO1 | 305 | |
| `snd11` | sgap_8 (head) | VM_NOWON | 300 | sgap_8 now starts at 0x80040DE8 with its game C |
Result: libgte **70 objects / 30 blocks** (was 53/22), libgs **33 / 7**, sound **62 / 11**; LINKED fns
959 → **1,040**; REAL 912 → 886 (26 inline-asm SDK wrappers re-provenanced); VERBATIM 146 → **85**;
13 TUs deleted; main `143dbb89` byte-identical **with and without** the SDK objects (the fallback had
been red since S7x: `CdReadyCallback` was called by its SDK name but the libcd stub carried
`func_800435B4` — curated now). Remaining located-but-unwired: `SYS.o` 3,109 + `VM_F` 237 (both
`.bss`-splittable, task #4), the libpad/libapi band pieces (task #5), `SSGM.o` 8, and the genuine
scattered-`.bss` walls `GS_001` / `S_R` / `S_GRMDT`.
**The metric correction this exposed (R35).** `progress.py`'s "MAIN game-code weighted" line claimed
its sig excluded the LINKED objects; it never did — the 2026-08-05 Ghidra sig carried all ~31,000
linked-SDK instructions, whose stub records read as unmatched game code. Honest figure, LINKED now
excluded live from the Makefile stub lists + yaml ranges: **91.8% (44,562 / 48,537)**, not 59.8%; the
3,975-ins remainder equals the sum of main's open stubs in `frontier_classify` exactly.
### S78 task #4 (S79, 2026-09-04) — the scattered-`.bss` class is closed: SYS.o, VM_F.o **and GS_001.o** linked via a link-prepare section split
| new block | was | object | ins | `.bss` pieces (cut → base) | note |
|---|---|---|---|---|---|
| `libgpu2` | 800c (56 hand-matched SDK fns + 62 verbatim frags) | SYS.o | 3,109 | `.bss` [0,0x144) → 0x80078830 · `.bss2` = `_que` → 0x800C5510 | `_que` recovered BY NAME from SYS.o's own 4 named refs = 0x800C5510 — the cut is confirmed by an independent oracle |
| `snd12` | sgap_6 tail (hand-matched `func_8003FA64` = `_SsVmFlush`) | VM_F.o | 237 | `.bss` [0,0x508) → 0x80079580 · `.bss2` = `_svm_sreg_buf` → 0x800B9B58 | 62 other sound objects recover `_svm_sreg_buf` = 0x800B9B58 |
| `libgs8` | gsgap3 (hand-matched as game C: `func_800525DC`…) | GS_001.o | 384 | six pieces: 0x80078810 · PSDBASEX 0x800A4F3C · CLIP2 0x800AE820 · PSDBASEY 0x800A4F40 · POSITION 0x800A5E50 · GsDRAWENV 0x800A6438 | the S77 probe called this "5 interleaved bases, NOT splittable" — it grouped by BASE; by RUN it is six symbol-aligned pieces, and the other libgs objects recover all five cut symbols at exactly those addresses |
**Mechanism** (`tools/psyq_bss_split.py`, cookbook §489): a pure-Python ELF32 REL rewrite that derives
each reference's base from the game bytes, walks the references in offset order, cuts at symbol starts
between runs, moves the symbols, inserts a LOCAL section symbol per piece, retargets the relocations
with the addend rewritten in the instruction immediates, and self-diffs. It runs inside the shared
link-prepare step (`psyq_link.link_object`, `psyq_link_region.build_region`, `psyq_integrate.integrate`),
derived from the bytes on every build — nothing recorded, nothing to go stale. Negative control (R39):
235 placed objects across the 9 curated dirs, 0 refusals, exactly these 3 splits; an end-of-buffer
reference (`.bss + size`, libcd) caught the first bounds check and is why problems are fatal only when a
split is actually needed.
**Wiring:** `LIBGPU_ELF` is the raw `.run/obj40/libgpu` (the `libgpu_used` dir existed only to exclude
SYS.o — retired); libgs 34 objects / 8 blocks (`make_libgs.sh` +GS_001); sound 63 / 12 (`make_snd_used.py`
exclusions 4 → 3). Three TUs went: `src/800c.c`, `src/gsgap3.c`, and the `_SsVmFlush` body of
`src/sgap_6.c` (its 4-ins game function stays). Main `143dbb89` byte-identical WITH and WITHOUT the SDK
objects (fallback from a fresh extract). Remaining located-but-unwired SDK code in main: the libpad/libapi
band pieces (task #5), `SSGM.o` 8 ins, and the two cross-object-common walls `S_R`/`S_W` + `S_GRMDT*`
(no `.bss` of their own — a different class from this one).
### S79 task #13 — FOUND: the archive that links the whole band (libpad 4.2.1 + libapi 4.2), 46/46 byte-identical
**Where.** archive.org item `play-station-programmer-tool-runtime-library-version-4.2.7z` (383 KB; sha256 in
`tools/psyq/CHECKSUMS.sha256`; now tracked as `tools/psyq/PlayStation_Programmer_Tool_-_Runtime_Library_Version_4.2.7z`,
R20). It is the PsyQ **Runtime Library 4.2** (LIB/*.LIB + INCLUDE, dated 1998-01-21) plus `LIB/42PATCH/J421PD.ZIP`
— SCE R&D's "Libpad.lib version 4.2.1 for the Analog Controller (DUAL SHOCK)" notice of **1998-02-26**, which
ships `LIBPAD.LIB` 4.2.1 together with `LIBAPI.LIB` 4.2 and the matching `LIBPAD.H`/`LIBAPI.H`/`KERNEL.H` (the
prototype oracle for the band from now on — not the 4.7 headers). Extracted to `tools/psyq/lib42/` and
`tools/psyq/lib421/` (gitignored, regenerable); ELF in `.run/obj42/{libpad421,libapi42}`.
**Result (the success test from the S78 brief, passed).** `psyq_identify` over 0x8005CE18–0x800629DC, then
`psyq_link.py` per object — every located object links byte-identical:
| library | placed | objects (vram, ins) |
|---|---|---|
| libpad 4.2.1 | **7 / 11** | PADENTRY 0x8005D0D8 (300) · **PADMAIN 0x8005D588 (760 — the 4.2.1 build, exact)** · PADCMD 0x8005E188 (600) · PADIF 0x8005EAE8 (376) · PADPORTD 0x8005F0C8 (408) · PADSEQD 0x8005F728 (288) · WAITRC2 0x8005FBA8 (48); absent: PADGUN, PADPORTM, PADSEQM, GUNHOOK (the game uses neither multitap nor gun) |
| libapi 4.2 | **39 / 88** | the 21 band trampolines C57 C68 C73 C114 A07–A13 A23–A25 A36 A37 A52 A53 A91 L10 (0x8005CE18–0x8005CF58) · COUNTER 0x8005CF68 (92) · L02/L03 0x8005E168 · and in the apicard region C112 A50 A51 A54 A65 A67 A69 (0x80061F38–) · **FIRST 0x80061FA8 (168, the C `firstfile` — REAL C today)** · A66 0x80062248 · PAD 0x80062388 (192) · A18–A21 0x80062688 · PATCH 0x800626C8 (40) · CHCLRPAD 0x80062768 (28) |
| libpad 4.2 (plain, `LIB/LIBPAD.LIB`) | 4 / 11 | PADENTRY PADCMD PADPORTD PADSEQD only — PADMAIN/PADIF/WAITRC2 are the 4.2.1 fixes; this is what the loader's "4.2" signature set was generated from and why PADMAIN showed +4/+12 drift in S78 |
`.data` anchors agree with S78: PADMAIN's `.data` is at **0x80072954** — the 4.2.1x `Ps` stamp stands at its
head; COUNTER `.data` 0x80072934, PADIF 0x800729D4, PADPORTD 0x800729F4, PAD 0x80072A24; PADENTRY `.rdata`
0x800744A0.
**What this changes for task #5.** The band is no longer "C under the reorder island with real names": ALL of
0x8005CE18–0x8005FC68 (2,872 ins: 21 trampolines + COUNTER + libpad's seven + L02/L03) is linkable from real
objects — the 12 open band stubs, the 8 SDK-C-REORDER verbatims and the band's hand-matched C are all Sony
code and become LINKED. The apicard region's libapi pieces (currently 4.0 objects in `apicard_used`, byte-identical
twins) and `FIRST.o` (currently REAL C `firstfile`) come from the same 4.2 library — the EXE's actual libapi
(the 4.2 `Ps` stamp, SETUP §5.1 libnum 0). #5 = carve `800c3` / `800c2*` at object boundaries (§486/§488
procedure) + a curated `.run/obj42` dir + two integrate calls. The `REORDER_TUS` island (§332b) then holds no
game code at all and can be retired with it.
**The 4.3 disc for the record.** `Programmer Tool - Runtime Library Version 4.3 (Japan)_DTL-S2340_redump.zip`
(archive.org item `ps1_sdks`, 403 MB; disc dated 1998-05-18) was fetched for comparison only: its `PSX/LIB/LIBPAD.LIB`
places the same 4 objects as plain 4.2 (PADENTRY, PADCMD, PADPORTD, WAITRC2) — PADMAIN is 832 ins, PADIF 380,
PADSEQD 292 against the EXE's 760 / 376 / 288 — and its LIBAPI.LIB places 38 (no 4.2 `C114`). So 4.2.1 is the
unique exact match: the game was built between the February 1998 patch and the May 1998 4.3 disc. Not banked
(>100 MB, R20 exception; the item id is the pointer; the two LIBs sit in `.run/psyq_hunt/rtl43/lib43/`).
### S79 task #5 — the band and the apicard region LINKED from libapi 4.2 + libpad 4.2.1 (0 tokens; cookbook §490)
| new block | was | objects | ins | note |
|---|---|---|---|---|
| `libapi1` | 800c3 head | C57 C68 C73 C114 A07–A13 A23–A25 A36 A37 A52 A53 A91 L10 + COUNTER | 176 | 21 BIOS trampolines + the RCnt object; window 0x8005CE18–0x8005E188 |
| `libpad1` | 800c3 | PADENTRY + PADMAIN | 1,060 | PADMAIN 760 = the 4.2.1 build |
| `libapi2` | 800c3 | L02 + L03 | 8 | SysEnqIntRP / SysDeqIntRP |
| `libpad2` | 800c3 tail | PADCMD PADIF PADPORTD PADSEQD WAITRC2 | 1,720 | window 0x8005D0D8–0x8005FC68 |
| `apicard5` | 800c2 | FIRST | 168 | `firstfile` (REAL C until now) + the "no jump table wall" stub `func_80062144` |
| `apicard6` | 800c2_2 | PAD | 192 | SetInitPadFlag … StopPAD + 4 statics (hand-matched C + 4 verbatims until now) |
| `apicard7` | 800c2_3 | PATCH + CHCLRPAD | 68 | `_patch_pad`, `_remove_ChgclrPAD` (4 verbatims until now) |
Two windowed integrate calls from the raw `.run/obj42/{libapi42,libpad421}` dirs (the band is one interleaved
run and `psyq_integrate` tiles each stub with one library) + the apicard call from the re-sourced
`.run/obj42/apicard_used` (libapi 4.2 + libcard 4.0, 26 objects / 7 blocks, no game code left in
0x80061F38–0x80062888). TUs gone: `src/800c3.c` (129 hand-matched "C", 62 verbatim bodies, 19 stubs — Sony's
reorder-assembled code all along, §332b), `src/800c2.c`, `src/800c2_2.c`, `src/800c2_3.c`; the `REORDER_TUS`
island is EMPTY (the Makefile mechanism stays). main `143dbb89` with every SDK dir and, from a fresh extract,
with none. `config/wave_exclude.txt` regenerated (`exclude_audit` now lets LINKED outrank a pinned WALL: the
four §332 walls, `func_80062144`, and `PopMatrix`/`PushMatrix` — in libgte3 since Phase 8 — dropped);
verbatim manifest 33 → 6 rows. Located-but-unwired SDK code left in main: `SSGM.o` (8 ins, inside matched C)
and the sound region's two cross-object-common walls (S_R/S_W, S_GRMDT*).