# 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/.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/ --vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26 [--emit

]` → 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/_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_ELF/OBJDIR/SYMS` + a gated `psyq_integrate.py --vram-base $(main_VRAM_BASE) --exe $(main_EXE) --symbols $(main_SYMBOLS) … [lo hi]` call + `-T $(LIB_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/` 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

` 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//`, 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//.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*).