#!/usr/bin/env python3 """List the regions a Mesen CDL capture says are data, as nesdis.py --data args. The CDL marks each PRG byte as Code (the CPU fetched it as an opcode), Data (the CPU read it as data) or neither. A byte that is Data and not Code was never executed - so a static tracer that walks into it is walking into data. Those regions are handed to nesdis.py's --data option, which forces them back to data instead of letting the fall-through decode run away. The runs are merged across gaps of a few bytes, because a short code island inside a data region is usually a table of jump targets rather than code. Usage: python3 _tools/cdl_data_regions.py [--size 32768] [--gap 4] """ import argparse from pathlib import Path CODE, DATA = 0x01, 0x02 def main(): ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap.add_argument("cdl") ap.add_argument("--size", type=int, default=32768) ap.add_argument("--base", default="0x8000", help="the CPU address the PRG is mapped at; the CDL's " "offsets are relative to the start of the image") ap.add_argument("--gap", type=int, default=4, help="merge runs separated by fewer than this many bytes") args = ap.parse_args() base = int(args.base, 0) raw = Path(args.cdl).read_bytes() if raw[:5] == b"CDLv2": body = raw[9:9 + args.size] else: body = raw[-args.size:] if len(body) < args.size: raise SystemExit(f"{args.cdl}: expected {args.size} CDL bytes, " f"got {len(body)}") # a byte is data when the CPU read it and never fetched it as an opcode isdata = [bool(body[i] & DATA) and not (body[i] & CODE) for i in range(args.size)] # the runs of data runs = [] i = 0 while i < args.size: if isdata[i]: j = i while j < args.size and isdata[j]: j += 1 runs.append([i, j - 1]) i = j else: i += 1 # merge the runs that are close together: a short code island inside a # data region is usually a table of jump targets rather than code merged = [] for lo, hi in runs: if merged and lo - merged[-1][1] <= args.gap: merged[-1][1] = hi else: merged.append([lo, hi]) total = sum(hi - lo + 1 for lo, hi in merged) print(f"# {len(merged)} data region(s), {total} bytes") out = [] for lo, hi in merged: out.append(f"0x{lo + base:04X}:{hi - lo + 1}") print(",".join(out)) if __name__ == "__main__": main()