#!/usr/bin/env python3
"""Find every GPIO-base literal load in the RT-4D app and show the surrounding
code, so each (port, register-offset, bit-mask) access can be identified."""
import struct, re, sys, capstone

BASE = 0x08002800
IMG = open("/home/viktor/claude/rt-4d/stock-fw/rt4d_stock_v3.25_abs_0x08002800.bin", "rb").read()
md = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)

PORTS = {0x40020000: "GPIOA", 0x40020400: "GPIOB", 0x40020800: "GPIOC",
         0x40020C00: "GPIOD", 0x40021000: "GPIOE", 0x40021400: "GPIOF",
         0x40021800: "GPIOG", 0x40021C00: "GPIOH"}


def word(va):
    o = va - BASE
    return struct.unpack_from("<I", IMG, o)[0] if 0 <= o <= len(IMG) - 4 else None


def litval(ins):
    m = re.search(r"\[pc, #(?:0x)?([0-9a-fA-F]+)\]", ins.op_str)
    if m and ins.mnemonic.startswith("ldr"):
        return word(((ins.address + 4) & ~3) + int(m.group(1), 16))
    return None


def main(ctx=6, only=None):
    ins_list = list(md.disasm(IMG, BASE))
    idx = {i.address: n for n, i in enumerate(ins_list)}
    hits = []
    for n, i in enumerate(ins_list):
        v = litval(i)
        if v in PORTS and (only is None or v == only):
            hits.append((n, i, v))
    print(f"{len(hits)} GPIO-base literal loads\n")
    for n, i, v in hits:
        print(f"=== {PORTS[v]} (0x{v:08X}) referenced at 0x{i.address:08X} ===")
        for k in range(n, min(n + ctx, len(ins_list))):
            j = ins_list[k]
            extra = ""
            lv = litval(j)
            if lv is not None:
                extra = f"   ; =0x{lv:08X}"
            print(f"   0x{j.address:08X}: {j.mnemonic:8} {j.op_str}{extra}")
        print()


if __name__ == "__main__":
    only = int(sys.argv[1], 16) if len(sys.argv) > 1 else None
    main(only=only)
