wip casting, frames, equivilence, tests

This commit is contained in:
zongor 2026-02-10 00:34:05 -08:00
parent 11cfe34c95
commit ee39c8ba95
14 changed files with 759 additions and 227 deletions

View File

@ -1,24 +1,76 @@
#include "../../../vm/vm.h"
#include "../../../tools/compiler/compiler.h"
#include <SDL2/SDL.h>
#include <stdio.h>
#define CODE_SIZE 8192
#define MEMORY_SIZE 65536
#define CODE_SIZE 8192
#define STACK_SIZE 1024
u8 lmem[MEMORY_SIZE] = {0};
u32 lcode[CODE_SIZE] = {0};
u8 lcode[CODE_SIZE] = {0};
u32 lstack[STACK_SIZE] = {0};
Frame lframes[STACK_SIZE] = {0};
void reset() {
pc = 0;
cp = 0;
mp = 0;
fp = 0;
sp = 0;
interrupt = 0;
status = 0;
}
bool init_vm() {
mem = lmem;
code = lcode;
mp = 0;
cp = 0;
pc = 0;
interrupt = 0;
stack = lstack;
frames = lframes;
reset();
return true;
}
void error(const char* msg) {
printf("%s", msg);
}
bool table_realloc(ScopeTable *table) {
USED(table);
// static so do nothing;
return true;
}
u32 syscall(u32 id, u32 mem_ptr) {
return 0; // success
u32 size;
switch(id) {
case SYSCALL_CONSOLE_WRITE: {
u32 size = *(u32*)&mem[mem_ptr];
u8 *ptr = &mem[mem_ptr + 4];
for (u32 i = 0; i < size; i++) {
putchar(*(ptr++));
}
return 0;
}
case SYSCALL_CONSOLE_READ: {
u8 *ptr = &mem[mp];
mcpy(ptr, &size, sizeof(u32));
ptr += 4;
for (u32 i = 0; i < size; i++) {
u8 ch = getchar();
if (ch == '\0')
break;
if (ch == '\n')
break;
*(ptr++) = ch;
}
ptr[size] = '\0';
mp += 4 + size + 1;
}
}
return 1; // generic error
}
i32 main() {

View File

@ -9,11 +9,12 @@
u8 lmem[MEMORY_SIZE] = {0};
u8 lcode[CODE_SIZE] = {0};
u32 lstack[STACK_SIZE] = {0};
Frame lframes[STACK_SIZE] = {0};
void reset() {
pc = 0;
cp = 0;
mp = 255; // hardcoded for now, 255 locals
mp = 0;
fp = 0;
sp = 0;
interrupt = 0;
@ -24,6 +25,7 @@ bool init_vm() {
mem = lmem;
code = lcode;
stack = lstack;
frames = lframes;
reset();
return true;
}
@ -86,9 +88,7 @@ static void repl() {
while(step_vm()) {}
syscall(SYSCALL_CONSOLE_WRITE, 255);
printf("Result at top of stack: %d\n", stack[0]);
syscall(SYSCALL_CONSOLE_WRITE, stack[0]);
}
}

31
test/add.ul Normal file
View File

@ -0,0 +1,31 @@
/**
* Constants
*/
const str nl = "\n";
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln(add(1, 1).str);
}
/**
* Add two numbers together
*/
function add(int a, int b) int {
return a + b;
}
/**
* Print with a newline
*/
function pln(str message) {
Terminal term = open("term::/0", 0);
write(term, message, message.length);
write(term, nl, nl.length);
}

32
test/fib.ul Normal file
View File

@ -0,0 +1,32 @@
/**
* Constants
*/
const str nl = "\n";
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln(fib(35).str);
}
/**
* Recursively calculate fibonacci
*/
function fib(int n) int {
if (n < 2) { return n; }
return fib(n - 2) + fib(n - 1);
}
/**
* Print with a newline
*/
function pln(str message) {
Terminal term = open("term::/0", 0);
write(term, message, message.length);
write(term, nl, nl.length);
}

23
test/hello.ul Normal file
View File

@ -0,0 +1,23 @@
/**
* Plexes
*/
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln("nuqneH 'u'?");
}
/**
* Print with a newline
*/
function pln(str message) {
Terminal term = open("term::/0", 0);
write(term, message, message.length);
const str nl = "\n";
write(term, nl, nl.length);
}

26
test/malloc.ul Normal file
View File

@ -0,0 +1,26 @@
/**
* Constants
*/
const str nl = "\n";
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
Terminal term = open("term::/0", 0);
pln(term, "Enter a string: ");
pln(term, term.read(32));
return 0;
}
/**
* Print with a newline
*/
function pln(Terminal term, str message) {
write(term, message, message.length);
write(term, nl, nl.length);
}

109
test/paint.ul Normal file
View File

@ -0,0 +1,109 @@
/**
* Constants
*/
const byte BLACK = 0;
const byte WHITE = 255;
const byte DARK_GRAY = 73;
const byte GRAY = 146;
const byte LIGHT_GRAY = 182;
byte selected_color = 255;
trait Device {
nat handle;
}
plex Screen implements Device {
nat handle;
nat width;
nat height;
byte[] buffer;
draw() {
write(this, this.buffer, this.buffer.length);
}
}
plex Mouse implements Device {
nat handle;
nat x;
nat y;
bool left;
bool right;
bool middle;
bool btn4;
}
/**
* Main function
*/
function main() {
Screen screen = open("screen::/0", 0);
Mouse mouse = open("mouse::/0", 0);
outline_swatch(screen, BLACK, 1, 1);
outline_swatch(screen, WHITE, 21, 1);
screen.draw();
loop {
mouse.refresh();
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);
color = WHITE;
x = 21;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
screen.draw();
rectangle(screen, selected_color, x, y, 5, 5);
}
exit(0);
}
/**
* Checks if the click is within the bound and update the selected color if so.
*/
function set_color(int box_size, int bx, int by, int mx, int my, byte color) {
int right = bx + box_size;
int bottom = by + box_size;
if (mx < bx) return;
if (mx > right) return;
if (my < by) return;
if (my > bottom) return;
selected_color = color;
}
/**
* Draw a color box with a grey outline, if selected use a darker color
*/
function outline_swatch(Device screen, byte color, int x, int y) {
byte bg_color = GRAY;
if (selected_color == color) {
bg_color = DARK_GRAY;
}
rectangle(screen, bg_color, x, y, 20, 20);
rectangle(screen, color, x + 2, y + 2, 17, 17);
}
/**
* Draw a rectangle
*/
function rectangle(Device screen, byte color, int x, int y, int width, int height) {
int base = y * screen.width + x + screen.buffer.ptr + 4;
do (int i = height; i > 0; i--) {
int row = base + width;
memset(screen.buffer, row, color, width);
base += screen.width;
}
}

25
test/simple.ul Normal file
View File

@ -0,0 +1,25 @@
/**
* Constants
*/
const str nl = "\n";
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln((1.0 + 1.0) as str);
exit(0);
}
/**
* Print with a newline
*/
function pln(str message) {
Terminal term = open("term::/0", 0);
write(term, message, message.length);
write(term, nl, nl.length);
}

72
test/window.ul Normal file
View File

@ -0,0 +1,72 @@
/**
* Constants
*/
const str screen_namespace = "screen::/0"
const str mouse_namespace = "mouse::/0"
const str terminal_namespace = "term::/0"
const str new_line = "\n"
const byte WHITE = 255
/**
* Devices
*/
plex Terminal {
nat handle;
}
plex Screen {
nat handle;
nat width;
nat height;
byte[] buffer;
draw() {
write(this, this.buffer, this.buffer_size);
}
}
plex Mouse {
nat handle;
nat x;
nat y;
bool left;
bool right;
bool middle;
bool btn4;
nat size;
}
/**
* Main function
*/
function main() {
Screen screen = open(screen_namespace, 0);
pln(screen.handle as str);
pln(screen.width as str);
pln(screen.size as str);
unsafe {
pln(screen.screen_buffer.ptr as str);
}
Mouse mouse = open(mouse_namespace, 0);
screen.draw();
loop {
if (mouse.left) {
unsafe {
screen.buffer[mouse.y * width + mouse.x +
screen.buffer.ptr + 4] = WHITE;
screen.draw();
}
}
}
}
/**
* Print with a newline
*/
function pln(str message) {
Terminal term = open(terminal_namespace, 0);
write(term, message, message.length);
write(term, nl, nl.length);
}

View File

@ -235,9 +235,7 @@ static void grouping() {
static void unary() {
TokenType operatorType = parser.previous.type;
parsePrecedence(PREC_UNARY);
switch (operatorType) {
case TOKEN_MINUS: {
code[cp++] = OP_NEG;
@ -252,11 +250,67 @@ static void unary() {
}
}
static void cast(TokenType prev) {
switch (prev) {
case TOKEN_TYPE_I8: {
break;
}
case TOKEN_TYPE_I16: {
break;
}
case TOKEN_TYPE_INT: {
break;
}
case TOKEN_TYPE_U8: {
break;
}
case TOKEN_TYPE_U16: {
break;
}
case TOKEN_TYPE_NAT: {
break;
}
case TOKEN_TYPE_REAL: {
break;
}
case TOKEN_TYPE_BOOL: {
break;
}
case TOKEN_TYPE_STR: {
break;
}
default: {
printf("Cannot cast to type (%s)\n", token_type_to_string(parser.previous.type));
}
}
}
static void binary() {
TokenType operatorType = parser.previous.type;
TokenType operand = parser.current.type;
ParseRule *rule = getRule(operatorType);
parsePrecedence((Precedence)(rule->precedence + 1));
printf("before prev: %s, operatorType: %s, operand: %s\n",
token_type_to_string(parser.previous.type),
token_type_to_string(operatorType),
token_type_to_string(operand));
switch (operatorType) {
case TOKEN_KEYWORD_AS: {
cast(parser.previous.type);
break;
}
case TOKEN_PLUS: {
switch (parser.previous.type) {
case TOKEN_LITERAL_INT:
@ -273,7 +327,7 @@ static void binary() {
break;
default:
printf("Unknown Add Arg=%d\n", parser.previous.type);
return; // Unreachable.
return;
}
break;
}
@ -338,23 +392,111 @@ static void binary() {
break;
}
case TOKEN_EQ_EQ: {
code[cp++] = OP_EQ;
switch (parser.previous.type) {
case TOKEN_LITERAL_INT:
case TOKEN_LITERAL_REAL:
code[cp++] = OP_EQS;
break;
case TOKEN_LITERAL_NAT:
code[cp++] = OP_EQU;
break;
case TOKEN_IDENTIFIER:
printf("FIXME: find the identifier's type for ==\n");
break;
default:
printf("Unknown == Arg=%d\n", parser.previous.type);
return; // Unreachable.
}
break;
}
case TOKEN_BANG_EQ: {
switch (parser.previous.type) {
case TOKEN_LITERAL_INT:
case TOKEN_LITERAL_REAL:
code[cp++] = OP_NES;
break;
case TOKEN_LITERAL_NAT:
code[cp++] = OP_NEU;
break;
case TOKEN_IDENTIFIER:
printf("FIXME: find the identifier's type for !=\n");
break;
default:
printf("Unknown != Arg=%d\n", parser.previous.type);
return; // Unreachable.
}
break;
}
case TOKEN_GT: {
code[cp++] = OP_GT;
switch (parser.previous.type) {
case TOKEN_LITERAL_INT:
case TOKEN_LITERAL_REAL:
code[cp++] = OP_GTS;
break;
case TOKEN_LITERAL_NAT:
code[cp++] = OP_GTU;
break;
case TOKEN_IDENTIFIER:
printf("FIXME: find the identifier's type for >\n");
break;
default:
printf("Unknown > Arg=%d\n", parser.previous.type);
return; // Unreachable.
}
break;
}
case TOKEN_GTE: {
code[cp++] = OP_GE;
switch (parser.previous.type) {
case TOKEN_LITERAL_INT:
case TOKEN_LITERAL_REAL:
code[cp++] = OP_GES;
break;
case TOKEN_LITERAL_NAT:
code[cp++] = OP_GEU;
break;
case TOKEN_IDENTIFIER:
printf("FIXME: find the identifier's type for >=\n");
break;
default:
printf("Unknown >= Arg=%d\n", parser.previous.type);
return; // Unreachable.
}
break;
}
case TOKEN_LT: {
code[cp++] = OP_LT;
switch (parser.previous.type) {
case TOKEN_LITERAL_INT:
case TOKEN_LITERAL_REAL:
code[cp++] = OP_LTS;
break;
case TOKEN_LITERAL_NAT:
code[cp++] = OP_LTU;
break;
case TOKEN_IDENTIFIER:
printf("FIXME: find the identifier's type for <\n");
break;
default:
printf("Unknown < Arg=%d\n", parser.previous.type);
return; // Unreachable.
}
break;
}
case TOKEN_LTE: {
code[cp++] = OP_LE;
switch (parser.previous.type) {
case TOKEN_LITERAL_REAL:
case TOKEN_LITERAL_INT:
code[cp++] = OP_LES;
break;
case TOKEN_LITERAL_NAT:
code[cp++] = OP_LEU;
break;
case TOKEN_IDENTIFIER:
printf("FIXME: find the identifier's type for <=\n");
break;
default:
printf("Unknown <= Arg=%d\n", parser.previous.type);
return; // Unreachable.
}
break;
}
default:
@ -421,10 +563,12 @@ static void string() {
WRITE_U32(addr, len);
WRITE_U8(addr + 4 + len, '\0');
// TODO: this really should always be tied to a global or local variable
// we can fake it for now
WRITE_U32(fp + lp, addr);
lp++;
/* push address of string on the stack */
code[cp++] = OP_PUSH_32;
code[cp++] = (addr) & 0xFF;
code[cp++] = ((addr) >> 8) & 0xFF;
code[cp++] = ((addr) >> 16) & 0xFF;
code[cp++] = ((addr) >> 24) & 0xFF;
}
ParseRule rules[] = {
@ -453,6 +597,7 @@ ParseRule rules[] = {
[TOKEN_LITERAL_NAT] = {number, NULL, PREC_NONE},
[TOKEN_LITERAL_REAL] = {number, NULL, PREC_NONE},
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
[TOKEN_KEYWORD_AS] = {NULL, binary, PREC_CAST},
[TOKEN_KEYWORD_PLEX] = {NULL, NULL, PREC_NONE},
[TOKEN_KEYWORD_ELSE] = {NULL, NULL, PREC_NONE},
[TOKEN_KEYWORD_FALSE] = {literal, NULL, PREC_NONE},

View File

@ -93,7 +93,8 @@ typedef enum {
PREC_COMPARISON, // < > <= >=
PREC_TERM, // + -
PREC_FACTOR, // * /
PREC_UNARY, // ! -
PREC_CAST, // as
PREC_UNARY, // ! -
PREC_CALL, // . ()
PREC_PRIMARY
} Precedence;

View File

@ -206,12 +206,21 @@ static TokenType identifierType() {
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);
case 'a':
if (lexer.current - lexer.start > 3) {
switch (lexer.start[3]) {
case 'd': {
return checkKeyword(4, 0, "", TOKEN_KEYWORD_READ);
}
case 'l': {
return checkKeyword(4, 0, "", TOKEN_TYPE_REAL);
}
}
}
}
}
break;

218
vm/vm.c
View File

@ -1,21 +1,19 @@
#include "vm.h"
#define FRAME_HEADER_SIZE 12
u32 pc; /* program counter */
u32 cp; /* code pointer */
u32 mp; /* memory pointer */
u32 fp; /* frame pointer */
u32 *stack; /* stack */
u32 sp; /* stack pointer */
u8 lp; /* locals pointer */
u8 *code; /* code */
u32 cp; /* code pointer */
u8 *mem; /* memory */
u32 mp; /* memory pointer */
Frame *frames; /* call frames */
u32 fp; /* frame pointer */
u32 pc; /* program counter */
u8 status; /* status flag */
u8 interrupt; /* device interrupt */
u32 *stack; /* stack */
u8 *code; /* code */
u8 *mem; /* memory */
#define MAX_LEN_INT32 11
#define MAX_INT32 2147483647
#define MIN_INT32 -2147483648
const char radix_set[11] = "0123456789";
u32 str_alloc(char *str, u32 length) {
@ -41,7 +39,6 @@ bool step_vm() {
return false;
}
case OP_CALL: {
/* TODO: Fix this one so it makes sense with a stack based system */
/* function to jump to */
u32 fn_ptr = stack[--sp];
/* get mp in 'global indexing mode' */
@ -93,7 +90,7 @@ bool step_vm() {
return false;
}
case OP_SYSCALL: {
u32 id = stack[--sp]; /* syscall id */
u32 id = stack[--sp]; /* syscall id */
u32 rd = stack[--sp]; /* the pointer */
status = syscall(id, rd);
return true;
@ -111,8 +108,9 @@ bool step_vm() {
return true;
}
case OP_PUSH_32: {
u32 *values = (u32*)(code);
u32 value = values[pc/4];
u32 value = ((u32)code[(pc) + 3] << 24) |
((u32)code[(pc) + 2] << 16) |
((u32)code[(pc) + 1] << 8) | ((u32)mem[(pc)]);
pc+=4;
stack[sp++] = value;
return true;
@ -122,28 +120,44 @@ bool step_vm() {
return true;
}
case OP_LOAD_8: {
return false;
u32 ptr = stack[--sp];
u32 value = mem[ptr];
stack[sp++] = value;
return true;
}
case OP_LOAD_16: {
return false;
u32 ptr = stack[--sp];
u16 *values = (u16*)(&mem[ptr]);
u32 value = values[0];
stack[sp++] = value;
return true;
}
case OP_LOAD_32: {
return false;
u32 ptr = stack[--sp];
u32 *values = (u32*)(&mem[ptr]);
u32 value = values[0];
stack[sp++] = value;
return true;
}
case OP_STORE_8: {
return false;
u32 ptr = stack[--sp];
u8 value = (u8)stack[--sp];
mem[ptr] = value;
return true;
}
case OP_STORE_16: {
return false;
u32 ptr = stack[--sp];
u16 value = (u16)stack[--sp];
u16 *values = (u16*)(&mem[ptr]);
values[0] = value;
return true;
}
case OP_STORE_32: {
return false;
}
case OP_SET: {
return false;
}
case OP_GET: {
return false;
u32 ptr = stack[--sp];
u32 value = stack[--sp];
u32 *values = (u32*)(&mem[ptr]);
values[0] = value;
return true;
}
case OP_MEM_ALLOC: {
u32 size = stack[--sp];
@ -153,113 +167,109 @@ bool step_vm() {
return true;
}
case OP_MEM_CPY_8: {
u32 i = 0;
u8 *ptr_src;
u8 *ptr_dest;
u32 count = stack[--sp];
u8 msrc = (u8)stack[--sp];
u32 mdest = stack[--sp];
u32 src = stack[--sp];
u32 dest = stack[--sp];
if (mdest + count >= mp) {
if (dest + count >= mp) {
status = 1;
return true;
}
for (i = 0; i < count; i++) {
mem[msrc + i] = mem[mdest + i];
}
ptr_dest = &mem[dest];
ptr_src = &mem[src];
mcpy(ptr_dest, ptr_src, count*sizeof(u8));
status = 0;
return true;
}
case OP_MEM_CPY_16: {
u32 i = 0;
u8 *ptr_src;
u8 *ptr_dest;
u32 count = stack[--sp];
u16 msrc = (u16)stack[--sp];
u32 mdest = stack[--sp];
u32 src = stack[--sp];
u32 dest = stack[--sp];
if (mdest + count >= mp) {
if (dest + count >= mp) {
status = 1;
return true;
}
for (i = 0; i < count; i++) {
u16 value = READ_U16(mdest + i);
WRITE_U16(msrc + i, value);
}
ptr_dest = &mem[dest];
ptr_src = &mem[src];
mcpy(ptr_dest, ptr_src, count*sizeof(u16));
status = 0;
return true;
}
case OP_MEM_CPY_32: {
u32 i = 0;
u8 *ptr_src;
u8 *ptr_dest;
u32 count = stack[--sp];
u32 msrc = stack[--sp];
u32 mdest = stack[--sp];
u32 src = stack[--sp];
u32 dest = stack[--sp];
if (mdest + count >= mp) {
if (dest + count >= mp) {
status = 1;
return true;
}
for (i = 0; i < count; i++) {
globals[msrc + i] = globals[mdest + i];
}
ptr_dest = &mem[dest];
ptr_src = &mem[src];
mcpy(ptr_dest, ptr_src, count*sizeof(u32));
status = 0;
return true;
}
case OP_MEM_SET_8: {
u32 i, start, end;
u8 *ptr_dest;
u8 value = (u8)stack[--sp];
u32 count = stack[--sp];
start = stack[--sp];
end = start + count;
u32 dest = stack[--sp];
if (start >= mp || end > mp) {
if (dest + count >= mp) {
status = 1;
return true;
}
for (i = start; i < end; i++) {
mem[i] = value;
}
ptr_dest = &mem[dest];
mcpy(ptr_dest, &value, count*sizeof(u8));
status = 0;
return true;
}
case OP_MEM_SET_16: {
u32 i, start, end;
u8 *ptr_dest;
u16 value = (u16)stack[--sp];
u32 count = stack[--sp];
start = stack[--sp];
end = start + count;
u32 dest = stack[--sp];
if (start >= mp || end > mp) {
if (dest + count >= mp) {
status = 1;
return true;
}
for (i = start; i < end; i += 2) {
WRITE_U16(i, value);
}
ptr_dest = &mem[dest];
mcpy(ptr_dest, &value, count*sizeof(u16));
status = 0;
return true;
}
case OP_MEM_SET_32: {
u32 i, start, end;
u8 *ptr_dest;
u32 value = stack[--sp];
u32 count = stack[--sp];
start = stack[--sp];
end = start + count;
u32 dest = stack[--sp];
if (start >= mp || end > mp) {
if (dest + count >= mp) {
status = 1;
return true;
}
for (i = start; i < end; i += 4) {
WRITE_U32(i, value);
}
ptr_dest = &mem[dest];
mcpy(ptr_dest, &value, count*sizeof(u32));
status = 0;
return true;
@ -288,9 +298,6 @@ bool step_vm() {
stack[sp++] = b;
return true;
}
case OP_ROT: {
return false;
}
case OP_DEPTH: {
u32 a = sp;
stack[sp++] = a;
@ -400,64 +407,71 @@ bool step_vm() {
return true;
}
case OP_BIT_SHIFT_LEFT: {
MATH_OP_NO_CAST(<<);
MATH_OP(u32, <<);
}
case OP_BIT_SHIFT_RIGHT: {
MATH_OP_NO_CAST(>>);
MATH_OP(u32, >>);
}
case OP_BIT_SHIFT_R_EXT: {
MATH_OP(i32, >>);
}
case OP_BIT_AND: {
MATH_OP_NO_CAST(&);
MATH_OP(u32, &);
}
case OP_BIT_OR: {
MATH_OP_NO_CAST(|);
MATH_OP(u32, |);
}
case OP_BIT_XOR: {
MATH_OP_NO_CAST(^);
MATH_OP(u32, ^);
}
case OP_EQ: {
MATH_OP_NO_CAST(==);
case OP_EQS: {
MATH_OP(i32, ==);
}
case OP_NE: {
MATH_OP_NO_CAST(!=);
case OP_NES: {
MATH_OP(i32, !=);
}
case OP_GT: {
MATH_OP_NO_CAST(>);
case OP_GTS: {
MATH_OP(i32, >);
}
case OP_LT: {
MATH_OP_NO_CAST(<);
case OP_LTS: {
MATH_OP(i32, <);
}
case OP_LE: {
MATH_OP_NO_CAST(<=);
case OP_LES: {
MATH_OP(i32, <=);
}
case OP_GE: {
MATH_OP_NO_CAST(>=);
case OP_GES: {
MATH_OP(i32, >=);
}
case OP_EQU: {
MATH_OP(u32, ==);
}
case OP_NEU: {
MATH_OP(u32, !=);
}
case OP_GTU: {
MATH_OP(u32, >);
}
case OP_LTU: {
MATH_OP(u32, <);
}
case OP_LEU: {
MATH_OP(u32, <=);
}
case OP_GEU: {
MATH_OP(u32, >=);
}
case OP_JMP: {
u32 jmp_dest = stack[--sp];
if (jmp_dest > cp) {
status = 1;
return true;
}
pc = jmp_dest;
pc = stack[--sp];
return true;
}
case OP_JMP_FLAG: {
u32 mask;
u32 jmp_dest = stack[--sp];
if (jmp_dest > cp) {
status = 1;
return true;
}
mask = -(u32)(status == 0);
pc = (jmp_dest & mask) | (pc & ~mask);
return true;
}
case OP_JMP_IF: {
case OP_JNZ: {
u32 mask, target;
i32 cond = stack[--sp];
target = stack[--sp];

191
vm/vm.h
View File

@ -4,80 +4,81 @@
#include "libc.h"
typedef enum {
OP_HALT, /* - `halt` | halt execution */
OP_CALL, /* ptr `call` | creates a new frame */
OP_RETURN, /* - `return` | returns from a frame to the parent frame */
OP_SYSCALL, /* id args mem_ptr `syscall` - | id args mem_ptr : does a system call based on id with args */
OP_LOAD_8, /* dest `load-8` u8 | push memory[obj1] onto stack as u8 */
OP_LOAD_16, /* dest `load-16` u16 | push memory[obj1] onto stack as u16 */
OP_LOAD_32, /* dest `load` u32 | push memory[obj1] onto stack as u32 */
OP_STORE_8, /* dest obj1 `store-8` - | memory[dest] = obj1 << 8 */
OP_STORE_16, /* dest obj1 `store-16`- | memory[dest] = obj1 << 16 */
OP_STORE_32, /* dest obj1 `store` - | memory[dest] = obj1 */
OP_MALLOC, /* size `malloc` ptr | allocate 'size + 4' of memory and push ptr to memory on stack */
OP_PUSH_8, /* const `push8` obj1 | push a 8 bit const onto the stack */
OP_PUSH_16, /* const `push16` obj1 | push a 16 bit const onto the stack */
OP_PUSH_32, /* const `push32` obj1 | push a 32 bit const onto the stack */
OP_POP, /* - `pop` - | removes top item from the stack */
OP_SET, /* obj1 local `set` - | set the value of the local slot */
/*OP_SET_ADDRESS, addr local `set_address` - | set the address of the value of the local slot */
OP_GET, /* local `get` obj1 | get the value of the local slot */
/*OP_GET_ADDRESS, local `get_address` obj1 | get the address of the value of the local slot */
OP_DUP, /* obj1 `dup` obj1 obj1 | duplicates the top of the stack */
OP_EXCH, /* obj2 obj1 `exch` obj1 obj2 | swaps the top two values on the stack */
OP_OVER, /* obj2 obj1 `over` obj2 | copys the 2nd to the top element and pushes to the stack */
OP_PICK, /* N `pick` objN | gets the nth element on the stack and pushes it on top */
OP_ROT, /* obj3 obj2 obj1 `rot` obj2 obj1 obj3 | takes the 3rd element and moves it to the top of the stack */
OP_DEPTH, /* - `depth` heap_count | pushes the number of elements on the stack to the stack*/
OP_MEM_ALLOC, /* size `alloc` ptr | allocate 'size + 4' of memory and push ptr to memory on stack */
OP_MEM_CPY_8, /* size src dest `memcpy_8` - | memory[src..src+size] = memory[dest..dest+size] */
OP_MEM_CPY_16, /* size src dest `memcpy_16` - | memory[src..src+size] = memory[dest..dest+size] */
OP_MEM_CPY_32, /* size src dest `memcpy_32` - | memory[src..src+size] = memory[dest..dest+size] */
OP_MEM_SET_8, /* size src dest `memset_8` - | memory[dest..dest+size] = local[src] as u8 */
OP_MEM_SET_16, /* size src dest `memset_16` - | memory[dest..dest+size] = local[src] as u16 */
OP_MEM_SET_32, /* size src dest `memset_32` - | memory[dest..dest+size] = local[src] as u32 */
OP_ADD_INT, /* obj2 obj1 `add_int` obj | obj1 + obj2 then push result on stack */
OP_SUB_INT, /* obj2 obj1 `sub_int` obj | obj1 - obj2 then push result on stack */
OP_MUL_INT, /* obj2 obj1 `mul_int` obj | obj1 * obj2 then push result on stack */
OP_DIV_INT, /* obj2 obj1 `div_int` obj | obj1 / obj2 then push result on stack */
OP_ADD_NAT, /* obj2 obj1 `add_nat` obj | obj1 + obj2 then push result on stack */
OP_SUB_NAT, /* obj2 obj1 `sub_nat` obj | obj1 - obj2 then push result on stack */
OP_MUL_NAT, /* obj2 obj1 `mul_nat` obj | obj1 * obj2 then push result on stack */
OP_DIV_NAT, /* obj2 obj1 `div_nat` obj | obj1 / obj2 then push result on stack */
OP_ADD_REAL, /* obj2 obj1 `add_real` obj | obj1 + obj2 then push result on stack */
OP_SUB_REAL, /* obj2 obj1 `sub_real` obj | obj1 - obj2 then push result on stack */
OP_MUL_REAL, /* obj2 obj1 `mul_real` obj | obj1 * obj2 then push result on stack */
OP_DIV_REAL, /* obj2 obj1 `div_real` obj | obj1 / obj2 then push result on stack */
OP_INT_TO_REAL, /* obj1 `int_to_real` obj1 as real | casts an int to a fixed number */
OP_INT_TO_NAT, /* obj1 `int_to_nat` obj1 as nat | casts an int to a unsigned int */
OP_NAT_TO_REAL, /* obj1 `nat_to_real` obj1 as real | casts a unsigned int to a fixed number */
OP_NAT_TO_INT, /* obj1 `nat_to_int` obj1 as int | casts a unsigned int to an int */
OP_REAL_TO_INT, /* obj1 `real_to_int` obj1 as int | casts a fixed number to an int */
OP_REAL_TO_NAT, /* obj1 `real_to_nat` obj1 as nat | casts a fixed number to an unsigned int */
OP_BIT_SHIFT_LEFT, /* obj2 obj1 `bit_shift_left` obj | src1] << locals[src2] */
OP_BIT_SHIFT_RIGHT, /* obj2 obj1 `bit_shift_right` obj | src1] >> locals[src2] */
OP_BIT_SHIFT_R_EXT, /* obj2 obj1 `bit_shift_r_ext` obj | src1 >> src2 then cast result as i32 */
OP_BIT_AND, /* obj2 obj1 `bit_and` obj | obj1 & obj2 */
OP_BIT_OR, /* obj2 obj1 `bit_or` obj | obj1 | obj2 */
OP_BIT_XOR, /* obj2 obj1 `bit_xor` obj | obj1 ^ obj2 */
OP_NEG, /* obj1 `neg` -obj | -obj1 */
OP_NOT, /* obj1 `bit_xor` !obj | not obj1 */
OP_JMP, /* pc `jump` | jump unconditionally */
OP_JMP_FLAG, /* pc `jump_if_flag` | jump to pc if flag > 0 */
OP_JMP_IF, /* obj1 pc `jump_if | jump to pc if obj1 != 0 */
OP_EQ, /* obj2 obj1 `eq` | obj1 == obj2*/
OP_NE, /* obj2 obj1 `ne` | obj1 != obj2*/
OP_GT, /* obj2 obj1 `gt` | obj1 > obj2*/
OP_LT, /* obj2 obj1 `lt` | obj1 < obj2*/
OP_LE, /* obj2 obj1 `le` | obj1 <= obj2*/
OP_GE, /* obj2 obj1 `ge` | obj1 >= obj2*/
OP_INT_TO_STR, /* obj1 `int_to_string` str_ptr | convert obj1 to str */
OP_NAT_TO_STR, /* obj1 `nat_to_string` str_ptr | convert obj1 to str */
OP_REAL_TO_STR, /* obj1 `real_to_string` str_ptr | convert obj1 to str */
OP_STR_TO_INT, /* str_ptr `string_to_int` obj | convert obj1 to int */
OP_STR_TO_NAT, /* str_ptr `string_to_nat` obj | convert obj1 to nat */
OP_STR_TO_REAL, /* str_ptr `string_to_real` obj | convert obj1 to real */
OP_MAX_OPCODE /* not an opcode count of instructions */
OP_HALT, /* - `halt` | halt execution */
OP_CALL, /* ptr `call` - | creates a new frame */
OP_RETURN, /* - `return` - | returns from a frame to the parent frame */
OP_SYSCALL, /* id mem_ptr `syscall` - | does a system call based on id with args */
OP_LOAD_8, /* dest `ld8` u8 | push memory[obj1] onto stack as u8 */
OP_LOAD_16, /* dest `ld16` u16 | push memory[obj1] onto stack as u16 */
OP_LOAD_32, /* dest `ld32` u32 | push memory[obj1] onto stack as u32 */
OP_STORE_8, /* dest obj1 `st8` - | memory[dest] = obj1 << 8 */
OP_STORE_16, /* dest obj1 `st16` - | memory[dest] = obj1 << 16 */
OP_STORE_32, /* dest obj1 `st32` - | memory[dest] = obj1 */
OP_MALLOC, /* size `alloc` ptr | allocate 'size + 4' of memory and push ptr to memory on stack */
OP_PUSH_8, /* const `push8` obj1 | push a 8 bit const onto the stack */
OP_PUSH_16, /* const `push16` obj1 | push a 16 bit const onto the stack */
OP_PUSH_32, /* const `push32` obj1 | push a 32 bit const onto the stack */
OP_POP, /* - `pop` - | removes top item from the stack */
OP_DUP, /* obj1 `dup` obj1 obj1 | duplicates the top of the stack */
OP_EXCH, /* obj2 obj1 `exch` obj1 obj2 | swaps the top two values on the stack */
OP_OVER, /* obj2 obj1 `over` obj2 | copys the 2nd to the top element and pushes to the stack */
OP_PICK, /* N `pick` objN | gets the nth element on the stack and pushes it on top */
OP_DEPTH, /* - `depth` stack_count | pushes the number of elements on the stack to the stack */
OP_MEM_ALLOC, /* size `alloc` ptr | allocate 'size + 4' of memory and push ptr to memory on stack */
OP_MEM_CPY_8, /* size src dest `mcpy8` - | memory[src..src+size] = memory[dest..dest+size] */
OP_MEM_CPY_16, /* size src dest `mcpy16` - | memory[src..src+size] = memory[dest..dest+size] */
OP_MEM_CPY_32, /* size src dest `mcpy32` - | memory[src..src+size] = memory[dest..dest+size] */
OP_MEM_SET_8, /* size src dest `mset8` - | memory[dest..dest+size] = local[src] as u8 */
OP_MEM_SET_16, /* size src dest `mset16` - | memory[dest..dest+size] = local[src] as u16 */
OP_MEM_SET_32, /* size src dest `mset32` - | memory[dest..dest+size] = local[src] as u32 */
OP_ADD_INT, /* obj2 obj1 `addi` obj | obj1 + obj2 then push result on stack */
OP_SUB_INT, /* obj2 obj1 `subi` obj | obj1 - obj2 then push result on stack */
OP_MUL_INT, /* obj2 obj1 `muli` obj | obj1 * obj2 then push result on stack */
OP_DIV_INT, /* obj2 obj1 `divi` obj | obj1 / obj2 then push result on stack */
OP_ADD_NAT, /* obj2 obj1 `addn` obj | obj1 + obj2 then push result on stack */
OP_SUB_NAT, /* obj2 obj1 `subn` obj | obj1 - obj2 then push result on stack */
OP_MUL_NAT, /* obj2 obj1 `muln` obj | obj1 * obj2 then push result on stack */
OP_DIV_NAT, /* obj2 obj1 `divn` obj | obj1 / obj2 then push result on stack */
OP_ADD_REAL, /* obj2 obj1 `addr` obj | obj1 + obj2 then push result on stack */
OP_SUB_REAL, /* obj2 obj1 `subr` obj | obj1 - obj2 then push result on stack */
OP_MUL_REAL, /* obj2 obj1 `mulr` obj | obj1 * obj2 then push result on stack */
OP_DIV_REAL, /* obj2 obj1 `divr` obj | obj1 / obj2 then push result on stack */
OP_INT_TO_REAL, /* obj1 `itor` real | casts an int to a fixed number */
OP_INT_TO_NAT, /* obj1 `iton` nat | casts an int to a unsigned int */
OP_NAT_TO_REAL, /* obj1 `ntor` real | casts a unsigned int to a fixed number */
OP_NAT_TO_INT, /* obj1 `ntoi` int | casts a unsigned int to an int */
OP_REAL_TO_INT, /* obj1 `rtoi` int | casts a fixed number to an int */
OP_REAL_TO_NAT, /* obj1 `rton` nat | casts a fixed number to an unsigned int */
OP_BIT_SHIFT_LEFT, /* obj2 obj1 `sll` obj | src1] << locals[src2] */
OP_BIT_SHIFT_RIGHT, /* obj2 obj1 `srl` obj | src1] >> locals[src2] */
OP_BIT_SHIFT_R_EXT, /* obj2 obj1 `sre` obj | src1 >> src2 then cast result as i32 */
OP_BIT_AND, /* obj2 obj1 `band` obj | obj1 & obj2 */
OP_BIT_OR, /* obj2 obj1 `bor` obj | obj1 | obj2 */
OP_BIT_XOR, /* obj2 obj1 `bxor` obj | obj1 ^ obj2 */
OP_NEG, /* obj1 `neg` obj | -obj1 */
OP_NOT, /* obj1 `not` obj | not obj1 */
OP_JMP, /* pc `jump` | jump unconditionally */
OP_JMP_FLAG, /* pc `jmpf` | jump to pc if flag > 0 */
OP_JNZ, /* obj1 pc `jnz` | jump to pc if obj1 != 0 */
OP_EQU, /* obj2 obj1 `equ` bool | unsigned obj1 == obj2 */
OP_NEU, /* obj2 obj1 `neu` bool | unsigned obj1 != obj2 */
OP_GTU, /* obj2 obj1 `gtu` bool | unsigned obj1 > obj2 */
OP_LTU, /* obj2 obj1 `ltu` bool | unsigned obj1 < obj2 */
OP_LEU, /* obj2 obj1 `leu` bool | unsigned obj1 <= obj2 */
OP_GEU, /* obj2 obj1 `geu` bool | unsigned obj1 >= obj2 */
OP_EQS, /* obj2 obj1 `eqs` bool | signed obj1 == obj2 */
OP_NES, /* obj2 obj1 `nes` bool | signed obj1 != obj2 */
OP_GTS, /* obj2 obj1 `gts` bool | signed obj1 > obj2 */
OP_LTS, /* obj2 obj1 `lts` bool | signed obj1 < obj2 */
OP_LES, /* obj2 obj1 `les` bool | signed obj1 <= obj2 */
OP_GES, /* obj2 obj1 `ges` bool | signed obj1 >= obj2 */
OP_INT_TO_STR, /* obj1 `itos` str_ptr | convert obj1 to str */
OP_NAT_TO_STR, /* obj1 `ntos` str_ptr | convert obj1 to str */
OP_REAL_TO_STR, /* obj1 `rtos` str_ptr | convert obj1 to str */
OP_STR_TO_INT, /* str_ptr `stoi` obj | convert obj1 to int */
OP_STR_TO_NAT, /* str_ptr `ntoi` obj | convert obj1 to nat */
OP_STR_TO_REAL, /* str_ptr `stor` obj | convert obj1 to real */
OP_MAX_OPCODE /* not an opcode count of instructions */
} Opcode;
typedef enum {
@ -92,17 +93,17 @@ struct frame_s {
u32 start_mp;
};
extern u32 pc; /* program counter */
extern u32 cp; /* code pointer */
extern u32 mp; /* memory pointer */
extern u32 fp; /* frame pointer */
extern u32 sp; /* stack pointer */
extern u8 lp; /* locals pointer */
extern u8 status; /* status flag */
extern u8 interrupt; /* device interrupt */
extern u32 *stack; /* stack */
extern u8 *code; /* code */
extern u8 *mem; /* memory */
extern u8 *code; /* code */
extern u32 cp; /* code pointer */
extern u8 *mem; /* memory */
extern u32 mp; /* memory pointer */
extern u32 *stack; /* stack */
extern u32 sp; /* stack pointer */
extern Frame *frames; /* call frames */
extern u32 fp; /* frame pointer */
extern u32 pc; /* program counter */
extern u8 status; /* status flag */
extern u8 interrupt; /* device interrupt */
#define READ_U8(addr) (mem[addr])
@ -133,20 +134,12 @@ extern u8 *mem; /* memory */
mem[addr + 3] = ((value) >> 24) & 0xFF; \
} while (0)
#define MATH_OP(type, op) \
do { \
type b = (type)stack[--sp]; \
type a = (type)stack[--sp]; \
stack[sp++] = (type)(a op b); \
return true; \
} while (0)
#define MATH_OP_NO_CAST(op) \
do { \
u32 b = stack[--sp]; \
u32 a = stack[--sp]; \
stack[sp++] = a op b; \
return true; \
#define MATH_OP(type, op) \
do { \
type b = (type)stack[--sp]; \
type a = (type)stack[--sp]; \
stack[sp++] = (type)(a op b); \
return true; \
} while (0)
extern bool init_vm();