WORKING DEVICES!!

This commit is contained in:
zongor 2025-09-02 22:57:34 -07:00
parent b6f5766a07
commit fde5c82d0d
14 changed files with 560 additions and 574 deletions

1
LANG.md Normal file
View File

@ -0,0 +1 @@
is a programming language for the purpose of creating three dimensional video games and graphical user interfaces that work on constrained systems, microcontrollers, retro consoles, and the using emscripten. it conforms to permacomputing principles. permacomputing encourages the maximization of hardware lifespan, minimization of energy usage and focuses on the use of already available computational resources. it values maintenance and refactoring of systems to keep them efficient, instead of planned obsolescence, permacomputing practices planned longevity. it is about using computation only when it has a strengthening effect on ecosystems. it is written in c eighty nine, has a cisc like instruction format of one byte opcode and three byte operand. thirty two general purpose registers, one stack for function calls, one stack for return values. zero initialized plexs are always valid. memory is managed via frame based arenas function scopes defines a memory frame. heap allocations push pointers within this frame. when a frame exits, the pointer is reset like stack based gc. 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 copy the parents value and append it to its own frame with the modification. it has a type system of nat, int, str, real. it also has a plex based on a generalized technique for symbol manipulation and numerical calculation by douglas ross, allows for efficient symbolic manipulation of data, works like a struct but syntactically looks like a class. versioning and shadowing when you redefine a plex, the old version is shadowed but preserved unless explicitly removed. the language is statically typed and similar to c but with some array semantic ideas from fortran like row major, fortran style replaces need for vec or mat. arrays are first class values, the compiler generates array multiplication code. also the idea of having a ref type to pass by reference similar to a pointer in c. it has a coroutine system that uses the yield keyword so it can do async operations. it has a abstraction layer for devices that work as system calls that can open, read, write, close, and use ioclt for extra system calls that are beyond ops. it has a abstraction called a tunnel that is inspired by plan nine. it allows files, web requests, sockets, etc. to be viewed through a simple unified interface attach open communication. clunk close communication. flush cancels long operation and dumps whatever is in buffer. open opens a tunnel for doing operations on. create creates the plex from the database graph file from file structure. read reads from a tunnel. write writes to a tunnel. remove removes the plex from the database graph file from file structure. stat returns the status of the file resource. version returns the version code for the connected tunnel. walk moves around the filesystem or through the graph. goals are run on most things and preserve digital art and games forever. mit zero license or public domain with an ethical understanding this software should not be used to accelerate obsolescence, exploit users, or harm ecosystems.

View File

@ -1,4 +1,4 @@
#+TITLE: Zirûl: A Language for Enduring Realities
#+TITLE: A Language for Enduring Realities
#+SUBTITLE: "Shape realities that outlast their makers."
#+AUTHOR: Zongor
#+EMAIL: archive@zirul-lang.org
@ -16,16 +16,6 @@
· · · ᛚ]
#+END_SRC
Zirûl (pronounced /'zi.rul/) is a permacomputing oriented, statically-typed, zero-allocation programming language and virtual machine system designed for:
- =Constrained systems=: microcontrollers, retro consoles (PS1, N64, Mac Classic)
- =Portable environments=: Web (Emscripten), embedded, CLI
- =Permacomputing=: long-term survivability, sustainability, minimalism
- =3D world-building=: built-in primitives for PS1/N64-style rendering
- =Live development=: hot reloading, REPL, shadowing, symbol versioning
It runs on the =Reality Engine=, a minimal C89 VM inspired by Uxn, Plan 9, and Forth - but built for =spatial software=, =deterministic execution=, and =software that lasts=.
* The Reality Engine
The =Reality Engine= is a register-based virtual machine designed to render not just graphics, but *realities* - persistent, inspectable, reproducible computational worlds.
@ -55,9 +45,16 @@ This ensures:
- Perfect reproducibility
- Safe failure modes
* Zirûl Language (ZREL)
Zirûl is a statically-typed language with **first-class arrays**, **immediate-mode semantics**, and **symbolic clarity** - designed for game developers, artists, and preservationists.
is a permacomputing oriented, statically-typed language with **first-class arrays**, **immediate-mode semantics**, and **symbolic clarity**
- =Constrained systems=: microcontrollers, retro consoles (PS1, N64, Mac Classic)
- =Portable environments=: Web (Emscripten), embedded, CLI
- =Permacomputing=: long-term survivability, sustainability, minimalism
- =3D world-building=: built-in primitives for PS1/N64-style rendering
- =Live development=: hot reloading, REPL, shadowing, symbol versioning
It runs on the =Reality Engine=, a minimal C89 VM inspired by Uxn, Plan 9, and Forth - but built for =spatial software=, =deterministic execution=, and =software that lasts=.
**Core Types**
@ -215,7 +212,7 @@ Tunnels make I/O **uniform, composable, and archival**.
* Development Environment
Zirûl supports **live coding** and **temporal development**:
supports **live coding** and **temporal development**:
**Live Coding Features**
- Hot module reloading: inject code while VM runs
@ -231,20 +228,6 @@ Zirûl supports **live coding** and **temporal development**:
- Can be saved, restored, or archived as =.zbin= files
- Are fully deterministic and reproducible
* Sustainability & Permacomputing
Zirûl embodies **permacomputing principles**:
| Principle | How Zirûl Supports It |
|-------------------------------|------------------------------------------------|
| Long-Term Survivability | C89 base, no deps, text-based source |
| Zero Dynamic Allocation | Frame arenas, copy-on-write, ZII |
| Energy Efficiency | Fixed-point math, no GC, minimal ops |
| Maintenance Over Obsolescence | Versioned plexes, refactorable code |
| Use Existing Hardware | Runs on microcontrollers, old consoles |
| Sustainability | No bloat, no planned obsolescence |
| Compute to Strengthen | Designed for art, education, preservation |
* Getting Started
**Build the Reality Engine**
@ -284,7 +267,7 @@ function main(int argc, str[] argv) {
bool running = true;
while (running) {
window("Zirûl Client", w, h) {
window("Client", w, h) {
splitbox(parent.size, 0.25) {
canvas() {
if (button("Logout")) {
@ -350,7 +333,7 @@ With an ethical understanding:
* Join the Effort
Zirûl is a community project. We welcome:
The Reality Engine is a community project. We welcome:
- Compiler contributors
- Port developers (Web, Game Boy, etc.)
- Artists and game designers

View File

@ -15,22 +15,26 @@ LDLIBS_WASM =
# Source and build configuration
# ----------------------------
COMMON_SRC = $(wildcard *.c)
ARCH_SRC_EMSCRIPTEN = arch/emscripten/main.c
ARCH_SRC_LINUX = arch/linux/main.c
EXEC_NATIVE = zre
EXEC_WASM = zre.wasm
# Build directories
# Separate architecture sources
ARCH_SRC_LINUX = $(wildcard arch/linux/*.c)
ARCH_SRC_EMSCRIPTEN = $(wildcard arch/emscripten/*.c)
# Object directories
OBJ_DIR_NATIVE = build/native/obj
OBJ_DIR_WASM = build/wasm/obj
# Create output paths
# Common objects from root
OBJ_NATIVE = $(addprefix $(OBJ_DIR_NATIVE)/,$(notdir $(COMMON_SRC:.c=.o)))
OBJ_WASM = $(addprefix $(OBJ_DIR_WASM)/,$(notdir $(COMMON_SRC:.c=.o)))
# Add main.c object files
OBJ_NATIVE += $(OBJ_DIR_NATIVE)/$(notdir $(ARCH_SRC_LINUX:.c=.o))
OBJ_WASM += $(OBJ_DIR_WASM)/$(notdir $(ARCH_SRC_EMSCRIPTEN:.c=.o))
# Architecture-specific objects
OBJ_ARCH_NATIVE = $(addprefix $(OBJ_DIR_NATIVE)/,$(notdir $(ARCH_SRC_LINUX:.c=.o)))
OBJ_ARCH_WASM = $(addprefix $(OBJ_DIR_WASM)/,$(notdir $(ARCH_SRC_EMSCRIPTEN:.c=.o)))
# Executables
EXEC_NATIVE = zre
EXEC_WASM = zre.wasm
# Phony targets
.PHONY: all clean install wasm native emscripten linux macos
@ -44,7 +48,7 @@ native: linux
linux: $(EXEC_NATIVE)
$(EXEC_NATIVE): $(OBJ_NATIVE)
$(EXEC_NATIVE): $(OBJ_NATIVE) $(OBJ_ARCH_NATIVE)
$(CC_NATIVE) $(LDFLAGS_NATIVE) $^ $(LDLIBS_NATIVE) -o $@
# WASM build rules
@ -53,7 +57,7 @@ wasm: emscripten
emscripten: $(EXEC_WASM)
$(EXEC_WASM): $(OBJ_WASM)
$(EXEC_WASM): $(OBJ_WASM) $(OBJ_ARCH_WASM)
$(CC_WASM) $(LDFLAGS_WASM) $^ $(LDLIBS_WASM) -o $@
# Object file rules
@ -66,12 +70,12 @@ $(OBJ_DIR_WASM)/%.o: %.c
@mkdir -p $(dir $@)
$(CC_WASM) $(CFLAGS_WASM) -c $< -o $@
# Add specific rules for main.c files
$(OBJ_DIR_NATIVE)/$(notdir $(ARCH_SRC_LINUX:.c=.o)): $(ARCH_SRC_LINUX)
# Architecture-specific source rules
$(OBJ_DIR_NATIVE)/%.o: arch/linux/%.c
@mkdir -p $(dir $@)
$(CC_NATIVE) $(CFLAGS_NATIVE) -c $< -o $@
$(OBJ_DIR_WASM)/$(notdir $(ARCH_SRC_EMSCRIPTEN:.c=.o)): $(ARCH_SRC_EMSCRIPTEN)
$(OBJ_DIR_WASM)/%.o: arch/emscripten/%.c
@mkdir -p $(dir $@)
$(CC_WASM) $(CFLAGS_WASM) -c $< -o $@
@ -84,4 +88,4 @@ clean:
# ------------------------
install: native
install -d $(DESTDIR)/usr/local/bin
install $(EXEC_NATIVE) $(DESTDIR)/usr/local/bin/
install $(EXEC_NATIVE) $(DESTDIR)/usr/local/bin/

View File

@ -1,4 +1,3 @@
#include "../../debug.h"
#include "../../test.h"
#include "../../vm.h"
#include <emscripten.h>
@ -12,11 +11,6 @@ void mainloop() {
}
int main(int argc, char **argv) {
vm.frames_size = FRAMES_SIZE;
vm.return_stack_size = STACK_SIZE;
vm.stack_size = STACK_SIZE;
vm.memory_size = MEMORY_SIZE;
test_add_function_compile(vm.memory);
/* test_recursive_function_compile(vm.memory); */
emscripten_set_main_loop(mainloop, 0, 1);

View File

@ -1,156 +0,0 @@
#include "../../compiler.h"
#include "../../vm.h"
#include <SDL2/SDL.h>
#define MAX_SRC_SIZE 16384
void compileFile(const char *path, VM *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);
compile(source, vm);
}
static void repl(VM *vm) {
char line[1024];
for (;;) {
printf("> ");
if (!fgets(line, sizeof(line), stdin)) {
printf("\n");
break;
}
/* reset the code counter to 0 */
vm->cp = 0;
vm->sp = 0;
vm->pc = 0;
vm->mp = 0;
compile(line, vm);
while (step_vm(vm));
}
exit(0);
}
int main(int argc, char **argv) {
VM vm = {0};
vm.frames_size = FRAMES_SIZE;
vm.return_stack_size = STACK_SIZE;
vm.stack_size = STACK_SIZE;
vm.memory_size = MEMORY_SIZE;
if (argc == 1) {
repl(&vm);
} else if (argc == 2) {
compileFile(argv[1], &vm);
} else {
fprintf(stderr, "Usage: %s <file.zrl>\n", argv[0]);
return 64;
}
uint32_t buffer_size = 640 * 480 * sizeof(uint32_t);
/*
Device screen;
screen.type = SCREEN;
screen.s = (Screen){.width = (uint8_t)480,
.height = (uint8_t)640,
.allocated = {vm.mp, buffer_size},
.buffer = &vm.memory[vm.mp]};
vm.devices[vm.dp++] = screen;
vm.mp += buffer_size;
Device mouse;
mouse.type = MOUSE;
mouse.m = (Mouse){.x = 0, .y = 0, .btn1 = 0, .btn2 = 0, .btn3 = 0};
vm.devices[vm.dp++] = mouse;
Device keyboard;
keyboard.type = KEYBOARD;
const uint8_t *state = SDL_GetKeyboardState(NULL);
keyboard.k = (Keyboard){.length = SDL_NUM_SCANCODES, .keys = state};
vm.devices[vm.dp++] = keyboard;
SDL_Window *window =
SDL_CreateWindow("Reality Engine VM", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, screen.s.width, screen.s.height,
SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
screen.s.width, screen.s.height);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
bool running = true;
while (running) {
step_vm(&vm);
SDL_PumpEvents();
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
case SDL_MOUSEBUTTONDOWN:
printf("mouse_down: x=%d, y=%d, btn=%d\n", event.button.x,
event.button.y, event.button.button);
break;
case SDL_MOUSEMOTION:
printf("mouse_move: x=%d, y=%d\n", event.motion.x, event.motion.y);
break;
case SDL_FINGERDOWN:
printf("touch_down x=%d, y=%d\n",
(int)(event.tfinger.x * screen.s.width),
(int)(event.tfinger.y * screen.s.height));
break;
case SDL_FINGERMOTION:
printf("touch_move x=%d, y=%d\n",
(int)(event.tfinger.x * screen.s.width),
(int)(event.tfinger.y * screen.s.height));
break;
}
}
SDL_UpdateTexture(texture, NULL, screen.s.buffer,
screen.s.width * sizeof(uint32_t));
SDL_RenderClear(renderer);
SDL_Rect output_rect;
SDL_RenderGetViewport(renderer, &output_rect);
float scale_x = (float)output_rect.w / screen.s.width;
float scale_y = (float)output_rect.h / screen.s.height;
float scale = SDL_min(scale_x, scale_y);
SDL_Rect dstrect = {(int)((output_rect.w - screen.s.width * scale) / 2),
(int)((output_rect.h - screen.s.height * scale) / 2),
(int)(screen.s.width * scale),
(int)(screen.s.height * scale)};
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
SDL_RenderPresent(renderer);
}
*/
return 0;
}

98
src/arch/linux/devices.c Normal file
View File

@ -0,0 +1,98 @@
#include "devices.h"
int screen_open(void *data, uint32_t mode) {
ScreenDeviceData *screen = (ScreenDeviceData *)data;
screen->window =
SDL_CreateWindow("Reality Engine VM", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, screen->width, screen->height,
SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
if (!screen->window)
return -1;
screen->renderer = SDL_CreateRenderer(
screen->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!screen->renderer)
return -1;
screen->texture = SDL_CreateTexture(
screen->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
screen->width, screen->height);
if (!screen->texture)
return -1;
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
return 0;
}
int screen_read(void *data, uint8_t *buffer, uint32_t size) { return -1; }
int screen_write(void *data, const uint8_t *buffer, uint32_t size) {
ScreenDeviceData *screen = (ScreenDeviceData *)data;
if (size > screen->framebuffer_size * sizeof(uint32_t)) {
return -1;
}
memcpy(&screen->vm->memory[screen->framebuffer_pos], buffer, size);
return 0;
}
int screen_close(void *data) {
ScreenDeviceData *screen = (ScreenDeviceData *)data;
if (screen->texture) {
SDL_DestroyTexture(screen->texture);
screen->texture = NULL;
}
if (screen->renderer) {
SDL_DestroyRenderer(screen->renderer);
screen->renderer = NULL;
}
if (screen->window) {
SDL_DestroyWindow(screen->window);
screen->window = NULL;
}
return 0;
}
/* MOUSE */
int mouse_open(void *data, uint32_t mode) { return 0; }
int mouse_read(void *data, uint8_t *buffer, uint32_t size) {
MouseDeviceData *mouse = (MouseDeviceData *)data;
if (size < 3 * sizeof(uint32_t))
return -1;
uint32_t *out = (uint32_t *)buffer;
out[0] = mouse->x;
out[1] = mouse->y;
out[2] = (mouse->btn1 | (mouse->btn2 << 1) | (mouse->btn3 << 2) |
(mouse->btn4 << 3));
return 0;
}
int mouse_write(void *data, const uint8_t *buffer, uint32_t size) { return -1; }
int mouse_close(void *data) { return 0; }
int keyboard_open(void *data, uint32_t mode) { return 0; }
int keyboard_read(void *data, uint8_t *buffer, uint32_t size) {
KeyboardDeviceData *kbd = (KeyboardDeviceData *)data;
if (size < (uint32_t)kbd->key_count)
return -1;
memcpy(buffer, kbd->keys, kbd->key_count);
return 0;
}
int keyboard_write(void *data, const uint8_t *buffer, uint32_t size) {
return -1; /* not writable */
}
int keyboard_close(void *data) { return 0; }

47
src/arch/linux/devices.h Normal file
View File

@ -0,0 +1,47 @@
#include "../../device.h"
#include "../../vm.h"
#include <SDL2/SDL.h>
/* Screen device data */
typedef struct screen_device_data_s {
uint32_t width;
uint32_t height;
uint32_t framebuffer_pos;
uint32_t framebuffer_size;
VM* vm;
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
} ScreenDeviceData;
/* Mouse device data */
typedef struct mouse_device_data_s {
uint32_t x;
uint32_t y;
uint8_t btn1;
uint8_t btn2;
uint8_t btn3;
uint8_t btn4;
} MouseDeviceData;
/* Keyboard device data */
typedef struct keyboard_device_data_s {
const uint8_t *keys;
int32_t key_count;
} KeyboardDeviceData;
int screen_open(void *data, uint32_t mode);
int screen_read(void *data, uint8_t *buffer, uint32_t size);
int screen_write(void *data, const uint8_t *buffer, uint32_t size);
int screen_close(void *data);
int mouse_open(void *data, uint32_t mode);
int mouse_read(void *data, uint8_t *buffer, uint32_t size);
int mouse_write(void *data, const uint8_t *buffer, uint32_t size);
int mouse_close(void *data);
int keyboard_open(void *data, uint32_t mode);
int keyboard_read(void *data, uint8_t *buffer, uint32_t size);
int keyboard_write(void *data, const uint8_t *buffer, uint32_t size);
int keyboard_close(void *data);

View File

@ -1,12 +1,42 @@
#include "../../compiler.h"
#include "../../debug.h"
#include "../../test.h"
#include "../../vm.h"
#include "devices.h"
#include <SDL2/SDL.h>
#include <stdint.h>
#include <string.h>
#define MAX_SRC_SIZE 16384
static DeviceOps screen_ops = {
.open = screen_open,
.read = screen_read,
.write = screen_write,
.close = screen_close,
.ioctl = NULL
};
static DeviceOps mouse_ops = {
.open = mouse_open,
.read = mouse_read,
.write = mouse_write,
.close = mouse_close,
.ioctl = NULL
};
static DeviceOps keyboard_ops= {
.open = keyboard_open,
.read = keyboard_read,
.write = keyboard_write,
.close = keyboard_close,
.ioctl = NULL
};
static ScreenDeviceData screen_data = {0};
static MouseDeviceData mouse_data = {0};
static KeyboardDeviceData keyboard_data = {0};
void compileFile(const char *path, VM *vm) {
FILE *f = fopen(path, "rb");
if (!f) {
@ -30,7 +60,7 @@ void compileFile(const char *path, VM *vm) {
compile(source, vm);
}
static void repl(VM *vm) {
void repl(VM *vm) {
char line[1024];
for (;;) {
printf("> ");
@ -56,14 +86,14 @@ enum FlagType {
FLAG_NONE = 0,
FLAG_DEV_MODE = 1,
FLAG_TEST_MODE = 2,
FLAG_DUMP_ROM = 4
/* Easy to extend with bitwise OR */
FLAG_DUMP_ROM = 4,
FLAG_GUI_MODE = 8,
};
#define MAX_INPUT_FILES 16 /* Adjust based on your system's constraints */
struct CompilerConfig {
int flags;
uint32_t flags;
char *input_files[MAX_INPUT_FILES];
int input_file_count;
};
@ -85,6 +115,8 @@ int parse_arguments(int argc, char *argv[], struct CompilerConfig *config) {
/* Long and short flag handling */
if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dev") == 0) {
config->flags |= FLAG_DEV_MODE;
} else 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 ||
@ -108,21 +140,42 @@ int parse_arguments(int argc, char *argv[], struct CompilerConfig *config) {
return 0;
}
void register_sdl_devices(VM *vm) {
screen_data.vm = vm;
screen_data.width = 640;
screen_data.height = 480;
screen_data.framebuffer_size = 640 * 480;
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);
mouse_data.x = 0;
mouse_data.y = 0;
mouse_data.btn1 = 0;
mouse_data.btn2 = 0;
mouse_data.btn3 = 0;
mouse_data.btn4 = 0;
vm_register_device(vm, "/dev/mouse/0", "mouse", &mouse_data, &mouse_ops);
keyboard_data.keys = SDL_GetKeyboardState(&keyboard_data.key_count);
vm_register_device(vm, "/dev/keyboard/0", "keyboard", &keyboard_data,
&keyboard_ops);
}
int main(int argc, char *argv[]) {
struct CompilerConfig config = {0};
if (parse_arguments(argc, argv, &config) != 0) {
fprintf(stderr,
"Usage: %s [-d] [-t] [-r] [-o] <file1.zrl> [file2.zrl] ...\n",
"Usage: %s [-d] [-t] [-g] [-o] <file1.zrl> [file2.zrl] ...\n",
argv[0]);
return 64;
}
VM vm = {0};
vm.frames_size = FRAMES_SIZE;
vm.return_stack_size = STACK_SIZE;
vm.stack_size = STACK_SIZE;
vm.memory_size = MEMORY_SIZE;
if (config.input_file_count == 0) {
repl(&vm);
@ -137,13 +190,113 @@ int main(int argc, char *argv[]) {
}
if (config.flags & FLAG_DUMP_ROM) {
core_dump(&vm);
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 %u\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 %u\n",
memory_written, MEMORY_SIZE);
fclose(file);
return EXIT_FAILURE;
}
fclose(file);
}
}
bool running = true;
register_sdl_devices(&vm);
if (config.flags & FLAG_GUI_MODE) {
uint32_t i;
while (running) {
for (i = 0; i < vm.dc; i++) {
Device *dev = &vm.devices[i];
if (strcmp(dev->type, "mouse") == 0) {
MouseDeviceData *mouse = (MouseDeviceData *)dev->data;
SDL_PumpEvents();
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
case SDL_MOUSEMOTION:
mouse->x = event.motion.x;
mouse->y = event.motion.y;
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT)
mouse->btn1 = 1;
if (event.button.button == SDL_BUTTON_RIGHT)
mouse->btn2 = 1;
if (event.button.button == SDL_BUTTON_MIDDLE)
mouse->btn3 = 1;
if (event.button.button == SDL_BUTTON_X1)
mouse->btn4 = 1;
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT)
mouse->btn1 = 0;
if (event.button.button == SDL_BUTTON_RIGHT)
mouse->btn2 = 0;
if (event.button.button == SDL_BUTTON_MIDDLE)
mouse->btn3 = 0;
if (event.button.button == SDL_BUTTON_X1)
mouse->btn4 = 0;
break;
}
}
}
}
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;
if (screen->texture && screen->renderer) {
SDL_UpdateTexture(screen->texture, NULL, &vm.memory[screen->framebuffer_pos],
screen->width * sizeof(uint32_t));
SDL_RenderClear(screen->renderer);
SDL_Rect output_rect;
SDL_RenderGetViewport(screen->renderer, &output_rect);
float scale_x = (float)output_rect.w / screen->width;
float scale_y = (float)output_rect.h / screen->height;
float scale = SDL_min(scale_x, scale_y);
SDL_Rect dstrect = {
(int)((output_rect.w - screen->width * scale) / 2),
(int)((output_rect.h - screen->height * scale) / 2),
(int)(screen->width * scale), (int)(screen->height * scale)};
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &dstrect);
SDL_RenderPresent(screen->renderer);
}
break;
}
}
}
} else {
while (running) {
running = step_vm(&vm);
}
}
bool running = true;
while (running) {
running = step_vm(&vm);
}
return 0;
}

View File

@ -1,207 +0,0 @@
#include "debug.h"
/**
* Dumps the vm memory and code to a file.
*/
int core_dump(VM *vm) {
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, vm->code_size, file);
if (code_written != vm->code_size) {
fprintf(stderr, "Incomplete code write: %zu bytes written out of %u\n", code_written, vm->code_size);
fclose(file);
return EXIT_FAILURE;
}
size_t memory_written = fwrite(vm->memory, 1, vm->memory_size, file);
if (memory_written != vm->memory_size) {
fprintf(stderr, "Incomplete memory write: %zu bytes written out of %u\n", memory_written, vm->memory_size);
fclose(file);
return EXIT_FAILURE;
}
fclose(file);
return EXIT_SUCCESS;
}
/**
* Print opcode.
*/
void printOp(uint8_t op, uint8_t dest, uint8_t src1, uint8_t src2) {
switch (op) {
case OP_HALT:
printf("[HALT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_CALL:
printf("[CALL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_RETURN:
printf("[RETURN] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_LOADI:
printf("[LOADI] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_LOADU:
printf("[LOADU] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_LOADF:
printf("[LOADF] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_STOREI:
printf("[STOREI] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_STOREU:
printf("[STOREU] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_STOREF:
printf("[STOREF] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_PUSHI:
printf("[PUSHI] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_PUSHU:
printf("[PUSHU] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_PUSHF:
printf("[PUSHF] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_POPI:
printf("[POPI] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_POPU:
printf("[POPU] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_POPF:
printf("[POPF] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_ADD_INT:
printf("[ADD_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_SUB_INT:
printf("[SUB_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_MUL_INT:
printf("[MUL_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_DIV_INT:
printf("[DIV_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JEQ_INT:
printf("[JEQ_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JGT_INT:
printf("[JGT_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JLT_INT:
printf("[JLT_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JLE_INT:
printf("[JLE_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JGE_INT:
printf("[JGE_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_INT_TO_REAL:
printf("[INT_TO_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_ADD_UINT:
printf("[ADD_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_SUB_UINT:
printf("[SUB_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_MUL_UINT:
printf("[MUL_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_DIV_UINT:
printf("[DIV_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JEQ_UINT:
printf("[JEQ_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JGT_UINT:
printf("[JGT_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JLT_UINT:
printf("[JLT_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JLE_UINT:
printf("[JLE_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JGE_UINT:
printf("[JGE_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_UINT_TO_REAL:
printf("[UINT_TO_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_ADD_REAL:
printf("[ADD_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_SUB_REAL:
printf("[SUB_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_MUL_REAL:
printf("[MUL_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_DIV_REAL:
printf("[DIV_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JEQ_REAL:
printf("[JEQ_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JGE_REAL:
printf("[JGE_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JGT_REAL:
printf("[JGT_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JLT_REAL:
printf("[JLT_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JLE_REAL:
printf("[JLE_REAL] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_REAL_TO_INT:
printf("[REAL_TO_INT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_REAL_TO_UINT:
printf("[REAL_TO_UINT] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_MEM_MOV:
printf("[MEM_MOV] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_MEM_ALLOC:
printf("[MEM_ALLOC] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_MEM_SWAP:
printf("[MEM_SWP] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_JMP:
printf("[JMP] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_INT_TO_STRING:
printf("[INT_TO_STRING] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_UINT_TO_STRING:
printf("[UINT_TO_STRING] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_REAL_TO_STRING:
printf("[REAL_TO_STRING] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_READ_STRING:
printf("[READ_STRING] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_PRINT_STRING:
printf("[PRINT_STRING] $%d, $%d, $%d\n", dest, src1, src2);
break;
case OP_CMP_STRING:
printf("[CMP_STRING] $%d, $%d, $%d\n", dest, src1, src2);
break;
}
}

View File

@ -1,10 +0,0 @@
#ifndef ZRL_DEBUG_H
#define ZRL_DEBUG_H
#include "vm.h"
#include "opcodes.h"
int core_dump(VM *vm);
void printOp(uint8_t op, uint8_t dest, uint8_t src1, uint8_t src2);
#endif

View File

@ -5,55 +5,56 @@
#include <stdint.h>
typedef enum {
OP_HALT, /* halt : terminate execution */
OP_JMP, /* jump : jump to address dest unconditionally */
OP_GET_PC, /* pc : dest = current program counter */
OP_CALL, /* call : creates a new frame */
OP_RETURN, /* retn : returns from a frame to the parent frame */
OP_LOAD, /* load : dest = &[next memory location] */
OP_STORE, /* stor : next memory location = src1 as float */
OP_PUSH, /* push : push str ref from register onto the stack and copy str */
OP_POP, /* pop : pop int from stack onto the register */
OP_REG_MOV, /* rmov : dest = src1 */
OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */
OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */
OP_MEM_MOV, /* mmov : &dest = &src1 */
OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */
OP_GET, /* get : dest = ptr : dest = memory[ptr] */
OP_PUT, /* put : ptr = src1 : memory[ptr] = src */
OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */
OP_SYSCALL, /* sysc : */
OP_ADD_INT, /* addi : dest = src1 + src2 */
OP_SUB_INT, /* subi : dest = src1 - src2 */
OP_MUL_INT, /* muli : dest = src1 * src2 */
OP_DIV_INT, /* divi : dest = src1 / src2 */
OP_ADD_UINT, /* addu : dest = src1 + src2 */
OP_SUB_UINT, /* subu : dest = src1 - src2 */
OP_MUL_UINT, /* mulu : dest = src1 * src2 */
OP_DIV_UINT, /* divu : dest = src1 / src2 */
OP_ADD_REAL, /* addr : dest = src1 + src2 */
OP_SUB_REAL, /* subr : dest = src1 - src2 */
OP_MUL_REAL, /* mulr : dest = src1 * src2 */
OP_DIV_REAL, /* divr : dest = src1 / src2 */
OP_INT_TO_REAL, /* itor : dest = src1 as real */
OP_UINT_TO_REAL, /* utor : dest = src1 as real */
OP_REAL_TO_INT, /* rtoi : dest = src1 as int */
OP_REAL_TO_UINT, /* rtou : dest = src1 as uint */
OP_JEQ_INT, /* jeqi : jump to address dest if src1 as int == src2 as int */
OP_JGT_INT, /* jgti : jump to address dest if src1 as int > src2 as int*/
OP_JLT_INT, /* jlti : jump to address dest if src1 as int < src2 as int */
OP_JLE_INT, /* jlei : jump to address dest if src1 as int <= src2 as int */
OP_JGE_INT, /* jgei : jump to address dest if src1 as int >= src2 as int*/
OP_JEQ_UINT, /* jequ : jump to address dest if src1 as int == src2 as uint */
OP_JGT_UINT, /* jgtu : jump to address dest if src1 as int > src2 as uint*/
OP_JLT_UINT, /* jltu : jump to address dest if src1 as int < src2 as uint */
OP_JLE_UINT, /* jleu : jump to address dest if src1 as int <= src2 as uint */
OP_JGE_UINT, /* jgeu : jump to address dest if src1 as int >= src2 as uint*/
OP_JEQ_REAL, /* jeqr : jump to address dest if src1 as real == src2 as real */
OP_JGE_REAL, /* jgtr : jump to address dest if src1 as real >= src2 as real */
OP_JGT_REAL, /* jltr : jump to address dest if src1 as real > src2 as real */
OP_JLT_REAL, /* jler : jump to address dest if src1 as real < src2 as real */
OP_JLE_REAL, /* jger : jump to address dest if src1 as real <= src2 as real */
OP_HALT, /* halt : terminate execution */
OP_JMP, /* jump : jump to address dest unconditionally */
OP_GET_PC, /* pc : dest = current program counter */
OP_CALL, /* call : creates a new frame */
OP_RETURN, /* retn : returns from a frame to the parent frame */
OP_LOAD, /* load : dest = &[next memory location] */
OP_STORE, /* stor : next memory location = src1 as float */
OP_PUSH, /* push : push str ref from register onto the stack and copy str */
OP_POP, /* pop : pop int from stack onto the register */
OP_REG_MOV, /* rmov : dest = src1 */
OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */
OP_GET_ACC, /* gacc : dest = accumulator */
OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */
OP_MEM_MOV, /* mmov : &dest = &src1 */
OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */
OP_GET, /* get : dest = ptr : dest = memory[ptr] */
OP_PUT, /* put : ptr = src1 : memory[ptr] = src */
OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */
OP_SYSCALL, /* sysc : */
OP_ADD_INT, /* addi : dest = src1 + src2 */
OP_SUB_INT, /* subi : dest = src1 - src2 */
OP_MUL_INT, /* muli : dest = src1 * src2 */
OP_DIV_INT, /* divi : dest = src1 / src2 */
OP_ADD_UINT, /* addu : dest = src1 + src2 */
OP_SUB_UINT, /* subu : dest = src1 - src2 */
OP_MUL_UINT, /* mulu : dest = src1 * src2 */
OP_DIV_UINT, /* divu : dest = src1 / src2 */
OP_ADD_REAL, /* addr : dest = src1 + src2 */
OP_SUB_REAL, /* subr : dest = src1 - src2 */
OP_MUL_REAL, /* mulr : dest = src1 * src2 */
OP_DIV_REAL, /* divr : dest = src1 / src2 */
OP_INT_TO_REAL, /* itor : dest = src1 as real */
OP_UINT_TO_REAL, /* utor : dest = src1 as real */
OP_REAL_TO_INT, /* rtoi : dest = src1 as int */
OP_REAL_TO_UINT, /* rtou : dest = src1 as uint */
OP_JEQ_INT, /* jeqi : jump to address dest if src1 as int == src2 as int */
OP_JGT_INT, /* jgti : jump to address dest if src1 as int > src2 as int*/
OP_JLT_INT, /* jlti : jump to address dest if src1 as int < src2 as int */
OP_JLE_INT, /* jlei : jump to address dest if src1 as int <= src2 as int */
OP_JGE_INT, /* jgei : jump to address dest if src1 as int >= src2 as int*/
OP_JEQ_UINT, /* jequ : jump to address dest if src1 as int == src2 as uint */
OP_JGT_UINT, /* jgtu : jump to address dest if src1 as int > src2 as uint*/
OP_JLT_UINT, /* jltu : jump to address dest if src1 as int < src2 as uint */
OP_JLE_UINT, /* jleu : jump to address dest if src1 as int <= src2 as uint */
OP_JGE_UINT, /* jgeu : jump to address dest if src1 as int >= src2 as uint*/
OP_JEQ_REAL, /* jeqr : jump to address dest if src1 as real == src2 as real */
OP_JGE_REAL, /* jgtr : jump to address dest if src1 as real >= src2 as real */
OP_JGT_REAL, /* jltr : jump to address dest if src1 as real > src2 as real */
OP_JLT_REAL, /* jler : jump to address dest if src1 as real < src2 as real */
OP_JLE_REAL, /* jger : jump to address dest if src1 as real <= src2 as real */
OP_INT_TO_STRING, /* itos : dest = src1 as str */
OP_UINT_TO_STRING, /* utos : dest = src1 as str */
OP_REAL_TO_STRING, /* rtos : dest = src1 as str */
@ -70,6 +71,9 @@ typedef enum {
#define OP(opcode, dest, src1, src2) \
((opcode << 24) | (dest << 16) | (src1 << 8) | src2)
#define OP_SYSCALL_OPCODE(syscall_id, arg_count, src) \
((OP_SYSCALL << 24) | ((syscall_id & 0xFF) << 16) | (arg_count & 0xFF) | src)
typedef union value_u {
int32_t i; /* Integers */
float f; /* Float */
@ -99,49 +103,46 @@ typedef enum {
} SyscallID;
typedef struct device_ops_s {
int (*open)(void *device_data, uint32_t mode);
int (*read)(void *device_data, uint8_t *buffer, uint32_t size);
int (*write)(void *device_data, const uint8_t *buffer, uint32_t size);
int (*close)(void *device_data);
int (*ioctl)(void *device_data, uint32_t cmd, void *args); /* optional control */
int (*open)(void *device_data, uint32_t mode);
int (*read)(void *device_data, uint8_t *buffer, uint32_t size);
int (*write)(void *device_data, const uint8_t *buffer, uint32_t size);
int (*close)(void *device_data);
int (*ioctl)(void *device_data, uint32_t cmd,
void *args); /* optional control */
} DeviceOps;
#define DEVICE_TYPE_MAX_LENGTH 24 /* 23 chars + null terminator */
#define DEVICE_PATH_MAX_LENGTH 64 /* 63 chars + null terminator */
#define DEVICE_TYPE_MAX_LENGTH 24 /* 23 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 */
DeviceOps *ops; /* operations vtable */
uint32_t flags; /* permissions, status, etc. */
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 */
DeviceOps *ops; /* operations vtable */
uint32_t flags; /* permissions, status, etc. */
} Device;
#define MEMORY_SIZE 65536
#define MEMORY_SIZE (640 * 480 + 65536)
#define CODE_SIZE 8192
#define FRAMES_SIZE 128
#define STACK_SIZE 256
#define DEVICES_SIZE 8
typedef struct vm_s {
uint32_t pc; /* program counter */
uint32_t cp; /* code pointer (last allocated opcode) */
uint32_t fp; /* frame pointer (current frame) */
uint32_t sp; /* stack pointer (top of stack) */
uint32_t rp; /* return stack pointer (top of stack) */
uint32_t mp; /* memory pointer (last allocated value) */
uint32_t dc; /* device count */
uint32_t device_size;
Device devices[DEVICES_SIZE];
uint32_t frames_size;
Frame frames[FRAMES_SIZE]; /* function call frames */
uint32_t stack_size;
Value stack[STACK_SIZE]; /* main stack */
uint32_t return_stack_size;
Value return_stack[STACK_SIZE]; /* return stack (for recursion) */
uint32_t code_size;
Value code[CODE_SIZE]; /* code block */
uint32_t memory_size;
Value memory[MEMORY_SIZE]; /* memory block */
uint32_t pc; /* program counter */
uint32_t cp; /* code pointer (last allocated opcode) */
uint32_t fp; /* frame pointer (current frame) */
uint32_t sp; /* stack pointer (top of stack) */
uint32_t rp; /* return stack pointer (top of stack) */
uint32_t mp; /* memory pointer (last allocated value) */
uint32_t dc; /* device count */
uint32_t acc; /* accumulator (temporary results like SYSCALL status) */
Device devices[DEVICES_SIZE]; /* device definitions */
Frame frames[FRAMES_SIZE]; /* function call frames */
Value stack[STACK_SIZE]; /* main stack */
Value return_stack[STACK_SIZE]; /* return stack (for call recursion) */
Value code[CODE_SIZE]; /* code block */
Value memory[MEMORY_SIZE]; /* memory block */
} VM;
#endif

View File

@ -8,6 +8,7 @@ struct TestMapping internal_tests[] = {
{"loop.zrl", test_loop_compile},
{"add.zrl", test_add_function_compile},
{"fib.zrl", test_recursive_function_compile},
{"window.zrl", test_window_click_compile},
/* Add more test mappings here */
{NULL, NULL} /* Sentinel to mark end of array */
};
@ -128,3 +129,41 @@ bool test_recursive_function_compile(VM *vm) {
vm->code[vm->cp++].u = OP(OP_RETURN, 0, 0, 0);
return true;
}
bool test_window_click_compile(VM *vm) {
uint32_t screen_path_addr = str_alloc(vm, "/dev/screen/0", 14);
/*uint32_t mouse_path_addr = str_alloc(vm, "/dev/mouse/0", 12);*/
/* Open screen device: R0=path, R1=mode */
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0); /* R0 = screen path */
vm->code[vm->cp++].u = screen_path_addr;
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0); /* R1 = mode (0) */
vm->code[vm->cp++].u = int_alloc(vm, 0);
vm->code[vm->cp++].u = OP(OP_SYSCALL, SYSCALL_DEVICE_OPEN, 0, 2); /* syscall_id, first_reg=0, arg_count=2 */
/* Check if open succeeded (result in vm->acc) */
vm->code[vm->cp++].u = OP(OP_GET_ACC, 2, 0, 0); /* Move result to R2 */
/* Create simple test pixel data */
uint32_t test_pixel_addr = vm->mp;
vm->memory[vm->mp++].u = 0x00FF0000;
/* Main loop to check for mouse input */
uint32_t loop_start = vm->cp;
/* Write to screen: R0=path, R1=buffer, R2=size */
vm->code[vm->cp++].u = OP(OP_LOAD, 0, 0, 0);
vm->code[vm->cp++].u = screen_path_addr;
vm->code[vm->cp++].u = OP(OP_LOAD, 1, 0, 0);
vm->code[vm->cp++].u = test_pixel_addr;
vm->code[vm->cp++].u = OP(OP_LOAD, 2, 0, 0);
vm->code[vm->cp++].u = int_alloc(vm, 1);
vm->code[vm->cp++].u = OP(OP_SYSCALL, SYSCALL_DEVICE_WRITE, 0, 3); /* syscall_id, first_reg=0, arg_count=3 */
vm->code[vm->cp++].u = OP(OP_LOAD, 3, 0, 0);
vm->code[vm->cp++].u = loop_start;
vm->code[vm->cp++].u = OP(OP_JMP, 3, 0, 0); /* Infinite loop for testing */
vm->code[vm->cp++].u = OP(OP_HALT, 0, 0, 0);
return true;
}

View File

@ -17,5 +17,6 @@ bool test_add_compile (VM *vm);
bool test_loop_compile (VM *vm);
bool test_add_function_compile(VM *vm);
bool test_recursive_function_compile(VM *vm);
bool test_window_click_compile(VM *vm);
#endif

146
src/vm.c
View File

@ -27,20 +27,29 @@
* Embeds a string into the VM
*/
uint32_t str_alloc(VM *vm, const char *str, uint32_t length) {
if (!length) {
length = strlen(str);
}
uint32_t str_addr = vm->mp;
vm->memory[vm->mp++].u = length;
uint32_t i, j = 0;
for (i = 0; i < length; i++) {
vm->memory[vm->mp].c[i % 4] = str[i];
if (++j == 4) {
j = 0;
uint32_t str_addr = vm->mp++;
uint32_t temp = 0;
while (temp < length) {
uint32_t idx = temp % 4;
vm->memory[vm->mp].c[idx] = str[temp];
temp++;
if (idx == 3)
vm->mp++;
}
}
vm->frames[vm->fp].allocated.end += length / 4;
uint32_t i, rem = temp % 4;
if (rem != 0) {
for (i = rem; i < 4; i++) {
vm->memory[vm->mp].c[i] = '\0';
}
vm->mp++;
}
vm->memory[str_addr].u = length;
vm->frames[vm->fp].allocated.end = vm->mp;
return str_addr;
}
@ -99,8 +108,7 @@ bool step_vm(VM *vm) {
return true;
}
case OP_LOAD: {
uint32_t ptr = vm->code[vm->pc++].u;
vm->frames[vm->fp].registers[dest] = vm->memory[ptr];
vm->frames[vm->fp].registers[dest].u = vm->code[vm->pc++].u;
return true;
}
case OP_STORE: {
@ -160,114 +168,140 @@ bool step_vm(VM *vm) {
}
case OP_SYSCALL: {
uint32_t syscall_id = dest;
uint32_t arg_ptr = src1;
uint32_t arg_count = src2;
Frame *frame = &vm->frames[vm->fp];
Value *regs = frame->registers;
uint32_t memory_ptr = regs[arg_ptr].u;
uint32_t first_reg = src1;
switch (syscall_id) {
case SYSCALL_DEVICE_OPEN: {
if (arg_count >= 2) {
uint32_t path_offset = vm->memory[memory_ptr].u;
uint32_t mode = vm->memory[memory_ptr + 1].u;
uint32_t path_ptr =
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
uint32_t mode =
vm->frames[vm->fp].registers[first_reg + 1].u; /* R1: mode */
/* uint32_t length = vm->memory[path_ptr].u; */
uint32_t str_src = path_ptr + 1;
const char *path = (const char *)&vm->memory[str_src];
const char *path = (const char *)&vm->memory[path_offset];
Device *dev = find_device_by_path(vm, path);
if (dev) {
if (dev->ops->open) {
int result = dev->ops->open(dev->data, mode);
vm->stack[++vm->sp].i = result;
vm->acc = result;
} else {
vm->stack[++vm->sp].i = 1; /* success, no open needed */
vm->acc = 1; /* success, no open needed */
}
} else {
vm->stack[++vm->sp].i = 0; /* error */
vm->acc = 0; /* error */
}
} else {
vm->acc = 0; /* error: not enough arguments */
}
return true;
}
case SYSCALL_DEVICE_READ: {
if (arg_count >= 3) {
uint32_t path_offset =
vm->memory[memory_ptr].u; /* offset 0: path pointer */
uint32_t buffer_ptr =
vm->memory[memory_ptr + 1].u; /* offset 1: buffer pointer */
uint32_t size = vm->memory[memory_ptr + 2].u; /* offset 2: size */
uint32_t path_ptr =
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
uint32_t buffer_ptr = vm->frames[vm->fp]
.registers[first_reg + 1]
.u; /* R1: buffer pointer */
uint32_t size =
vm->frames[vm->fp].registers[first_reg + 2].u; /* R2: size */
/* uint32_t length = vm->memory[path_ptr].u; */
uint32_t str_src = path_ptr + 1;
const char *path = (const char *)&vm->memory[path_offset];
const char *path = (const char *)&vm->memory[str_src];
Device *dev = find_device_by_path(vm, path);
if (dev && dev->ops->read) {
int result = dev->ops->read(dev->data,
(uint8_t *)&vm->memory[buffer_ptr], size);
vm->stack[++vm->sp].i = result;
vm->acc = result;
} else {
vm->stack[++vm->sp].i = 0;
vm->acc = 0;
}
} else {
vm->acc = 0; /* error: not enough arguments */
}
return true;
}
case SYSCALL_DEVICE_WRITE: {
if (arg_count >= 3) {
uint32_t path_offset =
vm->memory[memory_ptr].u; /* offset 0: path pointer */
uint32_t buffer_ptr =
vm->memory[memory_ptr + 1].u; /* offset 1: buffer pointer */
uint32_t size = vm->memory[memory_ptr + 2].u; /* offset 2: size */
uint32_t path_ptr =
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
uint32_t buffer_ptr = vm->frames[vm->fp]
.registers[first_reg + 1]
.u; /* R1: buffer pointer */
uint32_t size =
vm->frames[vm->fp].registers[first_reg + 2].u; /* R2: size */
/* uint32_t length = vm->memory[path_ptr].u; */
uint32_t str_src = path_ptr + 1;
const char *path = (const char *)&vm->memory[path_offset];
const char *path = (const char *)&vm->memory[str_src];
Device *dev = find_device_by_path(vm, path);
if (dev && dev->ops->write) {
int result = dev->ops->write(
dev->data, (const uint8_t *)&vm->memory[buffer_ptr], size);
vm->stack[++vm->sp].i = result;
vm->acc = result;
} else {
vm->stack[++vm->sp].i = 0;
vm->acc = 0;
}
} else {
vm->acc = 0; /* error: not enough arguments */
}
return true;
}
case SYSCALL_DEVICE_CLOSE: {
if (arg_count >= 1) {
uint32_t path_offset =
vm->memory[memory_ptr].u; /* offset 0: path pointer */
uint32_t path_ptr =
vm->frames[vm->fp].registers[first_reg].u; /* R0: path pointer */
/* uint32_t length = vm->memory[path_ptr].u; */
uint32_t str_src = path_ptr + 1;
const char *path = (const char *)&vm->memory[path_offset];
const char *path = (const char *)&vm->memory[str_src];
Device *dev = find_device_by_path(vm, path);
if (dev && dev->ops->close) {
int result = dev->ops->close(dev->data);
vm->stack[++vm->sp].i = result;
vm->acc = result;
} else {
vm->stack[++vm->sp].i = 0;
vm->acc = 0;
}
} else {
vm->acc = 0; /* error: not enough arguments */
}
return true;
}
case SYSCALL_DEVICE_IOCTL: {
if (arg_count >= 3) {
uint32_t path_offset = vm->memory[memory_ptr].u; /* device path */
uint32_t cmd = vm->memory[memory_ptr + 1].u; /* ioctl command */
uint32_t args_ptr = vm->memory[memory_ptr + 2].u; /* args pointer */
uint32_t path_ptr =
vm->frames[vm->fp].registers[first_reg].u; /* R0: device path */
uint32_t cmd = vm->frames[vm->fp]
.registers[first_reg + 1]
.u; /* R1: ioctl command */
uint32_t args_ptr = vm->frames[vm->fp]
.registers[first_reg + 2]
.u; /* R2: args pointer */
/* uint32_t length = vm->memory[path_ptr].u; */
uint32_t str_src = path_ptr + 1;
const char *path = (const char *)&vm->memory[path_offset];
const char *path = (const char *)&vm->memory[str_src];
Device *dev = find_device_by_path(vm, path);
if (dev && dev->ops && dev->ops->ioctl) {
int result = dev->ops->ioctl(dev->data, cmd, &vm->memory[args_ptr]);
vm->stack[++vm->sp].i = result;
vm->acc = result;
} else {
vm->stack[++vm->sp].i = 0; /* error or no ioctl support */
vm->acc = 0; /* error or no ioctl support */
}
} else {
vm->acc = 0; /* error: not enough arguments */
}
return true;
}
@ -277,7 +311,7 @@ bool step_vm(VM *vm) {
}
default: {
/* NOOP */
vm->acc = 0; /* unknown syscall */
return true;
}
}
@ -340,6 +374,10 @@ bool step_vm(VM *vm) {
vm->frames[vm->fp].registers[dest].i = vm->frames[vm->fp].registers[src1].i;
return true;
}
case OP_GET_ACC: {
vm->frames[vm->fp].registers[dest].u = vm->acc;
return true;
}
case OP_JMP: {
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
return true;