Compare commits

...

25 Commits

Author SHA1 Message Date
zongor 0fd2656704 Code cleanup 2025-12-08 21:43:56 -08:00
zongor 80d9c67b9e Code cleanup 2025-12-08 21:42:59 -08:00
zongor edb10db545 Fix documentation 2025-12-07 15:31:41 -08:00
zongor 0d30ea292a Fix assembler, update tests, update roms, add back nogui mode for speed. 2025-12-07 15:29:49 -08:00
zongor 07528b1f3f fix silly missing `get_reg` error. all non gui ones are working now 2025-12-03 21:45:03 -08:00
zongor 9218051b87 Add better debugging for assembler. Simplify symbol table creation (somewhat). 2025-12-01 23:20:36 -08:00
zongor f901dafa2b WIP Code generation, 'simple' works 2025-11-30 23:22:25 -08:00
zongor 2e5eb03227 remove old assembler, finish symbol table pass, start working on code gen 2025-11-29 19:44:54 -08:00
zongor 733dfc0364 wip, add all the sizing for symbols 2025-11-29 13:52:12 -08:00
zongor 89a17f06f0 start making changes to init_vm 2025-11-29 10:50:29 -08:00
zongor b638fedd81 Update readme 2025-11-29 09:26:35 -08:00
zongor 55b6145064 Add semicolon 2025-11-29 09:21:12 -08:00
zongor 32ae0eeb8f fix readme 2025-11-29 08:28:17 -08:00
zongor 9d2053aef0 wip symbol table, remove malloc_imm, fix docs 2025-11-29 08:26:12 -08:00
zongor 4f73339efb wip symbols + offsets 2025-11-29 00:50:25 -08:00
zongor 7b8059e6c7 refactor strings, add loop to lexer 2025-11-24 21:40:57 -08:00
zongor 6f47ee7ea1 wip new assembler; start of compiler 2025-11-23 23:48:26 -08:00
zongor 96deaa9ff7 more syntax refinement 2025-11-22 13:52:31 -08:00
zongor 32365a8895 wip assembler v2 2025-11-10 23:45:09 -08:00
zongor 0113411f89 rename some things, create new parser defs. 2025-11-09 22:23:55 -08:00
zongor c90f236ab3 Refactor and rename to align with new IR better 2025-11-09 13:22:46 -08:00
zongor 99e2f2c0c3 some experimental syntax 2025-11-08 23:44:42 -08:00
zongor a1197e8b43 fix small bug 2025-11-08 22:29:32 -08:00
zongor 1311659bed Add new IR language, update docs 2025-11-08 22:19:18 -08:00
zongor 5d2aa1ad22 wip compiler stuff 2025-11-04 17:09:55 -08:00
73 changed files with 5514 additions and 3557 deletions

View File

@ -2,6 +2,9 @@
PLATFORM ?= linux
BUILD_MODE ?= debug # 'debug' or 'release'
# Ensure BUILD_MODE is fixed before any conditionals
$(eval BUILD_MODE := $(or $(BUILD_MODE),debug))
# --- DIRECTORIES ---
SRC_DIR := src
BUILD_DIR := build/$(PLATFORM)
@ -80,20 +83,19 @@ endif
VM_SOURCES := \
$(SRC_DIR)/vm/vm.c \
$(SRC_DIR)/vm/device.c \
$(SRC_DIR)/vm/fixed.c \
$(SRC_DIR)/vm/libc.c
ifeq ($(BUILD_MODE), release)
PLATFORM_SOURCE := $(ARCH_DIR)/main.c \
$(ARCH_DIR)/devices.c\
$(SRC_DIR)/tools/parser.c \
$(SRC_DIR)/tools/lexer.c \
$(SRC_DIR)/tools/assembler.c
$(SRC_DIR)/tools/assembler/lexer.c \
$(SRC_DIR)/tools/assembler/assembler.c
else
PLATFORM_SOURCE := $(ARCH_DIR)/main.c \
$(ARCH_DIR)/devices.c \
$(SRC_DIR)/tools/parser.c \
$(SRC_DIR)/tools/lexer.c \
$(SRC_DIR)/tools/assembler.c
$(SRC_DIR)/tools/assembler/lexer.c \
$(SRC_DIR)/tools/assembler/assembler.c
endif
# --- OBJECT FILES ---
@ -108,14 +110,12 @@ DEPS := $(VM_OBJS:.o=.d) $(PLATFORM_OBJ:.o=.d)
# Default target
all: $(TARGET)
# 'debug' target — just set BUILD_MODE and build
debug: BUILD_MODE=debug
debug: $(TARGET)
# 'release' target — just set BUILD_MODE and build
release: BUILD_MODE=release
release: $(TARGET)
debug:
$(MAKE) BUILD_MODE=debug all
release:
$(MAKE) BUILD_MODE=release all
# --- COMPILE VM CORE (freestanding) ---
$(BUILD_DIR)/vm/%.o: $(SRC_DIR)/vm/%.c
@mkdir -p $(dir $@)
@ -156,11 +156,11 @@ clean-all:
# --- TEST COMPILATION TARGET ---
# Compiles all .asm.lisp test files to .rom using the debug VM executable
# Usage: make compile-tests PLATFORM=linux
compile-tests: $(BUILD_DIR)/undar-$(PLATFORM)$(TARGET_SUFFIX)
# Usage: make tests PLATFORM=linux
tests: $(BUILD_DIR)/undar-$(PLATFORM)$(TARGET_SUFFIX)
@echo "Compiling test assembly files for $(PLATFORM)..."
@for f in ./test/*.asm.lisp; do \
base=$$(basename "$$f" .asm.lisp); \
@for f in ./test/*.ul.ir; do \
base=$$(basename "$$f" .ul.ir); \
echo " [$$base] $$f -> ./test/$$base.rom"; \
$(BUILD_DIR)/undar-$(PLATFORM)$(TARGET_SUFFIX) "$$f" -o "./test/$$base.rom"; \
done
@ -184,4 +184,4 @@ help:
@echo ""
@echo "Output:"
@echo " Linux: build/linux/undar-linux-<debug|release>"
@echo " Web: build/emscripten/undar.html (+ .js, .wasm)"
@echo " Web: build/emscripten/undar.html (+ .js, .wasm)"

View File

@ -17,11 +17,11 @@
* Undâr
Undâr is a programming language for the purpose of creating 3D games and graphical user interfaces that work on constrained systems, microcontrollers, retro consoles, and the web using emscripten. The language emphasizes hardware longevity, energy efficiency, and the preservation of digital art and games for future generations.
Undâr is a programming language for the purpose of creating 3D games and graphical user traits that work on constrained systems, microcontrollers, retro consoles, and the web using emscripten. The language emphasizes hardware longevity, energy efficiency, and the preservation of digital art and games for future generations.
It has an internal REPL that allows for quick development as well as the ability to dump the program to a binary rom for preserving that program/game/etc.
It runs on the =Reality Engine=, a VM written in freestanding C89, has a CISC like instruction format of one byte opcode and a variable byte operand. 32 general purpose registers.
It runs on the =Reality Engine=, a VM written in freestanding C89, has a CISC like instruction format of one byte opcode and a variable byte operand. 32 local variables per frame.
* Philosophy
@ -49,36 +49,44 @@ git clone https://git.alfrescocavern.com/zongor/undar-lang.git
cd undar-lang && make
#+END_SRC
=Sċieppan= is a minimal assembler that uses s-expressions.
You can view some examples in the =.asm.lisp= files in =/test=
The Undâr compiler will be written in Sċieppan, as well as core VM tests.
=Sċieppan= is a intermediate representation.
You can view some examples in the =.ul.ir= files in =/test=
**Sample Program: =hello.asm.lisp=**
**Sample Program: =hello.ul.ir=**
#+BEGIN_SRC lisp
((code
(label main
(load-immediate $1 &hello-str) ; load hello string ptr
(call &pln ($1) nil)
(halt)) ; done
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label new-line "\n")
(label hello-str "nuqneH 'u'?")))
#+BEGIN_SRC sh
global str terminal_namespace = "/dev/term/0";
global str new_line = "\n";
global str hello = "nuqneH 'u'?";
function main ()
str msg $0;
load_address hello -> msg;
call pln (msg);
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;
#+END_SRC
#+BEGIN_SRC sh
./build/linux/undar-linux-debug ./test/hello.asm.lisp
./build/linux/undar-linux-debug ./test/hello.ul.ir
#+END_SRC
Running the compiler without arguments will put it in "REPL" mode. It will function similar to a LISP repl.
@ -89,33 +97,49 @@ memory is managed via frame based arenas. function scopes defines a memory frame
heap allocations using the internal malloc opcode push pointers within this frame. when a frame exits, the pointer is reset like stack based gc.
#+BEGIN_SRC lisp
((code
(label main
(load-immediate $0 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $0 $0 $11)
#+BEGIN_SRC sh
global str terminal_namespace = "/dev/term/0";
global str prompt = "Enter a string:";
global str new_line = "\n";
(load-immediate $1 &help) ; print help message
(call &pln ($0 $1) nil)
function main ()
int in_mode $11;
str in_term $10;
(load-immediate $1 32) ; read in a string of max 32 char length
(malloc $4 $1) ; allocate memory for the string
(syscall READ $0 $4 $1) ; read the string
load_address terminal_namespace -> in_term;
load_immediate 0 -> in_mode;
syscall OPEN in_term in_mode in_term; // Terminal term = open("/dev/term/0", 0);
(call &pln ($0 $4) nil) ; print the string
(halt))
(label pln
(load-immediate $3 &new-line)
(string-length $2 $1)
(syscall WRITE $0 $1 $2)
(string-length $4 $3)
(syscall WRITE $0 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label help "Enter a string: ")
(label new-line "\n")))
load_address prompt -> $7;
string_length $7 -> $8;
syscall WRITE in_term $7 $8; // print prompt
str user_string $9;
load_immediate 32 -> $8;
malloc $8 -> user_string;
syscall READ in_term user_string $8; // read in max 32 byte string
call pln (user_string);
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;
#+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.

View File

@ -10,6 +10,16 @@
* Roadmap
** Fixes for devices
Devices should be moved into the Tunnel concept
Make it so that instead of returning the whole plex from the OPEN syscall we only return the handle
We should remove the "refesh" options and make it so that "stat"
Stat is the one that actually returns the plex with that info
** Example: Hello world (=hello.ul=)
*WIP syntax, not final implementation**
@ -170,7 +180,7 @@ function main(int argc, str[] argv) {
}
}
exits("Client Closed Successfully");
exit(0);
}
#+END_SRC
@ -184,7 +194,7 @@ function main(int argc, str[] argv) {
Player[] players = [Player("user", [0.0, 0.0, 0.0], RED)];
while (running) {
if (Client client = s.accept("players")) {
if (Message message = client.get()) {
if (Message message = client.read()) {
if (message.t == "close") {
client.close();
running = false;
@ -196,7 +206,7 @@ function main(int argc, str[] argv) {
}
}
}
exits(nil);
exit(0);
}
#+END_SRC
@ -204,7 +214,7 @@ function main(int argc, str[] argv) {
A =plex= is a structure which, works like a struct but syntactically looks like a class. the naming of "plex" comes from douglas ross's paper "a generalized technique for symbol manipulation and numerical calculation". It is used instead of "class" or "struct" is to make a break from the historical baggage of "classes". unlike classes it does not describe a object in real life and copy it, but it allows for a convenient way to mediate states and handling. i.e. compositions + encodings instead of oop/polymorphisms. for example, instead of having a struct with a bool flag in it (like for a game alive/dead), we can create a multilist where the structs move from a "alive" list to a "dead" list; or things like that. instances of a plex in memory are called "atoms".
Plexes support permacomputing by allowing the developer to use lower level interfaces with a friendly syntax.
Plexes support permacomputing by allowing the developer to use lower level traits with a friendly syntax.
**WIP syntax, not final implementation**
@ -250,7 +260,7 @@ core devices include
- terminal
- tunnel
Devices are accessed via a namespace "path/to/device" and are implemented outside of the VM's runtime. This allows for the interface of the system to be the same within the VM but allow for specific variations on a concept depending on the device it is running on.
Devices are accessed via a namespace "path/to/device" and are implemented outside of the VM's runtime. This allows for the trait of the system to be the same within the VM but allow for specific variations on a concept depending on the device it is running on.
*** Immediate Mode GUI
@ -262,7 +272,7 @@ UI elements are draw directly on the canvas and syntactically are spacial to sho
** Tunnels: Unified I/O (Plan 9 / 9P-Inspired)
Tunnels are an abstraction called a tunnel which acts like a device like a screen, mouse, etc. that is inspired by Plan9. Plan 9 was an operating system developed at Bell Labs that treated all resources (including network connections and UI elements) as files in a hierarchical namespace. it allows files, web requests, sockets, etc. to be viewed through a simple unified interface. it is very similar to the 9p protocol where everything is a file in a filesystem, it just might be a file located on a server halfway across the world or on another planet. the only thing that is the difference is that it takes longer to read the file on mars compared to the file on your local system. the way the device system works is that it is written by a developer in another language like c, but from Undâr's pov the interface remains the same, its just a namespaced device like all the other devices, so that it is easy to understand by a new developer. so it is comparable with tcp/udp sockets or something esoteric like a lorawan network, or some new communication method that hasn't been invented yet.
Tunnels are an abstraction called a tunnel which acts like a device like a screen, mouse, etc. that is inspired by Plan9. Plan 9 was an operating system developed at Bell Labs that treated all resources (including network connections and UI elements) as files in a hierarchical namespace. it allows files, web requests, sockets, etc. to be viewed through a simple unified trait. it is very similar to the 9p protocol where everything is a file in a filesystem, it just might be a file located on a server halfway across the world or on another planet. the only thing that is the difference is that it takes longer to read the file on mars compared to the file on your local system. the way the device system works is that it is written by a developer in another language like c, but from Undâr's pov the trait remains the same, its just a namespaced device like all the other devices, so that it is easy to understand by a new developer. so it is comparable with tcp/udp sockets or something esoteric like a lorawan network, or some new communication method that hasn't been invented yet.
**WIP syntax, not final implementation**

View File

@ -1,4 +0,0 @@
(lambda add-two (a b)
(return (+ a b)))
(print (to-string (add-two 1 1)))

View File

@ -1,6 +0,0 @@
(lambda fib (n)
(if (n < 2)
(return n))
(return (+ (fib (- n 2)) (fib (- n 1)))))
(print (fib 35))

View File

@ -1 +0,0 @@
(print "nuqneH 'u'?")

View File

@ -41,9 +41,9 @@ print_section "zre ($FILENAME.t.ul)"
echo "test input" | time ../build/old/zre -t "$FILENAME.ul"
# Undâr Implementation (inline assembled)
print_section "undar ($FILENAME.asm.lisp)"
echo "test input" | time ../build/linux/undar-linux-release "../test/$FILENAME.asm.lisp"
print_section "undar ($FILENAME.ul.ir)"
echo "test input" | time ../build/linux/undar-linux-release -t "../test/$FILENAME.ul.ir"
# Undâr Implementation (binary)
print_section "undar ($FILENAME.rom)"
echo "test input" | time ../build/linux/undar-linux-release "../test/$FILENAME.rom"
echo "test input" | time ../build/linux/undar-linux-release -t "../test/$FILENAME.rom"

View File

@ -1 +0,0 @@
(print (+ 1.0 2.0))

View File

@ -1 +1 @@
print(tostring(1 + 2))
print(tostring(1.0 + 2.0))

View File

@ -1 +1 @@
print((1 + 2) . "\n");
print((1.0 + 2.0) . "\n");

View File

@ -1 +1 @@
print(str(1 + 2))
print(str(1.0 + 2.0))

View File

@ -1,2 +1,2 @@
let sum = 1 + 2;
let sum = 1.0 + 2.0;
print sum;

View File

@ -261,7 +261,7 @@ scope closes the tunnel
note the plex must always be of a plex which is "tunnel-able"
i.e. Files, sockets, etc
Tunnels have almost the same interface as 9p since they are closely
Tunnels have almost the same trait as 9p since they are closely
based on 9p.
*** transplexs for tunnels

View File

@ -1,8 +1,7 @@
#include "devices.h"
#include <stdio.h>
#include <string.h>
#include <emscripten.h>
#include <emscripten/html5.h>
#include <unistd.h>
i32 console_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size) {
USED(mode);
@ -141,10 +140,14 @@ i32 mouse_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size) {
}
i32 mouse_read(void *data, u8 *buffer, u32 size) {
MouseDeviceData *mouse_data = (MouseDeviceData *)data;
USED(data);
USED(buffer);
USED(size);
return -1;
}
if (size < 12)
return -1;
i32 mouse_refresh(void *data, u8 *buffer) {
MouseDeviceData *mouse_data = (MouseDeviceData *)data;
u8 *info = (u8 *)buffer;
memcpy(&info[4], &mouse_data->x, sizeof(u32));

View File

@ -26,16 +26,13 @@ typedef struct mouse_device_data_s {
u8 btn2;
u8 btn3;
u8 btn4;
u32 size;
} MouseDeviceData;
/* Keyboard device data */
typedef struct keyboard_device_data_s {
u32 handle;
const u8 *keys;
i32 key_count;
u32 pos;
u32 size;
const u8 *keys;
} KeyboardDeviceData;
/* Console device data */
@ -52,6 +49,7 @@ i32 screen_ioctl(void *data, u32 cmd, const u8 *buffer);
i32 mouse_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size);
i32 mouse_read(void *data, u8 *buffer, u32 size);
i32 mouse_refresh(void *data, u8 *buffer);
i32 mouse_write(void *data, const u8 *buffer, u32 size);
i32 mouse_close(void *data);

View File

@ -18,7 +18,8 @@ static DeviceOps mouse_ops = {.open = mouse_open,
.read = mouse_read,
.write = mouse_write,
.close = mouse_close,
.ioctl = nil};
.ioctl = nil,
.refresh = mouse_refresh};
static DeviceOps console_device_ops = {
.open = console_open,
@ -65,7 +66,6 @@ void mainloop() {
mouse_data.btn4 = 0;
break;
// Touch events (map to mouse_data as left-click equivalent)
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP: {
@ -76,8 +76,6 @@ void mainloop() {
mouse_data.x = (int)x;
mouse_data.y = (int)y;
// Only treat the first finger as mouse input (ignore multi-touch beyond 1
// finger)
if (event.tfinger.fingerId == 0) {
if (event.type == SDL_FINGERDOWN || event.type == SDL_FINGERMOTION) {
mouse_data.btn1 = 1;
@ -90,9 +88,8 @@ void mainloop() {
}
}
// Run VM for a fixed number of cycles or a time slice
int cycles_this_frame = 0;
int max_cycles_per_frame = 2000; // Adjust this value
int max_cycles_per_frame = 2000;
while (cycles_this_frame < max_cycles_per_frame) {
if (!step_vm(&vm)) {
emscripten_cancel_main_loop();
@ -101,13 +98,12 @@ void mainloop() {
cycles_this_frame++;
}
// Render only if the screen buffer was updated AND at a reasonable rate
if (screen_data.update) {
if (screen_data.renderer && screen_data.texture) {
SDL_RenderCopy(screen_data.renderer, screen_data.texture, NULL, NULL);
SDL_RenderPresent(screen_data.renderer);
}
screen_data.update = false; // Reset flag after rendering
screen_data.update = false;
}
}
@ -118,7 +114,6 @@ bool loadVM(const char *filename, VM *vm) {
return false;
}
// Read VM state
if (fread(&vm->pc, sizeof(u32), 1, file) != 1 ||
fread(&vm->cp, sizeof(u32), 1, file) != 1 ||
fread(&vm->fp, sizeof(u32), 1, file) != 1 ||
@ -131,14 +126,12 @@ bool loadVM(const char *filename, VM *vm) {
return false;
}
// Read code section
if (fread(vm->code, 1, vm->cp, file) != vm->cp) {
printf("Failed to read code section\n");
fclose(file);
return false;
}
// Read memory section
if (fread(vm->memory, 1, vm->mp, file) != vm->mp) {
printf("Failed to read memory section\n");
fclose(file);
@ -177,17 +170,15 @@ int main(int argc, char **argv) {
mouse_data.btn2 = 0;
mouse_data.btn3 = 0;
mouse_data.btn4 = 0;
mouse_data.size = 16;
// Register devices
vm_register_device(&vm, "/dev/screen/0", "screen", &screen_data,
&screen_ops, 16 + screen_data.buffer_size);
vm_register_device(&vm, "/dev/mouse/0", "mouse", &mouse_data, &mouse_ops,
mouse_data.size);
16);
vm_register_device(&vm, "/dev/term/0", "terminal", &console_data,
&console_device_ops, 4);
// Set up main loop
emscripten_set_main_loop(mainloop, 0, 1);
return 0;
}

View File

@ -1,98 +0,0 @@
#include "devices.h"
#include <unistd.h>
#include <string.h>
i32 console_open(void *data, u32 mode) {
USED(mode);
USED(data);
/* Nothing to open — stdin/stdout are always available */
return 0; /* Success */
}
i32 console_read(void *data, u8 *buffer, u32 size) {
USED(data);
ssize_t result = read(STDIN_FILENO, buffer, size);
if (result < 0) return -1; /* Error */
return (i32)result; /* Bytes read */
}
i32 console_write(void *data, const u8 *buffer, u32 size) {
USED(data);
ssize_t result = write(STDOUT_FILENO, buffer, size);
if (result < 0) return -1; /* Error */
return (i32)result; /* Bytes written */
}
i32 console_close(void *data) {
USED(data);
/* Nothing to close — stdin/stdout are process-owned */
return 0;
}
i32 console_ioctl(void *data, u32 cmd, void *args) {
USED(data);
USED(cmd);
USED(args);
return -1; /* Unsupported */
}
i32 screen_open(void *data, u32 mode) {
USED(mode);
ScreenDeviceData *screen = (ScreenDeviceData *)data;
USED(screen);
return 0;
}
i32 screen_read(void *data, u8 *buffer, u32 size) {
USED(data);
USED(buffer);
USED(size);
return -1;
}
i32 screen_write(void *data, const u8 *buffer, u32 size) {
ScreenDeviceData *screen = (ScreenDeviceData *)data;
if (size > screen->framebuffer_size * sizeof(u8)) {
return -1;
}
memcpy(&screen->vm->memory[screen->framebuffer_pos], buffer, size);
return 0;
}
i32 screen_close(void *data) {
ScreenDeviceData *screen = (ScreenDeviceData *)data;
USED(screen);
return 0;
}
i32 keyboard_open(void *data, u32 mode) {
USED(data);
USED(mode);
return 0;
}
i32 keyboard_read(void *data, u8 *buffer, u32 size) {
KeyboardDeviceData *kbd = (KeyboardDeviceData *)data;
if (size < (u32)kbd->key_count)
return -1;
memcpy(buffer, kbd->keys, kbd->key_count);
return 0;
}
i32 keyboard_write(void *data, const u8 *buffer, u32 size) {
USED(data);
USED(buffer);
USED(size);
return -1; /* not writable */
}
i32 keyboard_close(void *data) {
USED(data);
return 0;
}

View File

@ -1,33 +0,0 @@
#include "../../vm/device.h"
#include "../../vm/vm.h"
/* Screen device data */
typedef struct screen_device_data_s {
u32 width;
u32 height;
u32 framebuffer_pos;
u32 framebuffer_size;
VM* vm;
} ScreenDeviceData;
/* Keyboard device data */
typedef struct keyboard_device_data_s {
const u8 *keys;
i32 key_count;
} KeyboardDeviceData;
i32 screen_open(void *data, u32 mode);
i32 screen_read(void *data, u8 *buffer, u32 size);
i32 screen_write(void *data, const u8 *buffer, u32 size);
i32 screen_close(void *data);
i32 keyboard_open(void *data, u32 mode);
i32 keyboard_read(void *data, u8 *buffer, u32 size);
i32 keyboard_write(void *data, const u8 *buffer, u32 size);
i32 keyboard_close(void *data);
i32 console_open(void *data, u32 mode);
i32 console_read(void *data, u8 *buffer, u32 size);
i32 console_write(void *data, const u8 *buffer, u32 size);
i32 console_close(void *data);
i32 console_ioctl(void *data, u32 cmd, void *args);

View File

@ -1,264 +0,0 @@
#include "../../tools/test.h"
#include "../../tools/parser.h"
#include "../../vm/vm.h"
#include "devices.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#define MAX_SRC_SIZE 16384
static DeviceOps screen_ops = {.open = screen_open,
.read = screen_read,
.write = screen_write,
.close = screen_close,
.ioctl = nil};
static DeviceOps console_device_ops = {
.open = console_open,
.read = console_read,
.write = console_write,
.close = console_close,
.ioctl = console_ioctl,
};
static ScreenDeviceData screen_data = {0};
void compileFile(const char *path, VM *vm) {
USED(vm);
FILE *f = fopen(path, "rb");
if (!f) {
perror("fopen");
exit(1);
}
static char source[MAX_SRC_SIZE + 1];
fseek(f, 0, SEEK_END);
long len = ftell(f);
fseek(f, 0, SEEK_SET);
if (len >= MAX_SRC_SIZE) {
perror("source is larget than buffer");
exit(1);
}
size_t read = fread(source, 1, len, f);
source[read] = '\0';
fclose(f);
}
void repl(VM *vm) {
USED(vm);
char buffer[1024 * 10] = {0}; // Larger buffer for multi-line input
char line[1024];
for (;;) {
// Count current parentheses balance
int paren_balance = 0;
for (int i = 0; buffer[i]; i++) {
if (buffer[i] == '(') paren_balance++;
else if (buffer[i] == ')') paren_balance--;
}
// Show appropriate prompt
if (paren_balance > 0) {
printf(".. "); // Continuation prompt when unbalanced
} else {
printf("> "); // Normal prompt when balanced
}
fflush(stdout);
if (!fgets(line, sizeof(line), stdin)) {
printf("\n");
break;
}
// Append the new line to buffer
strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1);
// Recalculate balance after adding new line
paren_balance = 0;
for (int i = 0; buffer[i]; i++) {
if (buffer[i] == '(') paren_balance++;
else if (buffer[i] == ')') paren_balance--;
}
// Only parse when parentheses are balanced
if (paren_balance == 0) {
// Check if buffer has actual content (not just whitespace)
int has_content = 0;
for (int i = 0; buffer[i]; i++) {
if (!isspace(buffer[i])) {
has_content = 1;
break;
}
}
if (has_content) {
ExprNode *ast = expr_parse(buffer, strlen(buffer));
if (!ast) {
printf("Parse failed.\n");
} else {
printf("AST:\n");
expr_print(ast, 0);
expr_free(ast);
}
}
// Reset buffer for next input
buffer[0] = '\0';
}
// If unbalanced, continue reading more lines
}
exit(0);
}
enum FlagType {
FLAG_NONE = 0,
FLAG_TEST_MODE = 1,
FLAG_DUMP_ROM = 2,
FLAG_GUI_MODE = 4,
};
#define MAX_INPUT_FILES 16 /* Adjust based on your system's constraints */
struct CompilerConfig {
u32 flags;
char *input_files[MAX_INPUT_FILES];
i32 input_file_count;
};
i32 parse_arguments(i32 argc, char *argv[], struct CompilerConfig *config) {
i32 i;
/* Initialize config */
config->flags = 0;
config->input_file_count = 0;
/* Zero out input files array for safety */
for (i = 0; i < MAX_INPUT_FILES; i++) {
config->input_files[i] = NULL;
}
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
/* Long and short flag handling */
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gui") == 0) {
config->flags |= FLAG_GUI_MODE;
} else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--test") == 0) {
config->flags |= FLAG_TEST_MODE;
} else if (strcmp(argv[i], "-o") == 0 ||
strcmp(argv[i], "--dump-rom") == 0) {
config->flags |= FLAG_DUMP_ROM;
} else {
fprintf(stderr, "Unknown flag: %s\n", argv[i]);
return -1;
}
} else if (strstr(argv[i], ".ul") != NULL) {
/* Collect input files */
if (config->input_file_count >= MAX_INPUT_FILES) {
fprintf(stderr, "Too many input files. Maximum is %lld\n",
MAX_INPUT_FILES);
return -1;
}
config->input_files[config->input_file_count++] = argv[i];
}
}
return 0;
}
/*
* This needs to be done dynamically eventually
*/
void register_sdl_devices(VM *vm) {
screen_data.vm = vm;
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
screen_data.width = w.ws_row * 2;
screen_data.height = w.ws_col;
screen_data.framebuffer_size = w.ws_row * w.ws_col * 2;
screen_data.framebuffer_pos = vm->mp;
vm->mp += screen_data.framebuffer_size; /* advance memory pointer */
vm_register_device(vm, "/dev/screen/0", "screen", &screen_data, &screen_ops);
}
i32 main(i32 argc, char *argv[]) {
struct CompilerConfig config = {0};
if (parse_arguments(argc, argv, &config) != 0) {
fprintf(stderr, "Usage: %s [-d] [-g] [-o] <file1.ul> [file2.ul] ...\n",
argv[0]);
return 64;
}
VM vm = {0};
if (config.input_file_count == 0) {
repl(&vm);
} else {
for (i32 j = 0; j < config.input_file_count; j++) {
compileFile(config.input_files[j], &vm);
}
if (config.flags & FLAG_DUMP_ROM) {
FILE *file = fopen("memory_dump.bin", "wb");
if (!file) {
perror("Failed to open file");
return EXIT_FAILURE;
}
size_t code_written = fwrite(vm.code, 1, CODE_SIZE, file);
if (code_written != CODE_SIZE) {
fprintf(stderr, "Incomplete code write: %zu bytes written out of %llu\n",
code_written, CODE_SIZE);
fclose(file);
return EXIT_FAILURE;
}
size_t memory_written = fwrite(vm.memory, 1, MEMORY_SIZE, file);
if (memory_written != MEMORY_SIZE) {
fprintf(stderr,
"Incomplete memory write: %zu bytes written out of %llu\n",
memory_written, MEMORY_SIZE);
fclose(file);
return EXIT_FAILURE;
}
fclose(file);
}
}
bool running = true;
vm_register_device(&vm, "/dev/term/0", "terminal", nil, &console_device_ops);
if (config.flags & FLAG_GUI_MODE) {
u32 i;
register_sdl_devices(&vm);
while (running) {
step_vm(&vm);
for (i = 0; i < vm.dc; i++) {
Device *dev = &vm.devices[i];
if (strcmp(dev->type, "screen") == 0) {
//ScreenDeviceData *screen = (ScreenDeviceData *)dev->data;
//vm.memory[screen->framebuffer_pos],
break;
}
}
}
} else {
while (running) {
running = step_vm(&vm);
}
}
return 0;
}

View File

@ -29,6 +29,11 @@ i32 console_read(void *data, u8 *buffer, u32 size) {
i32 console_write(void *data, const u8 *buffer, u32 size) {
USED(data);
if (size > MEMORY_SIZE) {
return 0;
}
for (u32 i = 0; i < size; i++) {
putchar(buffer[i]);
}

View File

@ -1,11 +1,10 @@
#include "../../tools/assembler.h"
#include "../../tools/lexer.h"
#include "../../tools/parser.h"
#include "../../tools/assembler/assembler.h"
#include "../../vm/vm.h"
#include "devices.h"
#include <SDL2/SDL.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -52,7 +51,7 @@ bool saveVM(const char *filename, VM *vm) {
return false;
}
// Write VM state (registers and pointers)
// Write VM state (locals and pointers)
if (fwrite(&vm->pc, sizeof(u32), 1, file) != 1 ||
fwrite(&vm->cp, sizeof(u32), 1, file) != 1 ||
fwrite(&vm->fp, sizeof(u32), 1, file) != 1 ||
@ -90,7 +89,7 @@ bool loadVM(const char *filename, VM *vm) {
return false;
}
// Read VM state (registers and pointers)
// Read VM state (locals and pointers)
if (fread(&vm->pc, sizeof(u32), 1, file) != 1 ||
fread(&vm->cp, sizeof(u32), 1, file) != 1 ||
fread(&vm->fp, sizeof(u32), 1, file) != 1 ||
@ -125,40 +124,40 @@ bool loadVM(const char *filename, VM *vm) {
bool compileAndSave(const char *source_file, const char *output_file, VM *vm) {
USED(vm);
USED(output_file);
FILE *f = fopen(source_file, "rb");
if (!f) {
perror("fopen");
USED(source_file);
return true;
}
#ifdef STATIC
#define SYMBOLS_COUNT 2048
Symbol symbols[SYMBOLS_COUNT];
#endif
void symbol_table_init(SymbolTable *t) {
#ifdef STATIC
memset(symbols, 0, SYMBOLS_COUNT*sizeof(Symbol));
t->symbols = symbols;
t->count = 0;
t->capacity = SYMBOLS_COUNT;
#else
t->symbols = calloc(16, sizeof(Symbol));
t->count = 0;
t->capacity = 16;
#endif
}
bool resize_or_check_size(SymbolTable *table) {
#ifdef STATIC
if (table->count >= table->capacity) {
return false;
}
static char source[MAX_SRC_SIZE + 1];
fseek(f, 0, SEEK_END);
long len = ftell(f);
fseek(f, 0, SEEK_SET);
if (len >= MAX_SRC_SIZE) {
fprintf(stderr, "Source is larger than buffer\n");
fclose(f);
return false;
#else
if (table->count >= table->capacity) {
table->capacity *= 2;
table->symbols = realloc(table->symbols, table->capacity * sizeof(Symbol));
}
size_t read = fread(source, 1, len, f);
source[read] = '\0';
fclose(f);
initLexer(source);
Token token;
do {
token = nextToken();
if (token.type == TOKEN_ERROR) {
printf("ERROR at line %d: %.*s\n", token.line, token.length, token.start);
break; // Stop on error, or continue if you want to see more
}
if (token.type != TOKEN_EOF) {
printf("Line %d [%s]: %.*s\n", token.line, tokenTypeToString(token.type),
token.length, token.start);
}
} while (token.type != TOKEN_EOF);
#endif
return true;
}
@ -184,247 +183,37 @@ bool assembleAndSave(const char *source_file, const char *output_file, VM *vm) {
source[read] = '\0';
fclose(f);
ExprNode *ast = expr_parse(source, strlen(source));
if (!ast) {
printf("Parse failed.\n");
return false;
} else {
assemble(vm, ast);
expr_free(ast);
// If output file specified, save the VM
if (output_file) {
if (!saveVM(output_file, vm)) {
printf("Failed to save VM to %s\n", output_file);
return false;
}
printf("VM saved to %s\n", output_file);
}
return true;
}
}
void repl(VM *vm) {
USED(vm);
char buffer[1024 * 10] = {0}; // Larger buffer for multi-line input
char line[1024];
for (;;) {
// Count current parentheses balance
i32 paren_balance = 0;
for (i32 i = 0; buffer[i]; i++) {
if (buffer[i] == '(')
paren_balance++;
else if (buffer[i] == ')')
paren_balance--;
}
// Show appropriate prompt
if (paren_balance > 0) {
printf(".. "); // Continuation prompt when unbalanced
} else {
printf("> "); // Normal prompt when balanced
}
fflush(stdout);
if (!fgets(line, sizeof(line), stdin)) {
printf("\n");
break;
}
// Append the new line to buffer
strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1);
// Recalculate balance after adding new line
paren_balance = 0;
for (i32 i = 0; buffer[i]; i++) {
if (buffer[i] == '(')
paren_balance++;
else if (buffer[i] == ')')
paren_balance--;
}
// Only parse when parentheses are balanced
if (paren_balance == 0) {
// Check if buffer has actual content (not just whitespace)
i32 has_content = 0;
for (i32 i = 0; buffer[i]; i++) {
if (!isspace(buffer[i])) {
has_content = 1;
break;
}
}
if (has_content) {
ExprNode *ast = expr_parse(buffer, strlen(buffer));
if (!ast) {
printf("Parse failed.\n");
} else {
assemble(vm, ast);
while (step_vm(vm)) {
}
expr_free(ast);
}
}
// Reset buffer for next input
buffer[0] = '\0';
}
// If unbalanced, continue reading more lines
}
exit(vm->flag);
}
#ifdef ASM_DEBUG
const char *opcode_to_string(Opcode op) {
static const char *names[] = {
[OP_HALT] = "halt",
[OP_JMP] = "jump",
[OP_JMPF] = "jump-if-flag",
[OP_CALL] = "call",
[OP_RETURN] = "return",
/* Immediate loads (only 32-bit variant needed) */
[OP_LOAD_IMM] = "load-immediate",
/* Register-indirect loads */
[OP_LOAD_IND_8] = "load-indirect-8",
[OP_LOAD_IND_16] = "load-indirect-16",
[OP_LOAD_IND_32] = "load-indirect-32",
/* Absolute address loads */
[OP_LOAD_ABS_8] = "load-absolute-8",
[OP_LOAD_ABS_16] = "load-absolute-16",
[OP_LOAD_ABS_32] = "load-absolute-32",
/* Base+offset loads */
[OP_LOAD_OFF_8] = "load-offset-8",
[OP_LOAD_OFF_16] = "load-offset-16",
[OP_LOAD_OFF_32] = "load-offset-32",
/* Absolute address stores */
[OP_STORE_ABS_8] = "store-absolute-8",
[OP_STORE_ABS_16] = "store-absolute-16",
[OP_STORE_ABS_32] = "store-absolute-32",
/* Register-indirect stores */
[OP_STORE_IND_8] = "store-indirect-8",
[OP_STORE_IND_16] = "store-indirect-16",
[OP_STORE_IND_32] = "store-indirect-32",
/* Base+offset stores */
[OP_STORE_OFF_8] = "store-offset-8",
[OP_STORE_OFF_16] = "store-offset-16",
[OP_STORE_OFF_32] = "store-offset-32",
/* Memory operations */
[OP_MALLOC] = "malloc",
[OP_MEMSET_8] = "memset-8",
[OP_MEMSET_16] = "memset-16",
[OP_MEMSET_32] = "memset-32",
/* Register operations */
[OP_REG_MOV] = "register-move",
[OP_SYSCALL] = "syscall",
/* Bit operations */
[OP_BIT_SHIFT_LEFT] = "bit-shift-left",
[OP_BIT_SHIFT_RIGHT] = "bit-shift-right",
[OP_BIT_SHIFT_R_EXT] = "bit-shift-r-ext",
[OP_BAND] = "bit-and",
[OP_BOR] = "bit-or",
[OP_BXOR] = "bit-xor",
/* Integer arithmetic */
[OP_ADD_INT] = "add-int",
[OP_SUB_INT] = "sub-int",
[OP_MUL_INT] = "mul-int",
[OP_DIV_INT] = "div-int",
[OP_ABS_INT] = "abs-int", // ← NEW
[OP_NEG_INT] = "neg-int", // ← NEW
/* Natural number arithmetic */
[OP_ADD_NAT] = "add-nat",
[OP_SUB_NAT] = "sub-nat",
[OP_MUL_NAT] = "mul-nat",
[OP_DIV_NAT] = "div-nat",
[OP_ABS_NAT] = "abs-nat", // ← NEW
[OP_NEG_NAT] = "neg-nat", // ← NEW
/* Floating point operations */
[OP_ADD_REAL] = "add-real",
[OP_SUB_REAL] = "sub-real",
[OP_MUL_REAL] = "mul-real",
[OP_DIV_REAL] = "div-real",
[OP_ABS_REAL] = "abs-real", // ← NEW
[OP_NEG_REAL] = "neg-real", // ← NEW
/* Type conversions */
[OP_INT_TO_REAL] = "int-to-real",
[OP_NAT_TO_REAL] = "nat-to-real",
[OP_REAL_TO_INT] = "real-to-int",
[OP_REAL_TO_NAT] = "real-to-nat",
/* Integer comparisons */
[OP_JEQ_INT] = "jump-eq-int",
[OP_JNEQ_INT] = "jump-neq-int",
[OP_JGT_INT] = "jump-gt-int",
[OP_JLT_INT] = "jump-lt-int",
[OP_JLE_INT] = "jump-le-int",
[OP_JGE_INT] = "jump-ge-int",
/* Natural number comparisons */
[OP_JEQ_NAT] = "jump-eq-nat",
[OP_JNEQ_NAT] = "jump-neq-nat",
[OP_JGT_NAT] = "jump-gt-nat",
[OP_JLT_NAT] = "jump-lt-nat",
[OP_JLE_NAT] = "jump-le-nat",
[OP_JGE_NAT] = "jump-ge-nat",
/* Floating point comparisons */
[OP_JEQ_REAL] = "jump-eq-real",
[OP_JNEQ_REAL] = "jump-neq-real",
[OP_JGE_REAL] = "jump-ge-real",
[OP_JGT_REAL] = "jump-gt-real",
[OP_JLT_REAL] = "jump-lt-real",
[OP_JLE_REAL] = "jump-le-real",
/* String operations */
[OP_STRLEN] = "string-length",
[OP_STREQ] = "string-eq",
[OP_STRCAT] = "string-concat",
[OP_STR_GET_CHAR] = "string-get-char",
[OP_STR_FIND_CHAR] = "string-find-char",
[OP_STR_SLICE] = "string-slice",
/* String conversions */
[OP_INT_TO_STRING] = "int-to-string",
[OP_NAT_TO_STRING] = "nat-to-string",
[OP_REAL_TO_STRING] = "real-to-string",
[OP_STRING_TO_INT] = "string-to-int",
[OP_STRING_TO_NAT] = "string-to-nat",
[OP_STRING_TO_REAL] = "string-to-real"};
if (op < 0 || op >= (int)(sizeof(names) / sizeof(names[0]))) {
return "<invalid-opcode>";
}
const char *name = names[op];
return name ? name : "<unknown-opcode>";
}
SymbolTable table = {0};
symbol_table_init(&table);
assemble(vm, &table, source);
#ifndef STATIC
free(table.symbols);
#endif
if (output_file) {
if (!saveVM(output_file, vm)) {
printf("Failed to save VM to %s\n", output_file);
return false;
}
printf("VM saved to %s\n", output_file);
}
return true;
}
i32 main(i32 argc, char *argv[]) {
bool dump_rom = false;
char *input_file = nil;
char *output_file = nil;
bool terminal_only_mode = false;
bool is_rom = false;
bool is_assembly = false;
bool is_ir = false;
// Parse command line arguments
for (i32 i = 1; i < argc; i++) {
if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--dump-rom") == 0) {
dump_rom = true;
} else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--term") == 0) {
terminal_only_mode = true;
} else if (input_file == nil) {
// This is the input file
input_file = argv[i];
@ -434,8 +223,8 @@ i32 main(i32 argc, char *argv[]) {
if (ext && (strcmp(ext, ".rom") == 0)) {
is_rom = true;
}
if (ext && (strcmp(ext, ".lisp") == 0)) {
is_assembly = true;
if (ext && (strcmp(ext, ".ir") == 0)) {
is_ir = true;
}
} else if (output_file == nil && dump_rom) {
// This is the output file for -o flag
@ -444,13 +233,12 @@ i32 main(i32 argc, char *argv[]) {
}
VM vm = {0};
bool compilation_success = true;
if (input_file) {
if (is_rom) {
// Load ROM file directly
compilation_success = loadVM(input_file, &vm);
} else if (is_assembly) {
} else if (is_ir) {
// Compile Lisp file
if (dump_rom && output_file) {
compilation_success = assembleAndSave(input_file, output_file, &vm);
@ -465,9 +253,8 @@ i32 main(i32 argc, char *argv[]) {
}
}
} else {
// No input file - enter REPL mode
repl(&vm);
return 0;
printf("usage: undar <src.ul>...");
return 1;
}
if (dump_rom) {
@ -487,6 +274,11 @@ i32 main(i32 argc, char *argv[]) {
vm_register_device(&vm, "/dev/term/0", "terminal", &console_data,
&console_device_ops, 4);
if (terminal_only_mode) {
while (step_vm(&vm));
return 0;
}
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("SDL initialization failed: %s\n", SDL_GetError());
return 1;
@ -555,11 +347,11 @@ i32 main(i32 argc, char *argv[]) {
case SDL_FINGERDOWN:
case SDL_FINGERUP: {
float x = event.tfinger.x * 640;
float y = event.tfinger.y * 480;
f32 x = event.tfinger.x * 640;
f32 y = event.tfinger.y * 480;
mouse_data.x = (int)x;
mouse_data.y = (int)y;
mouse_data.x = (i32)x;
mouse_data.y = (i32)y;
// Only treat the first finger as mouse input (ignore multi-touch
// beyond 1 finger)
@ -576,12 +368,10 @@ 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 = 100; // Adjust this value
i32 cycles_this_frame = 0;
i32 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);
#endif
//printf("code[%d] = %s\n", vm.pc, opcode_to_string(vm.code[vm.pc]));
if (!step_vm(&vm)) {
running = false;
break;
@ -599,9 +389,9 @@ i32 main(i32 argc, char *argv[]) {
SDL_RenderGetViewport(screen_data.renderer, &output_rect);
// Calculate aspect ratio preserving scaling
float scale_x = (float)output_rect.w / screen_data.width;
float scale_y = (float)output_rect.h / screen_data.height;
float scale = SDL_min(scale_x, scale_y);
f32 scale_x = (f32)output_rect.w / screen_data.width;
f32 scale_y = (f32)output_rect.h / screen_data.height;
f32 scale = SDL_min(scale_x, scale_y);
SDL_Rect dstrect = {
(i32)((output_rect.w - screen_data.width * scale) / 2),

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
#ifndef ASSEMBLER_H
#define ASSEMBLER_H
#include "../vm/common.h"
#include "../vm/vm.h"
#include "parser.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define AS_FIXED(v) ((float)(i32)(v) / 65536.0f)
#define TO_FIXED(f) ((i32)( \
((f) >= 0.0f) ? ((f) * 65536.0f + 0.5f) : ((f) * 65536.0f - 0.5f) \
))
void assemble(VM *vm, ExprNode *program);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
#ifndef UNDAR_IR_ASSEMBLER_H
#define UNDAR_IR_ASSEMBLER_H
#include "../../vm/common.h"
#include "../../vm/opcodes.h"
#include "lexer.h"
typedef enum { GLOBAL, LOCAL, VAR } ScopeType;
typedef enum {
VOID,
BOOL,
I8,
I16,
I32,
U8,
U16,
U32,
F8,
F16,
F32,
STR,
PLEX,
ARRAY,
FUNCTION
} SymbolType;
typedef struct symbol_s Symbol;
typedef struct symbol_tab_s SymbolTable;
#define MAX_SYMBOL_NAME_LENGTH 64
struct symbol_s {
char name[MAX_SYMBOL_NAME_LENGTH];
u8 name_length;
SymbolType type;
ScopeType scope;
u32 ref; // vm->mp if global, vm->pc local, register if var
u32 size; // size of symbol
};
struct symbol_tab_s {
Symbol *symbols;
u32 count;
u32 capacity;
};
void assemble(VM *vm, SymbolTable *st, char *source);
extern bool resize_or_check_size(SymbolTable *table);/* implement this in arch/ not here */
const char *opcode_to_string(Opcode op);
#endif

534
src/tools/assembler/lexer.c Normal file
View File

@ -0,0 +1,534 @@
#include <string.h>
#include "../../vm/common.h"
#include "lexer.h"
typedef struct {
const char *start;
const char *current;
i32 line;
} Lexer;
Lexer lexer;
void init_lexer(const char *source) {
lexer.start = source;
lexer.current = source;
lexer.line = 1;
}
static bool is_alpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
static bool is_digit(char c) { return c >= '0' && c <= '9'; }
static bool is_at_end() { return *lexer.current == '\0'; }
static char advance() {
lexer.current++;
return lexer.current[-1];
}
static char peek() { return *lexer.current; }
static char peek_next() {
if (is_at_end())
return '\0';
return lexer.current[1];
}
static bool match(char expected) {
if (is_at_end())
return false;
if (*lexer.current != expected)
return false;
lexer.current++;
return true;
}
static Token make_token(TokenType type) {
Token token;
token.type = type;
token.start = lexer.start;
token.length = (i32)(lexer.current - lexer.start);
token.line = lexer.line;
return token;
}
static Token error_token(const char *message) {
Token token;
token.type = TOKEN_ERROR;
token.start = message;
token.length = (i32)strlen(message);
token.line = lexer.line;
return token;
}
static void skip_whitespace() {
for (;;) {
char c = peek();
switch (c) {
case ' ':
case '\r':
case '\t':
advance();
break;
case '\n':
lexer.line++;
advance();
break;
case '/':
if (peek_next() == '/') {
// Single-line comment: skip until newline or end of file
advance();
while (peek() != '\n' && !is_at_end())
advance();
} else if (peek_next() == '*') {
// Multi-line comment: skip until '*/' or end of file
advance();
advance();
while (!is_at_end()) {
if (peek() == '\n')
lexer.line++;
if (peek() == '*' && peek_next() == '/') {
advance();
advance();
break; // Exit loop, comment ended
}
advance();
}
} else {
return; // Not a comment, let tokenization handle it
}
break;
default:
return;
}
}
}
static TokenType check_keyword(i32 start, i32 length, const char *rest,
TokenType type) {
if (lexer.current - lexer.start == start + length &&
memcmp(lexer.start + start, rest, length) == 0) {
return type;
}
return TOKEN_IDENTIFIER;
}
static TokenType identifierType() {
switch (lexer.start[0]) {
case 'a':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'n':
return check_keyword(2, 1, "d", TOKEN_OPERATOR_AND);
case 's':
return check_keyword(2, 0, "", TOKEN_KEYWORD_AS);
}
}
break;
case 'c':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'l':
return check_keyword(2, 3, "ose", TOKEN_KEYWORD_CLOSE);
case 'o':
return check_keyword(2, 3, "nst", TOKEN_KEYWORD_CONST);
}
}
break;
case 'e':
return check_keyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
case 'f':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'a':
return check_keyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
case 'o':
return check_keyword(2, 1, "r", TOKEN_KEYWORD_FOR);
case '3':
return check_keyword(1, 1, "2", TOKEN_TYPE_REAL);
}
return check_keyword(1, 7, "unction", TOKEN_KEYWORD_FN);
}
break;
case 'i':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'f':
return check_keyword(2, 0, "", TOKEN_KEYWORD_IF);
case 's':
return check_keyword(2, 0, "", TOKEN_KEYWORD_IS);
case '8':
return check_keyword(2, 0, "", TOKEN_TYPE_I8);
case '1':
return check_keyword(2, 1, "6", TOKEN_TYPE_I16);
case '3':
return check_keyword(2, 1, "2", TOKEN_TYPE_INT);
case 'n':
if (lexer.current - lexer.start > 2) {
switch (lexer.start[2]) {
case 'i':
return check_keyword(3, 2, "t", TOKEN_KEYWORD_INIT);
case 't':
return check_keyword(3, 0, "", TOKEN_TYPE_INT);
}
}
break;
}
}
break;
case 'n':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'a':
return check_keyword(2, 1, "t", TOKEN_TYPE_NAT);
case 'i':
return check_keyword(2, 1, "l", TOKEN_KEYWORD_NIL);
}
}
break;
case 'o':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'p':
return check_keyword(2, 2, "en", TOKEN_KEYWORD_OPEN);
case 'r':
return check_keyword(2, 0, "", TOKEN_OPERATOR_OR);
}
}
break;
case 'p':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'l':
return check_keyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
}
}
break;
case 'r':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'e':
if (lexer.current - lexer.start > 2) {
switch (lexer.start[2]) {
case 'f':
return check_keyword(3, 4, "resh", TOKEN_KEYWORD_REFRESH);
case 't':
return check_keyword(3, 3, "urn", TOKEN_KEYWORD_RETURN);
case 'a':
if (lexer.current - lexer.start > 3) {
switch(lexer.start[3]) {
case 'd':
return check_keyword(4, 0, "", TOKEN_KEYWORD_READ);
case 'l':
return check_keyword(4, 0, "", TOKEN_TYPE_REAL);
}
}
}
}
break;
}
}
break;
case 's':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 't':
return check_keyword(2, 1, "r", TOKEN_TYPE_STR);
}
}
break;
case 't':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'h':
return check_keyword(2, 2, "is", TOKEN_KEYWORD_THIS);
case 'r':
return check_keyword(2, 2, "ue", TOKEN_KEYWORD_TRUE);
}
}
break;
case 'u':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 's':
return check_keyword(2, 1, "e", TOKEN_KEYWORD_USE);
case '8':
return check_keyword(2, 0, "", TOKEN_TYPE_U8);
case '1':
return check_keyword(2, 1, "6", TOKEN_TYPE_U16);
case '3':
return check_keyword(2, 1, "2", TOKEN_TYPE_NAT);
}
}
break;
case 'w':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'h':
return check_keyword(2, 3, "ile", TOKEN_KEYWORD_WHILE);
case 'r':
return check_keyword(2, 3, "ite", TOKEN_KEYWORD_WRITE);
}
}
break;
case 'b':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'y':
return check_keyword(2, 2, "te", TOKEN_TYPE_U8);
case 'o':
return check_keyword(2, 2, "ol", TOKEN_TYPE_U8);
}
}
break;
case 'g':
return check_keyword(1, 5, "lobal", TOKEN_KEYWORD_GLOBAL);
case 'l':
return check_keyword(1, 3, "oop", TOKEN_KEYWORD_LOOP);
case 'd':
return check_keyword(1, 1, "o", TOKEN_KEYWORD_DO);
case 'v':
return check_keyword(1, 3, "oid", TOKEN_TYPE_VOID);
}
return TOKEN_IDENTIFIER;
}
static Token identifier() {
while (is_alpha(peek()) || is_digit(peek()))
advance();
return make_token(identifierType());
}
static Token number() {
while (is_digit(peek()))
advance();
/* Look for a fractional part. */
if (peek() == '.' && is_digit(peek_next())) {
/* Consume the ".". */
advance();
while (is_digit(peek()))
advance();
return make_token(TOKEN_LITERAL_REAL);
}
return make_token(TOKEN_LITERAL_INT);
}
static Token string() {
while (peek() != '"' && !is_at_end()) {
if (peek() == '\n')
lexer.line++;
advance();
}
if (is_at_end())
return error_token("Unterminated string.");
/* The closing quote. */
advance();
return make_token(TOKEN_LITERAL_STR);
}
Token next_token() {
skip_whitespace();
lexer.start = lexer.current;
if (is_at_end())
return make_token(TOKEN_EOF);
char c = advance();
if (is_alpha(c))
return identifier();
char next = peek();
if ((c == '-' && is_digit(next)) || is_digit(c))
return number();
switch (c) {
case '(':
return make_token(TOKEN_LPAREN);
case ')':
return make_token(TOKEN_RPAREN);
case '{':
return make_token(TOKEN_LBRACE);
case '}':
return make_token(TOKEN_RBRACE);
case '[':
return make_token(TOKEN_LBRACKET);
case ']':
return make_token(TOKEN_RBRACKET);
case ';':
return make_token(TOKEN_SEMICOLON);
case ',':
return make_token(TOKEN_COMMA);
case '.':
return make_token(TOKEN_DOT);
case '-':
return make_token(match('>') ? TOKEN_ARROW_RIGHT : TOKEN_MINUS);
case '+':
return make_token(TOKEN_PLUS);
case '/':
return make_token(TOKEN_SLASH);
case '&':
return make_token(match('&') ? TOKEN_AND_AND : TOKEN_AND);
case '#':
return make_token(TOKEN_MESH);
case '$':
return make_token(TOKEN_BIG_MONEY);
case '*':
return make_token(TOKEN_STAR);
case '!':
return make_token(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
case '=':
return make_token(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
case '<':
return make_token(match('=') ? TOKEN_LTE : TOKEN_LT);
case '>':
return make_token(match('=') ? TOKEN_GTE : TOKEN_GT);
case '"':
return string();
}
return error_token("Unexpected character.");
}
const char *token_type_to_string(TokenType type) {
switch (type) {
case TOKEN_EOF:
return "EOF";
case TOKEN_IDENTIFIER:
return "IDENTIFIER";
case TOKEN_LITERAL_INT:
return "LITERAL_INT";
case TOKEN_LITERAL_NAT:
return "LITERAL_NAT";
case TOKEN_LITERAL_REAL:
return "LITERAL_REAL";
case TOKEN_LITERAL_STR:
return "LITERAL_STR";
case TOKEN_TYPE_INT:
return "TYPE_INT";
case TOKEN_TYPE_NAT:
return "TYPE_NAT";
case TOKEN_TYPE_REAL:
return "TYPE_REAL";
case TOKEN_TYPE_STR:
return "TYPE_STR";
case TOKEN_KEYWORD_PLEX:
return "KEYWORD_PLEX";
case TOKEN_KEYWORD_FN:
return "KEYWORD_FN";
case TOKEN_KEYWORD_CONST:
return "KEYWORD_CONST";
case TOKEN_KEYWORD_IF:
return "KEYWORD_IF";
case TOKEN_KEYWORD_IS:
return "IS";
case TOKEN_KEYWORD_AS:
return "AS";
case TOKEN_KEYWORD_ELSE:
return "KEYWORD_ELSE";
case TOKEN_KEYWORD_WHILE:
return "KEYWORD_WHILE";
case TOKEN_KEYWORD_FOR:
return "KEYWORD_FOR";
case TOKEN_KEYWORD_RETURN:
return "KEYWORD_RETURN";
case TOKEN_KEYWORD_USE:
return "KEYWORD_USE";
case TOKEN_KEYWORD_INIT:
return "KEYWORD_INIT";
case TOKEN_KEYWORD_THIS:
return "KEYWORD_THIS";
case TOKEN_KEYWORD_OPEN:
return "TOKEN_KEYWORD_OPEN";
case TOKEN_KEYWORD_READ:
return "TOKEN_KEYWORD_READ";
case TOKEN_KEYWORD_WRITE:
return "TOKEN_KEYWORD_WRITE";
case TOKEN_KEYWORD_REFRESH:
return "TOKEN_KEYWORD_REFRESH";
case TOKEN_KEYWORD_CLOSE:
return "TOKEN_KEYWORD_CLOSE";
case TOKEN_KEYWORD_NIL:
return "KEYWORD_NIL";
case TOKEN_KEYWORD_TRUE:
return "KEYWORD_TRUE";
case TOKEN_KEYWORD_FALSE:
return "KEYWORD_FALSE";
case TOKEN_KEYWORD_GLOBAL:
return "KEYWORD_GLOBAL";
case TOKEN_OPERATOR_NOT:
return "OPERATOR_NOT";
case TOKEN_OPERATOR_AND:
return "OPERATOR_AND";
case TOKEN_OPERATOR_OR:
return "OPERATOR_OR";
case TOKEN_BANG:
return "BANG";
case TOKEN_BANG_EQ:
return "BANG_EQ";
case TOKEN_EQ:
return "EQ";
case TOKEN_EQ_EQ:
return "EQ_EQ";
case TOKEN_GT:
return "GT";
case TOKEN_LT:
return "LT";
case TOKEN_GTE:
return "GTE";
case TOKEN_LTE:
return "LTE";
case TOKEN_DOT:
return "DOT";
case TOKEN_COMMA:
return "COMMA";
case TOKEN_COLON:
return "COLON";
case TOKEN_SEMICOLON:
return "SEMICOLON";
case TOKEN_PLUS:
return "PLUS";
case TOKEN_MINUS:
return "MINUS";
case TOKEN_STAR:
return "STAR";
case TOKEN_SLASH:
return "SLASH";
case TOKEN_LPAREN:
return "LPAREN";
case TOKEN_RPAREN:
return "RPAREN";
case TOKEN_LBRACE:
return "LBRACE";
case TOKEN_RBRACE:
return "RBRACE";
case TOKEN_LBRACKET:
return "LBRACKET";
case TOKEN_RBRACKET:
return "RBRACKET";
case TOKEN_ARROW_RIGHT:
return "ARROW_RIGHT";
case TOKEN_MESH:
return "MESH";
case TOKEN_BIG_MONEY:
return "BIG_MONEY";
case TOKEN_AND:
return "AND";
case TOKEN_AND_AND:
return "AND_AND";
case TOKEN_ERROR:
return "ERROR";
default:
return "UNKNOWN_TOKEN";
}
}

View File

@ -0,0 +1,89 @@
#ifndef UNDAR_LEXER_H
#define UNDAR_LEXER_H
typedef enum {
TOKEN_EOF,
TOKEN_IDENTIFIER,
TOKEN_LITERAL_INT,
TOKEN_LITERAL_NAT,
TOKEN_LITERAL_REAL,
TOKEN_LITERAL_STR,
TOKEN_TYPE_I8,
TOKEN_TYPE_I16,
TOKEN_TYPE_INT,
TOKEN_TYPE_U8,
TOKEN_TYPE_U16,
TOKEN_TYPE_NAT,
TOKEN_TYPE_REAL,
TOKEN_TYPE_STR,
TOKEN_TYPE_BOOL,
TOKEN_TYPE_VOID,
TOKEN_KEYWORD_PLEX,
TOKEN_KEYWORD_FN,
TOKEN_KEYWORD_CONST,
TOKEN_KEYWORD_IF,
TOKEN_KEYWORD_IS,
TOKEN_KEYWORD_AS,
TOKEN_KEYWORD_ELSE,
TOKEN_KEYWORD_WHILE,
TOKEN_KEYWORD_FOR,
TOKEN_KEYWORD_RETURN,
TOKEN_KEYWORD_USE,
TOKEN_KEYWORD_INIT,
TOKEN_KEYWORD_THIS,
TOKEN_KEYWORD_GLOBAL,
TOKEN_KEYWORD_OPEN,
TOKEN_KEYWORD_READ,
TOKEN_KEYWORD_WRITE,
TOKEN_KEYWORD_REFRESH,
TOKEN_KEYWORD_CLOSE,
TOKEN_KEYWORD_LOOP,
TOKEN_KEYWORD_DO,
TOKEN_KEYWORD_NIL,
TOKEN_KEYWORD_TRUE,
TOKEN_KEYWORD_FALSE,
TOKEN_OPERATOR_NOT,
TOKEN_OPERATOR_AND,
TOKEN_OPERATOR_OR,
TOKEN_BANG,
TOKEN_BANG_EQ,
TOKEN_EQ,
TOKEN_EQ_EQ,
TOKEN_AND,
TOKEN_AND_AND,
TOKEN_GT,
TOKEN_LT,
TOKEN_GTE,
TOKEN_LTE,
TOKEN_DOT,
TOKEN_COMMA,
TOKEN_COLON,
TOKEN_SEMICOLON,
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_STAR,
TOKEN_SLASH,
TOKEN_MESH,
TOKEN_BIG_MONEY,
TOKEN_LPAREN,
TOKEN_RPAREN,
TOKEN_LBRACE,
TOKEN_RBRACE,
TOKEN_LBRACKET,
TOKEN_RBRACKET,
TOKEN_ARROW_RIGHT,
TOKEN_ERROR
} TokenType;
typedef struct {
TokenType type;
const char *start;
int length;
int line;
} Token;
void init_lexer(const char *source);
Token next_token();
const char* token_type_to_string(TokenType type);
#endif

View File

@ -0,0 +1,409 @@
#include "parser.h"
#include "compiler.h"
#include "../../vm/common.h"
#include "../../vm/opcodes.h"
#include "../../vm/libc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
NamesTable *names_table_init() {
NamesTable *table = malloc(sizeof(NamesTable));
table->names = malloc(16 * sizeof(char *));
table->count = 0;
table->capacity = 16;
return table;
}
FunctionTable *function_table_init() {
FunctionTable *table = malloc(sizeof(FunctionTable));
table->symbols = malloc(16 * sizeof(FunctionDef));
table->count = 0;
table->capacity = 16;
return table;
}
ArrayTable *array_table_init() {
ArrayTable *table = malloc(sizeof(ArrayTable));
table->symbols = malloc(16 * sizeof(ArrayDef));
table->count = 0;
table->capacity = 16;
return table;
}
PlexTable *plex_table_init() {
PlexTable *table = malloc(sizeof(PlexTable));
table->symbols = malloc(16 * sizeof(PlexDef));
table->count = 0;
table->capacity = 16;
return table;
}
PlexFieldsTable *plex_fields_table_init() {
PlexFieldsTable *table = malloc(sizeof(PlexFieldsTable));
table->plex_refs = malloc(64 * sizeof(u32));
table->fields = malloc(64 * sizeof(ValueType));
table->count = 0;
table->capacity = 64;
return table;
}
u32 names_table_add(NamesTable *table, const char *name) {
for (u32 i = 0; i < table->count; i++) {
if (strcmp(table->names[i], name) == 0) {
return (u32)i;
}
}
if (table->count >= table->capacity) {
table->capacity *= 2;
table->names = realloc(table->names, table->capacity * sizeof(char *));
}
table->names[table->count] = malloc(strlen(name) + 1);
strcpy(table->names[table->count], name);
u32 index = (u32)table->count;
table->count++;
return index;
}
u32 function_table_add(FunctionTable *table, FunctionDef def) {
if (table->count >= table->capacity) {
table->capacity *= 2;
table->symbols =
realloc(table->symbols, table->capacity * sizeof(FunctionDef));
}
table->symbols[table->count] = def;
u32 index = (u32)table->count;
table->count++;
return index;
}
u32 array_table_add(ArrayTable *table, ArrayDef def) {
if (table->count >= table->capacity) {
table->capacity *= 2;
table->symbols = realloc(table->symbols, table->capacity * sizeof(ArrayDef));
}
table->symbols[table->count] = def;
u32 index = (u32)table->count;
table->count++;
return index;
}
u32 plex_add(PlexTable *plex_table, u32 name, u32 size, u32 field_start,
u32 field_count) {
if (plex_table->count >= plex_table->capacity) {
plex_table->capacity *= 2;
plex_table->symbols =
realloc(plex_table->symbols, plex_table->capacity * sizeof(PlexDef));
}
plex_table->symbols[plex_table->count].name = name;
plex_table->symbols[plex_table->count].size = size;
plex_table->symbols[plex_table->count].field_ref_start = field_start;
plex_table->symbols[plex_table->count].field_count = field_count;
u32 index = (u32)plex_table->count;
plex_table->count++;
return index;
}
u32 plex_fields_add(PlexFieldsTable *fields_table, u32 plex_ref,
ValueType field) {
if (fields_table->count + 1 > fields_table->capacity) {
u32 new_capacity = fields_table->capacity * 2;
if (new_capacity < fields_table->count + 1) {
new_capacity = fields_table->count + 1;
}
fields_table->plex_refs =
realloc(fields_table->plex_refs, new_capacity * sizeof(u32));
fields_table->fields =
realloc(fields_table->fields, new_capacity * sizeof(ValueType));
fields_table->capacity = new_capacity;
}
u32 start_index = fields_table->count;
fields_table->plex_refs[start_index] = plex_ref;
fields_table->fields[start_index] = field;
fields_table->count++;
return start_index;
}
int plex_get_field_index_by_name(PlexTable *plex_table,
PlexFieldsTable *fields_table,
NamesTable *names_table, u32 plex_index,
const char *field_name) {
if (plex_index >= plex_table->count)
return -1;
PlexDef *plex_def = &plex_table->symbols[plex_index];
u32 field_start = plex_def->field_ref_start;
u32 field_count = plex_def->field_count;
for (u32 i = 0; i < field_count; i++) {
u32 field_table_index = field_start + i;
ValueType *field = &fields_table->fields[field_table_index];
if (field->name < names_table->count) {
if (strcmp(names_table->names[field->name], field_name) == 0) {
return (int)i; // Return field index within the plex
}
}
}
return -1; // Not found
}
ValueType *plex_get_field(PlexTable *plex_table, PlexFieldsTable *fields_table,
u32 plex_index, u32 field_in_plex_index) {
if (plex_index >= plex_table->count)
return nil;
PlexDef *plex_def = &plex_table->symbols[plex_index];
if (field_in_plex_index >= plex_def->field_count)
return nil;
u32 field_table_index = plex_def->field_ref_start + field_in_plex_index;
return &fields_table->fields[field_table_index];
}
ValueType *plex_get_field_by_name(PlexTable *plex_table,
PlexFieldsTable *fields_table,
NamesTable *names_table, u32 plex_index,
const char *field_name) {
int field_index = plex_get_field_index_by_name(
plex_table, fields_table, names_table, plex_index, field_name);
if (field_index == -1)
return nil;
return plex_get_field(plex_table, fields_table, plex_index, (u32)field_index);
}
typedef struct {
Token current;
Token previous;
bool hadError;
bool panicMode;
} Parser;
typedef enum {
PREC_NONE,
PREC_ASSIGNMENT, /* = */
PREC_OR, /* or */
PREC_AND, /* and */
PREC_EQUALITY, /* == != */
PREC_COMPARISON, /* < > <= >= */
PREC_TERM, /* + - */
PREC_FACTOR, /* * / */
PREC_UNARY, /* not */
PREC_CALL, /* . () */
PREC_PRIMARY
} Precedence;
typedef void (*ParseFn)(char *program);
typedef struct {
ParseFn prefix;
ParseFn infix;
Precedence precedence;
} ParseRule;
typedef struct {
SymbolTable table;
Symbol current;
Symbol last;
i8 rp; // Next free register
} Compiler;
Parser parser;
const char *internalErrorMsg =
"FLAGRANT COMPILER ERROR\n\nCompiler over.\nBug = Very Yes.";
bool isType(TokenType type) {
return type == TOKEN_TYPE_INT || type == TOKEN_TYPE_NAT ||
type == TOKEN_TYPE_REAL || type == TOKEN_TYPE_STR ||
type == TOKEN_TYPE_BOOL;
}
void errorAt(Token *token, const char *message) {
if (parser.panicMode)
return;
parser.panicMode = true;
fprintf(stderr, "[line %d] Error", token->line);
if (token->type == TOKEN_EOF) {
fprintf(stderr, " at end");
} else if (token->type == TOKEN_ERROR) {
} else {
fprintf(stderr, " at '%.*s'", token->length, token->start);
}
fprintf(stderr, ": %s\n", message);
parser.hadError = true;
}
void error(const char *message) { errorAt(&parser.previous, message); }
void errorAtCurrent(const char *message) { errorAt(&parser.current, message); }
void advance() {
parser.previous = parser.current;
for (;;) {
parser.current = next_token();
if (parser.current.type != TOKEN_ERROR)
break;
errorAtCurrent(parser.current.start);
}
}
void consume(TokenType type, const char *message) {
if (parser.current.type == type) {
advance();
return;
}
errorAtCurrent(message);
}
static int allocateRegister(Compiler *c) {
char buffer[38];
if (c->rp + 1 > 31) {
sprintf(buffer, "Out of registers (used %d, max 32)", c->rp + 1);
error(buffer);
return -1;
}
return c->rp++;
}
static void popRegister(Compiler *c) {
if (c->rp - 1 > 0) {
c->rp--;
}
}
static void freeRegister(Compiler *c, u8 reg) {
if (reg == c->rp - 1) {
c->rp--;
}
}
static void clearRegisters(Compiler *c, u8 reg) { c->rp = 0; }
void emit_byte(VM *vm, u8 byte) { vm->code[vm->cp++] = byte; }
void emit_u32(VM *vm, u32 value) {
write_u32(vm, code, vm->cp, value);
vm->cp += 4;
}
void emit_opcode(VM *vm, Opcode op) { emit_byte(vm, op); }
static bool check(TokenType type) { return parser.current.type == type; }
static bool match(TokenType type) {
if (!check(type))
return false;
advance();
return true;
}
static void expression(Compiler *c, VM *vm) {
USED(c);
USED(vm);
}
void number(Compiler *c, VM *vm) {
emit_opcode(vm, OP_LOAD_IMM);
int reg = allocateRegister(c);
if (reg < 0)
return;
emit_byte(vm, reg);
c->last = Symbol{ .type=parser.previous.type };
switch (parser.previous.type) {
case TOKEN_LITERAL_INT: {
char *endptr;
i32 value = (i32)strtol(parser.previous.start, &endptr, 10);
emit_u32(vm, value);
return;
}
case TOKEN_LITERAL_NAT: {
long value = atol(parser.previous.start);
emit_u32(vm, value);
return;
}
case TOKEN_LITERAL_REAL: {
float value = atof(parser.previous.start);
fixed_t fvalue = float_to_fixed(value);
emit_u32(vm, fvalue);
return;
}
default:
return; // Unreachable.
}
errorAtCurrent("Invalid number format");
}
static void unary(Compiler *c, VM *vm) {
TokenType operatorType = parser.previous.type;
// Compile the operand.
expression(c, vm);
// Emit the operator instruction.
switch (operatorType) {
case TOKEN_MINUS: {
switch (c->last.type) {
case TOKEN_LITERAL_NAT:
emit_opcode(vm, OP_NEG_NAT);
case TOKEN_LITERAL_REAL:
emit_opcode(vm, OP_NEG_REAL);
default:
emit_opcode(vm, OP_NEG_INT);
}
int dest = allocateRegister();
emit_byte(vm, dest);
emit_byte(vm, dest);
}
default:
return; // Unreachable.
}
}
static void emitHalt(Compiler *c, VM *vm) {
emit_opcode(vm, OP_EXIT);
advance();
number(c, vm);
}
static void endCompiler(Compiler *c, VM *vm) { emitHalt(c, vm); }
static void grouping(Compiler *c, VM *vm) {
expression(c, vm);
consume(TOKEN_RPAREN, "Expect ')' after expression.");
}
bool compile(const char *source, VM *vm) {
USED(source);
USED(vm);
initLexer(source);
parser.hadError = false;
parser.panicMode = false;
Compiler compiler;
advance();
expression(&compiler, vm);
consume(TOKEN_EOF, "Expect end of expression.");
endCompiler(&compiler, vm);
return parser.hadError;
}

View File

@ -0,0 +1,122 @@
#ifndef UNDAR_COMPILER_H
#define UNDAR_COMPILER_H
#import "../../vm/common.h"
typedef enum {
VOID,
BOOL,
I8,
I16,
I32,
U8,
U16,
U32,
F8,
F16,
F32,
STR,
PLEX,
ARRAY,
FUNCTION
} SymbolType;
typedef struct value_type_s ValueType;
typedef struct function_def_s FunctionDef;
typedef struct function_tab_s FunctionTable;
typedef struct plex_def_s PlexDef;
typedef struct plex_tab_s PlexTable;
typedef struct array_def_s ArrayDef;
typedef struct array_tab_s ArrayTable;
typedef struct symbol_s Symbol;
typedef struct symbol_tab_s SymbolTable;
typedef struct names_tab_s NamesTable;
typedef struct plex_fields_tab_s PlexFieldsTable;
typedef struct scope_s Scope;
typedef struct scope_tab_s ScopeTable;
struct value_type_s {
SymbolType type;
u32 name;
u32 size;
u32 table_ref; // if it is a heap object
};
struct function_def_s {
u32 name;
ValueType args[8];
u8 arg_count;
ValueType return_type;
};
struct plex_def_s {
u32 name;
u32 size;
u32 field_ref_start;
u32 field_count;
};
struct array_def_s {
ValueType type;
u32 length;
u32 logical_size; // length of the array
u32 physical_size; // logical_size * type_size + fat pointer
};
struct symbol_s {
u32 name;
ValueType type;
union {
u32 local; // register
u32 global; // address
} ref;
};
struct plex_fields_tab_s {
u32 *plex_refs;
ValueType *fields;
u32 count;
u32 capacity;
};
struct plex_tab_s {
PlexDef *symbols;
u32 count;
u32 capacity;
};
struct array_tab_s {
ArrayDef *symbols;
u32 count;
u32 capacity;
};
struct function_tab_s {
FunctionDef *symbols;
u32 count;
u32 capacity;
};
struct names_tab_s {
char **names;
u32 count;
u32 capacity;
};
struct symbol_tab_s {
Symbol *symbols;
u32 count;
u32 capacity;
};
struct scope_s {
SymbolTable table;
};
struct scope_tab_s {
Scope *scopes;
u32 count;
u32 capacity;
};
#endif

View File

@ -1,6 +1,6 @@
#include <string.h>
#include "../vm/common.h"
#include "../../vm/common.h"
#include "lexer.h"
typedef struct {
@ -81,19 +81,20 @@ static void skipWhitespace() {
case '/':
if (peekNext() == '/') {
// Single-line comment: skip until newline or end of file
advance();
advance();
while (peek() != '\n' && !isAtEnd())
advance();
} else if (peekNext() == '*') {
// Multi-line comment: skip until '*/' or end of file
advance();
advance();
advance();
advance();
while (!isAtEnd()) {
if (peek() == '\n') lexer.line++;
if (peek() == '\n')
lexer.line++;
if (peek() == '*' && peekNext() == '/') {
advance();
advance();
break; // Exit loop, comment ended
advance();
advance();
break; // Exit loop, comment ended
}
advance();
}
@ -120,7 +121,15 @@ static TokenType checkKeyword(int start, int length, const char *rest,
static TokenType identifierType() {
switch (lexer.start[0]) {
case 'a':
return checkKeyword(1, 2, "nd", TOKEN_OPERATOR_AND);
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'n':
return checkKeyword(2, 1, "d", TOKEN_OPERATOR_AND);
case 's':
return checkKeyword(2, 0, "", TOKEN_KEYWORD_AS);
}
}
break;
case 'c':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
@ -130,7 +139,7 @@ static TokenType identifierType() {
return checkKeyword(2, 3, "nst", TOKEN_KEYWORD_CONST);
}
}
break;
break;
case 'e':
return checkKeyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
case 'f':
@ -140,6 +149,8 @@ static TokenType identifierType() {
return checkKeyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
case 'o':
return checkKeyword(2, 1, "r", TOKEN_KEYWORD_FOR);
case '3':
return checkKeyword(2, 1, "2", TOKEN_TYPE_REAL);
}
return checkKeyword(1, 7, "unction", TOKEN_KEYWORD_FN);
}
@ -149,13 +160,21 @@ static TokenType identifierType() {
switch (lexer.start[1]) {
case 'f':
return checkKeyword(2, 0, "", TOKEN_KEYWORD_IF);
case 's':
return checkKeyword(2, 0, "", TOKEN_KEYWORD_IS);
case '8':
return checkKeyword(2, 0, "", TOKEN_TYPE_I8);
case '1':
return checkKeyword(2, 1, "6", TOKEN_TYPE_I16);
case '3':
return checkKeyword(2, 1, "2", TOKEN_TYPE_INT);
case 'n':
if (lexer.current - lexer.start > 2) {
switch (lexer.start[2]) {
case 'i':
return checkKeyword(3, 2, "t", TOKEN_KEYWORD_INIT);
case 't':
return checkKeyword(3, 1, "", TOKEN_TYPE_INT);
return checkKeyword(3, 0, "", TOKEN_TYPE_INT);
}
}
break;
@ -215,7 +234,7 @@ static TokenType identifierType() {
return checkKeyword(2, 1, "r", TOKEN_TYPE_STR);
}
}
break;
break;
case 't':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
@ -231,6 +250,12 @@ static TokenType identifierType() {
switch (lexer.start[1]) {
case 's':
return checkKeyword(2, 1, "e", TOKEN_KEYWORD_USE);
case '8':
return checkKeyword(2, 0, "", TOKEN_TYPE_U8);
case '1':
return checkKeyword(2, 1, "6", TOKEN_TYPE_U16);
case '3':
return checkKeyword(2, 1, "2", TOKEN_TYPE_NAT);
}
}
break;
@ -244,6 +269,8 @@ static TokenType identifierType() {
}
}
break;
case 'g':
return checkKeyword(1, 5, "lobal", TOKEN_KEYWORD_GLOBAL);
}
return TOKEN_IDENTIFIER;
@ -267,10 +294,10 @@ static Token number() {
while (isDigit(peek()))
advance();
return makeToken(TOKEN_FLOAT_LITERAL);
return makeToken(TOKEN_LITERAL_REAL);
}
return makeToken(TOKEN_INT_LITERAL);
return makeToken(TOKEN_LITERAL_INT);
}
static Token string() {
@ -285,10 +312,10 @@ static Token string() {
/* The closing quote. */
advance();
return makeToken(TOKEN_STRING_LITERAL);
return makeToken(TOKEN_LITERAL_STR);
}
Token nextToken() {
Token next_token() {
skipWhitespace();
lexer.start = lexer.current;
@ -321,11 +348,17 @@ Token nextToken() {
case '.':
return makeToken(TOKEN_DOT);
case '-':
return makeToken(TOKEN_MINUS);
return makeToken(match('>') ? TOKEN_ARROW_RIGHT : TOKEN_MINUS);
case '+':
return makeToken(TOKEN_PLUS);
case '/':
return makeToken(TOKEN_SLASH);
case '&':
return makeToken(match('&') ? TOKEN_AND_AND : TOKEN_AND);
case '#':
return makeToken(TOKEN_MESH);
case '$':
return makeToken(TOKEN_BIG_MONEY);
case '*':
return makeToken(TOKEN_STAR);
case '!':
@ -343,64 +376,135 @@ Token nextToken() {
return errorToken("Unexpected character.");
}
const char* tokenTypeToString(TokenType type) {
const char *token_type_to_string(TokenType type) {
switch (type) {
case TOKEN_EOF: return "EOF";
case TOKEN_IDENTIFIER: return "IDENTIFIER";
case TOKEN_INT_LITERAL: return "INT_LITERAL";
case TOKEN_UINT_LITERAL: return "UINT_LITERAL";
case TOKEN_FLOAT_LITERAL: return "FLOAT_LITERAL";
case TOKEN_STRING_LITERAL: return "STRING_LITERAL";
case TOKEN_TYPE_INT: return "TYPE_INT";
case TOKEN_TYPE_NAT: return "TYPE_NAT";
case TOKEN_TYPE_REAL: return "TYPE_REAL";
case TOKEN_TYPE_STR: return "TYPE_STR";
case TOKEN_KEYWORD_PLEX: return "KEYWORD_PLEX";
case TOKEN_KEYWORD_FN: return "KEYWORD_FN";
case TOKEN_KEYWORD_CONST: return "KEYWORD_CONST";
case TOKEN_KEYWORD_IF: return "KEYWORD_IF";
case TOKEN_KEYWORD_ELSE: return "KEYWORD_ELSE";
case TOKEN_KEYWORD_WHILE: return "KEYWORD_WHILE";
case TOKEN_KEYWORD_FOR: return "KEYWORD_FOR";
case TOKEN_KEYWORD_RETURN: return "KEYWORD_RETURN";
case TOKEN_KEYWORD_USE: return "KEYWORD_USE";
case TOKEN_KEYWORD_INIT: return "KEYWORD_INIT";
case TOKEN_KEYWORD_THIS: return "KEYWORD_THIS";
case TOKEN_KEYWORD_OPEN: return "TOKEN_KEYWORD_OPEN";
case TOKEN_KEYWORD_READ: return "TOKEN_KEYWORD_READ";
case TOKEN_KEYWORD_WRITE: return "TOKEN_KEYWORD_WRITE";
case TOKEN_KEYWORD_REFRESH: return "TOKEN_KEYWORD_REFRESH";
case TOKEN_KEYWORD_CLOSE: return "TOKEN_KEYWORD_CLOSE";
case TOKEN_KEYWORD_NIL: return "KEYWORD_NIL";
case TOKEN_KEYWORD_TRUE: return "KEYWORD_TRUE";
case TOKEN_KEYWORD_FALSE: return "KEYWORD_FALSE";
case TOKEN_OPERATOR_IS: return "OPERATOR_IS";
case TOKEN_OPERATOR_NOT: return "OPERATOR_NOT";
case TOKEN_OPERATOR_AND: return "OPERATOR_AND";
case TOKEN_OPERATOR_OR: return "OPERATOR_OR";
case TOKEN_BANG: return "BANG";
case TOKEN_BANG_EQ: return "BANG_EQ";
case TOKEN_EQ: return "EQ";
case TOKEN_EQ_EQ: return "EQ_EQ";
case TOKEN_GT: return "GT";
case TOKEN_LT: return "LT";
case TOKEN_GTE: return "GTE";
case TOKEN_LTE: return "LTE";
case TOKEN_DOT: return "DOT";
case TOKEN_COMMA: return "COMMA";
case TOKEN_COLON: return "COLON";
case TOKEN_SEMICOLON: return "SEMICOLON";
case TOKEN_PLUS: return "PLUS";
case TOKEN_MINUS: return "MINUS";
case TOKEN_STAR: return "STAR";
case TOKEN_SLASH: return "SLASH";
case TOKEN_LPAREN: return "LPAREN";
case TOKEN_RPAREN: return "RPAREN";
case TOKEN_LBRACE: return "LBRACE";
case TOKEN_RBRACE: return "RBRACE";
case TOKEN_LBRACKET: return "LBRACKET";
case TOKEN_RBRACKET: return "RBRACKET";
case TOKEN_ERROR: return "ERROR";
default: return "UNKNOWN_TOKEN";
case TOKEN_EOF:
return "EOF";
case TOKEN_IDENTIFIER:
return "IDENTIFIER";
case TOKEN_LITERAL_INT:
return "LITERAL_INT";
case TOKEN_LITERAL_NAT:
return "LITERAL_NAT";
case TOKEN_LITERAL_REAL:
return "LITERAL_REAL";
case TOKEN_LITERAL_STR:
return "LITERAL_STR";
case TOKEN_TYPE_INT:
return "TYPE_INT";
case TOKEN_TYPE_NAT:
return "TYPE_NAT";
case TOKEN_TYPE_REAL:
return "TYPE_REAL";
case TOKEN_TYPE_STR:
return "TYPE_STR";
case TOKEN_KEYWORD_PLEX:
return "KEYWORD_PLEX";
case TOKEN_KEYWORD_FN:
return "KEYWORD_FN";
case TOKEN_KEYWORD_CONST:
return "KEYWORD_CONST";
case TOKEN_KEYWORD_IF:
return "KEYWORD_IF";
case TOKEN_KEYWORD_IS:
return "IS";
case TOKEN_KEYWORD_AS:
return "AS";
case TOKEN_KEYWORD_ELSE:
return "KEYWORD_ELSE";
case TOKEN_KEYWORD_WHILE:
return "KEYWORD_WHILE";
case TOKEN_KEYWORD_FOR:
return "KEYWORD_FOR";
case TOKEN_KEYWORD_RETURN:
return "KEYWORD_RETURN";
case TOKEN_KEYWORD_USE:
return "KEYWORD_USE";
case TOKEN_KEYWORD_INIT:
return "KEYWORD_INIT";
case TOKEN_KEYWORD_THIS:
return "KEYWORD_THIS";
case TOKEN_KEYWORD_OPEN:
return "TOKEN_KEYWORD_OPEN";
case TOKEN_KEYWORD_READ:
return "TOKEN_KEYWORD_READ";
case TOKEN_KEYWORD_WRITE:
return "TOKEN_KEYWORD_WRITE";
case TOKEN_KEYWORD_REFRESH:
return "TOKEN_KEYWORD_REFRESH";
case TOKEN_KEYWORD_CLOSE:
return "TOKEN_KEYWORD_CLOSE";
case TOKEN_KEYWORD_NIL:
return "KEYWORD_NIL";
case TOKEN_KEYWORD_TRUE:
return "KEYWORD_TRUE";
case TOKEN_KEYWORD_FALSE:
return "KEYWORD_FALSE";
case TOKEN_KEYWORD_GLOBAL:
return "KEYWORD_GLOBAL";
case TOKEN_OPERATOR_NOT:
return "OPERATOR_NOT";
case TOKEN_OPERATOR_AND:
return "OPERATOR_AND";
case TOKEN_OPERATOR_OR:
return "OPERATOR_OR";
case TOKEN_BANG:
return "BANG";
case TOKEN_BANG_EQ:
return "BANG_EQ";
case TOKEN_EQ:
return "EQ";
case TOKEN_EQ_EQ:
return "EQ_EQ";
case TOKEN_GT:
return "GT";
case TOKEN_LT:
return "LT";
case TOKEN_GTE:
return "GTE";
case TOKEN_LTE:
return "LTE";
case TOKEN_DOT:
return "DOT";
case TOKEN_COMMA:
return "COMMA";
case TOKEN_COLON:
return "COLON";
case TOKEN_SEMICOLON:
return "SEMICOLON";
case TOKEN_PLUS:
return "PLUS";
case TOKEN_MINUS:
return "MINUS";
case TOKEN_STAR:
return "STAR";
case TOKEN_SLASH:
return "SLASH";
case TOKEN_LPAREN:
return "LPAREN";
case TOKEN_RPAREN:
return "RPAREN";
case TOKEN_LBRACE:
return "LBRACE";
case TOKEN_RBRACE:
return "RBRACE";
case TOKEN_LBRACKET:
return "LBRACKET";
case TOKEN_RBRACKET:
return "RBRACKET";
case TOKEN_ARROW_RIGHT:
return "ARROW_RIGHT";
case TOKEN_MESH:
return "MESH";
case TOKEN_BIG_MONEY:
return "BIG_MONEY";
case TOKEN_AND:
return "AND";
case TOKEN_AND_AND:
return "AND_AND";
case TOKEN_ERROR:
return "ERROR";
default:
return "UNKNOWN_TOKEN";
}
}
}

View File

@ -4,11 +4,15 @@
typedef enum {
TOKEN_EOF,
TOKEN_IDENTIFIER,
TOKEN_INT_LITERAL,
TOKEN_UINT_LITERAL,
TOKEN_FLOAT_LITERAL,
TOKEN_STRING_LITERAL,
TOKEN_LITERAL_INT,
TOKEN_LITERAL_NAT,
TOKEN_LITERAL_REAL,
TOKEN_LITERAL_STR,
TOKEN_TYPE_I8,
TOKEN_TYPE_I16,
TOKEN_TYPE_INT,
TOKEN_TYPE_U8,
TOKEN_TYPE_U16,
TOKEN_TYPE_NAT,
TOKEN_TYPE_REAL,
TOKEN_TYPE_STR,
@ -16,6 +20,8 @@ typedef enum {
TOKEN_KEYWORD_FN,
TOKEN_KEYWORD_CONST,
TOKEN_KEYWORD_IF,
TOKEN_KEYWORD_IS,
TOKEN_KEYWORD_AS,
TOKEN_KEYWORD_ELSE,
TOKEN_KEYWORD_WHILE,
TOKEN_KEYWORD_FOR,
@ -23,15 +29,15 @@ typedef enum {
TOKEN_KEYWORD_USE,
TOKEN_KEYWORD_INIT,
TOKEN_KEYWORD_THIS,
TOKEN_KEYWORD_GLOBAL,
TOKEN_KEYWORD_OPEN,
TOKEN_KEYWORD_READ,
TOKEN_KEYWORD_WRITE,
TOKEN_KEYWORD_REFRESH,
TOKEN_KEYWORD_CLOSE,
TOKEN_KEYWORD_CLOSE,
TOKEN_KEYWORD_NIL,
TOKEN_KEYWORD_TRUE,
TOKEN_KEYWORD_FALSE,
TOKEN_OPERATOR_IS,
TOKEN_OPERATOR_NOT,
TOKEN_OPERATOR_AND,
TOKEN_OPERATOR_OR,
@ -39,6 +45,8 @@ typedef enum {
TOKEN_BANG_EQ,
TOKEN_EQ,
TOKEN_EQ_EQ,
TOKEN_AND,
TOKEN_AND_AND,
TOKEN_GT,
TOKEN_LT,
TOKEN_GTE,
@ -51,12 +59,15 @@ typedef enum {
TOKEN_MINUS,
TOKEN_STAR,
TOKEN_SLASH,
TOKEN_MESH,
TOKEN_BIG_MONEY,
TOKEN_LPAREN,
TOKEN_RPAREN,
TOKEN_LBRACE,
TOKEN_RBRACE,
TOKEN_LBRACKET,
TOKEN_RBRACKET,
TOKEN_ARROW_RIGHT,
TOKEN_ERROR
} TokenType;
@ -68,7 +79,7 @@ typedef struct {
} Token;
void initLexer(const char *source);
Token nextToken();
const char* tokenTypeToString(TokenType type);
Token next_token();
const char* token_type_to_string(TokenType type);
#endif
#endif

View File

@ -1,244 +0,0 @@
#include "parser.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uchar.h>
// Helper function to create a new node
static ExprNode *expr_node_create(const char *token, int line) {
ExprNode *node = (ExprNode *)malloc(sizeof(ExprNode));
node->token = strdup(token ? token : "");
node->children = NULL;
node->child_count = 0;
node->line = line;
return node;
}
// Forward declaration
static ExprNode *parse_expression(const char **ptr, int line);
// Skip whitespace characters and comments
static const char *skip_whitespace(const char *ptr) {
while (*ptr) {
// Skip regular whitespace
if (isspace(*ptr)) {
ptr++;
continue;
}
// Check for comment start
if (*ptr == ';') {
// Skip everything until end of line
while (*ptr && *ptr != '\n') {
ptr++;
}
continue;
}
break;
}
return ptr;
}
// Parse a token (atom)
static char *parse_token(const char **ptr, int line) {
const char *start = *ptr;
// Skip leading whitespace and comments
start = skip_whitespace(start);
if (!*start) {
printf("Error at line:%d\n", line);
return NULL;
}
const char *end = start;
// Handle quoted strings
if (*start == '"') {
end++; // Skip opening quote
// Read until closing quote or end of string
while (*end && *end != '"') {
if (*end == '\\' && *(end + 1)) {
end += 2; // Skip escaped character
} else {
end++;
}
}
if (*end == '"') {
end++; // Include closing quote
}
}
// Handle parentheses as separate tokens
else if (*end == '(' || *end == ')') {
end++;
} else {
// Read until whitespace, parentheses, or comment
while (*end && !isspace(*end) && *end != '(' && *end != ')' &&
*end != ';') {
end++;
}
}
if (end == start) {
printf("Error at line:%d\n", line);
return NULL;
}
size_t len = end - start;
char *token = (char *)malloc(len + 1);
memcpy(token, start, len);
token[len] = '\0';
*ptr = end;
return token;
}
// Parse a list (expression starting with '(')
static ExprNode *parse_list(const char **ptr, int line) {
// Skip the opening parenthesis
(*ptr)++;
*ptr = skip_whitespace(*ptr);
if (**ptr == ')') {
// Empty list
(*ptr)++;
return expr_node_create("\0", line);
}
// Parse all children first
ExprNode **temp_children = NULL;
size_t temp_count = 0;
while (**ptr && **ptr != ')') {
ExprNode *child = parse_expression(ptr, line);
if (child) {
// Resize temp children array
ExprNode **new_temp =
(ExprNode **)malloc(sizeof(ExprNode *) * (temp_count + 1));
// Copy existing children
for (size_t i = 0; i < temp_count; i++) {
new_temp[i] = temp_children[i];
}
// Add new child
new_temp[temp_count] = child;
temp_count++;
// Free old array and update
free(temp_children);
temp_children = new_temp;
}
*ptr = skip_whitespace(*ptr);
}
if (**ptr == ')') {
(*ptr)++; // Skip closing parenthesis
} else {
fprintf(stderr, "Error: Missing closing parenthesis at line %d\n", line);
}
// Create the actual node
ExprNode *node;
if (temp_count > 0 && temp_children[0]->child_count == 0) {
// First child is an atom, use it as the operator
node = expr_node_create(temp_children[0]->token, line);
// Move remaining children
node->child_count = temp_count - 1;
if (node->child_count > 0) {
node->children =
(ExprNode **)malloc(sizeof(ExprNode *) * node->child_count);
for (size_t i = 0; i < node->child_count; i++) {
node->children[i] = temp_children[i + 1];
}
}
// Free the first child since we used its token
expr_free(temp_children[0]);
} else {
// No operator or first child is a list
node = expr_node_create("list", line);
node->children = temp_children;
node->child_count = temp_count;
}
if (temp_count == 0) {
free(temp_children);
}
return node;
}
// Parse an expression (either atom or list)
static ExprNode *parse_expression(const char **ptr, int line) {
*ptr = skip_whitespace(*ptr);
if (!**ptr)
return NULL;
if (**ptr == '(') {
return parse_list(ptr, line);
} else {
// Parse atom
char *token = parse_token(ptr, line);
if (token) {
ExprNode *node = expr_node_create(token, line);
free(token);
return node;
}
return NULL;
}
}
// Main parsing function
ExprNode *expr_parse(const char *source, size_t source_len) {
if (!source || source_len == 0)
return NULL;
const char *ptr = source;
int line = 1;
ptr = skip_whitespace(ptr);
if (!*ptr)
return NULL;
return parse_expression(&ptr, line);
}
// Free an Expr AST (and all children)
void expr_free(ExprNode *node) {
if (!node)
return;
free(node->token);
for (size_t i = 0; i < node->child_count; i++) {
expr_free(node->children[i]);
}
free(node->children);
free(node);
}
// Debug: print AST (for dev)
void expr_print(ExprNode *node, int indent) {
if (!node)
return;
for (int i = 0; i < indent; i++) {
printf(" ");
}
if (node->child_count == 0) {
// Atom
printf("Atom: '%s' (line %d)\n", node->token, node->line);
} else {
// List
printf("List: '%s' (line %d) [%zu children]\n", node->token, node->line,
node->child_count);
for (size_t i = 0; i < node->child_count; i++) {
expr_print(node->children[i], indent + 1);
}
}
}

View File

@ -1,25 +0,0 @@
#ifndef PARSER_H
#define PARSER_H
#include <stddef.h> // for size_t
// Forward declare
typedef struct ExprNode ExprNode;
// Node type: atom or list
struct ExprNode {
char *token; // For atoms: the value ("123", "$0", "add")
// For lists: the operator (first token)
ExprNode **children; // Array of child nodes (NULL if atom)
size_t child_count; // 0 if atom
int line; // Source line number (for errors)
};
ExprNode *expr_parse(const char *source, size_t source_len);
ExprNode* expand_macros(ExprNode* node);
ExprNode* expand_lambda(ExprNode* lambda_node);
void expr_free(ExprNode *node);
void expr_print(ExprNode *node, int indent);
void *safe_malloc(size_t size);
#endif

View File

@ -10,6 +10,7 @@ typedef uint16_t u16;
typedef int16_t i16;
typedef uint32_t u32;
typedef int32_t i32;
typedef float f32;
#define true 1
#define false 0

View File

@ -10,12 +10,9 @@ i32 vm_register_device(VM *vm, const char *path, const char *type, void *data,
dev = &vm->devices[vm->dc];
dev->handle = vm->dc++;
strcopy(dev->path, path, DEVICE_PATH_MAX_LENGTH);
dev->path[DEVICE_PATH_MAX_LENGTH - 1] = '\0';
strcopy(dev->type, type, DEVICE_TYPE_MAX_LENGTH);
dev->type[DEVICE_TYPE_MAX_LENGTH - 1] = '\0';
dev->path = path;
dev->type = type;
dev->data = data;
dev->ops = ops;
dev->size = size;

79
src/vm/fixed.c Normal file
View File

@ -0,0 +1,79 @@
#include "fixed.h"
fixed_t int_to_fixed(i32 i) { return i << 16; }
i32 fixed_to_int(fixed_t f) { return f >> 16; }
fixed_t float_to_fixed(f32 f) { return (fixed_t)(f * 65536.0f); }
f32 fixed_to_float(fixed_t f) { return (f32)f / 65536.0f; }
fixed_t fixed_add(fixed_t a, fixed_t b) { return a + b; }
fixed_t fixed_sub(fixed_t a, fixed_t b) { return a - b; }
fixed_t fixed_mul(fixed_t a, fixed_t b) {
i32 a_hi = a >> 16;
u32 a_lo = (u32)a & 0xFFFFU;
i32 b_hi = b >> 16;
u32 b_lo = (u32)b & 0xFFFFU;
i32 p0 = (i32)(a_lo * b_lo) >> 16;
i32 p1 = a_hi * (i32)b_lo;
i32 p2 = (i32)a_lo * b_hi;
i32 p3 = (a_hi * b_hi) << 16;
return p0 + p1 + p2 + p3;
}
fixed_t fixed_div(fixed_t a, fixed_t b) {
i32 negative;
u32 ua, ub, quotient, remainder;
i32 i;
if (b == 0)
return 0;
negative = ((a < 0) ^ (b < 0));
ua = (a < 0) ? -a : a;
ub = (b < 0) ? -b : b;
quotient = 0;
remainder = 0;
for (i = 0; i < 32; i++) {
remainder <<= 1;
if (ua & 0x80000000U) {
remainder |= 1;
}
ua <<= 1;
if (remainder >= ub) {
remainder -= ub;
quotient |= 1;
}
if (i < 31) {
quotient <<= 1;
}
}
return negative ? -(i32)quotient : (i32)quotient;
}
i32 fixed_eq(fixed_t a, fixed_t b) { return a == b; }
i32 fixed_ne(fixed_t a, fixed_t b) { return a != b; }
i32 fixed_lt(fixed_t a, fixed_t b) { return a < b; }
i32 fixed_le(fixed_t a, fixed_t b) { return a <= b; }
i32 fixed_gt(fixed_t a, fixed_t b) { return a > b; }
i32 fixed_ge(fixed_t a, fixed_t b) { return a >= b; }
fixed_t fixed_neg(fixed_t f) { return -f; }
fixed_t fixed_abs(fixed_t f) { return (f < 0) ? -f : f; }

36
src/vm/fixed.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef FIXED_H
#define FIXED_H
#include "common.h"
typedef i32 fixed_t;
#define FIXED_ONE 0x00010000L /* 1.0 in Q16.16 */
#define FIXED_ZERO 0x00000000L /* 0.0 in Q16.16 */
#define FIXED_HALF 0x00008000L /* 0.5 in Q16.16 */
#define FIXED_PI 0x0003243FL /* 3.14159 */
#define FIXED_E 0x0002B7E1L /* 2.71828 */
#define FIXED_MAX 0x7FFFFFFFL /* Maximum positive value */
#define FIXED_MIN 0x80000000L /* Minimum negative value */
fixed_t int_to_fixed(i32 i);
i32 fixed_to_int(fixed_t f);
fixed_t float_to_fixed(f32 f);
f32 fixed_to_float(fixed_t f);
fixed_t fixed_add(fixed_t a, fixed_t b);
fixed_t fixed_sub(fixed_t a, fixed_t b);
fixed_t fixed_mul(fixed_t a, fixed_t b);
fixed_t fixed_div(fixed_t a, fixed_t b);
i32 fixed_eq(fixed_t a, fixed_t b);
i32 fixed_ne(fixed_t a, fixed_t b);
i32 fixed_lt(fixed_t a, fixed_t b);
i32 fixed_le(fixed_t a, fixed_t b);
i32 fixed_gt(fixed_t a, fixed_t b);
i32 fixed_ge(fixed_t a, fixed_t b);
fixed_t fixed_neg(fixed_t f);
fixed_t fixed_abs(fixed_t f);
#endif

View File

@ -95,6 +95,21 @@ bool streq(const char *s1, const char *s2) {
return (*s1 == '\0' && *s2 == '\0');
}
bool strleq(const char *s1, const char *s2, u32 length) {
u32 i;
if (s1 == nil && s2 == nil) return true;
if (s1 == nil || s2 == nil) return false;
i = 0;
while (i < length && *s1 && *s2) {
if (*s1 != *s2) return false;
s1++;
s2++;
i++;
}
if (i == length) return true;
return (*s1 == '\0' && *s2 == '\0');
}
u32 strlength(const char *str) {
u32 i;
@ -238,4 +253,4 @@ void fixed_to_string(i32 value, char *buffer) {
}
strcopy(buffer, end, temp + sizeof(temp) - end);
}
}

View File

@ -4,6 +4,7 @@
#include "common.h"
bool streq(const char *s1, const char *s2);
bool strleq(const char *s1, const char *s2, u32 length);
i32 strcopy(char* to, const char *from, u32 length);
u32 strlength(const char *str);
u32 strnlength(const char *str, u32 max_len);

View File

@ -4,99 +4,102 @@
#include "common.h"
typedef enum {
OP_HALT, /* halt : terminate execution with code [src1] */
OP_EXIT, /* exit : terminate execution with code [src1] */
OP_CALL, /* call : creates a new frame */
OP_RETURN, /* return : returns from a frame to the parent frame */
OP_SYSCALL, /* syscall : src1 src2 src3 src4 more? does a system call based on args */
OP_LOAD_IMM, /* load-immediate : registers[dest] = constant */
OP_LOAD_IND_8, /* load-indirect-8 : registers[dest] = memory[registers[src1]] as u8 */
OP_LOAD_IND_16, /* load-indirect-16 : registers[dest] = memory[registers[src1]] as u8 */
OP_LOAD_IND_32, /* load-indirect-32 : registers[dest] = memory[registers[src1]] as u32 */
OP_LOAD_ABS_8, /* load-absolute-8 : registers[dest] = memory[src1 as u32] */
OP_LOAD_ABS_16, /* load-absolute-16 : registers[dest] = memory[src1 as u32] */
OP_LOAD_ABS_32, /* load-absolute-32 : registers[dest] = memory[src1 as u32] */
OP_LOAD_OFF_8, /* load-offset-8 : registers[dest] = memory[registers[src1] + offset] as u8 */
OP_LOAD_OFF_16, /* load-offset-16 : registers[dest] = memory[registers[src1] + offset] as u16 */
OP_LOAD_OFF_32, /* load-offset-32 : registers[dest] = memory[registers[src1] + offset] as u32 */
OP_STORE_ABS_8, /* store-absolute-8 : memory[dest] = src1 && 0xFF */
OP_STORE_ABS_16, /* store-absolute-16 : memory[dest] = src1 && 0xFFFF */
OP_STORE_ABS_32, /* store-absolute-32 : memory[dest] = src1 */
OP_STORE_IND_8, /* store-indirect-8 : memory[dest] = registers[src1] && 0xFF */
OP_STORE_IND_16, /* store-indirect-16 : memory[dest] = registers[src1] && 0xFFFF*/
OP_STORE_IND_32, /* store-indirect-32 : memory[dest] = registers[src1] */
OP_STORE_OFF_8, /* store-offset-8 : memory[registers[dest] + offset] = registers[src1] && 0xFF */
OP_STORE_OFF_16, /* store-offset-16 : memory[registers[dest] + offset] = registers[src1] && 0xFFFF */
OP_STORE_OFF_32, /* store-offset-32 : memory[registers[dest] + offset] = registers[src1] */
OP_LOAD_IMM, /* load_immediate : locals[dest] = constant */
OP_LOAD_IND_8, /* load_indirect_8 : locals[dest] = memory[locals[src1]] as u8 */
OP_LOAD_IND_16, /* load_indirect_16 : locals[dest] = memory[locals[src1]] as u8 */
OP_LOAD_IND_32, /* load_indirect_32 : locals[dest] = memory[locals[src1]] as u32 */
OP_LOAD_ABS_8, /* load_absolute_8 : locals[dest] = memory[src1 as u32] */
OP_LOAD_ABS_16, /* load_absolute_16 : locals[dest] = memory[src1 as u32] */
OP_LOAD_ABS_32, /* load_absolute_32 : locals[dest] = memory[src1 as u32] */
OP_LOAD_OFF_8, /* load_offset_8 : locals[dest] = memory[locals[src1] + offset] as u8 */
OP_LOAD_OFF_16, /* load_offset_16 : locals[dest] = memory[locals[src1] + offset] as u16 */
OP_LOAD_OFF_32, /* load_offset_32 : locals[dest] = memory[locals[src1] + offset] as u32 */
OP_STORE_ABS_8, /* store_absolute_8 : memory[dest] = src1 && 0xFF */
OP_STORE_ABS_16, /* store_absolute_16 : memory[dest] = src1 && 0xFFFF */
OP_STORE_ABS_32, /* store_absolute_32 : memory[dest] = src1 */
OP_STORE_IND_8, /* store_indirect_8 : memory[dest] = locals[src1] && 0xFF */
OP_STORE_IND_16, /* store_indirect_16 : memory[dest] = locals[src1] && 0xFFFF*/
OP_STORE_IND_32, /* store_indirect_32 : memory[dest] = locals[src1] */
OP_STORE_OFF_8, /* store_offset_8 : memory[locals[dest] + offset] = locals[src1] && 0xFF */
OP_STORE_OFF_16, /* store_offset_16 : memory[locals[dest] + offset] = locals[src1] && 0xFFFF */
OP_STORE_OFF_32, /* store_offset_32 : memory[locals[dest] + offset] = locals[src1] */
OP_MALLOC, /* malloc : dest = fat ptr to memory of ((src1 as size) + 4) */
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_REG_MOV, /* register-move : registers[dest] = registers[src1] */
OP_ADD_INT, /* add-int : registers[dest] = registers[src1] + registers[src2] */
OP_SUB_INT, /* sub-int : registers[dest] = registers[src1] - registers[src2] */
OP_MUL_INT, /* mul-int : registers[dest] = registers[src1] * registers[src2] */
OP_DIV_INT, /* div-int : registers[dest] = registers[src1] / registers[src2] */
OP_ABS_INT, /* abs-int : registers[dest] = | registers[src1] | */
OP_NEG_INT, /* neg-int : registers[dest] = -registers[src1] */
OP_ADD_NAT, /* add-nat : registers[dest] = registers[src1] + registers[src2] */
OP_SUB_NAT, /* sub-nat : registers[dest] = registers[src1] - registers[src2] */
OP_MUL_NAT, /* mul-nat : registers[dest] = registers[src1] * registers[src2] */
OP_DIV_NAT, /* div-nat : registers[dest] = registers[src1] / registers[src2] */
OP_ABS_NAT, /* abs-nat : registers[dest] = | registers[src1] | */
OP_NEG_NAT, /* neg-nat : registers[dest] = -registers[src1] */
OP_ADD_REAL, /* add-real : registers[dest] = registers[src1] + registers[src2] */
OP_SUB_REAL, /* sub-real : registers[dest] = registers[src1] - registers[src2] */
OP_MUL_REAL, /* mul-real : registers[dest] = registers[src1] * registers[src2] */
OP_DIV_REAL, /* div-real : registers[dest] = registers[src1] / registers[src2] */
OP_ABS_REAL, /* abs-real : registers[dest] = | registers[src1] | */
OP_NEG_REAL, /* neg-real : registers[dest] = -registers[src1] */
OP_INT_TO_REAL, /* int-to-real : registers[dest] = registers[src1] as real */
OP_NAT_TO_REAL, /* nat-to-real : registers[dest] = registers[src1] as real */
OP_REAL_TO_INT, /* real-to-int : registers[dest] = registers[src1] as int */
OP_REAL_TO_NAT, /* real-to-nat : registers[dest] = registers[src1] as nat */
OP_BIT_SHIFT_LEFT, /* bit-shift-left : registers[dest] = registers[src1] << registers[src2] */
OP_BIT_SHIFT_RIGHT,/* bit-shift-right : registers[dest] = registers[src1] >> registers[src2] */
OP_BIT_SHIFT_R_EXT,/* bit-shift-r-ext : registers[dest] as i32 = registers[src1] >> registers[src2] */
OP_BAND, /* bit-and : registers[dest] = registers[src1] & registers[src2] */
OP_BOR, /* bit-or : registers[dest] = registers[src1] | registers[src2] */
OP_BXOR, /* bit-xor : registers[dest] = registers[src1] ^ registers[src2] */
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_REG_MOV, /* register_move : locals[dest] = locals[src1] */
OP_ADD_INT, /* add_int : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_INT, /* sub_int : locals[dest] = locals[src1] _ locals[src2] */
OP_MUL_INT, /* mul_int : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_INT, /* div_int : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_INT, /* abs_int : locals[dest] = | locals[src1] | */
OP_NEG_INT, /* neg_int : locals[dest] = -locals[src1] */
OP_ADD_NAT, /* add_nat : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_NAT, /* sub_nat : locals[dest] = locals[src1] _ locals[src2] */
OP_MUL_NAT, /* mul_nat : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_NAT, /* div_nat : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_NAT, /* abs_nat : locals[dest] = | locals[src1] | */
OP_NEG_NAT, /* neg_nat : locals[dest] = -locals[src1] */
OP_ADD_REAL, /* add_real : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_REAL, /* sub_real : locals[dest] = locals[src1] _ locals[src2] */
OP_MUL_REAL, /* mul_real : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_REAL, /* div_real : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_REAL, /* abs_real : locals[dest] = | locals[src1] | */
OP_NEG_REAL, /* neg_real : locals[dest] = _locals[src1] */
OP_INT_TO_REAL, /* int_to_real : locals[dest] = locals[src1] as real */
OP_INT_TO_NAT, /* int_to_nat : locals[dest] = locals[src1] as nat */
OP_NAT_TO_REAL, /* nat_to_real : locals[dest] = locals[src1] as real */
OP_NAT_TO_INT, /* nat_to_int : locals[dest] = locals[src1] as int */
OP_REAL_TO_INT, /* real_to_int : locals[dest] = locals[src1] as int */
OP_REAL_TO_NAT, /* real_to_nat : locals[dest] = locals[src1] as nat */
OP_BIT_SHIFT_LEFT, /* bit_shift_left : locals[dest] = locals[src1] << locals[src2] */
OP_BIT_SHIFT_RIGHT,/* bit_shift_right : locals[dest] = locals[src1] >> locals[src2] */
OP_BIT_SHIFT_R_EXT,/* bit_shift_r_ext : locals[dest] as i32 = locals[src1] >> locals[src2] */
OP_BAND, /* bit_and : locals[dest] = locals[src1] & locals[src2] */
OP_BOR, /* bit_or : locals[dest] = locals[src1] | locals[src2] */
OP_BXOR, /* bit_xor : locals[dest] = locals[src1] ^ locals[src2] */
OP_JMP, /* jump : jump to &dest unconditionally */
OP_JMPF, /* jump-if-flag : jump to &dest if flag != 0 */
OP_JEQ_INT, /* jump-eq-int : jump to &dest if registers[src1] as int == registers[src2] as int */
OP_JNEQ_INT, /* jump-neq-int : jump to &dest if registers[src1] as int != registers[src2] as int */
OP_JGT_INT, /* jump-gt-int : jump to &dest if registers[src1] as int > registers[src2] as int */
OP_JLT_INT, /* jump-lt-int : jump to &dest if registers[src1] as int < registers[src2] as int */
OP_JLE_INT, /* jump-le-int : jump to &dest if registers[src1] as int <= registers[src2] as int */
OP_JGE_INT, /* jump-ge-int : jump to &dest if registers[src1] as int >= registers[src2] as int */
OP_JEQ_NAT, /* jump-eq-nat : jump to &dest if registers[src1] as nat == registers[src2] as nat */
OP_JNEQ_NAT, /* jump-neq-nat : jump to &dest if registers[src1] as nat != registers[src2] as nat */
OP_JGT_NAT, /* jump-gt-nat : jump to &dest if registers[src1] as nat > registers[src2] as nat */
OP_JLT_NAT, /* jump-lt-nat : jump to &dest if registers[src1] as nat < registers[src2] as nat */
OP_JLE_NAT, /* jump-le-nat : jump to &dest if registers[src1] as nat <= registers[src2] as nat */
OP_JGE_NAT, /* jump-ge-nat : jump to &dest if registers[src1] as nat >= registers[src2] as nat */
OP_JEQ_REAL, /* jump-eq-real : jump to &dest if registers[src1] as real == registers[src2] as real */
OP_JNEQ_REAL, /* jump-neq-real : jump to &dest if registers[src1] as real != registers[src2] as real */
OP_JGE_REAL, /* jump-ge-real : jump to &dest if registers[src1] as real >= registers[src2] as real */
OP_JGT_REAL, /* jump-gt-real : jump to &dest if registers[src1] as real > registers[src2] as real */
OP_JLT_REAL, /* jump-lt-real : jump to &dest if registers[src1] as real < registers[src2] as real */
OP_JLE_REAL, /* jump-le-real : jump to &dest if registers[src1] as real <= registers[src2] as real */
OP_STRLEN, /* string-length : registers[dest] = length of str at src1 ptr */
OP_STREQ, /* string-eq : registers[dest] = src1 ptr string == src2 ptr string */
OP_STRCAT, /* string-concat : registers[dest] = ptr of src1 ptr string + src2 ptr string */
OP_STR_GET_CHAR, /* string-get-char : registers[dest] = ptr of src1 ptr str, src2 index of str */
OP_STR_FIND_CHAR, /* string-find-char : registers[dest] = ptr of src1 ptr string, src2 nat8 char */
OP_STR_SLICE, /* string-slice : registers[dest] = ptr of src1 ptr str, src2 start index, src3 end index */
OP_INT_TO_STRING, /* int-to-string : registers[dest] = src1 as str */
OP_NAT_TO_STRING, /* nat-to-string : registers[dest] = src1 as str */
OP_REAL_TO_STRING, /* real-to-string : registers[dest] = src1 as str */
OP_STRING_TO_INT, /* string-to-int : registers[dest] = src1 as int */
OP_STRING_TO_NAT, /* string-to-nat : registers[dest] = src1 as nat */
OP_STRING_TO_REAL /* string-to-real : registers[dest] = src1 as real */
OP_JMPF, /* jump_if_flag : jump to &dest if flag != 0 */
OP_JEQ_INT, /* jump_eq_int : jump to &dest if locals[src1] as int == locals[src2] as int */
OP_JNEQ_INT, /* jump_neq_int : jump to &dest if locals[src1] as int != locals[src2] as int */
OP_JGT_INT, /* jump_gt_int : jump to &dest if locals[src1] as int > locals[src2] as int */
OP_JLT_INT, /* jump_lt_int : jump to &dest if locals[src1] as int < locals[src2] as int */
OP_JLE_INT, /* jump_le_int : jump to &dest if locals[src1] as int <= locals[src2] as int */
OP_JGE_INT, /* jump_ge_int : jump to &dest if locals[src1] as int >= locals[src2] as int */
OP_JEQ_NAT, /* jump_eq_nat : jump to &dest if locals[src1] as nat == locals[src2] as nat */
OP_JNEQ_NAT, /* jump_neq_nat : jump to &dest if locals[src1] as nat != locals[src2] as nat */
OP_JGT_NAT, /* jump_gt_nat : jump to &dest if locals[src1] as nat > locals[src2] as nat */
OP_JLT_NAT, /* jump_lt_nat : jump to &dest if locals[src1] as nat < locals[src2] as nat */
OP_JLE_NAT, /* jump_le_nat : jump to &dest if locals[src1] as nat <= locals[src2] as nat */
OP_JGE_NAT, /* jump_ge_nat : jump to &dest if locals[src1] as nat >= locals[src2] as nat */
OP_JEQ_REAL, /* jump_eq_real : jump to &dest if locals[src1] as real == locals[src2] as real */
OP_JNEQ_REAL, /* jump_neq_real : jump to &dest if locals[src1] as real != locals[src2] as real */
OP_JGE_REAL, /* jump_ge_real : jump to &dest if locals[src1] as real >= locals[src2] as real */
OP_JGT_REAL, /* jump_gt_real : jump to &dest if locals[src1] as real > locals[src2] as real */
OP_JLT_REAL, /* jump_lt_real : jump to &dest if locals[src1] as real < locals[src2] as real */
OP_JLE_REAL, /* jump_le_real : jump to &dest if locals[src1] as real <= locals[src2] as real */
OP_STRLEN, /* string_length : locals[dest] = length of str at src1 ptr */
OP_STREQ, /* string_eq : locals[dest] = src1 ptr string == src2 ptr string */
OP_STRCAT, /* string_concat : locals[dest] = ptr of src1 ptr string + src2 ptr string */
OP_STR_GET_CHAR, /* string_get_char : locals[dest] = ptr of src1 ptr str, src2 index of str */
OP_STR_FIND_CHAR, /* string_find_char : locals[dest] = ptr of src1 ptr string, src2 nat8 char */
OP_STR_SLICE, /* string_slice : locals[dest] = ptr of src1 ptr str, src2 start index, src3 end index */
OP_INT_TO_STRING, /* int_to_string : locals[dest] = src1 as str */
OP_NAT_TO_STRING, /* nat_to_string : locals[dest] = src1 as str */
OP_REAL_TO_STRING, /* real_to_string : locals[dest] = src1 as str */
OP_STRING_TO_INT, /* string_to_int : locals[dest] = src1 as int */
OP_STRING_TO_NAT, /* string_to_nat : locals[dest] = src1 as nat */
OP_STRING_TO_REAL, /* string_to_real : locals[dest] = src1 as real */
OP_MAX_OPCODE /* not really an opcode but used to check max length of ops */
} Opcode;
#define MAX_REGS 32
#define MAX_LOCALS 32
typedef struct frame_s {
u32 registers[MAX_REGS]; /* R0-R31 */
u32 locals[MAX_LOCALS]; /* $0-$31 */
u32 start; /* start of memory block */
u32 end; /* end of memory block */
u32 return_reg; /* register to store return value in parent */
@ -123,14 +126,11 @@ typedef struct device_ops_s {
i32 (*refresh)(void *device_data, u8 *buffer);
} DeviceOps;
#define DEVICE_TYPE_MAX_LENGTH 16 /* 15 chars + null terminator */
#define DEVICE_PATH_MAX_LENGTH 64 /* 63 chars + null terminator */
typedef struct device_s {
char type[DEVICE_TYPE_MAX_LENGTH]; /* e.g., "screen", "mouse", "gpio" */
char path[DEVICE_PATH_MAX_LENGTH]; /* "/dev/screen", "/dev/input/mouse/0",
etc. */
void *data; /* device-specific data */
const char *type; /* e.g., "screen", "mouse", "gpio" */
const char *path; /* "/dev/screen", "/dev/input/mouse/0", etc. */
void *data; /* device_specific data */
DeviceOps *ops; /* operations vtable */
u32 flags; /* permissions, status, etc. */
u32 handle; /* id for fast access in VM */
@ -143,20 +143,25 @@ typedef struct device_s {
#define STACK_SIZE 256
#define DEVICES_SIZE 8
typedef struct vm_s {
u32 pc; /* program counter */
u32 cp; /* code pointer (last allocated opcode) */
u32 fp; /* frame pointer (current frame) */
u32 sp; /* 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 pc; /* program counter */
u32 cp; /* code pointer (last allocated opcode) */
u32 fp; /* frame pointer (current frame) */
u32 sp; /* 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 */
Device devices[DEVICES_SIZE]; /* device definitions */
u8 code[CODE_SIZE]; /* code block */
u8 memory[MEMORY_SIZE]; /* memory block */
u8 code[CODE_SIZE]; /* code block */
u8 memory[MEMORY_SIZE]; /* memory block */
} VM;
/**
* Creates a new vm based on the arch.
*/
bool init_vm(VM *vm);
#define read_u8(vm, location, addr) ((vm)->location[addr])
#define read_u16(vm, location, addr) \

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
((code
(label main
(load-immediate $0 1)
(load-immediate $1 1)
(call &add ($0 $1) $2)
(int-to-string $3 $2)
(call &pln ($3) nil)
(halt 0))
(label add
(add-int $2 $1 $0)
(return $2))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(load-immediate $3 &new-line)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label new-line "\n")))

Binary file not shown.

35
test/add.ul.ir Normal file
View File

@ -0,0 +1,35 @@
global str terminal_namespace = "/dev/term/0";
global str new_line = "\n";
global int x = 1;
global int y = 1;
function main ()
load_absolute_32 x -> $0;
load_absolute_32 y -> $1;
call add ($0 $1) -> $2;
int_to_string $2 -> $3;
call pln ($3);
exit 0;
function add (int a $0, int b $1)
int result $2;
add_int a b -> result;
return result;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;

View File

@ -1,33 +0,0 @@
((code
(label main
(load-immediate $0 35)
(call &fib ($0) $0)
(int-to-string $1 $0)
(call &pln ($1) nil)
(halt 0))
(label fib
(load-immediate $1 2)
(jump-lt-int &base-case $0 $1)
(load-immediate $3 2)
(sub-int $4 $0 $3)
(call &fib ($4) $5)
(load-immediate $3 1)
(sub-int $4 $0 $3)
(call &fib ($4) $6)
(add-int $7 $6 $5)
(return $7)
(label base-case
(return $0)))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label new-line "\n")))

Binary file not shown.

49
test/fib.ul.ir Normal file
View File

@ -0,0 +1,49 @@
global str terminal_namespace = "/dev/term/0";
global str new_line = "\n";
function main ()
int str_n $1;
load_immediate 35 -> $0;
call fib ($0) -> $0;
int_to_string $0 -> str_n;
call pln (str_n);
exit 0;
function fib (int n $0)
load_immediate 2 -> $1;
jump_lt_int base_case n $1;
load_immediate 2 -> $3;
sub_int n $3 -> $4;
call fib ($4) -> $5;
load_immediate 1 -> $3;
sub_int n $3 -> $4;
call fib ($4) -> $6;
add_int $6 $5 -> $7;
return $7;
else base_case;
return n;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;

View File

@ -1,19 +0,0 @@
((code
(label main
(load-immediate $1 &hello-str) ; load hello string ptr
(call &pln ($1) nil)
(halt 0)) ; done
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label new-line "\n")
(label hello-str "nuqneH 'u'?")))

Binary file not shown.

28
test/hello.ul.ir Normal file
View File

@ -0,0 +1,28 @@
global str terminal_namespace = "/dev/term/0";
global str new_line = "\n";
global str hello = "nuqneH 'u'?";
function main ()
str msg $0;
load_address hello -> msg;
call pln (msg);
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;

View File

@ -1,44 +0,0 @@
((code
(label main
(load-immediate $0 5.0)
(load-immediate $1 5000)
(load-immediate $2 0)
(load-immediate $3 -1)
(load-immediate $5 5.0)
(label loop-body
(add-real $0 $0 $5)
(add-int $1 $1 $3)
(jump-ge-int &loop-body $1 $2))
(load-immediate $10 &terminal-namespace)
(load-immediate $11 0)
(syscall OPEN $10 $10 $11) ; Terminal term = open(namespace, flags)
(real-to-nat $1 $0)
(load-immediate $7 &prompt)
(string-length $8 $7)
(syscall WRITE $10 $7 $8) ; print prompt
(load-immediate $8 32)
(malloc $11 $8)
(syscall READ $10 $11 $8) ; read in max 32 byte string
(call &pln ($11) nil)
(nat-to-string $4 $1)
(call &pln ($4) nil)
(real-to-string $3 $0)
(call &pln ($3) nil)
(halt 0))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label prompt "Enter a string: ")
(label new-line "\n")))

Binary file not shown.

View File

@ -1,8 +1,3 @@
/**
* Constants
*/
const str nl = "\n";
plex Terminal {
nat handle;
}
@ -17,17 +12,20 @@ function main() {
a = a + 5.0;
}
nat b = a as nat;
pln(term, "Enter a string:");
pln("Enter a string:");
str user_string = term.read(32);
pln(term, a.str);
pln(term, b.str);
pln(term, user_string);
pln(a as str);
pln(b as str);
pln(user_string);
}
/**
* Print with a newline
*/
function pln(Terminal term, str message) {
function pln(str message) {
const str nl = "\n";
Terminal term = open("/dev/term/0", 0);
write(term, message, message.length);
write(term, nl, nl.length);
}

58
test/loop.ul.ir Normal file
View File

@ -0,0 +1,58 @@
global str terminal_namespace = "/dev/term/0";
global str prompt = "Enter a string:";
global str new_line = "\n";
function main ()
real a $0;
int i $1;
int in_mode $11;
str in_term $10;
load_immediate 5.0 -> a;
load_immediate 5000 -> i;
load_immediate 0 -> $2;
load_immediate -1 -> $3;
load_immediate 5.0 -> $5;
loop loop_body
add_real a $5 -> a;
add_int i $3 -> i;
jump_ge_int loop_body i $2;
load_address terminal_namespace -> in_term;
load_immediate 0 -> in_mode;
syscall OPEN in_term in_mode in_term; // Terminal term = open("/dev/term/0", 0);
nat b $1;
real_to_nat a -> b;
load_address prompt -> $7;
string_length $7 -> $8;
syscall WRITE in_term $7 $8; // print prompt
str user_string $9;
load_immediate 32 -> $8;
malloc $8 -> user_string;
syscall READ in_term user_string $8; // read in max 32 byte string
call pln (user_string);
nat_to_string b -> $4;
call pln ($4);
real_to_string a -> $3;
call pln ($3);
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
load_address terminal_namespace -> term;
load_immediate 0 -> mode;
syscall OPEN term mode term; // Terminal term = open("/dev/term/0", 0);
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;

View File

@ -1,26 +0,0 @@
((code
(label main
(load-immediate $0 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $0 $0 $11)
(load-immediate $1 &help) ; print help message
(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
(syscall READ $0 $4 $1) ; read the string
(call &pln ($0 $4) nil) ; print the string
(halt 0))
(label pln
(load-immediate $3 &new-line)
(string-length $2 $1)
(syscall WRITE $0 $1 $2)
(string-length $4 $3)
(syscall WRITE $0 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label help "Enter a string: ")
(label new-line "\n")))

Binary file not shown.

42
test/malloc.ul.ir Normal file
View File

@ -0,0 +1,42 @@
global str terminal_namespace = "/dev/term/0";
global str prompt = "Enter a string:";
global str new_line = "\n";
function main ()
int in_mode $11;
str in_term $10;
load_address terminal_namespace -> in_term;
load_immediate 0 -> in_mode;
syscall OPEN in_term in_mode in_term; // Terminal term = open("/dev/term/0", 0);
load_address prompt -> $7;
string_length $7 -> $8;
syscall WRITE in_term $7 $8; // print prompt
str user_string $9;
load_immediate 32 -> $8;
malloc $8 -> user_string;
syscall READ in_term user_string $8; // read in max 32 byte string
call pln (user_string);
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;

View File

@ -1,147 +0,0 @@
((code
(label main
; Open screen
; 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 $0 $18 $11) ; open(out Plex screen, in namespace, in flags)
(load-offset-32 $20 $0 8) ; load width
(load-offset-32 $22 $0 12) ; load size
(load-immediate $1 16) ; offset for screen buffer
(add-nat $21 $0 $1)
; open mouse
(load-immediate $16 &mouse-namespace)
(syscall OPEN $15 $16 $11) ; open(out Plex mouse, in namespace, in flags)
; outline_swatch(screen, BLACK, 1, 1);
(load-absolute-32 $1 &BLACK)
(load-immediate $12 1)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
; outline_swatch(screen, WHITE, 1, 1);
(load-absolute-32 $1 &WHITE)
(load-immediate $12 21)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
; screen.draw();
(syscall WRITE $0 $21 $22)
(label draw-loop
; load mouse click data
(syscall REFRESH $15)
(load-offset-8 $9 $15 16) ; load btn1 pressed
(jump-eq-nat &draw-loop $9 $11)
(load-offset-32 $7 $15 8) ; load x
(load-offset-32 $8 $15 12) ; load y
(load-immediate $14 20) ; box size
; first row
(load-absolute-32 $1 &BLACK)
(load-immediate $12 1)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &WHITE)
(load-immediate $12 21)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(syscall WRITE $0 $21 $22)
(load-absolute-32 $22 &SELECTED-COLOR) ; color
(load-immediate $1 5) ; size of brush
(call &draw-box ($21 $20 $22 $7 $8 $1 $1) nil)
(jump &draw-loop))
; Flush and halt
(halt 0))
(label set-color-if-clicked
; (click_x, click_y, box_x, box_y, color, box_size)
; Compute right = box_x + box_size
(add-int $6 $2 $5) ; $6 = right edge
; Compute bottom = box_y + box_size
(add-int $7 $3 $5) ; $7 = bottom edge
; Bounds check: x in [box_x, right] and y in [box_y, bottom]
(jump-lt-int &fail $0 $2)
(jump-gt-int &fail $0 $6)
(jump-lt-int &fail $1 $3)
(jump-gt-int &fail $1 $7)
(load-immediate $10 &SELECTED-COLOR)
(store-absolute-8 $10 $4)
(label fail)
(return nil))
(label draw-outlined-swatch
; (base, color, x, y, width)
; Constants
(load-absolute-32 $5 &GRAY)
(load-absolute-32 $10 &SELECTED-COLOR)
(jump-eq-int &set-selected $10 $1)
(jump-eq-int &end-set-selected $5 $5)
(label set-selected)
(load-absolute-32 $5 &DARK-GRAY)
(label end-set-selected)
(load-immediate $6 20) ; outline size
(load-immediate $7 17) ; fill size
(load-immediate $8 2) ; offset
(call &draw-box ($0 $4 $5 $2 $3 $6 $6) nil)
(add-int $9 $2 $8) ; x + 2
(add-int $10 $3 $8) ; y + 2
(call &draw-box ($0 $4 $1 $9 $10 $7 $7) nil)
(return nil))
(label draw-box
; (base, screen_width, color, x_start, y_start, width, height)
; Compute start address: base + y*640 + x
(mul-int $15 $4 $1) ; $15 = y * 640
(add-int $15 $15 $3) ; $15 += x
(add-nat $15 $0 $15) ; $15 = base + pixel_offset
(load-immediate $25 4)
(add-nat $15 $15 $25) ; need to add offset for fat pointer size
; Outer loop: height times
(load-immediate $30 1) ; increment
(label draw-box-outer
(add-int $27 $15 $5) ; $27 = row end = current + width
(register-move $29 $15) ; $7 = pixel pointer
(memset-8 $29 $2 $5) ; draw row
(add-int $15 $15 $1) ; next row (+= 640)
(sub-int $6 $6 $30) ; decrement row count
(jump-gt-int &draw-box-outer $6 0))
(return nil)))
(data
(label screen-namespace "/dev/screen/0")
(label mouse-namespace "/dev/mouse/0")
(label SELECTED-COLOR 255)
(label BLACK 0)
(label WHITE 255)
(label DARK-GRAY 73)
(label GRAY 146)
(label LIGHT-GRAY 182)))

Binary file not shown.

View File

@ -9,7 +9,7 @@ const byte LIGHT_GRAY = 182;
byte selected_color = 255;
interface Device {
trait Device {
nat handle;
}
@ -21,7 +21,7 @@ plex Screen implements Device {
draw() {
unsafe {
write(this, this.buffer, this.buffer_size);
write(this, this.buffer, this.buffer.size);
}
}
}

180
test/paint-bw.ul.ir Normal file
View File

@ -0,0 +1,180 @@
global str screen_namespace = "/dev/screen/0";
global str mouse_namespace = "/dev/mouse/0";
global byte BLACK = 0;
global byte WHITE = 255;
global byte DARK_GRAY = 73;
global byte GRAY = 146;
global byte LIGHT_GRAY = 182;
global byte SELECTED_COLOR = 255;
function main ()
// Open screen
plex screen $0;
str screen_name $18;
int mode $11;
nat screen_buffer $21;
// use load immediate because it a pointer to a string, not a value
load_address screen_namespace -> screen_name;
load_immediate 0 -> mode;
syscall OPEN screen_name mode screen; // Screen screen = open("/dev/screen/0", 0);
nat width $20;
nat size $22;
load_offset_32 screen 8 -> width; // load width
load_offset_32 screen 12 -> size; // load size
load_immediate 16 -> $1; // offset for screen buffer
add_nat screen $1 -> screen_buffer;
// open mouse
plex mouse $15;
str mouse_name $16;
load_address mouse_namespace -> mouse_name;
syscall OPEN mouse_name mode mouse; // Mouse mouse = open("/dev/mouse/0", 0);
byte color $1;
nat x_pos $12;
nat y_pos $13;
load_absolute_8 BLACK -> color;
load_immediate 1 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 WHITE -> color;
load_immediate 21 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
// screen.draw
syscall WRITE screen screen_buffer size;
nat m_zero $11;
loop draw_loop
// load mouse click data
syscall REFRESH mouse;
byte left_down $9;
load_offset_8 mouse 16 -> left_down; // load btn1 pressed
jump_eq_nat draw_loop left_down m_zero;
nat mouse_x $7;
nat mouse_y $8;
load_offset_32 mouse 8 -> mouse_x; // load x
load_offset_32 mouse 12 -> mouse_y; // load y
nat box_size $14;
load_immediate 20 -> box_size;
// first row
load_absolute_8 BLACK -> color;
load_immediate 1 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 WHITE -> color;
load_immediate 21 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
syscall WRITE screen screen_buffer size;
byte selected_color $25;
load_absolute_8 SELECTED_COLOR -> selected_color;
nat brush_size $19;
load_immediate 5 -> brush_size;
call draw_box (screen_buffer width selected_color mouse_x mouse_y brush_size brush_size);
jump draw_loop;
// Flush and exit
exit 0;
function set_color_if_clicked (int click_x $0, int click_y $1,
int box_x $2, int box_y $3, byte check_color $4, int bsize $5)
// Compute right
int right_edge $6;
add_int box_x bsize -> right_edge;
// Compute bottom = box_y + bsize
int bottom_edge $7;
add_int box_y bsize -> bottom_edge;
// Bounds check: x in [box_x, right] and y in [box_y, bottom]
jump_lt_int fail click_x box_x;
jump_gt_int fail click_x right_edge;
jump_lt_int fail click_y box_y;
jump_gt_int fail click_y bottom_edge;
store_absolute_8 check_color -> SELECTED_COLOR;
else fail
return;
function draw_outlined_swatch(nat dos_base $0,
byte swatch_color $1, int x $2, int y $3, int dos_width $4)
// Constants
nat background_color $5;
load_absolute_8 GRAY -> background_color;
byte dos_selected_color $10;
load_absolute_8 SELECTED_COLOR -> dos_selected_color;
jump_eq_int set_selected swatch_color dos_selected_color;
jump end_set_selected;
do set_selected
load_absolute_8 DARK_GRAY -> background_color;
else end_set_selected
nat outline_size $6;
load_immediate 20 -> outline_size;
nat fill_size $7;
load_immediate 17 -> fill_size;
nat dos_offset $8;
load_immediate 2 -> dos_offset;
call draw_box (dos_base dos_width background_color x y outline_size outline_size);
add_int x dos_offset -> $9; // x + 2
add_int y dos_offset -> $10; // y + 2
call draw_box (dos_base dos_width swatch_color $9 $10 fill_size fill_size);
return;
function draw_box (nat db_base $0, nat screen_width $1,
byte box_color $2, nat x_start $3, nat y_start $4,
nat db_width $5, nat height $6)
// Compute start address: base + y*640 + x
nat offset $15;
mul_int y_start screen_width -> offset;
add_int offset x_start -> offset;
add_nat offset db_base -> offset;
nat fat_ptr_size $25;
load_immediate 4 -> fat_ptr_size;
add_nat offset fat_ptr_size -> offset; // need to add offset for fat pointer size
int i $30;
load_immediate 1 -> i;
int zero $26;
load_immediate 0 -> zero;
loop draw_box_outer
memset_8 box_color db_width -> offset; // draw row
add_int offset screen_width -> offset; // next row += 640
sub_int height i -> height; // decrement row count
jump_gt_int draw_box_outer height zero;
return;

View File

@ -1,261 +0,0 @@
((code
(label main
; Open screen
; 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 $0 $0 $11) ; Screen screen = open(namespace, flags)
(load-offset-32 $20 $0 8) ; load width
(load-offset-32 $22 $0 12) ; load size
(load-immediate $1 16) ; pointer offset for screen buffer
(add-nat $21 $0 $1)
; open mouse
(load-immediate $16 &mouse-namespace)
(syscall OPEN $15 $16 $11) ; Mouse mouse = open(namespace, flags)
; outline_swatch(screen, BLACK, 1, 1);
(load-absolute-32 $1 &BLACK)
(load-immediate $12 1)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
; outline_swatch(screen, WHITE, 1, 1);
(load-absolute-32 $1 &WHITE)
(load-immediate $12 21)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &CHARCOAL)
(load-immediate $12 1)
(load-immediate $13 21)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &DARK-GRAY)
(load-immediate $12 21)
(load-immediate $13 21)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &RED)
(load-immediate $12 1)
(load-immediate $13 41)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &ORANGE)
(load-immediate $12 21)
(load-immediate $13 41)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &YELLOW)
(load-immediate $12 1)
(load-immediate $13 61)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &GREEN)
(load-immediate $12 21)
(load-immediate $13 61)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &BLUE)
(load-immediate $12 1)
(load-immediate $13 81)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(load-absolute-32 $1 &PURPLE)
(load-immediate $12 21)
(load-immediate $13 81)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
; screen.draw();
(syscall WRITE $0 $21 $22)
(label draw-loop
; load mouse click data
(syscall REFRESH $15)
(load-offset-8 $9 $15 16) ; load btn1 pressed
(jump-eq-nat &draw-loop $9 $11)
(load-offset-32 $7 $15 8) ; load x
(load-offset-32 $8 $15 12) ; load y
(load-immediate $14 20) ; box size
; outline_swatch(screen, BLACK, 1, 1);
(load-absolute-32 $1 &BLACK)
(load-immediate $12 1)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
; outline_swatch(screen, WHITE, 1, 1);
(load-absolute-32 $1 &WHITE)
(load-immediate $12 21)
(load-immediate $13 1)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &CHARCOAL)
(load-immediate $12 1)
(load-immediate $13 21)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &DARK-GRAY)
(load-immediate $12 21)
(load-immediate $13 21)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &RED)
(load-immediate $12 1)
(load-immediate $13 41)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &ORANGE)
(load-immediate $12 21)
(load-immediate $13 41)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &YELLOW)
(load-immediate $12 1)
(load-immediate $13 61)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &GREEN)
(load-immediate $12 21)
(load-immediate $13 61)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &BLUE)
(load-immediate $12 1)
(load-immediate $13 81)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(load-absolute-32 $1 &PURPLE)
(load-immediate $12 21)
(load-immediate $13 81)
(call &draw-outlined-swatch ($21 $1 $12 $13 $20) nil)
(call &set-color-if-clicked ($7 $8 $12 $13 $1 $14) nil)
(syscall WRITE $0 $21 $22)
(load-absolute-32 $22 &SELECTED-COLOR) ; color
(load-immediate $1 5) ; size of brush
(call &draw-box ($21 $20 $22 $7 $8 $1 $1) nil)
(jump &draw-loop))
; Flush and halt
(halt 0))
(label set-color-if-clicked
; (click_x, click_y, box_x, box_y, color, box_size)
; Compute right = box_x + box_size
(add-int $6 $2 $5) ; $6 = right edge
; Compute bottom = box_y + box_size
(add-int $7 $3 $5) ; $7 = bottom edge
; Bounds check: x in [box_x, right] and y in [box_y, bottom]
(jump-lt-int &fail $0 $2)
(jump-gt-int &fail $0 $6)
(jump-lt-int &fail $1 $3)
(jump-gt-int &fail $1 $7)
(load-immediate $10 &SELECTED-COLOR)
(store-absolute-8 $10 $4)
(label fail)
(return nil))
(label draw-outlined-swatch
; (base, color, x, y, width)
; Constants
(load-absolute-32 $5 &GRAY)
(load-absolute-32 $10 &SELECTED-COLOR)
(jump-eq-int &set-selected $10 $1)
(jump-eq-int &end-set-selected $5 $5)
(label set-selected)
(load-absolute-32 $5 &DARK-GRAY)
(label end-set-selected)
(load-immediate $6 20) ; outline size
(load-immediate $7 17) ; fill size
(load-immediate $8 2) ; offset
(call &draw-box ($0 $4 $5 $2 $3 $6 $6) nil)
(add-int $9 $2 $8) ; x + 2
(add-int $10 $3 $8) ; y + 2
(call &draw-box ($0 $4 $1 $9 $10 $7 $7) nil)
(return nil))
(label draw-box
; (base, screen_width, color, x_start, y_start, width, height)
; Compute start address: base + y*640 + x
(mul-int $15 $4 $1) ; $15 = y * 640
(add-int $15 $15 $3) ; $15 += x
(add-nat $15 $0 $15) ; $15 = base + pixel_offset
(load-immediate $25 4)
(add-nat $15 $15 $25) ; need to add offset for fat pointer size
; Outer loop: height times
(load-immediate $30 1) ; increment
(label draw-box-outer
(add-int $27 $15 $5) ; $27 = row end = current + width
(register-move $29 $15) ; $7 = pixel pointer
(memset-8 $29 $2 $5) ; draw row
(add-int $15 $15 $1) ; next row (+= 640)
(sub-int $6 $6 $30) ; decrement row count
(jump-gt-int &draw-box-outer $6 0))
(return nil)))
(data
(label screen-namespace "/dev/screen/0")
(label mouse-namespace "/dev/mouse/0")
(label SELECTED-COLOR 255)
(label BLACK 0)
(label WHITE 255)
(label CHARCOAL 36)
(label DARK-GRAY 73)
(label GRAY 146)
(label LIGHT-GRAY 182)
(label DARK-RED 128)
(label RED 224)
(label DARK-YELLOW 144)
(label YELLOW 252)
(label DARK-TEAL 9)
(label TEAL 18)
(label DARK-GREEN 12)
(label GREEN 16)
(label LIME 28)
(label LIGHT-CYAN 159)
(label NAVY 2)
(label BLUE 3)
(label DEEP-SKY-BLUE 10)
(label LIGHT-BLUE 19)
(label PURPLE 131)
(label LIGHT-PURPLE 147)
(label DARK-MAGENTA 130)
(label MAGENTA 227)
(label PLUM 129)
(label PINK 226)
(label SADDLE-BROWN 72)
(label PERU 141)
(label SIENNA 136)
(label ORANGE 241)
(label DARK-ORANGE 208)
(label GOLD 244)))

Binary file not shown.

View File

@ -9,7 +9,7 @@ const byte LIGHT_GRAY = 182;
byte selected_color = 255;
interface Device {
trait Device {
nat handle;
}
@ -20,9 +20,7 @@ plex Screen implements Device {
byte[] buffer;
draw() {
unsafe {
write(this, this.buffer, this.buffer.length);
}
write(this, this.buffer, this.buffer.length);
}
}
@ -82,8 +80,6 @@ function set_color(int box_size, int bx, int by, int mx, int my, byte color) {
if (my > bottom) return;
selected_color = color;
return;
}
/**
@ -97,22 +93,17 @@ function outline_swatch(Device screen, byte color, int x, int y) {
rectangle(screen, bg_color, x, y, 20, 20);
rectangle(screen, color, x + 2, y + 2, 17, 17);
return;
}
/**
* Draw a rectangle
*/
function rectangle(Device screen, byte color, int x, int y, int width, int height) {
// we need unsafe because we are using pointers `.ptr` and `memset` directly
// unsafe takes the guardrails off and allows you to access/modify memory directly
unsafe {
int base = y * screen.width + x + screen.buffer.ptr + 4;
do (int i = height; i > 0; i--) {
int row = base + width;
memset(screen.buffer, row, color, width);
base += screen.width;
}
}
return;
int base = y * screen.width + x + screen.buffer.ptr + 4;
do (int i = height; i > 0; i--) {
int row = base + width;
memset(screen.buffer, row, color, width);
base += screen.width;
}
}

294
test/paint.ul.ir Normal file
View File

@ -0,0 +1,294 @@
global str screen_namespace = "/dev/screen/0";
global str mouse_namespace = "/dev/mouse/0";
global byte BLACK = 0;
global byte WHITE = 255;
global byte DARK_GRAY = 73;
global byte GRAY = 146;
global byte LIGHT_GRAY = 182;
global byte CHARCOAL = 36;
global byte DARK_RED = 128;
global byte RED = 224;
global byte DARK_YELLOW = 144;
global byte YELLOW = 252;
global byte DARK_TEAL = 9;
global byte TEAL = 18;
global byte DARK_GREEN = 12;
global byte GREEN = 16;
global byte LIME = 28;
global byte LIGHT_CYAN = 159;
global byte NAVY = 2;
global byte BLUE = 3;
global byte DEEP_SKY_BLUE = 10;
global byte LIGHT_BLUE = 19;
global byte PURPLE = 131;
global byte LIGHT_PURPLE = 147;
global byte DARK_MAGENTA = 130;
global byte MAGENTA = 227;
global byte PLUM = 129;
global byte PINK = 226;
global byte SADDLE_BROWN = 72;
global byte PERU = 141;
global byte SIENNA = 136;
global byte ORANGE = 241;
global byte DARK_ORANGE = 208;
global byte GOLD = 244;
global byte SELECTED_COLOR = 255;
function main ()
// Open screen
plex screen $0;
str screen_name $18;
int mode $11;
nat screen_buffer $21;
// use load immediate because it a pointer to a string, not a value
load_address screen_namespace -> screen_name;
load_immediate 0 -> mode;
syscall OPEN screen_name mode screen; // Screen screen = open("/dev/screen/0", 0);
nat width $20;
nat size $22;
load_offset_32 screen 8 -> width; // load width
load_offset_32 screen 12 -> size; // load size
load_immediate 16 -> $1; // offset for screen buffer
add_nat screen $1 -> screen_buffer;
// open mouse
plex mouse $15;
str mouse_name $16;
load_address mouse_namespace -> mouse_name;
syscall OPEN mouse_name mode mouse; // Mouse mouse = open("/dev/mouse/0", 0);
byte color $1;
nat x_pos $12;
nat y_pos $13;
load_absolute_8 BLACK -> color;
load_immediate 1 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 WHITE -> color;
load_immediate 21 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 CHARCOAL -> color;
load_immediate 1 -> x_pos;
load_immediate 21 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 DARK_GRAY -> color;
load_immediate 21 -> x_pos;
load_immediate 21 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 RED -> color;
load_immediate 1 -> x_pos;
load_immediate 41 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 ORANGE -> color;
load_immediate 21 -> x_pos;
load_immediate 41 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 YELLOW -> color;
load_immediate 1 -> x_pos;
load_immediate 61 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 GREEN -> color;
load_immediate 21 -> x_pos;
load_immediate 61 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 BLUE -> color;
load_immediate 1 -> x_pos;
load_immediate 81 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
load_absolute_8 PURPLE -> color;
load_immediate 21 -> x_pos;
load_immediate 81 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
// screen.draw
syscall WRITE screen screen_buffer size;
nat m_zero $11;
loop draw_loop
// load mouse click data
syscall REFRESH mouse;
byte left_down $9;
load_offset_8 mouse 16 -> left_down; // load btn1 pressed
jump_eq_nat draw_loop left_down m_zero;
nat mouse_x $7;
nat mouse_y $8;
load_offset_32 mouse 8 -> mouse_x; // load x
load_offset_32 mouse 12 -> mouse_y; // load y
nat box_size $14;
load_immediate 20 -> box_size;
// first row
load_absolute_8 BLACK -> color;
load_immediate 1 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 WHITE -> color;
load_immediate 21 -> x_pos;
load_immediate 1 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 CHARCOAL -> color;
load_immediate 1 -> x_pos;
load_immediate 21 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 DARK_GRAY -> color;
load_immediate 21 -> x_pos;
load_immediate 21 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 RED -> color;
load_immediate 1 -> x_pos;
load_immediate 41 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 ORANGE -> color;
load_immediate 21 -> x_pos;
load_immediate 41 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 YELLOW -> color;
load_immediate 1 -> x_pos;
load_immediate 61 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 GREEN -> color;
load_immediate 21 -> x_pos;
load_immediate 61 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 BLUE -> color;
load_immediate 1 -> x_pos;
load_immediate 81 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
load_absolute_8 PURPLE -> color;
load_immediate 21 -> x_pos;
load_immediate 81 -> y_pos;
call draw_outlined_swatch (screen_buffer color x_pos y_pos width);
call set_color_if_clicked (mouse_x mouse_y x_pos y_pos color box_size);
syscall WRITE screen screen_buffer size;
byte selected_color $25;
load_absolute_8 SELECTED_COLOR -> selected_color;
nat brush_size $19;
load_immediate 5 -> brush_size;
call draw_box (screen_buffer width selected_color mouse_x mouse_y brush_size brush_size);
jump draw_loop;
// Flush and exit
exit 0;
function set_color_if_clicked (int click_x $0, int click_y $1,
int box_x $2, int box_y $3, byte check_color $4, int bsize $5)
// Compute right
int right_edge $6;
add_int box_x bsize -> right_edge;
// Compute bottom = box_y + bsize
int bottom_edge $7;
add_int box_y bsize -> bottom_edge;
// Bounds check: x in [box_x, right] and y in [box_y, bottom]
jump_lt_int fail click_x box_x;
jump_gt_int fail click_x right_edge;
jump_lt_int fail click_y box_y;
jump_gt_int fail click_y bottom_edge;
store_absolute_8 check_color -> SELECTED_COLOR;
else fail
return;
function draw_outlined_swatch(nat dos_base $0,
byte swatch_color $1, int x $2, int y $3, int dos_width $4)
// Constants
nat background_color $5;
load_absolute_8 GRAY -> background_color;
byte dos_selected_color $10;
load_absolute_8 SELECTED_COLOR -> dos_selected_color;
jump_eq_int set_selected swatch_color dos_selected_color;
jump end_set_selected;
do set_selected
load_absolute_8 DARK_GRAY -> background_color;
else end_set_selected
nat outline_size $6;
load_immediate 20 -> outline_size;
nat fill_size $7;
load_immediate 17 -> fill_size;
nat dos_offset $8;
load_immediate 2 -> dos_offset;
call draw_box (dos_base dos_width background_color x y outline_size outline_size);
add_int x dos_offset -> $9; // x + 2
add_int y dos_offset -> $10; // y + 2
call draw_box (dos_base dos_width swatch_color $9 $10 fill_size fill_size);
return;
function draw_box (nat db_base $0, nat screen_width $1,
byte box_color $2, nat x_start $3, nat y_start $4,
nat db_width $5, nat height $6)
// Compute start address: base + y*640 + x
nat offset $15;
mul_int y_start screen_width -> offset;
add_int offset x_start -> offset;
add_nat offset db_base -> offset;
nat fat_ptr_size $25;
load_immediate 4 -> fat_ptr_size;
add_nat offset fat_ptr_size -> offset; // need to add offset for fat pointer size
int i $30;
load_immediate 1 -> i;
int zero $26;
load_immediate 0 -> zero;
loop draw_box_outer
memset_8 box_color db_width -> offset; // draw row
add_int offset screen_width -> offset; // next row += 640
sub_int height i -> height; // decrement row count
jump_gt_int draw_box_outer height zero;
return;

View File

@ -1,22 +0,0 @@
((code
(label main
(load-absolute-32 $0 &x)
(load-absolute-32 $1 &y)
(add-real $2 $1 $0)
(real-to-string $3 $2)
(call &pln ($3) nil)
(halt 0))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data (label terminal-namespace "/dev/term/0")
(label new-line "\n")
(label x 1.0)
(label y 2.0)))

Binary file not shown.

View File

@ -2,8 +2,6 @@
* Constants
*/
const str nl = "\n";
const real x = 1.0;
const real y = 1.0;
plex Terminal {
nat handle;
@ -13,7 +11,8 @@ plex Terminal {
* Main function
*/
function main() {
pln((x + y).str);
pln((1.0 + 1.0) as str);
exit(0);
}
/**

28
test/simple.ul.ir Normal file
View File

@ -0,0 +1,28 @@
global str terminal_namespace = "/dev/term/0";
global str new_line = "\n";
function main ()
load_immediate 1.0 -> $0;
load_immediate 2.0 -> $1;
add_real $0 $1 -> $0;
real_to_string $0 -> $0;
call pln ($0);
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int mode $5;
str term_ns $6;
load_immediate 0 -> mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;

View File

@ -1,71 +0,0 @@
((code
(label main
; Open screen
; use load immediate because it is a pointer to a string, not a value
(load-immediate $18 &screen-namespace)
(syscall OPEN $0 $18 $11) ; open(out Plex screen, in namespace, in flags)
(nat-to-string $5 $0)
(call &pln ($5) nil)
(load-offset-32 $20 $0 8) ; load width
(nat-to-string $5 $20)
(call &pln ($5) nil)
(load-offset-32 $22 $0 12) ; load size
(nat-to-string $5 $22)
(call &pln ($5) nil)
(load-immediate $1 16) ; offset for screen buffer
(add-nat $21 $0 $1)
(nat-to-string $5 $21)
(call &pln ($5) nil)
; open mouse
(load-immediate $16 &mouse-namespace)
(syscall OPEN $15 $16 $11) ; open(out Plex mouse, in namespace, in flags)
(syscall WRITE $0 $21 $22) ; redraw
(label draw-loop
; load mouse click data
(syscall REFRESH $15)
(load-offset-8 $9 $15 16) ; load btn1 pressed
(jump-eq-nat &draw-loop $9 $11)
(load-offset-32 $7 $15 8) ; load x
(load-offset-32 $8 $15 12) ; load y
; Compute start address: y*width + x
(mul-nat $30 $8 $20) ; $15 = y * width
(add-nat $30 $30 $7) ; $15 += x
(add-nat $30 $30 $21) ; $15 += pixel_offset
(load-immediate $1 4) ; need to add offset for fat pointer size
(add-nat $30 $30 $1)
(load-absolute-32 $3 &WHITE) ; color
(store-absolute-8 $30 $3) ; draw color at screen [x,y]
(syscall WRITE $0 $21 $22) ; redraw
(jump &draw-loop))
(halt 0))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(string-length $2 $0)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label screen-namespace "/dev/screen/0")
(label mouse-namespace "/dev/mouse/0")
(label terminal-namespace "/dev/term/0")
(label new-line "\n")
(label WHITE 255)))

Binary file not shown.

View File

@ -1,8 +1,11 @@
/**
* Constants
*/
const str nl = "\n";
const nat WHITE = 255;
const str screen_namespace = "/dev/screen/0"
const str mouse_namespace = "/dev/mouse/0"
const str terminal_namespace = "/dev/term/0"
const str new_line = "\n"
const byte WHITE = 255
/**
* Devices
@ -37,19 +40,19 @@ plex Mouse {
* Main function
*/
function main() {
Screen screen = open("/dev/screen/0", 0);
pln(screen.handle.str);
pln(screen.width.str);
pln(screen.size.str);
Screen screen = open(screen_namespace, 0);
pln(screen.handle as str);
pln(screen.width as str);
pln(screen.size as str);
unsafe {
pln(screen.screen_buffer.ptr.str);
pln(screen.screen_buffer.ptr as str);
}
Mouse mouse = open("/dev/mouse/0", 0);
Mouse mouse = open(mouse_namespace, 0);
screen.draw();
loop {
if (mouse.btn1) {
if (mouse.left) {
unsafe {
screen.buffer[mouse.y * width + mouse.x +
screen.buffer.ptr + 4] = WHITE;
@ -63,7 +66,7 @@ function main() {
* Print with a newline
*/
function pln(str message) {
Terminal term = open("/dev/term/0", 0);
Terminal term = open(terminal_namespace, 0);
write(term, message, message.length);
write(term, nl, nl.length);
}

92
test/window.ul.ir Normal file
View File

@ -0,0 +1,92 @@
global str screen_namespace = "/dev/screen/0";
global str mouse_namespace = "/dev/mouse/0";
global str terminal_namespace = "/dev/term/0";
global str new_line = "\n";
global byte WHITE = 255;
function main ()
plex screen $0;
plex mouse $1;
str tmp_str $2;
byte color $3;
bool left_down $4;
int mode $5;
nat offset_temp $6;
nat x $7;
nat y $8;
nat width $9;
nat screen_buffer $10;
nat buffer_size $11;
nat pixel_pos $12;
nat fat_ptr_size $13;
load_address screen_namespace -> screen;
load_immediate 0 -> mode;
syscall OPEN screen mode screen;
nat_to_string screen -> tmp_str;
call pln (tmp_str);
load_offset_32 screen 8 -> width;
nat_to_string width -> tmp_str;
call pln (tmp_str);
load_offset_32 screen 12 -> buffer_size;
nat_to_string buffer_size -> tmp_str;
call pln (tmp_str);
load_immediate 16 -> offset_temp;
add_nat screen offset_temp -> screen_buffer;
nat_to_string screen_buffer -> tmp_str;
call pln (tmp_str);
// open mouse
load_address mouse_namespace -> mouse;
syscall OPEN mouse mode mouse;
syscall WRITE screen screen_buffer buffer_size; // redraw
loop draw_loop
// load mouse click data
syscall REFRESH mouse;
load_offset_8 mouse 16 -> left_down;
jump_eq_nat draw_loop left_down mode; // mode = 0 / false
load_offset_32 mouse 8 -> x;
load_offset_32 mouse 12 -> y;
// Compute start address: y *width + x
mul_nat y width -> pixel_pos;
add_nat x pixel_pos -> pixel_pos;
add_nat screen_buffer pixel_pos -> pixel_pos;
load_immediate 4 -> fat_ptr_size;
add_nat pixel_pos fat_ptr_size -> pixel_pos;
load_absolute_32 WHITE -> color;
store_absolute_8 color -> pixel_pos; // draw color at screen [x,y]
syscall WRITE screen screen_buffer buffer_size; // redraw
jump draw_loop;
exit 0;
function pln (str message $0)
plex term $1;
int msg_length $2;
str nl $3;
int nl_length $4;
int pln_mode $5;
str term_ns $6;
load_immediate 0 -> pln_mode;
load_address terminal_namespace -> term_ns;
syscall OPEN term_ns pln_mode term;
string_length message -> msg_length;
syscall WRITE term message msg_length;
load_address new_line -> nl;
string_length nl -> nl_length;
syscall WRITE term nl nl_length;
return;