mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Find the TMS34010 bit-address constants in a VT1200 ROM image.
|
|
|
|
The firmware stores absolute **bit** addresses: a 32-bit value ``P`` refers to
|
|
physical byte ``P >> 3``. A byte-addressed tool such as Ghidra therefore cannot
|
|
resolve those constants directly. This tool scans an address-ordered image
|
|
(see ``reconstruct.py``) for 32-bit little-endian values whose ``>> 3`` falls
|
|
inside the ROM, and reports where each one points.
|
|
|
|
Typical uses:
|
|
python3 tools/ptr_constants.py vt1200_v22.bin
|
|
python3 tools/ptr_constants.py vt1200_v22.bin --tsv pointers.tsv
|
|
python3 tools/ptr_constants.py vt1200_v22.bin --strings
|
|
|
|
The report is the input for the next step: a Ghidra script that rewrites these
|
|
constants (``P`` -> ``P >> 3``) so cross-references resolve. See MEMORY_MAP.md.
|
|
"""
|
|
import argparse
|
|
import struct
|
|
import sys
|
|
|
|
ROM_BASE = 0x07E40000 # physical byte base of the ROM
|
|
DEFAULT_STEP = 2 # TMS34010 is bit-addressed; 2-byte alignment
|
|
MIN_STRING = 4 # printable run length that counts as a string
|
|
|
|
|
|
def classify(image, target):
|
|
"""Return 'string' if *target* starts a printable run, else 'other'.
|
|
|
|
Returns None when *target* is outside the image.
|
|
"""
|
|
if not 0 <= target < len(image):
|
|
return None
|
|
if 0x20 <= image[target] < 0x7F:
|
|
end = target
|
|
while end < len(image) and 0x20 <= image[end] < 0x7F:
|
|
end += 1
|
|
if end - target >= MIN_STRING:
|
|
return "string"
|
|
return "other"
|
|
|
|
|
|
def scan(image, base=ROM_BASE, step=DEFAULT_STEP):
|
|
"""Yield (offset, value, target_offset, kind) for each pointer constant.
|
|
|
|
*offset* is the file offset of the 32-bit constant, *value* its raw value,
|
|
*target_offset* the ROM-relative byte it refers to.
|
|
"""
|
|
size = len(image)
|
|
for off in range(0, size - 3, step):
|
|
value = struct.unpack_from("<I", image, off)[0]
|
|
rel = (value >> 3) - base
|
|
if 0 <= rel < size:
|
|
yield off, value, rel, classify(image, rel)
|
|
|
|
|
|
def cstring(image, offset, limit=64):
|
|
end = offset
|
|
while end < len(image) and end - offset < limit and 0x20 <= image[end] < 0x7F:
|
|
end += 1
|
|
return image[offset:end].decode("latin1")
|
|
|
|
|
|
def main(argv=None):
|
|
ap = argparse.ArgumentParser(description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
ap.add_argument("image", help="address-ordered ROM image")
|
|
ap.add_argument("--base", type=lambda s: int(s, 0), default=ROM_BASE,
|
|
help=f"ROM physical byte base (default 0x{ROM_BASE:X})")
|
|
ap.add_argument("--step", type=int, default=DEFAULT_STEP,
|
|
help=f"scan step in bytes (default {DEFAULT_STEP})")
|
|
ap.add_argument("--tsv", metavar="FILE",
|
|
help="write every hit as offset<TAB>value<TAB>target<TAB>kind")
|
|
ap.add_argument("--strings", action="store_true",
|
|
help="list distinct string targets")
|
|
args = ap.parse_args(argv)
|
|
|
|
image = open(args.image, "rb").read()
|
|
hits = list(scan(image, base=args.base, step=args.step))
|
|
|
|
strings = {}
|
|
other = 0
|
|
for off, value, tgt, kind in hits:
|
|
if kind == "string":
|
|
strings.setdefault(tgt, (off, value))
|
|
else:
|
|
other += 1
|
|
|
|
print(f"image : {args.image} ({len(image)} bytes)")
|
|
print(f"pointer constants: {len(hits)} "
|
|
f"({len(strings)} distinct string targets, {other} other)")
|
|
print(f"ROM byte range : 0x{args.base:08X}-0x{args.base + len(image) - 1:08X}")
|
|
print(f"bit range : 0x{args.base << 3:08X}-0x{((args.base + len(image)) << 3) - 1:08X}")
|
|
|
|
if args.strings:
|
|
print(f"\ndistinct string targets ({len(strings)}):")
|
|
for tgt in sorted(strings):
|
|
off, value = strings[tgt]
|
|
print(f" 0x{tgt:06X} <- 0x{value:08X} @file 0x{off:06X} {cstring(image, tgt)!r}")
|
|
|
|
if args.tsv:
|
|
with open(args.tsv, "w") as fh:
|
|
fh.write("offset\tvalue\ttarget\tkind\n")
|
|
for off, value, tgt, kind in hits:
|
|
fh.write(f"0x{off:06X}\t0x{value:08X}\t0x{tgt:06X}\t{kind}\n")
|
|
print(f"\nwrote {len(hits)} hits to {args.tsv}")
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|