ilo monitor for the ilo virtual computer. Commands: - `e start data...` writes one or more decimal cells to memory - `a start code...` writes pali bundles or raw cells (prefix with `#`) to memory - `c address` calls a function - `d start cells` displays memory as `address: value` - `u address cells` disassembles memory as bundles - `l block count address` loads blocks into memory - `w block count address` writes memory to blocks The monitor is split into a few small parts. There is a simple read/eval loop, a set of command handlers, some terminal and number output helpers, and a small parser for decimal numbers and pali instruction bundles. I read a line into a buffer, look at the first character, and jump to the matching command. The individual commands then parse the rest of the line in whatever way they need. ~~~ c startup : start c The repl is intentionally small. It prints a prompt, reads a c line, skips leading spaces, and dispatches based on the first c non-space character. : repl i lica.... r prompt i lica.... r read-line i lifelieq r eof-flag d -1 i licj.... r halt i lilist.. r input-buffer r src-ptr i lica.... r skip-spaces i lifefeli r src-ptr r temp-char i stlifeli r temp-char d 0 i eqlicj.. r repl i life.... r temp-char i liorlist d 32 r temp-char i life.... r temp-char i lieqlicj d 101 r cmd-enter i life.... r temp-char i lieqlicj d 97 r cmd-assemble i life.... r temp-char i lieqlicj d 99 r cmd-call i life.... r temp-char i lieqlicj d 100 r cmd-display i life.... r temp-char i lieqlicj d 117 r cmd-unassemble i life.... r temp-char i lieqlicj d 108 r cmd-load i life.... r temp-char i lieqlicj d 119 r cmd-write i liju.... r repl c `e` is the simplest writer. It takes a start address, then c keeps parsing decimal values until it reaches the end of the c line. Each parsed value is stored and the target address is c advanced by one cell. : cmd-enter i lifeliad r src-ptr d 1 i listlica r src-ptr r parse-number i lifelist r parse-result r cmd-addr : ce-loop i lica.... r skip-spaces i lifefeli r src-ptr r temp-char i stlife.. r temp-char i lieqlicj d 0 r ce-done i lica.... r parse-number i life.... r parse-result i lifestli r cmd-addr r cmd-addr i feliadli d 1 r cmd-addr i stliju.. r ce-loop : ce-done i liju.... r repl c `a` follows the same model as `e`, but each token is either a c pali bundle or a raw cell prefixed with `#`. Bundles are c packed into one instruction cell; raw cells are stored as-is. : cmd-assemble i lifeliad r src-ptr d 1 i listlica r src-ptr r parse-number i lifelist r parse-result r cmd-addr : ca-loop i lica.... r skip-spaces i lifefeli r src-ptr r temp-char i stlife.. r temp-char i lieqlicj d 0 r ca-done i lifelieq r temp-char d 35 i licj.... r ca-raw i lica.... r parse-bundle i liju.... r ca-store : ca-raw i lifeliad r src-ptr d 1 i listlica r src-ptr r parse-number : ca-store i lifelife r parse-result r cmd-addr i stlife.. r cmd-addr i liadlist d 1 r cmd-addr i liju.... r ca-loop : ca-done i liju.... r repl c `c` parses one address and calls it directly. : cmd-call i lifeliad r src-ptr d 1 i listlica r src-ptr r parse-number i lifeca.. r parse-result i liju.... r repl c `d` shows memory in a simple `address: value` form. It walks c a range one cell at a time and uses the number printer for c both the address and the contents. : cmd-display i lica.... r parse-range-args : cd-loop i lifeligt r cmd-count d 0 i licj.... r cd-body i liju.... r repl : cd-body i lifelica r cmd-addr r print-stack-number i lica.... r print-colon-space i lifefe.. r cmd-addr i lica.... r print-stack-number i lica.... r newline i life.... r cmd-addr i liadli.. d 1 r cmd-addr i stlife.. r cmd-count i lisuli.. d 1 r cmd-count i stliju.. r cd-loop c `u` walks memory like `d`, but treats each cell as a packed c instruction bundle. It prints the address, a decoded bundle, c and then the original numeric value. : cmd-unassemble i lica.... r parse-range-args : cu-loop i lifeligt r cmd-count d 0 i licj.... r cu-body i liju.... r repl : cu-body i lifelica r cmd-addr r print-stack-number i lica.... r print-colon-space i lifefe.. r cmd-addr i lica.... r print-bundle i liliio.. d 32 d 0 i lifefe.. r cmd-addr i lica.... r print-stack-number i lica.... r newline i life.... r cmd-addr i liadli.. d 1 r cmd-addr i stlife.. r cmd-count i lisuli.. d 1 r cmd-count i stliju.. r cu-loop c The block i/o commands are small counted loops around the ilo c block devices. Each block maps to 1024 cells in memory. : cmd-load i lilist.. d 2 r cmd-op i lica.... r parse-block-args i liju.... r cb-loop : cmd-write i lilist.. d 3 r cmd-op i lica.... r parse-block-args i liju.... r cb-loop : cb-loop i life.... r cmd-count i ligtlicj d 0 r cb-body i liju.... r repl : cb-body i lifelife r cmd-block r cmd-addr i lifeioli r cmd-op d 1 i lifeadli r cmd-block r cmd-block i stlife.. r cmd-count i lisuli.. d 1 r cmd-count i stlife.. r cmd-addr i liadli.. d 1024 r cmd-addr i stliju.. r cb-loop c terminal helpers c These are the low level output routines used by the command c handlers. Everything printed by the monitor eventually goes c through `emit`. : halt i liio.... d 6 : emit i liiore.. d 0 : newline i liliiore d 10 d 0 : print-colon-space i liliio.. d 58 d 0 i liliiore d 32 d 0 : print-stack-number i listliju r number-temp r print-number : print-bundle i listlica r dis-word r print-op-step i lica.... r print-op-step i lica.... r print-op-step i liju.... r print-op-step : print-op-step i lifelian r dis-word d 255 i listlife r op-byte r op-byte i liltlicj d 30 r pos-valid i liliio.. d 63 d 0 i liliio.. d 63 d 0 i liju.... r pos-next : pos-valid i life.... r op-byte i limulist d 2 r temp i lifeliad r temp r op-names i listlife r bundle-ptr r bundle-ptr i felica.. r emit i life.... r bundle-ptr i liadlist d 1 r bundle-ptr i lifefe.. r bundle-ptr i lica.... r emit : pos-next i life.... r dis-word i lisrlist d 8 r dis-word i re...... : print-z i list.... r z-ptr : pz-loop i lifefe.. r z-ptr i listlife r temp-char r temp-char i lieqlicj d 0 r pz-done i lifelica r temp-char r emit i lifeliad r z-ptr d 1 i listliju r z-ptr r pz-loop : pz-done i re...... : prompt i liliju.. r prompt-text r print-z ~~~ ~~~ c input and parsing c Input is kept line-oriented. `read-line` fills the buffer and c leaves a terminating zero so later code can treat it as a c simple string in memory. : read-line i lili.... r input-buffer r input-ptr i stlilist d 0 r read-len r eof-flag i lilist.. d 0 r eof-flag : rl-loop i liioduli d 1 d -1 i eqlicj.. r rl-eof i dulieqli d 10 r rl-finish i cj...... i dulieqli d 13 r rl-finish i cj...... i life.... r input-ptr i stlife.. r input-ptr i liadlist d 1 r input-ptr i lifeliad r read-len d 1 i listliju r read-len r rl-loop : rl-eof i lifelieq r read-len d 0 i licj.... r rl-set-eof i liju.... r rl-finish : rl-set-eof i lilistdr d -1 r eof-flag : rl-finish i lilifest d 0 r input-ptr i re...... : skip-spaces i lifefeli r src-ptr r temp-char i stlife.. r temp-char i lieqlicj d 32 r ss-advance i re...... : ss-advance i lifeliad r src-ptr d 1 i listliju r src-ptr r skip-spaces c `parse-number` handles optional leading `-` and then folds c digits into a signed integer. The result is left in the c shared `parse-result` variable. : parse-number i lica.... r skip-spaces i lilistli d 1 r num-sign d 0 i listlife r parse-result r src-ptr i listlife r temp-char r temp-char i lieqlicj d 45 r pn-neg i liju.... r pn-digits : pn-neg i lili.... d -1 r num-sign i stlife.. r src-ptr i liadlist d 1 r src-ptr : pn-digits i lifefeli r src-ptr r temp-char i stlife.. r temp-char i liltlicj d 48 r pn-done i life.... r temp-char i ligtlicj d 57 r pn-done i life.... r parse-result i limuli.. d 10 r parse-result i stlife.. r temp-char i lisuli.. d 48 r temp i stlife.. r parse-result i lifeadli r temp r parse-result i stlife.. r src-ptr i liadli.. d 1 r src-ptr i stliju.. r pn-digits : pn-done i life.... r num-sign i lieqlicj d -1 r pn-apply-sign i re...... : pn-apply-sign i lilifesu d 0 r parse-result i listre.. r parse-result c `parse-bundle` packs four two-character opcodes into the one c 32-bit cell format expected by ilo. It does this by parsing c one opcode at a time and scaling by powers of 256. : parse-bundle i lilist.. d 0 r parse-result i lilist.. d 1 r bundle-factor i lica.... r bundle-step i lica.... r bundle-step i lica.... r bundle-step i lica.... r bundle-step i re...... c Each step looks up one two-character opcode, adds it into the c accumulated result, then advances the source pointer by two c characters for the next opcode in the bundle. : bundle-step i lica.... r parse-op i lifelife r op-code r bundle-factor i mulist.. r temp i lifelife r parse-result r temp i adlist.. r parse-result i lifelimu r bundle-factor d 256 i list.... r bundle-factor i lifeliad r src-ptr d 2 i listre.. r src-ptr c `parse-op` is a small table scan over the opcode name string. c It compares the next two source characters against each entry c and leaves the matching opcode number in `op-code`. : parse-op i lilist.. d 0 r op-index i lilist.. d 0 r op-code : po-loop i life.... r op-index i limulist d 2 r temp i lifeliad r temp r op-names i list.... r entry-ptr i lifelife r src-ptr r entry-ptr i licplicj d 2 r po-found i life.... r op-index i lieqlicj d 29 r po-done i life.... r op-index i liadli.. d 1 r op-index i stliju.. r po-loop : po-found i lifelist r op-index r op-code : po-done i re...... : parse-block-args i life.... r src-ptr i liadlist d 1 r src-ptr i lica.... r parse-number i lifelist r parse-result r cmd-block i lica.... r parse-number i lifelist r parse-result r cmd-count i lica.... r parse-number i lifelist r parse-result r cmd-addr i re...... : parse-range-args i life.... r src-ptr i liadlist d 1 r src-ptr i lica.... r parse-number i lifelist r parse-result r cmd-addr i lica.... r parse-number i lifelist r parse-result r cmd-count i re...... c numeric output c The number printer is a basic repeated divide-by-10 routine. c Digits are collected into a temporary buffer and then emitted c in reverse order. : print-number i life.... r number-temp i lieqlicj d 0 r pn-zero i life.... r number-temp i liltlicj d 0 r pn-neg-out i liju.... r pn-prepare : pn-neg-out i liiolili d 45 d 0 r number-temp i fesulist r number-temp : pn-prepare i lilist.. d 0 r digit-count : pn-split i life.... r number-temp i lidi.... d 10 i swli.... r remainder i stli.... r number-temp i stlife.. r remainder i life.... r digit-count i liad.... r digits i stlife.. r digit-count i liadli.. d 1 r digit-count i stlife.. r number-temp i ligtli.. d 0 r pn-split i cj...... : pn-emit-loop i life.... r digit-count i lieqli.. d 0 r pn-done-emit i cj...... i life.... r digit-count i lisuli.. d 1 r digit-count i stlife.. r digit-count i liad.... r digits i fe...... i liadli.. d 48 d 0 i io...... i liju.... r pn-emit-loop : pn-zero i lili.... d 48 d 0 i io...... i re...... : pn-done-emit i re...... ~~~ ~~~ c o 62000 c A small block of memory is reserved here for the input c buffer, help text, opcode name table, and the state used by c the parser and command handlers. : digits * 16 : input-buffer * 128 : prompt-text z ilo> : op-names z ..lidudrswpupojucacccjreeqneltgtfestadsumudianorxoslsrcpcyio : src-ptr d 0 : input-ptr d 0 : read-len d 0 : eof-flag d 0 : parse-result d 0 : num-sign d 0 : temp d 0 : temp-char d 0 : bundle-factor d 0 : op-code d 0 : op-index d 0 : entry-ptr d 0 : dis-word d 0 : op-byte d 0 : bundle-ptr d 0 : number-temp d 0 : digit-count d 0 : remainder d 0 : cmd-addr d 0 : cmd-count d 0 : cmd-block d 0 : cmd-op d 0 : z-ptr d 0 ~~~