*
This commit is contained in:
parent
def41120c1
commit
3af4843598
17 changed files with 391 additions and 921 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,6 +1,5 @@
|
||||||
*.rom
|
|
||||||
*.rom.sym
|
*.rom.sym
|
||||||
|
/roms
|
||||||
/_opam
|
/_opam
|
||||||
/_build
|
/_build
|
||||||
/utils/uxnmin
|
|
||||||
/.envrc
|
/.envrc
|
||||||
|
|
|
||||||
43
README.md
43
README.md
|
|
@ -1,5 +1,42 @@
|
||||||
# Kestrel
|
# Kestrel
|
||||||
|
|
||||||
**Kestrel** is an implementation of the
|
**Kestrel** is:
|
||||||
[Uxn](https://wiki.xxiivv.com/site/uxn.html) virtual machine and
|
|
||||||
[Varvara](https://wiki.xxiivv.com/site/varvara.html) ecosystem in OCaml.
|
- 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.
|
||||||
|
|
|
||||||
1
boot/xh.rom
Normal file
1
boot/xh.rom
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
FAFAFXFZFwfaalFBFAAFFZFZXVGfoAAFXFZFFXXfoKgaam&/$AAAFXJgaam&/AFAAFXFFZZDYJF$/FXAKF|Bgaam/AAFXFZFXgBFAYXFEF|BGGoGAFAFXFZDYFEF|B/DAAFX_gBAGYgDAAFXFXAXZEEZXGPgBAAFXFFZZ_]GQFAPGA^GAQFAPgaamFPGAAFXFXFFXXW
|
||||||
25
bootstrap.sh
Normal file → Executable file
25
bootstrap.sh
Normal file → Executable file
|
|
@ -1,15 +1,14 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "======> Building seed Drifloon using uxnmin"
|
kestrel() {
|
||||||
make -sC utils clean
|
dune exec -- kestrel "${@}"
|
||||||
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
|
mkdir -p roms/
|
||||||
echo "======> Comparing ROMs (should be identical)"
|
kestrel boot/xh.rom < boot/drifloon.rom.txt > roms/drifloon.rom
|
||||||
cmp drifloon-ocaml.rom utils/drifloon.rom
|
kestrel roms/drifloon.rom < boot/drifblim.tal > roms/drifblim.rom
|
||||||
echo "======> Building Drifloon using drifloon-ocaml.rom"
|
|
||||||
dune exec -- ./exe/uxnemu.exe drifloon-ocaml.rom < utils/drifloon.tal > drifloon-boot.rom
|
# Run opctest
|
||||||
echo "======> Comparing ROMs (should be identical)"
|
kestrel roms/drifblim.rom boot/opctest.tal roms/opctest.rom
|
||||||
cmp drifloon-ocaml.rom drifloon-boot.rom
|
kestrel roms/opctest.rom
|
||||||
echo "======> Done!"
|
|
||||||
|
|
|
||||||
4
exe/dune
4
exe/dune
|
|
@ -2,3 +2,7 @@
|
||||||
(public_name kestrel)
|
(public_name kestrel)
|
||||||
(name main)
|
(name main)
|
||||||
(libraries kestrel kestrel_varvara unix))
|
(libraries kestrel kestrel_varvara unix))
|
||||||
|
|
||||||
|
(executable
|
||||||
|
(public_name uxnmin)
|
||||||
|
(name uxnmin))
|
||||||
|
|
|
||||||
332
exe/uxnmin.ml
Normal file
332
exe/uxnmin.ml
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
(* uxnmin.ml -- Javier B. Torres <lobo@quiltro.org>
|
||||||
|
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 ()
|
||||||
|
|
@ -10,6 +10,7 @@ pkgs.mkShell {
|
||||||
uxn
|
uxn
|
||||||
ocamlPackages.ocaml
|
ocamlPackages.ocaml
|
||||||
ocamlPackages.dune_3
|
ocamlPackages.dune_3
|
||||||
|
ocamlPackages.graphics
|
||||||
ocamlPackages.findlib
|
ocamlPackages.findlib
|
||||||
ocamlPackages.odoc
|
ocamlPackages.odoc
|
||||||
ocamlPackages.ocamlformat
|
ocamlPackages.ocamlformat
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
make -q uxnmin
|
|
||||||
make -q drifloon.rom
|
|
||||||
./uxnmin drifloon.rom < $1 > $2
|
|
||||||
|
|
@ -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/<set>
|
|
||||||
;assembly/on-console .Console/vector DEO2
|
|
||||||
BRK
|
|
||||||
|
|
||||||
@assembly/on-console ( -> )
|
|
||||||
[ LIT2 04 -Console/type ] DEI EQU ?{ .Console/read DEI token/<push-byte>
|
|
||||||
BRK }
|
|
||||||
/<resolve>
|
|
||||||
BRK
|
|
||||||
|
|
||||||
@rom/<put> ( byte addr* -- )
|
|
||||||
;&mem ADD2 STA
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@rom/<emit> ( -- )
|
|
||||||
;dict/assembled err/<print>
|
|
||||||
;dict/in err/<print>
|
|
||||||
;head/length LDA2 DUP2 #0100 SUB2 err/<pdec>
|
|
||||||
;dict/bytes err/<print>
|
|
||||||
( | write )
|
|
||||||
;rom/mem ADD2 ;rom/output
|
|
||||||
&>l
|
|
||||||
LDAk #18 DEO
|
|
||||||
INC2 GTH2k ?&>l
|
|
||||||
POP2 POP2 JMP2r
|
|
||||||
|
|
||||||
@runes/concat ( t* -- )
|
|
||||||
POP2 JMP2r
|
|
||||||
|
|
||||||
@err/<emit> ( 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/<resolve> ( -- )
|
|
||||||
( cap ) #0a token/<push-byte>
|
|
||||||
,&mode LDR2 ;comment/assemble NEQ2 ?{
|
|
||||||
( ! ) ;dict/open ;dict/trail ;dict/Comment err/<generic> }
|
|
||||||
,&mode LDR2 ;macros/assemble NEQ2 ?{
|
|
||||||
( ! ) ;dict/open ;dict/trail ;dict/Macro err/<generic> }
|
|
||||||
.System/state DEI ?{
|
|
||||||
refs/<resolve-all>
|
|
||||||
.System/state DEI ?{
|
|
||||||
[ LIT2 80 -System/state ] DEO !syms/<emit> } }
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@assembly/apply ( t* -- )
|
|
||||||
LDZk ?{ POP2 JMP2r }
|
|
||||||
[ LIT2 &mode =standard/assemble ] JMP2
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Standard )
|
|
||||||
|
|
||||||
@standard/<latch> ( -- )
|
|
||||||
;&assemble ;assembly/mode STA2
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@standard/assemble ( t* -- )
|
|
||||||
( hex ) str/is-hex ?rom/<write-rawhex>
|
|
||||||
( opc ) opcodes/is-opcode ?rom/<write-opcode>
|
|
||||||
LDZk runes/find INC2k ORA ?{
|
|
||||||
POP2
|
|
||||||
( mac ) DUP2 macros/find-name INC2k ORA ?macros/<write>
|
|
||||||
POP2
|
|
||||||
( imm ) !runes/litjsi }
|
|
||||||
INC2 LDA2 JMP2
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Comment )
|
|
||||||
|
|
||||||
@comment/<latch> ( 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/<latch> }
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Macros )
|
|
||||||
|
|
||||||
@macros/<latch> ( t* -- )
|
|
||||||
name/<validate>
|
|
||||||
/<push-word>
|
|
||||||
#00 /<push-byte>
|
|
||||||
;&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 /<push-byte> !standard/<latch> }
|
|
||||||
/<push-word>
|
|
||||||
#20 !/<push-byte>
|
|
||||||
|
|
||||||
@macros/<push-word> ( t* -- )
|
|
||||||
;/<push-byte> !hof/<each>
|
|
||||||
|
|
||||||
@macros/<push-byte> ( byte -- )
|
|
||||||
[ LIT2 &ptr =&mem ] INC2k
|
|
||||||
( | check overflow )
|
|
||||||
DUP2 ;&memend LTH2 ?{
|
|
||||||
( ! ) ;dict/exceeded ;dict/Macros err/<token> }
|
|
||||||
,&ptr STR2
|
|
||||||
STA
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@macros/find-name ( name* -- <addr>* )
|
|
||||||
STH2
|
|
||||||
,&ptr LDR2 ;&mem
|
|
||||||
&>lf
|
|
||||||
DUP2 STH2kr str/cmp ?{
|
|
||||||
str/cap str/cap GTH2k ?&>lf
|
|
||||||
POP2 #ffff }
|
|
||||||
NIP2 POP2r JMP2r
|
|
||||||
|
|
||||||
@macros/<write> ( t* macro* -- )
|
|
||||||
NIP2 token/<new>
|
|
||||||
str/cap ;token/<push-byte> !hof/<each>
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Token )
|
|
||||||
|
|
||||||
@token/<new> ( -- )
|
|
||||||
[ LIT2 -&buf _&ptr ] STR
|
|
||||||
[ LIT2 00 -&buf ] STZ
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@token/<push-byte> ( c -- )
|
|
||||||
DUP #20 GTH ?{
|
|
||||||
;&buf assembly/apply #0a NEQ ?{
|
|
||||||
[ LIT2 &line 0001 ] INC2 ,&line STR2 }
|
|
||||||
!/<new> }
|
|
||||||
[ LIT2 00 &ptr -&buf ] INCk
|
|
||||||
( | check overflow )
|
|
||||||
DUP .&cap LTH ?{
|
|
||||||
( ! ) ;dict/exceeded ;dict/Name err/<token> }
|
|
||||||
,&ptr STR
|
|
||||||
STZ2
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Scope )
|
|
||||||
|
|
||||||
@scope/<push-byte> ( c -- )
|
|
||||||
[ LIT2 00 &ptr -&buf ] INCk
|
|
||||||
( | check overflow )
|
|
||||||
DUP .&cap LTH ?{
|
|
||||||
( ! ) ;dict/exceeded ;dict/Symbol err/<token> }
|
|
||||||
,&ptr STR
|
|
||||||
STZ2
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@scope/<set> ( name* -- )
|
|
||||||
[ LIT2 -&buf _&ptr ] STR
|
|
||||||
&>w
|
|
||||||
LDAk [ LIT "/ ] EQU ?{
|
|
||||||
LDAk /<push-byte>
|
|
||||||
INC2 LDAk ?&>w }
|
|
||||||
POP2 ,&ptr LDR ,&anchor STR
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@scope/make-name ( name* -- scope/label* )
|
|
||||||
INC2 [ LIT2 &anchor $1 _&ptr ] STR
|
|
||||||
[ LIT "/ ] /<push-byte>
|
|
||||||
;&buf SWP2 ;/<push-byte> !hof/<each>
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Runes )
|
|
||||||
|
|
||||||
@runes/find ( char -- <addr>* )
|
|
||||||
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/<latch>
|
|
||||||
|
|
||||||
¯os ( t* -- )
|
|
||||||
/req-name !macros/<latch>
|
|
||||||
|
|
||||||
&padabs ( t* -- )
|
|
||||||
/req-name syms/find-addr !head/<set>
|
|
||||||
|
|
||||||
&padrel ( t* -- )
|
|
||||||
/req-name syms/find-addr !head/<set-rel>
|
|
||||||
|
|
||||||
&toplab ( t* -- )
|
|
||||||
/req-name DUP2 scope/<set> !syms/<new>
|
|
||||||
|
|
||||||
&sublab ( t* -- )
|
|
||||||
scope/make-name !syms/<new>
|
|
||||||
|
|
||||||
&litrel ( t* -- )
|
|
||||||
#80 rom/<write-byte> &rawrel /req-name refs/get-rel !rom/<write-byte>
|
|
||||||
|
|
||||||
&litzep ( t* -- )
|
|
||||||
#80 rom/<write-byte> &rawzep /req-name refs/get-abs !rom/<write-byte>
|
|
||||||
|
|
||||||
&litabs ( t* -- )
|
|
||||||
#a0 rom/<write-byte> &rawabs /req-name refs/get-abs2 !rom/<write-short>
|
|
||||||
|
|
||||||
&litjci ( t* -- )
|
|
||||||
/req-name #20 !rom/<write-call>
|
|
||||||
|
|
||||||
&litjmi ( t* -- )
|
|
||||||
/req-name #40 !rom/<write-call>
|
|
||||||
|
|
||||||
&litjsi ( t* -- )
|
|
||||||
#60 !rom/<write-call>
|
|
||||||
|
|
||||||
&lithex ( t* -- )
|
|
||||||
/req-name !rom/<write-lithex>
|
|
||||||
|
|
||||||
&rawstr ( t* -- )
|
|
||||||
/req-name !rom/<write-str>
|
|
||||||
|
|
||||||
@runes/req-name ( str* -- str1* )
|
|
||||||
INC2 LDAk #20 GTH ?{ ;dict/invalid ;dict/Name !err/<token> }
|
|
||||||
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/<new>
|
|
||||||
,&ptr STR2
|
|
||||||
JMP2r
|
|
||||||
&sym cebb
|
|
||||||
&id ".. $1
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Name )
|
|
||||||
|
|
||||||
@name/<validate> ( 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/<token>
|
|
||||||
|
|
||||||
&dup ( -- )
|
|
||||||
;dict/duplicate ;dict/Name !err/<token>
|
|
||||||
|
|
||||||
@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/<new> ( name* -- )
|
|
||||||
DUP2 /find-name INC2k ORA ?{
|
|
||||||
POP2 ;&ptr LDA2 refs/<record-scope>
|
|
||||||
.SymType/declared head/get !/<push-sym> }
|
|
||||||
( | name* sym* -- )
|
|
||||||
NIP2 DUP2 refs/<record-scope>
|
|
||||||
/is-declared ?{ head/get OVR2 STA2 !/<declare> }
|
|
||||||
POP2
|
|
||||||
( ! ) ;dict/duplicate ;dict/Symbol !err/<token>
|
|
||||||
|
|
||||||
@syms/<push-sym> ( name* type addr* -- )
|
|
||||||
( hb ) SWP /<push-byte>
|
|
||||||
( lb ) /<push-byte>
|
|
||||||
( type ) /<push-byte>
|
|
||||||
name/<validate>
|
|
||||||
;/<push-byte> hof/<each>
|
|
||||||
#00
|
|
||||||
( >> )
|
|
||||||
|
|
||||||
@syms/<push-byte> ( byte -- )
|
|
||||||
[ LIT2 &ptr =&mem ] INC2k
|
|
||||||
( | check overflow )
|
|
||||||
DUP2 ;&memend LTH2 ?{
|
|
||||||
( ! ) ;dict/exceeded ;dict/Symbols err/<token> }
|
|
||||||
,&ptr STR2
|
|
||||||
STA
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@syms/find-name ( name* -- <sym>* )
|
|
||||||
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* -- <addr>* )
|
|
||||||
DUP2 /find-name INC2k ORA ?{
|
|
||||||
( null* .. next* ) POP2 ,&ptr LDR2
|
|
||||||
( alloc ) SWP2 .SymType/used #ffff !/<push-sym> }
|
|
||||||
NIP2 JMP2r
|
|
||||||
|
|
||||||
@syms/find-addr ( name* -- <addr>* )
|
|
||||||
str/is-hex ?str/hex
|
|
||||||
name/unpack /find-name /is-defined ?{
|
|
||||||
( ! ) ;dict/invalid ;dict/Symbol err/<token> }
|
|
||||||
/use LDA2 JMP2r
|
|
||||||
|
|
||||||
@syms/<emit> ( -- )
|
|
||||||
;&ptr LDA2 ;&mem
|
|
||||||
&>ls
|
|
||||||
EQU2k ?{
|
|
||||||
/is-used ?{
|
|
||||||
LDA2k #0100 EQU2 ?{
|
|
||||||
DUP2 #0003 ADD2 LDAk [ LIT "A ] SUB #1a LTH ?{
|
|
||||||
;dict/unused err/<print>
|
|
||||||
DUP2 err/<print>
|
|
||||||
#0a err/<emit> }
|
|
||||||
POP2 } }
|
|
||||||
#0003 ADD2 str/cap !&>ls }
|
|
||||||
POP2 POP2 !rom/<emit>
|
|
||||||
|
|
||||||
@syms/byte-distance ( addr* -- addr* )
|
|
||||||
DUP2 #0080 ADD2 POP ?{ JMP2r }
|
|
||||||
( ! ) ;dict/too-far ;dict/Symbol !err/<token>
|
|
||||||
|
|
||||||
@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/<declare> ( 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* ) /<push-short>
|
|
||||||
( symbol* ) /<push-short>
|
|
||||||
( type-fn* ) [ LIT2 &type $2 ] /<push-short>
|
|
||||||
( scope* ) [ LIT2 &scope $2 ] /<push-short>
|
|
||||||
( line* ) ;token/line LDA2 /<push-short> }
|
|
||||||
( | mark as used )
|
|
||||||
syms/use LDA2 JMP2r
|
|
||||||
|
|
||||||
@refs/<push-short> ( value* -- )
|
|
||||||
SWP /<push-byte>
|
|
||||||
( >> )
|
|
||||||
|
|
||||||
@refs/<push-byte> ( byte -- )
|
|
||||||
[ LIT2 &ptr =&mem ] INC2k
|
|
||||||
( | check overflow )
|
|
||||||
DUP2 ;&memend LTH2 ?{
|
|
||||||
( ! ) ;dict/exceeded ;dict/References err/<token> }
|
|
||||||
,&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/<resolve-all> ( -- )
|
|
||||||
,&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/<set>
|
|
||||||
( ref* sym* ) INC2k INC2 LDA2
|
|
||||||
( ref* sym/addr* ) LDA2
|
|
||||||
( ref* sym/addr* ) INC2k ORA ?{
|
|
||||||
( ! ) ;dict/invalid !err/<resolution> }
|
|
||||||
( ref* sym/addr* ) JMP2r
|
|
||||||
|
|
||||||
@refs/handle-abs ( ref* -- )
|
|
||||||
/resolve-sym NIP2 NIP !rom/<write-byte>
|
|
||||||
|
|
||||||
@refs/handle-abs2 ( ref* -- )
|
|
||||||
/resolve-sym NIP2 !rom/<write-short>
|
|
||||||
|
|
||||||
@refs/handle-rel ( ref* -- )
|
|
||||||
/resolve-sym SWP2 LDA2 /get-distance /byte-distance NIP !rom/<write-byte>
|
|
||||||
|
|
||||||
@refs/handle-rel2 ( ref* -- )
|
|
||||||
/resolve-sym SWP2 LDA2 /get-distance !rom/<write-short>
|
|
||||||
|
|
||||||
@refs/byte-distance ( addr* -- addr* )
|
|
||||||
DUP2 #0080 ADD2 POP ?{ JMP2r }
|
|
||||||
( ! ) ;dict/too-far !err/<resolution>
|
|
||||||
|
|
||||||
@refs/<record-scope> ( sym* -- )
|
|
||||||
DUP2 #0003 ADD2 LDA2 #cebb NEQ2 ?{ POP2 JMP2r }
|
|
||||||
;refs/scope STA2
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Rom )
|
|
||||||
|
|
||||||
@rom/<write-str> ( str* -- )
|
|
||||||
;/<write-byte> !hof/<each>
|
|
||||||
|
|
||||||
@rom/<write-opcode> ( str* -- )
|
|
||||||
opcodes/parse !/<write-byte>
|
|
||||||
|
|
||||||
@rom/<write-lithex> ( str* -- )
|
|
||||||
str/len #02 NEQ #50 SFT #80 ORA /<write-byte>
|
|
||||||
( >> )
|
|
||||||
|
|
||||||
@rom/<write-rawhex> ( str* -- )
|
|
||||||
str/is-hex #00 EQU ?{
|
|
||||||
str/len DUP #02 NEQ ?{ POP str/hex NIP !/<write-byte> }
|
|
||||||
#04 NEQ ?{ str/hex !/<write-short> } }
|
|
||||||
POP2 ;dict/invalid ;dict/Number !err/<token>
|
|
||||||
|
|
||||||
@rom/<write-call> ( str* opc -- )
|
|
||||||
/<write-byte>
|
|
||||||
refs/get-rel2
|
|
||||||
( >> )
|
|
||||||
|
|
||||||
@rom/<write-short> ( short* -- )
|
|
||||||
SWP /<write-byte>
|
|
||||||
( >> )
|
|
||||||
|
|
||||||
@rom/<write-byte> ( byte -- )
|
|
||||||
head/get-inc
|
|
||||||
( | test zero-page )
|
|
||||||
OVR ?{
|
|
||||||
POP2 POP
|
|
||||||
( ! ) ;dict/zero-page ;dict/Writing !err/<token> }
|
|
||||||
!rom/<put>
|
|
||||||
|
|
||||||
@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/<set-rel> ( addr* -- )
|
|
||||||
/get ADD2
|
|
||||||
( >> )
|
|
||||||
|
|
||||||
@head/<set> ( addr* -- )
|
|
||||||
,&addr STR2
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
(
|
|
||||||
@|Stdlib )
|
|
||||||
|
|
||||||
@hof/<each> ( 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* <val> )
|
|
||||||
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/<token> ( adj* topic* -- )
|
|
||||||
.System/state DEI ?{
|
|
||||||
[ LIT2 01 -System/state ] DEO
|
|
||||||
/<print>
|
|
||||||
#20 /<emit>
|
|
||||||
/<print>
|
|
||||||
;dict/spacer /<print>
|
|
||||||
;token/buf /<print>
|
|
||||||
;token/line LDA2 ;scope/buf !/<print-location> }
|
|
||||||
POP2 POP2 JMP2r
|
|
||||||
|
|
||||||
@err/<resolution> ( adj* -- )
|
|
||||||
.System/state DEI ?{
|
|
||||||
[ LIT2 01 -System/state ] DEO
|
|
||||||
;dict/Reference /<print>
|
|
||||||
#20 /<emit>
|
|
||||||
/<print>
|
|
||||||
;dict/spacer /<print>
|
|
||||||
[ LIT2 &ref $2 ] INC2k INC2 LDA2 #0003 ADD2 /<print>
|
|
||||||
DUP2 #0008 ADD2 LDA2 SWP2 #0006 ADD2 LDA2 #0003 ADD2 !/<print-location> }
|
|
||||||
POP2 JMP2r
|
|
||||||
|
|
||||||
@err/<print-location> ( line* scope* -- )
|
|
||||||
;dict/in /<print>
|
|
||||||
/<print>
|
|
||||||
LIT ": /<emit>
|
|
||||||
/<pdec>
|
|
||||||
#0a /<emit>
|
|
||||||
JMP2r
|
|
||||||
|
|
||||||
@err/<generic> ( adj* keyword* topic* -- )
|
|
||||||
.System/state DEI ?{
|
|
||||||
[ LIT2 01 -System/state ] DEO
|
|
||||||
/<print>
|
|
||||||
#20 /<emit>
|
|
||||||
SWP2 /<print>
|
|
||||||
;dict/spacer /<print>
|
|
||||||
/<print>
|
|
||||||
#0a /<emit>
|
|
||||||
JMP2r }
|
|
||||||
POP2 POP2 POP2 JMP2r
|
|
||||||
|
|
||||||
@err/<print> ( str* -- )
|
|
||||||
;/<emit> !hof/<each>
|
|
||||||
|
|
||||||
@err/<pdec> ( short* -- )
|
|
||||||
[ LIT2r ff00 ]
|
|
||||||
&>read
|
|
||||||
#000a DIV2k STH2k MUL2 SUB2 STH2r INCr ORAk ?&>read
|
|
||||||
POP2
|
|
||||||
&>write
|
|
||||||
NIP #30 ADD /<emit>
|
|
||||||
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
|
|
||||||
135
utils/uxnmin.c
135
utils/uxnmin.c
|
|
@ -1,135 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue