diff --git a/README.md b/README.md index 32ab29c..782c453 100644 --- 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 index 6410abe..d4cf583 100755 --- 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/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 - -# 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 +rm -f ./bin/debugger +rm -f ./bin/boot.rom -# Emulator(CLI) -clang-format -i emulator-cli.c -rm -f ./bin/emulator-cli +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 -# run -./bin/assembler projects/software/nasu.usm bin/boot.rom -./bin/emulator bin/boot.rom +echo "Assembling.." +./bin/assembler projects/examples/dev.console.usm bin/boot.rom + +echo "Running.." +if [ "${2}" = '--cli' ]; +then + echo "[cli]" + ./bin/debugger bin/boot.rom +else + ./bin/emulator bin/boot.rom +fi + +echo "Done." \ No newline at end of file diff --git a/projects/tests/cond.usm b/projects/tests/cond.usm deleted file mode 100644 index 0c0866b..0000000 --- a/projects/tests/cond.usm +++ /dev/null @@ -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 deleted file mode 100644 index 9c4ecf3..0000000 --- a/projects/tests/draw.usm +++ /dev/null @@ -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 new file mode 100644 index 0000000..8b0b83f --- /dev/null +++ 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 deleted file mode 100644 index 620f7e6..0000000 --- a/projects/tests/jump.usm +++ /dev/null @@ -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 deleted file mode 100644 index c01e0cd..0000000 --- a/projects/tests/peek.usm +++ /dev/null @@ -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 deleted file mode 100755 index b977995..0000000 --- a/run.sh +++ /dev/null @@ -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 similarity index 100% rename from assembler.c rename to src/assembler.c diff --git a/emulator-cli.c b/src/backup-debugdev.c similarity index 53% rename from emulator-cli.c rename to src/backup-debugdev.c index e9702c0..3b91b0c 100644 --- a/emulator-cli.c +++ b/src/backup-debugdev.c @@ -1,68 +1,3 @@ -#include - -/* -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) @@ -181,56 +116,4 @@ debug_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) } 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; -} +} \ No newline at end of file diff --git a/src/debugger.c b/src/debugger.c new file mode 100644 index 0000000..96047c0 --- /dev/null +++ b/src/debugger.c @@ -0,0 +1,112 @@ +#include + +/* +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 similarity index 100% rename from emulator.c rename to src/emulator.c diff --git a/uxn.c b/src/uxn.c similarity index 99% rename from uxn.c rename to src/uxn.c index a4c384d..d2a2636 100644 --- a/uxn.c +++ b/src/uxn.c @@ -201,7 +201,6 @@ bootuxn(Uxn *u) char *cptr = (char *)u; for(i = 0; i < sizeof(*u); i++) cptr[i] = 0; - return 1; } diff --git a/uxn.h b/src/uxn.h similarity index 100% rename from uxn.h rename to src/uxn.h