diff --git a/.gitignore b/.gitignore index a116061..84e9ee7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ -*.rom *.rom.sym +/roms /_opam /_build -/utils/uxnmin /.envrc diff --git a/README.md b/README.md index cbbb12f..d4fc836 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,42 @@ # Kestrel -**Kestrel** is an implementation of the -[Uxn](https://wiki.xxiivv.com/site/uxn.html) virtual machine and -[Varvara](https://wiki.xxiivv.com/site/varvara.html) ecosystem in OCaml. +**Kestrel** is: + +- an implementation of the [Uxn](https://wiki.xxiivv.com/site/uxn.html) virtual + machine as a library in OCaml +- an implementation of the [Varvara](https://wiki.xxiivv.com/site/varvara.html) + ecosystem using said library, supporting the devices needed for command-line programs. + +## Building + +**Kestrel** requires OCaml 5.3 or upper, as it uses the newly introduced +effects syntax. + +There are no requirements aside from the OCaml standard library, so it can be +built simply using `dune`. + +```shell-session +$ dune build +``` + +It then can be run using `dune exec -- kestrel`. + +```shell-session +$ dune exec -- kestrel file.rom +``` + +The `uxnmin` program, which is similar to upstream's in functionality, can be +built without `dune`: + +```shell-session +$ ocamlopt exe/uxnmin.ml -o uxnmin +``` + +## Bootstrapping + +Since **Kestrel** supports the needed Varvara devices for running the Drifblim +assembler, it can be used to bootstrap the ecosystem. Bundled in the repository +are copies of the Drifloon hex dump, the source code for Drifblim, and `dzoe`'s +`xh.rom`. + +A bootstrap script is provided that builds the assembler and some test ROMs. diff --git a/utils/drifblim.tal b/boot/drifblim.tal similarity index 100% rename from utils/drifblim.tal rename to boot/drifblim.tal diff --git a/utils/drifloon.rom.txt b/boot/drifloon.rom.txt similarity index 100% rename from utils/drifloon.rom.txt rename to boot/drifloon.rom.txt diff --git a/utils/opctest.tal b/boot/opctest.tal similarity index 100% rename from utils/opctest.tal rename to boot/opctest.tal diff --git a/utils/console.tal b/boot/varvara.console.tal similarity index 100% rename from utils/console.tal rename to boot/varvara.console.tal diff --git a/utils/varvara.file.tal b/boot/varvara.file.tal similarity index 100% rename from utils/varvara.file.tal rename to boot/varvara.file.tal diff --git a/boot/xh.rom b/boot/xh.rom new file mode 100644 index 0000000..4c96eb9 --- /dev/null +++ b/boot/xh.rom @@ -0,0 +1 @@ +FAFAFXFZFwfaalFBFAAFFZFZXVGfoAAFXFZFFXXfoKgaam&/$AAAFXJgaam&/AFAAFXFFZZDYJF$/FXAKF|Bgaam/AAFXFZFXgBFAYXFEF|BGGoGAFAFXFZDYFEF|B/DAAFX_gBAGYgDAAFXFXAXZEEZXGPgBAAFXFFZZ_]GQFAPGA^GAQFAPgaamFPGAAFXFXFFXXW diff --git a/bootstrap.sh b/bootstrap.sh old mode 100644 new mode 100755 index f319346..eef2ae2 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,15 +1,14 @@ #!/bin/sh -set -e -echo "======> Building seed Drifloon using uxnmin" -make -sC utils clean -make -sC utils drifloon.rom -echo "======> Building Drifloon using utils/drifloon.rom (seed)" -dune exec -- ./exe/uxnemu.exe utils/drifloon.rom < utils/drifloon.tal > drifloon-ocaml.rom -echo "======> Comparing ROMs (should be identical)" -cmp drifloon-ocaml.rom utils/drifloon.rom -echo "======> Building Drifloon using drifloon-ocaml.rom" -dune exec -- ./exe/uxnemu.exe drifloon-ocaml.rom < utils/drifloon.tal > drifloon-boot.rom -echo "======> Comparing ROMs (should be identical)" -cmp drifloon-ocaml.rom drifloon-boot.rom -echo "======> Done!" +kestrel() { + dune exec -- kestrel "${@}" +} + +mkdir -p roms/ +kestrel boot/xh.rom < boot/drifloon.rom.txt > roms/drifloon.rom +kestrel roms/drifloon.rom < boot/drifblim.tal > roms/drifblim.rom + +# Run opctest +kestrel roms/drifblim.rom boot/opctest.tal roms/opctest.rom +kestrel roms/opctest.rom + diff --git a/exe/dune b/exe/dune index a6d5204..c3ffd9e 100644 --- a/exe/dune +++ b/exe/dune @@ -2,3 +2,7 @@ (public_name kestrel) (name main) (libraries kestrel kestrel_varvara unix)) + +(executable + (public_name uxnmin) + (name uxnmin)) diff --git a/exe/uxnmin.ml b/exe/uxnmin.ml new file mode 100644 index 0000000..44d8fc6 --- /dev/null +++ b/exe/uxnmin.ml @@ -0,0 +1,332 @@ +(* uxnmin.ml -- Javier B. Torres + This file is under the public domain. *) +(* ocamlopt uxnmin.ml -o uxnmin *) + +(* Wrapping read/write utilities *) +let uint8_to_int8 i = (i lsl (Sys.int_size - 8)) asr (Sys.int_size - 8) +let uint16_to_int16 i = (i lsl (Sys.int_size - 16)) asr (Sys.int_size - 16) + +let get_uint16_wrap ?(wrap = 0xff) (bytes : bytes) (position : int) : int = + let i0 = position land wrap in + let hi = Bytes.get_uint8 bytes i0 in + let lo = Bytes.get_uint8 bytes ((i0 + 1) land wrap) in + (hi lsl 8) lor lo + +let get_int16_wrap ?(wrap = 0xff) (bytes : bytes) (position : int) : int = + get_uint16_wrap ~wrap bytes position |> uint16_to_int16 + +let set_uint16_wrap ?(wrap = 0xff) (bytes : bytes) (position : int) + (value : int) : unit = + let i0 = position land wrap in + Bytes.set_uint8 bytes i0 ((value lsr 8) land 0xff); + Bytes.set_uint8 bytes ((i0 + 1) land wrap) (value land 0xff) + +(* Stack manipulation primitives *) +type stack = { data : bytes; mutable sp : int } +type mode = { short : bool; keep : bool; mutable temp : int } + +let peek { short; keep; temp } { data; sp } : int = + let amt = if short then 2 else 1 in + let sp = if keep then (temp - amt) land 0xff else (sp - amt) land 0xff in + if short then get_uint16_wrap data sp else Bytes.get_uint8 data sp +[@@inline] + +let pop m s = + let res = peek m s in + let amt = if m.short then 2 else 1 in + if m.keep then m.temp <- (m.temp - amt) land 0xff + else s.sp <- (s.sp - amt) land 0xff; + res +[@@inline] + +let push ({ short; keep; _ } as m) (s : stack) (v : int) = + if short then set_uint16_wrap s.data s.sp (v land 0xffff) + else Bytes.set_uint8 s.data s.sp (v land 0xff); + let amt = if m.short then 2 else 1 in + if keep then m.temp <- (m.temp + amt) land 0xff; + s.sp <- (s.sp + amt) land 0xff +[@@inline] + +let pushbyte m s v = + let m' = { m with short = false } in + push m' s v; + let { temp; _ } = m' in + m.temp <- temp +[@@inline] + +let popbyte m s = + let m' = { m with short = false } in + let r = pop m' s in + let { temp; _ } = m' in + m.temp <- temp; + r +[@@inline] + +let popshort m s = + let m' = { m with short = true } in + let r = pop m' s in + let { temp; _ } = m' in + m.temp <- temp; + r +[@@inline] + +let pop1 s = pop { short = false; keep = false; temp = 0 } s [@@inline] +let push1 s v = push { short = false; keep = false; temp = 0 } s v [@@inline] +let push2 s v = push { short = true; keep = false; temp = 0 } s v [@@inline] + +(* Machine state *) +let ram = Bytes.create 65536 +let dev = Bytes.create 256 +let wst = { data = Bytes.create 256; sp = 0 } +let rst = { data = Bytes.create 256; sp = 0 } +let console_vector = ref 0 + +(* Devices *) +let dei port = Bytes.get_uint8 dev port + +let deo port = + match port with + | 0x10 | 0x11 -> console_vector := get_uint16_wrap dev 0x10 + | 0x18 -> + print_char (Bytes.get dev 0x18); + Out_channel.flush stdout + | 0x19 -> + prerr_char (Bytes.get dev 0x19); + Out_channel.flush stderr + | _ -> () + +(* Dispatch loop *) +let dispatch (pc : int) = + let pc = ref pc in + + try + while true do + let op = Bytes.get_uint8 ram (!pc land 0xffff) in + pc := (!pc + 1) land 0xffff; + + let short = op land 0x20 <> 0 in + let keep = op land 0x80 <> 0 in + let return = op land 0x40 <> 0 in + let opcode = op land 0x1f in + + match op with + | 0x00 -> raise Exit + | 0x20 (* JCI *) -> + let cond = pop1 wst in + let addr = get_int16_wrap ~wrap:0xffff ram !pc in + if cond != 0 then pc := !pc + addr + 2 else pc := !pc + 2 + | 0x40 (* JMI *) -> + let addr = get_int16_wrap ~wrap:0xffff ram !pc in + pc := !pc + addr + 2 + | 0x60 (* JSI *) -> + let addr = get_int16_wrap ~wrap:0xffff ram !pc in + push2 rst (!pc + 2); + pc := !pc + addr + 2 + | 0x80 (* LIT *) -> + let lit = Bytes.get_uint8 ram !pc in + push1 wst lit; + pc := !pc + 1 + | 0xa0 (* LIT2 *) -> + let lit = get_uint16_wrap ~wrap:0xffff ram !pc in + push2 wst lit; + pc := !pc + 2 + | 0xc0 (* LITr *) -> + let lit = Bytes.get_uint8 ram !pc in + push1 rst lit; + pc := !pc + 1 + | 0xe0 (* LIT2r *) -> + let lit = get_uint16_wrap ~wrap:0xffff ram !pc in + push2 rst lit; + pc := !pc + 2 + | _ -> begin + let stk = if return then rst else wst in + let stk' = if return then wst else rst in + let mode = { short; keep; temp = stk.sp } in + match[@warning "-8"] opcode with + | 0x01 (* INC *) -> + let r = pop mode stk in + push mode stk (r + 1) + | 0x02 (* POP *) -> ignore (pop mode stk) + | 0x03 (* NIP *) -> + let b = pop mode stk in + ignore (pop mode stk); + push mode stk b + | 0x04 (* SWP *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk b; + push mode stk a + | 0x05 (* ROT *) -> + let c = pop mode stk in + let b = pop mode stk in + let a = pop mode stk in + push mode stk b; + push mode stk c; + push mode stk a + | 0x06 (* DUP *) -> + let a = pop mode stk in + push mode stk a; + push mode stk a + | 0x07 (* OVR *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk a; + push mode stk b; + push mode stk a + | 0x08 (* EQU *) -> + let b = pop mode stk in + let a = pop mode stk in + pushbyte mode stk (if a = b then 1 else 0) + | 0x09 (* NEQ *) -> + let b = pop mode stk in + let a = pop mode stk in + pushbyte mode stk (if a != b then 1 else 0) + | 0x0a (* GTH *) -> + let b = pop mode stk in + let a = pop mode stk in + pushbyte mode stk (if a > b then 1 else 0) + | 0x0b (* LTH *) -> + let b = pop mode stk in + let a = pop mode stk in + pushbyte mode stk (if a < b then 1 else 0) + | 0x0c (* JMP *) -> + let addr = pop mode stk in + if short then pc := addr else pc := !pc + uint8_to_int8 addr + | 0x0d (* JCN *) -> + let addr = pop mode stk in + let cond = popbyte mode stk in + if cond != 0 then + if short then pc := addr else pc := !pc + uint8_to_int8 addr + | 0x0e (* JSR *) -> + push2 rst !pc; + let addr = pop mode stk in + if short then pc := addr else pc := !pc + uint8_to_int8 addr + | 0x0f (* STH *) -> + let a = pop mode stk in + push mode stk' a + | 0x10 (* LDZ *) -> + let addr = popbyte mode stk in + push mode stk + (if short then get_uint16_wrap ram addr + else Bytes.get_uint8 ram addr) + | 0x11 (* STZ *) -> + let addr = popbyte mode stk in + let v = pop mode stk in + if short then set_uint16_wrap ram addr v + else Bytes.set_uint8 ram addr v + | 0x12 (* LDR *) -> + let addr = !pc + uint8_to_int8 (popbyte mode stk) in + push mode stk + (if short then get_uint16_wrap ~wrap:0xffff ram addr + else Bytes.get_uint8 ram addr) + | 0x13 (* STR *) -> + let addr = !pc + uint8_to_int8 (popbyte mode stk) in + let v = pop mode stk in + if short then set_uint16_wrap ~wrap:0xffff ram addr v + else Bytes.set_uint8 ram addr v + | 0x14 (* LDA *) -> + let addr = popshort mode stk in + push mode stk + (if short then get_uint16_wrap ~wrap:0xffff ram addr + else Bytes.get_uint8 ram addr) + | 0x15 (* STA *) -> + let addr = popshort mode stk in + let v = pop mode stk in + if short then set_uint16_wrap ~wrap:0xffff ram addr v + else Bytes.set_uint8 ram addr v + | 0x16 (* DEI *) -> + let port = popbyte mode stk in + push mode stk (dei port) + | 0x17 (* DEO *) -> + let port = popbyte mode stk in + let value = pop mode stk in + if short then set_uint16_wrap dev port value + else Bytes.set_uint8 dev port value; + deo port + | 0x18 (* ADD *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (a + b) + | 0x19 (* SUB *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (a - b) + | 0x1a (* MUL *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (a * b) + | 0x1b (* DIV *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (if b = 0 then 0 else a / b) + | 0x1c (* AND *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (a land b) + | 0x1d (* ORA *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (a lor b) + | 0x1e (* EOR *) -> + let b = pop mode stk in + let a = pop mode stk in + push mode stk (a lxor b) + | 0x1f (* SFT *) -> + let sft = popbyte mode stk in + let value = pop mode stk in + push mode stk ((value lsr (sft land 0xf)) lsl sft lsr 4) + end + done + with Exit -> () + +let main () = + (* Initialize machine *) + Bytes.unsafe_fill ram 0 65536 '\x00'; + Bytes.unsafe_fill dev 0 256 '\x00'; + Bytes.unsafe_fill wst.data 0 256 '\x00'; + Bytes.unsafe_fill rst.data 0 256 '\x00'; + + if Array.length Sys.argv < 2 then ( + Printf.eprintf "usage: uxnmin file.rom ...\n"; + exit 1); + let code = + In_channel.with_open_bin Sys.argv.(1) (fun i -> In_channel.input_all i) + in + Bytes.blit_string code 0 ram 0x100 (String.length code); + + In_channel.set_binary_mode stdin true; + Out_channel.set_binary_mode stdout true; + + let has_args = Array.length Sys.argv > 2 in + Bytes.set_uint8 dev 0x17 (if has_args then 1 else 0); + + dispatch 0x100; + + if !console_vector <> 0 then begin + let console_input ch ty = + Bytes.set_uint8 dev 0x12 ch; + Bytes.set_uint8 dev 0x17 ty; + if Bytes.get_uint8 dev 0x0f = 0 then dispatch !console_vector + in + if has_args then begin + for i = 2 to Array.length Sys.argv - 1 do + let arg = Sys.argv.(i) in + String.iter + (fun c -> + if Bytes.get_uint8 dev 0x0f = 0 then console_input (Char.code c) 2) + arg; + if Bytes.get_uint8 dev 0x0f = 0 then + console_input 0 (if i = Array.length Sys.argv - 1 then 4 else 3) + done + end; + try + while Bytes.get_uint8 dev 0x0f = 0 do + match In_channel.input_byte stdin with + | None -> raise Exit + | Some c -> console_input c 1 + done + with Exit -> console_input 0 4 + end; + exit (Bytes.get_uint8 dev 0x0f land 0x7f) + +let _ = main () diff --git a/shell.nix b/shell.nix index 6b2bc7e..b93d2e6 100644 --- a/shell.nix +++ b/shell.nix @@ -10,6 +10,7 @@ pkgs.mkShell { uxn ocamlPackages.ocaml ocamlPackages.dune_3 + ocamlPackages.graphics ocamlPackages.findlib ocamlPackages.odoc ocamlPackages.ocamlformat diff --git a/utils/LICENSE b/utils/LICENSE deleted file mode 100644 index d0716ec..0000000 --- a/utils/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2020-2025 Devine Lu Linvega - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/utils/Makefile b/utils/Makefile deleted file mode 100644 index 6882f8e..0000000 --- a/utils/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -.PHONY: all clean -.SUFFIXES: .tal .rom - -all: uxnmin drifloon.rom opctest.rom varvara.file.rom -clean: - rm -f uxnmin *.rom - -uxnmin: uxnmin.c -drifloon.rom: uxnmin - xxd -r -p drifloon.rom.txt drifloon-seed.rom - ./uxnmin drifloon-seed.rom < drifloon.tal > drifloon.rom - cmp drifloon.rom drifloon-seed.rom - rm drifloon-seed.rom - -.tal.rom: - sh assemble.sh $< $@ - -#opctest.rom: uxnmin drifloon.rom opctest.tal -# ./uxnmin drifloon.rom < opctest.tal > opctest.rom diff --git a/utils/assemble.sh b/utils/assemble.sh deleted file mode 100644 index 7bd01d7..0000000 --- a/utils/assemble.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -make -q uxnmin -make -q drifloon.rom -./uxnmin drifloon.rom < $1 > $2 diff --git a/utils/drifloon.tal b/utils/drifloon.tal deleted file mode 100644 index bfef597..0000000 --- a/utils/drifloon.tal +++ /dev/null @@ -1,732 +0,0 @@ -( cat input.tal | uxncli drifloon.rom > output.rom ) - -|00 @System/vector $2 &expansion $2 &wst $1 &rst $1 &metadata $2 &r $2 &g $2 &b $2 &debug $1 &state $1 -|10 @Console/vector $2 &read $1 &pad $4 &type $1 &write $1 &error $1 - -|000 - - @scope/buf $3f &cap $1 - @token/buf $3f &cap $1 - -|100 - -@on-reset ( -> ) - ;meta #06 DEO2 - ;dict/reset scope/ - ;assembly/on-console .Console/vector DEO2 - BRK - -@assembly/on-console ( -> ) - [ LIT2 04 -Console/type ] DEI EQU ?{ .Console/read DEI token/ - BRK } - / - BRK - -@rom/ ( byte addr* -- ) - ;&mem ADD2 STA - JMP2r - -@rom/ ( -- ) - ;dict/assembled err/ - ;dict/in err/ - ;head/length LDA2 DUP2 #0100 SUB2 err/ - ;dict/bytes err/ - ( | write ) - ;rom/mem ADD2 ;rom/output - &>l - LDAk #18 DEO - INC2 GTH2k ?&>l - POP2 POP2 JMP2r - -@runes/concat ( t* -- ) - POP2 JMP2r - -@err/ ( c -- ) - #19 DEO JMP2r - -@meta $1 - ( name ) "Drifloon 0a - ( desc ) "Uxntal 20 "Assembler 0a - ( auth ) "By 20 "Devine 20 "Lu 20 "Linvega 0a - ( date ) "8 20 "Nov 20 "2025 $2 - - - -( Core ) - -@assembly/ ( -- ) - ( cap ) #0a token/ - ,&mode LDR2 ;comment/assemble NEQ2 ?{ - ( ! ) ;dict/open ;dict/trail ;dict/Comment err/ } - ,&mode LDR2 ;macros/assemble NEQ2 ?{ - ( ! ) ;dict/open ;dict/trail ;dict/Macro err/ } - .System/state DEI ?{ - refs/ - .System/state DEI ?{ - [ LIT2 80 -System/state ] DEO !syms/ } } - JMP2r - -@assembly/apply ( t* -- ) - LDZk ?{ POP2 JMP2r } - [ LIT2 &mode =standard/assemble ] JMP2 - -( -@|Standard ) - -@standard/ ( -- ) - ;&assemble ;assembly/mode STA2 - JMP2r - -@standard/assemble ( t* -- ) - ( hex ) str/is-hex ?rom/ - ( opc ) opcodes/is-opcode ?rom/ - LDZk runes/find INC2k ORA ?{ - POP2 - ( mac ) DUP2 macros/find-name INC2k ORA ?macros/ - POP2 - ( imm ) !runes/litjsi } - INC2 LDA2 JMP2 - -( -@|Comment ) - -@comment/ ( t* -- ) - POP2 ;&assemble ;assembly/mode STA2 - [ LIT2 01 _&depth ] STR - JMP2r - -@comment/assemble ( t* -- ) - LDA2 DUP2 [ LITr &depth $1 ] - ( a ) LIT2 "( $1 EQU2 [ STH ADDr ] - ( b ) LIT2 ") $1 EQU2 [ STH SUBr ] - ( . ) STHkr LITr _&depth STRr - ?{ !standard/ } - JMP2r - -( -@|Macros ) - -@macros/ ( t* -- ) - name/ - / - #00 / - ;&walk ;assembly/mode STA2 - JMP2r - - &walk ( t* -- ) - LDA2 [ LIT2 "{ $1 ] NEQ2 ?{ - ;&assemble ;assembly/mode STA2 - [ LIT2 01 _&depth ] STR } - JMP2r - -@macros/assemble ( t* -- ) - LDA2k DUP2 [ LITr &depth $1 ] - ( a ) LIT "{ EQU SWP LIT "{ EQU ORA [ STH ADDr ] - ( b ) LIT2 "} $1 EQU2 [ STH SUBr ] - ( . ) STHkr LITr _&depth STRr - ?{ POP2 #00 / !standard/ } - / - #20 !/ - -@macros/ ( t* -- ) - ;/ !hof/ - -@macros/ ( byte -- ) - [ LIT2 &ptr =&mem ] INC2k - ( | check overflow ) - DUP2 ;&memend LTH2 ?{ - ( ! ) ;dict/exceeded ;dict/Macros err/ } - ,&ptr STR2 - STA - JMP2r - -@macros/find-name ( name* -- * ) - STH2 - ,&ptr LDR2 ;&mem - &>lf - DUP2 STH2kr str/cmp ?{ - str/cap str/cap GTH2k ?&>lf - POP2 #ffff } - NIP2 POP2r JMP2r - -@macros/ ( t* macro* -- ) - NIP2 token/ - str/cap ;token/ !hof/ - -( -@|Token ) - -@token/ ( -- ) - [ LIT2 -&buf _&ptr ] STR - [ LIT2 00 -&buf ] STZ - JMP2r - -@token/ ( c -- ) - DUP #20 GTH ?{ - ;&buf assembly/apply #0a NEQ ?{ - [ LIT2 &line 0001 ] INC2 ,&line STR2 } - !/ } - [ LIT2 00 &ptr -&buf ] INCk - ( | check overflow ) - DUP .&cap LTH ?{ - ( ! ) ;dict/exceeded ;dict/Name err/ } - ,&ptr STR - STZ2 - JMP2r - -( -@|Scope ) - -@scope/ ( c -- ) - [ LIT2 00 &ptr -&buf ] INCk - ( | check overflow ) - DUP .&cap LTH ?{ - ( ! ) ;dict/exceeded ;dict/Symbol err/ } - ,&ptr STR - STZ2 - JMP2r - -@scope/ ( name* -- ) - [ LIT2 -&buf _&ptr ] STR - &>w - LDAk [ LIT "/ ] EQU ?{ - LDAk / - INC2 LDAk ?&>w } - POP2 ,&ptr LDR ,&anchor STR - JMP2r - -@scope/make-name ( name* -- scope/label* ) - INC2 [ LIT2 &anchor $1 _&ptr ] STR - [ LIT "/ ] / - ;&buf SWP2 ;/ !hof/ - -( -@|Runes ) - -@runes/find ( char -- * ) - STH - ;&lut - &>w - LDAk STHkr EQU ?{ - #0003 ADD2 LDAk ?&>w - POP2 #ffff } - POPr JMP2r - -@runes/ignore ( t* -- ) - POP2 JMP2r - - &lambda ( t* -- ) - POP2 !lambda/pop - - &coment ( t* -- ) - !comment/ - - ¯os ( t* -- ) - /req-name !macros/ - - &padabs ( t* -- ) - /req-name syms/find-addr !head/ - - &padrel ( t* -- ) - /req-name syms/find-addr !head/ - - &toplab ( t* -- ) - /req-name DUP2 scope/ !syms/ - - &sublab ( t* -- ) - scope/make-name !syms/ - - &litrel ( t* -- ) - #80 rom/ &rawrel /req-name refs/get-rel !rom/ - - &litzep ( t* -- ) - #80 rom/ &rawzep /req-name refs/get-abs !rom/ - - &litabs ( t* -- ) - #a0 rom/ &rawabs /req-name refs/get-abs2 !rom/ - - &litjci ( t* -- ) - /req-name #20 !rom/ - - &litjmi ( t* -- ) - /req-name #40 !rom/ - - &litjsi ( t* -- ) - #60 !rom/ - - &lithex ( t* -- ) - /req-name !rom/ - - &rawstr ( t* -- ) - /req-name !rom/ - -@runes/req-name ( str* -- str1* ) - INC2 LDAk #20 GTH ?{ ;dict/invalid ;dict/Name !err/ } - JMP2r - -@runes/lut [ - "| =&padabs "$ =&padrel - "@ =&toplab "& =&sublab - ", =&litrel "_ =&rawrel - ". =&litzep "- =&rawzep - "; =&litabs "= =&rawabs - "! =&litjmi "? =&litjci - "# =&lithex "" =&rawstr - "} =&lambda "~ =&concat - "( =&coment ") =&ignore - "[ =&ignore "] =&ignore "% =¯os ] $1 - -( -@|Opcodes ) - -@opcodes/is-opcode ( str* -- str* bool ) - DUP2 /parse #00 NEQ STH - DUP2 ;&brk str/cmp STHr ORA JMP2r - -@opcodes/parse ( str* -- byte ) - [ LIT2r 1f00 ] ;&lut - &>w1 - SWP2k #0003 SWP2 mem/cmp ?{ - INCr #0003 ADD2 LDAk ?&>w1 - POP2 POP2 POP2r #00 JMP2r } - POP2 - ( mask ) ANDr - ( litk ) LDA2k [ LIT2 "LI ] EQU2 #70 SFT [ STH ORAr ] - ( move ) #0003 ADD2 - &>w2 - LDAk #21 LTH ?{ - ( | parse modes ) - LDAk [ LIT "2 ] NEQ ?{ LITr 20 !&r } - LDAk [ LIT "r ] NEQ ?{ LITr 40 !&r } - LDAk [ LIT "k ] NEQ ?{ LITr 80 !&r } - POP2 POPr #00 JMP2r - &r ORAr INC2 !&>w2 } - POP2 STHr JMP2r - -@opcodes/lut [ - "LIT "INC "POP "NIP "SWP "ROT "DUP "OVR - "EQU "NEQ "GTH "LTH "JMP "JCN "JSR "STH - "LDZ "STZ "LDR "STR "LDA "STA "DEI "DEO - "ADD "SUB "MUL "DIV "AND "ORA "EOR "SFT ] - &brk "BRK $1 - -( -@|Lambda ) - -@lambda/make-name ( token* -- name* ) - POP2 [ LIT &count $1 ] INCk ,&count STR - DUP [ LIT2 &ptr =&mem ] INC2k ,&ptr STR2 - STA - ( >> ) - -@lambda/name ( id -- str* ) - DUP #04 SFT hexc SWP hexc ,&id STR2 - ;&sym JMP2r - -@lambda/pop ( -- ) - ,&ptr LDR2 #0001 SUB2 LDAk /name syms/ - ,&ptr STR2 - JMP2r - &sym cebb - &id ".. $1 - -( -@|Name ) - -@name/ ( name* -- name* ) - ( not hex ) str/is-hex ?&fail - ( not lambda ) LDAk LIT "{ EQU ?&fail - ( not runic ) LDAk runes/find INC2 ORA ?&fail - ( dup macros ) DUP2 macros/find-name INC2 ORA ?&dup - ( dup symbol ) DUP2 syms/find-name INC2 ORA ?&dup - ( not opcode ) opcodes/is-opcode [ JMP JMP2r ] - &fail ( -- ) - ;dict/invalid ;dict/Name !err/ - - &dup ( -- ) - ;dict/duplicate ;dict/Name !err/ - -@name/unpack ( name* -- name* ) - LDAk [ LIT "{ ] EQU ?lambda/make-name - LDAk [ LIT "/ ] EQU ?scope/make-name - LDAk [ LIT "& ] EQU ?scope/make-name - JMP2r - -( -@|Syms ) - -@syms/ ( name* -- ) - DUP2 /find-name INC2k ORA ?{ - POP2 ;&ptr LDA2 refs/ - .SymType/declared head/get !/ } - ( | name* sym* -- ) - NIP2 DUP2 refs/ - /is-declared ?{ head/get OVR2 STA2 !/ } - POP2 - ( ! ) ;dict/duplicate ;dict/Symbol !err/ - -@syms/ ( name* type addr* -- ) - ( hb ) SWP / - ( lb ) / - ( type ) / - name/ - ;/ hof/ - #00 - ( >> ) - -@syms/ ( byte -- ) - [ LIT2 &ptr =&mem ] INC2k - ( | check overflow ) - DUP2 ;&memend LTH2 ?{ - ( ! ) ;dict/exceeded ;dict/Symbols err/ } - ,&ptr STR2 - STA - JMP2r - -@syms/find-name ( name* -- * ) - STH2 - ,&ptr LDR2 ;&mem - &>lfn - DUP2 #0003 ADD2 STH2kr str/cmp ?{ - #0003 ADD2 str/cap GTH2k ?&>lfn - POP2 #ffff } - NIP2 POP2r JMP2r - -@syms/find-alloc ( name* -- * ) - DUP2 /find-name INC2k ORA ?{ - ( null* .. next* ) POP2 ,&ptr LDR2 - ( alloc ) SWP2 .SymType/used #ffff !/ } - NIP2 JMP2r - -@syms/find-addr ( name* -- * ) - str/is-hex ?str/hex - name/unpack /find-name /is-defined ?{ - ( ! ) ;dict/invalid ;dict/Symbol err/ } - /use LDA2 JMP2r - -@syms/ ( -- ) - ;&ptr LDA2 ;&mem - &>ls - EQU2k ?{ - /is-used ?{ - LDA2k #0100 EQU2 ?{ - DUP2 #0003 ADD2 LDAk [ LIT "A ] SUB #1a LTH ?{ - ;dict/unused err/ - DUP2 err/ - #0a err/ } - POP2 } } - #0003 ADD2 str/cap !&>ls } - POP2 POP2 !rom/ - -@syms/byte-distance ( addr* -- addr* ) - DUP2 #0080 ADD2 POP ?{ JMP2r } - ( ! ) ;dict/too-far ;dict/Symbol !err/ - -@syms/is-defined ( sym* -- sym* t ) - INC2k ORA ?{ #00 JMP2r } - ( >> ) - -@syms/is-declared ( sym* -- sym* t ) - INC2k INC2 LDA .SymType/declared AND JMP2r - -@syms/is-used ( sym* -- sym* t ) - INC2k INC2 LDA .SymType/used AND JMP2r - -@syms/use ( sym* -- sym* ) - INC2k INC2 STH2k LDA .SymType/used ORA STH2r STA - JMP2r - -@syms/ ( sym* -- ) - INC2 INC2 STH2k LDA .SymType/declared ORA STH2r STA - JMP2r - -( -@|References ) - -@refs/get-type ( token* type* -- addr* ) - ,&type STR2 - name/unpack syms/find-alloc syms/is-declared ?{ - DUP2 head/get - ( addr* ) / - ( symbol* ) / - ( type-fn* ) [ LIT2 &type $2 ] / - ( scope* ) [ LIT2 &scope $2 ] / - ( line* ) ;token/line LDA2 / } - ( | mark as used ) - syms/use LDA2 JMP2r - -@refs/ ( value* -- ) - SWP / - ( >> ) - -@refs/ ( byte -- ) - [ LIT2 &ptr =&mem ] INC2k - ( | check overflow ) - DUP2 ;&memend LTH2 ?{ - ( ! ) ;dict/exceeded ;dict/References err/ } - ,&ptr STR2 - STA - JMP2r - -@refs/get-abs ( label* -- addr ) - ;&handle-abs /get-type NIP JMP2r - -@refs/get-abs2 ( label* -- addr* ) - ;&handle-abs2 !/get-type - -@refs/get-rel ( label* -- distance ) - ;&handle-rel /get-type INC2k ORA ?{ - ( undefined ) POP2 #00 JMP2r } - head/get /get-distance syms/byte-distance NIP JMP2r - -@refs/get-rel2 ( label* -- distance* ) - ;&handle-rel2 /get-type head/get - ( >> ) - -@refs/get-distance ( a* b* -- distance* ) - INC2 INC2 SUB2 JMP2r - -@refs/ ( -- ) - ,&ptr LDR2 ;&mem - &>l - EQU2k ?{ - DUP2 ;err/ref STA2 - DUP2k #0004 ADD2 LDA2 JSR2 - ( ) #000a ADD2 !&>l } - POP2 POP2 JMP2r - -@refs/resolve-sym ( ref* -- ref* sym/addr* ) - LDA2k head/ - ( ref* sym* ) INC2k INC2 LDA2 - ( ref* sym/addr* ) LDA2 - ( ref* sym/addr* ) INC2k ORA ?{ - ( ! ) ;dict/invalid !err/ } - ( ref* sym/addr* ) JMP2r - -@refs/handle-abs ( ref* -- ) - /resolve-sym NIP2 NIP !rom/ - -@refs/handle-abs2 ( ref* -- ) - /resolve-sym NIP2 !rom/ - -@refs/handle-rel ( ref* -- ) - /resolve-sym SWP2 LDA2 /get-distance /byte-distance NIP !rom/ - -@refs/handle-rel2 ( ref* -- ) - /resolve-sym SWP2 LDA2 /get-distance !rom/ - -@refs/byte-distance ( addr* -- addr* ) - DUP2 #0080 ADD2 POP ?{ JMP2r } - ( ! ) ;dict/too-far !err/ - -@refs/ ( sym* -- ) - DUP2 #0003 ADD2 LDA2 #cebb NEQ2 ?{ POP2 JMP2r } - ;refs/scope STA2 - JMP2r - -( -@|Rom ) - -@rom/ ( str* -- ) - ;/ !hof/ - -@rom/ ( str* -- ) - opcodes/parse !/ - -@rom/ ( str* -- ) - str/len #02 NEQ #50 SFT #80 ORA / - ( >> ) - -@rom/ ( str* -- ) - str/is-hex #00 EQU ?{ - str/len DUP #02 NEQ ?{ POP str/hex NIP !/ } - #04 NEQ ?{ str/hex !/ } } - POP2 ;dict/invalid ;dict/Number !err/ - -@rom/ ( str* opc -- ) - / - refs/get-rel2 - ( >> ) - -@rom/ ( short* -- ) - SWP / - ( >> ) - -@rom/ ( byte -- ) - head/get-inc - ( | test zero-page ) - OVR ?{ - POP2 POP - ( ! ) ;dict/zero-page ;dict/Writing !err/ } - !rom/ - -@head/get-inc ( -- addr* ) - [ LIT2 &addr 0100 ] INC2k ,&addr STR2 - INC2k [ LIT2 &length 0100 ] LTH2 ?{ INC2k ,&length STR2 } - JMP2r - -@head/get ( -- addr* ) - ,&addr LDR2 JMP2r - -@head/ ( addr* -- ) - /get ADD2 - ( >> ) - -@head/ ( addr* -- ) - ,&addr STR2 - JMP2r - -( -@|Stdlib ) - -@hof/ ( data* byte-fn* -- ) - STH2 - &>w - LDAk DUP ?{ POP POP2 POP2r JMP2r } - STH2kr JSR2 INC2 !&>w - -@hexc ( hex -- char ) - #0f AND #0a LTHk ?{ - SUB [ LIT "a ] ADD JMP2r } - POP [ LIT "0 ] ADD JMP2r - -@chex ( addr* -- addr* ) - LDAk - ( dec ) [ LIT "0 ] SUB DUP #09 GTH [ JMP JMP2r ] - ( hex ) #27 SUB DUP #0a SUB #05 GTH [ JMP JMP2r ] - ( nil ) POP #ff JMP2r - -@str/hex ( str* -- value* ) - [ LIT2r 0000 ] - &>wh - [ LITr 40 ] SFT2r chex [ LITr 00 ] STH - ADD2r INC2 LDAk ?&>wh - POP2 STH2r JMP2r - -@str/len ( str* -- str* length ) - DUP2k /cap SWP2 INC2 SUB2 NIP JMP2r - -@str/is-hex ( str* -- str* f ) - DUP2 - &>wih - chex INC ?{ LDA #00 EQU JMP2r } - INC2 !&>wih - -@str/cap ( str* -- end* ) - LDAk ?{ INC2 JMP2r } - INC2 !/cap - -@str/cmp ( a* b* -- bool ) - DUP2k /cap SWP2 SUB2 SWP2 - ( >> ) - -@mem/cmp ( a* length* b* -- t ) - STH2 - OVR2 ADD2 SWP2 - &>l - EQU2k ?{ - LDAk LDAkr STHr NEQ ?{ INC2 INC2r !&>l } } - POP2r EQU2 JMP2r - -( -@|Error ) - -@err/ ( adj* topic* -- ) - .System/state DEI ?{ - [ LIT2 01 -System/state ] DEO - / - #20 / - / - ;dict/spacer / - ;token/buf / - ;token/line LDA2 ;scope/buf !/ } - POP2 POP2 JMP2r - -@err/ ( adj* -- ) - .System/state DEI ?{ - [ LIT2 01 -System/state ] DEO - ;dict/Reference / - #20 / - / - ;dict/spacer / - [ LIT2 &ref $2 ] INC2k INC2 LDA2 #0003 ADD2 / - DUP2 #0008 ADD2 LDA2 SWP2 #0006 ADD2 LDA2 #0003 ADD2 !/ } - POP2 JMP2r - -@err/ ( line* scope* -- ) - ;dict/in / - / - LIT ": / - / - #0a / - JMP2r - -@err/ ( adj* keyword* topic* -- ) - .System/state DEI ?{ - [ LIT2 01 -System/state ] DEO - / - #20 / - SWP2 / - ;dict/spacer / - / - #0a / - JMP2r } - POP2 POP2 POP2 JMP2r - -@err/ ( str* -- ) - ;/ !hof/ - -@err/ ( short* -- ) - [ LIT2r ff00 ] - &>read - #000a DIV2k STH2k MUL2 SUB2 STH2r INCr ORAk ?&>read - POP2 - &>write - NIP #30 ADD / - OVRr ADDr STHkr ?&>write - POP2r JMP2r - -@dict/assembled "Assembled $1 &in 20 "in 20 $1 &bytes 20 "bytes. 0a $1 - &unused "-- 20 "Unused - &spacer ": 20 $1 - &References "References $1 - &Reference "Reference $1 - &Symbols "Symbols $1 - &Symbol "Symbol $1 - &Macros "Macros $1 - &Macro "Macro $1 - &Name "Name $1 - &Number "Number $1 - &Comment "Comment $1 - &Writing "Writing $1 - &exceeded "exceeded $1 - &invalid "invalid $1 - &duplicate "duplicate $1 - &too-far "too 20 "far $1 - &zero-page "zero-page $1 - &open "open $1 - &trail ".. $1 - &reset "RESET $1 - -( -@|Buffers ) - -@lambda/mem $100 - -@macros/mem ( name\0, value\0 ) - $1000 &memend - -@refs/mem ( addr*, symbol*, type-fn*, scope*, line* ) - $3000 &memend - -@syms/mem ( addr*, SymType, name\0 ) - $4800 &memend - -@rom/mem ( zeropage ) - $100 - &output -( -@|Enums ) - - -|00 @SymType/empty $1 &used $1 &declared diff --git a/utils/uxnmin.c b/utils/uxnmin.c deleted file mode 100644 index d29a854..0000000 --- a/utils/uxnmin.c +++ /dev/null @@ -1,135 +0,0 @@ -#include - -static unsigned int console_vector; -static unsigned char ram[0x10000], dev[0x100], ptr[2], stk[2][0x100]; - -static unsigned char -emu_dei(const unsigned char port) -{ - return dev[port]; -} - -static void -emu_deo(const unsigned char port, const unsigned char value) -{ - dev[port] = value; - switch(port) { - case 0x11: console_vector = dev[0x10] << 8 | value; return; - case 0x18: fputc(value, stdout); return; - case 0x19: fputc(value, stderr); return; - } -} - -#define REM ptr[_r] -= 1 + _2; -#define DEC(m) stk[m][--ptr[m]] -#define INC(m) stk[m][ptr[m]++] -#define IMM(r) { r = ram[pc++] << 8, r |= ram[pc++]; } -#define MOV(x) { if(_2) pc = x; else pc += (signed char)x; } -#define PO1(o) o = DEC(_r); -#define PO2(o) { PO1(o) o |= DEC(_r) << 8; } -#define POx(o) if(_2) PO2(o) else PO1(o) -#define GOT(o) if(_2) PO1(o[1]) PO1(o[0]) -#define DEO(o,r) emu_deo(o, r[0]); if(_2) emu_deo(o + 1, r[1]); -#define POK(o,r,m) ram[o] = r[0]; if(_2) ram[(o + 1) & m] = r[1]; -#define RP1(i) INC(!_r) = i; -#define PU1(i) INC(_r) = i; -#define PUx(i) if(_2) { c = (i); PU1(c >> 8) PU1(c) } else PU1(i) -#define PUT(i) PU1(i[0]) if(_2) PU1(i[1]) -#define DEI(i,r) r[0] = emu_dei(i); if(_2) r[1] = emu_dei(i + 1); PUT(r) -#define PEK(i,r,m) r[0] = ram[i]; if(_2) r[1] = ram[(i + 1) & m]; PUT(r) - -#define NEXT if(--cycles) goto step; else return 0; -#define OPC(opc, A, B) {\ - case 0x00|opc: {const int _2=0,_r=0;A B} NEXT\ - case 0x20|opc: {const int _2=1,_r=0;A B} NEXT\ - case 0x40|opc: {const int _2=0,_r=1;A B} NEXT\ - case 0x60|opc: {const int _2=1,_r=1;A B} NEXT\ - case 0x80|opc: {const int _2=0,_r=0;int k=ptr[0];A ptr[0]=k;B} NEXT\ - case 0xa0|opc: {const int _2=1,_r=0;int k=ptr[0];A ptr[0]=k;B} NEXT\ - case 0xc0|opc: {const int _2=0,_r=1;int k=ptr[1];A ptr[1]=k;B} NEXT\ - case 0xe0|opc: {const int _2=1,_r=1;int k=ptr[1];A ptr[1]=k;B} NEXT } - -static unsigned int -uxn_eval(unsigned short pc) -{ - unsigned int a, b, c, x[2], y[2], z[2], cycles = 0x80000000; -step: - switch(ram[pc++]) { - /* BRK */ case 0x00: return 1; - /* JCI */ case 0x20: if(DEC(0)) { IMM(c) pc += c; } else pc += 2; NEXT - /* JMI */ case 0x40: IMM(c) pc += c; NEXT - /* JSI */ case 0x60: IMM(c) INC(1) = pc >> 8, INC(1) = pc, pc += c; NEXT - /* LI2 */ case 0xa0: INC(0) = ram[pc++]; /* fall-through */ - /* LIT */ case 0x80: INC(0) = ram[pc++]; NEXT - /* L2r */ case 0xe0: INC(1) = ram[pc++]; /* fall-through */ - /* LIr */ case 0xc0: INC(1) = ram[pc++]; NEXT - /* INC */ OPC(0x01,POx(a),PUx(a + 1)) - /* POP */ OPC(0x02,REM,{}) - /* NIP */ OPC(0x03,GOT(x) REM,PUT(x)) - /* SWP */ OPC(0x04,GOT(x) GOT(y),PUT(x) PUT(y)) - /* ROT */ OPC(0x05,GOT(x) GOT(y) GOT(z),PUT(y) PUT(x) PUT(z)) - /* DUP */ OPC(0x06,GOT(x),PUT(x) PUT(x)) - /* OVR */ OPC(0x07,GOT(x) GOT(y),PUT(y) PUT(x) PUT(y)) - /* EQU */ OPC(0x08,POx(a) POx(b),PU1(b == a)) - /* NEQ */ OPC(0x09,POx(a) POx(b),PU1(b != a)) - /* GTH */ OPC(0x0a,POx(a) POx(b),PU1(b > a)) - /* LTH */ OPC(0x0b,POx(a) POx(b),PU1(b < a)) - /* JMP */ OPC(0x0c,POx(a),MOV(a)) - /* JCN */ OPC(0x0d,POx(a) PO1(b),if(b) MOV(a)) - /* JSR */ OPC(0x0e,POx(a),RP1(pc >> 8) RP1(pc) MOV(a)) - /* STH */ OPC(0x0f,GOT(x),RP1(x[0]) if(_2) RP1(x[1])) - /* LDZ */ OPC(0x10,PO1(a),PEK(a, x, 0xff)) - /* STZ */ OPC(0x11,PO1(a) GOT(y),POK(a, y, 0xff)) - /* LDR */ OPC(0x12,PO1(a),PEK(pc + (signed char)a, x, 0xffff)) - /* STR */ OPC(0x13,PO1(a) GOT(y),POK(pc + (signed char)a, y, 0xffff)) - /* LDA */ OPC(0x14,PO2(a),PEK(a, x, 0xffff)) - /* STA */ OPC(0x15,PO2(a) GOT(y),POK(a, y, 0xffff)) - /* DEI */ OPC(0x16,PO1(a),DEI(a, x)) - /* DEO */ OPC(0x17,PO1(a) GOT(y),DEO(a, y)) - /* ADD */ OPC(0x18,POx(a) POx(b),PUx(b + a)) - /* SUB */ OPC(0x19,POx(a) POx(b),PUx(b - a)) - /* MUL */ OPC(0x1a,POx(a) POx(b),PUx(b * a)) - /* DIV */ OPC(0x1b,POx(a) POx(b),PUx(a ? b / a : 0)) - /* AND */ OPC(0x1c,POx(a) POx(b),PUx(b & a)) - /* ORA */ OPC(0x1d,POx(a) POx(b),PUx(b | a)) - /* EOR */ OPC(0x1e,POx(a) POx(b),PUx(b ^ a)) - /* SFT */ OPC(0x1f,PO1(a) POx(b),PUx(b >> (a & 0xf) << (a >> 4))) - } - return 0; -} - -static void -console_input(int c, unsigned int type) -{ - dev[0x12] = c, dev[0x17] = type; - if(console_vector && !dev[0x0f]) - uxn_eval(console_vector); -} - -int -main(int argc, char **argv) -{ - FILE *f; - if(argc < 2) - return fprintf(stdout, "usage: %s file.rom [args..]\n", argv[0]); - else if(!(f = fopen(argv[1], "rb"))) - return fprintf(stderr, "%s: %s not found.\n", argv[0], argv[1]); - fread(&ram[0x100], 0xff00, 1, f), fclose(f); - dev[0x17] = argc > 2; - if(uxn_eval(0x100) && console_vector) { - int i = 2; - for(; i < argc; i++) { - char c, *p = argv[i]; - while(!dev[0x0f] && (c = *p++)) - console_input(c, 2); - console_input(0, 3 + (i == argc - 1)); - } - while(!dev[0x0f]) { - char c = fgetc(stdin); - if(feof(stdin)) break; - console_input(c, 1); - } - console_input(0, 4); - } - return dev[0x0f] & 0x7f; -}