commit 82674a14844dc805ae8c75464a4f5d69ebf3e022
parent 1db61376a234334c630f40810bb813cdf8825c49
Author: neauoire <aliceffekt@gmail.com>
Date: Mon, 22 Mar 2021 19:04:31 -0700
Created a debugger
Diffstat:
16 files changed, 515 insertions(+), 692 deletions(-)
diff --git a/README.md b/README.md
@@ -1,25 +1,15 @@
# Uxn
-A [stack-based VM](https://wiki.xxiivv.com/site/uxn.html), written in ANSI C.
+A [stack-based VM](https://wiki.xxiivv.com/site/uxn.html), written in ANSI C.
-## Setup
+## Build
-If you wish to build your own emulator, you can create a new instance of Uxn like:
+To build the Uxn emulator, you must have [SDL2](https://wiki.libsdl.org/).
-```
-#include "uxn.h"
-
-Uxn u;
-
-if(!bootuxn(&u))
- return error("Boot", "Failed");
-if(!loaduxn(&u, argv[1]))
- return error("Load", "Failed");
-if(!init())
- return error("Init", "Failed");
-
-evaluxn(u, u->vreset); /* Once on start */
-evaluxn(u, u->vframe); /* Each frame
+```sh
+./build.sh
+ --debug # Add debug flags to compiler
+ --cli # Run rom without graphics
```
## Uxambly
diff --git a/build.sh b/build.sh
@@ -1,28 +1,42 @@
#!/bin/bash
-# Create bin folder
-mkdir -p bin
+echo "Formatting.."
+clang-format -i src/assembler.c
+clang-format -i src/uxn.h
+clang-format -i src/uxn.c
+clang-format -i src/emulator.c
+clang-format -i src/debugger.c
-# Assembler
-clang-format -i assembler.c
+echo "Cleaning.."
rm -f ./bin/assembler
+rm -f ./bin/emulator
+rm -f ./bin/debugger
rm -f ./bin/boot.rom
-cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined assembler.c -o bin/assembler
-# Core
-clang-format -i uxn.h
-clang-format -i uxn.c
+echo "Building.."
+mkdir -p bin
+if [ "${1}" = '--debug' ];
+then
+ echo "[debug]"
+ cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/assembler.c -o bin/assembler
+ cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/emulator.c -L/usr/local/lib -lSDL2 -o bin/emulator
+ cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/debugger.c -o bin/debugger
+else
+ cc src/assembler.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -o bin/assembler
+ cc src/uxn.c src/debugger.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -o bin/debugger
+ cc src/uxn.c src/emulator.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -L/usr/local/lib -lSDL2 -o bin/emulator
+fi
-# Emulator
-clang-format -i emulator.c
-rm -f ./bin/emulator
-cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined uxn.c emulator.c -L/usr/local/lib -lSDL2 -o bin/emulator
-# cc uxn.c emulator.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -L/usr/local/lib -lSDL2 -o bin/emulator
+echo "Assembling.."
+./bin/assembler projects/examples/dev.console.usm bin/boot.rom
-# Emulator(CLI)
-clang-format -i emulator-cli.c
-rm -f ./bin/emulator-cli
+echo "Running.."
+if [ "${2}" = '--cli' ];
+then
+ echo "[cli]"
+ ./bin/debugger bin/boot.rom
+else
+ ./bin/emulator bin/boot.rom
+fi
-# run
-./bin/assembler projects/software/nasu.usm bin/boot.rom
-./bin/emulator bin/boot.rom
+echo "Done."
+\ No newline at end of file
diff --git a/emulator-cli.c b/emulator-cli.c
@@ -1,236 +0,0 @@
-#include <stdio.h>
-
-/*
-Copyright (c) 2021 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.
-*/
-
-#include "uxn.h"
-
-#pragma mark - Core
-
-int
-error(char *msg, const char *err)
-{
- printf("Error %s: %s\n", msg, err);
- return 0;
-}
-
-#pragma mark - Devices
-
-Uint8
-console_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
-{
- Uint8 *m = u->ram.dat;
- switch(b0) {
- case 0x08: printf("%c", b1); break;
- case 0x09: printf("0x%02x\n", b1); break;
- case 0x0b: printf("0x%04x\n", (m[ptr + 0x0a] << 8) + b1); break;
- }
- fflush(stdout);
- (void)m;
- (void)ptr;
- (void)b0;
- return b1;
-}
-
-Uint8
-file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
-{
- Uint8 *m = u->ram.dat;
- char *name = (char *)&m[(m[ptr + 8] << 8) + m[ptr + 8 + 1]];
- Uint16 length = (m[ptr + 8 + 2] << 8) + m[ptr + 8 + 3];
- if(b0 == 0x0d) {
- Uint16 addr = (m[ptr + 8 + 4] << 8) + b1;
- FILE *f = fopen(name, "r");
- if(f && fread(&m[addr], length, 1, f)) {
- fclose(f);
- printf("Loaded %d bytes, at %04x from %s\n", length, addr, name);
- }
- } else if(b0 == 0x0f) {
- Uint16 addr = (m[ptr + 8 + 6] << 8) + b1;
- FILE *f = fopen(name, "w");
- if(fwrite(&m[addr], length, 1, f)) {
- fclose(f);
- printf("Saved %d bytes, at %04x from %s\n", length, addr, name);
- }
- }
- return b1;
-}
-
-static void
-stack_diff(Stack *old, Stack *new, char *title)
-{
- size_t i;
- printf("%6s: ", title);
- for(i = 0;; ++i) {
- if(i < old->ptr) {
- if(i < new->ptr) {
- if(old->dat[i] == new->dat[i]) {
- printf(" \033[0m%02x", new->dat[i]);
- } else {
- printf(" \033[0;31m%02x\033[33;1m%02x", old->dat[i], new->dat[i]);
- }
- } else { /* only in old stack */
- printf(" \033[0;31m%02x", old->dat[i]);
- }
- } else {
- if(i < new->ptr) { /* only in new stack */
- printf(" \033[33;1m%02x", new->dat[i]);
- } else { /* in neither stack, end of loop */
- break;
- }
- }
- }
- printf("\033[0m\n");
-}
-
-static void
-memory_diff(Uint8 *old, Uint8 *new, size_t start, size_t end)
-{
- size_t i, j;
- for(i = start; i < end; i += 0x10) {
- int changes = 0;
- for(j = i; j < i + 0x10; ++j) {
- if(old[j] != new[j]) {
- changes = 1;
- break;
- }
- }
- if(!changes) continue;
- printf("0x%04lx: ", i);
- for(j = i; j < i + 0x10; ++j) {
- printf("\033[%sm%02x", old[j] == new[j] ? "0" : "33;1", new[j]);
- if(j % 2) putchar(' ');
- }
- printf(" ");
- for(j = i; j < i + 0x10; ++j) {
- printf("\033[%sm%c", old[j] == new[j] ? "0" : "33;1", (new[j] < ' ' || new[j] > '~') ? '.' : new[j]);
- }
- printf("\033[0m\n");
- }
-}
-
-Uint8
-debug_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
-{
- size_t i;
- (void)ptr;
- switch(b0) {
- case 0x08: /* stack */
- printf("pc %04x working stack:", u->ram.ptr);
- for(i = 0; i < u->wst.ptr; ++i) {
- printf(" %02x", u->wst.dat[i]);
- }
- printf(", return stack: ");
- for(i = 0; i < u->rst.ptr; ++i) {
- printf(" %02x", u->rst.dat[i]);
- }
- printf("\n");
- if(b1 && b1 != u->wst.ptr) {
- printf("length %d failed to match %d!\n", b1, u->wst.ptr);
- exit(1);
- }
- break;
- case 0x09: /* snapshot */
- if(u->snapshot != NULL) {
- if(!(b1 & 0x01)) {
- stack_diff(&u->snapshot->wst, &u->wst, "work");
- }
- if(!(b1 & 0x02)) {
- stack_diff(&u->snapshot->rst, &u->rst, "return");
- }
- if(!(b1 & 0x04)) {
- memory_diff(u->snapshot->ram.dat, u->ram.dat, 0, PAGE_DEVICE);
- memory_diff(u->snapshot->ram.dat, u->ram.dat, PAGE_DEVICE + 0x0100, 0x10000);
- }
- }
- {
- int want_snapshot = !(b1 & 0x80);
- if(want_snapshot) {
- if(u->snapshot == NULL) {
- u->snapshot = malloc(sizeof(*u));
- }
- for(i = 0; i < sizeof(*u); ++i) {
- ((char *)u->snapshot)[i] = ((char *)u)[i];
- }
- }
- printf("pc 0x%04x snapshot%s taken\n", u->counter, want_snapshot ? "" : " not");
- }
- break;
- case 0x0a: /* exit */
- printf("Exited after 0x%04x cycles.\n", u->counter);
- exit(b1);
- break;
- case 0x0f: /* test mode */
- u->test_mode = b1;
- printf("Test mode is now 0x%02x: ", u->test_mode);
- if(b1 & 0x01) {
- printf("BRK resets stacks to zero length");
- } else {
- printf("all test mode features disabled");
- }
- printf("\n");
- break;
- }
- fflush(stdout);
- return b1;
-}
-
-Uint8
-ppnil(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
-{
- (void)u;
- (void)ptr;
- (void)b0;
- return b1;
-}
-
-#pragma mark - Generics
-
-int
-start(Uxn *u)
-{
- evaluxn(u, u->vreset);
- evaluxn(u, u->vframe);
-}
-
-int
-main(int argc, char **argv)
-{
- Uxn u;
-
- if(argc < 2)
- return error("Input", "Missing");
- if(!bootuxn(&u))
- return error("Boot", "Failed");
- if(!loaduxn(&u, argv[1]))
- return error("Load", "Failed");
- if(!init())
- return error("Init", "Failed");
-
- portuxn(&u, "console", console_poke);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "file", file_poke);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "empty", ppnil);
- portuxn(&u, "debug", debug_poke);
- portuxn(&u, "system", system_poke);
-
- return 0;
-}
diff --git a/projects/tests/cond.usm b/projects/tests/cond.usm
@@ -1,18 +0,0 @@
-( tests/cond )
-
-|0100 @RESET
-
- #1234 POP2
- #00 DUP2?
-
-BRK
-
-
-
-|c000 @FRAME
-|d000 @ERROR
-
-|FF00 ;Console { pad 8 char 1 byte 1 short 2 }
-
-|FFF0 .RESET .FRAME .ERROR ( vectors )
-|FFF8 [ 13fd 1ef3 1bf2 ] ( palette )
-\ No newline at end of file
diff --git a/projects/tests/draw.usm b/projects/tests/draw.usm
@@ -1,71 +0,0 @@
-( tests/draw )
-
-%RTN { JMP2r }
-%RTN? { JMP2r? }
-%ABS { DUP #07 SHR #ff SWP MUL? }
-%ABS2 { DUP2 #000f SFT2 #ffff SWP2 SWP POP MUL2? }
-
-;cursor { x 2 y 2 }
-;a { x 2 y 2 }
-;b { x 2 y 2 }
-;s { x 2 y 2 }
-;d { x 2 y 2 }
-;err { short 2 }
-;err2 { short 2 }
-;i { byte 1 }
-;color { byte 1 }
-
-|0100 @RESET
-
- #0020 #0020 #0070 #0080 #01 ,draw-line JSR2
- #0020 #0080 #0070 #0030 #02 ,draw-line JSR2
- #00a0 #0020 #0050 #00b0 #03 ,draw-line JSR2
- #00b0 #0090 #0030 #0010 #01 ,draw-line JSR2
-
-BRK
-
-@draw-line ( x1 y1 x2 y2 )
-
- =color
- =b.y =b.x =a.y =a.x
- ~b.x ~a.x SUB2 ABS2 =d.x
- ~b.y ~a.y SUB2 ABS2 #0000 SWP2 SUB2 =d.y
- #ffff #00 ~a.x ~b.x LTS2 #0002 MUL2 ADD2 =s.x
- #ffff #00 ~a.y ~b.y LTS2 #0002 MUL2 ADD2 =s.y
- ~d.x ~d.y ADD2 =err
-
- $loop
-
- ~a.x =Screen.x ~a.y =Screen.y ~color =Screen.color
- ,$end ~a.x ~b.x EQU2 ~a.y ~b.y EQU2 #0101 EQU2 JMP2?
- ~err #0002 MUL2 =err2
-
- ,$skipy ~err2 ~d.y LTS2 JMP2?
- ~err ~d.y ADD2 =err
- ~a.x ~s.x ADD2 =a.x
- $skipy
-
- ,$skipx ~err2 ~d.x GTS2 JMP2?
- ~err ~d.x ADD2 =err
- ~a.y ~s.y ADD2 =a.y
- $skipx
-
- ,$loop JMP2
-
- $end
-
-RTN
-
-|c000 @FRAME
-|d000 @ERROR
-
-|FF00 ;Console { pad 8 char 1 byte 1 short 2 }
-|FF10 ;Screen { width 2 height 2 pad 4 x 2 y 2 color 1 }
-|FF20 ;Sprite { pad 8 x 2 y 2 addr 2 color 1 }
-|FF30 ;Controller { buttons 1 }
-|FF40 ;Keys { key 1 }
-|FF50 ;Mouse { x 2 y 2 state 1 chord 1 change 1 }
-|FF60 ;File { pad 8 name 2 length 2 load 2 save 2 }
-
-|FFF0 .RESET .FRAME .ERROR ( vectors )
-|FFF8 [ 13fd 1ef3 1bf2 ] ( palette )
-\ No newline at end of file
diff --git a/projects/tests/generic.usm b/projects/tests/generic.usm
@@ -0,0 +1,9 @@
+|0100 ;Console { pad 8 char 1 byte 1 short 2 }
+|01F0 .RESET .FRAME .ERROR ( vectors )
+
+@RESET
+
+BRK
+
+@FRAME BRK
+@ERROR BRK
diff --git a/projects/tests/jump.usm b/projects/tests/jump.usm
@@ -1,59 +0,0 @@
-( tests/jump )
-
-|0100 @RESET
-
- ,test1 JSR2
- ,test2 JSR2
-
-BRK
-
-@test1
-
- ( should print 11, 22, 33, 44 )
-
- #11 =Console.byte
- #03 JMP BRK BRK BRK
-
- ( skip foward with id )
-
- #22 =Console.byte
- ^jump JMP BRK BRK BRK @jump
-
- ( skip patterns )
-
- #33 =Console.byte
-
- ,skip1 #12 #34 LTH JMP2?
- #ff =Console.byte
- @skip1
-
- #12 #34 LTH ^skip2 #04 SUB MUL JMP
- #ff =Console.byte
- @skip2
-
- #44 =Console.byte
-
-RTN
-
-@test2
-
- ,end JMP2
-
- ( should print aa, bb, cc, dd )
-
- @label1 #aa =Console.byte ^label3 JMP
- @label2 #cc =Console.byte ^label4 JMP
- @label3 #bb =Console.byte ^label2 JMP
- @label4 #dd =Console.byte BRK
-
- @end
-
-RTN
-
-|c000 @FRAME
-|d000 @ERROR
-
-|FF00 ;Console { pad 8 char 1 byte 1 short 2 }
-
-|FFF0 .RESET .FRAME .ERROR ( vectors )
-|FFF8 [ 13fd 1ef3 1bf2 ] ( palette )
-\ No newline at end of file
diff --git a/projects/tests/peek.usm b/projects/tests/peek.usm
@@ -1,21 +0,0 @@
-( tests/cond )
-
-;a { byte 1 }
-;b { byte1 1 byte2 1 }
-;c { byte1 1 byte2 1 byte3 1 }
-
-|0100 @RESET
-
- #12 #00 POK
-
- #00 PEK
-
-BRK
-
-|c000 @FRAME
-|d000 @ERROR
-
-|FF00 ;Console { pad 8 char 1 byte 1 short 2 }
-
-|FFF0 .RESET .FRAME .ERROR ( vectors )
-|FFF8 [ 13fd 1ef3 1bf2 ] ( palette )
-\ No newline at end of file
diff --git a/run.sh b/run.sh
@@ -1,13 +0,0 @@
-#!/bin/bash
-set -e
-EMULATOR=./bin/emulator
-if [ "${1}" = '--no-sdl' ]; then
- EMULATOR=./bin/emulator-nosdl
- shift
-fi
-if [ -z "${1}" ]; then
- printf 'usage: %s [--no-sdl] USM_FILE\n' "${0}" >&2
- exit 2
-fi
-./bin/assembler "${1}" bin/boot.rom
-"${EMULATOR}" bin/boot.rom
diff --git a/assembler.c b/src/assembler.c
diff --git a/src/backup-debugdev.c b/src/backup-debugdev.c
@@ -0,0 +1,119 @@
+
+static void
+stack_diff(Stack *old, Stack *new, char *title)
+{
+ size_t i;
+ printf("%6s: ", title);
+ for(i = 0;; ++i) {
+ if(i < old->ptr) {
+ if(i < new->ptr) {
+ if(old->dat[i] == new->dat[i]) {
+ printf(" \033[0m%02x", new->dat[i]);
+ } else {
+ printf(" \033[0;31m%02x\033[33;1m%02x", old->dat[i], new->dat[i]);
+ }
+ } else { /* only in old stack */
+ printf(" \033[0;31m%02x", old->dat[i]);
+ }
+ } else {
+ if(i < new->ptr) { /* only in new stack */
+ printf(" \033[33;1m%02x", new->dat[i]);
+ } else { /* in neither stack, end of loop */
+ break;
+ }
+ }
+ }
+ printf("\033[0m\n");
+}
+
+static void
+memory_diff(Uint8 *old, Uint8 *new, size_t start, size_t end)
+{
+ size_t i, j;
+ for(i = start; i < end; i += 0x10) {
+ int changes = 0;
+ for(j = i; j < i + 0x10; ++j) {
+ if(old[j] != new[j]) {
+ changes = 1;
+ break;
+ }
+ }
+ if(!changes) continue;
+ printf("0x%04lx: ", i);
+ for(j = i; j < i + 0x10; ++j) {
+ printf("\033[%sm%02x", old[j] == new[j] ? "0" : "33;1", new[j]);
+ if(j % 2) putchar(' ');
+ }
+ printf(" ");
+ for(j = i; j < i + 0x10; ++j) {
+ printf("\033[%sm%c", old[j] == new[j] ? "0" : "33;1", (new[j] < ' ' || new[j] > '~') ? '.' : new[j]);
+ }
+ printf("\033[0m\n");
+ }
+}
+
+Uint8
+debug_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
+{
+ size_t i;
+ (void)ptr;
+ switch(b0) {
+ case 0x08: /* stack */
+ printf("pc %04x working stack:", u->ram.ptr);
+ for(i = 0; i < u->wst.ptr; ++i) {
+ printf(" %02x", u->wst.dat[i]);
+ }
+ printf(", return stack: ");
+ for(i = 0; i < u->rst.ptr; ++i) {
+ printf(" %02x", u->rst.dat[i]);
+ }
+ printf("\n");
+ if(b1 && b1 != u->wst.ptr) {
+ printf("length %d failed to match %d!\n", b1, u->wst.ptr);
+ exit(1);
+ }
+ break;
+ case 0x09: /* snapshot */
+ if(u->snapshot != NULL) {
+ if(!(b1 & 0x01)) {
+ stack_diff(&u->snapshot->wst, &u->wst, "work");
+ }
+ if(!(b1 & 0x02)) {
+ stack_diff(&u->snapshot->rst, &u->rst, "return");
+ }
+ if(!(b1 & 0x04)) {
+ memory_diff(u->snapshot->ram.dat, u->ram.dat, 0, PAGE_DEVICE);
+ memory_diff(u->snapshot->ram.dat, u->ram.dat, PAGE_DEVICE + 0x0100, 0x10000);
+ }
+ }
+ {
+ int want_snapshot = !(b1 & 0x80);
+ if(want_snapshot) {
+ if(u->snapshot == NULL) {
+ u->snapshot = malloc(sizeof(*u));
+ }
+ for(i = 0; i < sizeof(*u); ++i) {
+ ((char *)u->snapshot)[i] = ((char *)u)[i];
+ }
+ }
+ printf("pc 0x%04x snapshot%s taken\n", u->counter, want_snapshot ? "" : " not");
+ }
+ break;
+ case 0x0a: /* exit */
+ printf("Exited after 0x%04x cycles.\n", u->counter);
+ exit(b1);
+ break;
+ case 0x0f: /* test mode */
+ u->test_mode = b1;
+ printf("Test mode is now 0x%02x: ", u->test_mode);
+ if(b1 & 0x01) {
+ printf("BRK resets stacks to zero length");
+ } else {
+ printf("all test mode features disabled");
+ }
+ printf("\n");
+ break;
+ }
+ fflush(stdout);
+ return b1;
+}
+\ No newline at end of file
diff --git a/src/debugger.c b/src/debugger.c
@@ -0,0 +1,112 @@
+#include <stdio.h>
+
+/*
+Copyright (c) 2021 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.
+*/
+
+#include "uxn.h"
+
+#pragma mark - Core
+
+int
+error(char *msg, const char *err)
+{
+ printf("Error %s: %s\n", msg, err);
+ return 0;
+}
+
+#pragma mark - Devices
+
+Uint8
+console_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
+{
+ Uint8 *m = u->ram.dat;
+ switch(b0) {
+ case 0x08: printf("%c", b1); break;
+ case 0x09: printf("0x%02x\n", b1); break;
+ case 0x0b: printf("0x%04x\n", (m[ptr + 0x0a] << 8) + b1); break;
+ }
+ fflush(stdout);
+ (void)m;
+ (void)ptr;
+ (void)b0;
+ return b1;
+}
+
+Uint8
+file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
+{
+ Uint8 *m = u->ram.dat;
+ char *name = (char *)&m[(m[ptr + 8] << 8) + m[ptr + 8 + 1]];
+ Uint16 length = (m[ptr + 8 + 2] << 8) + m[ptr + 8 + 3];
+ if(b0 == 0x0d) {
+ Uint16 addr = (m[ptr + 8 + 4] << 8) + b1;
+ FILE *f = fopen(name, "r");
+ if(f && fread(&m[addr], length, 1, f)) {
+ fclose(f);
+ printf("Loaded %d bytes, at %04x from %s\n", length, addr, name);
+ }
+ } else if(b0 == 0x0f) {
+ Uint16 addr = (m[ptr + 8 + 6] << 8) + b1;
+ FILE *f = fopen(name, "w");
+ if(fwrite(&m[addr], length, 1, f)) {
+ fclose(f);
+ printf("Saved %d bytes, at %04x from %s\n", length, addr, name);
+ }
+ }
+ return b1;
+}
+
+Uint8
+ppnil(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
+{
+ (void)u;
+ (void)ptr;
+ (void)b0;
+ return b1;
+}
+
+#pragma mark - Generics
+
+int
+start(Uxn *u)
+{
+ printf("RESET --------\n");
+ if(!evaluxn(u, u->vreset))
+ return error("Reset", "Failed");
+ printf("FRAME --------\n");
+ if(!evaluxn(u, u->vframe))
+ return error("Frame", "Failed");
+ return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ Uxn u;
+
+ if(argc < 2)
+ return error("Input", "Missing");
+ if(!bootuxn(&u))
+ return error("Boot", "Failed");
+ if(!loaduxn(&u, argv[1]))
+ return error("Load", "Failed");
+
+ portuxn(&u, "console", console_poke);
+ portuxn(&u, "empty", ppnil);
+ portuxn(&u, "empty", ppnil);
+ portuxn(&u, "empty", ppnil);
+ portuxn(&u, "empty", ppnil);
+ portuxn(&u, "empty", ppnil);
+ portuxn(&u, "file", file_poke);
+ start(&u);
+
+ return 0;
+}
diff --git a/emulator.c b/src/emulator.c
diff --git a/src/uxn.c b/src/uxn.c
@@ -0,0 +1,233 @@
+#include <stdio.h>
+
+/*
+Copyright (u) 2021 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.
+*/
+
+#include "uxn.h"
+
+#pragma mark - Operations
+
+/* clang-format off */
+void setflag(Uint8 *a, char flag, int b) { if(b) *a |= flag; else *a &= (~flag); }
+int getflag(Uint8 *a, char flag) { return *a & flag; }
+Uint8 devpoke8(Uxn *u, Uint8 id, Uint8 b0, Uint8 b1){ return id < u->devices ? u->dev[id].poke(u, PAGE_DEVICE + id * 0x10, b0, b1) : b1; }
+void mempoke8(Uxn *u, Uint16 a, Uint8 b) { u->ram.dat[a] = (a & 0xff00) == PAGE_DEVICE ? devpoke8(u, (a & 0xff) >> 4, a & 0xf, b) : b; }
+Uint8 mempeek8(Uxn *u, Uint16 a) { return u->ram.dat[a]; }
+void mempoke16(Uxn *u, Uint16 a, Uint16 b) { mempoke8(u, a, b >> 8); mempoke8(u, a + 1, b); }
+Uint16 mempeek16(Uxn *u, Uint16 a) { return (mempeek8(u, a) << 8) + mempeek8(u, a + 1); }
+void push8(Stack *s, Uint8 a) { s->dat[s->ptr++] = a; }
+Uint8 pop8(Stack *s) { return s->dat[--s->ptr]; }
+Uint8 peek8(Stack *s, Uint8 a) { return s->dat[s->ptr - a - 1]; }
+void push16(Stack *s, Uint16 a) { push8(s, a >> 8); push8(s, a); }
+Uint16 pop16(Stack *s) { return pop8(s) + (pop8(s) << 8); }
+Uint16 peek16(Stack *s, Uint8 a) { return peek8(s, a * 2) + (peek8(s, a * 2 + 1) << 8); }
+/* Stack */
+void op_brk(Uxn *u) { setflag(&u->status, FLAG_HALT, 1); }
+void op_lit(Uxn *u) { u->literal += 1; }
+void op_nop(Uxn *u) { (void)u; }
+void op_pop(Uxn *u) { pop8(u->src); }
+void op_dup(Uxn *u) { push8(u->src, peek8(u->src, 0)); }
+void op_swp(Uxn *u) { Uint8 b = pop8(u->src), a = pop8(u->src); push8(u->src, b); push8(u->src, a); }
+void op_ovr(Uxn *u) { push8(u->src, peek8(u->src, 1)); }
+void op_rot(Uxn *u) { Uint8 c = pop8(u->src), b = pop8(u->src), a = pop8(u->src); push8(u->src, b); push8(u->src, c); push8(u->src, a); }
+/* Logic */
+void op_equ(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b == a); }
+void op_neq(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b != a); }
+void op_gth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b > a); }
+void op_lth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b < a); }
+void op_gts(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, (Sint8)b > (Sint8)a); }
+void op_lts(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, (Sint8)b < (Sint8)a); }
+void op_jmp(Uxn *u) { Uint8 a = pop8(u->src); u->ram.ptr += (Sint8)a; }
+void op_jsr(Uxn *u) { Uint8 a = pop8(u->src); push16(u->dst, u->ram.ptr); u->ram.ptr += (Sint8)a; }
+/* Memory */
+void op_pek(Uxn *u) { Uint16 a = pop8(u->src); push8(u->src, mempeek8(u, a)); }
+void op_pok(Uxn *u) { Uint16 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u, a, b); }
+void op_ldr(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u, a)); }
+void op_str(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u, a, b); }
+void op_cln(Uxn *u) { push8(u->src, peek8(u->dst, 0)); }
+void op_sth(Uxn *u) { Uint8 a = pop8(u->src); push8(u->dst, a); }
+/* Arithmetic */
+void op_add(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b + a); }
+void op_sub(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b - a); }
+void op_mul(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b * a); }
+void op_div(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b / a); }
+void op_and(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b & a); }
+void op_ora(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b | a); }
+void op_eor(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b ^ a); }
+void op_sft(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); Uint8 left = (a & 0xf0) >> 4; Uint8 right = (a & 0x0f); push8(u->src, b >> (right % 8) << (left % 8)); }
+/* Stack */
+void op_lit16(Uxn *u) { u->literal += 2; }
+void op_nop16(Uxn *u) { printf("%04x\n", pop16(u->src)); }
+void op_pop16(Uxn *u) { pop16(u->src); }
+void op_dup16(Uxn *u) { push16(u->src, peek16(u->src, 0)); }
+void op_swp16(Uxn *u) { Uint16 b = pop16(u->src), a = pop16(u->src); push16(u->src, b); push16(u->src, a); }
+void op_ovr16(Uxn *u) { push16(u->src, peek16(u->src, 1)); }
+void op_rot16(Uxn *u) { Uint16 c = pop16(u->src), b = pop16(u->src), a = pop16(u->src); push16(u->src, b); push16(u->src, c); push16(u->src, a); }
+/* Logic(16-bits) */
+void op_equ16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b == a); }
+void op_neq16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b != a); }
+void op_gth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b > a); }
+void op_lth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b < a); }
+void op_gts16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, (Sint16)b > (Sint16)a); }
+void op_lts16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, (Sint16)b < (Sint16)a); }
+void op_jmp16(Uxn *u) { u->ram.ptr = pop16(u->src); }
+void op_jsr16(Uxn *u) { push16(u->dst, u->ram.ptr); u->ram.ptr = pop16(u->src); }
+/* Memory(16-bits) */
+void op_pek16(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u, a)); }
+void op_pok16(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u, a, b); }
+void op_ldr16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, mempeek16(u, a)); }
+void op_str16(Uxn *u) { Uint16 a = pop16(u->src); Uint16 b = pop16(u->src); mempoke16(u, a, b); }
+void op_cln16(Uxn *u) { push16(u->src, peek16(u->dst, 0)); }
+void op_sth16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->dst, a); }
+/* Arithmetic(16-bits) */
+void op_add16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b + a); }
+void op_sub16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b - a); }
+void op_mul16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b * a); }
+void op_div16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b / a); }
+void op_and16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b & a); }
+void op_ora16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b | a); }
+void op_eor16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b ^ a); }
+void op_sft16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); Uint8 left = (a & 0x00f0) >> 4; Uint8 right = (a & 0x000f); push16(u->src, b >> (right % 16) << (left % 16)); }
+
+void (*ops[])(Uxn *u) = {
+ op_brk, op_nop, op_lit, op_pop, op_dup, op_swp, op_ovr, op_rot,
+ op_equ, op_neq, op_gth, op_lth, op_gts, op_lts, op_jmp, op_jsr,
+ op_pek, op_pok, op_ldr, op_str, op_nop, op_nop, op_cln, op_sth,
+ op_add, op_sub, op_mul, op_div, op_and, op_ora, op_eor, op_sft,
+ /* 16-bit */
+ op_brk, op_nop16, op_lit16, op_pop16, op_dup16, op_swp16, op_ovr16, op_rot16,
+ op_equ16, op_neq16, op_gth16, op_lth16, op_gts16, op_lts16, op_jmp16, op_jsr16,
+ op_pek16, op_pok16, op_ldr16, op_str16, op_nop, op_nop, op_cln16, op_sth16,
+ op_add16, op_sub16, op_mul16, op_div16, op_and16, op_ora16, op_eor16, op_sft16
+};
+
+Uint8 opr[][4] = { /* wstack-/+ rstack-/+ */
+ {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {1,0,0,0}, {1,2,0,0}, {2,2,0,0}, {2,3,0,0}, {3,3,0,0},
+ {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {1,0,0,0}, {1,0,0,2},
+ {1,1,0,0}, {2,0,0,0}, {2,1,0,0}, {3,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,1,1,1}, {1,0,0,1},
+ {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0},
+ /* 16-bit */
+ {0,0,0,0}, {2,0,0,0}, {0,0,0,0}, {2,0,0,0}, {2,4,0,0}, {4,4,0,0}, {4,6,0,0}, {6,6,0,0},
+ {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {2,0,0,0}, {2,0,0,2},
+ {2,1,0,0}, {3,0,0,0}, {2,2,0,0}, {4,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,2,2,2}, {2,0,0,2},
+ {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}
+};
+
+/* clang-format on */
+
+#pragma mark - Core
+
+int
+haltuxn(Uxn *u, char *name, int id)
+{
+ printf("Halted: %s#%04x, at 0x%04x\n", name, id, u->counter);
+ return 0;
+}
+
+int
+lituxn(Uxn *u, Uint8 instr)
+{
+ if(u->wst.ptr >= 255)
+ return haltuxn(u, "Stack overflow", instr);
+ push8(&u->wst, instr);
+ u->literal--;
+ return 1;
+}
+
+int
+opcuxn(Uxn *u, Uint8 instr)
+{
+ Uint8 op = instr & 0x1f, fcond, freturn;
+ setflag(&u->status, FLAG_SHORT, (instr >> 5) & 1);
+ setflag(&u->status, FLAG_RETURN, (instr >> 6) & 1);
+ setflag(&u->status, FLAG_COND, (instr >> 7) & 1);
+ fcond = getflag(&u->status, FLAG_COND);
+ freturn = getflag(&u->status, FLAG_RETURN);
+ u->src = freturn ? &u->rst : &u->wst;
+ u->dst = freturn ? &u->wst : &u->rst;
+ if(getflag(&u->status, FLAG_SHORT))
+ op += 32;
+ if(u->src->ptr < opr[op][0] || (fcond && u->src->ptr < 1))
+ return haltuxn(u, "Working-stack underflow", op);
+ if(u->src->ptr + opr[op][1] - opr[op][0] >= 255)
+ return haltuxn(u, "Working-stack overflow", instr);
+ if(u->dst->ptr < opr[op][2])
+ return haltuxn(u, "Return-stack underflow", op);
+ if(u->dst->ptr + opr[op][3] - opr[op][2] >= 255)
+ return haltuxn(u, "Return-stack overflow", instr);
+ if(!fcond || (fcond && pop8(&u->wst)))
+ (*ops[op])(u);
+ else
+ u->src->ptr -= opr[op][freturn ? 2 : 0] - opr[op][freturn ? 3 : 1];
+ return 1;
+}
+
+int
+stepuxn(Uxn *u, Uint8 instr)
+{
+ if(u->literal > 0)
+ return lituxn(u, instr);
+ else
+ return opcuxn(u, instr);
+}
+
+int
+evaluxn(Uxn *u, Uint16 vec)
+{
+ u->literal = 0;
+ u->ram.ptr = vec;
+ setflag(&u->status, FLAG_HALT, 0);
+ while(!(u->status & FLAG_HALT)) {
+ Uint8 instr = u->ram.dat[u->ram.ptr++];
+ if(!stepuxn(u, instr))
+ return 0;
+ u->counter++;
+ }
+ return 1;
+}
+
+int
+bootuxn(Uxn *u)
+{
+ size_t i;
+ char *cptr = (char *)u;
+ for(i = 0; i < sizeof(*u); i++)
+ cptr[i] = 0;
+ return 1;
+}
+
+int
+loaduxn(Uxn *u, char *filepath)
+{
+ FILE *f;
+ if(!(f = fopen(filepath, "rb")))
+ return haltuxn(u, "Missing input rom.", 0);
+ fread(u->ram.dat, sizeof(u->ram.dat), 1, f);
+ u->vreset = mempeek16(u, PAGE_DEVICE + 0x00f0);
+ u->vframe = mempeek16(u, PAGE_DEVICE + 0x00f2);
+ u->verror = mempeek16(u, PAGE_DEVICE + 0x00f4);
+ printf("Uxn loaded[%s] vrst:%04x vfrm:%04x verr:%04x.\n",
+ filepath,
+ u->vreset,
+ u->vframe,
+ u->verror);
+ return 1;
+}
+
+Device *
+portuxn(Uxn *u, char *name, Uint8 (*pofn)(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1))
+{
+ Device *d = &u->dev[u->devices++];
+ d->addr = PAGE_DEVICE + (u->devices - 1) * 0x10;
+ d->poke = pofn;
+ printf("Device #%d: %s, at 0x%04x \n", u->devices - 1, name, d->addr);
+ return d;
+}
diff --git a/uxn.h b/src/uxn.h
diff --git a/uxn.c b/uxn.c
@@ -1,234 +0,0 @@
-#include <stdio.h>
-
-/*
-Copyright (u) 2021 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.
-*/
-
-#include "uxn.h"
-
-#pragma mark - Operations
-
-/* clang-format off */
-void setflag(Uint8 *a, char flag, int b) { if(b) *a |= flag; else *a &= (~flag); }
-int getflag(Uint8 *a, char flag) { return *a & flag; }
-Uint8 devpoke8(Uxn *u, Uint8 id, Uint8 b0, Uint8 b1){ return id < u->devices ? u->dev[id].poke(u, PAGE_DEVICE + id * 0x10, b0, b1) : b1; }
-void mempoke8(Uxn *u, Uint16 a, Uint8 b) { u->ram.dat[a] = (a & 0xff00) == PAGE_DEVICE ? devpoke8(u, (a & 0xff) >> 4, a & 0xf, b) : b; }
-Uint8 mempeek8(Uxn *u, Uint16 a) { return u->ram.dat[a]; }
-void mempoke16(Uxn *u, Uint16 a, Uint16 b) { mempoke8(u, a, b >> 8); mempoke8(u, a + 1, b); }
-Uint16 mempeek16(Uxn *u, Uint16 a) { return (mempeek8(u, a) << 8) + mempeek8(u, a + 1); }
-void push8(Stack *s, Uint8 a) { s->dat[s->ptr++] = a; }
-Uint8 pop8(Stack *s) { return s->dat[--s->ptr]; }
-Uint8 peek8(Stack *s, Uint8 a) { return s->dat[s->ptr - a - 1]; }
-void push16(Stack *s, Uint16 a) { push8(s, a >> 8); push8(s, a); }
-Uint16 pop16(Stack *s) { return pop8(s) + (pop8(s) << 8); }
-Uint16 peek16(Stack *s, Uint8 a) { return peek8(s, a * 2) + (peek8(s, a * 2 + 1) << 8); }
-/* Stack */
-void op_brk(Uxn *u) { setflag(&u->status, FLAG_HALT, 1); }
-void op_lit(Uxn *u) { u->literal += 1; }
-void op_nop(Uxn *u) { (void)u; }
-void op_pop(Uxn *u) { pop8(u->src); }
-void op_dup(Uxn *u) { push8(u->src, peek8(u->src, 0)); }
-void op_swp(Uxn *u) { Uint8 b = pop8(u->src), a = pop8(u->src); push8(u->src, b); push8(u->src, a); }
-void op_ovr(Uxn *u) { push8(u->src, peek8(u->src, 1)); }
-void op_rot(Uxn *u) { Uint8 c = pop8(u->src), b = pop8(u->src), a = pop8(u->src); push8(u->src, b); push8(u->src, c); push8(u->src, a); }
-/* Logic */
-void op_equ(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b == a); }
-void op_neq(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b != a); }
-void op_gth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b > a); }
-void op_lth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b < a); }
-void op_gts(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, (Sint8)b > (Sint8)a); }
-void op_lts(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, (Sint8)b < (Sint8)a); }
-void op_jmp(Uxn *u) { Uint8 a = pop8(u->src); u->ram.ptr += (Sint8)a; }
-void op_jsr(Uxn *u) { Uint8 a = pop8(u->src); push16(u->dst, u->ram.ptr); u->ram.ptr += (Sint8)a; }
-/* Memory */
-void op_pek(Uxn *u) { Uint16 a = pop8(u->src); push8(u->src, mempeek8(u, a)); }
-void op_pok(Uxn *u) { Uint16 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u, a, b); }
-void op_ldr(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u, a)); }
-void op_str(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u, a, b); }
-void op_cln(Uxn *u) { push8(u->src, peek8(u->dst, 0)); }
-void op_sth(Uxn *u) { Uint8 a = pop8(u->src); push8(u->dst, a); }
-/* Arithmetic */
-void op_add(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b + a); }
-void op_sub(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b - a); }
-void op_mul(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b * a); }
-void op_div(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b / a); }
-void op_and(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b & a); }
-void op_ora(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b | a); }
-void op_eor(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b ^ a); }
-void op_sft(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); Uint8 left = (a & 0xf0) >> 4; Uint8 right = (a & 0x0f); push8(u->src, b >> (right % 8) << (left % 8)); }
-/* Stack */
-void op_lit16(Uxn *u) { u->literal += 2; }
-void op_nop16(Uxn *u) { printf("%04x\n", pop16(u->src)); }
-void op_pop16(Uxn *u) { pop16(u->src); }
-void op_dup16(Uxn *u) { push16(u->src, peek16(u->src, 0)); }
-void op_swp16(Uxn *u) { Uint16 b = pop16(u->src), a = pop16(u->src); push16(u->src, b); push16(u->src, a); }
-void op_ovr16(Uxn *u) { push16(u->src, peek16(u->src, 1)); }
-void op_rot16(Uxn *u) { Uint16 c = pop16(u->src), b = pop16(u->src), a = pop16(u->src); push16(u->src, b); push16(u->src, c); push16(u->src, a); }
-/* Logic(16-bits) */
-void op_equ16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b == a); }
-void op_neq16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b != a); }
-void op_gth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b > a); }
-void op_lth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b < a); }
-void op_gts16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, (Sint16)b > (Sint16)a); }
-void op_lts16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, (Sint16)b < (Sint16)a); }
-void op_jmp16(Uxn *u) { u->ram.ptr = pop16(u->src); }
-void op_jsr16(Uxn *u) { push16(u->dst, u->ram.ptr); u->ram.ptr = pop16(u->src); }
-/* Memory(16-bits) */
-void op_pek16(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u, a)); }
-void op_pok16(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u, a, b); }
-void op_ldr16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, mempeek16(u, a)); }
-void op_str16(Uxn *u) { Uint16 a = pop16(u->src); Uint16 b = pop16(u->src); mempoke16(u, a, b); }
-void op_cln16(Uxn *u) { push16(u->src, peek16(u->dst, 0)); }
-void op_sth16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->dst, a); }
-/* Arithmetic(16-bits) */
-void op_add16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b + a); }
-void op_sub16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b - a); }
-void op_mul16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b * a); }
-void op_div16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b / a); }
-void op_and16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b & a); }
-void op_ora16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b | a); }
-void op_eor16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b ^ a); }
-void op_sft16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); Uint8 left = (a & 0x00f0) >> 4; Uint8 right = (a & 0x000f); push16(u->src, b >> (right % 16) << (left % 16)); }
-
-void (*ops[])(Uxn *u) = {
- op_brk, op_nop, op_lit, op_pop, op_dup, op_swp, op_ovr, op_rot,
- op_equ, op_neq, op_gth, op_lth, op_gts, op_lts, op_jmp, op_jsr,
- op_pek, op_pok, op_ldr, op_str, op_nop, op_nop, op_cln, op_sth,
- op_add, op_sub, op_mul, op_div, op_and, op_ora, op_eor, op_sft,
- /* 16-bit */
- op_brk, op_nop16, op_lit16, op_pop16, op_dup16, op_swp16, op_ovr16, op_rot16,
- op_equ16, op_neq16, op_gth16, op_lth16, op_gts16, op_lts16, op_jmp16, op_jsr16,
- op_pek16, op_pok16, op_ldr16, op_str16, op_nop, op_nop, op_cln16, op_sth16,
- op_add16, op_sub16, op_mul16, op_div16, op_and16, op_ora16, op_eor16, op_sft16
-};
-
-Uint8 opr[][4] = { /* wstack-/+ rstack-/+ */
- {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {1,0,0,0}, {1,2,0,0}, {2,2,0,0}, {2,3,0,0}, {3,3,0,0},
- {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {1,0,0,0}, {1,0,0,2},
- {1,1,0,0}, {2,0,0,0}, {2,1,0,0}, {3,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,1,1,1}, {1,0,0,1},
- {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0}, {2,1,0,0},
- /* 16-bit */
- {0,0,0,0}, {2,0,0,0}, {0,0,0,0}, {2,0,0,0}, {2,4,0,0}, {4,4,0,0}, {4,6,0,0}, {6,6,0,0},
- {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {4,1,0,0}, {2,0,0,0}, {2,0,0,2},
- {2,1,0,0}, {3,0,0,0}, {2,2,0,0}, {4,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,2,2,2}, {2,0,0,2},
- {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}, {4,2,0,0}
-};
-
-/* clang-format on */
-
-#pragma mark - Core
-
-int
-haltuxn(Uxn *u, char *name, int id)
-{
- printf("Halted: %s#%04x, at 0x%04x\n", name, id, u->counter);
- return 0;
-}
-
-int
-lituxn(Uxn *u, Uint8 instr)
-{
- if(u->wst.ptr >= 255)
- return haltuxn(u, "Stack overflow", instr);
- push8(&u->wst, instr);
- u->literal--;
- return 1;
-}
-
-int
-opcuxn(Uxn *u, Uint8 instr)
-{
- Uint8 op = instr & 0x1f, fcond, freturn;
- setflag(&u->status, FLAG_SHORT, (instr >> 5) & 1);
- setflag(&u->status, FLAG_RETURN, (instr >> 6) & 1);
- setflag(&u->status, FLAG_COND, (instr >> 7) & 1);
- fcond = getflag(&u->status, FLAG_COND);
- freturn = getflag(&u->status, FLAG_RETURN);
- u->src = freturn ? &u->rst : &u->wst;
- u->dst = freturn ? &u->wst : &u->rst;
- if(getflag(&u->status, FLAG_SHORT))
- op += 32;
- if(u->src->ptr < opr[op][0] || (fcond && u->src->ptr < 1))
- return haltuxn(u, "Working-stack underflow", op);
- if(u->src->ptr + opr[op][1] - opr[op][0] >= 255)
- return haltuxn(u, "Working-stack overflow", instr);
- if(u->dst->ptr < opr[op][2])
- return haltuxn(u, "Return-stack underflow", op);
- if(u->dst->ptr + opr[op][3] - opr[op][2] >= 255)
- return haltuxn(u, "Return-stack overflow", instr);
- if(!fcond || (fcond && pop8(&u->wst)))
- (*ops[op])(u);
- else
- u->src->ptr -= opr[op][freturn ? 2 : 0] - opr[op][freturn ? 3 : 1];
- return 1;
-}
-
-int
-stepuxn(Uxn *u, Uint8 instr)
-{
- if(u->literal > 0)
- return lituxn(u, instr);
- else
- return opcuxn(u, instr);
-}
-
-int
-evaluxn(Uxn *u, Uint16 vec)
-{
- u->literal = 0;
- u->ram.ptr = vec;
- setflag(&u->status, FLAG_HALT, 0);
- while(!(u->status & FLAG_HALT)) {
- Uint8 instr = u->ram.dat[u->ram.ptr++];
- if(!stepuxn(u, instr))
- return 0;
- u->counter++;
- }
- return 1;
-}
-
-int
-bootuxn(Uxn *u)
-{
- size_t i;
- char *cptr = (char *)u;
- for(i = 0; i < sizeof(*u); i++)
- cptr[i] = 0;
-
- return 1;
-}
-
-int
-loaduxn(Uxn *u, char *filepath)
-{
- FILE *f;
- if(!(f = fopen(filepath, "rb")))
- return haltuxn(u, "Missing input rom.", 0);
- fread(u->ram.dat, sizeof(u->ram.dat), 1, f);
- u->vreset = mempeek16(u, PAGE_DEVICE + 0x00f0);
- u->vframe = mempeek16(u, PAGE_DEVICE + 0x00f2);
- u->verror = mempeek16(u, PAGE_DEVICE + 0x00f4);
- printf("Uxn loaded[%s] vrst:%04x vfrm:%04x verr:%04x.\n",
- filepath,
- u->vreset,
- u->vframe,
- u->verror);
- return 1;
-}
-
-Device *
-portuxn(Uxn *u, char *name, Uint8 (*pofn)(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1))
-{
- Device *d = &u->dev[u->devices++];
- d->addr = PAGE_DEVICE + (u->devices - 1) * 0x10;
- d->poke = pofn;
- printf("Device #%d: %s, at 0x%04x \n", u->devices - 1, name, d->addr);
- return d;
-}