#!/usr/bin/env python3 # chunks.py - split the rendered listings into agent-sized chunks at routine boundaries. # Writes disassembly/build/chunks.json : [{id, file, unit, startAddr, endAddr, startLine, endLine, codeLines}] import os, re, json, sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OUT = os.path.join(ROOT, "disassembly") TARGET = int(sys.argv[1]) if len(sys.argv) > 1 else 700 # target code lines per chunk ADDR_RE = re.compile(r";\s([0-9A-F]{4})(\s|$)") def chunkFile(relPath): lines = open(os.path.join(OUT, relPath)).read().split("\n") # candidate cut points: a line that is a label definition for a sub_/named routine, whose previous # non-empty line is rts/rti/jmp or a data row or a comment banner insnCount = 0 cuts = [] addrAt = {} lastAddr = None for i, l in enumerate(lines): m = None if l.startswith(";") else ADDR_RE.search(l) # ignore comment-only lines if m: lastAddr = int(m.group(1), 16) addrAt[i] = lastAddr if re.match(r"^[A-Za-z_][A-Za-z0-9_]*:\s*$", l) and i > 0: j = i - 1 while j > 0 and (lines[j].strip() == "" or lines[j].startswith(";")): j -= 1 prev = lines[j].strip() if re.match(r"^(rts|rti|jmp)\b", prev) or prev.startswith(".byte") or prev.startswith(".word") or prev.startswith(".addr"): cuts.append(i) orgLine = next(i for i, l in enumerate(lines) if l.strip().startswith(".org")) codeLineIdx = [i for i in range(len(lines)) if re.match(r"^\s+[a-z]{3}\b", lines[i])] chunks = [] start = orgLine + 1 count = 0 lastCut = start for i in range(start, len(lines)): if re.match(r"^\s+[a-z]{3}\b", lines[i]): count += 1 if i in cuts and count >= TARGET: chunks.append((lastCut, i - 1)) lastCut = i count = 0 chunks.append((lastCut, len(lines) - 1)) out = [] for s, e in chunks: addrs = [addrAt[i] for i in range(s, e + 1) if i in addrAt] if not addrs: continue n = sum(1 for i in range(s, e + 1) if re.match(r"^\s+[a-z]{3}\b", lines[i])) out.append({"file": relPath, "startAddr": f"{min(addrs):04X}", "endAddr": f"{max(addrs):04X}", "startLine": s + 1, "endLine": e + 1, "codeLines": n}) return out def main(): files = [] for d in ("boot", "drive", "game"): for f in sorted(os.listdir(os.path.join(OUT, d))): if f.endswith(".s"): files.append(f"{d}/{f}") allChunks = [] for f in files: for c in chunkFile(f): c["id"] = f"{f.replace('/', '_')[:-2]}_{c['startAddr']}" c["unit"] = f[:-2] allChunks.append(c) json.dump(allChunks, open(os.path.join(OUT, "build", "chunks.json"), "w"), indent=1) for c in allChunks: print(f"{c['id']:<34} lines {c['startLine']:>5}-{c['endLine']:>5} ${c['startAddr']}-${c['endAddr']} code lines {c['codeLines']}") print(len(allChunks), "chunks") main()