|
|
@@ -0,0 +1,102 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+"""check_realconst.py -- the six bytes of TP3's Real, against CALC-PORT.md.
|
|
|
+
|
|
|
+CALC-PORT.md decoded the format from CALCDEMO.MCS, cross-checked against the
|
|
|
+cells holding 1, 2, 3, 4 and 5:
|
|
|
+
|
|
|
+ value = (1 + N/2^39) * 2^(E-129)
|
|
|
+
|
|
|
+byte 0 = E biased by 129, bytes 1..5 = N little-endian, the sign is bit 7 of
|
|
|
+byte 5, and all-zero is 0.0. No execution oracle can read a Real back yet, so
|
|
|
+this check asserts the OPERAND BYTES in the emitted code instead: integer
|
|
|
+constants stored through a symbol (B8 vv 00 A2 addr), through DI (C6 45 off
|
|
|
+vv), and the six-byte copy (A0 src / 88 45 off).
|
|
|
+
|
|
|
+The fixture is t80_realconst; its execution still checks the integer fields on
|
|
|
+either side of the Real, which is what pins the store's WIDTH.
|
|
|
+"""
|
|
|
+import os
|
|
|
+import re
|
|
|
+import subprocess
|
|
|
+import sys
|
|
|
+
|
|
|
+HERE = os.path.dirname(os.path.abspath(__file__))
|
|
|
+SHELL = os.path.dirname(HERE)
|
|
|
+FIX = os.path.join(HERE, "fixtures", "t80_realconst.pas")
|
|
|
+
|
|
|
+# From the measured table (CALC-PORT.md).
|
|
|
+MBF = {
|
|
|
+ "0": bytes.fromhex("000000000000"),
|
|
|
+ "1": bytes.fromhex("810000000000"),
|
|
|
+ "2": bytes.fromhex("820000000000"),
|
|
|
+ "3": bytes.fromhex("820000000040"),
|
|
|
+ "5": bytes.fromhex("830000000020"),
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+def dump():
|
|
|
+ r = subprocess.run([os.path.join(SHELL, "compiletest")],
|
|
|
+ input=("@image\n" + FIX + "\n").encode(),
|
|
|
+ capture_output=True, cwd=SHELL)
|
|
|
+ text = r.stdout.decode("latin-1")
|
|
|
+ if " OK " not in text:
|
|
|
+ sys.exit("the fixture did not compile:\n" + text)
|
|
|
+ data = bytearray()
|
|
|
+ for line in text.splitlines():
|
|
|
+ m = re.match(r"^\s+([0-9A-Fa-f]{4}):\s+((?:[0-9A-Fa-f]{2}\s*)+)$", line)
|
|
|
+ if m:
|
|
|
+ data.extend(int(t, 16) for t in m.group(2).split())
|
|
|
+ return bytes(data)
|
|
|
+
|
|
|
+
|
|
|
+def symbol_stores(data):
|
|
|
+ """The 6-byte runs of B8 vv 00 A2 lo hi with consecutive addresses."""
|
|
|
+ runs = []
|
|
|
+ i = 0
|
|
|
+ while i + 6 <= len(data):
|
|
|
+ if data[i] == 0xB8 and data[i + 2] == 0x00 and data[i + 3] == 0xA2:
|
|
|
+ runs.append((data[i + 1], data[i + 4] | (data[i + 5] << 8)))
|
|
|
+ i += 6
|
|
|
+ else:
|
|
|
+ i += 1
|
|
|
+ found = {}
|
|
|
+ for s in range(len(runs) - 5):
|
|
|
+ seg = runs[s:s + 6]
|
|
|
+ if all(seg[t + 1][1] == seg[t][1] + 1 for t in range(5)):
|
|
|
+ found[bytes(v for v, _ in seg)] = seg[0][1]
|
|
|
+ return found
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ data = dump()
|
|
|
+ problems = []
|
|
|
+
|
|
|
+ found = symbol_stores(data)
|
|
|
+ for name, want in MBF.items():
|
|
|
+ if want not in found:
|
|
|
+ problems.append("no symbol store of %s as %s" % (name, want.hex(" ")))
|
|
|
+ else:
|
|
|
+ print(" %s -> %s at %04xh" % (name, want.hex(" "), found[want]))
|
|
|
+
|
|
|
+ # The DI form of `V := 1'.
|
|
|
+ di1 = bytes.fromhex("C6450081C6450100C6450200C6450300C6450400C6450500")
|
|
|
+ if di1 not in data:
|
|
|
+ problems.append("the DI store of 1 is not C6 45 00 81 ... 05 00")
|
|
|
+
|
|
|
+ # The six-byte copy through DI.
|
|
|
+ for k in range(6):
|
|
|
+ if bytes([0x88, 0x45, k]) not in data:
|
|
|
+ problems.append("the copy never stores byte %d through DI" % k)
|
|
|
+
|
|
|
+ if problems:
|
|
|
+ print("FAIL:")
|
|
|
+ for p in problems:
|
|
|
+ print(" - " + p)
|
|
|
+ return 1
|
|
|
+ print("PASS: the measured MBF bytes for 0,1,2,3,5 are emitted, the DI "
|
|
|
+ "store holds them,\n and the six-byte copy stores every byte")
|
|
|
+ return 0
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ sys.exit(main())
|