From 3be87619300dff3be4f6b314a05a8913124d2e2b Mon Sep 17 00:00:00 2001 From: zongor Date: Fri, 24 Oct 2025 23:04:05 -0700 Subject: [PATCH] WIP passing args and returning values from a function call. Updates to documentation, update logo --- README.org | 36 +- ROADMAP.org | 45 ++- docs/convert.sh | 3 + docs/favicon.ico | Bin 0 -> 305684 bytes docs/undar.png | Bin 0 -> 1802 bytes docs/undar.svg | 56 +++ docs/undârsċieppan-w.svg | 732 ------------------------------------- docs/undârsċieppan.svg | 732 ------------------------------------- src/arch/emscripten/main.c | 1 - src/arch/linux/main.c | 6 +- src/tools/assembler.c | 168 +++++++-- src/vm/opcodes.h | 11 +- src/vm/vm.c | 149 ++++++-- test/add.asm.lisp | 30 +- test/hello.asm.lisp | 16 +- test/loop.asm.lisp | 22 +- test/malloc.asm.lisp | 12 +- test/malloc.ul | 13 + test/paint-bw.rom | Bin 695 -> 0 bytes test/paint.asm.lisp | 192 +++------- {docs => test}/paint.ul | 246 +++++++------ test/simple.asm.lisp | 16 +- test/window.asm.lisp | 44 +-- 23 files changed, 609 insertions(+), 1921 deletions(-) create mode 100755 docs/convert.sh create mode 100644 docs/favicon.ico create mode 100644 docs/undar.png create mode 100644 docs/undar.svg delete mode 100644 docs/undârsċieppan-w.svg delete mode 100644 docs/undârsċieppan.svg create mode 100644 test/malloc.ul delete mode 100644 test/paint-bw.rom rename {docs => test}/paint.ul (78%) mode change 100755 => 100644 diff --git a/README.org b/README.org index f51ebe2..268f167 100644 --- a/README.org +++ b/README.org @@ -10,9 +10,9 @@ #+BEGIN_SRC [ᚢ ᛫ ᛫ ᛫ - ᛫ ᚱ ᛫ ᛫ - ᛫ ᛫ ᛋ ᛫ - ᛫ ᛫ ᛫ ᚾ] + ᛫ ᚾ ᛫ ᛫ + ᛫ ᛫ ᛞ ᛫ + ᛫ ᛫ ᛫ ᚱ] #+END_SRC * Undâr @@ -94,29 +94,17 @@ heap allocations using the internal malloc opcode push pointers within this fram #+BEGIN_SRC lisp ((code - (label main + (label main + (load-immediate $1 &hello-str) ; load hello string ptr + (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 $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 $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 + (syscall OPEN $0 $0 $11) (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) @@ -125,8 +113,8 @@ heap allocations using the internal malloc opcode push pointers within this fram (return))) (data (label terminal-namespace "/dev/term/0") - (label help "Enter a string: ") - (label new-line "\n"))) + (label new-line "\n") + (label hello-str "nuqneH 'u'?"))) #+END_SRC values passed to functions must be explicitly returned to propagate. heap values are copy on write, so if a value is modified in a child function it will change the parents value, unless the size of the structure changes then it will copy the parents value and append it to its own frame with the modification. this allows for the low resource usage of a C but the convenience of a Java/Go without the garbage collection. diff --git a/ROADMAP.org b/ROADMAP.org index c01a59e..aaef8cb 100644 --- a/ROADMAP.org +++ b/ROADMAP.org @@ -10,12 +10,49 @@ * Roadmap -** Memory System Improvements -in the beta phase the bump allocator will be removed and replaced with the following system: instead of the bump allocator memory and registers being separate, the frames are in vm memory and then dynamically allocate a frame and register with a size each time a function gets called of a particular arena size. the syntax would be fnN where N is the number is the size in kb for that particular frame, however the compiler will split up the available memory for that architecture into function arenas of the defined "theoretical maximum" of running coroutines. the "maximum" is going to be whatever is hardcoded for the VM, like 1MB or 100MB or 1GB or whatever. The true max will be <4GB because that is all that can be addressed by a u32. the sizes we can say that for a particular architecture has n many 128kb function and y many 8kb functions and so on; then the compiler could partition the system and report that the program requires x of this many slots of this size and y slots of another size. for strings we will extend the syntax we can use str8 is 8 bytes or str16 is 16 bytes or whatever, that way we have a shorthand that is consistent with the rest of the language and is friendly to new developers; however we would keep array syntax the same as in c-like languages `[size]`. the stack and return stack need to be removed as well; like imagine you push some value onto the stack and then switch coroutines and then in that one it pops or pushes that would break. instead we will have a tiny stack per frame for function arguments and have a "return register" to allocate where the function should return to when it finishes. if it gets called recursively it will just take up a new block of the same size. recursive calls should be discouraged in general for this reason, its not a good technique for embedded development or permacomputing. so yes, it *will* compile, but guarantee the compiler *will* make surreal comments about your code. The compiler output in general should have an [[esolangs.org/wiki/INTERCAL][INTERCAL]] feel to it; the compiler needs to not take itself too seriously, but that does not mean it should not give useful advice to the programmer. +** Actor System -** Coroutine System +* Actor-Based Concurrency Model (Frame-Managed Memory) -coroutine system that uses the yield keyword so that functions can run out of order. make all functions coroutines i.e. they should be first class objects in the vm. the system will work similar to a real time microkernel based os like qnx. when a coroutine yields the system will continue from the last function that was running, or it will continue in the "main function" which may spawn new coroutines. each call will allocate a new block in memory. there will also be a "behavior" system where a specific coroutine might have "restart on fail" where others might have "exit on fail" or still others might have "crash on fail", this allows for systems to react to changing external factors. in the long long term (post 1.0) i want to make coroutines similar to beam where they run mutithreaded an use mailboxes and message passing to communicate. +** Core Principles +- Deterministic memory management with no garbage collection or manual free +- Zero fragmentation via contiguous arena allocation +- Isolated memory spaces for all actors (no shared memory) +- Compile-time memory bounds enforcement +- Mission-critical certified (DO-178C Level A compliant) + +** Memory Architecture +- Actor memory is partitioned into fixed-size arenas +- Two primary arenas: + - =Main Thread Arena= (top of memory) + - =Actor Arenas= (bottom-up allocation) +- Arena layout: + #+begin_src text + |---------------------| <-- Top of memory + | MAIN THREAD | (e.g., 8KB) + |---------------------| <-- Base address of actor pool + | ACTOR 1 (4KB) | <-- Preallocated at system startup + |---------------------| + | ACTOR 2 (2KB) | <-- Size specified by developer + |---------------------| + | ACTOR 3 (1KB) | + |---------------------| <-- Bottom of memory + #+end_src + +*** Communication Protocol +- Bounded mailbox system +- Message flow: + 1. Main thread sends via =send_message()= + 2. Message copied into actor's mailbox + 3. Actor processes in =on_message()= during next cycle + 4. Actor sends response via =send_response()= + +*** Failure Behaviors +| Behavior | Action | Certification Impact | Example Use Case | +|----------------|----------------------------|----------------------|--------------------------| +| =RESTART= | Reset arena offset to 0 | 2 artifacts | Network reconnection | +| =EXIT= | Free arena | 1 artifact | Session termination | +| =CRASH= | Halt entire VM | 1 artifact | Critical subsystem fail | ** Example: Hello world (=hello.ul=) diff --git a/docs/convert.sh b/docs/convert.sh new file mode 100755 index 0000000..5394537 --- /dev/null +++ b/docs/convert.sh @@ -0,0 +1,3 @@ +#!/bin/sh +magick -background none -density 384 undar.svg -define icon:auto-resize favicon.ico +magick -background none -density 100 undar.svg -define icon:auto-resize undar.png \ No newline at end of file diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1bfaf27ed5467381c049f8e30946b5850e912ebd GIT binary patch literal 305684 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 XH%2G@%zMtVM3=B@qeNBl2u|z=YC3En literal 0 HcmV?d00001 diff --git a/docs/undar.png b/docs/undar.png new file mode 100644 index 0000000000000000000000000000000000000000..40cd8073e79d426fa11a8f10727603f74acc8a7a GIT binary patch literal 1802 zcmZ{kc{JPU8pnUA##+_I(rMCGtJ)wUf)=rrCM2ZxqFf>=Mnfu5iMbK%hEl`SPA1N|=bm%_nCG15{hs%Ezn|y*?|c7>BU(}Jm>d8A zigp+qXR*fZwah;8iGh-(#BwqK}s+58j zCo}PbBms-I0f4I1(+KfRmV)u50)W=Ry_U!fc2XA)K{PuDTaZvr?Fa;FugSR}?`5E>jVf)vMg6oL7 z4U%WJZjuku8<%ZKDY7ic$pd=36b+$w!-wqrQo`$|Y6?$svRA)K5tW^yPz&Az8*A9H zwE_XEgUT474s5OJo;+f`k8bQjzJ zzL38nG54YK^z!=J4is&WEHvOu^A%*jjPs1Gz_Dau=`)2Ps{qblCcZ5^W8rw^Dp5yX zJ3^N3t2xEUBFysC1MOb>WS_Ao~ydVwSTb!l^CpUO5#9f-fJEkuV-snn_uXN zzCYlRd;l>QY{}_#dGfKnccLbv8V=I?g*yZpiKvZYNG~3|TdqF=C`EV^n`4%DH>2!FpLTUmev*EN;gmm{*l)p>xOU3JS~aoz-_doStx2=8$aLk$Sg@^_ISc8@^cI6nT(-MC>j2UX5{d^x~j|NL`zZEvTjB5&*x4 zo%xae@;0Z=8T;jC(X<={=%!?CtG8)DqXS%g>1n3c1ldl!tEFZo)0+`AKbm%_3QCw2 z+aS{gRdEgrA@lM=<;G`)Ht$xz7wVU$(3*_yGueUuqsv0f>z!Jm`}acC>07g4bSW2A z9L?UvI>_oXPH`2U%xpFocAZoUSH=RSHY#Ej#Pt)-e^p6FpIhWZs&1xKabRTJA|^z( zt)*s&10K<9&c6bG7dhxFg__%(>gk_ksrkK7+?29z>@lk80J|uEsFuqd=Z_@izh5SK^&u;goqp8`JrVovZMB8cz)Dw&O8x6khC|iH zZ);H}s6HE0&B5MR^3&52sywAEJf){A(5QazJa()gCZ<`mBPeNYFEj}l>BPHRnZ^~B zJ0t3DsXT3(TC16?MaP#^4?>mcz{#C+WjV!-Iv=x6Od=P)z;V=yJMVwcX2_cX595WA z%5KUaX1iNifstZ*c~}OEv;?p#N?Qd&q^Z25x^2UoL=ZqI1nofb%y%!oU~UZ@e`w%#K;!cqmS;}C4~dZ?Z#%?CdY7p3E7ZKlr) zp_kr>He#pdm;ZIOS!LIMfK^cW(Tvh}5IFM$W4<-)0c%J1lr9lv8esJ!Gu+fXO}_r1 zuO!RkzoC*KaC6r%EB;v8R%#^TI*@P{m%YU41#9DyU4ujBu;!PwMX?0w-7f8puXSyd zytO~@b(cU(g#Ro)Sh;}@T~I#iKJ{(XtELwl9~c~eO%Wpg!E#p8rH(lu$eS7}6l8p$LeoFo>ce#rNLK{h!~kxBL}k+9&<5Nd!wg#-%=4}7#}c1&yn>}(xvo?83e F`5Vv-H&*}v literal 0 HcmV?d00001 diff --git a/docs/undar.svg b/docs/undar.svg new file mode 100644 index 0000000..a54757f --- /dev/null +++ b/docs/undar.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + diff --git a/docs/undârsċieppan-w.svg b/docs/undârsċieppan-w.svg deleted file mode 100644 index fe6eda8..0000000 --- a/docs/undârsċieppan-w.svg +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/undârsċieppan.svg b/docs/undârsċieppan.svg deleted file mode 100644 index a472393..0000000 --- a/docs/undârsċieppan.svg +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/arch/emscripten/main.c b/src/arch/emscripten/main.c index 820abef..93aed84 100644 --- a/src/arch/emscripten/main.c +++ b/src/arch/emscripten/main.c @@ -123,7 +123,6 @@ bool loadVM(const char *filename, VM *vm) { fread(&vm->cp, sizeof(u32), 1, file) != 1 || fread(&vm->fp, sizeof(u32), 1, file) != 1 || fread(&vm->sp, sizeof(u32), 1, file) != 1 || - fread(&vm->rp, sizeof(u32), 1, file) != 1 || fread(&vm->mp, sizeof(u32), 1, file) != 1 || fread(&vm->dc, sizeof(u32), 1, file) != 1 || fread(&vm->flag, sizeof(i32), 1, file) != 1) { diff --git a/src/arch/linux/main.c b/src/arch/linux/main.c index bfabc37..fb51e2e 100644 --- a/src/arch/linux/main.c +++ b/src/arch/linux/main.c @@ -55,7 +55,6 @@ bool saveVM(const char *filename, VM *vm) { fwrite(&vm->cp, sizeof(u32), 1, file) != 1 || fwrite(&vm->fp, sizeof(u32), 1, file) != 1 || fwrite(&vm->sp, sizeof(u32), 1, file) != 1 || - fwrite(&vm->rp, sizeof(u32), 1, file) != 1 || fwrite(&vm->mp, sizeof(u32), 1, file) != 1 || fwrite(&vm->dc, sizeof(u32), 1, file) != 1 || fwrite(&vm->flag, sizeof(i32), 1, file) != 1) { @@ -94,7 +93,6 @@ bool loadVM(const char *filename, VM *vm) { fread(&vm->cp, sizeof(u32), 1, file) != 1 || fread(&vm->fp, sizeof(u32), 1, file) != 1 || fread(&vm->sp, sizeof(u32), 1, file) != 1 || - fread(&vm->rp, sizeof(u32), 1, file) != 1 || fread(&vm->mp, sizeof(u32), 1, file) != 1 || fread(&vm->dc, sizeof(u32), 1, file) != 1 || fread(&vm->flag, sizeof(i32), 1, file) != 1) { @@ -440,7 +438,7 @@ i32 main(i32 argc, char *argv[]) { if (ext && (strcmp(ext, ".rom") == 0)) { is_rom = true; } - if (ext && (strcmp(ext, ".asm.lisp") == 0)) { + if (ext && (strcmp(ext, ".lisp") == 0)) { is_assembly = true; } } else if (output_file == nil && dump_rom) { @@ -587,7 +585,7 @@ i32 main(i32 argc, char *argv[]) { // Run VM for a fixed number of cycles or a time slice int cycles_this_frame = 0; - int max_cycles_per_frame = 1000; // Adjust this value + int max_cycles_per_frame = 100; // 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); diff --git a/src/tools/assembler.c b/src/tools/assembler.c index fa6c645..b97622d 100644 --- a/src/tools/assembler.c +++ b/src/tools/assembler.c @@ -1,4 +1,5 @@ #include "assembler.h" +#include "parser.h" typedef enum { SYMBOL_CODE, SYMBOL_DATA } SymbolType; typedef struct { @@ -66,25 +67,33 @@ int get_instruction_byte_size(ExprNode *node) { const char *opname = node->token; // Simple opcodes (1 byte) - if (strcmp(opname, "halt") == 0 || strcmp(opname, "return") == 0) { + if (strcmp(opname, "halt") == 0) { return 1; } - // Register-based opcodes (2 bytes: opcode + register) - if (strcmp(opname, "pop") == 0 || strcmp(opname, "push") == 0) { - return 2; + // Return (1 + 1) + if (strcmp(opname, "return") == 0) { + return 2; // 1 byte opcode + 1 byte return register } - if (strcmp(opname, "int-to-string") == 0 || strcmp(opname, "load-indirect-8") == 0 || - strcmp(opname, "nat-to-string") == 0 || strcmp(opname, "load-indirect-16") == 0 || - strcmp(opname, "real-to-string") == 0 || strcmp(opname, "load-indirect-32") == 0 || - strcmp(opname, "int-to-real") == 0 || strcmp(opname, "store-indirect-8") == 0 || - strcmp(opname, "nat-to-real") == 0 || strcmp(opname, "store-indirect-16") == 0 || - strcmp(opname, "real-to-int") == 0 || strcmp(opname, "store-indirect-32") == 0 || - strcmp(opname, "real-to-nat") == 0 || - strcmp(opname, "nat-to-int") == 0 || strcmp(opname, "int-to-nat") == 0 || - strcmp(opname, "string-length") == 0 || strcmp(opname, "store-absolute-32") == 0 || - strcmp(opname, "store-absolute-8") == 0 || strcmp(opname, "store-absolute-16") == 0 || + if (strcmp(opname, "int-to-string") == 0 || + strcmp(opname, "load-indirect-8") == 0 || + strcmp(opname, "nat-to-string") == 0 || + strcmp(opname, "load-indirect-16") == 0 || + strcmp(opname, "real-to-string") == 0 || + strcmp(opname, "load-indirect-32") == 0 || + strcmp(opname, "int-to-real") == 0 || + strcmp(opname, "store-indirect-8") == 0 || + strcmp(opname, "nat-to-real") == 0 || + strcmp(opname, "store-indirect-16") == 0 || + strcmp(opname, "real-to-int") == 0 || + strcmp(opname, "store-indirect-32") == 0 || + strcmp(opname, "real-to-nat") == 0 || strcmp(opname, "nat-to-int") == 0 || + strcmp(opname, "int-to-nat") == 0 || + strcmp(opname, "string-length") == 0 || + strcmp(opname, "store-absolute-32") == 0 || + strcmp(opname, "store-absolute-8") == 0 || + strcmp(opname, "store-absolute-16") == 0 || strcmp(opname, "memset") == 0 || strcmp(opname, "memset") == 0 || strcmp(opname, "memset-8") == 0 || strcmp(opname, "memset-16") == 0 || strcmp(opname, "register-move") == 0 || strcmp(opname, "malloc") == 0) { @@ -97,7 +106,8 @@ int get_instruction_byte_size(ExprNode *node) { strcmp(opname, "add-nat") == 0 || strcmp(opname, "sub-nat") == 0 || strcmp(opname, "mul-nat") == 0 || strcmp(opname, "div-nat") == 0 || strcmp(opname, "add-real") == 0 || strcmp(opname, "sub-real") == 0 || - strcmp(opname, "bit-shift-left") == 0 || strcmp(opname, "bit-shift-right") == 0 || + strcmp(opname, "bit-shift-left") == 0 || + strcmp(opname, "bit-shift-right") == 0 || strcmp(opname, "bit-and") == 0 || strcmp(opname, "bit-or") == 0 || strcmp(opname, "bit-xor") == 0 || strcmp(opname, "mul-real") == 0 || strcmp(opname, "div-real") == 0) { @@ -105,14 +115,15 @@ int get_instruction_byte_size(ExprNode *node) { } // (5 bytes: 1 + 4) - if (strcmp(opname, "call") == 0 || strcmp(opname, "jump-if-flag") == 0 || - strcmp(opname, "jump") == 0) { + if (strcmp(opname, "jump-if-flag") == 0 || strcmp(opname, "jump") == 0) { return 5; } // Load, Load-immediate (6 bytes: 1 + 1 + 4) - if (strcmp(opname, "load-absolute-32") == 0 || strcmp(opname, "load-immediate") == 0 || - strcmp(opname, "load-absolute-16") == 0 || strcmp(opname, "load-absolute-8") == 0) { + if (strcmp(opname, "load-absolute-32") == 0 || + strcmp(opname, "load-immediate") == 0 || + strcmp(opname, "load-absolute-16") == 0 || + strcmp(opname, "load-absolute-8") == 0) { return 6; } @@ -139,11 +150,31 @@ int get_instruction_byte_size(ExprNode *node) { strcmp(opname, "store-offset-16") == 0 || strcmp(opname, "store-offset-32") == 0 || strcmp(opname, "load-offset-8") == 0 || - strcmp(opname, "load-offset-16") == 0 || + strcmp(opname, "load-offset-16") == 0 || strcmp(opname, "load-offset-32") == 0) { return 7; } + // Call (1 + 4 + 1 + args + 1) + if (strcmp(opname, "call") == 0) { + ExprNode *args_node = node->children[1]; + u32 args_count; + + // Calculate actual argument count + if (strcmp(args_node->token, "nil") == 0) { + args_count = 0; + } else { + args_count = 1 + args_node->child_count; + } + + // Binary format: + // [1] OP_CALL + // [1] arg_count + // [1] return_reg + // [4] address + // [args_count] arguments (each 1 byte) + return 1 + 1 + 1 + 4 + args_count; + } // Syscall (1 + syscall_id (4) + args) if (strcmp(opname, "syscall") == 0) { @@ -431,10 +462,95 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) { emit_u32(vm, addr); } else if (strcmp(opname, "call") == 0) { emit_opcode(vm, OP_CALL); + + if (node->child_count < 3) { + fprintf(stderr, "Error: call requires (args) and return register\n"); + return; + } + + // Parse function address (first child) u32 addr = resolve_symbol(table, node->children[0]->token); + if (addr == (u32)-1) { + fprintf(stderr, "Error: undefined symbol '%s'\n", + node->children[0]->token); + return; + } emit_u32(vm, addr); - } else if (strcmp(opname, "return") == 0) { - emit_opcode(vm, OP_RETURN); + + // Parse argument list (second child) + ExprNode *args_node = node->children[1]; + u8 arg_count = 0; + + // Handle two possible representations: + // 1. Single element: represented as a node with token (child_count=0) + // 2. Multiple elements: represented as node with children (child_count>0) + if (args_node->child_count > 0) { + // Multiple arguments case + arg_count = args_node->child_count + 1; // +1 for the token + } else { + // Single argument case - token is the argument + arg_count = (args_node->token[0] != '\0') ? 1 : 0; + } + emit_byte(vm, arg_count); + + // Emit arguments based on representation + if (arg_count > 0) { + // First argument is always the token + const char *reg_str = args_node->token; + int reg = parse_register(reg_str); + if (reg < 0) { + fprintf(stderr, "Error: invalid argument register '%s'\n", reg_str); + return; + } + emit_byte(vm, (u8)reg); + + // Emit children if present + for (size_t i = 0; i < args_node->child_count; i++) { + reg_str = args_node->children[i]->token; + reg = parse_register(reg_str); + if (reg < 0) { + fprintf(stderr, "Error: invalid argument register '%s'\n", reg_str); + return; + } + emit_byte(vm, (u8)reg); + } + } + // Parse return register (third child) + const char *return_reg_str = node->children[2]->token; + int return_reg = parse_register(return_reg_str); + + if (return_reg < 0) { + if (strcmp(return_reg_str, "nil") == 0) { + return_reg = 0xFF; + } else { + fprintf(stderr, "Error: invalid return register '%s'\n", + return_reg_str); + return; + } + } + emit_byte(vm, (u8)return_reg); + +} else if (strcmp(opname, "return") == 0) { + emit_opcode(vm, OP_RETURN); + + if (node->child_count != 1) { + fprintf(stderr, "Error: return requires exactly one argument\n"); + return; + } + + const char *reg_str = node->children[0]->token; + int reg = parse_register(reg_str); + + // Handle "nil" as special case (no return value) + if (reg < 0) { + if (strcmp(reg_str, "nil") == 0) { + reg = 0xFF; // Special value for "no return" + } else { + fprintf(stderr, "Error: invalid return register '%s'\n", reg_str); + return; + } + } + emit_byte(vm, (u8)reg); } else if (strcmp(opname, "load-immediate") == 0) { emit_opcode(vm, OP_LOAD_IMM); int reg = parse_register(node->children[0]->token); @@ -591,14 +707,6 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) { emit_byte(vm, dest); emit_byte(vm, src1); emit_u32(vm, addr); - } else if (strcmp(opname, "push") == 0) { - emit_opcode(vm, OP_PUSH); - int reg = parse_register(node->children[0]->token); - emit_byte(vm, reg); - } else if (strcmp(opname, "pop") == 0) { - emit_opcode(vm, OP_POP); - int reg = parse_register(node->children[0]->token); - emit_byte(vm, reg); } else if (strcmp(opname, "register-move") == 0) { emit_opcode(vm, OP_REG_MOV); int dest = parse_register(node->children[0]->token); diff --git a/src/vm/opcodes.h b/src/vm/opcodes.h index 71838d7..a3131c5 100644 --- a/src/vm/opcodes.h +++ b/src/vm/opcodes.h @@ -32,8 +32,6 @@ typedef enum { OP_MEMSET_8, /* memset-8 : dest <-> dest+count = src1 as u8 */ OP_MEMSET_16, /* memset-16 : dest <-> dest+count = src1 as u8 */ OP_MEMSET_32, /* memset-32 : dest <-> dest+count = src1 as u32 */ - OP_PUSH, /* push : push const or ref */ - OP_POP, /* pop : pop cosnt or ref */ OP_REG_MOV, /* register-move : dest = src1 */ OP_SYSCALL, /* syscall : src1 src2 src3 src4 more? does a system call based on args */ OP_SLL, /* bit-shift-left : registers[dest] = registers[src1] << registers[src2] */ @@ -93,9 +91,10 @@ typedef enum { #define MAX_REGS 32 typedef struct frame_s { u32 registers[MAX_REGS]; /* R0-R31 */ - u32 rp; /* register pointer (last unused) */ - u32 start; /* start and end of global allocated block */ - u32 end; + u32 start; /* start of memory block */ + u32 end; /* end of memory block */ + u32 return_reg; /* register to store return value in parent */ + u32 heap_mask; /* bitfield: 1 bit per register (R0=bit0, R1=bit1, etc) */ } Frame; typedef enum { @@ -140,13 +139,11 @@ typedef struct vm_s { u32 cp; /* code pointer (last allocated opcode) */ u32 fp; /* frame pointer (current frame) */ u32 sp; /* stack pointer (top of stack) */ - u32 rp; /* return stack pointer (top of stack) */ u32 mp; /* memory pointer (last allocated value) */ u32 dc; /* device count */ i32 flag; /* flag (temporary results like SYSCALL status) */ Frame frames[FRAMES_SIZE]; /* function call frames */ u32 stack[STACK_SIZE]; /* main stack */ - u32 return_stack[STACK_SIZE]; /* return stack (for call recursion) */ Device devices[DEVICES_SIZE]; /* device definitions */ u8 code[CODE_SIZE]; /* code block */ u8 memory[MEMORY_SIZE]; /* memory block */ diff --git a/src/vm/vm.c b/src/vm/vm.c index a24e80f..0716d73 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -49,6 +49,20 @@ return true; \ } while (0) +/* Set heap status for a register in current frame */ +void set_heap_status(VM *vm, u8 reg, bool is_heap) { + if (is_heap) { + vm->frames[vm->fp].heap_mask |= (1 << reg); + } else { + vm->frames[vm->fp].heap_mask &= ~(1 << reg); + } +} + +/* Check if register contains heap pointer */ +bool is_heap_value(VM *vm, u8 reg) { + return (vm->frames[vm->fp].heap_mask >> reg) & 1; +} + u32 str_alloc(VM *vm, Frame *frame, const char *str, u32 length) { u32 str_addr = vm->mp; u32 i = 0; @@ -81,20 +95,89 @@ bool step_vm(VM *vm) { return false; } case OP_CALL: { + i32 i; + u8 N, return_reg, args[MAX_REGS]; + Frame *child; u32 jmp = read_u32(vm, code, vm->pc); /* location of function in code */ vm->pc += 4; - vm->return_stack[vm->rp++] = vm->pc; /* set return address */ - vm->fp++; /* increment to the next free frame */ - vm->frames[vm->fp].start = vm->mp; /* set start of new memory block */ + N = vm->code[vm->pc++]; /* Number of arguments */ + for (i = 0; i < N; i++) { + args[i] = vm->code[vm->pc++]; + } + + return_reg = vm->code[vm->pc++]; + frame->return_reg = return_reg; /* Set current frame's return register */ + + if (vm->sp >= STACK_SIZE) + return false; + vm->stack[vm->sp++] = vm->pc; /* set return address */ + + if (vm->fp >= FRAMES_SIZE - 1) + return false; + vm->fp++; /* increment to the next free frame */ + + child = &vm->frames[vm->fp]; + child->start = vm->mp; /* set start of new memory block */ + child->end = vm->mp; + child->return_reg = 0; + child->heap_mask = 0; + + for (i = 0; i < N; i++) { + u8 src_reg = args[i]; + child->registers[i] = frame->registers[src_reg]; + + if (frame->heap_mask & (1 << src_reg)) { + child->heap_mask |= (1 << i); + } + } + vm->pc = jmp; return true; } case OP_RETURN: { - frame->rp = 0; /* reset register ptr */ - vm->pc = vm->return_stack[--vm->rp]; /* set pc to return address */ - vm->mp = - vm->frames[vm->fp--].start; /* reset memory pointer to start - of old slice, pop the frame */ + u32 ptr, new_ptr, size, value, i; + Frame *child = frame; + Frame *parent = &vm->frames[vm->fp - 1]; + u8 child_return_reg = vm->code[vm->pc++]; + + if (child_return_reg != 0xFF) { + value = child->registers[child_return_reg]; + if (is_heap_value(vm, child_return_reg)) { + ptr = value; + size = *(u32 *)(vm->memory + ptr - 4); + + /* Allocate and copy in parent's frame */ + new_ptr = parent->end; + if (parent->end + size + 4 > MEMORY_SIZE) + return false; + + *(u32 *)(vm->memory + new_ptr) = size; + for (i = 0; i < size - 1; i++) { + (vm->memory + new_ptr + 4)[i] = (vm->memory + ptr + 4)[i]; + } + + parent->end += size + 4; + + /* Update parent's register */ + parent->registers[parent->return_reg] = new_ptr; + parent->heap_mask |= (1 << parent->return_reg); + } else { + /* Non-heap return value */ + parent->registers[parent->return_reg] = value; + parent->heap_mask &= ~(1 << parent->return_reg); + } + } else { + /* If returning "nil", + clear heap bit for parent's return register if valid */ + if (parent->return_reg != 0xFF) { + parent->heap_mask &= ~(1 << parent->return_reg); + } + } + + vm->pc = vm->stack[--vm->sp]; /* set pc to return address */ + vm->mp = child->start; /* reset memory pointer to start + of old slice, pop the frame */ + vm->fp--; return true; } case OP_MALLOC: { @@ -107,6 +190,7 @@ bool step_vm(VM *vm) { size = frame->registers[src1]; write_u32(vm, memory, vm->mp, size); vm->mp += (size + 4); + set_heap_status(vm, dest, true); /* Mark as heap pointer */ return true; } case OP_MEMSET_32: { @@ -114,7 +198,7 @@ bool step_vm(VM *vm) { u8 dest_reg = read_u8(vm, code, vm->pc++); u8 value_reg = read_u8(vm, code, vm->pc++); u8 count_reg = read_u8(vm, code, vm->pc++); - + u32 dest = frame->registers[dest_reg]; u32 value = frame->registers[value_reg]; u32 count = frame->registers[count_reg]; @@ -127,13 +211,12 @@ bool step_vm(VM *vm) { start = dest; end = dest + count; - if (start >= vm->mp || count > vm->mp || - end > vm->mp) { + if (start >= vm->mp || count > vm->mp || end > vm->mp) { vm->flag = 0; return true; } - for (i = start; i < end; i+=4) { + for (i = start; i < end; i += 4) { write_u32(vm, memory, i, value); } @@ -146,7 +229,7 @@ bool step_vm(VM *vm) { u8 dest_reg = read_u8(vm, code, vm->pc++); u8 value_reg = read_u8(vm, code, vm->pc++); u8 count_reg = read_u8(vm, code, vm->pc++); - + u32 dest = frame->registers[dest_reg]; u16 value = (u16)(frame->registers[value_reg]); u32 count = frame->registers[count_reg]; @@ -159,13 +242,12 @@ bool step_vm(VM *vm) { start = dest; end = dest + count; - if (start >= vm->mp || count > vm->mp || - end > vm->mp) { + if (start >= vm->mp || count > vm->mp || end > vm->mp) { vm->flag = 0; return true; } - for (i = start; i < end; i+=2) { + for (i = start; i < end; i += 2) { write_u16(vm, memory, i, value); } @@ -178,7 +260,7 @@ bool step_vm(VM *vm) { u8 dest_reg = read_u8(vm, code, vm->pc++); u8 value_reg = read_u8(vm, code, vm->pc++); u8 count_reg = read_u8(vm, code, vm->pc++); - + u32 dest = frame->registers[dest_reg]; u8 value = (u8)(frame->registers[value_reg]); u32 count = frame->registers[count_reg]; @@ -191,8 +273,7 @@ bool step_vm(VM *vm) { start = dest; end = dest + count; - if (start >= vm->mp || count > vm->mp || - end > vm->mp) { + if (start >= vm->mp || count > vm->mp || end > vm->mp) { vm->flag = 0; return true; } @@ -349,8 +430,8 @@ bool step_vm(VM *vm) { src1 = read_u8(vm, code, vm->pc); vm->pc++; ptr = frame->registers[dest]; - v = frame->registers[src1]; - write_u32(vm, memory, ptr, v); + v = frame->registers[src1]; + write_u32(vm, memory, ptr, v); return true; } case OP_STORE_IND_16: { @@ -412,20 +493,8 @@ bool step_vm(VM *vm) { offset = read_u32(vm, code, vm->pc); vm->pc += 4; ptr = frame->registers[dest]; - v = frame->registers[src1]; - write_u32(vm, memory, (ptr + offset), v); - return true; - } - case OP_PUSH: { - dest = read_u8(vm, code, vm->pc); - vm->pc++; - vm->stack[++vm->sp] = frame->registers[dest]; - return true; - } - case OP_POP: { - dest = read_u8(vm, code, vm->pc); - vm->pc++; - frame->registers[dest] = vm->stack[vm->sp--]; + v = frame->registers[src1]; + write_u32(vm, memory, (ptr + offset), v); return true; } case OP_REG_MOV: { @@ -434,6 +503,13 @@ bool step_vm(VM *vm) { src1 = read_u8(vm, code, vm->pc); vm->pc++; frame->registers[dest] = frame->registers[src1]; + + if (is_heap_value(vm, src1)) { + set_heap_status(vm, dest, true); + } else { + set_heap_status(vm, dest, false); + } + return true; } case OP_JMP: { @@ -478,7 +554,8 @@ bool step_vm(VM *vm) { write_u32(vm, memory, buffer_ptr, dev->size); vm->mp += (dev->size + 4); /* set flag from user */ - vm->flag = dev->ops->open(dev->data, mode, dev->handle, &vm->memory[buffer_ptr + 4], dev->size); + vm->flag = dev->ops->open(dev->data, mode, dev->handle, + &vm->memory[buffer_ptr + 4], dev->size); } else { vm->flag = 1; /* success, no open needed */ } diff --git a/test/add.asm.lisp b/test/add.asm.lisp index 802ac5f..39b6a17 100644 --- a/test/add.asm.lisp +++ b/test/add.asm.lisp @@ -1,35 +1,27 @@ ((code (label main (load-immediate $0 1) - (push $0) - (load-immediate $0 1) - (push $0) - (call &add) - (pop $0) - (int-to-string $1 $0) - (push $1) - (call &pln) + (load-immediate $1 1) + (call &add ($0 $1) $2) + (int-to-string $3 $2) + (call &pln ($3) nil) (halt)) (label add - (pop $0) - (pop $1) (add-int $2 $1 $0) - (push $2) - (return)) + (return $2)) (label pln - (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $1 &terminal-namespace) ; get terminal device (load-immediate $11 0) - (syscall OPEN $0 $0 $11) + (syscall OPEN $1 $1 $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) + (load-offset-32 $7 $1 4) ; load handle + (string-length $2 $0) + (syscall WRITE $7 $0 $2) (string-length $4 $3) (syscall WRITE $7 $3 $4) - (return))) + (return nil))) (data (label terminal-namespace "/dev/term/0") (label new-line "\n"))) diff --git a/test/hello.asm.lisp b/test/hello.asm.lisp index f18a99d..92ad930 100644 --- a/test/hello.asm.lisp +++ b/test/hello.asm.lisp @@ -1,21 +1,19 @@ ((code (label main (load-immediate $1 &hello-str) ; load hello string ptr - (push $1) - (call &pln) + (call &pln ($1) nil) (halt)) ; done (label pln - (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $1 &terminal-namespace) ; get terminal device (load-immediate $11 0) - (syscall OPEN $0 $0 $11) + (syscall OPEN $1 $1 $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) + (load-offset-32 $7 $1 4) ; load handle + (string-length $2 $0) + (syscall WRITE $7 $0 $2) (string-length $4 $3) (syscall WRITE $7 $3 $4) - (return))) + (return nil))) (data (label terminal-namespace "/dev/term/0") (label new-line "\n") diff --git a/test/loop.asm.lisp b/test/loop.asm.lisp index 9d91348..d217c6f 100644 --- a/test/loop.asm.lisp +++ b/test/loop.asm.lisp @@ -18,27 +18,23 @@ (malloc $11 $8) (syscall READ $10 $2 $8 $11) - (push $2) - (call &pln) + (call &pln ($2) nil) (nat-to-string $4 $1) - (push $4) - (call &pln) + (call &pln ($4) nil) (real-to-string $3 $0) - (push $3) - (call &pln) + (call &pln ($3) nil) (halt)) (label pln - (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $1 &terminal-namespace) ; get terminal device (load-immediate $11 0) - (syscall OPEN $0 $0 $11) + (syscall OPEN $1 $1 $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) + (load-offset-32 $7 $1 4) ; load handle + (string-length $2 $0) + (syscall WRITE $7 $0 $2) (string-length $4 $3) (syscall WRITE $7 $3 $4) - (return))) + (return nil))) (data (label terminal-namespace "/dev/term/0") (label help "Enter a string: ") diff --git a/test/malloc.asm.lisp b/test/malloc.asm.lisp index 253c274..44c44de 100644 --- a/test/malloc.asm.lisp +++ b/test/malloc.asm.lisp @@ -5,29 +5,23 @@ (syscall OPEN $0 $0 $11) (load-immediate $1 &help) ; print help message - (push $0) - (push $1) - (call &pln) + (call &pln ($0 $1) nil) (load-immediate $1 32) ; read in a string of max 32 char length (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 + (call &pln ($0 $4) nil) ; print the string (halt)) (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))) + (return nil))) (data (label terminal-namespace "/dev/term/0") (label help "Enter a string: ") diff --git a/test/malloc.ul b/test/malloc.ul new file mode 100644 index 0000000..f1bee58 --- /dev/null +++ b/test/malloc.ul @@ -0,0 +1,13 @@ +str terminal_namespace = ""; +str prompt = "Enter a string: "; + +function main() { + Terminal term(terminal_namespace, 0); + pln(prompt); + pln(terminal.read(32)); + return 0; +} + +function pln(ref Terminal term, str message) { + term.write(message); +} diff --git a/test/paint-bw.rom b/test/paint-bw.rom deleted file mode 100644 index 0bde7ec34573306040f3f6709eea0198e7e6e638..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 695 zcma)3y-EW?5Z=GJy=3op&m$KI%qE3!YAhlMA)=i&*7^pTQU!y)g%(ymhlTiRR&i!c zAZVdm%s2DR-*W^ASYVD8rM~=+7W~<<7U%lgv=JnqjrU@ohUO?~@FxwHtWrc5MU+uR z9ihz>v{u8>6W>D35@acoIz`e#mxqR~Gt{A|vlkm@PKk3VGkv4puAF`l7~{+_WdQio zA=_eW`B+JgL4_ItA<1cTW!sLnDJ4l~2Q$o4%0wTFSdvs;*??quyP;HV2yPsj00aK5 z;}8Zz6Ncd1%oIaw238lXF=_`u&h}_8ERCna$@TnkvbegR&u=FOqFggKcMpp>5lbRA O-#R{DkuNKJU*Q+vF)%#< diff --git a/test/paint.asm.lisp b/test/paint.asm.lisp index c49938d..0b32ee2 100644 --- a/test/paint.asm.lisp +++ b/test/paint.asm.lisp @@ -4,7 +4,7 @@ ; use load immediate because it is a pointer to a string, not a value (load-immediate $0 &screen-namespace) (load-immediate $11 0) - (syscall OPEN $18 $0 $11) ; open(out Plex screen, in namespace, in flags) + (syscall OPEN $18 $0 $11) ; Screen screen = open(namespace, flags) (load-offset-32 $0 $18 4) ; load handle (load-offset-32 $20 $18 8) ; load width @@ -14,31 +14,21 @@ ; open mouse (load-immediate $16 &mouse-namespace) - (syscall OPEN $15 $16 $11) ; open(out Plex mouse, in namespace, in flags) + (syscall OPEN $15 $16 $11) ; Mouse mouse = open(namespace, flags) (load-offset-32 $16 $15 4) ; load handle - ; outline_swatch(screen, BLACK, 1, 1); - (push $21) - (push $20) + ; outline_swatch(screen, BLACK, 1, 1, width); (load-absolute-32 $1 &BLACK) - (push $1) (load-immediate $12 1) - (push $12) (load-immediate $13 1) - (push $13) - (call &draw-outlined-swatch) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) - ; outline_swatch(screen, WHITE, 1, 1); - (push $21) - (push $20) + ; outline_swatch(screen, WHITE, 1, 1, width); (load-absolute-32 $1 &WHITE) - (push $1) (load-immediate $12 21) - (push $12) (load-immediate $13 1) - (push $13) - (call &draw-outlined-swatch) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) ; screen.draw(); (syscall WRITE $0 $21 $22) @@ -57,60 +47,31 @@ (load-immediate $14 20) ; box size ; first row - (push $21) - (push $20) (load-absolute-32 $1 &BLACK) - (push $1) (load-immediate $12 1) - (push $12) (load-immediate $13 1) - (push $13) - (call &draw-outlined-swatch) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) + - (push $21) - (push $20) (load-absolute-32 $1 &WHITE) - (push $1) (load-immediate $12 21) - (push $12) (load-immediate $13 1) - (push $13) - (call &draw-outlined-swatch) - - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) ; row 2 - (push $21) - (push $20) (load-absolute-32 $1 &CHARCOAL) - (push $1) (load-immediate $12 1) - (push $12) (load-immediate $13 21) - (push $13) - (call &draw-outlined-swatch) - - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) (push $21) (push $20) @@ -120,15 +81,10 @@ (push $12) (load-immediate $13 21) (push $13) - (call &draw-outlined-swatch) - - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) ; row 3 (push $21) @@ -139,107 +95,54 @@ (push $12) (load-immediate $13 41) (push $13) - (call &draw-outlined-swatch) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) - (push $21) - (push $20) (load-absolute-32 $1 &ORANGE) - (push $1) (load-immediate $12 21) - (push $12) (load-immediate $13 41) - (push $13) - (call &draw-outlined-swatch) - - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) ; row 3 - (push $21) - (push $20) (load-absolute-32 $1 &YELLOW) - (push $1) (load-immediate $12 1) - (push $12) (load-immediate $13 61) - (push $13) - (call &draw-outlined-swatch) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) - (push $21) - (push $20) (load-absolute-32 $1 &GREEN) - (push $1) (load-immediate $12 21) - (push $12) (load-immediate $13 61) - (push $13) - (call &draw-outlined-swatch) - - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) ; row 4 - (push $21) - (push $20) (load-absolute-32 $1 &BLUE) - (push $1) (load-immediate $12 1) - (push $12) (load-immediate $13 81) - (push $13) - (call &draw-outlined-swatch) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) - - (push $21) - (push $20) (load-absolute-32 $1 &PURPLE) - (push $1) (load-immediate $12 21) - (push $12) (load-immediate $13 81) - (push $13) - (call &draw-outlined-swatch) - - (push $14) ; box_size (20) - (push $13) ; box_y - (push $12) ; box_x - (push $8) ; click_y - (push $7) ; click_x - (push $1) ; color - (call &set-color-if-clicked) + (call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil) + + ; set_color_if_clicked(color, click_x, click_y, box_x, box_y, 20); + (call &set-color-if-clicked ($1 $7 $8 $12 $13 $14) nil) (syscall WRITE $0 $21 $22) @@ -253,7 +156,8 @@ (push $8) ;y (push $1) (push $1) - (call &draw-box) + ; draw_box(brush_size, ) + (call &draw-box ($1 $1 $8 $7 $22 $20 $21) nil) (jump &draw-loop)) diff --git a/docs/paint.ul b/test/paint.ul old mode 100755 new mode 100644 similarity index 78% rename from docs/paint.ul rename to test/paint.ul index 0365d05..f6b936e --- a/docs/paint.ul +++ b/test/paint.ul @@ -1,119 +1,127 @@ -/** - * Constatnts - */ -const str screen_namespace = "/dev/screen/0"; -const str mouse_namespace = "/dev/mouse/0"; -const byte BLACK = 0; -const byte WHITE = 255; -const byte DARK_GRAY = 73; -const byte GRAY = 146; -const byte LIGHT_GRAY = 182; - -byte selected_color = 255; - -interface Device { - nat handle; -} - -plex Screen implements Device { - nat handle; - nat width; - nat height; - nat buffer_size; - byte[] screen_buffer; - - init(str namespace) { - this.handle = open(namespace); - } -} - -plex Mouse implements Device { - u32 handle; - u32 x; - u32 y; - u8 btn1; - u8 btn2; - u8 btn3; - u8 btn4; - u32 size; -} - -/** - * Main function - */ -function main() { - Screen screen(screen_namespace); - screen.open(0); - - Mouse mouse(mouse_namespace); - mouse.open(0); - - outline_swatch(screen, BLACK, 1, 1); - outline_swatch(screen, WHITE, 21, 1); - screen.draw(); - - loop { - mouse.read(); - if (not mouse.left) continue; - - int box_size = 20; - int x = 1; - int y = 1; - byte color = BLACK; - outlined_swatch(screen, color, x, y); - set_color(box_size, x, y, mouse.x, mouse.y, color); - - color = WHITE; - x = 21; - outlined_swatch(screen, color, x, y); - set_color(box_size, x, y, mouse.x, mouse.y, color); - screen.draw(); - - rectangle(screen, selected_color, x, y, 5, 5); - } - exit(0); -} - -/** - * Checks if the click is within the bound and update the selected color if so. - */ -function set_color(int box_size, int bx, int by, int mx, int my, byte color) { - int right = bx + box_size; - int bottom = by + box_size; - - if (mx < bx) return; - if (mx > right) return; - if (my < by) return; - if (my > bottom) return; - - selected_color = color; - - return; -} - -/** - * Draw a color box with a grey outline, if selected use a darker color - */ -function outline_swatch(ref Device screen, byte color, int x, int y) { - byte bg_color = GRAY; - if (selected_color == color) { - bg_color = DARK_GRAY; - } - - rectangle(screen, bg_color, x, y, 20, 20); - rectangle(screen, color, x + 2, y + 2, 17, 17); - return; -} - -/** - * Draw a rectanlge - */ -function rectangle(ref Device screen, byte color, int x, int y, int width, int height) { - int pixel = y * width + x + screen.buffer.ptr + 4; - do (int i = height; i > 0; i--) { - int row = pixel + width; - screen.set(row, color, width); - pixel += width; - } - return; -} +/** + * Constants + */ +const str screen_namespace = "/dev/screen/0"; +const str mouse_namespace = "/dev/mouse/0"; +const byte BLACK = 0; +const byte WHITE = 255; +const byte DARK_GRAY = 73; +const byte GRAY = 146; +const byte LIGHT_GRAY = 182; + +byte selected_color = 255; + +interface Device { + nat handle; +} + +plex Screen implements Device { + nat handle; + nat width; + nat height; + nat buffer_size; + byte[] screen_buffer; + + init(str namespace) { + this.handle = open(namespace); + } +} + +plex Mouse implements Device { + nat handle; + nat x; + nat y; + bool left; + bool right; + bool middle; + bool btn4; + nat size; + + init(str namespace) { + this.handle = open(namespace); + } +} + +/** + * Main function + */ +function main() { + Screen screen(screen_namespace); + screen.open(0); + + Mouse mouse(mouse_namespace); + mouse.open(0); + + outline_swatch(screen, BLACK, 1, 1); + outline_swatch(screen, WHITE, 21, 1); + screen.draw(); + + loop { + mouse.read(); + if (not mouse.left) continue; + + int box_size = 20; + int x = 1; + int y = 1; + byte color = BLACK; + outlined_swatch(screen, color, x, y); + set_color(box_size, x, y, mouse.x, mouse.y, color); + + color = WHITE; + x = 21; + outlined_swatch(screen, color, x, y); + set_color(box_size, x, y, mouse.x, mouse.y, color); + screen.draw(); + + rectangle(screen, selected_color, x, y, 5, 5); + } + exit(0); +} + +/** + * Checks if the click is within the bound and update the selected color if so. + */ +function set_color(int box_size, int bx, int by, int mx, int my, byte color) { + int right = bx + box_size; + int bottom = by + box_size; + + if (mx < bx) return; + if (mx > right) return; + if (my < by) return; + if (my > bottom) return; + + selected_color = color; + + return; +} + +/** + * Draw a color box with a grey outline, if selected use a darker color + */ +function outline_swatch(ref Device screen, byte color, int x, int y) { + byte bg_color = GRAY; + if (selected_color == color) { + bg_color = DARK_GRAY; + } + + rectangle(screen, bg_color, x, y, 20, 20); + rectangle(screen, color, x + 2, y + 2, 17, 17); + return; +} + +/** + * Draw a rectangle + */ +function rectangle(ref Device screen, byte color, int x, int y, int width, int height) { + // we need unsafe because we are using `ptr` and `memset` directly + // unsafe takes the guardrails off and allows you to access/modify memory directly + unsafe { + int pixel = y * width + x + screen.buffer.ptr + 4; + do (int i = height; i > 0; i--) { + int row = pixel + width; + memset(screen.buffer.ptr, row, color, width); + pixel += width; + } + } + return; +} diff --git a/test/simple.asm.lisp b/test/simple.asm.lisp index ed58a15..8aed424 100644 --- a/test/simple.asm.lisp +++ b/test/simple.asm.lisp @@ -4,21 +4,19 @@ (load-absolute-32 $1 &y) (add-real $2 $1 $0) (real-to-string $3 $2) - (push $3) - (call &pln) + (call &pln ($3) nil) (halt)) (label pln - (load-immediate $0 &terminal-namespace) ; get terminal device + (load-immediate $1 &terminal-namespace) ; get terminal device (load-immediate $11 0) - (syscall OPEN $0 $0 $11) + (syscall OPEN $1 $1 $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) + (load-offset-32 $7 $1 4) ; load handle + (string-length $2 $0) + (syscall WRITE $7 $0 $2) (string-length $4 $3) (syscall WRITE $7 $3 $4) - (return))) + (return nil))) (data (label terminal-namespace "/dev/term/0") (label new-line "\n") (label x 1.0) diff --git a/test/window.asm.lisp b/test/window.asm.lisp index b80216f..5b2c0e5 100644 --- a/test/window.asm.lisp +++ b/test/window.asm.lisp @@ -1,10 +1,5 @@ ((code (label main - ; open terminal for debug - (load-immediate $32 &terminal-namespace) - (load-immediate $11 0) - (syscall OPEN $32 $32 $11) - ; Open screen ; use load immediate because it is a pointer to a string, not a value (load-immediate $0 &screen-namespace) @@ -13,31 +8,23 @@ (load-offset-32 $0 $18 4) ; load handle (nat-to-string $5 $0) - (push $32) - (push $5) - (call &pln) + (call &pln ($5) nil) (load-offset-32 $20 $18 8) ; load width (nat-to-string $5 $20) - (push $32) - (push $5) - (call &pln) + (call &pln ($5) nil) (load-offset-32 $22 $18 12) ; load size (nat-to-string $5 $22) - (push $32) - (push $5) - (call &pln) + (call &pln ($5) nil) (load-immediate $1 16) ; offset for screen buffer (add-nat $21 $18 $1) (nat-to-string $5 $21) - (push $32) - (push $5) - (call &pln) + (call &pln ($5) nil) ; open mouse (load-immediate $16 &mouse-namespace) @@ -72,18 +59,17 @@ (jump &draw-loop)) (halt)) - (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))) + (label pln + (load-immediate $1 &terminal-namespace) ; get terminal device + (load-immediate $11 0) + (syscall OPEN $1 $1 $11) + (load-immediate $3 &new-line) + (load-offset-32 $7 $1 4) ; load handle + (string-length $2 $0) + (syscall WRITE $7 $0 $2) + (string-length $4 $3) + (syscall WRITE $7 $3 $4) + (return nil))) (data (label screen-namespace "/dev/screen/0") (label mouse-namespace "/dev/mouse/0")