modemwars/tools/findLoaderCalls.py
2026-08-23 02:09:40 -05:00

44 lines
2 KiB
Python

#!/usr/bin/env python3
# findLoaderCalls.py - linear sweep of a memory image for calls into the disk loader entry points,
# recovering the immediate A/X/Y arguments that precede each call.
import sys
from m6502 import decode, IMM, ABS
ENTRIES = {0x0804:"read(A=count,Y=track,X=sector)", 0x0843:"write(A=count,Y=track,X=sector)",
0x085C:"setDest(X=lo,Y=hi)", 0x0863:"writeOne", 0x08EB:"readOne",
0xC004:"bootRead", 0xC034:"bootSetDest", 0xC08A:"bootReadOne", 0xC03B:"sendByte"}
def scan(mem, lo, hi, label):
hits = []
a = lo
while a < hi:
insn = decode(mem, a)
if insn.mnemonic in ("jsr","jmp") and insn.mode == ABS and insn.target in ENTRIES:
# walk back up to 12 bytes looking for immediate loads
regs = {}
b = a
for _ in range(8):
# find previous instruction by trying lengths 1..3 (heuristic: prefer the one that decodes to an imm load)
found = None
for ln in (2, 3, 1):
p = b - ln
if p < lo: continue
pi = decode(mem, p)
if pi.length == ln and pi.isLegal():
found = pi; break
if not found: break
if found.mnemonic in ("lda","ldx","ldy") and found.mode == IMM:
regs.setdefault(found.mnemonic[2].upper(), found.operand)
elif found.mnemonic in ("jsr","jmp","rts","rti") or found.mnemonic.startswith("b") and found.mode==12:
break
b = found.addr
hits.append((a, insn.mnemonic, insn.target, regs))
a += insn.length if insn.isLegal() else 1
for a, mn, t, regs in hits:
print(f"{label} ${a:04X}: {mn} ${t:04X} {ENTRIES[t]:<28} " + " ".join(f"{k}=${v:02X}" for k,v in sorted(regs.items())))
return hits
if __name__ == "__main__":
mem = open(sys.argv[1], "rb").read()
for lo, hi in [(0x0400,0x0800),(0x0800,0x8800),(0x8C00,0x9000),(0x9300,0xC400),(0xFBB8,0xFFD2)]:
scan(mem, lo, hi, "boot")