check_realconst.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #!/usr/bin/env python3
  2. """check_realconst.py -- the six bytes of TP3's Real, against CALC-PORT.md.
  3. CALC-PORT.md decoded the format from CALCDEMO.MCS, cross-checked against the
  4. cells holding 1, 2, 3, 4 and 5:
  5. value = (1 + N/2^39) * 2^(E-129)
  6. byte 0 = E biased by 129, bytes 1..5 = N little-endian, the sign is bit 7 of
  7. byte 5, and all-zero is 0.0. No execution oracle can read a Real back yet, so
  8. this check asserts the OPERAND BYTES in the emitted code instead: integer
  9. constants stored through a symbol (B8 vv 00 A2 addr), through DI (C6 45 off
  10. vv), and the six-byte copy (A0 src / 88 45 off).
  11. The fixture is t80_realconst; its execution still checks the integer fields on
  12. either side of the Real, which is what pins the store's WIDTH.
  13. """
  14. import os
  15. import re
  16. import subprocess
  17. import sys
  18. HERE = os.path.dirname(os.path.abspath(__file__))
  19. SHELL = os.path.dirname(HERE)
  20. FIX = os.path.join(HERE, "fixtures", "t80_realconst.pas")
  21. # From the measured table (CALC-PORT.md).
  22. MBF = {
  23. "0": bytes.fromhex("000000000000"),
  24. "1": bytes.fromhex("810000000000"),
  25. "2": bytes.fromhex("820000000000"),
  26. "3": bytes.fromhex("820000000040"),
  27. "5": bytes.fromhex("830000000020"),
  28. }
  29. def dump():
  30. r = subprocess.run([os.path.join(SHELL, "compiletest")],
  31. input=("@image\n" + FIX + "\n").encode(),
  32. capture_output=True, cwd=SHELL)
  33. text = r.stdout.decode("latin-1")
  34. if " OK " not in text:
  35. sys.exit("the fixture did not compile:\n" + text)
  36. data = bytearray()
  37. for line in text.splitlines():
  38. m = re.match(r"^\s+([0-9A-Fa-f]{4}):\s+((?:[0-9A-Fa-f]{2}\s*)+)$", line)
  39. if m:
  40. data.extend(int(t, 16) for t in m.group(2).split())
  41. return bytes(data)
  42. def symbol_stores(data):
  43. """The 6-byte runs of B8 vv 00 A2 lo hi with consecutive addresses."""
  44. runs = []
  45. i = 0
  46. while i + 6 <= len(data):
  47. if data[i] == 0xB8 and data[i + 2] == 0x00 and data[i + 3] == 0xA2:
  48. runs.append((data[i + 1], data[i + 4] | (data[i + 5] << 8)))
  49. i += 6
  50. else:
  51. i += 1
  52. found = {}
  53. for s in range(len(runs) - 5):
  54. seg = runs[s:s + 6]
  55. if all(seg[t + 1][1] == seg[t][1] + 1 for t in range(5)):
  56. found[bytes(v for v, _ in seg)] = seg[0][1]
  57. return found
  58. def main():
  59. data = dump()
  60. problems = []
  61. found = symbol_stores(data)
  62. for name, want in MBF.items():
  63. if want not in found:
  64. problems.append("no symbol store of %s as %s" % (name, want.hex(" ")))
  65. else:
  66. print(" %s -> %s at %04xh" % (name, want.hex(" "), found[want]))
  67. # The DI form of `V := 1'.
  68. di1 = bytes.fromhex("C6450081C6450100C6450200C6450300C6450400C6450500")
  69. if di1 not in data:
  70. problems.append("the DI store of 1 is not C6 45 00 81 ... 05 00")
  71. # The six-byte copy through DI.
  72. for k in range(6):
  73. if bytes([0x88, 0x45, k]) not in data:
  74. problems.append("the copy never stores byte %d through DI" % k)
  75. if problems:
  76. print("FAIL:")
  77. for p in problems:
  78. print(" - " + p)
  79. return 1
  80. print("PASS: the measured MBF bytes for 0,1,2,3,5 are emitted, the DI "
  81. "store holds them,\n and the six-byte copy stores every byte")
  82. return 0
  83. if __name__ == "__main__":
  84. sys.exit(main())