joeylib2/tools/scanPtrBug.py
2026-09-17 17:46:47 -05:00

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)