#!/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")