feat(phase-7): Gen1 close-out — matching loop industrialized, PsyQ libs linked, LZSS matched (v1.7.0)

- reports (Task 3): tools/{progress,difficulty,dup_report}.py + make report/sig-refresh
  (deterministic digests); harvest to 43 REAL matches incl. LzssDecodeSector
- per-module -O0 split (Task 5.5): src/boot.c + Makefile per-file CC1FLAGS (cookbook §6)
- PsyQ SDK linking (sessions C-G): split/build/identify/link/integrate pipeline
  (tools/psyq_*.py, make_libgs.sh, ld_interleave.py, split_src_region.py) — libcd (18 obj)
  + FULL libgs (31 obj / 6 blocks) linked BYTE-IDENTICAL from real PsyQ 4.0 libs; byte-
  identical with or without the SDK objects (stub fallback); cookbook §7/§8/§9.1-9.5
- LZSS MATCHED byte-for-byte (session F): §5a cross-jump barrier + §10 regalloc/schedule
  idioms, ground-truthed against pinned gcc-2.7.2 source (the Gen1 LZSS gate)
- file-loader cluster: 6 matched + 5 NON_MATCHING-drafted (CdReadStateMachine,
  CdReadSectorReadyCB, StreamLoadStateMachine new this session, valid under -DNON_MATCHING);
  SaveLoadRoutine DEFERRED to Q#5 (save/memcard, documented stub)
- README Gen1-complete; worklog preserved -> phase-ends/logs/Phase7.md
- rules R17 (web-research compiler quirks), R18 (plain-English session summaries),
  R19 (preserve phase worklog out of load order)
- MILESTONE: Gen1 EXIT confirmed — byte-identical build, 43 matches incl LZSS, 7 zero-
  regression sessions, loader cluster matched/drafted, overlay map emulator-proven
- bumps project version 1.6.0 -> 1.7.0
This commit is contained in:
Drew T
2026-06-15 13:44:32 -06:00
parent f91a67a6fe
commit 49db81d8ae
24 changed files with 1147 additions and 214 deletions
+10 -10
View File
@@ -1,9 +1,9 @@
# Unmatched difficulty inventory (generated by tools/difficulty.py — harvest queue)
unmatched functions : 1962
trivial (<=5 ins) : 291
non-jtbl leaves : 1093 (best harvest targets)
jump-table funcs : 84 (deferred — need the rodata-island workflow, Task 2')
unmatched functions : 2001
trivial (<=5 ins) : 297
non-jtbl leaves : 1123 (best harvest targets)
jump-table funcs : 80 (deferred — need the rodata-island workflow, Task 2')
## Easiest 120 unmatched (score asc) — the work queue
| score | name | nins | br | calls | jtbl | leaf |
@@ -57,6 +57,8 @@ jump-table funcs : 84 (deferred — need the rodata-island workflow, Task
| 3 | SetData32 | 3 | 0 | 0 | - | Y |
| 3 | SetIR0 | 3 | 0 | 0 | - | Y |
| 3 | UT_KEYV_OBJ_174 | 3 | 0 | 0 | - | Y |
| 3 | func_8005E188 | 3 | 0 | 0 | - | Y |
| 3 | func_80062388 | 3 | 0 | 0 | - | Y |
| 4 | CD_set_test_parmnum | 4 | 0 | 0 | - | Y |
| 4 | C_011_OBJ_960 | 4 | 0 | 0 | - | Y |
| 4 | CdLastCom | 4 | 0 | 0 | - | Y |
@@ -113,6 +115,10 @@ jump-table funcs : 84 (deferred — need the rodata-island workflow, Task
| 4 | func_8005C4CC | 4 | 0 | 0 | - | Y |
| 4 | func_8005CF08 | 4 | 0 | 0 | - | Y |
| 4 | func_8005CF18 | 4 | 0 | 0 | - | Y |
| 4 | func_8005E194 | 4 | 0 | 0 | - | Y |
| 4 | func_8005F384 | 4 | 0 | 0 | - | Y |
| 4 | func_80062394 | 4 | 0 | 0 | - | Y |
| 4 | func_80062988 | 4 | 0 | 0 | - | Y |
| 4 | gfx2D_COM0_OBJ_30 | 4 | 0 | 0 | - | Y |
| 5 | C_004_OBJ_DC | 5 | 0 | 0 | - | Y |
| 5 | DsReadyCallback | 5 | 0 | 0 | - | Y |
@@ -122,9 +128,3 @@ jump-table funcs : 84 (deferred — need the rodata-island workflow, Task
| 5 | INTR_OBJ_6D0 | 5 | 0 | 0 | - | Y |
| 5 | ISO9660_OBJ_29C | 3 | 0 | 1 | - | - |
| 5 | LIBMCRD_OBJ_5C0 | 5 | 0 | 0 | - | Y |
| 5 | LIBMCRD_OBJ_840 | 3 | 0 | 1 | - | - |
| 5 | LIBMCRD_OBJ_A50 | 3 | 0 | 1 | - | - |
| 5 | LIBMCRD_OBJ_C90 | 3 | 0 | 1 | - | - |
| 5 | LIBMCRD_OBJ_F4C | 3 | 0 | 1 | - | - |
| 5 | OBJT_OBJ_108 | 2 | 1 | 0 | - | Y |
| 5 | OBJT_OBJ_110 | 2 | 1 | 0 | - | Y |
+27 -1
View File
@@ -409,7 +409,33 @@ single-library path never hit. All fixed in `tools/psyq_integrate.py`; reuse for
`psyq_identify <full-lib-dir> <lo> <hi>` over ONE block's range reports the right objects PLUS byte-
identical-`.text` aliases (GS_131≡RVWUNIT, PRESET2≡PRESET3, OBJT2≡OBJT3); keep the one that also
matches `.data/.rdata` (`psyq_link_region --verify` confirms per-object). Hardcode the disambiguated
object list in a committed regen script (`tools/make_libgs_block6.sh`) — SDK-derived dir, gitignored.
object list in a committed regen script (`tools/make_libgs.sh`) — SDK-derived dir, gitignored.
### §9.5 Integrating a WHOLE multi-block library in one call (full libgs — Phase 7 session G)
Block 6 alone (§9.4) proved the pattern; the full library is then ONE `psyq_integrate` call over all of
its used objects — no separate integration per block. integrate auto-splits the elf dir's vram-ordered
objects into contiguous runs (`contiguous_blocks`) and maps the i-th run to the i-th stub: pass one
block stub per contiguous run, and a plain asm stub per non-library gap (gaps keep their own subseg).
- **Derive the block/gap structure empirically (G1), don't trust the notes.** `psyq_identify <used-dir>`
prints each object's vram; a new block starts wherever the next object's vram ≠ the previous object's
end (`vram + nins*4`). libgs = 6 blocks, gaps 80/48/1536/48/304 B (the 1536 is the excluded GS_001).
Resegment the splat code subseg into `[game-pre][block1][gap1]…[blockN][gapN]…` (vram→file =
−0x8000F800); block subsegs become the `libgsN` integrate stubs, gap subsegs stay `gsgapN` asm stubs
(incl. the GS_001 gap). One `split_src_region.py trim` drops the old single-subseg's stubs from the
pre-file — but verify FIRST that **no matched C lives in [lo,hi)** (trim drops that range
unconditionally; here all matched fns were ≤0x8002Axxx, far below the 0x80051804 libgs base).
- **Short objects need the placement WINDOW (new `psyq_integrate <stubs> <lo> <hi>` arg).** An object
whose `.text` is too short to anchor uniquely over the whole EXE — libgs **GS_106** (8 ins; its pattern
recurs in game code) — is `ambiguous` in the default 0x80010000..0x800629DC scan, so it drops from the
placement map, its block splits, and the block↔stub count breaks. Pass the library's text window
(wired in the Makefile integrate call: `… libgs1,…,libgs6 0x80051804 0x80057928`); the object anchors
uniquely there. `tools/make_libgs.sh --verify` (psyq_link_region over the same window) is the preflight.
- **Result + checks:** 31 libgs objects / 6 blocks linked byte-identical in one integrate (≈49 named SDK
functions — count OBJECTS, not stubs: splat over-segments library code into ~5× more INCLUDE_ASM stubs
than real functions). The finer resegmentation (10 new subsegs) was **split-deterministic** across two
clean extracts (no new §9.4 boundary re-detection — the GsMulCoord2/3 declarations + the 53198 data
carve from §9.4 already cover it) and **byte-identical WITH or WITHOUT** the SDK objects (stub
fallback, fresh-clone-safe). The unified call cleanly supersedes the §9.4 block-6-only integration.
---
+9 -9
View File
@@ -1,17 +1,17 @@
# BFM matching progress (generated by tools/progress.py — authoritative)
REAL substantive matches : 42 <- the Gen1-exit >=25 bar counts THIS
NON_MATCHING (near-miss) : 4
REAL substantive matches : 43 <- the Gen1-exit >=25 bar counts THIS
NON_MATCHING (near-miss) : 7
splat-auto empty no-ops : 42
INCLUDE_ASM stubs : 1962
data blobs (excluded) : 4
INCLUDE_ASM stubs : 2001
data blobs (excluded) : 2
----------------------------------------
matchable functions : 2050
REAL / matchable : 42 / 2050 = 2.05%
incl. empties : 84 / 2050 = 4.10%
matchable functions : 2093
REAL / matchable : 43 / 2093 = 2.05%
incl. empties : 85 / 2093 = 4.06%
REAL matches: CdQueueBusy CdReadRequest DebugMenuHandler GameModeDispatch LoaderResetReadState ResourceGetCdLoc func_80012AB0 func_800168B4 func_80018F20 func_80019018 func_80019198 func_80019378 func_80019388 func_80019398 func_800193A8 func_8001AA78 func_8001AA88 func_8001B22C func_8001B374 func_8001B384 func_8001B85C func_8001BE20 func_8001BFA0 func_8001BFE8 func_8001D0E8 func_80029254 func_80029264 func_80029504 func_80029514 func_8002953C func_8002954C func_8002957C func_8002958C func_80029FD4 func_8002A26C func_8002A27C func_8002A4B8 func_8002A4C8 func_8002A728 func_8002A738 func_8002A998 func_8002A9A8
NON_MATCHING: LoaderInitFileTable ResourceLoadStateMachine func_80015A74 func_80016714
REAL matches: CdQueueBusy CdReadRequest DebugMenuHandler GameModeDispatch LoaderResetReadState LzssDecodeSector ResourceGetCdLoc func_80012AB0 func_800168B4 func_80018F20 func_80019018 func_80019198 func_80019378 func_80019388 func_80019398 func_800193A8 func_8001AA78 func_8001AA88 func_8001B22C func_8001B374 func_8001B384 func_8001B85C func_8001BE20 func_8001BFA0 func_8001BFE8 func_8001D0E8 func_80029254 func_80029264 func_80029504 func_80029514 func_8002953C func_8002954C func_8002957C func_8002958C func_80029FD4 func_8002A26C func_8002A27C func_8002A4B8 func_8002A4C8 func_8002A728 func_8002A738 func_8002A998 func_8002A9A8
NON_MATCHING: CdReadSectorReadyCB CdReadStateMachine LoaderInitFileTable ResourceLoadStateMachine StreamLoadStateMachine func_80015A74 func_80016714
build SHA1: 143dbb89f34491258bbc27810d0a12ec8b43a8dd (byte-identical)