uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

commit 82674a14844dc805ae8c75464a4f5d69ebf3e022
parent 1db61376a234334c630f40810bb813cdf8825c49
Author: neauoire <aliceffekt@gmail.com>
Date:   Mon, 22 Mar 2021 19:04:31 -0700

Created a debugger

Diffstat:
MREADME.md | 24+++++++-----------------
Mbuild.sh | 53++++++++++++++++++++++++++++++++++-------------------
Demulator-cli.c | 236-------------------------------------------------------------------------------
Dprojects/tests/cond.usm | 19-------------------
Dprojects/tests/draw.usm | 72------------------------------------------------------------------------
Aprojects/tests/generic.usm | 9+++++++++
Dprojects/tests/jump.usm | 60------------------------------------------------------------
Dprojects/tests/peek.usm | 22----------------------
Drun.sh | 13-------------
Rassembler.c -> src/assembler.c | 0
Asrc/backup-debugdev.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/debugger.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remulator.c -> src/emulator.c | 0
Asrc/uxn.c | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ruxn.h -> src/uxn.h | 0
Duxn.c | 234-------------------------------------------------------------------------------
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; -}