#!/usr/bin/env python3 """Turn a Mesen CDL capture into tracer seed addresses. Mesen's Code Data Logger records, per PRG byte, whether the CPU fetched it as an opcode (Code), read it as data (Data), whether it was executed, and - the flag this tool wants - whether the byte is a **jump target**: the address some branch or jump in the ROM points at. That last flag is what a static tracer cannot work out on its own. The tracer follows the control flow from the vectors; a game whose dispatch table is computed at run time keeps the tracer at the reset stub. Feeding it the addresses the CPU actually branched to lets the trace reach the same code the CPU did, and the tracer's own rules (a control-flow target wins over a fall-through decode) then produce the right instruction boundaries. Usage: python3 _tools/cdl_seeds.py [--base 0x8000] [--all-code] """ import argparse from pathlib import Path CODE = 0x01 DATA = 0x02 EXEC = 0x04 JUMP = 0x08 def read_cdl(path, size=32768): raw = Path(path).read_bytes() if raw[:5] == b"CDLv2": body = raw[9:9 + size] else: body = raw[-size:] if len(body) < size: raise SystemExit(f"{path}: expected {size} CDL bytes, got {len(body)}") return body def main(): ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap.add_argument("cdl") ap.add_argument("--base", default="0x8000") ap.add_argument("--size", type=int, default=32768, help="the PRG size in bytes (the CDL holds one flag per " "PRG byte)") ap.add_argument("--all-code", action="store_true", help="seed the start of every run of code bytes as well as " "the jump targets") args = ap.parse_args() base = int(args.base, 0) body = read_cdl(args.cdl, args.size) jumps = [base + i for i, f in enumerate(body) if f & JUMP] print(f"# {len(jumps)} jump-target address(es) in the capture") if args.all_code: # the first byte of every run of code extra = [] prev_code = False for i, f in enumerate(body): code = bool(f & CODE) if code and not prev_code: extra.append(base + i) prev_code = code print(f"# plus {len(extra)} run start(s)") jumps = sorted(set(jumps) | set(extra)) print(",".join(f"0x{a:X}" for a in jumps)) if __name__ == "__main__": main()