Skip to content

overlays: list hand-written level functions as original assembly - #101

Merged
Lynder063 merged 1 commit into
OpenRAC:mainfrom
xx255q:levels/handwritten-asm
Oct 19, 2026
Merged

Lynder063 merged 1 commit into
OpenRAC:mainfrom
xx255q:levels/handwritten-asm

Conversation

@xx255q

@xx255q xx255q commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Level code has functions written as assembly in the original, like the executable's config/handwritten_asm.txt. Until now it had no way to say so: they sit as INCLUDE_ASM stubs that can never be matched in C and are counted as unfinished. This adds the level-code counterpart, built the same way as the level linker remnants (config/overlays/linker_remnants.txt / LINKER_REMNANT).

docs/ASM_CLASSIFICATION.md notes that changing the published progress policy needs upstream review, so this is a proposal. Happy to narrow the list or change the approach.

What changes

  • config/overlays/handwritten_asm.txt: 67 entries, covering 94 catalogue entries / 51,748 bytes once joined pieces are counted.
  • src/overlays/: each entry's INCLUDE_ASM("asm/overlays", …) becomes ASM_FUNC("asm/overlays", …). ASM_FUNC is INCLUDE_ASM, so the image is unchanged.
  • tools/gen_progress_report.py:
    • counts listed functions as finished, in both the build and --check;
    • checks the list against the ASM_FUNC lines and the catalogue, sharing the remnants' check (overlay_listed());
    • finishes later pieces of a listed joined function with it, as for C.
  • tools/overlay_variants.py: its stub pattern accepts ASM_FUNC, as it already does LINKER_REMNANT.
  • docs/ASM_CLASSIFICATION.md: a "Hand-written level functions" section with the rule and evidence.

Which functions
The executable's rule: every function the disassembler marks "Handwritten function" (87 entries, 67 after folding joined pieces into their first names). They hold instructions ee-gcc 2.9 doesn't emit: a trapping addi/teq, VU0 macro instructions, MMI, $at as a working register.

  • Two joined functions are listed under first pieces the disassembler didn't mark. Their other pieces are marked, and the first pieces hold VU0/MMI themselves: func_L00_001FD1D8 (lqc2) and func_L00_00257F4C (prot3w).
  • Two are listed by hand:
    • func_L00_00251328: a nop before jr $31 with the slot filled;
    • func_L00_00251358: $at as an ordinary register, and the jr slot left empty.

Cross-check against the NTSC-U decompilation (Lombyte, MIT)

  • 87 of the 89 are assembly there too: INCLUDE_ASM stubs, or copies of executable functions it keeps under assembly/.
  • It documents the two hand-listed ones as "hand-written in retail" for the same reasons.
  • The other two have no NTSC-U counterpart: func_L00_0024EC20 and func_L01_00262BB0 are teq $0, $0, 0 trap stubs.

Every listed function already works in the native port
None of these are marked finished without working code behind them. Checked against OpenRAC's port/game/rac1-pal (no_c.tsv, hand/hand.tsv, libraries.tsv) and port/game/rac1/host:

  • 48 of the 67 entries have port C here, in nonmatching/functional.tsv (the entry or one of its joined pieces).
  • 13 are implemented by the port:
    • host/collision.c: func_L00_001EFFF0, func_L00_001F10E0, func_L00_001F1D20, func_L00_001F2BE8, func_L00_001F34F0;
    • host/collision.c + host/render_native.c: func_L00_00254218;
    • host/render_native.c: func_L00_001FE688, func_L00_002963D8;
    • host/anim.c: func_L00_00252D20;
    • host/effect_quad.c: func_L00_001FD1D8;
    • hand/: func_L02_00250A58, func_L00_002539C8;
    • behind a host function: func_L00_001EEBD8.
  • 6 need no C of their own (no_c.tsv classes a/b): pieces of a function the port replaces whole, or unreachable. They are func_L00_001EF944, func_L00_001F080C, func_L00_001FEFE0, func_L00_002535C8, func_L00_00254A70 and func_L01_00262BB0.
    Left out: func_L00_002569F8 + func_L00_00257848, the level programs' copy of MobyProc (5,152 bytes, as func_00212658 is). The disassembler marks both, but the port does not run this copy yet (no_c.tsv: needs C). It replaces the executable's MobyProc in host/render_native.c, so they stay INCLUDE_ASM until the port does the same for the level copy.

Functions already done in C are untouched.

Checks

  • tools/overlay_file_check.py on the 17 changed level files: 0 problems (ASM_FUNC lines assemble to the same bytes).
  • progress/report.json regenerated: 4,904 → 4,998 of 5,121 functions finished (+94: the 67 entries and their joined pieces), code finished 95.14% → 96.53% (level code 95.39% → 96.98%).
  • python tools/gen_progress_report.py --check passes.

🤖 Generated with Claude Code

Level code gets the counterpart of config/handwritten_asm.txt:
config/overlays/handwritten_asm.txt (67 entries, 94 catalogue entries with
joined pieces), marked ASM_FUNC("asm/overlays", ...) in src/overlays/ and
counted as finished by tools/gen_progress_report.py, which checks the list
against the source as it does the level linker remnants. The entries are
the disassembler's "Handwritten function"s plus two listed by hand; every
one already has working C for the native port. The level copy of
MobyProc is left out until the port runs it. docs/ASM_CLASSIFICATION.md
has the rule and the evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bytematchdev

bytematchdev Bot commented Oct 19, 2026

Copy link
Copy Markdown

ByteMatch progress report

Code matched 96.53% (+1.39%) · 3.58 MB of 3.71 MB
Fuzzy match 97.17% (+1.39%)
Functions 4,998 (+94) / 5,121
Data matched 0.52%
Units complete 112 (+1) / 141
✅ Newly matched (94)
  • func_L00_001F1D20 (3,784 bytes)
  • func_L00_001EEBD8 (3,436 bytes)
  • func_L00_00252018 (3,284 bytes)
  • func_L00_001F10E0 (3,136 bytes)
  • func_L00_00255B90 (2,664 bytes)
  • func_L00_001F2BE8 (2,248 bytes)
  • func_L00_00252D20 (2,216 bytes)
  • func_L00_001F080C (2,212 bytes)
  • func_L00_00254218 (2,136 bytes)
  • func_L00_001EFFF0 (1,976 bytes)
  • func_L00_002539C8 (1,896 bytes)
  • func_L00_001EF944 (1,548 bytes)
  • func_L00_001F34F0 (1,128 bytes)
  • func_L00_00254A70 (1,040 bytes)
  • func_L00_002535C8 (1,024 bytes)
  • func_L06_00216B38 (940 bytes)
  • func_L01_00262FE8 (848 bytes)
  • func_L02_00250C78 (836 bytes)
  • func_L02_00250720 (824 bytes)
  • func_L01_0021F9C8 (784 bytes)
  • func_L00_001FE688 (696 bytes)
  • func_L01_00263338 (656 bytes)
  • func_L00_001FD428 (640 bytes)
  • func_L00_001FD1D8 (592 bytes)
  • func_L00_002963D8 (584 bytes)
  • func_L01_00262DA8 (576 bytes)
  • func_L02_00250A58 (544 bytes)
  • func_L00_001FDE48 (500 bytes)
  • func_L00_0025805C (496 bytes)
  • func_L00_00251B58 (448 bytes)
  • …and 64 more

Commit 1619a1e · Ratchet & Clank (PAL) on ByteMatch

@Lynder063
Lynder063 merged commit 5b7e23a into OpenRAC:main Oct 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants