librarylibs/Hex1000/src/Hex1000.xtl

source · imports s: work/libs/Strings/src/Strings.xtl; rx: libs/Radix/src/Radix.xtl

The program counter

ˡn̲extPc : Int -> Int

function · line 27

n_extPc pc: the address after pc, the shift register's rule: shifted left, the new low bit the XNOR of the two top bits, inverted when the low five bits are all 1 (so all 64 addresses are visited).

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣn̲extPc 0 1 3 31 63 62
1 3 7 63 62 61
ˡn̲extPc ← { pc →
  f ← 1 − a̲bs (pc d̲iv 32) − (pc d̲iv 16) m̲od 2
  (2 × pc m̲od 32) + a̲bs f − 31 = pc m̲od 32
}
Used in: ˡOrder, ˡs̲tep

ˡOrder : Int

value · line 38

Order: the 64 addresses in the order the program counter visits them from 0. The assembler puts a page's n-th instruction at address n s_elect Order, so a program is written in reading order.

      ʰˣ⁼u̲se< "Hex1000"
      12 t̲ake ʰˣOrder
0 1 3 7 15 31 63 62 61 59 55 47
ˡOrder ← 63 '{ v → v c̲at ˡn̲extPc f̲irst (n̲eg 1) t̲ake v } p̲ower 1 r̲eshape 0

The instructions

ˡNames : Box Char

value · line 48

Names: the mnemonics, in the order of their kinds: the 29 fixed instructions (code = kind), then the bit group, LDP, TCY, TCMIY, YNEC, ACC, BR and CALL, which take an operand.

      ʰˣ⁼u̲se< "Hex1000"
      t̲ally ʰˣNames
40
ˡNames ← ˢw̲ords "NOP TAY TYA CLA TAM TMA TMY TAMIY TAMZA XMA AMAAC SAMAN IMAC DMAN IA IYC DYN CPAIZ ALEM MNEZ YNEA KNEZ TKA SETR RSTR TDO CLO COMX RETN SBIT RBIT TBIT LDX LDP TCY TCMIY YNEC ACC BR CALL"

Base : Int

value (private) · line 52

Base: each kind's first code; an instruction's code is its base plus its operand.

Base ← (o̲ffsets 29) c̲at 32 36 40 44 48 64 80 96 112 128 192

Top : Int

value (private) · line 55

Top: each kind's largest operand (0: none).

Top ← (29 r̲eshape 0) c̲at 3 3 3 3 15 15 15 15 15 63 63

ˡk̲ind : Int -> Int

function · line 62

k_ind op: the kind of an instruction code, its place in Names from 0 (the unused fixed codes 29 to 31 are NOP).

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣk̲ind 0 1 28 31 32 47 48 127 128 255
0 1 28 0 29 32 33 37 38 39
ˡk̲ind ← { op →
  fixed ← op × op < 29
  bits ← (op ≥ 32) × (op < 48) × 29 + (op − 32) d̲iv 4
  imm ← (op ≥ 48) × (op < 128) × 30 + op d̲iv 16
  jump ← (op ≥ 128) × 38 + op ≥ 192
  fixed + bits + imm + jump
}

ˡd̲isasm : Int -> Char

function · line 75

d_isasm op: an instruction code as text (BR and CALL show the address they go to, as PC holds it).

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣd̲isasm 113
ACC 1
ˡd̲isasm ← { op →
  i ← ˡk̲ind op
  name ← d̲isclose (1 + i) s̲elect ˡNames
  0 = (1 + i) s̲elect Top ? name
  name c̲at " " c̲at f̲ormat op − (1 + i) s̲elect Base
}
Used in: ˡl̲isting

The state

ˡReset : (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)

value · line 89

Reset: the state after reset, the tuple (A, Y, X, S, PC, PA, PB, SR, CL, O, R, RAM): everything 0 but S, which is 1.

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣr̲egs ʰˣReset
0 0 0 1 0 0 0 0 0 0
ˡReset ← (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 16 r̲eshape 0, 64 r̲eshape 0)

ˡr̲egs : (Any b, Any c) => (a, a, a, a, a, a, a, a, a, a, b, c) -> a

function · line 96

r_egs st: the registers as numbers, A Y X S PC PA PB SR CL O (the view a trace shows).

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣr̲egs (1, 2, 3, 0, 4, 5, 6, 7, 1, 8, 16 r̲eshape 0, 64 r̲eshape 0)
1 2 3 0 4 5 6 7 1 8
ˡr̲egs ← { (a, y, x, s, pc, pa, pb, sr, cl, o, r, ram) →
  a c̲at y c̲at x c̲at s c̲at pc c̲at pa c̲at pb c̲at sr c̲at cl c̲at o
}

ˡr̲am : (Any a, Any b, Any c, Any d, Any e, Any f, Any g, Any h, Any i, Any j, Any k) => (a, b, c, d, e, f, g, h, i, j, k, l) -> l

function · line 104

r_am st: the RAM as four files of 16 words, a row per file.

      ʰˣ⁼u̲se< "Hex1000"
      s̲hape ʰˣr̲am ʰˣReset
4 16
ˡr̲am ← { (a, y, x, s, pc, pa, pb, sr, cl, o, r, ram) → 4 16 r̲eshape ram }

ˡl̲atches : (Any a, Any b, Any c, Any d, Any e, Any f, Any g, Any h, Any i, Any j, Any k, Any l) => (a, b, c, d, e, f, g, h, i, j, k, l) -> k

function · line 110

l_atches st: the 16 output latches R, R0 first.

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣl̲atches ʰˣReset
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ˡl̲atches ← { (a, y, x, s, pc, pa, pb, sr, cl, o, r, ram) → r }

One instruction

ˡs̲tep : (Num a, Truthy a) => Int -> (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int) -> Int -> (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int)

function · line 124

rom s_tep st k: the state after one instruction, with k (0 to 15) on the K inputs: the instruction at 64 PA + PC is fetched and executed (two clocks in the circuit). S goes back to 1 after every instruction that does not set it; BR and CALL go when S is 1 and otherwise only set S to 1. A word of RAM is 16 X + Y. Bind the ROM once: u:s_tep := rom hx:s_tep, then st u:s_tep k.

      ʰˣ⁼u̲se< "Hex1000"
      rom ← ʰˣa̲ssemble "TCY 9\nTYA\nIA"
      ʰˣr̲egs (rom ʰˣs̲tep ʰˣReset)_ 0
0 9 0 1 1 0 0 0 0 0
ˡs̲tep ← { rom (a, y, x, s, pc, pa, pb, sr, cl, o, r, ram) k →
  op ← (1 + pc + 64 × pa) s̲elect rom
  i ← ˡk̲ind op
  n ← op m̲od 16
  at ← y + 16 × x
  m ← (1 + at) s̲elect ram
  bit ← 2 ^ op m̲od 4
  set ← 1 = (m d̲iv bit) m̲od 2
  go ← op m̲od 64
  p1 ← ˡn̲extPc pc
  y1 ← (y + 1) m̲od 16
  w̲r ← { v → ram + (v − m) × at = o̲ffsets 64 }
  i = 1 ? (a, a, x, 1, p1, pa, pb, sr, cl, o, r, ram)                          ⍝ TAY
  i = 2 ? (y, y, x, 1, p1, pa, pb, sr, cl, o, r, ram)                          ⍝ TYA
  i = 3 ? (0, y, x, 1, p1, pa, pb, sr, cl, o, r, ram)                          ⍝ CLA
  i = 4 ? (a, y, x, 1, p1, pa, pb, sr, cl, o, r, w̲r a)                         ⍝ TAM
  i = 5 ? (m, y, x, 1, p1, pa, pb, sr, cl, o, r, ram)                          ⍝ TMA
  i = 6 ? (a, m, x, 1, p1, pa, pb, sr, cl, o, r, ram)                          ⍝ TMY
  i = 7 ? (a, y1, x, 1, p1, pa, pb, sr, cl, o, r, w̲r a)                        ⍝ TAMIY
  i = 8 ? (0, y, x, 1, p1, pa, pb, sr, cl, o, r, w̲r a)                         ⍝ TAMZA
  i = 9 ? (m, y, x, 1, p1, pa, pb, sr, cl, o, r, w̲r a)                         ⍝ XMA
  i = 10 ? ((a + m) m̲od 16, y, x, 0 + (a + m) > 15, p1, pa, pb, sr, cl, o, r, ram)    ⍝ AMAAC
  i = 11 ? ((m − a) m̲od 16, y, x, 0 + m ≥ a, p1, pa, pb, sr, cl, o, r, ram)         ⍝ SAMAN
  i = 12 ? ((m + 1) m̲od 16, y, x, 0 + m = 15, p1, pa, pb, sr, cl, o, r, ram)        ⍝ IMAC
  i = 13 ? ((m − 1) m̲od 16, y, x, 0 + m > 0, p1, pa, pb, sr, cl, o, r, ram)         ⍝ DMAN
  i = 14 ? ((a + 1) m̲od 16, y, x, 1, p1, pa, pb, sr, cl, o, r, ram)                 ⍝ IA
  i = 15 ? (a, y1, x, 0 + y = 15, p1, pa, pb, sr, cl, o, r, ram)                    ⍝ IYC
  i = 16 ? (a, (y − 1) m̲od 16, x, 0 + y > 0, p1, pa, pb, sr, cl, o, r, ram)         ⍝ DYN
  i = 17 ? ((16 − a) m̲od 16, y, x, 0 + a = 0, p1, pa, pb, sr, cl, o, r, ram)        ⍝ CPAIZ
  i = 18 ? (a, y, x, 0 + a ≤ m, p1, pa, pb, sr, cl, o, r, ram)                 ⍝ ALEM
  i = 19 ? (a, y, x, 0 + m ≠ 0, p1, pa, pb, sr, cl, o, r, ram)                 ⍝ MNEZ
  i = 20 ? (a, y, x, 0 + y ≠ a, p1, pa, pb, sr, cl, o, r, ram)                 ⍝ YNEA
  i = 21 ? (a, y, x, 0 + k ≠ 0, p1, pa, pb, sr, cl, o, r, ram)                 ⍝ KNEZ
  i = 22 ? (k, y, x, 1, p1, pa, pb, sr, cl, o, r, ram)                         ⍝ TKA
  i = 23 ? (a, y, x, 1, p1, pa, pb, sr, cl, o, r m̲ax 0 + y = o̲ffsets 16, ram)      ⍝ SETR
  i = 24 ? (a, y, x, 1, p1, pa, pb, sr, cl, o, r × y ≠ o̲ffsets 16, ram)        ⍝ RSTR
  i = 25 ? (a, y, x, 1, p1, pa, pb, sr, cl, a + 16 × s, r, ram)                ⍝ TDO
  i = 26 ? (a, y, x, 1, p1, pa, pb, sr, cl, 0, r, ram)                         ⍝ CLO
  i = 27 ? (a, y, 3 − x, 1, p1, pa, pb, sr, cl, o, r, ram)                     ⍝ COMX
  (i = 28) ∧ cl = 1 ? (a, y, x, 1, sr, pb, pb, sr, 0, o, r, ram)               ⍝ RETN
  i = 29 ? (a, y, x, 1, p1, pa, pb, sr, cl, o, r, w̲r m + bit × n̲ot set)       ⍝ SBIT
  i = 30 ? (a, y, x, 1, p1, pa, pb, sr, cl, o, r, w̲r m − bit × set)            ⍝ RBIT
  i = 31 ? (a, y, x, 0 + set, p1, pa, pb, sr, cl, o, r, ram)                   ⍝ TBIT
  i = 32 ? (a, y, op m̲od 4, 1, p1, pa, pb, sr, cl, o, r, ram)                  ⍝ LDX
  i = 33 ? (a, y, x, 1, p1, pa, n, sr, cl, o, r, ram)                          ⍝ LDP
  i = 34 ? (a, n, x, 1, p1, pa, pb, sr, cl, o, r, ram)                         ⍝ TCY
  i = 35 ? (a, y1, x, 1, p1, pa, pb, sr, cl, o, r, w̲r n)                       ⍝ TCMIY
  i = 36 ? (a, y, x, 0 + y ≠ n, p1, pa, pb, sr, cl, o, r, ram)                 ⍝ YNEC
  i = 37 ? ((a + n) m̲od 16, y, x, 0 + (a + n) > 15, p1, pa, pb, sr, cl, o, r, ram)    ⍝ ACC
  (i = 38) ∧ s = 1 ? (a, y, x, 1, go, pa + (1 − cl) × pb − pa, pb, sr, cl, o, r, ram)   ⍝ BR
  (i = 39) ∧ (s = 1) ∧ cl = 0 ? (a, y, x, 1, go, pb, pa, p1, 1, o, r, ram)     ⍝ CALL
  (i = 39) ∧ s = 1 ? (a, y, x, 1, go, pa, pb, sr, cl, o, r, ram)               ⍝ CALL in a subroutine: a branch in the page
  (a, y, x, 1, p1, pa, pb, sr, cl, o, r, ram)                                  ⍝ NOP, and BR or CALL with S 0
}

Running a program

ˡr̲un : (Num a, Truthy a) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int, Int) -> (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int)

function · line 187

rom r_un (st, k, n): the state after n instructions with k held on the K inputs.

      ʰˣ⁼u̲se< "Hex1000"
      rom ← ʰˣa̲ssemble "loop: IA\nBR loop"
      f̲irst ʰˣr̲egs rom ʰˣr̲un (ʰˣReset, 0, 9)
5
ˡr̲un ← { rom (st, k, n) → n '{ t → (rom ˡs̲tep t)_ k } p̲ower st }
Used in: ʰp̲ress

ˡr̲unKeys : (Num a, Truthy a) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int) -> (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int)

function · line 196

rom r_unKeys (st, ks): the state after a run of key presses: ks is a matrix with a row per press, the K value and how many instructions it is held (K 0 for no key).

      ʰˣ⁼u̲se< "Hex1000"
      rom ← ʰˣa̲ssemble "loop: TKA\nTAMIY\nBR loop"
      8 t̲ake f̲irst ʰˣr̲am rom ʰˣr̲unKeys (ʰˣReset, 2 2 r̲eshape 7 3 5 3)
7 5 0 0 0 0 0 0
ˡr̲unKeys ← { rom (st, ks) →
  (i, last) ← (t̲ally ks) '{ (j, t) → (j + 1, rom ʰp̲ress (t, j s̲elect ks)) } p̲ower (1, st)
  last
}
Used in: after

ʰp̲ress : (Num a, Truthy a) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int) -> (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int)

function (private) · line 202

One press: a row of ks, the K value and how many instructions.

ʰp̲ress ← { rom (t, row) → rom ˡr̲un (t, f̲irst row, f̲irst 1 d̲rop row) }
Used in: ˡr̲unKeys

ˡt̲race : (Num a, Truthy a) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int, Int) -> Int

function · line 213

rom t_race (st, k, n): the registers (r_egs) after reset and after each of n instructions, a row each, with k on the K inputs.

      ʰˣ⁼u̲se< "Hex1000"
      rom ← ʰˣa̲ssemble "TCY 3\nDYN\nBR 1"
      rom ʰˣt̲race (ʰˣReset, 0, 3)
0 0 0 1 0 0 0 0 0 0
0 3 0 1 1 0 0 0 0 0
0 2 0 1 3 0 0 0 0 0
0 2 0 1 1 0 0 0 0 0
ˡt̲race ← { rom (st, k, n) →
  (last, rows) ← n '{ (t, m) → ʰn̲ext (rom, t, m, k) } p̲ower (st, 1 10 r̲eshape ˡr̲egs st)
  rows
}

ʰn̲ext : (Num a, Truthy a) => (Int, (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int, Int) -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int)

function (private) · line 219

One more instruction, its registers added as a row.

ʰn̲ext ← { (rom, t, m, k) →
  t1 ← (rom ˡs̲tep t)_ k
  (t1, m c̲at ˡr̲egs t1)
}
Used in: ˡt̲race

Showing it

ˡRegNames : Char

value · line 230

RegNames: the names of r_egs's columns, for t_able.

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣRegNames
A Y X S PC PA PB SR CL O
ˡRegNames ← "A Y X S PC PA PB SR CL O"

ˡt̲able : Char -> a -> Char

function · line 239

names t_able m: the rows of a matrix of numbers (or one vector) as text under column names, each column five characters wide.

      ʰˣ⁼u̲se< "Hex1000"
      "K held" ʰˣt̲able 2 2 r̲eshape 12 8 0 9
    K held
   12    8
    0    9
ˡt̲able ← { names m →
  rows ← ʰm̲atrix m
  ws ← ˢw̲ords names
  head ← '{ j → 5 ˢp̲adLeft d̲isclose j s̲elect ws } ʰj̲oin t̲ally ws
  head c̲at '{ i → "\n" c̲at ʰr̲ow i s̲elect rows } ʰj̲oin t̲ally rows
}

ʰm̲atrix : a -> a

function (private) · line 247

A vector as a matrix of one row.

ʰm̲atrix ← { m →
  1 = t̲ally s̲hape m ? (1 c̲at t̲ally m) r̲eshape m
  m
}
Used in: ˡt̲able

ʰr̲ow : a -> Char

function (private) · line 253

One row of numbers, five characters each.

ʰr̲ow ← { row → '{ j → 5 ˢp̲adLeft f̲ormat j s̲elect row } ʰj̲oin t̲ally row }
Used in: ˡt̲able

ʰj̲oin : (Int -> Char) -> Int -> Char

function (private) · line 256

'f j_oin n: the texts f 1, f 2, ... f n, one after another (a loop).

ʰj̲oin ← { f̲ n →
  (j, t) ← n '{ (j, t) → (j + 1, t c̲at f̲ j) } p̲ower (1, "")
  t
}

ˡl̲isting : Int -> Int -> Char

function · line 269

p l_isting rom: page p of a ROM in the order it runs, up to its last instruction that is not NOP: each instruction's PC, code and text.

      ʰˣ⁼u̲se< "Hex1000"
      0 ʰˣl̲isting ʰˣa̲ssemble "loop: TDO\nIA\nBR loop"
  PC code  instruction
   0   25  TDO
   1   14  IA
   3  128  BR 0
ˡl̲isting ← { p rom →
  codes ← (1 + (64 × p) + ˡOrder) s̲elect rom
  n ← 'm̲ax r̲/ (codes ≠ 0) × 1 + o̲ffsets 64
  l̲ine ← { j → "\n" c̲at (4 ˢp̲adLeft f̲ormat j s̲elect ˡOrder) c̲at (5 ˢp̲adLeft f̲ormat j s̲elect codes) c̲at "  " c̲at ˡd̲isasm j s̲elect codes }
  "  PC code  instruction" c̲at 'l̲ine ʰj̲oin n
}

The assembler

ˡa̲ssemble : Char -> Int

function · line 288

a_ssemble text: the 1024-byte ROM of a program. A line is an optional label ending in ":", then an instruction and its operand (a number, or a label: BR and CALL go to it, LDP takes its page), then an optional comment after ";". "PAGE n" starts page n (from 0); a page's instructions are placed in the program counter's order (Order), so they run in the order they are written. Unused bytes are 0, NOP. A mistake stops the program with the line it is on.

      ʰˣ⁼u̲se< "Hex1000"
      8 t̲ake ʰˣa̲ssemble "CLA\nTCY 5\nloop: IA\nBR loop"
3 69 0 14 0 0 0 131
ˡa̲ssemble ← { text →
  lines ← ˢl̲ines text
  n ← t̲ally lines
  (i, page, slot, names, places) ← n '{ t → lines ʰl̲abel t } p̲ower (1, 0, 0, 0 r̲eshape e̲nclose "", 0 r̲eshape 0)
  (j, pg, sl, addrs, codes) ← n '{ t → (lines, names, places) ʰp̲lace t } p̲ower (1, 0, 0, 0 r̲eshape 0, 0 r̲eshape 0)
  (addrs i̲ndexOf o̲ffsets 1024) s̲elect codes c̲at 0
}
Used in: ˡCalcRom, rom, scan

ʰw̲ords : Char -> Box Char

function (private) · line 297

The words of a line, its comment dropped.

ʰw̲ords ← { t → ˢw̲ords ('∧ s̲\ t ≠ f̲irst ";") r̲eplicate t }

ʰl̲abel? : Box Char -> Int

function (private) · line 300

Whether a line's first word is a label.

ʰl̲abel? ← { ws →
  0 = t̲ally ws ? 0 = 1
  (f̲irst ":") = f̲irst (n̲eg 1) t̲ake d̲isclose f̲irst ws
}

ʰr̲est : Box Char -> Box Char

function (private) · line 306

Where the instruction words start, past a label.

ʰr̲est ← { ws → (0 + ʰl̲abel? ws) d̲rop ws }

ʰl̲abel : Box Char -> (Int, Int, Int, Box Char, Int) -> (Int, Int, Int, Box Char, Int)

function (private) · line 311

The first pass, one line: a label's name and place (64 page + slot) recorded; the slot advanced past an instruction; PAGE n starts page n.

ʰl̲abel ← { lines (i, page, slot, names, places) →
  ws ← ʰw̲ords d̲isclose i s̲elect lines
  rest ← ʰr̲est ws
  named ← 0 + ʰl̲abel? ws
  name ← (n̲eg 1) d̲rop d̲isclose f̲irst ws c̲at e̲nclose " "
  nn ← names c̲at named t̲ake 1 r̲eshape e̲nclose name
  np ← places c̲at named t̲ake 1 r̲eshape slot + 64 × page
  0 = t̲ally rest ? (i + 1, page, slot, nn, np)
  "PAGE" m̲atch d̲isclose f̲irst rest ? (i + 1, ʰn̲umber 1 d̲rop rest, 0, nn, np)
  (i + 1, page, slot + 1, nn, np)
}
Used in: ˡa̲ssemble

ʰp̲lace : (Box Char, Box Char, Int) -> (Int, Int, Int, Int, Int) -> (Int, Int, Int, Int, Int)

function (private) · line 324

The second pass, one line: the instruction's address and code.

ʰp̲lace ← { (lines, names, places) (i, page, slot, addrs, codes) →
  rest ← ʰr̲est ʰw̲ords d̲isclose i s̲elect lines
  0 = t̲ally rest ? (i + 1, page, slot, addrs, codes)
  word ← d̲isclose f̲irst rest
  "PAGE" m̲atch word ? (i + 1, ʰn̲umber 1 d̲rop rest, 0, addrs, codes)
  where ← "line " c̲at (f̲ormat i) c̲at ": "
  slot > 63 ? ⎕P̲ANIC where c̲at "page " c̲at (f̲ormat page) c̲at " is full"
  kind ← (ˡNames i̲ndexOf e̲nclose word) − 1
  kind ≥ t̲ally ˡNames ? ⎕P̲ANIC where c̲at "unknown instruction " c̲at word
  v ← (names, places) ʰo̲perand (where, kind, 1 d̲rop rest)
  (v < 0) ∨ v > (1 + kind) s̲elect Top ? ⎕P̲ANIC where c̲at "operand out of range for " c̲at word
  addr ← (64 × page) + (1 + slot) s̲elect ˡOrder
  (i + 1, page, slot + 1, addrs c̲at addr, codes c̲at v + (1 + kind) s̲elect Base)
}
Used in: ˡa̲ssemble

ʰo̲perand : (Box Char, Int) -> (Char, Int, Box Char) -> Int

function (private) · line 341

An operand: a number, or a label (BR and CALL: its address as PC holds it; LDP: its page); none is 0.

ʰo̲perand ← { (names, places) (where, kind, ws) →
  0 = t̲ally ws ? 0
  w ← d̲isclose f̲irst ws
  '∧ r̲/ w m̲ember? ⎕D ? ʰn̲umber ws
  j ← names i̲ndexOf e̲nclose w
  j > t̲ally names ? ⎕P̲ANIC where c̲at "unknown label " c̲at w
  place ← j s̲elect places
  kind = 33 ? place d̲iv 64
  kind ≥ 38 ? (1 + place m̲od 64) s̲elect ˡOrder
  ⎕P̲ANIC where c̲at "a label is an operand only of BR, CALL and LDP"
}
Used in: ʰp̲lace

ʰn̲umber : Box Char -> Int

function (private) · line 354

A decimal number, the first word.

ʰn̲umber ← { ws → 10 d̲ecode (⎕D i̲ndexOf d̲isclose f̲irst ws) − 1 }

The calculator

ˡCalc : Char

value · line 363

Calc: the calculator program's source (rom/calc.asm): an eight-digit hexadecimal calculator, Radix's behavior key for key.

      ʰˣ⁼u̲se< "Hex1000"
      5 t̲ake ʰˣCalc
; HEX
ˡCalc ← @ i̲nclude< "../rom/calc.asm"
i̲nclude< expands to
("; HEX1000's calculator program: an eight-digit hexadecimal calculator\n; with + - * = and X (clear), the behavior of the Radix library\n; (libs/Radix) key for key. Assembled by hx:a_ssemble; the library\n; includes this file as hx:Calc.\n;\n; RAM: file 0 the accumulator, file 1 the entry, file 2 the\n; multiplier's digits while multiplying, each eight words, word 0 the\n; least significant digit. File 3 holds the control words:\n;   word 0  the column of the key just read (8 to 13)\n;   word 1  the pending operation: 0 none, 1 +, 2 -, 3 *\n;   word 2  the phase: 0 after an operator, 1 entering digits, 2 after =\n;   word 3  the digit just keyed\n;   word 4  the operator key's row: 0 +, 1 -, 2 *, 3 =\n;   word 5  the multiplier digit being worked on\n;   words 8 to 13: the K bits read from that column\n;\n; The R latches: R0 to R7 strobe the display's digits (digit 0 on the\n; right): a digit takes O when its latch rises; O's bit 4 (S at TDO)\n; set shows the digit A, clear blanks it. R8 to R13 select the key\n; columns: the key with code c is in column c / 4 and drives K's bit\n; c mod 4 while its column is selected (codes as Radix's: 0 to 15 the\n; digits, 16 + 17 - 18 * 19 =, 20 X). R15 is 1 while the program waits\n; for a key, so a terminal (or the test harness) types the next key\n; only then.\n;\n; One subroutine level: the subroutines (pages 4 to 6) call nothing;\n; the main code (pages 0 to 3) calls them, after LDP of their page.\n\nPAGE 0          ; reset, the key scan, what kind of key\n\ninit:   LDX 1\n        LDP show\n        CALL show       ; show the entry: 0\nready:  TCY 8           ; wait until every key is up\nrl:     SETR\n        KNEZ\n        BR held\n        RSTR\n        IYC\n        YNEC 14\n        BR rl\n        TCY 15\n        SETR            ; R15: ready for a key\nscan:   TCY 8\ncol:    SETR            ; select a column\n        KNEZ\n        BR hit          ; a key in it\n        RSTR\n        IYC\n        YNEC 14\n        BR col\n        BR scan\nheld:   RSTR\n        BR ready\nhit:    TKA             ; the key's row bit\n        RSTR\n        LDX 3\n        TAM             ; kept in file 3 at the column's word\n        TYA\n        TCY 0\n        TAM             ; the column\n        TCY 15\n        RSTR            ; R15 0: busy\n        TCY 0\n        TMY             ; Y: the column again\n        CLA             ; A: the row, from the bit\n        TBIT 0\n        BR rowok\n        IA\n        TBIT 1\n        BR rowok\n        IA\n        TBIT 2\n        BR rowok\n        IA\nrowok:  YNEC 12\n        BR notop\n        LDP opkey       ; column 12: + - * =\n        BR opkey\nnotop:  YNEC 13\n        BR digit\n        ACC 15          ; column 13: only row 0 is a key, X\n        BR ready\n        LDP clear\n        BR clear\ndigit:  YNEC 8          ; the digit: the row plus 4 per column\n        BR dm\n        LDP dgo\n        BR dgo\ndm:     ACC 4\n        DYN\n        BR digit\n\nPAGE 1          ; a digit key\n\ndgo:    LDX 3\n        TCY 3\n        TAM             ; keep the digit\n        TCY 2\n        TMA\n        ACC 14\n        BR fresh        ; phase 2: a new calculation\n        TMA\n        ACC 15\n        BR app          ; phase 1: append\n        LDX 1           ; phase 0: a new entry\n        LDP zero\n        CALL zero\n        BR app2\nfresh:  LDX 0\n        LDP zero\n        CALL zero\n        LDX 1\n        LDP zero\n        CALL zero\n        LDX 3\n        TCY 1\n        TCMIY 0         ; nothing pending\n        BR app2\napp:    LDX 1\n        TCY 7\n        MNEZ\n        BR full         ; eight digits already: ignored\napp2:   LDX 1\n        LDP shl\n        CALL shl        ; the entry times 16\n        LDX 3\n        TCY 3\n        TMA\n        LDX 1\n        TCY 0\n        TAM             ; plus the digit\n        LDX 3\n        TCY 2\n        TCMIY 1         ; phase 1\n        LDX 1\n        LDP show\n        CALL show       ; show the entry\n        LDP ready\n        BR ready\nfull:   LDP ready\n        BR ready\n\nPAGE 2          ; an operator key, or =: settle the pending operation\n\nopkey:  LDX 3\n        TCY 4\n        TAM             ; keep the row\n        TCY 2\n        TMA\n        ACC 14\n        BR settled      ; phase 2: nothing keyed since =\n        TMA\n        ACC 15\n        BR p1           ; phase 1: settle\n        BR settled      ; phase 0: nothing keyed since the operator\np1:     TCY 1\n        TMA             ; the pending operation\n        ACC 15\n        BR doop\n        LDP copy10\n        CALL copy10     ; none: the entry is the result\n        BR settled\ndoop:   ACC 15\n        BR nadd\n        LDP add\n        CALL add\n        BR settled\nnadd:   ACC 15\n        BR domul\n        LDP sub\n        CALL sub\n        BR settled\ndomul:  LDP mul\n        BR mul\nsettled: LDX 3\n        TCY 4\n        TMA\n        ACC 13\n        BR equals       ; row 3: =\n        TMA\n        IA\n        TCY 1\n        TAM             ; pending: the row plus 1\n        TCY 2\n        TCMIY 0         ; phase 0\n        BR cle\nequals: TCY 1\n        TCMIY 0         ; nothing pending\n        TCMIY 2         ; phase 2\ncle:    LDX 1\n        LDP zero\n        CALL zero       ; the entry starts at 0\n        LDX 0\n        LDP show\n        CALL show       ; show the result\n        LDP ready\n        BR ready\n\nPAGE 3          ; multiply, and clear\n\nmul:    LDP copy12\n        CALL copy12     ; the multiplier (the entry) to file 2\n        LDP copy01\n        CALL copy01     ; the multiplicand (the accumulator) to file 1\n        LDX 0\n        LDP zero\n        CALL zero       ; the product\n        LDX 3\n        TCY 5\n        TCMIY 7         ; from the multiplier's top digit\nmloop:  LDX 0\n        LDP shl\n        CALL shl        ; the product times 16\ninner:  LDX 3\n        TCY 5\n        TMY\n        LDX 2\n        MNEZ\n        BR mdo          ; this digit not yet used up\n        LDX 3\n        TCY 5\n        DMAN\n        BR mnext        ; the next digit down\n        LDP settled\n        BR settled      ; done: the product is in file 0\nmnext:  TAM\n        BR mloop\nmdo:    DMAN\n        TAM             ; one fewer\n        LDP add\n        CALL add        ; the product plus the multiplicand\n        BR inner\nclear:  LDX 0\n        LDP zero\n        CALL zero\n        LDX 1\n        LDP zero\n        CALL zero\n        LDX 3\n        TCY 1\n        TCMIY 0         ; nothing pending\n        TCMIY 0         ; phase 0\n        LDX 1\n        LDP show\n        CALL show\n        LDP ready\n        BR ready\n\nPAGE 4          ; subroutines: show, shl, zero (on file X)\n\nshow:   TCY 7           ; from the top digit: leading zeros blank\nlead:   TMA\n        MNEZ\n        BR digs         ; the first digit that is not 0\n        YNEC 0\n        BR blank\n        TDO             ; the number is 0: its last digit shows 0\n        SETR\n        RSTR\n        RETN\nblank:  MNEZ            ; S 0: blank\n        TDO\n        SETR\n        RSTR\n        DYN\n        BR lead\ndigs:   TMA\n        TDO             ; S 1: shown\n        SETR\n        RSTR\n        DYN\n        BR digs\n        RETN\nshl:    TCY 6           ; each word up one, 0 into word 0\nsl:     TMA\n        IYC\n        TAM\n        DYN\n        DYN\n        BR sl\n        TCY 0\n        TCMIY 0\n        RETN\nzero:   TCY 0\nzl:     TCMIY 0\n        YNEC 8\n        BR zl\n        RETN\n\nPAGE 5          ; subroutines: file 0 plus file 1, file 0 minus file 1\n\nadd:    TCY 0\nan:     LDX 1           ; no carry in\n        TMA\n        LDX 0\n        AMAAC\n        BR ac\n        TAMIY\n        YNEC 8\n        BR an\n        RETN\nac:     TAMIY           ; a carry out\n        YNEC 8\n        BR ci\n        RETN\nci:     LDX 1           ; a carry in\n        TMA\n        LDX 0\n        AMAAC\n        BR cc\n        ACC 1\n        BR ac\n        TAMIY\n        YNEC 8\n        BR an\n        RETN\ncc:     IA\n        BR ac\nsub:    TCY 0\nsn:     LDX 1           ; no borrow in\n        TMA\n        LDX 0\n        SAMAN\n        BR snb\n        TAMIY           ; a borrow out\n        YNEC 8\n        BR sb\n        RETN\nsnb:    TAMIY\n        YNEC 8\n        BR sn\n        RETN\nsb:     LDX 1           ; a borrow in\n        TMA\n        LDX 0\n        SAMAN\n        BR sb1\n        ACC 15\n        TAMIY\n        YNEC 8\n        BR sb\n        RETN\nsb1:    ACC 15\n        BR snb\n        TAMIY\n        YNEC 8\n        BR sb\n        RETN\n\nPAGE 6          ; subroutines: copies between files\n\ncopy10: TCY 0           ; file 1 to file 0\nc10:    LDX 1\n        TMA\n        LDX 0\n        TAMIY\n        YNEC 8\n        BR c10\n        RETN\ncopy12: TCY 0           ; file 1 to file 2\nc12:    LDX 1\n        TMA\n        LDX 2\n        TAMIY\n        YNEC 8\n        BR c12\n        RETN\ncopy01: TCY 0           ; file 0 to file 1\nc01:    LDX 0\n        TMA\n        LDX 1\n        TAMIY\n        YNEC 8\n        BR c01\n        RETN\n")
Used in: ˡCalcRom

ˡCalcRom : Int

value · line 369

CalcRom: the calculator program assembled, 1024 bytes.

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣd̲isasm 4 s̲elect ʰˣCalcRom
CALL 0
ˡCalcRom ← ˡa̲ssemble ˡCalc

ˡm̲atrix : (Int, Int) -> Int

function · line 378

m_atrix (c, r): the K inputs the keyboard gives with key c held and the latches r: the keys are a matrix of 6 columns, selected by R8 to R13, of 4 rows, one K bit each; key c is in column c / 4 and row c mod 4 (the codes are Radix's, rx:k_eys).

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣm̲atrix (18, (12 r̲eshape 0) c̲at 1 0 0 0)
4
ˡm̲atrix ← { (c, r) → ((9 + c d̲iv 4) s̲elect r) × 2 ^ c m̲od 4 }
Used in: ʰr̲ec, ʰt̲ick

ˡt̲ype : (Truthy a, Num b) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int) -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, a, b)

function · line 391

rom t_ype (st, keys, n): the machine typed at, as a terminal does: each key is held from when R15 says the program is ready until it says busy, and the display latches each digit's O when its latch R0 to R7 rises. Gives (the state, the eight digit latches, digit 0 first, whether every key was taken and the program is ready again, the instructions it took) after at most n instructions; once done it stops.

      ʰˣ⁼u̲se< "Hex1000"
      (st, digits, done, took) ← ʰˣCalcRom ʰˣt̲ype (ʰˣReset, 1 2, 2000)
      ʰˣs̲creen r̲ev digits
      12
ˡt̲ype ← { rom (st, keys, n) → rom ˡt̲ypeOn (st, 8 r̲eshape 0, keys, n) }
Used in: ˡc̲alc, ˡc̲ost, %39, %42

ˡt̲ypeOn : (Truthy a, Num b) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int, Int) -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, a, b)

function · line 401

rom t_ypeOn (st, digits, keys, n): t_ype from a display already showing digits (a terminal keeps its screen between keys: a key the program ignores changes nothing on it).

      ʰˣ⁼u̲se< "Hex1000"
      (st, digits, done, took) ← ʰˣCalcRom ʰˣt̲ype (ʰˣReset, 1 2, 2000)
      (st2, digits2, done2, took2) ← ʰˣCalcRom ʰˣt̲ypeOn (st, digits, 21 r̲eshape 0, 0)
      ʰˣs̲creen r̲ev digits2
      12
ˡt̲ypeOn ← { rom (st, digits, keys, n) →
  start ← (st, digits, ˡl̲atches st, 0, 1, 0)
  (t, d, r, ph, i, took) ← n '{ v → (rom, keys) ʰt̲ick v } p̲ower start
  (t, d, ph = 3, took)
}

ˡr̲ecord : Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int) -> (Int, Int, Int)

function · line 415

rom r_ecord (st, keys, n): t_ype's first n instructions as the circuit sees them at its ports: per instruction, the K value given, and O and the R latches after it, (ks, os, rs) (rs a row per instruction). n is t_ype's count for the keys, so nothing is idle.

      ʰˣ⁼u̲se< "Hex1000"
      (ks, os, rs) ← ʰˣCalcRom ʰˣr̲ecord (ʰˣReset, 0 r̲eshape 0, 3)
      ks
0 0 0
ˡr̲ecord ← { rom (st, keys, n) →
  start ← ((st, 8 r̲eshape 0, ˡl̲atches st, 0, 1, 0), 0 r̲eshape 0, 0 r̲eshape 0, 0 16 r̲eshape 0)
  (v, ks, os, rs) ← n '{ (v, ks, os, rs) → ʰr̲ec (rom, keys, v, ks, os, rs) } p̲ower start
  (ks, os, rs)
}
Used in: %40

ʰr̲ec : (Num a, Truthy a, Num b) => (Int, Int, ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int, a, Int, b), Int, Int, Int) -> (((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int, a, Int, b), Int, Int, Int)

function (private) · line 422

One recorded instruction: t_ick, and what crossed the ports.

ʰr̲ec ← { (rom, keys, (t, digits, r, ph, i, took), ks, os, rs) →
  c ← (i s̲elect keys c̲at 0) × ph = 1
  k ← (ph = 1) × ˡm̲atrix (c, r)
  v1 ← (rom, keys) ʰt̲ick (t, digits, r, ph, i, took)
  (t1, d1, r1, ph1, i1, took1) ← v1
  (v1, ks c̲at k, os c̲at f̲irst (n̲eg 1) t̲ake ˡr̲egs t1, rs c̲at r1)
}
Used in: ˡr̲ecord

ʰt̲ick : (Num a, Truthy a, Num b) => (Int, Int) -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int, a, Int, b) -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int), Int, Int, a, Int, b)

function (private) · line 432

One instruction of t_ype: phase 0 waits for R15, 1 holds key i, 3 is done.

ʰt̲ick ← { (rom, keys) (t, digits, r, ph, i, took) →
  ph = 3 ? (t, digits, r, ph, i, took)
  c ← (i s̲elect keys c̲at 0) × ph = 1
  k ← (ph = 1) × ˡm̲atrix (c, r)
  t1 ← (rom ˡs̲tep t)_ k
  r1 ← ˡl̲atches t1
  rose ← (8 t̲ake r1) > 8 t̲ake r
  o ← f̲irst (n̲eg 1) t̲ake ˡr̲egs t1
  d1 ← digits + rose × o − digits
  ready ← 1 = 16 s̲elect r1
  (ph = 0) ∧ ready ? (t1, d1, r1, 1 + 2 × i > t̲ally keys, i, took + 1)
  (ph = 1) ∧ n̲ot ready ? (t1, d1, r1, 0, i + 1, took + 1)
  (t1, d1, r1, ph, i, took + 1)
}

ˡs̲creen : Int -> Char

function · line 453

s_creen digits: latched digits as text, in the order given (the display's is digit 7 first: hx:s_creen r_ev digits); a digit latched with O's bit 4 clear is blank.

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣs̲creen 0 0 0 0 0 31 17 2
     F1 
ˡs̲creen ← { d → (1 + (d ≥ 16) × 1 + d m̲od 16) s̲elect " " c̲at ⎕D c̲at 6 t̲ake ⎕A }

ˡc̲alc : Char -> Char

function · line 472

c_ost text: how many instructions the calculator takes for the keys of a text, from reset until it is ready after the last key (at 13.5 million instructions a second on a board, two clocks each at 27 MHz).

      ʰˣ⁼u̲se< "Hex1000"
      ʰˣc̲ost "12+34="
1752
ˡc̲alc ← { text →
  codes ← ʳˣk̲eys text
  keys ← (codes < 21) r̲eplicate codes
  n ← 2000 + (1500 × t̲ally keys) + 12000 × '+ r̲/ text = f̲irst "*"
  (st, digits, done, took) ← ˡCalcRom ˡt̲ype (ˡReset, keys, n)
  n̲ot done ? ⎕P̲ANIC "hx:c_alc: the program did not finish " c̲at text c̲at " in " c̲at f̲ormat n
  ˡs̲creen r̲ev digits
}

ˡc̲ost : Num a => Char -> a

function · line 481
ˡc̲ost ← { text →
  codes ← ʳˣk̲eys text
  keys ← (codes < 21) r̲eplicate codes
  n ← 2000 + (1500 × t̲ally keys) + 12000 × '+ r̲/ text = f̲irst "*"
  (st, digits, done, took) ← ˡCalcRom ˡt̲ype (ˡReset, keys, n)
  took
}