3.0 KiB
§448 ★★★ — ASSEMBLY POSING AS C: 154 GAME FUNCTIONS THE REPORTS COUNTED AS DONE (P31 S75)
A .c file in src/ looks decompiled. 199 functions in this tree are not: they are the target
assembly pasted into a C string literal (§265), byte-identical BY CONSTRUCTION and completely
unexplained. 45 are PsyQ/CRT routines where that is defensible; 154 are game code, 171 of the 199
in main alone, the largest being SaveLoadRoutine at 1,165 instructions.
They were invisible because progress.py's classify() matched INCLUDE_ASM, INCLUDE_RODATA and
C definitions — and a file-scope __asm__ block is none of those, so each one landed in NO bucket:
either swallowed by a surrounding construct or surfacing as the single UNPLACED (parse hole) line.
Main's headline moved 45.88% → 42.15% once they were counted; nothing regressed, the denominator
had simply been missing 173 functions that are real work.
THE COUNTING LESSON, WHICH IS THE REUSABLE PART. Counting them by hand went 116 → 112 → 108 → 178 → 199 across five attempts in one session, every intermediate number reported with confidence. Every error was the same shape — a pattern narrower than the claim it supported:
- the sources use BOTH
".ent\tNAME\n"(escaped tab) and".ent NAME\n"(literal space); a pattern anchored on either one silently drops every instance of the other; - a bare fragment
".ent\t"also occurs, and an optional-\tpattern captured the literal nametfrom it, six times, which is the only reason the error was noticed at all; __asm__appears in 3,182 files, almost all of it the §3a zero-byte cross-jump barrier, so counting files — or counting__asm__— measures nothing;- a
.globl NAME+NAME:pair proves the symbol is EXPORTED, not CODE: the first real run reportedjtbl_80072ED4/EEC/F0C/F24as four "functions", when they are jump tables sitting in the same block; - a hand-written SDK name list reported 170 game functions because it did not know
VectorNormalSS,SquareRoot12andOuterProduct12are libgte — an incomplete list inflates exactly the number that matters.
tools/asm_in_c.py is the answer to all five, and its design is the lesson: three independent
detectors that must AGREE, with disagreement reported as a defect (R34 — that is what caught the
jump tables); SDK-ness derived from the 14 shipped PsyQ archives via nm (2,227 symbols) rather
than a list (R33); coverage asserted, so a definition-shaped block no detector claims fails loudly
(R32/R43); and a --selftest carrying a known-true case of every spelling plus the phantom t and
the jump-table regression, so the next spelling change fails loudly instead of quietly returning a
smaller number.
The law: when a count comes from a text pattern, the pattern has a denominator too. Validate it against one known-true case of every FORM the corpus contains before quoting the number — and if you cannot enumerate the forms, you do not yet know what you are counting.