Files
BFM-decomp/tools/decompile.py
T
Drew T 5f937bdda5 feat(phase-6): compiler pinned + matching loop proven (14 matches) — LZSS to Phase 7
- splat.us.exe.yaml: text subseg asm->c (INCLUDE_ASM scaffold); include/common.h
  (committed prelude); Makefile c-rule cpp->cc1->maspsx->as + -Map
- PIN (G8): gcc-2.7.2-psx -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker
  + maspsx --aspsx-version=2.56 --expand-div; byte-exact on func_80018F20 +
  instruction-identical across 3 idiom classes; --expand-div REQUIRED for div/rem
  (SETUP.md §5.4 PINNED, Makefile MASPSX_FLAGS)
- harness: diff_settings.py (asm-differ mipsel object mode), tools/decompile.py
  (m2c wrapper), tools/permuter/ (decomp-permuter harness + objdump shim), expected/
- 14 real functions matched (asm-differ score 0): func_80018F20 + 13 accessors;
  +14 splat-auto empty no-ops; make check BYTE-IDENTICAL throughout
- 2 instruction-identical near-misses guarded NON_MATCHING (func_80015A74 scheduling,
  func_80016714 phantom frame) -> decomp-permuter candidates
- flywheel: docs/matching-cookbook.md, memory + CLAUDE.md step 5 (consult+evolve), R16
- requirements-python.txt: venv freeze (pycparser<3.0 pinned — permuter needs plyparser)
- LZSS spike: needs jump-table-in-rodata workflow (102 tables, mixed blob) -> Phase 7
- venv: asm-differ + permuter deps installed
- bumps project version 1.5.0 -> 1.6.0
2026-06-14 14:47:01 -06:00

60 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""decompile.py — m2c scaffold helper for the BFM matching loop (Phase 6+).
Given a function's splat label (e.g. func_80015A74, main, GameModeDispatch), locate
its disassembly under asm/nonmatchings/ and run m2c (target mipsel-gcc-c) to emit a C
scaffold to stdout. The scaffold is a STARTING POINT only — refine it by hand against
asm-differ until the diff score is 0 (G3), then paste the matched body into src/ in
place of the INCLUDE_ASM stub.
Usage:
tools/decompile.py <function> # m2c scaffold -> stdout
tools/decompile.py <function> --context src/800.c # seed m2c with existing C types
tools/decompile.py <function> --stack-structs # also emit stack-struct templates
Notes:
- m2c parses splat's annotated .s natively (no preprocessing needed).
- For complex functions, Ghidra's decompiler (via MCP get_code) is often a richer
starting point than m2c; use whichever is closer, then converge with asm-differ.
"""
import argparse
import glob
import os
import subprocess
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
M2C = os.path.join(REPO, "tools", "m2c", "m2c.py")
def find_asm(fn):
return glob.glob(os.path.join(REPO, "asm", "nonmatchings", "**", fn + ".s"), recursive=True)
def main():
ap = argparse.ArgumentParser(description="m2c scaffold helper for the BFM matching loop")
ap.add_argument("function", help="splat function label, e.g. func_80015A74")
ap.add_argument("--context", help="preprocessed C file to seed m2c types/structs (e.g. src/800.c)")
ap.add_argument("--target", default="mipsel-gcc-c", help="m2c target (default: mipsel-gcc-c)")
ap.add_argument("--stack-structs", action="store_true", help="emit stack-struct templates")
args = ap.parse_args()
hits = find_asm(args.function)
if not hits:
sys.exit(f"decompile.py: no asm/nonmatchings/**/{args.function}.s "
f"(is {args.function} still an INCLUDE_ASM stub in src/?)")
if len(hits) > 1:
sys.stderr.write(f"decompile.py: {len(hits)} matches, using {hits[0]}\n")
cmd = [sys.executable, M2C, "-t", args.target, "-f", args.function]
if args.context:
cmd += ["--context", args.context]
if args.stack_structs:
cmd.append("--stack-structs")
cmd.append(hits[0])
sys.exit(subprocess.run(cmd).returncode)
if __name__ == "__main__":
main()