forked from arti/vt1200
The 16-bit-displacement jump (low byte 0x00, two words) is JRcc, not the three-word JAcc (low byte 0x80). Printing it as ja made linear disassembly report the wrong control flow and made every such branch grow by a word when reassembled. Register 15 is the stack pointer in either file: 0x0F is A15 and 0x1F is B15. The disassembler printed both as sp, hiding B-file stack-pointer code. Print b15 when the R bit selects the B file. Validated with the 1987 TI GSPA/GSPLNK/GSPROM round trip: the 32 KB region at bit 3F208000 still reassembles byte-for-byte (md5 43adb6669571041985f3e26fd9ff7cb9), as do the 4 KB and 16 KB spans and the 0x8000/3F240000 region. The converter no longer needs its ja->jr or sp->b15 rewrites (5 fixups -> 3); a whole-ROM diff contains only those two output categories. The ROM is unchanged (b46a3e442ccb86134d2356bd70d22fd2).
108 lines
3.9 KiB
Python
Executable File
108 lines
3.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Compare two dis34 builds over the whole ROM and classify every difference.
|
|
|
|
Purpose: prove the two `dis34` fixes are the *only* changes to its output.
|
|
|
|
* long-relative JR fix: the line must change from ``ja<cc>`` to ``jr<cc>``
|
|
and carry exactly two opcode words, the first being ``0xC?00``;
|
|
* B-file SP fix: the mnemonic must be unchanged, every ``sp`` operand must
|
|
become ``b15``, and the encoding must be able to name B15: bit 4 set, or
|
|
the cross-file MOVE Rs,Rd whose destination is the other file.
|
|
|
|
Usage: verify_dis.py OLD_DIS34 NEW_DIS34 [ROM]
|
|
|
|
Chunks are disassembled with 16 zero bytes of padding so a multi-word
|
|
instruction at the end of a chunk cannot abort the run. The padding is
|
|
identical for both builds and its decoded lines are still compared.
|
|
"""
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
HEXWORD = re.compile(r"\b[0-9a-fA-F]{4}\b")
|
|
|
|
|
|
def disassemble(exe, chunk, base, work):
|
|
part = os.path.join(work, "verify_chunk.bin")
|
|
with open(part, "wb") as f:
|
|
f.write(chunk + b"\0" * 16)
|
|
p = subprocess.run([exe, "-b", "%X" % base, "-o", part],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
check=True)
|
|
return p.stdout.decode("latin-1").splitlines()
|
|
|
|
|
|
def words(line):
|
|
code, _, comment = line.partition(";")
|
|
return HEXWORD.findall(comment)
|
|
|
|
|
|
def can_name_b15(w0):
|
|
"""True if the instruction's register field can select B-file reg 15.
|
|
|
|
Bit 4 (the R bit) normally selects the file. MOVE Rs,Rd cross-file
|
|
(op 0x4E00) is the exception: its *destination* is the other file
|
|
(dis34 prints ``regname(rd, rf ^ 1)``), so bit 4 = 0 still means B15
|
|
for the destination.
|
|
"""
|
|
op = int(w0, 16)
|
|
return bool(op & 0x10) or (op & 0xfe00) == 0x4e00
|
|
|
|
|
|
def main():
|
|
old_exe, new_exe = sys.argv[1], sys.argv[2]
|
|
rom_path = sys.argv[3] if len(sys.argv) > 3 else "vt1200_v22.bin"
|
|
work = os.path.dirname(os.path.abspath(__file__)) + "/work"
|
|
os.makedirs(work, exist_ok=True)
|
|
rom = open(rom_path, "rb").read()
|
|
|
|
chunk_size = 32768
|
|
base0 = 0x3F200000
|
|
n_jr = n_sp = 0
|
|
errors = []
|
|
for start in range(0, len(rom), chunk_size):
|
|
chunk = rom[start:start + chunk_size]
|
|
base = base0 + start * 8
|
|
old = disassemble(old_exe, chunk, base, work)
|
|
new = disassemble(new_exe, chunk, base, work)
|
|
if len(old) != len(new):
|
|
errors.append("chunk %#x: line count %d != %d" %
|
|
(start, len(old), len(new)))
|
|
continue
|
|
for i, (a, b) in enumerate(zip(old, new)):
|
|
if a == b:
|
|
continue
|
|
w = words(a)
|
|
code_a = a.partition(";")[0].strip()
|
|
code_b = b.partition(";")[0].strip()
|
|
tok_a = code_a.split()[0].lower() if code_a.split() else ""
|
|
tok_b = code_b.split()[0].lower() if code_b.split() else ""
|
|
ja = re.fullmatch(r"ja([a-z]+)", tok_a)
|
|
if (ja and tok_b == "jr" + ja.group(1) and len(w) == 2
|
|
and re.fullmatch(r"c[0-9a-f]00", w[0].lower())):
|
|
n_jr += 1
|
|
continue
|
|
# SP fix: same mnemonic, only sp -> b15, R bit set
|
|
if (tok_a == tok_b and "sp" in code_a and "b15" in code_b
|
|
and code_a.replace("sp", "b15") == code_b
|
|
and w and can_name_b15(w[0])):
|
|
n_sp += 1
|
|
continue
|
|
errors.append("chunk %#x line %d:\n - %s\n + %s" %
|
|
(start, i + 1, a, b))
|
|
|
|
print("long-relative JA->JR changes: %d" % n_jr)
|
|
print("B-file SP sp->b15 changes: %d" % n_sp)
|
|
if errors:
|
|
print("UNEXPECTED changes: %d" % len(errors))
|
|
for e in errors[:10]:
|
|
print(e)
|
|
return 1
|
|
print("all differences are the two intended categories")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|