Commit Graph

11 Commits

Author SHA1 Message Date
Christopher Williams 9a49ee62f7 phase8: close the assembler-anomaly and gp-site classes, reaching 109 bodies
Two harness gaps were closed as per-region overrides, both found by the workers
and implemented by the coordinator:

1. maspsx=off. Worker A isolated the $at-macro-store-in-the-jr-delay-slot shape
   with a decisive experiment: cc1 emits an empty delay slot, maspsx fills it with
   a nop, and GNU as in reorder mode fills it properly by moving the last half of
   the expanded store. Six regions need this, including 0x800F3160 -- Phase 5's
   first recorded bounded negative, unreachable for three phases -- and 0x800F8AEC,
   0x800F8B58, 0x800F8B6C, 0x800F8FE4, 0x80102B10. Worker B reverse-engineered the
   same mechanism independently from the cc1 output.

2. gp=-NAME. The gp marker is per symbol, but the original's access form is per
   SITE: 0x80121F84 is read gp-relative at 0x800A80BC and written absolutely at
   0x8002D288, in the same function. A byte search confirmed 6 gp-relative
   accesses at d=0x64c, so worker B's census row was right and worker A's source
   was right; only a per-region exclusion can express both. This makes cookbook
   finding 10 incomplete.

Both overrides are validated (199 tests, 10 added), and sf3_merge gained
--skip-registered because a worker's claims file is naturally cumulative.

Cycle-2 merge: 66 claims, 58 accepted, 8 skipped, 0 rejected. Candidate gate:
c_regions=115, 0 differing bytes, SHA-1 e173426c. Promoted, then make check green:
199 tests, regions=115 disagreements=0, c_regions=115 MATCH.

Distinct matched bodies: 51 -> 109, past the phase milestone of 70.
2026-09-23 23:05:35 -04:00
Christopher Williams 7c299aca26 phase8: merge worker B's first eight claims and 335 gp rows
P8-T2's protocol record plus worker B's cycle-1 merge.

Worker B verified 8 claims (7 bodies: g0029 is a two-address duplicate group).
It also classified its whole 946-row partition off one objdump and found that 226
rows (24%) access gp-relative globals, staging 335 distinct addresses. Those were
checked before use: every row is arithmetically exact under the list's decimal d
column (335 rows, 0 errors), and four sampled globals were confirmed against my
own Ghidra disassembly (0x564(gp) -> 0x80121E9C and three others). A risk check
for whether a new gp marker could change an already-matched region found only
worker A's unclaimed in-progress files, so no registered region is affected.

323 rows were new (12 already present), taking config/symbols.tsv to 354 rows and
unblocking a quarter of worker B's partition.

Candidate gate: c_regions=57, 0 differing bytes, SHA-1 e173426c. Promoted, then
make check green: 189 tests, regions=57 disagreements=0, c_regions=57 MATCH.
Distinct matched bodies: 34 -> 51.

Worker B also deferred 0x8001D98C (g0053, 436B, GTE 3x3 transform) into the
bounded GTE class, and recorded two byte-proven cookbook findings: BCD nibble
pairs must be their own parenthesised subexpression (a pure association tell, 9
shifted bytes), and a constant absolute address in a load folds the
carry-adjusted low half into the load displacement rather than using lui+ori.
2026-09-23 22:55:42 -04:00
Christopher Williams d7047e08c7 phase8: add the claim-merge tooling and merge worker A's first ten claims
P8-T2 plus the first verified cycle-1 merge.

tools/sf3_merge validates worker claims before anything tracked changes: a claim
is accepted only if its extent exists in the derived extents table and is graded
exact with exactly the claimed end, its source is a repo-relative
src/func_XXXXXXXX.c that exists, and it overlaps neither the registry nor another
worker's claim. Rejections are reported with reasons and nothing is written.

The workflow it enables is stronger than the plan's wording: merge to a CANDIDATE
registry, gate the candidate, and promote only on MATCH, so the tracked registry
never contains an unverified claim.

Worker A reported 10 claims (target 8) and correctly refused to edit the shared
symbol registry itself, instead requesting 9 gp-marked rows; each was checked
arithmetically as gp + d. Candidate gate: c_regions=49, 0 differing bytes, SHA-1
e173426c. Promoted, then make check green: 189 tests, regions=49
disagreements=0, c_regions=49 MATCH.

Worker A also reported a significant blocker: two of the remaining duplicate
groups are GTE (COP2) bodies -- 0x80018CB0 (3 addresses) and 0x8001084C (2
addresses, the 712-byte shared body) -- plus 0x80103A94 and 0x80103B60. That is
four GTE functions, which raises the value of the bounded SDK-shape
investigation in P8-T5.
2026-09-23 22:52:08 -04:00
Christopher Williams dd807ffb8c phase7: match 22 more function bodies from the worklist
Both batch targets are met in one pass: 22 new bodies (23 including the P7-T5
match), taking the project from 11 distinct bodies to 34 -- past the phase
milestone of 30 -- across 39 registered regions.

The batch is dominated by the small leaf shapes the worklist ranks first:
empty function (4 duplicate addresses), byte/halfword/word setters, field
getters, a word-buffer clearer, and eight gp-relative getters/setters. Every one
was byte-identical on the first or second attempt, and every extent came from
tools/sf3_extents rather than a hand-derived end.

Three shapes needed diagnosis, and two were solved:

- 0x8003636C was 16 bytes instead of 12 because `return 1` forced a second
  `li v0,1` into the delay slot; the original leaves 1 in v0 as scratch, so the
  function is `void`.
- 0x800F7FB4 needed the parameter reused as the loop counter.

Four candidates are recorded as bounded negatives or deferrals rather than
guesses:

- 0x800F7FB4: the loop body reproduces exactly, but the original has no frame
  and every tested formulation allocates 8 bytes (five C forms, and an
  -O1/-O2/-O3/-fomit-frame-pointer matrix). Stopped after two distinct attempts.
- 0x800F8AEC: the original schedules `lui` / `jr ra` / `addiu` with the low half
  in the delay slot; the reconstruction emits `lui` / `addiu` / `jr ra` / `nop`.
- 0x80010810: GTE (COP2) code needing the SDK's GTE macros, which the build has
  no headers for.
- 0x800FB5D4 (`move v0,sp`) and 0x80103FCC/0x80103FEC (BIOS `syscall` wrappers)
  and 0x8001EAFC (a shared jump block, not a standalone function).

make gate: c_regions=39, 0 differing bytes, SHA-1 e173426c. Extents verify agrees
on all 39 regions. 168 synthetic tests.
2026-09-23 22:34:02 -04:00
Christopher Williams 399da98a83 phase7: resolve address-named symbols implicitly and fail loudly otherwise
Every cross-reference used to need a hand-written config/symbols.tsv row, which
does not scale to a batch. A symbol whose name is an address now resolves to that
address with no row: func_XXXXXXXX, D_XXXXXXXX, g_XXXXXXXX, lbl_XXXXXXXX. This is
the convention the registry already used, applied without the row.

The names come from the object's own undefined-symbol list (nm -u), not from a
guess about the source, so a name the source defines but never references is
never mistaken for one needing resolution. A registry row still wins, which is
how a gp marker or a real name is attached. Anything neither registered nor
address-shaped now fails before the link with a message naming the symbol and the
fix, instead of a bare ld diagnostic.

First match from the worklist: func_800F8F9C (36 bytes, worklist rank 3), a
duplicate-group representative with a frame and a call. It matched on the first
attempt and is registered twice (0x800F8F9C, 0x80109314) against one source, so
two functions were matched for one body. Its callee needed no registry row.
Ghidra's independent body agrees with the derived extent.

make gate: c_regions=14, 0 differing bytes, SHA-1 e173426c. 168 synthetic tests.
2026-09-23 22:26:08 -04:00
Christopher Williams 2507994ac3 phase6: correct the compiler to PsyQ 4.0 (gcc-2.7.2-psx) and register the framed batch 2026-09-23 21:37:15 -04:00
Christopher Williams 8ece49c130 phase6: register the leaf/gp batch and record the framed-function blocker 2026-09-23 21:24:15 -04:00
Christopher Williams d379b84ec3 phase6: reproduce a gp-relative function and record the small-data evidence 2026-09-23 21:09:07 -04:00
Christopher Williams 4688662cf4 phase6: wire maspsx and link-time symbols, resolving the ASPSX la form 2026-09-23 21:01:20 -04:00
Christopher Williams 673edb2ded phase6: add the symbol registry and per-region flag overrides, and register 0x8002D2A0 2026-09-23 20:54:10 -04:00
Christopher Williams ff35291d40 phase5: determine the entry is CRT startup and match the first C function
P5-T5. Part A: the entry [0x800FB368,0x800FB410) is not compiler output. The
return address is round-tripped through an absolute global around the first
call, the range ends in break, the stack pointer is built from linker globals,
and the clear loop falls through with no jr ra. Two bounded compile experiments
failed and were stopped; the entry stays fallback and no C is claimed.

Part B: first byte-identical C match -- func_80017AD4 at 0x80017AD4..0x80017AE8
(20 bytes), src/func_80017AD4.c, registered in config/regions.tsv. make gate
reports c_regions=1, 0 differing bytes, SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
Body is unique with one caller at 0x80014C14; a shared-tail near-miss was
checked and rejected as a duplicate. The Phase 3 baseline is unaffected.

Records six codegen findings, notably that GNU as expands the la macro with ori
while the original assembler (ASPSX 2.81) uses addiu, so la-using functions will
need maspsx. No ROM-derived material is tracked.
2026-09-23 20:39:40 -04:00