diff --git a/Makefile b/Makefile index a076ddf..3c913ca 100644 --- a/Makefile +++ b/Makefile @@ -152,22 +152,34 @@ clean-all: @rm -rf build/ @echo "All cleaned." -# --- HELP --- +# --- TEST COMPILATION TARGET --- +# Compiles all .asm.lisp test files to .rom using the debug VM executable +# Usage: make compile-tests PLATFORM=linux +compile-tests: $(BUILD_DIR)/undar-$(PLATFORM)$(TARGET_SUFFIX) + @echo "Compiling test assembly files for $(PLATFORM)..." + @for f in ./test/*.asm.lisp; do \ + base=$$(basename "$$f" .asm.lisp); \ + echo " [$$base] $$f -> ./test/$$base.rom"; \ + $(BUILD_DIR)/undar-$(PLATFORM)$(TARGET_SUFFIX) "$$f" -o "./test/$$base.rom"; \ + done + @echo "Compilation complete: $$(ls -1 ./test/*.rom | wc -l) ROM files generated" + +# Update help target to include new command help: @echo "Undar VM" @echo "" @echo "Targets:" @echo " make -> debug build (default: linux)" @echo " make debug -> same as above" - @echo " make release -> optimized, stripped build" + @echo " make release -> optimized, stripped build" + @echo " make compile-tests -> compile all test assembly files" @echo " make clean -> clean current platform" - @echo " make clean-all→ clean all platforms" + @echo " make clean-all-> clean all platforms" @echo "" @echo "Platforms:" @echo " make PLATFORM=linux -> GCC + SDL2" @echo " make PLATFORM=emscripten -> Emscripten + SDL2 for Web" - @echo " make PLATFORM=avr -> (example) AVR-GCC" @echo "" @echo "Output:" @echo " Linux: build/linux/undar-linux-" - @echo " Web: build/emscripten/undar.html (+ .js, .wasm)" + @echo " Web: build/emscripten/undar.html (+ .js, .wasm)" \ No newline at end of file diff --git a/src/arch/linux/main.c b/src/arch/linux/main.c index 04eb012..480a99a 100644 --- a/src/arch/linux/main.c +++ b/src/arch/linux/main.c @@ -234,6 +234,144 @@ void repl(VM *vm) { exit(0); } +#ifdef ASM_DEBUG +const char *opcode_to_string(Opcode op) { + static const char *names[] = { + [OP_HALT] = "halt", + [OP_JMP] = "jump", + [OP_JMPF] = "jump-if-flag", + [OP_CALL] = "call", + [OP_RETURN] = "return", + + /* Immediate loads (only 32-bit variant needed) */ + [OP_LOAD_IMM] = "load-immediate", + + /* Register-indirect loads */ + [OP_LOAD_IND_8] = "load-indirect-8", + [OP_LOAD_IND_16] = "load-indirect-16", + [OP_LOAD_IND_32] = "load-indirect-32", + + /* Absolute address loads */ + [OP_LOAD_ABS_8] = "load-absolute-8", + [OP_LOAD_ABS_16] = "load-absolute-16", + [OP_LOAD_ABS_32] = "load-absolute-32", + + /* Base+offset loads */ + [OP_LOAD_OFF_8] = "load-offset-8", + [OP_LOAD_OFF_16] = "load-offset-16", + [OP_LOAD_OFF_32] = "load-offset-32", + + /* Absolute address stores */ + [OP_STORE_ABS_8] = "store-absolute-8", + [OP_STORE_ABS_16] = "store-absolute-16", + [OP_STORE_ABS_32] = "store-absolute-32", + + /* Register-indirect stores */ + [OP_STORE_IND_8] = "store-indirect-8", + [OP_STORE_IND_16] = "store-indirect-16", + [OP_STORE_IND_32] = "store-indirect-32", + + /* Base+offset stores */ + [OP_STORE_OFF_8] = "store-offset-8", + [OP_STORE_OFF_16] = "store-offset-16", + [OP_STORE_OFF_32] = "store-offset-32", + + /* Memory operations */ + [OP_MALLOC] = "malloc", + [OP_MEMSET_8] = "memset-8", + [OP_MEMSET_16] = "memset-16", + [OP_MEMSET_32] = "memset-32", + + /* Stack operations */ + [OP_PUSH] = "push", + [OP_POP] = "pop", + + /* Register operations */ + [OP_REG_MOV] = "register-move", + [OP_SYSCALL] = "syscall", + + /* Bit operations */ + [OP_SLL] = "bit-shift-left", + [OP_SRL] = "bit-shift-right", + [OP_SRE] = "bit-shift-re", + [OP_BAND] = "bit-and", + [OP_BOR] = "bit-or", + [OP_BXOR] = "bit-xor", + + /* Integer arithmetic */ + [OP_ADD_INT] = "add-int", + [OP_SUB_INT] = "sub-int", + [OP_MUL_INT] = "mul-int", + [OP_DIV_INT] = "div-int", + + /* Natural number arithmetic */ + [OP_ADD_NAT] = "add-nat", + [OP_SUB_NAT] = "sub-nat", + [OP_MUL_NAT] = "mul-nat", + [OP_DIV_NAT] = "div-nat", + + /* Floating point operations */ + [OP_ADD_REAL] = "add-real", + [OP_SUB_REAL] = "sub-real", + [OP_MUL_REAL] = "mul-real", + [OP_DIV_REAL] = "div-real", + + /* Type conversions */ + [OP_INT_TO_REAL] = "int-to-real", + [OP_NAT_TO_REAL] = "nat-to-real", + [OP_REAL_TO_INT] = "real-to-int", + [OP_REAL_TO_NAT] = "real-to-nat", + + /* Integer comparisons */ + [OP_JEQ_INT] = "jump-eq-int", + [OP_JNEQ_INT] = "jump-neq-int", + [OP_JGT_INT] = "jump-gt-int", + [OP_JLT_INT] = "jump-lt-int", + [OP_JLE_INT] = "jump-le-int", + [OP_JGE_INT] = "jump-ge-int", + + /* Natural number comparisons */ + [OP_JEQ_NAT] = "jump-eq-nat", + [OP_JNEQ_NAT] = "jump-neq-nat", + [OP_JGT_NAT] = "jump-gt-nat", + [OP_JLT_NAT] = "jump-lt-nat", + [OP_JLE_NAT] = "jump-le-nat", + [OP_JGE_NAT] = "jump-ge-nat", + + /* Floating point comparisons */ + [OP_JEQ_REAL] = "jump-eq-real", + [OP_JNEQ_REAL] = "jump-neq-real", + [OP_JGE_REAL] = "jump-ge-real", + [OP_JGT_REAL] = "jump-gt-real", + [OP_JLT_REAL] = "jump-lt-real", + [OP_JLE_REAL] = "jump-le-real", + + /* String operations */ + [OP_STRLEN] = "string-length", + [OP_STREQ] = "string-eq", + [OP_STRCAT] = "string-concat", + [OP_STR_GET_CHAR] = "string-get-char", + [OP_STR_FIND_CHAR] = "string-find-char", + [OP_STR_SLICE] = "string-slice", + + /* String conversions */ + [OP_INT_TO_STRING] = "int-to-string", + [OP_NAT_TO_STRING] = "nat-to-string", + [OP_REAL_TO_STRING] = "real-to-string", + [OP_STRING_TO_INT] = "string-to-int", + [OP_STRING_TO_NAT] = "string-to-nat", + [OP_STRING_TO_REAL] = "string-to-real" + }; + + if (op < 0 || op >= (int)(sizeof(names) / sizeof(names[0]))) { + return ""; + } + + const char *name = names[op]; + return name ? name : ""; +} +#endif + i32 main(i32 argc, char *argv[]) { bool gui_mode = false; bool dump_rom = false; @@ -397,8 +535,12 @@ i32 main(i32 argc, char *argv[]) { int cycles_this_frame = 0; int max_cycles_per_frame = 1000; // Adjust this value while (cycles_this_frame < max_cycles_per_frame) { + #ifdef ASM_DEBUG + printf("| %s %d\n", opcode_to_string(vm.code[vm.pc]),vm.pc); + #endif if (!step_vm(&vm)) { running = false; + break; } cycles_this_frame++; } diff --git a/src/tools/assembler.c b/src/tools/assembler.c index c9b5305..fa6c645 100644 --- a/src/tools/assembler.c +++ b/src/tools/assembler.c @@ -1,5 +1,5 @@ #include "assembler.h" -typedef enum { SYMBOL_CODE, SYMBOL_DATA, SYMBOL_PLEX } SymbolType; +typedef enum { SYMBOL_CODE, SYMBOL_DATA } SymbolType; typedef struct { char *name; @@ -26,10 +26,6 @@ void symbol_table_add(SymbolTable *table, const char *name, u32 address, // Check for duplicates for (int i = 0; i < table->count; i++) { if (strcmp(table->symbols[i].name, name) == 0) { - // Allow plex redefinition for compiler evolution - if (type == SYMBOL_PLEX && table->symbols[i].type == SYMBOL_PLEX) { - return; - } fprintf(stderr, "Error: Duplicate label '%s'\n", name); exit(1); } diff --git a/src/tools/assembler.h b/src/tools/assembler.h index 9dabb70..7c49cf9 100644 --- a/src/tools/assembler.h +++ b/src/tools/assembler.h @@ -11,9 +11,9 @@ #include #define AS_FIXED(v) ((float)(i32)(v) / 65536.0f) -#define TO_FIXED(f) ((u32)((i32)( \ +#define TO_FIXED(f) ((i32)( \ ((f) >= 0.0f) ? ((f) * 65536.0f + 0.5f) : ((f) * 65536.0f - 0.5f) \ -))) +)) void assemble(VM *vm, ExprNode *program); diff --git a/src/tools/build.sh b/src/tools/build.sh deleted file mode 100644 index b758b83..0000000 --- a/src/tools/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -gcc math_gen.c -o math_gen -lm -./math_gen > ./math.h \ No newline at end of file diff --git a/src/tools/math_gen.c b/src/tools/math_gen.c deleted file mode 100644 index 089d78c..0000000 --- a/src/tools/math_gen.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include - -int main() { - printf("#ifndef ZRE_TRIG_TABLES_H\n#define ZRE_TRIG_TABLES_H\n#include \n\n"); - - // Generate SINE table (256 entries, Q16.16 format) - printf("const int32_t sin_table[256] = {\n"); - for (int i = 0; i < 256; i++) { - double angle = i * 2 * M_PI / 256.0; // 0-360° in radians - double value = sin(angle) * 65536.0; // Scale to Q16.16 - int32_t fixed = (int32_t)(value + 0.5); // Round to nearest - printf(" %d,", fixed); - if (i % 8 == 7) printf("\n"); - } - printf("};\n\n"); - - // Generate COSINE table (256 entries, Q16.16 format) - printf("const int32_t cos_table[256] = {\n"); - for (int i = 0; i < 256; i++) { - double angle = i * 2 * M_PI / 256.0; // 0-360° in radians - double value = cos(angle) * 65536.0; // Scale to Q16.16 - int32_t fixed = (int32_t)(value + 0.5); // Round to nearest - printf(" %d,", fixed); - if (i % 8 == 7) printf("\n"); - } - printf("};\n"); - printf("#endif\n"); - return 0; -} \ No newline at end of file diff --git a/src/vm/str.c b/src/vm/str.c index 5f5173d..da0ca1e 100644 --- a/src/vm/str.c +++ b/src/vm/str.c @@ -108,8 +108,9 @@ void fixed_to_string(i32 value, char *buffer) { char temp[32]; i32 negative; u32 int_part; - u32 frac_part, frac_digits; + u32 frac_part, frac_digits, original_frac_digits; char *end = temp + sizeof(temp) - 1; + char *frac_start; *end = '\0'; negative = 0; @@ -122,14 +123,40 @@ void fixed_to_string(i32 value, char *buffer) { frac_part = AS_NAT(value & 0xFFFF); /* Convert fractional part to 5 decimal digits */ - frac_digits = (frac_part * 100000U) / 65536U; + original_frac_digits = (frac_part * 100000U) / 65536U; + frac_digits = original_frac_digits; if (frac_digits > 0) { - end = write_digits_backwards(frac_digits, end, temp); + /* Write fractional digits backwards */ + frac_start = write_digits_backwards(frac_digits, end, temp); + + /* Remove trailing zeros by moving the start pointer */ + while (*(end - 1) == '0' && end > frac_start) { + end--; + } + + /* If all fractional digits were zeros after removing trailing zeros, + we need to add back one zero to represent the .0 */ + if (end == frac_start) { + *--end = '0'; + } + + /* Add decimal point */ *--end = '.'; + } else if (frac_part > 0) { + /* Handle case where original_frac_digits was rounded to 0 but frac_part was not 0 */ + /* This means we have a very small fractional part that should be represented as .0 */ + *--end = '0'; + *--end = '.'; + } else if (frac_part == 0) { + /* No fractional part - just add .0 if we have an integer part */ + if (int_part != 0) { + *--end = '0'; + *--end = '.'; + } } - if (int_part == 0 && frac_digits == 0) { + if (int_part == 0 && frac_digits == 0 && frac_part == 0) { *--end = '0'; } else { end = write_digits_backwards(int_part, end, temp); @@ -140,4 +167,4 @@ void fixed_to_string(i32 value, char *buffer) { } strcopy(buffer, end, temp + sizeof(temp) - end); -} +} \ No newline at end of file diff --git a/test/add.asm.lisp b/test/add.asm.lisp index 9bd95bf..802ac5f 100644 --- a/test/add.asm.lisp +++ b/test/add.asm.lisp @@ -8,7 +8,7 @@ (pop $0) (int-to-string $1 $0) (push $1) - (call &println) + (call &pln) (halt)) (label add @@ -18,15 +18,17 @@ (push $2) (return)) - (label println - (load-immediate $0 &terminal-namespace) - (syscall OPEN $0 $0 $0) + (label pln + (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $0 $0 $11) (load-immediate $3 &new-line) (pop $1) + (load-offset-32 $7 $0 4) ; load handle (string-length $2 $1) - (syscall WRITE $0 $1 $2) + (syscall WRITE $7 $1 $2) (string-length $4 $3) - (syscall WRITE $0 $3 $4) + (syscall WRITE $7 $3 $4) (return))) (data (label terminal-namespace "/dev/term/0") diff --git a/test/add.rom b/test/add.rom index 3a769ff..d9dcc0a 100644 Binary files a/test/add.rom and b/test/add.rom differ diff --git a/test/fib.asm.lisp b/test/fib.asm.lisp index 9ba5ea7..75d2783 100644 --- a/test/fib.asm.lisp +++ b/test/fib.asm.lisp @@ -28,15 +28,17 @@ (label base-case (push $0) (return))) - (label pln - (load-immediate $0 &terminal-namespace) - (syscall OPEN $0 $0 $0) + (label pln + (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $0 $0 $11) (load-immediate $3 &new-line) (pop $1) + (load-offset-32 $7 $0 4) ; load handle (string-length $2 $1) - (syscall WRITE $0 $1 $2) + (syscall WRITE $7 $1 $2) (string-length $4 $3) - (syscall WRITE $0 $3 $4) + (syscall WRITE $7 $3 $4) (return))) (data (label terminal-namespace "/dev/term/0") diff --git a/test/fib.rom b/test/fib.rom index 4897cc1..763167d 100644 Binary files a/test/fib.rom and b/test/fib.rom differ diff --git a/test/hello.asm.lisp b/test/hello.asm.lisp index ce12640..f18a99d 100644 --- a/test/hello.asm.lisp +++ b/test/hello.asm.lisp @@ -1,11 +1,22 @@ ((code (label main - (load-immediate $0 &terminal-namespace) ; load terminal namespace - (syscall OPEN $0 $0 $0) (load-immediate $1 &hello-str) ; load hello string ptr - (string-length $2 $1) ; get length to write to stdout - (syscall WRITE $0 $1 $2) ; do the write syscall - (halt))) ; done + (push $1) + (call &pln) + (halt)) ; done + (label pln + (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $0 $0 $11) + (load-immediate $3 &new-line) + (pop $1) + (load-offset-32 $7 $0 4) ; load handle + (string-length $2 $1) + (syscall WRITE $7 $1 $2) + (string-length $4 $3) + (syscall WRITE $7 $3 $4) + (return))) (data (label terminal-namespace "/dev/term/0") - (label hello-str "nuqneH 'u'?\n"))) + (label new-line "\n") + (label hello-str "nuqneH 'u'?"))) diff --git a/test/hello.rom b/test/hello.rom index f375976..128f0e7 100644 Binary files a/test/hello.rom and b/test/hello.rom differ diff --git a/test/loop.asm.lisp b/test/loop.asm.lisp index 6f13625..9d91348 100644 --- a/test/loop.asm.lisp +++ b/test/loop.asm.lisp @@ -27,16 +27,18 @@ (push $3) (call &pln) (halt)) - (label pln - (load-immediate $0 &terminal-namespace) - (syscall OPEN $0 $0 $0) - (load-immediate $3 &new-line) - (pop $1) - (string-length $2 $1) - (syscall WRITE $0 $1 $2) - (string-length $4 $3) - (syscall WRITE $0 $3 $4) - (return))) + (label pln + (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $0 $0 $11) + (load-immediate $3 &new-line) + (pop $1) + (load-offset-32 $7 $0 4) ; load handle + (string-length $2 $1) + (syscall WRITE $7 $1 $2) + (string-length $4 $3) + (syscall WRITE $7 $3 $4) + (return))) (data (label terminal-namespace "/dev/term/0") (label help "Enter a string: ") diff --git a/test/loop.rom b/test/loop.rom index 70e8b57..60fb07c 100644 Binary files a/test/loop.rom and b/test/loop.rom differ diff --git a/test/malloc.asm.lisp b/test/malloc.asm.lisp index 5ace03f..253c274 100644 --- a/test/malloc.asm.lisp +++ b/test/malloc.asm.lisp @@ -1,25 +1,33 @@ ((code (label main (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $0 $0 $11) + (load-immediate $1 &help) ; print help message + (push $0) (push $1) (call &pln) + (load-immediate $1 32) ; read in a string of max 32 char length - (malloc $2 $1) ; allocate memory for the string - (syscall READ $0 $3 $1 $2) ; read the string - (push $3) + (malloc $4 $1) ; allocate memory for the string + (load-offset-32 $7 $0 4) ; load handle + (syscall READ $7 $2 $1 $4) ; read the string + + (push $0) + (push $4) (call &pln) ; print the string (halt)) - (label pln - (load-immediate $0 &terminal-namespace) - (syscall OPEN $0 $0 $0) - (load-immediate $3 &new-line) - (pop $1) - (string-length $2 $1) - (syscall WRITE $0 $1 $2) - (string-length $4 $3) - (syscall WRITE $0 $3 $4) - (return))) + (label pln + (load-immediate $3 &new-line) + (pop $1) + (pop $0) + (load-offset-32 $7 $0 4) ; load handle + (string-length $2 $1) + (syscall WRITE $7 $1 $2) + (string-length $4 $3) + (syscall WRITE $7 $3 $4) + (return))) (data (label terminal-namespace "/dev/term/0") (label help "Enter a string: ") diff --git a/test/malloc.rom b/test/malloc.rom new file mode 100644 index 0000000..8a8492d Binary files /dev/null and b/test/malloc.rom differ diff --git a/test/paint-bw.rom b/test/paint-bw.rom index 1758b2d..0bde7ec 100644 Binary files a/test/paint-bw.rom and b/test/paint-bw.rom differ diff --git a/test/paint.rom b/test/paint.rom index 754ba9d..4f67b04 100644 Binary files a/test/paint.rom and b/test/paint.rom differ diff --git a/test/simple.asm.lisp b/test/simple.asm.lisp index 55a17b3..ed58a15 100644 --- a/test/simple.asm.lisp +++ b/test/simple.asm.lisp @@ -1,22 +1,24 @@ ((code - (label main - (load-immediate $0 &x) - (load-immediate $1 &y) + (label main + (load-absolute-32 $0 &x) + (load-absolute-32 $1 &y) (add-real $2 $1 $0) (real-to-string $3 $2) (push $3) (call &pln) (halt)) - (label pln - (load-immediate $0 &terminal-namespace) - (syscall OPEN $0 $0 $0) - (load-immediate $3 &new-line) - (pop $1) - (string-length $2 $1) - (syscall WRITE $0 $1 $2) - (string-length $4 $3) - (syscall WRITE $0 $3 $4) - (return))) + (label pln + (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $0 $0 $11) + (load-immediate $3 &new-line) + (pop $1) + (load-offset-32 $7 $0 4) ; load handle + (string-length $2 $1) + (syscall WRITE $7 $1 $2) + (string-length $4 $3) + (syscall WRITE $7 $3 $4) + (return))) (data (label terminal-namespace "/dev/term/0") (label new-line "\n") (label x 1.0) diff --git a/test/simple.rom b/test/simple.rom index 775f8d9..90b0e81 100644 Binary files a/test/simple.rom and b/test/simple.rom differ diff --git a/test/window.asm.lisp b/test/window.asm.lisp index 53c81e4..b80216f 100644 --- a/test/window.asm.lisp +++ b/test/window.asm.lisp @@ -43,14 +43,7 @@ (load-immediate $16 &mouse-namespace) (syscall OPEN $15 $16 $11) ; open(out Plex mouse, in namespace, in flags) - (load-immediate $1 4) ; offset for handle - (add-nat $19 $15 $1) - - (nat-to-string $5 $19) - (push $32) - (push $5) - (call &pln) - (load-indirect-32 $16 $19) ; load handle + (load-offset-32 $16 $15 4) ; load handle (syscall WRITE $0 $21 $22) ; redraw diff --git a/test/window.rom b/test/window.rom index ddde16c..6810bc3 100644 Binary files a/test/window.rom and b/test/window.rom differ