modemwars/tools/chunks.py
2026-08-23 02:09:40 -05:00

72 lines
3 KiB
Python

#!/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()