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

30 KiB
Raw Blame History

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.

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).

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*).