Add new IR language, update docs

This commit is contained in:
zongor 2025-11-08 22:19:18 -08:00
parent 5d2aa1ad22
commit 1311659bed
23 changed files with 765 additions and 320 deletions

View File

@ -86,17 +86,13 @@ VM_SOURCES := \
ifeq ($(BUILD_MODE), release) ifeq ($(BUILD_MODE), release)
PLATFORM_SOURCE := $(ARCH_DIR)/main.c \ PLATFORM_SOURCE := $(ARCH_DIR)/main.c \
$(ARCH_DIR)/devices.c\ $(ARCH_DIR)/devices.c\
$(SRC_DIR)/tools/parser.c \ $(SRC_DIR)/tools/assembler/parser.c \
$(SRC_DIR)/tools/lexer.c \ $(SRC_DIR)/tools/assembler/assembler.c
$(SRC_DIR)/tools/assembler.c\
$(SRC_DIR)/tools/compiler.c
else else
PLATFORM_SOURCE := $(ARCH_DIR)/main.c \ PLATFORM_SOURCE := $(ARCH_DIR)/main.c \
$(ARCH_DIR)/devices.c \ $(ARCH_DIR)/devices.c \
$(SRC_DIR)/tools/parser.c \ $(SRC_DIR)/tools/assembler/parser.c \
$(SRC_DIR)/tools/lexer.c \ $(SRC_DIR)/tools/assembler/assembler.c
$(SRC_DIR)/tools/assembler.c\
$(SRC_DIR)/tools/compiler.c
endif endif
# --- OBJECT FILES --- # --- OBJECT FILES ---

View File

@ -49,25 +49,35 @@ git clone https://git.alfrescocavern.com/zongor/undar-lang.git
cd undar-lang && make cd undar-lang && make
#+END_SRC #+END_SRC
=Sċieppan= is a minimal assembler that uses s-expressions. =Sċieppan= is a intermediate representation.
You can view some examples in the =.asm.lisp= files in =/test= You can view some examples in the =.ul.ir= files in =/test=
The Undâr compiler will be written in Sċieppan, as well as core VM tests.
**Sample Program: =hello.asm.lisp=** **Sample Program: =hello.ul.ir=**
#+BEGIN_SRC lisp #+BEGIN_SRC sh
((code function main ()
(label main str hello is $0
(ldi $0 &terminal-namespace) ; get terminal device
(ldi $1 0) load_heap_immediate "nuqneH 'u'?" -> hello
(syscall OPEN $0 $0 $1) call pln hello
(ldi $1 &hello-str) ; load hello string ptr exit 0
(strlen $2 $1)
(syscall WRITE $0 $1 $2) function pln (str message is $0)
(halt 0))) str ts is $1
(data int msg_length is $2
(label terminal-namespace "/dev/term/0") str nl is $3
(label hello-str "nuqneH 'u'?\n"))) int nl_length is $4
int mode is $5
load_heap_immediate "/dev/term/0" -> ts # get terminal device
load_immediate 0 -> mode
syscall OPEN ts mode -> ts
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length
return
#+END_SRC #+END_SRC
#+BEGIN_SRC sh #+BEGIN_SRC sh
@ -82,33 +92,43 @@ memory is managed via frame based arenas. function scopes defines a memory frame
heap allocations using the internal malloc opcode push pointers within this frame. when a frame exits, the pointer is reset like stack based gc. heap allocations using the internal malloc opcode push pointers within this frame. when a frame exits, the pointer is reset like stack based gc.
#+BEGIN_SRC lisp #+BEGIN_SRC sh
((code function main ()
(label main int mode is $11
(ldi $0 &terminal-namespace) ; get terminal device str term is $10
(ldi $11 0)
(syscall OPEN $0 $0 $11)
(ldi $1 &help) ; print help message load_heap_immediate "/dev/term/0" -> term
(fcall &pln ($0 $1) nil) load_immediate 0 -> mode
syscall OPEN term mode -> term # Terminal term = open("/dev/term/0", 0);
(ldi $1 32) ; read in a string of max 32 char length load_heap_immediate "Enter a string:" -> $7
(malloc $4 $1) ; allocate memory for the string string_length $7 -> $8
(syscall READ $0 $4 $1) ; read the string syscall WRITE term $7 $8 # print prompt
str user_string is $9
load_immediate 32 -> $8
malloc $8 -> user_string
syscall READ term user_string $8 # read in max 32 byte string
call pln user_string
exit 0
function pln (str message is $0)
str ts is $1
int mode is $5
int msg_length is $2
str nl is $3
int nl_length is $4
load_heap_immediate "/dev/term/0" -> ts
load_immediate 0 -> mode
syscall OPEN ts mode -> ts # get terminal device
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length
(fcall &pln ($0 $4) nil) ; print the string
(halt))
(label pln
(ldi $3 &new-line)
(strlen $2 $1)
(syscall WRITE $0 $1 $2)
(strlen $4 $3)
(syscall WRITE $0 $3 $4)
(return nil)))
(data
(label terminal-namespace "/dev/term/0")
(label help "Enter a string: ")
(label new-line "\n")))
#+END_SRC #+END_SRC
values passed to functions must be explicitly returned to propagate. heap values are copy on write, so if a value is modified in a child function it will change the parents value, unless the size of the structure changes then it will copy the parents value and append it to its own frame with the modification. this allows for the low resource usage of a C but the convenience of a Java/Go without the garbage collection. values passed to functions must be explicitly returned to propagate. heap values are copy on write, so if a value is modified in a child function it will change the parents value, unless the size of the structure changes then it will copy the parents value and append it to its own frame with the modification. this allows for the low resource usage of a C but the convenience of a Java/Go without the garbage collection.

View File

@ -170,7 +170,7 @@ function main(int argc, str[] argv) {
} }
} }
exits("Client Closed Successfully"); exit(0);
} }
#+END_SRC #+END_SRC
@ -184,7 +184,7 @@ function main(int argc, str[] argv) {
Player[] players = [Player("user", [0.0, 0.0, 0.0], RED)]; Player[] players = [Player("user", [0.0, 0.0, 0.0], RED)];
while (running) { while (running) {
if (Client client = s.accept("players")) { if (Client client = s.accept("players")) {
if (Message message = client.get()) { if (Message message = client.read()) {
if (message.t == "close") { if (message.t == "close") {
client.close(); client.close();
running = false; running = false;
@ -196,7 +196,7 @@ function main(int argc, str[] argv) {
} }
} }
} }
exits(nil); exit(0);
} }
#+END_SRC #+END_SRC

View File

@ -1,6 +1,5 @@
#include "../../tools/assembler.h" #include "../../tools/assembler/assembler.h"
#include "../../tools/compiler.h" #include "../../tools/assembler/parser.h"
#include "../../tools/parser.h"
#include "../../vm/vm.h" #include "../../vm/vm.h"
#include "devices.h" #include "devices.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
@ -146,19 +145,7 @@ bool compileAndSave(const char *source_file, const char *output_file, VM *vm) {
source[read] = '\0'; source[read] = '\0';
fclose(f); fclose(f);
initLexer(source);
Token token;
do {
token = nextToken();
if (token.type == TOKEN_ERROR) {
printf("ERROR at line %d: %.*s\n", token.line, token.length, token.start);
break; // Stop on error, or continue if you want to see more
}
if (token.type != TOKEN_EOF) {
printf("Line %d [%s]: %.*s\n", token.line, tokenTypeToString(token.type),
token.length, token.start);
}
} while (token.type != TOKEN_EOF);
return true; return true;
} }

View File

@ -1,8 +1,8 @@
#ifndef ASSEMBLER_H #ifndef ASSEMBLER_H
#define ASSEMBLER_H #define ASSEMBLER_H
#include "../vm/common.h" #include "../../vm/common.h"
#include "../vm/vm.h" #include "../../vm/vm.h"
#include "parser.h" #include "parser.h"
#include <string.h> #include <string.h>

View File

@ -1,200 +0,0 @@
#include "compiler.h"
#include "lexer.h"
#include <stdarg.h>
#include <stdio.h>
//typedef struct {
// Token current;
// Token previous;
// bool hadError;
// bool panicMode;
//} Parser;
//
//typedef enum {
// PREC_NONE,
// PREC_ASSIGNMENT, /* = */
// PREC_OR, /* or */
// PREC_AND, /* and */
// PREC_EQUALITY, /* == != */
// PREC_COMPARISON, /* < > <= >= */
// PREC_TERM, /* + - */
// PREC_FACTOR, /* * / */
// PREC_UNARY, /* not */
// PREC_CALL, /* . () */
// PREC_PRIMARY
//} Precedence;
//
//typedef void (*ParseFn)(char *program);
//
//typedef struct {
// ParseFn prefix;
// ParseFn infix;
// Precedence precedence;
//} ParseRule;
//
//typedef struct {
// i8 rp; // Next free register
//} Compiler;
//
//Parser parser;
//
//const char *internalErrorMsg =
// "FLAGRANT COMPILER ERROR\n\nCompiler over.\nBug = Very Yes.";
//
//void errorAt(Token *token, const char *message) {
// if (parser.panicMode)
// return;
// parser.panicMode = true;
// fprintf(stderr, "[line %d] Error", token->line);
//
// if (token->type == TOKEN_EOF) {
// fprintf(stderr, " at end");
// } else if (token->type == TOKEN_ERROR) {
// } else {
// fprintf(stderr, " at '%.*s'", token->length, token->start);
// }
//
// fprintf(stderr, ": %s\n", message);
// parser.hadError = true;
//}
//
//void error(const char *message) { errorAt(&parser.previous, message); }
//
//void errorAtCurrent(const char *message) { errorAt(&parser.current, message); }
//
//void advance() {
// parser.previous = parser.current;
//
// for (;;) {
// parser.current = nextToken();
// if (parser.current.type != TOKEN_ERROR)
// break;
//
// errorAtCurrent(parser.current.start);
// }
//}
//
//void consume(TokenType type, const char *message) {
// if (parser.current.type == type) {
// advance();
// return;
// }
//
// errorAtCurrent(message);
//}
//
//static int allocateRegister(Compiler *c) {
// char buffer[38];
// if (c->rp > 28) {
// sprintf(buffer, "Out of registers (used %d, max 28)", c->rp);
// error(buffer);
// return -1;
// }
//
// return c->rp++;
//}
//static void freeRegister(Compiler *c, u8 reg) {
// if (reg == c->rp - 1) {
// c->rp--;
// }
//}
//void emit_byte(VM *vm, u8 byte) { vm->code[vm->cp++] = byte; }
//
//void emit_u32(VM *vm, u32 value) {
// write_u32(vm, code, vm->cp, value);
// vm->cp += 4;
//}
//
//void emit_opcode(VM *vm, Opcode op) { emit_byte(vm, op); }
//
//static bool check(TokenType type) { return parser.current.type == type; }
//
//static bool match(TokenType type) {
// if (!check(type))
// return false;
// advance();
// return true;
//}
//
//static void expression(Compiler *c, VM *vm) {
// USED(c);
// USED(vm);
//}
//
//void number(Compiler *c, VM *vm) {
// emit_opcode(vm, OP_LOAD_IMM);
// int reg = allocateRegister(c);
// if (reg < 0)
// return;
// emit_byte(vm, reg);
//
// switch (parser.previous.type) {
// case TOKEN_INT_LITERAL: {
// char *endptr;
// i32 value = (i32)strtol(parser.previous.start, &endptr, 10);
// emit_u32(vm, value);
// return;
// }
// case TOKEN_UINT_LITERAL: {
// long value = atol(parser.previous.start);
// emit_u32(vm, value);
// return;
// }
// case TOKEN_FLOAT_LITERAL: {
// float value = atof(parser.previous.start);
// fixed_t fvalue = float_to_fixed(value);
// emit_u32(vm, fvalue);
// return;
// }
// default:
// return; // Unreachable.
// }
//
// errorAtCurrent("Invalid number format");
//}
//
//static void unary(Compiler *c, VM *vm) {
// TokenType operatorType = parser.previous.type;
//
// // Compile the operand.
// expression(c, vm);
//
// // Emit the operator instruction.
// switch (operatorType) {
// default:
// return; // Unreachable.
// }
//}
//
//static void emitHalt(Compiler *c, VM *vm) {
// emit_opcode(vm, OP_HALT);
// advance();
// number(c, vm);
//}
//
//static void endCompiler(Compiler *c, VM *vm) { emitHalt(c, vm); }
//
//static void grouping(Compiler *c, VM *vm) {
// expression(c, vm);
// consume(TOKEN_RPAREN, "Expect ')' after expression.");
//}
bool compile(const char *source, VM *vm) {
USED(source);
USED(vm);
//initLexer(source);
//
//parser.hadError = false;
//parser.panicMode = false;
//
//Compiler compiler;
//advance();
//expression(&compiler, vm);
//consume(TOKEN_EOF, "Expect end of expression.");
//endCompiler(&compiler, vm);
//
//return parser.hadError;
return false;
}

View File

@ -1,38 +0,0 @@
#ifndef UNDAR_COMPILER_H
#define UNDAR_COMPILER_H
#include "../vm/common.h"
#include "../vm/vm.h"
#include "../vm/fixed.h"
#include "lexer.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct field_s {
char* name; // "handle"
TokenType type; // TOKEN_TYPE_NAT
u32 offset; // 4 (for first field in heap object)
u32 size; // 4 (bytes for nat)
} Field;
typedef struct plex_def_s {
char* name;
u32 field_count;
u32 logical_size; // Data size (e.g., 12 for Vec3)
u32 physical_size; // Total allocation (logical + 4)
Field *fields; // All offsets are PHYSICAL
} PlexDef;
typedef struct array_def_s {
TokenType type;
u32 length;
u32 logical_size; // length * element_size
u32 physical_size; // logical_size + 4
} ArrayDef;
bool compile(const char *source, VM *vm);
#endif

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

@ -0,0 +1,37 @@
global const int x = 1
global const int y = 1
function main ()
int a is $0
int b is $1
int ans is $2
str ans_string is $3
load_absolute_32 &x -> a
load_absolute_32 &y -> b
call add a b -> ans
int_to_string ans -> ans_string
call pln ans_string
exit 0
function add (int a is $0, int b is $1)
int result is $2
add_int a b -> result
return result
function pln (str message is $0)
str ts is $1
int mode is $5
int msg_length is $2
str nl is $3
int nl_length is $4
load_heap_immediate ts "/dev/term/0" # get terminal device
load_immediate 0 -> mode
syscall OPEN ts mode -> ts
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length
return

Binary file not shown.

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

@ -0,0 +1,44 @@
function main ()
int n is $0
int str_n is $1
load_immediate 35 -> n
call fib n -> n
int_to_string n -> str_n
call pln str_nn
exit 0
function fib (int n is $0)
load_immediate 2 -> $1
jump_lt_int &base_case n $1
load_immediate 2 -> $3
sub_int n $3 -> $4
call fib $4 -> $5
load_immediate 1 -> $3
sub_int n $3 -> $4
call fib $4 -> $6
add_int $6 $5 -> $7
return $7
&base_case
return n
function pln (str message is $0)
str ts is $1
int mode is $5
int msg_length is $2
str nl is $3
int nl_length is $4
load_heap_immediate ts "/dev/term/0" # get terminal device
load_immediate 0 -> mode
syscall OPEN ts mode -> ts
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length
return

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

@ -0,0 +1,23 @@
function main ()
str hello is $0
load_heap_immediate "nuqneH 'u'?" -> hello
call pln hello
exit 0
function pln (str message is $0)
str ts is $1
int msg_length is $2
str nl is $3
int nl_length is $4
int mode is $5
load_heap_immediate "/dev/term/0" -> ts # get terminal device
load_immediate 0 -> mode
syscall OPEN ts mode -> ts
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length
return

View File

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

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

@ -0,0 +1,54 @@
function main ()
real a is $0
int i is $1
int mode is $11
str term is $10
load_immediate 5.0 -> a
load_immediate 5000 -> i
load_immediate 0 -> $2
load_immediate -1 -> $3
load_immediate 5.0 -> $5
&loop_body
add_real a $5 -> a
add_int i $3 -> i
jump_ge_int &loop_body i $2
load_heap_immediate "/dev/term/0" -> term
load_immediate 0 -> mode
syscall OPEN term mode -> term # Terminal term = open("/dev/term/0", 0);
nat b is $1
real_to_nat a -> b
load_heap_immediate "Enter a string:" -> $7
string_length $7 -> $8
syscall WRITE term $7 $8 # print prompt
str user_string is $9
load_immediate 32 -> $8
malloc $8 -> user_string
syscall READ term user_string $8 # read in max 32 byte string
call pln user_string
nat_to_string b -> $4
call pln $4
real_to_string a -> $3
call pln $3
exit 0
function pln (str message is $0)
str ts is $1
int mode is $5
int msg_length is $2
str nl is $3
int nl_length is $4
load_heap_immediate "/dev/term/0" -> ts
load_immediate 0 -> mode
syscall OPEN ts mode -> ts # get terminal device
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length
return

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

@ -0,0 +1,36 @@
function main ()
int mode is $11
str term is $10
load_heap_immediate "/dev/term/0" -> term
load_immediate 0 -> mode
syscall OPEN term mode -> term # Terminal term = open("/dev/term/0", 0);
load_heap_immediate "Enter a string:" -> $7
string_length $7 -> $8
syscall WRITE term $7 $8 # print prompt
str user_string is $9
load_immediate 32 -> $8
malloc $8 -> user_string
syscall READ term user_string $8 # read in max 32 byte string
call pln user_string
exit 0
function pln (str message is $0)
str ts is $1
int mode is $5
int msg_length is $2
str nl is $3
int nl_length is $4
load_heap_immediate "/dev/term/0" -> ts
load_immediate 0 -> mode
syscall OPEN ts mode -> ts # get terminal device
strlen message -> msg_length
syscall WRITE ts message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE ts nl nl_length

View File

@ -21,7 +21,7 @@ plex Screen implements Device {
draw() { draw() {
unsafe { unsafe {
write(this, this.buffer, this.buffer_size); write(this, this.buffer, this.buffer.size);
} }
} }
} }

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

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

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

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

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

@ -0,0 +1,31 @@
global const real x = 1.0
global const real y = 1.0
function main ()
real x is $0
load_absolute_32 &x -> x
real y is $1
load_absolute_32 &y -> y
real result is $2
add_real x y -> result
str result_str is $3
real_to_string result -> result_str
call &pln result_str
exit 0
function pln (str message is $0)
str term is $1
int msg_length is $2
str nl is $3
int nl_length is $4
int mode is $5
load_heap_immediate "/dev/term/0" -> term # get terminal device
load_immediate 0 -> mode
syscall OPEN term mode -> term
strlen message -> msg_length
syscall WRITE term message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE term nl nl_length
return

View File

@ -49,7 +49,7 @@ function main() {
screen.draw(); screen.draw();
loop { loop {
if (mouse.btn1) { if (mouse.left) {
unsafe { unsafe {
screen.buffer[mouse.y * width + mouse.x + screen.buffer[mouse.y * width + mouse.x +
screen.buffer.ptr + 4] = WHITE; screen.buffer.ptr + 4] = WHITE;

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

@ -0,0 +1,89 @@
global const str screen_namespace = "/dev/screen/0"
global const str mouse_namespace = "/dev/mouse/0"
global const str terminal_namespace = "/dev/term/0"
global const str new_line = "\n"
global const byte WHITE = 255
function main ()
# Open screen
# use load immediate because it is a pointer to a string, not a value
plex screen
load_address &screen_namespace -> $18
int mode is $11
load_immediate 0 -> mode
syscall OPEN $18 mode -> screen # openout Plex screen, in namespace, in flags
nat_to_string screen -> $5
call &pln $5
nat width is $20
load_offset_32 screen 8 -> width # load width
nat_to_string width -> $5
call &pln $5
nat buffer_size is $22
load_offset_32 screen 12 -> buffer_size # load size
nat_to_string buffer_size -> $5
call &pln $5
nat screen_buffer is $21
load_immediate $1 16 # offset for screen buffer
add_nat screen $1 -> screen_buffer
nat_to_string screen_buffer -> $5
call &pln $5
# open mouse
plex mouse is $15
load_address &mouse_namespace -> $16
syscall OPEN $16 mode -> mouse # openout Plex mouse, in namespace, in flags
syscall WRITE screen screen_buffer buffer_size # redraw
draw_loop:
# load mouse click data
syscall STAT mouse
byte left_down is $9
load_offset_8 mouse 16 -> left_down # load btn1 pressed
jump_eq_nat &draw_loop left_down mode # mode is 0 which is an alias for false
nat x is $7
load_offset_32 mouse 8 -> x # load x
nat y is $8
load_offset_32 mouse 12 -> y # load y
# Compute start address: y*width + x
nat pixel_pos is $30
mul_nat y $20 -> pixel_pos # = y * width
add_nat x pixel_pos -> pixel_pos # += x
add_nat screen_buffer pixel_pos -> pixel_pos # += pixel_offset
nat fat_ptr_size is $1
load_immediate 4 -> fat_ptr_size # need to add offset for fat pointer size
add_nat pixel_pos fat_ptr_size -> pixel_pos
byte color is $3
load_absolute_32 &WHITE -> color
store_absolute_8 pixel_pos color # draw color at screen [x,y]
syscall WRITE screen screen_buffer buffer_size # redraw
jump &draw_loop
exit 0
function pln (str message is $0)
str term is $1
int msg_length is $2
str nl is $3
int nl_length is $4
int mode is $5
load_heap_immediate "/dev/term/0" -> term # get terminal device
load_immediate 0 -> mode
syscall OPEN term mode -> term
strlen message -> msg_length
syscall WRITE term message msg_length
load_heap_immediate "\n" -> nl
strlen nl -> nl_length
syscall WRITE term nl nl_length
return