librarylibs/Hex1000/src/Hex1000.xtl
The program counter
ˡn̲extPc : Int -> Int
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 }
ˡOrder : Int
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
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
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
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
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
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 }
The state
ˡReset : (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)
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
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
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
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)
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)
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 }
ˡ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)
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 }
ʰ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)
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) }
ˡt̲race : (Num a, Truthy a) => Int -> ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, a, Int), Int, Int) -> Int
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)
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) }
Showing it
ˡRegNames : Char
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
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
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 }
ʰr̲ow : a -> Char
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 }
ʰj̲oin : (Int -> Char) -> Int -> Char
'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
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
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 }
ʰw̲ords : Char -> Box Char
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
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
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)
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) }
ʰp̲lace : (Box Char, Box Char, Int) -> (Int, Int, Int, Int, Int) -> (Int, Int, Int, Int, Int)
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) }
ʰo̲perand : (Box Char, Int) -> (Char, Int, Box Char) -> Int
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" }
ʰn̲umber : Box Char -> Int
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
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")ˡCalcRom : Int
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
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 }
ˡ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)
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) }
ˡ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)
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)
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) }
ʰ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)
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) }
ʰ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)
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
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
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 }