forked from arti/vt1200
checkpoint: VT1200 reverse-engineering workspace before board-model work
This commit is contained in:
commit
acb92d3e44
325 files changed
+951894
No files matched your search
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Reconstruct the VT1200 X-server image from the raw EPROM dumps.
|
||||
|
||||
The VT1200 firmware is held in 14 x 128 KiB (27C010) byte-wide EPROMs per
|
||||
firmware revision. The EPROMs are wired as 16-bit words: consecutive part
|
||||
numbers form a pair, and within a pair the ODD-numbered part holds the low
|
||||
byte and the EVEN-numbered part holds the high byte of the word.
|
||||
|
||||
Example for v2.1:
|
||||
pair (23-210E9, 23-211E9) -> 256 KiB bank, 210E9 = high byte,
|
||||
211E9 = low byte
|
||||
|
||||
There are two possible orderings of the seven 256 KiB banks:
|
||||
|
||||
* ``linear`` (default) -- CPU *address* order. The ROM is top-aligned at
|
||||
physical byte 0x07E40000, and the bank with the *highest* part numbers sits
|
||||
at the lowest address. In this order file offset == physical byte - base,
|
||||
which is what a disassembler/emulator wants.
|
||||
* ``--part-order`` -- legacy ascending part-number order (bank 0 first).
|
||||
|
||||
The image is 1.75 MiB (0x1C0000) and can be fed to a TMS34010 disassembler
|
||||
(e.g. smart34010's ``dis34`` or MAME's ``unidasm``) or loaded into an emulator
|
||||
at base 0x07E40000. See MEMORY_MAP.md. Note the firmware stores *bit*
|
||||
addresses (physical byte = bit >> 3).
|
||||
|
||||
Usage:
|
||||
./reconstruct.py v2.2 # -> vt1200_v22.bin (address order)
|
||||
./reconstruct.py v2.1 out.bin
|
||||
./reconstruct.py v2.1 --part-order
|
||||
"""
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
import argparse
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Physical byte address the ROM is mapped at; also the size of one image.
|
||||
ROM_BASE = 0x07E40000
|
||||
|
||||
|
||||
def load(dirname):
|
||||
roms = {}
|
||||
for path in sorted(glob.glob(os.path.join(HERE, dirname, "*.bin"))):
|
||||
m = re.search(r"(\d+)E9", os.path.basename(path))
|
||||
if not m:
|
||||
continue
|
||||
with open(path, "rb") as fh:
|
||||
roms[int(m.group(1))] = fh.read()
|
||||
return roms
|
||||
|
||||
|
||||
def reconstruct(dirname, linear=True):
|
||||
"""De-interleave the EPROMs of a revision.
|
||||
|
||||
Returns (parts, image) where parts is the ascending list of part numbers
|
||||
and image is the reconstructed ROM. With linear=True the banks are placed
|
||||
in CPU address order (file offset == physical byte - ROM_BASE).
|
||||
"""
|
||||
roms = load(dirname)
|
||||
parts = sorted(roms)
|
||||
if len(parts) % 2:
|
||||
raise SystemExit(f"{dirname}: expected an even number of EPROMs, got {len(parts)}")
|
||||
banks = []
|
||||
for i in range(0, len(parts), 2):
|
||||
even_pn, odd_pn = parts[i], parts[i + 1] # odd part = low byte
|
||||
low, high = roms[odd_pn], roms[even_pn]
|
||||
if len(low) != len(high):
|
||||
raise SystemExit(f"{dirname}: size mismatch between {odd_pn} and {even_pn}")
|
||||
img = bytearray(len(low) * 2)
|
||||
img[0::2] = low
|
||||
img[1::2] = high
|
||||
banks.append(bytes(img))
|
||||
if linear:
|
||||
banks.reverse() # highest part number sits at the lowest address
|
||||
return parts, b"".join(banks)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
ap.add_argument("revision", help="ROM directory, e.g. v2.1 or v2.2")
|
||||
ap.add_argument("output", nargs="?", help="output file (default vt1200_vNN.bin)")
|
||||
ap.add_argument(
|
||||
"--linear", dest="linear", action="store_true",
|
||||
help="emit CPU address order (default): offset == physical byte - 0x07E40000",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--part-order", dest="linear", action="store_false",
|
||||
help="emit legacy ascending part-number order",
|
||||
)
|
||||
ap.set_defaults(linear=True)
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
default = "vt1200_" + args.revision.replace(".", "") + ".bin"
|
||||
out = args.output or os.path.join(HERE, default)
|
||||
parts, img = reconstruct(args.revision, linear=args.linear)
|
||||
with open(out, "wb") as fh:
|
||||
fh.write(img)
|
||||
|
||||
order = "address" if args.linear else "part-number"
|
||||
strings = len(re.findall(rb"[\x20-\x7e]{16,}", img))
|
||||
print(f"{args.revision}: {len(parts)} EPROMs ({parts[0]}..{parts[-1]}) "
|
||||
f"{order} order -> {out} ({len(img)} bytes = 0x{len(img):X}), "
|
||||
f"{strings} text runs")
|
||||
if args.linear:
|
||||
print(f" load base: 0x{ROM_BASE:08X} (bit 0x{ROM_BASE * 8:08X})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user