Created
September 22, 2020 21:59
-
-
Save zydeco/9c0bffeb87b883ec761cdabfc3b7f71c to your computer and use it in GitHub Desktop.
annotate mini vmac trace
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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