72 lines
3 KiB
Python
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()
|