modemwars/swiftlink/parseTrace.py
2026-08-23 02:09:40 -05:00

72 lines
2.9 KiB
Python

#!/usr/bin/env python3
# parseTrace.py - turn a raw VICE tracepoint log from testHarden.py into an ordered access list and
# a per-address / per-page census.
#
# python3 parseTrace.py testLogs/harden.de00.A.cold.txt [more logs ...]
#
# The monitor announces a tracepoint hit as a header line and then disassembles the instruction that
# caused it, with the CPU registers after it executed:
#
# #1 (Trace store de03) 40/$028, 50/$32
# .C:e567 8D 03 DE STA $DE03 - A:1E X:00 Y:24 SP:ed ..-..... 213365345
#
# For a store, A is the byte written; for a load, A is the byte read. Note that VICE prints the
# register values with UPPERCASE hex digits and the program counter with lowercase ones, which is
# the sort of detail that silently drops every access whose value contains A-F if the pattern only
# allows one case.
import re
import sys
TRACE_HEAD = re.compile(r"#(\d+)\s+\(Trace\s+(\w+)\s+([0-9a-f]{4})\)")
DISASM = re.compile(r"\.C:([0-9a-f]{4})\s+((?:[0-9A-F]{2} )+)\s*(\S+)\s+(\S*)\s*-\s*A:([0-9A-Fa-f]{2})")
def parse(text):
events = []
lines = text.splitlines()
for index, line in enumerate(lines):
head = TRACE_HEAD.search(line)
if not head:
continue
hit = DISASM.search(lines[index + 1]) if index + 1 < len(lines) else None
events.append({"kind": head.group(2), "addr": int(head.group(3), 16),
"pc": int(hit.group(1), 16) if hit else None,
"op": hit.group(3) if hit else "?",
"a": int(hit.group(5), 16) if hit else None})
return events
def census(events):
counts = {}
pages = {}
for event in events:
counts[(event["addr"], event["kind"])] = counts.get((event["addr"], event["kind"]), 0) + 1
page = event["addr"] & 0xFF00
pages[page] = pages.get(page, 0) + 1
return counts, pages
def main():
for path in sys.argv[1:]:
events = parse(open(path, encoding="latin-1").read())
counts, pages = census(events)
print(f"=== {path}: {len(events)} accesses ===")
unpaired = [e for e in events if e["a"] is None]
if unpaired:
print(f" WARNING: {len(unpaired)} headers had no disassembly line after them")
for addr, kind in sorted(counts):
print(f" ${addr:04X} {kind:5s} x {counts[(addr, kind)]}")
for page in sorted(pages):
print(f" page ${page:04X}: {pages[page]} accesses")
first = events[:14]
print(" first accesses, in order:")
for event in first:
print(f" {event['kind']:5s} ${event['addr']:04X} {event['op']} at "
f"${event['pc']:04X} A=${event['a']:02X}")
ctrl = [e for e in events if e["kind"] == "store" and (e["addr"] & 0x00FF) == 3]
print(f" every control-register write, in order: "
f"{' '.join('$%02X' % e['a'] for e in ctrl)}")
if __name__ == "__main__":
main()