File size: 4,478 Bytes
2166d7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# FA opcode tests.
#   1. Every case in sim/fa/vectors/fa_vectors.txt is run as a real SUBLEQ program in the Nim
#      interpreter (normal subtract/branch code, FA trap, then more SUBLEQ that prints the result)
#      and compared with the Python integer model's expected output.
#   2. The same programs, plus transpiled Brainfuck programs, are written as memory images to
#      sim/fa/work_img/ so the RTL SoC testbench can run them and compare against the same
#      expected outputs (see sim/fa/run_all.sh).
# Run from the repository root.

import std/[os, strutils]
import ../src/subleq_bf

const
  VecFile = "sim/fa/vectors/fa_vectors.txt"
  ImgDir = "sim/fa/work_img"
  Data = 2048
  ZeroC = Data
  OneC = Data + 1
  ResC = Data + 2
  TwoC = Data + 3
  CntC = Data + 4
  Desc = Data + 8
  Qa = Data + 32

var failures = 0
var passes = 0

proc fail(name, msg: string) =
  inc failures
  echo "FAIL ", name, ": ", msg

proc writeLines(path: string; xs: seq[int]) =
  var s = ""
  for x in xs: s.add $x & "\n"
  writeFile(path, s)

proc buildProgram(q, k, v: seq[int]; n, d: int): tuple[mem: seq[int], expected: seq[int]] =
  ## SUBLEQ program: RES = 10 - 2; loop CNT 3 -> 0 printing CNT; FA trap; print RES; print O.
  let nd = n * d
  let ka = Qa + nd + 4
  let va = ka + nd + 4
  let oa = va + nd + 4
  var mem = newSeq[int](oa + nd + 4)
  var code: seq[int]
  proc tri(a, b, c: int) =
    code.add a
    code.add b
    code.add c
  tri(TwoC, ResC, 3)                  # RES -= 2
  tri(OneC, CntC, 12)                 # 3: CNT -= 1; CNT <= 0 -> leave loop
  tri(CntC, -1, 9)                    # 6: print CNT
  tri(ZeroC, ZeroC, 3)                # 9: loop
  tri(-2, Desc, 15)                   # 12: FA trap
  var here = 15
  tri(ResC, -1, here + 3); here += 3  # print RES (proves state survived the trap)
  for i in 0 ..< nd:
    tri(oa + i, -1, here + 3); here += 3
  tri(ZeroC, ZeroC, -1)               # halt
  for i, w in code: mem[i] = w
  doAssert code.len < Data
  mem[OneC] = 1
  mem[ResC] = 10
  mem[TwoC] = 2
  mem[CntC] = 3
  mem[Desc] = Qa; mem[Desc + 1] = ka; mem[Desc + 2] = va; mem[Desc + 3] = oa
  mem[Desc + 4] = n; mem[Desc + 5] = d
  for i in 0 ..< nd:
    mem[Qa + i] = q[i]; mem[ka + i] = k[i]; mem[va + i] = v[i]
  (mem, @[2, 1, 8])

createDir ImgDir
let tokens = readFile(VecFile).splitWhitespace()
var pos = 0
proc nextInt(): int =
  result = parseInt(tokens[pos])
  inc pos

let ncases = nextInt()
for cs in 0 ..< ncases:
  let n = nextInt()
  let d = nextInt()
  var q, k, v, o: seq[int]
  for _ in 0 ..< n * d: q.add nextInt()
  for _ in 0 ..< n * d: k.add nextInt()
  for _ in 0 ..< n * d: v.add nextInt()
  for _ in 0 ..< n * d: o.add nextInt()
  let name = "fa_case_" & align($cs, 3, '0')
  var (mem, expected) = buildProgram(q, k, v, n, d)
  expected.add o
  writeLines(ImgDir / (name & ".img"), mem)
  writeLines(ImgDir / (name & ".exp"), expected)
  let res = runSubleq(mem, @[], 1_000_000)
  if res.fault.len > 0: fail name, "fault: " & res.fault
  elif not res.halted: fail name, "did not halt"
  elif res.output != expected: fail name, "output differs from the Python model"
  else: inc passes

# FA error handling: invalid descriptors fault
for (n, d) in [(0, 4), (4, 0), (4, 17)]:
  var (mem, _) = buildProgram(@[0], @[0], @[0], 1, 1)
  mem[Desc + 4] = n
  mem[Desc + 5] = d
  let res = runSubleq(mem, @[], 100_000)
  if res.fault.len == 0: fail "invalid-desc", "expected a fault for N=" & $n & " d=" & $d
  else: inc passes

# Brainfuck programs for the RTL CPU (word semantics are identical)
const HelloBF = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
for (name, bf, input) in [("bf_hello", HelloBF, newSeq[int]()),
                          ("bf_nested", "++[>++[>++<-]<-]>>.", newSeq[int]()),
                          ("bf_negative", "---[+]>-.", newSeq[int]()),
                          ("bf_cat", ",[.,]", @[104, 105, 33])]:
  let tr = brainfuckToSubleq(bf, 64)
  let ref0 = runBF(bf, input, 64)
  doAssert tr.error.len == 0 and ref0.error.len == 0
  writeLines(ImgDir / (name & ".img"), tr.mem)
  writeLines(ImgDir / (name & ".exp"), ref0.output)
  writeLines(ImgDir / (name & ".in"), input)
  var mem = tr.mem
  let res = runSubleq(mem, input, 5_000_000)
  if res.output != ref0.output or not res.halted: fail name, "transpiled program differs from reference"
  else: inc passes

echo "passed: ", passes, "  failed: ", failures
if failures > 0: quit 1