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 0000000..1bfaf27 Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/undar.png b/docs/undar.png new file mode 100644 index 0000000..40cd807 Binary files /dev/null and b/docs/undar.png differ 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 0bde7ec..0000000 Binary files a/test/paint-bw.rom and /dev/null differ 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")