Compare commits

...

5 Commits

20 changed files with 1376 additions and 666 deletions

View File

@ -1,6 +1,5 @@
#include "../../../vm/vm.h" #include "../../../vm/vm.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
#define CODE_SIZE 8192 #define CODE_SIZE 8192
#define MEMORY_SIZE 65536 #define MEMORY_SIZE 65536
@ -9,7 +8,6 @@ u32 lcode[CODE_SIZE] = {0};
bool init_vm() { bool init_vm() {
mem = lmem; mem = lmem;
memset(mem, 0, MEMORY_SIZE*sizeof(u8));
code = lcode; code = lcode;
lc = 0; lc = 0;
mp = 0; mp = 0;
@ -23,27 +21,47 @@ bool init_vm() {
u32 syscall(u32 id, u32 size, u32 mem_ptr) { u32 syscall(u32 id, u32 size, u32 mem_ptr) {
USED(size); USED(size);
switch(id) { switch(id) {
case SYSCALL_DBG_PRINT: { case SYSCALL_CONSOLE_WRITE: {
u32 val = READ_U32(mem_ptr); u32 size = *(u32*)&mem[mem_ptr];
printf("%d\n", val); u8 *ptr = &mem[mem_ptr + 4];
return 0; 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 return 1; // generic error
} }
void test_add_two_num() { void test_add_two_num() {
i32 main_local_count = 4; i32 main_local_count = 5;
mp += (4 * main_local_count); mp += (4 * main_local_count);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 1); code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 1);
code[cp++] = ENCODE_B(OP_PUSH, 0, 0); code[cp++] = ENCODE_B(OP_PUSH, 0, 0);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 1); code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 1);
code[cp++] = ENCODE_B(OP_PUSH, 1, 0); code[cp++] = ENCODE_B(OP_PUSH, 1, 0);
i32 add = cp + 4; i32 add = cp + 5;
code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, add); code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, add);
code[cp++] = ENCODE_A(OP_CALL, 2, 3, 3);
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_DBG_PRINT, 1, 3); code[cp++] = ENCODE_A(OP_CALL, 2, 3, 0);
code[cp++] = ENCODE_A(OP_INT_TO_STR, 4, 3, 0);
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 1, 4);
code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0); code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0);
/* add */ /* add */
@ -52,10 +70,10 @@ void test_add_two_num() {
} }
void test_fibonacci() { void test_fibonacci() {
i32 fib = 6; i32 fib = 7;
i32 base_case = 20; i32 base_case = 21;
/* function main() */ /* function main() */
i32 main_local_count = 3; i32 main_local_count = 4;
mp += (4 * main_local_count); mp += (4 * main_local_count);
/* fib(35) */ /* fib(35) */
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 35); code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 35);
@ -63,10 +81,10 @@ void test_fibonacci() {
code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, fib); code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, fib);
code[cp++] = ENCODE_A(OP_CALL, 1, 9, 2); code[cp++] = ENCODE_A(OP_CALL, 1, 9, 2);
/* print */ /* print */
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_DBG_PRINT, 1, 2); code[cp++] = ENCODE_A(OP_INT_TO_STR, 3, 2, 0);
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 1, 3);
code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0); code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0);
/* function fib (int n) int */ /* function fib (int n) int */
//code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_DBG_PRINT, 1, 0);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 8, fib); code[cp++] = ENCODE_B(OP_LOAD_IMM, 8, fib);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 2); code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 2);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, base_case); code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, base_case);
@ -84,16 +102,27 @@ void test_fibonacci() {
code[cp++] = ENCODE_B(OP_RETURN, 0, 0); code[cp++] = ENCODE_B(OP_RETURN, 0, 0);
} }
void test_hello() {
u32 hello =str_alloc("nuqneH 'u'?", 12);
u32 new_line = str_alloc("\n", 1);
fp = mp;
/* function main() */
i32 main_local_count = 3;
mp += (4 * main_local_count);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, hello);
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 0, 0);
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, new_line);
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 0, 0);
code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0);
}
i32 main() { i32 main() {
init_vm(); init_vm();
/* test_add_two_num(); */ test_hello();
test_fibonacci();
while(step_vm()) { while(step_vm()) {
// do stuff // do stuff
} }
return 0; return 0;
} }

2
build
View File

@ -69,7 +69,7 @@ case $MODE in
BUILD_FLAGS="-g -Wall -Wextra -Werror -pedantic" BUILD_FLAGS="-g -Wall -Wextra -Werror -pedantic"
;; ;;
"release") "release")
BUILD_FLAGS="-O2 -Wall -Wextra -Werror -pedantic" BUILD_FLAGS="-Ofast -Wall -Wextra -Werror -pedantic"
;; ;;
esac esac

29
test/add.ul Normal file
View File

@ -0,0 +1,29 @@
/**
* Plexes
*/
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln(add(1, 1) as 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("/dev/term/0", 0);
term.write(message);
term.write("\n");
}

30
test/fib.ul Normal file
View File

@ -0,0 +1,30 @@
/**
* Plexes
*/
plex Terminal {
nat handle;
}
/**
* Main function
*/
function main() {
pln(fib(35) as 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("/dev/term/0", 0);
term.write(message);
term.write("\n");
}

22
test/hello.ul Normal file
View File

@ -0,0 +1,22 @@
/**
* Plexes
*/
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);
term.write(message);
term.write("\n");
}

26
test/malloc.ul Normal file
View File

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

108
test/paint.ul Normal file
View File

@ -0,0 +1,108 @@
/**
* 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() {
this.write(this.buffer);
}
}
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("/dev/screen/0", 0);
Mouse mouse = open("/dev/mouse/0", 0);
outline_swatch(screen, BLACK, 1, 1);
outline_swatch(screen, WHITE, 21, 1);
screen.draw();
loop {
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("/dev/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 = "/dev/screen/0"
const str mouse_namespace = "/dev/mouse/0"
const str terminal_namespace = "/dev/term/0"
const str new_line = "\n"
const byte WHITE = 255
/**
* Devices
*/
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

@ -1,7 +0,0 @@
#include "assembler.h"
/**
* Emit bytecode to the VM from the source string.
*/
void assemble(char *source, ScopeTable *st) {
}

View File

@ -1,55 +0,0 @@
#ifndef UNDAR_IR_ASSEMBLER_H
#define UNDAR_IR_ASSEMBLER_H
#include "../../vm/libc.h"
#include "lexer.h"
typedef enum { GLOBAL, LOCAL, VAR } ScopeType;
typedef enum {
VOID,
BOOL,
I8,
I16,
I32,
U8,
U16,
U32,
F8,
F16,
F32,
STR,
PLEX,
ARRAY,
FUNCTION
} SymbolType;
typedef struct symbol_s Symbol;
typedef struct symbol_tab_s SymbolTable;
typedef struct scope_tab_s ScopeTable;
#define MAX_SYMBOL_NAME_LENGTH 64
struct symbol_s {
char name[MAX_SYMBOL_NAME_LENGTH];
u8 name_length;
SymbolType type;
ScopeType scope;
u32 ref; // vm->mp if global, vm->pc local, register if var
u32 size; // size of symbol
};
struct symbol_tab_s {
Symbol symbols[256];
u8 count;
i32 parent;
};
struct scope_tab_s {
SymbolTable *scopes;
u32 count;
u32 capacity;
i32 scope_ref;
};
void assemble(char *source, ScopeTable *st);
#endif

View File

@ -1,401 +0,0 @@
#include <string.h>
#include "../../vm/libc.h"
#include "lexer.h"
typedef struct {
const char *start;
const char *current;
i32 line;
} Lexer;
Lexer lexer;
void init_lexer(const char *source) {
lexer.start = source;
lexer.current = source;
lexer.line = 1;
}
static bool is_alpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
static bool is_digit(char c) { return c >= '0' && c <= '9'; }
static bool is_at_end() { return *lexer.current == '\0'; }
static char advance() {
lexer.current++;
return lexer.current[-1];
}
static char peek() { return *lexer.current; }
static char peek_next() {
if (is_at_end())
return '\0';
return lexer.current[1];
}
static bool match(char expected) {
if (is_at_end())
return false;
if (*lexer.current != expected)
return false;
lexer.current++;
return true;
}
static Token make_token(TokenType type) {
Token token;
token.type = type;
token.start = lexer.start;
token.length = (i32)(lexer.current - lexer.start);
token.line = lexer.line;
return token;
}
static Token error_token(const char *message) {
Token token;
token.type = TOKEN_ERROR;
token.start = message;
token.length = (i32)strlen(message);
token.line = lexer.line;
return token;
}
static void skip_whitespace() {
for (;;) {
char c = peek();
switch (c) {
case ' ':
case '\r':
case '\t':
advance();
break;
case '\n':
lexer.line++;
advance();
break;
case '/':
if (peek_next() == '/') {
// Single-line comment: skip until newline or end of file
advance();
while (peek() != '\n' && !is_at_end())
advance();
} else if (peek_next() == '*') {
// Multi-line comment: skip until '*/' or end of file
advance();
advance();
while (!is_at_end()) {
if (peek() == '\n')
lexer.line++;
if (peek() == '*' && peek_next() == '/') {
advance();
advance();
break; // Exit loop, comment ended
}
advance();
}
} else {
return; // Not a comment, let tokenization handle it
}
break;
default:
return;
}
}
}
static TokenType check_keyword(i32 start, i32 length, const char *rest,
TokenType type) {
if (lexer.current - lexer.start == start + length &&
memcmp(lexer.start + start, rest, length) == 0) {
return type;
}
return TOKEN_IDENTIFIER;
}
static TokenType identifierType() {
switch (lexer.start[0]) {
case 'a':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'n':
return check_keyword(2, 1, "d", TOKEN_OPERATOR_AND);
case 's':
return check_keyword(2, 0, "", TOKEN_KEYWORD_AS);
}
}
break;
case 'c':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'l':
return check_keyword(2, 3, "ose", TOKEN_KEYWORD_CLOSE);
case 'o':
return check_keyword(2, 3, "nst", TOKEN_KEYWORD_CONST);
}
}
break;
case 'e':
return check_keyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
case 'f':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'a':
return check_keyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
case 'o':
return check_keyword(2, 1, "r", TOKEN_KEYWORD_FOR);
case '3':
return check_keyword(1, 1, "2", TOKEN_TYPE_REAL);
}
return check_keyword(1, 7, "unction", TOKEN_KEYWORD_FN);
}
break;
case 'i':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'f':
return check_keyword(2, 0, "", TOKEN_KEYWORD_IF);
case 's':
return check_keyword(2, 0, "", TOKEN_KEYWORD_IS);
case '8':
return check_keyword(2, 0, "", TOKEN_TYPE_I8);
case '1':
return check_keyword(2, 1, "6", TOKEN_TYPE_I16);
case '3':
return check_keyword(2, 1, "2", TOKEN_TYPE_INT);
case 'n':
if (lexer.current - lexer.start > 2) {
switch (lexer.start[2]) {
case 'i':
return check_keyword(3, 2, "t", TOKEN_KEYWORD_INIT);
case 't':
return check_keyword(3, 0, "", TOKEN_TYPE_INT);
}
}
break;
}
}
break;
case 'n':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'a':
return check_keyword(2, 1, "t", TOKEN_TYPE_NAT);
case 'i':
return check_keyword(2, 1, "l", TOKEN_KEYWORD_NIL);
}
}
break;
case 'o':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'p':
return check_keyword(2, 2, "en", TOKEN_KEYWORD_OPEN);
case 'r':
return check_keyword(2, 0, "", TOKEN_OPERATOR_OR);
}
}
break;
case 'p':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'l':
return check_keyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
}
}
break;
case 'r':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'e':
if (lexer.current - lexer.start > 2) {
switch (lexer.start[2]) {
case 'f':
return check_keyword(3, 4, "resh", TOKEN_KEYWORD_REFRESH);
case 't':
return check_keyword(3, 3, "urn", TOKEN_KEYWORD_RETURN);
case 'a':
if (lexer.current - lexer.start > 3) {
switch(lexer.start[3]) {
case 'd':
return check_keyword(4, 0, "", TOKEN_KEYWORD_READ);
case 'l':
return check_keyword(4, 0, "", TOKEN_TYPE_REAL);
}
}
}
}
break;
}
}
break;
case 's':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 't':
return check_keyword(2, 1, "r", TOKEN_TYPE_STR);
}
}
break;
case 't':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'h':
return check_keyword(2, 2, "is", TOKEN_KEYWORD_THIS);
case 'r':
return check_keyword(2, 2, "ue", TOKEN_KEYWORD_TRUE);
}
}
break;
case 'u':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 's':
return check_keyword(2, 1, "e", TOKEN_KEYWORD_USE);
case '8':
return check_keyword(2, 0, "", TOKEN_TYPE_U8);
case '1':
return check_keyword(2, 1, "6", TOKEN_TYPE_U16);
case '3':
return check_keyword(2, 1, "2", TOKEN_TYPE_NAT);
}
}
break;
case 'w':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'h':
return check_keyword(2, 3, "ile", TOKEN_KEYWORD_WHILE);
case 'r':
return check_keyword(2, 3, "ite", TOKEN_KEYWORD_WRITE);
}
}
break;
case 'b':
if (lexer.current - lexer.start > 1) {
switch (lexer.start[1]) {
case 'y':
return check_keyword(2, 2, "te", TOKEN_TYPE_U8);
case 'o':
return check_keyword(2, 2, "ol", TOKEN_TYPE_U8);
}
}
break;
case 'g':
return check_keyword(1, 5, "lobal", TOKEN_KEYWORD_GLOBAL);
case 'l':
return check_keyword(1, 3, "oop", TOKEN_KEYWORD_LOOP);
case 'd':
return check_keyword(1, 1, "o", TOKEN_KEYWORD_DO);
case 'v':
return check_keyword(1, 3, "oid", TOKEN_TYPE_VOID);
}
return TOKEN_IDENTIFIER;
}
static Token identifier() {
while (is_alpha(peek()) || is_digit(peek()))
advance();
return make_token(identifierType());
}
static Token number() {
while (is_digit(peek()))
advance();
/* Look for a fractional part. */
if (peek() == '.' && is_digit(peek_next())) {
/* Consume the ".". */
advance();
while (is_digit(peek()))
advance();
return make_token(TOKEN_LITERAL_REAL);
}
return make_token(TOKEN_LITERAL_INT);
}
static Token string() {
while (peek() != '"' && !is_at_end()) {
if (peek() == '\n')
lexer.line++;
advance();
}
if (is_at_end())
return error_token("Unterminated string.");
/* The closing quote. */
advance();
return make_token(TOKEN_LITERAL_STR);
}
Token next_token() {
skip_whitespace();
lexer.start = lexer.current;
if (is_at_end())
return make_token(TOKEN_EOF);
char c = advance();
if (is_alpha(c))
return identifier();
char next = peek();
if ((c == '-' && is_digit(next)) || is_digit(c))
return number();
switch (c) {
case '(':
return make_token(TOKEN_LPAREN);
case ')':
return make_token(TOKEN_RPAREN);
case '{':
return make_token(TOKEN_LBRACE);
case '}':
return make_token(TOKEN_RBRACE);
case '[':
return make_token(TOKEN_LBRACKET);
case ']':
return make_token(TOKEN_RBRACKET);
case ';':
return make_token(TOKEN_SEMICOLON);
case ',':
return make_token(TOKEN_COMMA);
case '.':
return make_token(TOKEN_DOT);
case '-':
return make_token(match('>') ? TOKEN_ARROW_RIGHT : TOKEN_MINUS);
case '+':
return make_token(TOKEN_PLUS);
case '/':
return make_token(TOKEN_SLASH);
case '&':
return make_token(match('&') ? TOKEN_AND_AND : TOKEN_AND);
case '#':
return make_token(TOKEN_MESH);
case '$':
return make_token(TOKEN_BIG_MONEY);
case '*':
return make_token(TOKEN_STAR);
case '!':
return make_token(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
case '=':
return make_token(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
case '<':
return make_token(match('=') ? TOKEN_LTE : TOKEN_LT);
case '>':
return make_token(match('=') ? TOKEN_GTE : TOKEN_GT);
case '"':
return string();
}
return error_token("Unexpected character.");
}

152
tools/compiler.c Normal file
View File

@ -0,0 +1,152 @@
#include "parser.h"
#include "compiler.h"
#include <stdio.h>
#include <stdlib.h>
Symbol *symbol_table_lookup(ScopeTable *table, const char *name, u32 length,
i32 scope_ref) {
SymbolTable st = table->scopes[scope_ref];
for (u32 i = 0; i < st.count; i++) {
if (st.symbols[i].name_length == length) {
if (sleq(st.symbols[i].name, name, length)) {
return &table->scopes[scope_ref].symbols[i];
}
}
}
if (st.parent < 0)
return nil;
return symbol_table_lookup(table, name, length, st.parent);
}
u8 symbol_table_add(ScopeTable *table, Symbol s) {
Symbol *sym =
symbol_table_lookup(table, s.name, s.name_length, table->scope_ref);
if (sym != nil) {
fprintf(stderr,
"Error: Symbol '%.*s' already defined, in this scope"
" please pick a different variable name or create a new scope.\n",
s.name_length, s.name);
exit(1);
}
if (table->scopes[table->scope_ref].count + 1 > 255) {
fprintf(stderr, "Error: Only 255 symbols are allowed per scope"
" first off: impressive; secondly:"
" just create a new scope and keep going.\n");
exit(1);
}
if (!table_realloc(table)) {
fprintf(stderr,
"Error: Symbol table is out of memory! This is likely because you "
" built the assembler in static mode, increase the static size."
" if you built using malloc, that means your computer is out of"
" memory. Close a few tabs in your web browser and try again."
" Count was %d, while capacity was %d\n",
table->count, table->capacity);
exit(1);
}
table->scopes[table->scope_ref].symbols[table->scopes[table->scope_ref].count] = s;
u8 index = table->scopes[table->scope_ref].count;
table->scopes[table->scope_ref].count++;
return index;
}
u32 get_ref(ScopeTable *st, const char *name, u32 length) {
Symbol *sym = symbol_table_lookup(st, name, length, st->scope_ref);
if (!sym) {
fprintf(stderr, "Error: Assembler has no idea what Symbol '%.*s' means.\n",
length, name);
exit(1);
return 0;
}
return sym->ref;
}
u32 get_ptr(Token token, ScopeTable *st) {
if (token.type == TOKEN_IDENTIFIER) {
return get_ref(st, token.start, token.length);
}
if (token.type == TOKEN_LITERAL_INT) {
return atoi(token.start);
}
if (token.type == TOKEN_LITERAL_NAT) {
char *endptr;
u32 out = (u32)strtoul(token.start, &endptr, 10);
if (endptr == token.start || *endptr != '\0') {
fprintf(stderr, "Invalid decimal literal at line %d: %.*s\n", token.line,
token.length, token.start);
exit(1);
}
return out;
}
fprintf(stderr, "Error: Not a pointer or symbol at line %d: %.*s\n",
token.line, token.length, token.start);
exit(1);
}
u32 get_reg(Token token, ScopeTable *st) {
if (token.type == TOKEN_IDENTIFIER) {
return get_ref(st, token.start, token.length);
}
if (token.type == TOKEN_BIG_MONEY) {
token = next_token();
return atoi(token.start);
}
fprintf(stderr, "Error: Not a register or symbol at line %d: %.*s\n",
token.line, token.length, token.start);
exit(1);
}
Token next_id_or_reg() {
Token token = next_token();
if (token.type == TOKEN_IDENTIFIER) {
return token;
}
if (token.type == TOKEN_BIG_MONEY) {
token = next_token();
return token;
}
printf("Not an ID or register at line %d: %.*s\n", token.line, token.length,
token.start);
exit(1);
return token;
}
Token next_id_or_ptr() {
Token token = next_token();
if (token.type != TOKEN_IDENTIFIER && token.type != TOKEN_LITERAL_NAT &&
token.type != TOKEN_LITERAL_INT && token.type != TOKEN_LITERAL_REAL) {
printf("Not an ID or register at line %d: %.*s\n", token.line, token.length,
token.start);
exit(1);
}
return token;
}
Token next_token_is(TokenType type) {
Token token = next_token();
if (token.type != type) {
printf("ERROR at line %d: %.*s\n", token.line, token.length, token.start);
exit(1);
}
return token;
}
/**
* Compile.
*/
bool compile(ScopeTable *st, char *source) {
return false;
}

88
tools/compiler.h Normal file
View File

@ -0,0 +1,88 @@
#ifndef UNDAR_COMPILER_H
#define UNDAR_COMPILER_H
#include "../vm/libc.h"
#include "../vm/vm.h"
typedef enum { GLOBAL, LOCAL, VAR } ScopeType;
typedef enum {
VOID,
BOOL,
I8,
I16,
I32,
U8,
U16,
U32,
F8,
F16,
F32,
STR,
PLEX,
ARRAY,
FUNCTION
} SymbolType;
typedef struct symbol_s Symbol;
typedef struct symbol_tab_s SymbolTable;
typedef struct value_type_s ValueType;
typedef struct plex_fields_tab_s PlexFieldsTable;
typedef struct plex_def_s PlexDef;
typedef struct plex_tab_s PlexTable;
typedef struct scope_s Scope;
typedef struct scope_tab_s ScopeTable;
struct value_type_s {
SymbolType type;
u32 name;
u32 size;
u32 table_ref; // if it is a heap object
};
struct plex_def_s {
u32 name;
u32 size;
u32 field_ref_start;
u32 field_count;
};
struct plex_fields_tab_s {
u32 *plex_refs;
ValueType *fields;
u32 count;
u32 capacity;
};
struct plex_tab_s {
PlexDef *symbols;
u32 count;
u32 capacity;
};
#define MAX_SYMBOL_NAME_LENGTH 64
struct symbol_s {
char name[MAX_SYMBOL_NAME_LENGTH];
u8 name_length;
SymbolType type;
ScopeType scope;
u32 ref; // vm->mp if global, vm->pc local, register if var
u32 size; // size of symbol
};
struct symbol_tab_s {
Symbol symbols[256];
u8 count;
i32 parent;
};
struct scope_tab_s {
SymbolTable *scopes;
u32 count;
u32 capacity;
i32 scope_ref;
};
bool compile(ScopeTable *st, char *source);
extern bool table_realloc(ScopeTable *table);/* implement this in arch/ not here */
#endif

507
tools/parser.c Normal file
View File

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

View File

@ -1,6 +1,8 @@
#ifndef UNDAR_LEXER_H #ifndef UNDAR_LEXER_H
#define UNDAR_LEXER_H #define UNDAR_LEXER_H
#include "../vm/libc.h"
typedef enum { typedef enum {
TOKEN_EOF, TOKEN_EOF,
TOKEN_IDENTIFIER, TOKEN_IDENTIFIER,
@ -82,7 +84,8 @@ typedef struct {
int line; int line;
} Token; } Token;
void init_lexer(const char *source); void initLexer(const char *source);
Token next_token(); Token next_token();
const char* token_type_to_string(TokenType type);
#endif #endif

View File

@ -1,10 +1,14 @@
#include "libc.h" #include "libc.h"
void mcpy(u8 *to, const u8 *from, u32 length) { void mcpy(void *to, void *from, u32 length) {
u32 i; u8 *src, *dest;
if (to == nil || from == nil) return; if (to == nil || from == nil) return;
for (i = 0; i < length; i++) {
to[i] = from[i]; src = (u8 *)from;
dest = (u8 *)to;
while (length-- > 0) {
*(dest++) = *(src++);
} }
return; return;
} }

View File

@ -26,7 +26,7 @@ typedef float f32;
#define FLOAT_TO_REAL(v) (((i32)(v)) * 65536.0f) #define FLOAT_TO_REAL(v) (((i32)(v)) * 65536.0f)
#define REAL_TO_FLOAT(v) (((f32)(v)) / 65536.0f) #define REAL_TO_FLOAT(v) (((f32)(v)) / 65536.0f)
void mcpy(u8 *dest, const u8 *src, u32 n); void mcpy(void *dest, void *src, u32 n);
i32 scpy(char* to, const char *from, u32 length); i32 scpy(char* to, const char *from, u32 length);
bool seq(const char *s1, const char *s2); bool seq(const char *s1, const char *s2);
bool sleq(const char *s1, const char *s2, u32 length); bool sleq(const char *s1, const char *s2, u32 length);

397
vm/vm.c
View File

@ -11,8 +11,23 @@ u8 interrupt; /* device interrupt */
u32 *code; /* code */ u32 *code; /* code */
u8 *mem; /* memory */ u8 *mem; /* memory */
bool step_vm() { #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) {
u32 str_addr = mp;
u8 *ptr = &mem[mp];
mcpy(ptr, &length, sizeof(u32));
ptr += 4;
mcpy(ptr, str, length);
ptr[length] = '\0';
mp += 4 + length;
return str_addr;
}
bool step_vm() {
u32 instruction = code[pc++]; u32 instruction = code[pc++];
u8 opcode = DECODE_OP(instruction); u8 opcode = DECODE_OP(instruction);
u32 *locals = (u32*)(&mem[fp]); u32 *locals = (u32*)(&mem[fp]);
@ -28,37 +43,37 @@ bool step_vm() {
/* function to jump to */ /* function to jump to */
u32 fn_ptr = locals[dest]; u32 fn_ptr = locals[dest];
/* get mp in 'global indexing mode' */ /* get mp in 'global indexing mode' */
u32 gmp = mp / 4; u32 *header = &globals[mp / 4];
/* reset child locals counter */ /* reset child locals counter */
lc = 0; lc = 0;
/* push parents frame value to reset the heap to */ /* push parents frame value to reset the heap to */
globals[gmp] = fp; (*header++) = fp;
/* push return address to child frame */ /* push return address to child frame */
globals[gmp + 1] = pc; (*header++) = pc;
/* push local address to return the value to */ /* push local address to return the value to */
globals[gmp + 2] = fp + (src2 * 4); (*header++) = fp + (src2 * 4);
/* increase the mp to new size */ /* increase the mp to new size */
mp += FRAME_HEADER_SIZE; mp += FRAME_HEADER_SIZE;
/* now set the frame pointer, where the locals start */ /* now set the frame pointer, where the locals start */
fp = mp; fp = mp;
/* move mp forward by count many locals */ /* move mp forward by count many locals */
mp += (4 * src1); mp += (src1 * 4);
/* jump to dest_ptr */ /* jump to dest_ptr */
pc = fn_ptr; pc = fn_ptr;
return true; return true;
} }
case OP_RETURN: { case OP_RETURN: {
DECODE_B(instruction) DECODE_B(instruction)
u32 i, size = 0; u32 size = 0;
u32 return_value = locals[dest]; u32 return_value = locals[dest];
bool is_ptr = (((u32)(1)) << 15) & imm; bool is_ptr = (((u32)(1)) << 15) & imm;
bool replaces_value = (((u32)(1)) << 14) & imm; bool replaces_value = (((u32)(1)) << 14) & imm;
/* reset mp to saved mp, use header size to get "real" start of frame */ /* reset mp to saved mp, use header size to get "real" start of frame */
u32 frame_start = (fp / 4) - 3; u32 *frame_start = &globals[(fp / 4) - 3];
u32 parent_fp = globals[frame_start]; u32 parent_fp = *frame_start++;
u32 return_address = globals[frame_start + 1]; u32 return_address = *frame_start++;
u32 parent_local_return_address = globals[frame_start + 2]; u32 parent_local_return_address = *frame_start++;
USED(replaces_value); USED(replaces_value);
/* reset memory to parents end of memory */ /* reset memory to parents end of memory */
@ -68,18 +83,15 @@ bool step_vm() {
if (is_ptr) { if (is_ptr) {
/* copy value to end of mp if it is a pointer */ /* copy value to end of mp if it is a pointer */
WRITE_U32(parent_local_return_address, mp); globals[parent_local_return_address/4] = mp;
size = READ_U32(return_value); size = globals[return_value/4];
WRITE_U32(mp, size); globals[mp/4] = size;
mp += 4; mp += 4;
for (i = 0; i < size; i++) { mcpy(&mem[mp], &mem[return_value], size);
u8 value = READ_U8(return_value + i); mp += size;
WRITE_U8(mp, value);
mp++;
}
} else { } else {
/* otherwise just write the return value to its location */ /* otherwise just write the return value to its location */
WRITE_U32(parent_local_return_address, return_value); globals[(parent_local_return_address / 4)] = return_value;
} }
/* jump to parent frame */ /* jump to parent frame */
@ -90,7 +102,7 @@ bool step_vm() {
DECODE_A(instruction) DECODE_A(instruction)
u32 id = dest; /* syscall id */ u32 id = dest; /* syscall id */
u32 size = src1; /* size of heap at that pointer */ u32 size = src1; /* size of heap at that pointer */
u32 rd = fp + (src2 * 4); /* the pointer */ u32 rd = locals[src2]; /* the pointer */
status = syscall(id, size, rd); status = syscall(id, size, rd);
return true; return true;
} }
@ -102,7 +114,8 @@ bool step_vm() {
return true; return true;
} }
case OP_POP: { case OP_POP: {
DECODE_C(instruction) DECODE_B(instruction)
USED(dest);
USED(imm); USED(imm);
mp -= 4; mp -= 4;
lc--; lc--;
@ -120,57 +133,85 @@ bool step_vm() {
return true; return true;
} }
case OP_LOAD_IND_8: { case OP_LOAD_IND_8: {
DECODE_A(instruction)
USED(src2);
locals[dest] = READ_U8(locals[src1]);
return true;
} }
case OP_LOAD_IND_16: { case OP_LOAD_IND_16: {
DECODE_A(instruction)
USED(src2);
locals[dest] = READ_U16(locals[src1]);
return true;
} }
case OP_LOAD_IND_32: { case OP_LOAD_IND_32: {
} DECODE_A(instruction)
case OP_LOAD_ABS_8: { USED(src2);
} locals[dest] = READ_U32(locals[src1]);
case OP_LOAD_ABS_16: { return true;
}
case OP_LOAD_ABS_32: {
} }
case OP_LOAD_OFF_8: { case OP_LOAD_OFF_8: {
DECODE_A(instruction)
locals[dest] = READ_U8((locals[src1] + locals[src2]));
return true;
} }
case OP_LOAD_OFF_16: { case OP_LOAD_OFF_16: {
DECODE_A(instruction)
locals[dest] = READ_U16((locals[src1] + locals[src2]));
return true;
} }
case OP_LOAD_OFF_32: { case OP_LOAD_OFF_32: {
} DECODE_A(instruction)
case OP_STORE_ABS_8: { locals[dest] = READ_U32((locals[src1] + locals[src2]));
} return true;
case OP_STORE_ABS_16: {
}
case OP_STORE_ABS_32: {
} }
case OP_STORE_IND_8: { case OP_STORE_IND_8: {
DECODE_A(instruction)
USED(src2);
WRITE_U8(locals[dest], locals[src1]);
return true;
} }
case OP_STORE_IND_16: { case OP_STORE_IND_16: {
DECODE_A(instruction)
USED(src2);
WRITE_U16(locals[dest], locals[src1]);
return true;
} }
case OP_STORE_IND_32: { case OP_STORE_IND_32: {
DECODE_A(instruction)
USED(src2);
WRITE_U32(locals[dest], locals[src1]);
return true;
} }
case OP_STORE_OFF_8: { case OP_STORE_OFF_8: {
DECODE_A(instruction)
WRITE_U8((locals[dest] + locals[src2]), locals[src1]);
return true;
} }
case OP_STORE_OFF_16: { case OP_STORE_OFF_16: {
DECODE_A(instruction)
WRITE_U16((locals[dest] + locals[src2]), locals[src1]);
return true;
} }
case OP_STORE_OFF_32: { case OP_STORE_OFF_32: {
DECODE_A(instruction)
WRITE_U32((locals[dest] + locals[src2]), locals[src1]);
return true;
} }
case OP_MEM_ALLOC: { case OP_MEM_ALLOC: {
DECODE_A(instruction) DECODE_A(instruction)
u32 size, ldest; u32 size = locals[src1];
u32 rd = fp + (dest * 4); locals[dest] = mp;
u32 r1 = fp + (src1 * 4);
USED(src2);
ldest = READ_U32(rd);
WRITE_U32(ldest, mp);
size = READ_U32(r1);
WRITE_U32(mp, size); WRITE_U32(mp, size);
mp += (4 * (size + 4)); USED(src2);
mp += (size + 4);
return true; return true;
} }
case OP_MEM_CPY_8: { case OP_MEM_CPY_8: {
DECODE_A(instruction) DECODE_A(instruction)
u32 i = 0;
u8 *ptr_src;
u8 *ptr_dest;
u32 mdest = locals[dest]; u32 mdest = locals[dest];
u32 msrc = locals[src1]; u32 msrc = locals[src1];
u32 count = locals[src2]; u32 count = locals[src2];
@ -180,36 +221,18 @@ bool step_vm() {
return true; return true;
} }
for (i = 0; i < count; i++) { ptr_dest = &mem[mdest];
mem[msrc + i] = mem[mdest + i]; ptr_src = &mem[msrc];
} mcpy(ptr_dest, ptr_src, count*sizeof(u8));
status = 0; status = 0;
return true; return true;
} }
case OP_MEM_CPY_16: { case OP_MEM_CPY_16: {
DECODE_A(instruction) DECODE_A(instruction)
u32 i = 0;
u32 mdest = locals[dest];
u32 msrc = locals[src1];
u32 count = locals[src2] * 2;
if (mdest + count >= mp) { u8 *ptr_src;
status = 1; u8 *ptr_dest;
return true;
}
for (i = 0; i < count; i++) {
u16 value = READ_U16(mdest + i);
WRITE_U16(msrc + i, value);
}
status = 0;
return true;
}
case OP_MEM_CPY_32: {
DECODE_A(instruction)
u32 i = 0;
u32 mdest = locals[dest]; u32 mdest = locals[dest];
u32 msrc = locals[src1]; u32 msrc = locals[src1];
u32 count = locals[src2]; u32 count = locals[src2];
@ -219,113 +242,95 @@ bool step_vm() {
return true; return true;
} }
for (i = 0; i < count; i++) { ptr_dest = &mem[mdest];
globals[msrc + i] = globals[mdest + i]; ptr_src = &mem[msrc];
mcpy(ptr_dest, ptr_src, count*sizeof(u16));
status = 0;
return true;
}
case OP_MEM_CPY_32: {
DECODE_A(instruction)
u8 *ptr_src;
u8 *ptr_dest;
u32 mdest = locals[dest];
u32 msrc = locals[src1];
u32 count = locals[src2];
if (mdest + count >= mp) {
status = 1;
return true;
} }
ptr_dest = &mem[mdest];
ptr_src = &mem[msrc];
mcpy(ptr_dest, ptr_src, count*sizeof(u32));
status = 0; status = 0;
return true; return true;
} }
case OP_MEM_SET_8: { case OP_MEM_SET_8: {
DECODE_A(instruction) DECODE_A(instruction)
u32 i, start, end;
u32 rd = fp + (dest * 4); u8 *ptr_dest;
u32 r1 = fp + (src1 * 4); u8 value = (u8)locals[src1];
u32 r2 = fp + (src2 * 4); u32 count = locals[src2];
u32 mdest = locals[dest];
u8 value = (u8)READ_U32(r1); if (mdest + count >= mp) {
u32 count = READ_U32(r2);
if (r2 == 0) {
status = 1; status = 1;
return true; return true;
} }
start = READ_U32(rd); ptr_dest = &mem[mdest];
end = start + count; mcpy(ptr_dest, &value, count*sizeof(u8));
if (start >= mp || r2 > mp || end > mp) {
status = 1;
return true;
}
for (i = start; i < end; i++) {
mem[i] = value;
}
status = 0; status = 0;
return true; return true;
} }
case OP_MEM_SET_16: { case OP_MEM_SET_16: {
DECODE_A(instruction) DECODE_A(instruction)
u32 i, start, end;
u32 rd = fp + (dest * 4); u8 *ptr_dest;
u32 r1 = fp + (src1 * 4); u16 value = (u16)locals[src1];
u32 r2 = fp + (src2 * 4); u32 count = locals[src2];
u32 mdest = locals[dest];
u16 value = (u16)READ_U32(r1); if (mdest + count >= mp) {
u32 count = READ_U32(r2);
if (r2 == 0) {
status = 1; status = 1;
return true; return true;
} }
start = READ_U32(rd); ptr_dest = &mem[mdest];
end = start + count; mcpy(ptr_dest, &value, count*sizeof(u16));
if (start >= mp || r2 > mp || end > mp) {
status = 1;
return true;
}
for (i = start; i < end; i += 2) {
WRITE_U16(i, value);
}
status = 0; status = 0;
return true; return true;
} }
case OP_MEM_SET_32: { case OP_MEM_SET_32: {
DECODE_A(instruction) DECODE_A(instruction)
u32 i, start, end;
u32 rd = fp + (dest * 4); u8 *ptr_dest;
u32 r1 = fp + (src1 * 4); u32 value = locals[src1];
u32 r2 = fp + (src2 * 4); u32 count = locals[src2];
u32 mdest = locals[dest];
u32 value = READ_U32(r1); if (mdest + count >= mp) {
u32 count = READ_U32(r2);
if (r2 == 0) {
status = 1; status = 1;
return true; return true;
} }
start = READ_U32(rd); ptr_dest = &mem[mdest];
end = start + count; mcpy(ptr_dest, &value, count*sizeof(u32));
if (start >= mp || r2 > mp || end > mp) {
status = 1;
return true;
}
for (i = start; i < end; i += 4) {
WRITE_U32(i, value);
}
status = 0; status = 0;
return true; return true;
} }
case OP_MOV: { case OP_MOV: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4);
u32 r1 = fp + (src1 * 4);
u32 value = READ_U32(r1);
USED(src2); USED(src2);
WRITE_U32(rd, value); locals[dest] = locals[src1];
return true; return true;
} }
case OP_ADD_INT: { case OP_ADD_INT: {
@ -393,56 +398,44 @@ bool step_vm() {
} }
case OP_INT_TO_REAL: { case OP_INT_TO_REAL: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); i32 result = (i32)locals[src1] << 16;
u32 r1 = fp + (src1 * 4);
i32 result = (i32)READ_U32(r1) << 16;
USED(src2); USED(src2);
WRITE_U32(rd, result); locals[dest] = result;
return true; return true;
} }
case OP_INT_TO_NAT: { case OP_INT_TO_NAT: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); u32 result = (u32)locals[src1];
u32 r1 = fp + (src1 * 4);
u32 result = (u32)READ_U32(r1);
USED(src2); USED(src2);
WRITE_U32(rd, result); locals[dest] = result;
return true; return true;
} }
case OP_NAT_TO_REAL: { case OP_NAT_TO_REAL: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); i32 result = (i32)locals[src1] << 16;
u32 r1 = fp + (src1 * 4);
i32 result = ((i32)READ_U32(r1) << 16);
USED(src2); USED(src2);
WRITE_U32(rd, result); locals[dest] = result;
return true; return true;
} }
case OP_NAT_TO_INT: { case OP_NAT_TO_INT: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); i32 result = (i32)locals[src1];
u32 r1 = fp + (src1 * 4);
i32 result = ((i32)READ_U32(r1));
USED(src2); USED(src2);
WRITE_U32(rd, result); locals[dest] = result;
return true; return true;
} }
case OP_REAL_TO_INT: { case OP_REAL_TO_INT: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); i32 result = (i32)locals[src1] >> 16;
u32 r1 = fp + (src1 * 4);
i32 result = ((i32)READ_U32(r1) >> 16);
USED(src2); USED(src2);
WRITE_U32(rd, result); locals[dest] = result;
return true; return true;
} }
case OP_REAL_TO_NAT: { case OP_REAL_TO_NAT: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); u32 result = (u32)locals[src1] >> 16;
u32 r1 = fp + (src1 * 4);
u32 result = ((u32)READ_U32(r1) >> 16);
USED(src2); USED(src2);
WRITE_U32(rd, result); locals[dest] = result;
return true; return true;
} }
case OP_BIT_SHIFT_LEFT: { case OP_BIT_SHIFT_LEFT: {
@ -470,8 +463,7 @@ bool step_vm() {
} }
case OP_JMP_ABS: { case OP_JMP_ABS: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); u32 jmp_dest = locals[dest];
u32 jmp_dest = READ_U32(rd);
if (jmp_dest > cp) { if (jmp_dest > cp) {
status = 1; status = 1;
return true; return true;
@ -484,10 +476,7 @@ bool step_vm() {
} }
case OP_JMP_OFF: { case OP_JMP_OFF: {
DECODE_A(instruction) DECODE_A(instruction)
u32 rd = fp + (dest * 4); u32 jmp_dest = locals[dest] + locals[src1];
u32 r1 = fp + (src1 * 4);
u32 jmp_dest = READ_U32(rd) + READ_U32(r1);
if (jmp_dest > cp) { if (jmp_dest > cp) {
status = 1; status = 1;
return true; return true;
@ -566,6 +555,96 @@ bool step_vm() {
case OP_JLE_REAL: { case OP_JLE_REAL: {
COMPARE_AND_JUMP(i32, <=); COMPARE_AND_JUMP(i32, <=);
} }
case OP_INT_TO_STR: {
DECODE_A(instruction)
u32 i = MAX_LEN_INT32;
i32 v = (i32)locals[src1];
char buffer[MAX_LEN_INT32];
i32 n = v;
bool neg = n < 0;
USED(src2);
if (neg)
n = -n;
do {
buffer[--i] = radix_set[n % 10];
n /= 10;
} while (n > 0);
if (neg)
buffer[--i] = '-';
/* Ensure at least one digit is written for 0 */
if (v == 0)
buffer[--i] = '0';
/* Copy from buffer[i] to buffer + MAX_LEN_INT32 */
locals[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
return pc;
}
case OP_NAT_TO_STR: {
DECODE_A(instruction)
u32 v = (i32)locals[src1];
char buffer[MAX_LEN_INT32];
u32 n = v;
u32 i = MAX_LEN_INT32;
USED(src2);
do {
buffer[--i] = radix_set[n % 10];
n /= 10;
} while (n > 0);
/* Ensure at least one digit is written for 0 */
if (v == 0)
buffer[--i] = '0';
/* Copy from buffer[i] to buffer + MAX_LEN_INT32 */
locals[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
return pc;
}
case OP_REAL_TO_STR: {
DECODE_A(instruction)
u32 i = 0, j = 0;
i32 q = (i32)locals[src1];
char buffer[MAX_LEN_INT32];
u32 int_part, frac_part;
if (q < 0) {
buffer[i++] = '-';
q = -q;
}
int_part = q >> 16;
frac_part = q & 0xFFFF;
USED(src2);
if (int_part == 0) {
buffer[i++] = radix_set[0];
} else {
char tmp[16];
i32 tmp_i = 0;
while (int_part > 0) {
tmp[tmp_i++] = radix_set[int_part % 10];
int_part /= 10;
}
while (tmp_i > 0) {
buffer[i++] = tmp[--tmp_i];
}
}
buffer[i++] = '.';
for (j = 0; j < 6; j++) {
frac_part *= 10;
buffer[i++] = radix_set[frac_part >> 16];
frac_part &= 0xFFFF;
}
locals[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
return pc;
}
} }
/* something went very wrong */ /* something went very wrong */

33
vm/vm.h
View File

@ -44,21 +44,15 @@ typedef enum {
OP_LOAD_IND_8, /* load_indirect_8 : A : locals[dest] = memory[locals[src1]] as u8 */ OP_LOAD_IND_8, /* load_indirect_8 : A : locals[dest] = memory[locals[src1]] as u8 */
OP_LOAD_IND_16, /* load_indirect_16 : A : locals[dest] = memory[locals[src1]] as u16 */ OP_LOAD_IND_16, /* load_indirect_16 : A : locals[dest] = memory[locals[src1]] as u16 */
OP_LOAD_IND_32, /* load_indirect_32 : A : locals[dest] = memory[locals[src1]] as u32 */ OP_LOAD_IND_32, /* load_indirect_32 : A : locals[dest] = memory[locals[src1]] as u32 */
OP_LOAD_ABS_8, /* load_absolute_8 : A : locals[dest] = memory[src1] as u8 */ OP_LOAD_OFF_8, /* load_offset_8 : A : locals[dest] = memory[locals[src1] + locals[src2]] as u8 */
OP_LOAD_ABS_16, /* load_absolute_16 : A : locals[dest] = memory[src1] as u16 */ OP_LOAD_OFF_16, /* load_offset_16 : A : locals[dest] = memory[locals[src1] + locals[src2]] as u16 */
OP_LOAD_ABS_32, /* load_absolute_32 : A : locals[dest] = memory[src1] as u32 */ OP_LOAD_OFF_32, /* load_offset_32 : A : locals[dest] = memory[locals[src1] + locals[src2]] as u32 */
OP_LOAD_OFF_8, /* load_offset_8 : A : locals[dest] = memory[locals[src1] + src2] as u8 */ OP_STORE_IND_8, /* store_indirect_8 : A : memory[locals[dest]] = locals[src1] && 0xFF */
OP_LOAD_OFF_16, /* load_offset_16 : A : locals[dest] = memory[locals[src1] + src2] as u16 */ OP_STORE_IND_16, /* store_indirect_16 : A : memory[locals[dest]] = locals[src1] && 0xFFFF*/
OP_LOAD_OFF_32, /* load_offset_32 : A : locals[dest] = memory[locals[src1] + src2] as u32 */ OP_STORE_IND_32, /* store_indirect_32 : A : memory[locals[dest]] = locals[src1] */
OP_STORE_ABS_8, /* store_absolute_8 : A : memory[dest] = src1 && 0xFF */ OP_STORE_OFF_8, /* store_offset_8 : A : memory[locals[dest] + locals[src2]] = locals[src1] && 0xFF */
OP_STORE_ABS_16, /* store_absolute_16 : A : memory[dest] = src1 && 0xFFFF */ OP_STORE_OFF_16, /* store_offset_16 : A : memory[locals[dest] + locals[src2]] = locals[src1] && 0xFFFF */
OP_STORE_ABS_32, /* store_absolute_32 : A : memory[dest] = src1 */ OP_STORE_OFF_32, /* store_offset_32 : A : memory[locals[dest] + locals[src2]] = locals[src1] */
OP_STORE_IND_8, /* store_indirect_8 : A : memory[dest] = locals[src1] && 0xFF */
OP_STORE_IND_16, /* store_indirect_16 : A : memory[dest] = locals[src1] && 0xFFFF*/
OP_STORE_IND_32, /* store_indirect_32 : A : memory[dest] = locals[src1] */
OP_STORE_OFF_8, /* store_offset_8 : A : memory[locals[dest] + src2] = locals[src1] && 0xFF */
OP_STORE_OFF_16, /* store_offset_16 : A : memory[locals[dest] + src2] = locals[src1] && 0xFFFF */
OP_STORE_OFF_32, /* store_offset_32 : A : memory[locals[dest] + src2] = locals[src1] */
OP_MEM_ALLOC, /* alloc : A : memory[dest] = [locals[src1] as size + 4] */ OP_MEM_ALLOC, /* alloc : A : memory[dest] = [locals[src1] as size + 4] */
OP_MEM_CPY_8, /* memcpy_8 : A : memory[src1..src1+src2] = memory[dest..dest+src2] */ OP_MEM_CPY_8, /* memcpy_8 : A : memory[src1..src1+src2] = memory[dest..dest+src2] */
OP_MEM_CPY_16, /* memcpy_16 : A : memory[src1..src1+src2] = memory[dest..dest+src2] */ OP_MEM_CPY_16, /* memcpy_16 : A : memory[src1..src1+src2] = memory[dest..dest+src2] */
@ -93,7 +87,7 @@ typedef enum {
OP_BIT_AND, /* bit_and : A : locals[dest] = locals[src1] & locals[src2] */ OP_BIT_AND, /* bit_and : A : locals[dest] = locals[src1] & locals[src2] */
OP_BIT_OR, /* bit_or : A : locals[dest] = locals[src1] | locals[src2] */ OP_BIT_OR, /* bit_or : A : locals[dest] = locals[src1] | locals[src2] */
OP_BIT_XOR, /* bit_xor : A : locals[dest] = locals[src1] ^ locals[src2] */ OP_BIT_XOR, /* bit_xor : A : locals[dest] = locals[src1] ^ locals[src2] */
OP_JMP_IMM, /* jump_immediate : C : jump to imm unconditionally */ OP_JMP_IMM, /* jump_immediate : E : jump to imm unconditionally */
OP_JMP_ABS, /* jump_absolute : A : jump to locals[dest] unconditionally */ OP_JMP_ABS, /* jump_absolute : A : jump to locals[dest] unconditionally */
OP_JMP_OFF, /* jump_offset : A : jump to locals[dest] + locals[src1] unconditionally */ OP_JMP_OFF, /* jump_offset : A : jump to locals[dest] + locals[src1] unconditionally */
OP_JMP_FLAG, /* jump_if_flag : A : jump to locals[dest] if flag > 0 */ OP_JMP_FLAG, /* jump_if_flag : A : jump to locals[dest] if flag > 0 */
@ -115,11 +109,15 @@ typedef enum {
OP_JGT_REAL, /* jump_gt_real : A : jump to locals[dest] if locals[src1] as real > locals[src2] as real */ OP_JGT_REAL, /* jump_gt_real : A : jump to locals[dest] if locals[src1] as real > locals[src2] as real */
OP_JLT_REAL, /* jump_lt_real : A : jump to locals[dest] if locals[src1] as real < locals[src2] as real */ OP_JLT_REAL, /* jump_lt_real : A : jump to locals[dest] if locals[src1] as real < locals[src2] as real */
OP_JLE_REAL, /* jump_le_real : A : jump to locals[dest] if locals[src1] as real <= locals[src2] as real */ OP_JLE_REAL, /* jump_le_real : A : jump to locals[dest] if locals[src1] as real <= locals[src2] as real */
OP_INT_TO_STR, /* int_to_str : A : locals[dest] = &mem[mp..] locals[src1] as str */
OP_NAT_TO_STR, /* nat_to_str : A : locals[dest] = &mem[mp..] locals[src1] as str */
OP_REAL_TO_STR, /* real_to_str : A : locals[dest] = &mem[mp..] locals[src1] as str */
OP_MAX_OPCODE /* not an opcode count of instructions */ OP_MAX_OPCODE /* not an opcode count of instructions */
} Opcode; } Opcode;
typedef enum { typedef enum {
SYSCALL_DBG_PRINT, /* temporary debugging print, use tunnel later */ SYSCALL_CONSOLE_WRITE,
SYSCALL_CONSOLE_READ,
SYSCALL_MAX SYSCALL_MAX
} Syscall; } Syscall;
@ -193,5 +191,6 @@ extern u8 *mem; /* memory */
extern bool init_vm(); extern bool init_vm();
extern u32 syscall(u32 id, u32 args, u32 mem_ptr); extern u32 syscall(u32 id, u32 args, u32 mem_ptr);
bool step_vm(); bool step_vm();
u32 str_alloc(char *str, u32 length);
#endif #endif