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