|
|
@@ -63,25 +63,25 @@ SHELL = os.path.dirname(HERE)
|
|
|
# two are written down and then CHECKED against each other (see build_image).
|
|
|
# A restated constant that is never compared is a constant waiting to lie.
|
|
|
RT_BASE = 0x0190
|
|
|
-HDR = 0x0F00
|
|
|
+HDR = 0x1100
|
|
|
DLEN = 32
|
|
|
-DATA = 0x0F10
|
|
|
-STORE = 0x0F40
|
|
|
-WSTR = 0x0F50
|
|
|
-WSTR_EMPTY = 0x0F58
|
|
|
-CASE = 0x0F60
|
|
|
+DATA = 0x1110
|
|
|
+STORE = 0x1140
|
|
|
+WSTR = 0x1150
|
|
|
+WSTR_EMPTY = 0x1158
|
|
|
+CASE = 0x1160
|
|
|
CASE_SZ = 64
|
|
|
-RDSTR_DST = 0x0FB0
|
|
|
-SST_HELLO = 0x0FC0
|
|
|
-SST_LONG = 0x0FD0
|
|
|
-SST_EMPTY = 0x0FE0
|
|
|
-SST_DST = 0x0FF0
|
|
|
-SST_CAT = 0x1000
|
|
|
-SST_CAT0 = 0x1010
|
|
|
-SST_WORLD = 0x1020
|
|
|
-AXOUT = 0x0FA6 # the driver stores the last call's AX here
|
|
|
-FMTDEST = 0x1230 # a 64-byte string for the real_fmt cases
|
|
|
-ASCSRC = 0x1270 # the ASCIIZ strings for real_val
|
|
|
+RDSTR_DST = 0x11B0
|
|
|
+SST_HELLO = 0x11C0
|
|
|
+SST_LONG = 0x11D0
|
|
|
+SST_EMPTY = 0x11E0
|
|
|
+SST_DST = 0x11F0
|
|
|
+SST_CAT = 0x1200
|
|
|
+SST_CAT0 = 0x1210
|
|
|
+SST_WORLD = 0x1220
|
|
|
+AXOUT = 0x11A6 # the driver stores the last call's AX here
|
|
|
+FMTDEST = 0x1430 # a 64-byte string for the real_fmt cases
|
|
|
+ASCSRC = 0x1470 # the ASCIIZ strings for real_val
|
|
|
REALDST = 0x0FA8
|
|
|
|
|
|
# A .COM is loaded at CS:0100, and every address in the image is an image
|
|
|
@@ -109,7 +109,8 @@ ENTS = ["initmem", "progend", "stackchk", "wrint", "wrchar", "wrbool",
|
|
|
"wrreal", "wrln", "rdint", "rdchar", "rdbool", "rdln", "halt",
|
|
|
"wrtinl", "rdbyte", "wrstr", "rdstr", "strstore", "cmpstr", "strcat",
|
|
|
"wrintw", "real_from_int", "real_add", "real_sub", "real_mul",
|
|
|
- "real_div", "real_cmp", "real_int", "real_fmt", "real_val"]
|
|
|
+ "real_div", "real_cmp", "real_int", "real_fmt", "real_val",
|
|
|
+ "real_write"]
|
|
|
|
|
|
# A call slot's flags, as rtdrv.s reads them.
|
|
|
F_STACK = 1 # the argument goes on the stack, popped by the caller
|
|
|
@@ -119,6 +120,8 @@ F_STACK2 = 8 # two stack words: +6 pushed first (lands at [BP+6]), +2 second
|
|
|
# (at [BP+4]) -- strstore's dest/src order
|
|
|
F_CX = 16 # CX := the word at +8, the maximum length rdstr and strstore
|
|
|
# both read from the register
|
|
|
+F_STACK3 = 32 # three stack words: +8 first, then +6, then +2, so +2 is
|
|
|
+ # [BP+4], +6 is [BP+6] and +8 is [BP+8]
|
|
|
|
|
|
# The byte patterns check_bp_contract looks for, and the rule they express:
|
|
|
# "an entry that borrows BP opens with PUSH BP; MOV BP,SP and pops it again
|
|
|
@@ -315,12 +318,12 @@ def case_bytes(c, entries):
|
|
|
struct.pack_into("<H", b, 2, c["dumpadr"]) # OFF_DUMPADR
|
|
|
b[4] = len(c["in"]) # OFF_INLEN
|
|
|
b[5:5 + len(c["in"])] = c["in"] # OFF_INBUF
|
|
|
- b[13] = c["index"] # OFF_CASE
|
|
|
+ struct.pack_into("<H", b, 13, c["index"]) # OFF_CASE (word)
|
|
|
calls = c["calls"]
|
|
|
if len(calls) > 4:
|
|
|
sys.exit("case %s has %d calls; the record holds 4"
|
|
|
% (c["name"], len(calls)))
|
|
|
- struct.pack_into("<H", b, 14, len(calls)) # OFF_NCALLS
|
|
|
+ b[15] = len(calls) # OFF_NCALLS (byte)
|
|
|
for i, (ent, arg, mode, arg2, cx) in enumerate(calls):
|
|
|
o = 16 + 12 * i # SLOT0, SLOT_SZ = 12
|
|
|
struct.pack_into("<H", b, o, entries[ent])
|
|
|
@@ -387,20 +390,20 @@ def parse_record(raw, want_index):
|
|
|
"""
|
|
|
if not raw or raw[0] != SOH:
|
|
|
raise AssertionError("no record header: the driver printed %r" % raw[:64])
|
|
|
- if len(raw) < 5 or raw[3] != STX:
|
|
|
+ if len(raw) < 7 or raw[5] != STX:
|
|
|
raise AssertionError("malformed record header %r" % raw[:16])
|
|
|
- idx = int(raw[1:3], 16)
|
|
|
+ idx = int(raw[1:5], 16)
|
|
|
if idx != want_index:
|
|
|
- raise AssertionError("record is for case %02X, expected %02X"
|
|
|
+ raise AssertionError("record is for case %04X, expected %04X"
|
|
|
% (idx, want_index))
|
|
|
if raw[-1] != EOT:
|
|
|
raise AssertionError("record was never closed (EOT): the last call did "
|
|
|
"not return, or the machine halted early")
|
|
|
- end = raw.index(ETX, 4)
|
|
|
+ end = raw.index(ETX, 6)
|
|
|
if end < 0:
|
|
|
raise AssertionError("record has no ETX: the calls printed nothing and "
|
|
|
"the driver never got past them")
|
|
|
- printed, dump = raw[4:end], raw[end + 1:-1]
|
|
|
+ printed, dump = raw[6:end], raw[end + 1:-1]
|
|
|
if len(dump) % 2 or not re.fullmatch(b"[0-9A-F]*", dump):
|
|
|
raise AssertionError("the memory report %r is not hex" % dump)
|
|
|
return idx, printed, bytes.fromhex(dump.decode())
|
|
|
@@ -448,6 +451,11 @@ def build_cases():
|
|
|
second (at [BP+4]). real_from_int(src, dst) takes its source first."""
|
|
|
return (ent, arg, F_STACK | F_STACK2, arg2, 0)
|
|
|
|
|
|
+ def s3(ent, a, b, c):
|
|
|
+ """Three stack words: c is pushed first (landing at [BP+8]), then b
|
|
|
+ ([BP+6]), then a ([BP+4])."""
|
|
|
+ return (ent, a, F_STACK | F_STACK3, b, c)
|
|
|
+
|
|
|
def s2cx(ent, arg, arg2, cx):
|
|
|
"""Two stack words AND CX: arg2 is pushed first (it lands at [BP+6]),
|
|
|
arg second (at [BP+4]), and CX carries the maximum length. strstore is
|
|
|
@@ -524,7 +532,7 @@ def build_cases():
|
|
|
(6, 2), (10, 2), (15, 3), (1000, 2), (-6, 2), (6, -2),
|
|
|
(-6, -2), (0, 5), (5, 2), (42, 6)]
|
|
|
for bi, (a, b) in enumerate(BLOCKS):
|
|
|
- blk = 0x1030 + 16 * bi
|
|
|
+ blk = 0x1230 + 16 * bi
|
|
|
add("real_add(%d,%d)" % (a, b),
|
|
|
[s("real_add", blk + LOAD_BIAS)],
|
|
|
dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
|
|
|
@@ -557,7 +565,7 @@ def build_cases():
|
|
|
if (a, b) in seen:
|
|
|
continue
|
|
|
seen.add((a, b))
|
|
|
- blk = 0x1030 + 16 * bi
|
|
|
+ blk = 0x1230 + 16 * bi
|
|
|
want = (a > b) - (a < b)
|
|
|
add("real_cmp(%d,%d)" % (a, b),
|
|
|
[s("real_cmp", blk + LOAD_BIAS)],
|
|
|
@@ -569,14 +577,14 @@ def build_cases():
|
|
|
(-0.5, 0.5), (12345.75, 12345.5), (2.5, 0.0), (-2.5, 0.0),
|
|
|
(0.99, 0.0), (1.9, 0.0)]
|
|
|
for fi, (a, b) in enumerate(FRAC):
|
|
|
- blk = 0x1170 + 16 * fi
|
|
|
+ blk = 0x1370 + 16 * fi
|
|
|
want = (a > b) - (a < b)
|
|
|
add("real_cmp(%g,%g)" % (a, b),
|
|
|
[s("real_cmp", blk + LOAD_BIAS)],
|
|
|
dump=2, dumpbytes=struct.pack("<H", want & 0xFFFF), dumpadr=AXOUT,
|
|
|
note="fractional magnitudes: the mantissa decides")
|
|
|
add("real_cmp(2^40,0)",
|
|
|
- [s("real_cmp", 0x1210 + LOAD_BIAS)],
|
|
|
+ [s("real_cmp", 0x1410 + LOAD_BIAS)],
|
|
|
dump=2, dumpbytes=struct.pack("<h", 1), dumpadr=AXOUT,
|
|
|
note="the exponent alone decides")
|
|
|
|
|
|
@@ -586,20 +594,20 @@ def build_cases():
|
|
|
# (TPSRC's xint pushes the six-byte result back).
|
|
|
for bi, (a, b) in enumerate(BLOCKS):
|
|
|
if a in (0, 1, -3, 32767):
|
|
|
- blk = 0x1030 + 16 * bi
|
|
|
+ blk = 0x1230 + 16 * bi
|
|
|
add("real_int(%d)" % a,
|
|
|
[s("real_int", blk + LOAD_BIAS)],
|
|
|
dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
|
|
|
note="the integer part as a real, the reference's reint")
|
|
|
for fi, (a, b) in enumerate(FRAC):
|
|
|
- blk = 0x1170 + 16 * fi
|
|
|
+ blk = 0x1370 + 16 * fi
|
|
|
add("real_int(%g)" % a,
|
|
|
[s("real_int", blk + LOAD_BIAS)],
|
|
|
dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
|
|
|
note="the fraction is masked away; the sign and exponent stay")
|
|
|
add("real_int(2^40)",
|
|
|
- [s("real_int", 0x1210 + LOAD_BIAS)],
|
|
|
- dump=6, dumpbytes=bytes.fromhex("A90000000000"), dumpadr=0x1210,
|
|
|
+ [s("real_int", 0x1410 + LOAD_BIAS)],
|
|
|
+ dump=6, dumpbytes=bytes.fromhex("A90000000000"), dumpadr=0x1410,
|
|
|
note="exponent >= 0A8h is already integral and returns unchanged")
|
|
|
|
|
|
# --- fmtreal: real_fmt writes a length-prefixed, left-padded field -----
|
|
|
@@ -616,15 +624,15 @@ def build_cases():
|
|
|
s2cx("real_fmt", STORE + LOAD_BIAS, FMTDEST + LOAD_BIAS, 0)],
|
|
|
stdin=struct.pack("<h", 1), dump=2, dumpbytes=bytes([1]) + b"1",
|
|
|
dumpadr=FMTDEST, note="a known-good 1.0 from real_from_int")
|
|
|
- fmt("real_fmt(1:0:0)", 0x1030, 0, 0, b"1")
|
|
|
- fmt("real_fmt(0:0:0)", 0x10B0, 0, 0, b"0")
|
|
|
- fmt("real_fmt(3.7:0:2)", 0x1170, 0, 2, b"3.70")
|
|
|
- fmt("real_fmt(12345.75:12:2)", 0x11C0, 12, 2, b" 12345.75")
|
|
|
- fmt("real_fmt(0.5:6:3)", 0x11A0, 6, 3, b" 0.500")
|
|
|
- fmt("real_fmt(-3.7:0:1)", 0x1190, 0, 1, b"-3.7")
|
|
|
- fmt("real_fmt(0:5:2)", 0x10B0, 5, 2, b" 0.00")
|
|
|
- fmt("real_fmt(2.5:0:0)", 0x11D0, 0, 0, b"3")
|
|
|
- fmt("real_fmt(3.7:10:25)", 0x1170, 10, 25, b"3.7000E+00")
|
|
|
+ fmt("real_fmt(1:0:0)", 0x1230, 0, 0, b"1")
|
|
|
+ fmt("real_fmt(0:0:0)", 0x12B0, 0, 0, b"0")
|
|
|
+ fmt("real_fmt(3.7:0:2)", 0x1370, 0, 2, b"3.70")
|
|
|
+ fmt("real_fmt(12345.75:12:2)", 0x13C0, 12, 2, b" 12345.75")
|
|
|
+ fmt("real_fmt(0.5:6:3)", 0x13A0, 6, 3, b" 0.500")
|
|
|
+ fmt("real_fmt(-3.7:0:1)", 0x1390, 0, 1, b"-3.7")
|
|
|
+ fmt("real_fmt(0:5:2)", 0x12B0, 5, 2, b" 0.00")
|
|
|
+ fmt("real_fmt(2.5:0:0)", 0x13D0, 0, 0, b"3")
|
|
|
+ fmt("real_fmt(3.7:10:25)", 0x1370, 10, 25, b"3.7000E+00")
|
|
|
|
|
|
# --- ascreal: real_val parses an ASCIIZ string ------------------------
|
|
|
# A dump of AXOUT shows the returned offset, or FFFFh on a hard error.
|
|
|
@@ -655,17 +663,31 @@ def build_cases():
|
|
|
off += len(txt)
|
|
|
|
|
|
for bi, (a, b) in [(0, (12340, 5)), (1, (12340, 1))]:
|
|
|
- blk = 0x1300 + 16 * bi
|
|
|
+ blk = 0x1500 + 16 * bi
|
|
|
add("real_add(%d,%d)" % (a, b), [s("real_add", blk + LOAD_BIAS)],
|
|
|
dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
|
|
|
note="a large value plus a small digit: the add core's alignment")
|
|
|
|
|
|
for bi, a in [(2, 12341), (3, 12345), (4, 12340)]:
|
|
|
- blk = 0x1300 + 16 * bi
|
|
|
+ blk = 0x1500 + 16 * bi
|
|
|
add("real_mul(%d,1)" % a, [s("real_mul", blk + LOAD_BIAS)],
|
|
|
dump=6, dumpbytes=mbf(a), dumpadr=blk,
|
|
|
note="multiplying by 1.0 must be exact")
|
|
|
|
|
|
+ # --- real_write: [BP+4]=decimals, [BP+6]=width, [BP+8]=real ----------
|
|
|
+ def wr(name, blk, width, dec, text):
|
|
|
+ add(name, [s3("real_write", dec, width, blk + LOAD_BIAS)], out=text,
|
|
|
+ note="fmtreal's field, padded and printed by the entry itself")
|
|
|
+ wr("real_write(3.7:0:2)", 0x1370, 0, 2, b"3.70")
|
|
|
+ wr("real_write(3.7:10:2)", 0x1370, 10, 2, b" 3.70")
|
|
|
+ wr("real_write(12345.75:12:2)", 0x13C0, 12, 2, b" 12345.75")
|
|
|
+ wr("real_write(0.5:0:3)", 0x13A0, 0, 3, b"0.500")
|
|
|
+ wr("real_write(-3.7:0:1)", 0x1390, 0, 1, b"-3.7")
|
|
|
+ wr("real_write(2.5:0:0)", 0x13D0, 0, 0, b"3")
|
|
|
+ wr("real_write(42:0:0)", 0x1290, 0, 0, b"42")
|
|
|
+ wr("real_write(3.7:18:FFFF)", 0x1370, 18, 0xFFFF,
|
|
|
+ b" 3.7000000000E+00")
|
|
|
+
|
|
|
add("wrchar('A')", [s("wrchar", ord("A"))], out=b"A")
|
|
|
add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")
|
|
|
|