Code cleanup, add halt return type, add neg and abs ops, fix strings,

This commit is contained in:
zongor 2025-10-26 14:49:00 -07:00
parent feaebcec1d
commit 739c3638a6
33 changed files with 755 additions and 666 deletions

View File

@ -80,7 +80,7 @@ endif
VM_SOURCES := \
$(SRC_DIR)/vm/vm.c \
$(SRC_DIR)/vm/device.c \
$(SRC_DIR)/vm/str.c
$(SRC_DIR)/vm/libc.c
ifeq ($(BUILD_MODE), release)
PLATFORM_SOURCE := $(ARCH_DIR)/main.c \

View File

@ -17,7 +17,7 @@
* Undâr
Undâr 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. 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 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.
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.
@ -66,11 +66,10 @@ The Undâr compiler will be written in Sċieppan, as well as core VM tests.
(load-immediate $11 0)
(syscall OPEN $1 $1 $11)
(load-immediate $3 &new-line)
(load-offset-32 $7 $1 4) ; load handle
(string-length $2 $0)
(syscall WRITE $7 $0 $2)
(syscall WRITE $1 $0 $2)
(string-length $4 $3)
(syscall WRITE $7 $3 $4)
(syscall WRITE $1 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
@ -102,18 +101,16 @@ heap allocations using the internal malloc opcode push pointers within this fram
(load-immediate $1 32) ; read in a string of max 32 char length
(malloc $4 $1) ; allocate memory for the string
(load-offset-32 $7 $0 4) ; load handle
(syscall READ $7 $2 $1 $4) ; read the string
(syscall READ $0 $4 $1) ; read the string
(call &pln ($0 $4) nil) ; print the string
(halt))
(label pln
(load-immediate $3 &new-line)
(load-offset-32 $7 $0 4) ; load handle
(string-length $2 $1)
(syscall WRITE $7 $1 $2)
(syscall WRITE $0 $1 $2)
(string-length $4 $3)
(syscall WRITE $7 $3 $4)
(syscall WRITE $0 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
@ -125,15 +122,14 @@ values passed to functions must be explicitly returned to propagate. heap values
**Core Types**
| Type | Description |
|--------+-------------------------------------------|
| =int= | 32-bit signed integer |
| =nat= | 32-bit natural number |
| =real= | Q16.16 fixed-point real number |
| =str= | fat pointer [length + data] string |
| =bool= | true/false |
| =char= | Character |
| =ref= | Reference prefix for passing by reference |
| Type | Description |
|------+------------------------------------|
| =int= | 32-bit signed integer |
| =nat= | 32-bit natural number |
| =real= | Q16.16 fixed-point real number |
| =str= | fat pointer [length + data] string |
| =bool= | true/false |
| =byte= | Character/8 bit unsigned int |
A ref type allows pass by reference similar to a pointer in c. most types will be pass by value with some types being explicitly pass by reference.

View File

@ -10,57 +10,34 @@
* Roadmap
** Actor System
* Actor-Based Concurrency Model (Frame-Managed Memory)
** Core Principles
- Deterministic memory management with no garbage collection or manual free
- Zero fragmentation via contiguous arena allocation
- Isolated memory spaces for all actors (no shared memory)
- Compile-time memory bounds enforcement
- Mission-critical certified (DO-178C Level A compliant)
** Memory Architecture
- Actor memory is partitioned into fixed-size arenas
- Two primary arenas:
- =Main Thread Arena= (top of memory)
- =Actor Arenas= (bottom-up allocation)
- Arena layout:
#+begin_src text
|---------------------| <-- Top of memory
| MAIN THREAD | (e.g., 8KB)
|---------------------| <-- Base address of actor pool
| ACTOR 1 (4KB) | <-- Preallocated at system startup
|---------------------|
| ACTOR 2 (2KB) | <-- Size specified by developer
|---------------------|
| ACTOR 3 (1KB) |
|---------------------| <-- Bottom of memory
#+end_src
*** Communication Protocol
- Bounded mailbox system
- Message flow:
1. Main thread sends via =send_message()=
2. Message copied into actor's mailbox
3. Actor processes in =on_message()= during next cycle
4. Actor sends response via =send_response()=
*** Failure Behaviors
| Behavior | Action | Certification Impact | Example Use Case |
|----------------|----------------------------|----------------------|--------------------------|
| =RESTART= | Reset arena offset to 0 | 2 artifacts | Network reconnection |
| =EXIT= | Free arena | 1 artifact | Session termination |
| =CRASH= | Halt entire VM | 1 artifact | Critical subsystem fail |
** Example: Hello world (=hello.ul=)
*WIP syntax, not final implementation**
#+BEGIN_SRC ul
fn main(int argc, str[] argv) {
print("nuqneH 'u'?");
/**
* Constants
*/
const str nl = "\n";
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln("nuqneH 'u'?");
}
/**
* Print with a newline
*/
function pln(str message) {
Terminal term = open("/dev/term/0", 0);
write(term, message, message.length);
write(term, nl, nl.length);
}
#+END_SRC
@ -68,42 +45,161 @@ fn main(int argc, str[] argv) {
**WIP syntax, not final implementation**
#+BEGIN_SRC ul
use "common.ul";
Common:
fn256 main(int argc, str[] argv) {
nat w = 800, h = 450;
Player me(argv[1], [0.0, 1.0, 2.0], PURPLE);
#+BEGIN_SRC ul
/**
* Camera.
*/
plex Camera {
int setting;
real pov;
real[3] up;
real[3] pos;
real[3] look;
init(real[3] pos, real[3] look) {
this.setting = CAMERA_PERSPECTIVE;
this.pov = 45.0;
this.up = [0.0, 1.0, 0.0];
this.pos = pos;
this.look = look;
}
}
/**
* Player.
*/
plex Player {
Client client;
str username;
real[3] pos;
nat color;
Camera camera;
init(str username, real[3] pos, Color color) {
this.client = Client("tcp://localhost:25565");
this.username = username;
this.pos = pos;
this.color = color;
this.camera =
Camera([this.pos.x + 10.0, this.pos.y + 10.0, this.pos.z], this.pos);
}
login(str password) Player[] { // looks like a method but really it just has an implied "Player this" as the first argument
this.client.attach(this.username, password);
this.players = client.open("players");
return players.read();
}
logout() {
this.players.flush();
this.client.clunk();
}
update() {
if (key_down("right")) {
this.pos.x += 0.2;
this.client.write(Command(this.username, KEY_RIGHT))
}
if (key_down("left")) {
this.pos.x -= 0.2;
this.client.write(Command(this.username, KEY_LEFT))
}
if (key_down("down")) {
this.pos.z += 0.2;
this.client.write(Command(this.username, KEY_DOWN))
}
if (key_down("up")) {
this.pos.z -= 0.2;
this.client.write(Command(this.username, KEY_UP))
}
this.camera.sync();
}
}
const nat RED = rgb332([255, 0, 0]);
const nat WHITE = rgb332([0, 0, 0]);
const nat PURPLE = rgb332([255, 255, 0]);
#+END_SRC
Client:
#+BEGIN_SRC ul
use <str.ul>; // from stdlib
use "common.ul"; // from local
function main(int argc, str[] argv) {
nat screen_width = 800;
nat screen_height = 450;
if (argv < 2) {
pln("usage: undar client.ul <username> <password>");
exit(-1);
}
str username = argv[1];
str password = argv[2];
Player me(username, [0.0, 1.0, 2.0], PURPLE);
bool running = true;
while (running) {
window("Client", w, h) {
splitbox(parent.size, 0.25) {
window("zwl client", screen_width, screen_height) {
splitbox(parent.size 0.25) {
canvas() {
if (button("Logout")) {
if (button("logout")) {
me.logout();
running = false;
}
}
}
splitbox(parent.size, 0.75) {
splitbox(parent.size 0.75) {
canvas() {
model(Floor([0, 0, 0], 30));
me.update();
model(Cube(me.pos, [0.5,0.5,0.5], me.color));
if (Player[] others = me.server.read("players")) {
for (p in others) {
model(Cube(p.pos, [0.5,0.5,0.5], p.color));
model(Cube(me.pos, [0.5, 0.5, 0.5], me.appearance));
if (Player[] players = me.server.read("players")) {
for (p in players) {
model(Cube(p.pos, [0.5, 0.5, 0.5], p.apperance));
}
}
}
}
}
}
exits("Client Closed Successfully");
}
#+END_SRC
Server:
#+BEGIN_SRC ul
use "common.ul";
function main(int argc, str[] argv) {
Server s("tcp://0.0.0.0:25565");
bool running = true;
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.t == "close") {
client.close();
running = false;
} else if (message.t == "players") {
client.write(players);
} else {
print("unknown message {message}");
}
}
}
}
exits(nil);
}
#+END_SRC
** Plex
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".
@ -194,3 +290,45 @@ if (server.attach(auth)) {
| =.clunk()= | Close communication |
| =.stat()= | returns the status of the file resource |
| =.version()= | returns the version code for the connected tunnel |
** Actor System
*** Core Principles
- Deterministic memory management with no garbage collection or manual free
- Zero fragmentation via contiguous arena allocation
- Isolated memory spaces for all actors (no shared memory)
- Compile-time memory bounds enforcement
- Mission-critical certified (DO-178C Level A compliant)
*** Memory Architecture
- Actor memory is partitioned into fixed-size arenas
- Two primary arenas:
- =Main Thread Arena= (top of memory)
- =Actor Arenas= (bottom-up allocation)
- Arena layout:
#+begin_src text
|---------------------| <-- Top of memory
| MAIN THREAD | (e.g., 8KB)
|---------------------| <-- Base address of actor pool
| ACTOR 1 (4KB) | <-- Preallocated at system startup
|---------------------|
| ACTOR 2 (2KB) | <-- Size specified by developer
|---------------------|
| ACTOR 3 (1KB) |
|---------------------| <-- Bottom of memory
#+end_src
*** Communication Protocol
- Bounded mailbox system
- Message flow:
1. Main thread sends via =send_message()=
2. Message copied into actor's mailbox
3. Actor processes in =on_message()= during next cycle
4. Actor sends response via =send_response()=
*** Failure Behaviors
| Behavior | Action | Certification Impact | Example Use Case |
|----------+-------------------------+----------------------+-------------------------|
| =RESTART= | Reset arena offset to 0 | 2 artifacts | Network reconnection |
| =EXIT= | Free arena | 1 artifact | Session termination |
| =CRASH= | Halt entire VM | 1 artifact | Critical subsystem fail |

View File

@ -1,44 +0,0 @@
use <str.ul>; // from stdlib
use "common.ul"; // from local
function main(int argc, str[] argv) {
nat screen_width = 800;
nat screen_height = 450;
if (argv < 2) {
pln("usage: undar client.ul <username> <password>");
exit(-1);
}
str username = argv[1];
str password = argv[2];
Player me(username, [0.0, 1.0, 2.0], PURPLE);
bool running = true;
while (running) {
window("zwl client", screen_width, screen_height) {
splitbox(parent.size 0.25) {
canvas() {
if (button("logout")) {
me.logout();
running = false;
}
}
}
splitbox(parent.size 0.75) {
canvas() {
model(Floor([0, 0, 0], 30));
me.update();
model(Cube(me.pos, [0.5, 0.5, 0.5], me.appearance));
if (Player[] players = me.server.read("players")) {
for (p in players) {
model(Cube(p.pos, [0.5, 0.5, 0.5], p.apperance));
}
}
}
}
}
}
exits("Client Closed Successfully");
}

View File

@ -1,81 +0,0 @@
/**
* Note that these look like classes but act like structs
* the methods actually have a implied struct as their first argument
*/
/**
* Camera.
*/
plex Camera {
int setting;
real pov;
real[3] up;
real[3] pos;
real[3] look;
init(real[3] pos, real[3] look) {
this.setting = CAMERA_PERSPECTIVE;
this.pov = 45.0;
this.up = [0.0, 1.0, 0.0];
this.pos = pos;
this.look = look;
}
}
/**
* Player.
*/
plex Player {
Client client;
str username;
real[3] pos;
nat color;
Camera camera;
init(str username, real[3] pos, Color color) {
this.client = Client("tcp://localhost:25565");
this.username = username;
this.pos = pos;
this.color = color;
this.camera =
Camera([this.pos.x + 10.0, this.pos.y + 10.0, this.pos.z], this.pos);
}
login(str password) Player[] { // looks like a method but really it just has an implied "Player this" as the first argument
this.client.attach(this.username, password);
this.players = client.open("players");
return players.read();
}
logout() {
this.players.flush();
this.client.clunk();
}
update() {
if (key_down("right")) {
this.pos.x += 0.2;
this.client.write(Command(this.username, KEY_RIGHT))
}
if (key_down("left")) {
this.pos.x -= 0.2;
this.client.write(Command(this.username, KEY_LEFT))
}
if (key_down("down")) {
this.pos.z += 0.2;
this.client.write(Command(this.username, KEY_DOWN))
}
if (key_down("up")) {
this.pos.z -= 0.2;
this.client.write(Command(this.username, KEY_UP))
}
this.camera.sync();
}
}
const nat RED = rgb332([255, 0, 0]);
const nat WHITE = rgb332([0, 0, 0]);
const nat PURPLE = rgb332([255, 255, 0]);

View File

@ -1,22 +0,0 @@
use "common.ul";
function main(int argc, str[] argv) {
Server s("tcp://0.0.0.0:25565");
bool running = true;
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.t == "close") {
client.close();
running = false;
} else if (message.t == "players") {
client.write(players);
} else {
print("unknown message {message}");
}
}
}
}
exits(nil);
}

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 */

View File

@ -1,6 +1,6 @@
#include "../../tools/assembler.h"
#include "../../tools/parser.h"
#include "../../tools/lexer.h"
#include "../../tools/parser.h"
#include "../../vm/vm.h"
#include "devices.h"
#include <SDL2/SDL.h>
@ -32,14 +32,12 @@ static DeviceOps keyboard_ops = {.open = keyboard_open,
.ioctl = nil,
.refresh = nil};
static DeviceOps console_device_ops = {
.open = console_open,
.read = console_read,
.write = console_write,
.close = console_close,
.ioctl = console_ioctl,
.refresh = nil
};
static DeviceOps console_device_ops = {.open = console_open,
.read = console_read,
.write = console_write,
.close = console_close,
.ioctl = console_ioctl,
.refresh = nil};
static ScreenDeviceData screen_data = {0};
static MouseDeviceData mouse_data = {0};
@ -156,11 +154,8 @@ bool compileAndSave(const char *source_file, const char *output_file, VM *vm) {
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);
printf("Line %d [%s]: %.*s\n", token.line, tokenTypeToString(token.type),
token.length, token.start);
}
} while (token.type != TOKEN_EOF);
@ -278,137 +273,137 @@ void repl(VM *vm) {
}
// If unbalanced, continue reading more lines
}
exit(0);
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",
[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",
/* 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",
/* 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",
/* 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",
/* 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",
/* 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",
/* 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",
/* 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",
/* Memory operations */
[OP_MALLOC] = "malloc",
[OP_MEMSET_8] = "memset-8",
[OP_MEMSET_16] = "memset-16",
[OP_MEMSET_32] = "memset-32",
/* Stack operations */
[OP_PUSH] = "push",
[OP_POP] = "pop",
/* Register operations */
[OP_REG_MOV] = "register-move",
[OP_SYSCALL] = "syscall",
/* 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",
/* Bit operations */
[OP_SLL] = "bit-shift-left",
[OP_SRL] = "bit-shift-right",
[OP_SRE] = "bit-shift-re",
[OP_BAND] = "bit-and",
[OP_BOR] = "bit-or",
[OP_BXOR] = "bit-xor",
/* Integer arithmetic */
[OP_ADD_INT] = "add-int",
[OP_SUB_INT] = "sub-int",
[OP_MUL_INT] = "mul-int",
[OP_DIV_INT] = "div-int",
[OP_ABS_INT] = "abs-int", // ← NEW
[OP_NEG_INT] = "neg-int", // ← NEW
/* Integer arithmetic */
[OP_ADD_INT] = "add-int",
[OP_SUB_INT] = "sub-int",
[OP_MUL_INT] = "mul-int",
[OP_DIV_INT] = "div-int",
/* Natural number arithmetic */
[OP_ADD_NAT] = "add-nat",
[OP_SUB_NAT] = "sub-nat",
[OP_MUL_NAT] = "mul-nat",
[OP_DIV_NAT] = "div-nat",
[OP_ABS_NAT] = "abs-nat", // ← NEW
[OP_NEG_NAT] = "neg-nat", // ← NEW
/* Natural number arithmetic */
[OP_ADD_NAT] = "add-nat",
[OP_SUB_NAT] = "sub-nat",
[OP_MUL_NAT] = "mul-nat",
[OP_DIV_NAT] = "div-nat",
/* Floating point operations */
[OP_ADD_REAL] = "add-real",
[OP_SUB_REAL] = "sub-real",
[OP_MUL_REAL] = "mul-real",
[OP_DIV_REAL] = "div-real",
[OP_ABS_REAL] = "abs-real", // ← NEW
[OP_NEG_REAL] = "neg-real", // ← NEW
/* Floating point operations */
[OP_ADD_REAL] = "add-real",
[OP_SUB_REAL] = "sub-real",
[OP_MUL_REAL] = "mul-real",
[OP_DIV_REAL] = "div-real",
/* Type conversions */
[OP_INT_TO_REAL] = "int-to-real",
[OP_NAT_TO_REAL] = "nat-to-real",
[OP_REAL_TO_INT] = "real-to-int",
[OP_REAL_TO_NAT] = "real-to-nat",
/* 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",
/* 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",
/* 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",
/* 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 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"
};
/* 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>";
@ -420,7 +415,6 @@ const char *opcode_to_string(Opcode op) {
#endif
i32 main(i32 argc, char *argv[]) {
bool gui_mode = false;
bool dump_rom = false;
char *input_file = nil;
char *output_file = nil;
@ -429,10 +423,7 @@ i32 main(i32 argc, char *argv[]) {
// Parse command line arguments
for (i32 i = 1; i < argc; i++) {
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gui") == 0) {
gui_mode = true;
} else if (strcmp(argv[i], "-o") == 0 ||
strcmp(argv[i], "--dump-rom") == 0) {
if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--dump-rom") == 0) {
dump_rom = true;
} else if (input_file == nil) {
// This is the input file
@ -496,145 +487,135 @@ i32 main(i32 argc, char *argv[]) {
vm_register_device(&vm, "/dev/term/0", "terminal", &console_data,
&console_device_ops, 4);
if (gui_mode) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("SDL initialization failed: %s\n", SDL_GetError());
return 1;
}
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("SDL initialization failed: %s\n", SDL_GetError());
return 1;
}
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
screen_data.width = 640;
screen_data.height = 480;
screen_data.buffer_size = screen_data.width * screen_data.height;
screen_data.width = 640;
screen_data.height = 480;
screen_data.buffer_size = screen_data.width * screen_data.height;
vm_register_device(&vm, "/dev/screen/0", "screen", &screen_data,
&screen_ops, 16 + screen_data.buffer_size);
vm_register_device(&vm, "/dev/screen/0", "screen", &screen_data, &screen_ops,
16 + screen_data.buffer_size);
mouse_data.x = 0;
mouse_data.y = 0;
mouse_data.btn1 = 0;
mouse_data.btn2 = 0;
mouse_data.btn3 = 0;
mouse_data.btn4 = 0;
mouse_data.size = 16;
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,
mouse_data.size);
vm_register_device(&vm, "/dev/mouse/0", "mouse", &mouse_data, &mouse_ops, 16);
keyboard_data.keys = SDL_GetKeyboardState(&keyboard_data.key_count);
vm_register_device(&vm, "/dev/keyboard/0", "keyboard", &keyboard_data,
&keyboard_ops, keyboard_data.key_count + 4);
keyboard_data.keys = SDL_GetKeyboardState(&keyboard_data.key_count);
vm_register_device(&vm, "/dev/keyboard/0", "keyboard", &keyboard_data,
&keyboard_ops, keyboard_data.key_count + 4);
SDL_Event event;
bool running = true;
SDL_PumpEvents();
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
// Mouse events
case SDL_MOUSEMOTION:
mouse_data.x = event.motion.x;
mouse_data.y = event.motion.y;
break;
SDL_Event event;
bool running = true;
SDL_PumpEvents();
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
// Mouse events
case SDL_MOUSEMOTION:
mouse_data.x = event.motion.x;
mouse_data.y = event.motion.y;
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT)
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT)
mouse_data.btn1 = 1;
if (event.button.button == SDL_BUTTON_RIGHT)
mouse_data.btn2 = 1;
if (event.button.button == SDL_BUTTON_MIDDLE)
mouse_data.btn3 = 1;
if (event.button.button == SDL_BUTTON_X1)
mouse_data.btn4 = 1;
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT)
mouse_data.btn1 = 0;
if (event.button.button == SDL_BUTTON_RIGHT)
mouse_data.btn2 = 0;
if (event.button.button == SDL_BUTTON_MIDDLE)
mouse_data.btn3 = 0;
if (event.button.button == SDL_BUTTON_X1)
mouse_data.btn4 = 0;
break;
// Touch events (map to mouse_data as left-click equivalent)
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP: {
float x = event.tfinger.x * 640;
float y = event.tfinger.y * 480;
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;
if (event.button.button == SDL_BUTTON_RIGHT)
mouse_data.btn2 = 1;
if (event.button.button == SDL_BUTTON_MIDDLE)
mouse_data.btn3 = 1;
if (event.button.button == SDL_BUTTON_X1)
mouse_data.btn4 = 1;
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT)
} else if (event.type == SDL_FINGERUP) {
mouse_data.btn1 = 0;
if (event.button.button == SDL_BUTTON_RIGHT)
mouse_data.btn2 = 0;
if (event.button.button == SDL_BUTTON_MIDDLE)
mouse_data.btn3 = 0;
if (event.button.button == SDL_BUTTON_X1)
mouse_data.btn4 = 0;
break;
// Touch events (map to mouse_data as left-click equivalent)
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP: {
float x = event.tfinger.x * 640;
float y = event.tfinger.y * 480;
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;
} else if (event.type == SDL_FINGERUP) {
mouse_data.btn1 = 0;
}
}
break;
}
}
break;
}
// 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
while (cycles_this_frame < max_cycles_per_frame) {
#ifdef ASM_DEBUG
printf("| %s %d\n", opcode_to_string(vm.code[vm.pc]),vm.pc);
#endif
if (!step_vm(&vm)) {
running = false;
break;
}
cycles_this_frame++;
}
// Render only if the screen buffer was updated AND at a reasonable rate
if (screen_data.update) {
if (screen_data.renderer && screen_data.texture) {
// Clear and render
SDL_RenderClear(screen_data.renderer);
SDL_Rect output_rect;
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);
SDL_Rect dstrect = {
(i32)((output_rect.w - screen_data.width * scale) / 2),
(i32)((output_rect.h - screen_data.height * scale) / 2),
(i32)(screen_data.width * scale),
(i32)(screen_data.height * scale)};
SDL_RenderCopy(screen_data.renderer, screen_data.texture, NULL,
&dstrect);
SDL_RenderPresent(screen_data.renderer);
}
screen_data.update = false; // Reset flag after rendering
}
}
} else {
bool running = true;
while (running) {
running = step_vm(&vm);
// 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
while (cycles_this_frame < max_cycles_per_frame) {
#ifdef ASM_DEBUG
printf("| %s %d\n", opcode_to_string(vm.code[vm.pc]), vm.pc);
#endif
if (!step_vm(&vm)) {
running = false;
break;
}
cycles_this_frame++;
}
// Render only if the screen buffer was updated AND at a reasonable rate
if (screen_data.update) {
if (screen_data.renderer && screen_data.texture) {
// Clear and render
SDL_RenderClear(screen_data.renderer);
SDL_Rect output_rect;
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);
SDL_Rect dstrect = {
(i32)((output_rect.w - screen_data.width * scale) / 2),
(i32)((output_rect.h - screen_data.height * scale) / 2),
(i32)(screen_data.width * scale),
(i32)(screen_data.height * scale)};
SDL_RenderCopy(screen_data.renderer, screen_data.texture, NULL,
&dstrect);
SDL_RenderPresent(screen_data.renderer);
}
screen_data.update = false; // Reset flag after rendering
}
}
return EXIT_SUCCESS;
return vm.flag;
}

View File

@ -66,17 +66,18 @@ u32 find_label_in_table(SymbolTable *table, const char *name) {
int get_instruction_byte_size(ExprNode *node) {
const char *opname = node->token;
// Simple opcodes (1 byte)
if (strcmp(opname, "halt") == 0) {
return 1;
}
// Return (1 + 1)
if (strcmp(opname, "return") == 0) {
return 2; // 1 byte opcode + 1 byte return register
}
if (strcmp(opname, "int-to-string") == 0 ||
if (strcmp(opname, "neg-int") == 0 ||
strcmp(opname, "abs-int") == 0 ||
strcmp(opname, "neg-nat") == 0 ||
strcmp(opname, "abs-nat") == 0 ||
strcmp(opname, "neg-real") == 0 ||
strcmp(opname, "abs-real") == 0 ||
strcmp(opname, "int-to-string") == 0 ||
strcmp(opname, "load-indirect-8") == 0 ||
strcmp(opname, "nat-to-string") == 0 ||
strcmp(opname, "load-indirect-16") == 0 ||
@ -108,6 +109,7 @@ int get_instruction_byte_size(ExprNode *node) {
strcmp(opname, "add-real") == 0 || strcmp(opname, "sub-real") == 0 ||
strcmp(opname, "bit-shift-left") == 0 ||
strcmp(opname, "bit-shift-right") == 0 ||
strcmp(opname, "bit-shift-r-ext") == 0 ||
strcmp(opname, "bit-and") == 0 || strcmp(opname, "bit-or") == 0 ||
strcmp(opname, "bit-xor") == 0 || strcmp(opname, "mul-real") == 0 ||
strcmp(opname, "div-real") == 0) {
@ -115,7 +117,8 @@ int get_instruction_byte_size(ExprNode *node) {
}
// (5 bytes: 1 + 4)
if (strcmp(opname, "jump-if-flag") == 0 || strcmp(opname, "jump") == 0) {
if (strcmp(opname, "halt") == 0 || strcmp(opname, "jump-if-flag") == 0 ||
strcmp(opname, "jump") == 0) {
return 5;
}
@ -387,13 +390,14 @@ void process_data_block(VM *vm, SymbolTable *table, ExprNode *block) {
// Case 1: String literal (enclosed in quotes)
if (token[0] == '"' && token[strlen(token) - 1] == '"') {
char *unwrapped = unwrap_string(token);
int len = strlen(unwrapped) + 1;
u32 addr = allocate_data(vm, table, name, len + 4);
int len = strlen(unwrapped);
u32 addr = allocate_data(vm, table, name, len + 1 + 4);
write_u32(vm, memory, addr, len);
for (int i = 0; i < len; i++) {
write_u8(vm, memory, addr + 4 + i, unwrapped[i]);
}
write_u8(vm, memory, addr + 4 + len, '\0');
free(unwrapped);
}
// Case 2: Hexadecimal integer (0x...)
@ -427,7 +431,7 @@ void process_data_block(VM *vm, SymbolTable *table, ExprNode *block) {
u32 addr = allocate_data(vm, table, name, 4);
write_u32(vm, memory, addr, value);
vm->mp += 4;
//vm->mp += 4;
}
} else {
fprintf(stderr, "Unsupported data item\n");
@ -445,6 +449,8 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) {
}
} else if (strcmp(opname, "halt") == 0) {
emit_opcode(vm, OP_HALT);
u32 addr = resolve_symbol(table, node->children[0]->token);
emit_u32(vm, addr);
} else if (strcmp(opname, "jump") == 0) {
emit_opcode(vm, OP_JMP);
u32 addr = resolve_symbol(table, node->children[0]->token);
@ -732,7 +738,7 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) {
emit_byte(vm, reg);
}
} else if (strcmp(opname, "bit-shift-left") == 0) {
emit_opcode(vm, OP_SLL);
emit_opcode(vm, OP_BIT_SHIFT_LEFT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
int src2 = parse_register(node->children[2]->token);
@ -740,15 +746,15 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) {
emit_byte(vm, src1);
emit_byte(vm, src2);
} else if (strcmp(opname, "bit-shift-right") == 0) {
emit_opcode(vm, OP_SRL);
emit_opcode(vm, OP_BIT_SHIFT_RIGHT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
int src2 = parse_register(node->children[2]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
emit_byte(vm, src2);
} else if (strcmp(opname, "bit-shift-re") == 0) {
emit_opcode(vm, OP_SRE);
} else if (strcmp(opname, "bit-shift-r-ext") == 0) {
emit_opcode(vm, OP_BIT_SHIFT_R_EXT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
int src2 = parse_register(node->children[2]->token);
@ -811,6 +817,18 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) {
emit_byte(vm, dest);
emit_byte(vm, src1);
emit_byte(vm, src2);
} else if (strcmp(opname, "abs-int") == 0) {
emit_opcode(vm, OP_ABS_INT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
} else if (strcmp(opname, "neg-int") == 0) {
emit_opcode(vm, OP_NEG_INT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
} else if (strcmp(opname, "add-nat") == 0) {
emit_opcode(vm, OP_ADD_NAT);
int dest = parse_register(node->children[0]->token);
@ -843,6 +861,18 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) {
emit_byte(vm, dest);
emit_byte(vm, src1);
emit_byte(vm, src2);
} else if (strcmp(opname, "abs-nat") == 0) {
emit_opcode(vm, OP_ABS_INT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
} else if (strcmp(opname, "neg-nat") == 0) {
emit_opcode(vm, OP_NEG_INT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
} else if (strcmp(opname, "add-real") == 0) {
emit_opcode(vm, OP_ADD_REAL);
int dest = parse_register(node->children[0]->token);
@ -875,6 +905,18 @@ void process_code_expr(VM *vm, SymbolTable *table, ExprNode *node) {
emit_byte(vm, dest);
emit_byte(vm, src1);
emit_byte(vm, src2);
} else if (strcmp(opname, "abs-real") == 0) {
emit_opcode(vm, OP_ABS_INT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
} else if (strcmp(opname, "neg-real") == 0) {
emit_opcode(vm, OP_NEG_INT);
int dest = parse_register(node->children[0]->token);
int src1 = parse_register(node->children[1]->token);
emit_byte(vm, dest);
emit_byte(vm, src1);
} else if (strcmp(opname, "int-to-real") == 0) {
emit_opcode(vm, OP_INT_TO_REAL);
int dest = parse_register(node->children[0]->token);

View File

@ -89,6 +89,7 @@ static void skipWhitespace() {
advance();
advance();
while (!isAtEnd()) {
if (peek() == '\n') lexer.line++;
if (peek() == '*' && peekNext() == '/') {
advance();
advance();
@ -120,6 +121,16 @@ static TokenType identifierType() {
switch (lexer.start[0]) {
case 'a':
return checkKeyword(1, 2, "nd", TOKEN_OPERATOR_AND);
case 'c':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'l':
return checkKeyword(2, 3, "ose", TOKEN_KEYWORD_CLOSE);
case 'o':
return checkKeyword(2, 3, "nst", TOKEN_KEYWORD_CONST);
}
}
break;
case 'e':
return checkKeyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
case 'f':
@ -134,23 +145,77 @@ static TokenType identifierType() {
}
break;
case 'i':
return checkKeyword(1, 1, "f", TOKEN_KEYWORD_IF);
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'f':
return checkKeyword(2, 0, "", TOKEN_KEYWORD_IF);
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);
}
}
break;
}
}
break;
case 'n':
return checkKeyword(1, 2, "il", TOKEN_KEYWORD_NIL);
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'a':
return checkKeyword(2, 1, "t", TOKEN_TYPE_NAT);
case 'i':
return checkKeyword(2, 1, "l", TOKEN_KEYWORD_NIL);
}
}
break;
case 'o':
return checkKeyword(1, 1, "r", TOKEN_OPERATOR_OR);
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'p':
return checkKeyword(2, 2, "en", TOKEN_KEYWORD_OPEN);
case 'r':
return checkKeyword(2, 0, "", TOKEN_OPERATOR_OR);
}
}
break;
case 'p':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'l':
return checkKeyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
case 'r':
return checkKeyword(2, 3, "int", TOKEN_KEYWORD_PRINT);
}
}
break;
case 'r':
return checkKeyword(1, 5, "eturn", TOKEN_KEYWORD_RETURN);
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'e':
if (lexer.current - lexer.start > 2) {
switch (lexer.start[2]) {
case 'a':
return checkKeyword(3, 1, "d", TOKEN_KEYWORD_READ);
case 'f':
return checkKeyword(3, 4, "resh", TOKEN_KEYWORD_REFRESH);
case 't':
return checkKeyword(3, 3, "urn", TOKEN_KEYWORD_RETURN);
}
}
break;
}
}
break;
case 's':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 't':
return checkKeyword(2, 1, "r", TOKEN_TYPE_STR);
}
}
break;
case 't':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
@ -161,10 +226,24 @@ static TokenType identifierType() {
}
}
break;
case 'l':
return checkKeyword(1, 2, "et", TOKEN_KEYWORD_LET);
case 'u':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 's':
return checkKeyword(2, 1, "e", TOKEN_KEYWORD_USE);
}
}
break;
case 'w':
return checkKeyword(1, 4, "hile", TOKEN_KEYWORD_WHILE);
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'h':
return checkKeyword(2, 3, "ile", TOKEN_KEYWORD_WHILE);
case 'r':
return checkKeyword(2, 3, "ite", TOKEN_KEYWORD_WRITE);
}
}
break;
}
return TOKEN_IDENTIFIER;
@ -278,7 +357,6 @@ const char* tokenTypeToString(TokenType type) {
case TOKEN_TYPE_STR: return "TYPE_STR";
case TOKEN_KEYWORD_PLEX: return "KEYWORD_PLEX";
case TOKEN_KEYWORD_FN: return "KEYWORD_FN";
case TOKEN_KEYWORD_LET: return "KEYWORD_LET";
case TOKEN_KEYWORD_CONST: return "KEYWORD_CONST";
case TOKEN_KEYWORD_IF: return "KEYWORD_IF";
case TOKEN_KEYWORD_ELSE: return "KEYWORD_ELSE";
@ -288,7 +366,11 @@ const char* tokenTypeToString(TokenType type) {
case TOKEN_KEYWORD_USE: return "KEYWORD_USE";
case TOKEN_KEYWORD_INIT: return "KEYWORD_INIT";
case TOKEN_KEYWORD_THIS: return "KEYWORD_THIS";
case TOKEN_KEYWORD_PRINT: return "KEYWORD_PRINT";
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";

View File

@ -14,7 +14,6 @@ typedef enum {
TOKEN_TYPE_STR,
TOKEN_KEYWORD_PLEX,
TOKEN_KEYWORD_FN,
TOKEN_KEYWORD_LET,
TOKEN_KEYWORD_CONST,
TOKEN_KEYWORD_IF,
TOKEN_KEYWORD_ELSE,
@ -24,7 +23,11 @@ typedef enum {
TOKEN_KEYWORD_USE,
TOKEN_KEYWORD_INIT,
TOKEN_KEYWORD_THIS,
TOKEN_KEYWORD_PRINT,
TOKEN_KEYWORD_OPEN,
TOKEN_KEYWORD_READ,
TOKEN_KEYWORD_WRITE,
TOKEN_KEYWORD_REFRESH,
TOKEN_KEYWORD_CLOSE,
TOKEN_KEYWORD_NIL,
TOKEN_KEYWORD_TRUE,
TOKEN_KEYWORD_FALSE,

View File

@ -6,19 +6,9 @@
#include <string.h>
#include <uchar.h>
// Helper function to allocate memory and handle errors
void *safe_malloc(size_t size) {
void *ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
return ptr;
}
// Helper function to create a new node
static ExprNode *expr_node_create(const char *token, int line) {
ExprNode *node = (ExprNode *)safe_malloc(sizeof(ExprNode));
ExprNode *node = (ExprNode *)malloc(sizeof(ExprNode));
node->token = strdup(token ? token : "");
node->children = NULL;
node->child_count = 0;
@ -96,7 +86,7 @@ static char *parse_token(const char **ptr, int line) {
}
size_t len = end - start;
char *token = (char *)safe_malloc(len + 1);
char *token = (char *)malloc(len + 1);
memcpy(token, start, len);
token[len] = '\0';
@ -113,7 +103,7 @@ static ExprNode *parse_list(const char **ptr, int line) {
if (**ptr == ')') {
// Empty list
(*ptr)++;
return expr_node_create("nil", line);
return expr_node_create("\0", line);
}
// Parse all children first
@ -125,7 +115,7 @@ static ExprNode *parse_list(const char **ptr, int line) {
if (child) {
// Resize temp children array
ExprNode **new_temp =
(ExprNode **)safe_malloc(sizeof(ExprNode *) * (temp_count + 1));
(ExprNode **)malloc(sizeof(ExprNode *) * (temp_count + 1));
// Copy existing children
for (size_t i = 0; i < temp_count; i++) {
@ -159,7 +149,7 @@ static ExprNode *parse_list(const char **ptr, int line) {
node->child_count = temp_count - 1;
if (node->child_count > 0) {
node->children =
(ExprNode **)safe_malloc(sizeof(ExprNode *) * node->child_count);
(ExprNode **)malloc(sizeof(ExprNode *) * node->child_count);
for (size_t i = 0; i < node->child_count; i++) {
node->children[i] = temp_children[i + 1];
}

View File

@ -1,5 +1,5 @@
#ifndef ZRE_COMMON_H
#define ZRE_COMMON_H
#ifndef UNDAR_COMMON_H
#define UNDAR_COMMON_H
#include <stddef.h>
#include <stdint.h>

View File

@ -1,5 +1,5 @@
#include "device.h"
#include "str.h"
#include "libc.h"
i32 vm_register_device(VM *vm, const char *path, const char *type, void *data,
DeviceOps *ops, u32 size) {
@ -33,26 +33,3 @@ Device *find_device_by_path(VM *vm, const char *path) {
}
return NULL;
}
/* Find device by type (useful for checking capabilities) */
Device *find_device_by_type(VM *vm, const char *type) {
u32 i;
for (i = 0; i < vm->dc; i++) {
if (streq(vm->devices[i].type, type)) {
return &vm->devices[i];
}
}
return NULL;
}
/* Find all devices of a type */
i32 find_devices_by_type(VM *vm, const char *type, Device **results,
u32 max_results) {
u32 i, count = 0;
for (i = 0; i < vm->dc && count < max_results; i++) {
if (streq(vm->devices[i].type, type)) {
results[count++] = &vm->devices[i];
}
}
return count;
}

View File

@ -1,11 +1,9 @@
#ifndef ZRE_DEVICE_H
#define ZRE_DEVICE_H
#ifndef UNDAR_DEVICE_H
#define UNDAR_DEVICE_H
#include "opcodes.h"
i32 vm_register_device(VM *vm, const char *path, const char *type, void *data, DeviceOps *ops, u32 size);
Device* find_device_by_path(VM *vm, const char *path);
Device* find_device_by_type(VM *vm, const char *type);
i32 find_devices_by_type(VM *vm, const char *type, Device **results, uint32_t max_results);
#endif

View File

@ -1,4 +1,4 @@
#include "str.h"
#include "libc.h"
void memcopy(u8 *dest, const u8 *src, u32 n) {
size_t i;

View File

@ -1,5 +1,5 @@
#ifndef ZRE_STR_H
#define ZRE_STR_H
#ifndef UNDAR_STR_H
#define UNDAR_STR_H
#include "common.h"

View File

@ -1,5 +1,5 @@
#ifndef ZRE_OPCODES_H
#define ZRE_OPCODES_H
#ifndef UNDAR_OPCODES_H
#define UNDAR_OPCODES_H
#include "common.h"
@ -31,49 +31,55 @@ typedef enum {
OP_MEMSET_8, /* memset-8 : dest <-> dest+count = src1 as u8 */
OP_MEMSET_16, /* memset-16 : dest <-> dest+count = src1 as u8 */
OP_MEMSET_32, /* memset-32 : dest <-> dest+count = src1 as u32 */
OP_REG_MOV, /* register-move : dest = src1 */
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_SLL, /* bit-shift-left : registers[dest] = registers[src1] << registers[src2] */
OP_SRL, /* bit-shift-right : registers[dest] = registers[src1] >> registers[src2] */
OP_SRE, /* bit-shift-re : registers[dest] as i32 = registers[src1] >> registers[src2] */
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_JMP, /* jump : jump to address dest unconditionally */
OP_JMPF, /* jump-if-flag : jump to address dest if flag is ne 0 */
OP_JEQ_INT, /* jump-eq-int : jump to address dest if registers[src1] as int == registers[src2] as int */
OP_JNEQ_INT, /* jump-neq-int : jump to address dest if registers[src1] as int != registers[src2] as int */
OP_JGT_INT, /* jump-gt-int : jump to address dest if registers[src1] as int > registers[src2] as int */
OP_JLT_INT, /* jump-lt-int : jump to address dest if registers[src1] as int < registers[src2] as int */
OP_JLE_INT, /* jump-le-int : jump to address dest if registers[src1] as int <= registers[src2] as int */
OP_JGE_INT, /* jump-ge-int : jump to address dest if registers[src1] as int >= registers[src2] as int */
OP_JEQ_NAT, /* jump-eq-nat : jump to address dest if registers[src1] as nat == registers[src2] as nat */
OP_JNEQ_NAT, /* jump-neq-nat : jump to address dest if registers[src1] as nat != registers[src2] as nat */
OP_JGT_NAT, /* jump-gt-nat : jump to address dest if registers[src1] as nat > registers[src2] as nat */
OP_JLT_NAT, /* jump-lt-nat : jump to address dest if registers[src1] as nat < registers[src2] as nat */
OP_JLE_NAT, /* jump-le-nat : jump to address dest if registers[src1] as nat <= registers[src2] as nat */
OP_JGE_NAT, /* jump-ge-nat : jump to address dest if registers[src1] as nat >= registers[src2] as nat */
OP_JEQ_REAL, /* jump-eq-real : jump to address dest if registers[src1] as real == registers[src2] as real */
OP_JNEQ_REAL, /* jump-neq-real : jump to address dest if registers[src1] as real != registers[src2] as real */
OP_JGE_REAL, /* jump-ge-real : jump to address dest if registers[src1] as real >= registers[src2] as real */
OP_JGT_REAL, /* jump-gt-real : jump to address dest if registers[src1] as real > registers[src2] as real */
OP_JLT_REAL, /* jump-lt-real : jump to address dest if registers[src1] as real < registers[src2] as real */
OP_JLE_REAL, /* jump-le-real : jump to address dest if registers[src1] as real <= registers[src2] as real */
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 */

View File

@ -1,7 +1,7 @@
#include "vm.h"
#include "device.h"
#include "opcodes.h"
#include "str.h"
#include "libc.h"
#define COMPARE_AND_JUMP(type, op) \
do { \
@ -93,6 +93,7 @@ bool step_vm(VM *vm) {
switch (opcode) {
case OP_HALT: {
vm->flag = read_u32(vm, code, vm->pc);
return false;
}
case OP_CALL: {
@ -721,11 +722,11 @@ bool step_vm(VM *vm) {
}
return true;
}
case OP_SLL:
case OP_BIT_SHIFT_LEFT:
BIT_OP(<<);
case OP_SRL:
case OP_BIT_SHIFT_RIGHT:
BIT_OP(>>);
case OP_SRE:
case OP_BIT_SHIFT_R_EXT:
MATH_OP(i32, >>);
case OP_BAND:
BIT_OP(&);
@ -741,6 +742,30 @@ bool step_vm(VM *vm) {
MATH_OP(i32, *);
case OP_DIV_INT:
MATH_OP(i32, /);
case OP_ABS_INT: {
dest = read_u8(vm, code, vm->pc);
vm->pc++;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
value = frame->registers[src1];
if (value < 0) {
value = -value;
}
frame->registers[dest] = value;
return true;
}
case OP_NEG_INT: {
dest = read_u8(vm, code, vm->pc);
vm->pc++;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
value = frame->registers[src1];
frame->registers[dest] = -value;
return true;
}
case OP_ADD_NAT:
MATH_OP(u32, +);
case OP_SUB_NAT:
@ -901,6 +926,7 @@ bool step_vm(VM *vm) {
int_to_string(AS_INT(frame->registers[src1]), buffer);
ptr = str_alloc(vm, frame, buffer, strlength(buffer));
frame->registers[dest] = ptr;
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_NAT_TO_STRING: {
@ -912,6 +938,7 @@ bool step_vm(VM *vm) {
nat_to_string(frame->registers[src1], buffer);
ptr = str_alloc(vm, frame, buffer, strlength(buffer));
frame->registers[dest] = ptr;
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_REAL_TO_STRING: {
@ -924,6 +951,7 @@ bool step_vm(VM *vm) {
ptr = str_alloc(vm, frame, buffer,
strlength(buffer)); /* copy buffer to dest */
frame->registers[dest] = ptr;
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_STRLEN: {

View File

@ -1,12 +1,9 @@
#ifndef ZRE_VM_H
#define ZRE_VM_H
#ifndef UNDAR_VM_H
#define UNDAR_VM_H
#include "opcodes.h"
bool step_vm(VM *vm);
u32 str_alloc(VM *vm, Frame *frame, const char *str, u32 length);
void fixed_to_string(i32 value, char *buffer);
void int_to_string(i32 value, char *buffer);
void nat_to_string(u32 value, char *buffer);
#endif

View File

@ -5,7 +5,7 @@
(call &add ($0 $1) $2)
(int-to-string $3 $2)
(call &pln ($3) nil)
(halt))
(halt 0))
(label add
(add-int $2 $1 $0)
@ -15,9 +15,9 @@
(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)
(load-immediate $3 &new-line)
(string-length $4 $3)
(syscall WRITE $1 $3 $4)
(return nil)))

View File

@ -4,7 +4,7 @@
(call &fib ($0) $0)
(int-to-string $1 $0)
(call &pln ($1) nil)
(halt))
(halt 0))
(label fib
(load-immediate $1 2)
(jump-lt-int &base-case $0 $1)

View File

@ -2,7 +2,7 @@
(label main
(load-immediate $1 &hello-str) ; load hello string ptr
(call &pln ($1) nil)
(halt)) ; done
(halt 0)) ; done
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)

View File

@ -27,7 +27,7 @@
(call &pln ($4) nil)
(real-to-string $3 $0)
(call &pln ($3) nil)
(halt))
(halt 0))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)

View File

@ -18,7 +18,7 @@ function main() {
}
nat b = a as nat;
pln(term, "Enter a string:");
str user_string = read(term, 32);
str user_string = term.read(32);
pln(term, a.str);
pln(term, b.str);
pln(term, user_string);

View File

@ -12,7 +12,7 @@
(syscall READ $0 $4 $1) ; read the string
(call &pln ($0 $4) nil) ; print the string
(halt))
(halt 0))
(label pln
(load-immediate $3 &new-line)
(string-length $2 $1)

View File

@ -67,7 +67,7 @@
(jump &draw-loop))
; Flush and halt
(halt))
(halt 0))
(label set-color-if-clicked
; (click_x, click_y, box_x, box_y, color, box_size)

View File

@ -17,7 +17,6 @@ plex Screen implements Device {
nat handle;
nat width;
nat height;
nat buffer_size;
byte[] buffer;
draw() {
@ -50,22 +49,22 @@ function main() {
loop {
mouse.refresh();
if (not mouse.left) continue;
if (!mouse.left) continue;
int box_size = 20;
int x = 1;
int y = 1;
byte color = BLACK;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
int box_size = 20;
int x = 1;
int y = 1;
byte color = BLACK;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
color = WHITE;
x = 21;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
screen.draw();
color = WHITE;
x = 21;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
screen.draw();
rectangle(screen, selected_color, x, y, 5, 5);
rectangle(screen, selected_color, x, y, 5, 5);
}
exit(0);
}

View File

@ -154,7 +154,7 @@
(jump &draw-loop))
; Flush and halt
(halt))
(halt 0))
(label set-color-if-clicked
; (click_x, click_y, box_x, box_y, color, box_size)

View File

@ -17,12 +17,11 @@ plex Screen implements Device {
nat handle;
nat width;
nat height;
nat buffer_size;
byte[] buffer;
draw() {
unsafe {
write(this, this.buffer, this.buffer_size);
write(this, this.buffer, this.buffer.length);
}
}
}
@ -50,22 +49,22 @@ function main() {
loop {
mouse.refresh();
if (not mouse.left) continue;
if (!mouse.left) continue;
int box_size = 20;
int x = 1;
int y = 1;
byte color = BLACK;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
int box_size = 20;
int x = 1;
int y = 1;
byte color = BLACK;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
color = WHITE;
x = 21;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
screen.draw();
color = WHITE;
x = 21;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
screen.draw();
rectangle(screen, selected_color, x, y, 5, 5);
rectangle(screen, selected_color, x, y, 5, 5);
}
exit(0);
}
@ -105,14 +104,14 @@ function outline_swatch(Device screen, byte color, int x, int y) {
* 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 `&` and `memset` directly
// 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 pixel = y * width + x + &screen.buffer + 4;
int base = y * screen.width + x + screen.buffer.ptr + 4;
do (int i = height; i > 0; i--) {
int row = pixel + width;
int row = base + width;
memset(screen.buffer, row, color, width);
pixel += width;
base += screen.width;
}
}
return;

View File

@ -5,7 +5,7 @@
(add-real $2 $1 $0)
(real-to-string $3 $2)
(call &pln ($3) nil)
(halt))
(halt 0))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)

View File

@ -52,7 +52,7 @@
(syscall WRITE $0 $21 $22) ; redraw
(jump &draw-loop))
(halt))
(halt 0))
(label pln
(load-immediate $1 &terminal-namespace) ; get terminal device
(load-immediate $11 0)

View File

@ -15,11 +15,10 @@ plex Screen {
nat handle;
nat width;
nat height;
nat buffer_size;
byte[] screen_buffer;
byte[] buffer;
draw() {
write(&this, this.screen_buffer, this.buffer_size);
write(this, this.buffer, this.buffer_size);
}
}
@ -42,16 +41,20 @@ function main() {
pln(screen.handle.str);
pln(screen.width.str);
pln(screen.size.str);
pln(screen.screen_buffer.ptr.str);
unsafe {
pln(screen.screen_buffer.ptr.str);
}
Mouse mouse = open("/dev/mouse/0", 0);
screen.draw();
loop {
if (mouse.btn1) {
screen.screen_buffer[mouse.y * width + mouse.x +
screen.screen_buffer.ptr + 4] = WHITE;
screen.draw();
unsafe {
screen.buffer[mouse.y * width + mouse.x +
screen.buffer.ptr + 4] = WHITE;
screen.draw();
}
}
}
}
@ -60,7 +63,7 @@ function main() {
* Print with a newline
*/
function pln(str message) {
Terminal term();
Terminal term = open("/dev/term/0", 0);
write(term, message, message.length);
write(term, nl, nl.length);
}