48 lines
1.8 KiB
Python
Executable file
48 lines
1.8 KiB
Python
Executable file
# Detect the w65816 codegen bug where a far pointer's 16-bit OFFSET half is
|
|
# built from the pointer's BANK register.
|
|
#
|
|
# The bank register is the DP slot the prologue fills with `stx <dp>` (the
|
|
# incoming pointer arrives as offset-in-A, bank-in-X). Flagging a
|
|
# `lda <bankdp> ... sta 0xe0` means the pointer used for the following
|
|
# [dp],y access has the bank value in its offset half -- it reads a stray
|
|
# address. Correct code reloads A with the offset (pla / txa / lda n,s)
|
|
# before storing to 0xe0.
|
|
import re, sys
|
|
|
|
BOILER = re.compile(r'(sty\s+0xfa|ldy\s+0xfa|ldy\s+#|sta\s+\[0x[0-9a-f]+\], y|lda\s+\[0x[0-9a-f]+\], y|nop)')
|
|
|
|
def scan(path):
|
|
lines = [l.strip() for l in open(path).read().split("\n")]
|
|
# function start lines
|
|
starts = [i for i, l in enumerate(lines) if re.match(r'^[A-Za-z_][A-Za-z0-9_]*:', l)]
|
|
bounds = list(zip(starts, starts[1:] + [len(lines)]))
|
|
hits = []
|
|
for s, e in bounds:
|
|
name = lines[s].split(":")[0]
|
|
bank = None
|
|
for i in range(s, min(s + 40, e)):
|
|
m = re.match(r'stx\s+(0x[0-9a-f]+)$', lines[i])
|
|
if m:
|
|
bank = m.group(1)
|
|
break
|
|
if bank is None:
|
|
continue
|
|
for i in range(s, e):
|
|
if not re.match(r'sta\s+0x(e0|e4|e8|ec)$', lines[i]):
|
|
continue
|
|
j = i - 1
|
|
while j > s and BOILER.match(lines[j]):
|
|
j -= 1
|
|
if lines[j] == "lda\t%s" % bank or lines[j] == "lda %s" % bank:
|
|
hits.append((name, i + 1, bank, lines[i]))
|
|
return hits
|
|
|
|
bad = 0
|
|
for p in sys.argv[1:]:
|
|
h = scan(p)
|
|
if h:
|
|
bad += len(h)
|
|
print("%s: %d" % (p, len(h)))
|
|
for fn, ln, bk, u in h:
|
|
print(" %-28s line %-6d bank=%s -> %s" % (fn, ln, bk, u))
|
|
print("total suspect pointer builds: %d" % bad)
|