Skip to content

Instantly share code, notes, and snippets.

@zydeco
Created September 22, 2020 21:59
Show Gist options
  • Save zydeco/9c0bffeb87b883ec761cdabfc3b7f71c to your computer and use it in GitHub Desktop.
Save zydeco/9c0bffeb87b883ec761cdabfc3b7f71c to your computer and use it in GitHub Desktop.
annotate mini vmac trace
import sys
import re
A_LINE_PATTERN = re.compile(r"^([0-9A-F]+) \$(A[0-9A-F]{3})$")
HEX_PATTERN = re.compile(r"^[0-9A-F]+$")
BRANCH_PATTERN = re.compile(r'^(?P<address>[0-9A-F]+) (?P<instruction>B(?:RA|CC|CS|EQ|GE|GT|HI|LE|LS|LT|MI|NE|PL|VC|VS)) (?P<dest>[0-9A-F]+)$')
def read_rom_procs(rom_disasm_path):
# read procedure names from FDisasm rom disassembly
procs = {}
last_proc = None
fp = open(rom_disasm_path, 'r')
for line in fp:
if line.startswith(' ') and line.endswith(':\n'):
last_proc = line.strip()
elif last_proc and HEX_PATTERN.match(line.split(' ', 1)[0]):
address = int(line.split(' ', 1)[0], 16)
procs[0x400000 + address] = last_proc
last_proc = None
fp.close()
return procs
def read_traps(traps_path):
# read traps from text file formatted like 'Axxx "_TrapName"'
traps = {}
fp = open(traps_path, 'r')
for line in fp:
match = re.match(r'^(?P<trap>A[0-9A-F]{3}) "(?P<name>.+)"$', line)
if not match:
continue
traps[int(match.group('trap'), 16)] = match.group('name')
fp.close()
return traps
PROCS = read_rom_procs("Mac Plus ROM.s")
MAX_LOOP = 60
TRAPS = read_traps('trap_names.txt')
def loops(lines, n=None):
if n:
return lines[-n:] == lines[-(2*n):-n]
for n in range(2,MAX_LOOP):
if loops(lines, n):
# TODO: check that branch goes to first line of loop?
return n
return False
def could_loop(line):
# Bcc or BRA backward
match = BRANCH_PATTERN.match(line)
if match:
address = int(match.group('address'), 16)
dest = int(match.group('dest'), 16)
return dest < address
return False
lines = []
loop = None
loopbuf = None
loopcount = None
def postprocess(line, prev_line):
address_str = line.split(' ', 1)[0]
if HEX_PATTERN.match(address_str):
address = int(address_str, 16)
# insert procedure name
if address in PROCS and not prev_line.startswith(' ' + PROCS[address]):
print(' ' + PROCS[address])
# pad address to 6 chars
if len(address_str) < 6:
line = line.replace(address_str + ' ', '%06d ' % address)
# lowercase instruction
words = line.split(' ', 2)
if words[1] not in TRAPS.values():
words[1] = words[1].lower()
line = ' '.join(words)
print(line)
return line
prev_line = ''
for line in sys.stdin:
line = line.rstrip()
# replace a-line instructions with trap name
match_aline = A_LINE_PATTERN.match(line)
if match_aline:
trapNo = int(match_aline.group(2), 16)
if trapNo in TRAPS:
line = A_LINE_PATTERN.sub(r"\1 " + TRAPS[trapNo], line)
# find loops
lines.append(line)
if loop:
loopbuf.append(line)
if loopbuf == loop:
# another loop
lines[-len(loop):] = []
loopcount += 1
loopbuf = []
elif loopbuf[-1] != loop[len(loopbuf)-1]:
# doesn't match loop
lines.insert(-len(loopbuf), ' ; loop {} instructions {} times'.format(len(loop), loopcount))
loop = None
elif could_loop(line) and loops(lines):
# new loop
loop = lines[-loops(lines):]
loopcount = 1
loopbuf = []
lines[-len(loop):] = []
# continue processing
if len(lines) > 2*MAX_LOOP:
prev_line = postprocess(lines.pop(0), prev_line)
# process rest of lines
for line in lines:
prev_line = postprocess(line, prev_line)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment