BYTE-IDENTICAL with NO function banked (gate_main --assert-baseline, clean rebuild),
which is the whole point: the structure lands first and proves neutral, then drafts bank
against it.
One code object contributes exactly ONE contiguous .rodata run, and 800.o's is span A,
so spans B and C each needed their own object:
800 vram 0x800123F0-0x8002B0B4 -> .rodata span A (0x80072A38-0x80072C70)
800_b vram 0x8002B0B4-0x80035270 -> .rodata span B (0x80072E44-0x80073140)
800_c vram 0x80035270-0x8003A444 -> .rodata span C (0x800732A0-0x8007344C)
The span owners' address ranges are disjoint and ordered — tables pack tight WITHIN a
TU and are separated by other data ACROSS TUs — so these are (at least some of) the
original translation-unit boundaries. Splitting here is both the fix and the minimum;
any extra split would be speculation.
main's island is now a 7-piece data->rodata sandwich, so ld_interleave moves from
--front/--tail to --order.
THE SPLIT WAS CHEAP, AND MY FIRST ESTIMATE WAS WRONG. I costed it at '2,318 scattered
extern lines' — that is the TOTAL; what matters is how many CROSS a boundary, and that
is 57 of 1,247 declared names (4.6%), of which 19 are typedefs with exactly one
definition each and zero shape conflicts. Zero file-local statics. src/800_shared.h
carries exactly those, derived from the COMPILER's own errors rather than a regex model
of C (R33), and each typedef was MOVED, never copied.
Unlocks 17 functions / 4,471 instructions = 39% of what is left in main, incl.
SaveLoadRoutine (1139) and func_8003388C (663).