mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
V1.20
New feature: - Basic C compiler
This commit is contained in:
parent
3ba7d2da48
commit
afc85e8964
20 changed files with 1519 additions and 12 deletions
3
Makefile
3
Makefile
|
|
@ -91,6 +91,9 @@ $(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
|
|||
|
||||
# Copy README
|
||||
cp README.md $(ISO_DIR)/
|
||||
|
||||
# Copy user file.c if it exists
|
||||
@if [ -f file.c ]; then cp file.c $(ISO_DIR)/; fi
|
||||
|
||||
# Copy Wallpaper (if it exists)
|
||||
@if [ -f src/kernel/wallpaper.ppm ]; then cp src/kernel/wallpaper.ppm $(ISO_DIR)/; fi
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Brew OS 1.12 Alpha
|
||||
# Brew OS 1.20 Alpha
|
||||
|
||||
## Brewkernel is now BrewOS!
|
||||
Brewkernel will from now on be deprecated as it's core became too messy. I have built a less bloated kernel and wrote a DE above it, which is why it is now an OS instead of a kernel (in my opinion).
|
||||
|
|
|
|||
BIN
brewos.iso
BIN
brewos.iso
Binary file not shown.
BIN
build/brewos.elf
BIN
build/brewos.elf
Binary file not shown.
BIN
build/cli_apps/cc.o
Normal file
BIN
build/cli_apps/cc.o
Normal file
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
BIN
build/graphics.o
BIN
build/graphics.o
Binary file not shown.
BIN
build/vm.o
Normal file
BIN
build/vm.o
Normal file
Binary file not shown.
BIN
build/wm.o
BIN
build/wm.o
Binary file not shown.
Binary file not shown.
|
|
@ -35,8 +35,8 @@ static void about_paint(Window *win) {
|
|||
|
||||
// Version info
|
||||
draw_string(offset_x, offset_y + 105, "BrewOS", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 120, "BrewOS Version 1.12", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 135, "Kernel Version 2.1.0", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 120, "BrewOS Version 1.20", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 135, "Kernel Version 2.2.0", COLOR_BLACK);
|
||||
|
||||
// Copyright
|
||||
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_BLACK);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
void cli_cmd_brewver(char *args) {
|
||||
(void)args;
|
||||
cli_write("BrewOS v1.12 Alpha\n");
|
||||
cli_write("BrewOS Kernel V2.1.0 Pre-Alpha\n");
|
||||
cli_write("BrewOS v1.20 Alpha\n");
|
||||
cli_write("BrewOS Kernel V2.2.0 Pre-Alpha\n");
|
||||
}
|
||||
|
|
|
|||
730
src/kernel/cli_apps/cc.c
Normal file
730
src/kernel/cli_apps/cc.c
Normal file
|
|
@ -0,0 +1,730 @@
|
|||
#include "cli_apps.h"
|
||||
#include "cli_utils.h"
|
||||
#include "../vm.h"
|
||||
#include "../fat32.h"
|
||||
#include "../memory_manager.h"
|
||||
#include "../cmd.h"
|
||||
|
||||
// --- Compiler Limits ---
|
||||
#define MAX_SOURCE 8192
|
||||
#define MAX_TOKENS 2048
|
||||
#define MAX_VARS 64
|
||||
#define CODE_SIZE 4096
|
||||
#define STR_POOL_SIZE 2048
|
||||
|
||||
static int compile_error = 0;
|
||||
|
||||
// --- Lexer ---
|
||||
typedef enum {
|
||||
TOK_EOF,
|
||||
TOK_INT, // 123, 0xFF
|
||||
TOK_STRING, // "hello"
|
||||
TOK_ID, // abc
|
||||
TOK_PLUS, // +
|
||||
TOK_MINUS, // -
|
||||
TOK_MUL, // *
|
||||
TOK_DIV, // /
|
||||
TOK_ASSIGN, // =
|
||||
TOK_LPAREN, // (
|
||||
TOK_RPAREN, // )
|
||||
TOK_LBRACKET, // [
|
||||
TOK_RBRACKET, // ]
|
||||
TOK_LBRACE, // {
|
||||
TOK_RBRACE, // }
|
||||
TOK_SEMI, // ;
|
||||
TOK_COMMA, // ,
|
||||
TOK_EQ, // ==
|
||||
TOK_NEQ, // !=
|
||||
TOK_LT, // <
|
||||
TOK_GT, // >
|
||||
TOK_LE, // <=
|
||||
TOK_GE, // >=
|
||||
TOK_IF, // if
|
||||
TOK_ELSE, // else
|
||||
TOK_WHILE, // while
|
||||
TOK_INT_TYPE, // int
|
||||
TOK_CHAR_TYPE,// char
|
||||
TOK_VOID_TYPE,// void
|
||||
TOK_MAIN // main
|
||||
} TokenType;
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
int int_val;
|
||||
char str_val[64]; // Identifier or String Content
|
||||
} Token;
|
||||
|
||||
static char *source_ptr;
|
||||
static Token tokens[MAX_TOKENS];
|
||||
static int token_count = 0;
|
||||
|
||||
static void lex_error(const char *msg) {
|
||||
cmd_write("Compiler Error: ");
|
||||
cmd_write(msg);
|
||||
cmd_write("\n");
|
||||
compile_error = 1;
|
||||
}
|
||||
|
||||
static void lexer(const char *source) {
|
||||
source_ptr = (char*)source;
|
||||
token_count = 0;
|
||||
compile_error = 0;
|
||||
|
||||
while (*source_ptr) {
|
||||
// Skip whitespace
|
||||
while (*source_ptr == ' ' || *source_ptr == '\n' || *source_ptr == '\t' || *source_ptr == '\r') source_ptr++;
|
||||
if (!*source_ptr) break;
|
||||
|
||||
// Skip comments //
|
||||
if (*source_ptr == '/' && *(source_ptr+1) == '/') {
|
||||
while (*source_ptr && *source_ptr != '\n') source_ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Token *t = &tokens[token_count++];
|
||||
|
||||
// Hex Literals 0x...
|
||||
if (*source_ptr == '0' && (*(source_ptr+1) == 'x' || *(source_ptr+1) == 'X')) {
|
||||
source_ptr += 2; // Skip 0x
|
||||
t->type = TOK_INT;
|
||||
t->int_val = 0;
|
||||
int has_digits = 0;
|
||||
while ((*source_ptr >= '0' && *source_ptr <= '9') ||
|
||||
(*source_ptr >= 'a' && *source_ptr <= 'f') ||
|
||||
(*source_ptr >= 'A' && *source_ptr <= 'F')) {
|
||||
int digit = 0;
|
||||
if (*source_ptr >= '0' && *source_ptr <= '9') digit = *source_ptr - '0';
|
||||
else if (*source_ptr >= 'a' && *source_ptr <= 'f') digit = *source_ptr - 'a' + 10;
|
||||
else if (*source_ptr >= 'A' && *source_ptr <= 'F') digit = *source_ptr - 'A' + 10;
|
||||
|
||||
t->int_val = (t->int_val << 4) | digit;
|
||||
source_ptr++;
|
||||
has_digits = 1;
|
||||
}
|
||||
if (!has_digits) {
|
||||
lex_error("Invalid hex literal");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Decimal Integers
|
||||
else if (*source_ptr >= '0' && *source_ptr <= '9') {
|
||||
t->type = TOK_INT;
|
||||
t->int_val = 0;
|
||||
while (*source_ptr >= '0' && *source_ptr <= '9') {
|
||||
t->int_val = t->int_val * 10 + (*source_ptr - '0');
|
||||
source_ptr++;
|
||||
}
|
||||
}
|
||||
// Strings
|
||||
else if (*source_ptr == '"') {
|
||||
t->type = TOK_STRING;
|
||||
source_ptr++; // Skip "
|
||||
int len = 0;
|
||||
while (*source_ptr && *source_ptr != '"') {
|
||||
if (*source_ptr == '\\' && *(source_ptr+1) == 'n') {
|
||||
if (len < 63) t->str_val[len++] = '\n';
|
||||
source_ptr += 2;
|
||||
} else {
|
||||
if (len < 63) t->str_val[len++] = *source_ptr;
|
||||
source_ptr++;
|
||||
}
|
||||
}
|
||||
t->str_val[len] = 0;
|
||||
if (*source_ptr == '"') source_ptr++;
|
||||
}
|
||||
// Character Literals
|
||||
else if (*source_ptr == '\'') {
|
||||
t->type = TOK_INT;
|
||||
source_ptr++; // Skip '
|
||||
char c = 0;
|
||||
if (*source_ptr == '\\') {
|
||||
source_ptr++;
|
||||
if (*source_ptr == 'n') c = '\n';
|
||||
else if (*source_ptr == 't') c = '\t';
|
||||
else if (*source_ptr == '0') c = '\0';
|
||||
else if (*source_ptr == '\\') c = '\\';
|
||||
else if (*source_ptr == '\'') c = '\'';
|
||||
else c = *source_ptr;
|
||||
source_ptr++;
|
||||
} else {
|
||||
c = *source_ptr;
|
||||
source_ptr++;
|
||||
}
|
||||
if (*source_ptr == '\'') source_ptr++;
|
||||
else { lex_error("Expected closing '"); return; }
|
||||
t->int_val = (int)c;
|
||||
}
|
||||
// Identifiers
|
||||
else if ((*source_ptr >= 'a' && *source_ptr <= 'z') || (*source_ptr >= 'A' && *source_ptr <= 'Z') || *source_ptr == '_') {
|
||||
int len = 0;
|
||||
while ((*source_ptr >= 'a' && *source_ptr <= 'z') || (*source_ptr >= 'A' && *source_ptr <= 'Z') || (*source_ptr >= '0' && *source_ptr <= '9') || *source_ptr == '_') {
|
||||
if (len < 63) t->str_val[len++] = *source_ptr;
|
||||
source_ptr++;
|
||||
}
|
||||
t->str_val[len] = 0;
|
||||
|
||||
if (cli_strcmp(t->str_val, "if") == 0) t->type = TOK_IF;
|
||||
else if (cli_strcmp(t->str_val, "else") == 0) t->type = TOK_ELSE;
|
||||
else if (cli_strcmp(t->str_val, "while") == 0) t->type = TOK_WHILE;
|
||||
else if (cli_strcmp(t->str_val, "int") == 0) t->type = TOK_INT_TYPE;
|
||||
else if (cli_strcmp(t->str_val, "char") == 0) t->type = TOK_CHAR_TYPE;
|
||||
else if (cli_strcmp(t->str_val, "void") == 0) t->type = TOK_VOID_TYPE;
|
||||
else if (cli_strcmp(t->str_val, "main") == 0) t->type = TOK_MAIN;
|
||||
else t->type = TOK_ID;
|
||||
} else {
|
||||
switch (*source_ptr) {
|
||||
case '+': t->type = TOK_PLUS; break;
|
||||
case '-': t->type = TOK_MINUS; break;
|
||||
case '*': t->type = TOK_MUL; break;
|
||||
case '/': t->type = TOK_DIV; break;
|
||||
case '(': t->type = TOK_LPAREN; break;
|
||||
case ')': t->type = TOK_RPAREN; break;
|
||||
case '[': t->type = TOK_LBRACKET; break;
|
||||
case ']': t->type = TOK_RBRACKET; break;
|
||||
case '{': t->type = TOK_LBRACE; break;
|
||||
case '}': t->type = TOK_RBRACE; break;
|
||||
case ';': t->type = TOK_SEMI; break;
|
||||
case ',': t->type = TOK_COMMA; break;
|
||||
case '=':
|
||||
if (*(source_ptr+1) == '=') { t->type = TOK_EQ; source_ptr++; }
|
||||
else t->type = TOK_ASSIGN;
|
||||
break;
|
||||
case '!':
|
||||
if (*(source_ptr+1) == '=') { t->type = TOK_NEQ; source_ptr++; }
|
||||
else { lex_error("Unexpected !"); return; }
|
||||
break;
|
||||
case '<':
|
||||
if (*(source_ptr+1) == '=') { t->type = TOK_LE; source_ptr++; }
|
||||
else t->type = TOK_LT;
|
||||
break;
|
||||
case '>':
|
||||
if (*(source_ptr+1) == '=') { t->type = TOK_GE; source_ptr++; }
|
||||
else t->type = TOK_GT;
|
||||
break;
|
||||
default:
|
||||
lex_error("Unknown char");
|
||||
return;
|
||||
}
|
||||
source_ptr++;
|
||||
}
|
||||
}
|
||||
tokens[token_count].type = TOK_EOF;
|
||||
}
|
||||
|
||||
// --- Builtins ---
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
int syscall_id;
|
||||
} Builtin;
|
||||
|
||||
static const Builtin builtins[] = {
|
||||
{"exit", SYS_EXIT},
|
||||
{"print_int", SYS_PRINT_INT},
|
||||
{"print_char", SYS_PRINT_CHAR},
|
||||
{"print_str", SYS_PRINT_STR}, // puts
|
||||
{"print", SYS_PRINT_INT}, // Alias
|
||||
{"pritc", SYS_PRINT_CHAR}, // Alias
|
||||
{"puts", SYS_PRINT_STR}, // Alias
|
||||
{"nl", SYS_NL},
|
||||
{"cls", SYS_CLS},
|
||||
{"getchar", SYS_GETCHAR},
|
||||
{"strlen", SYS_STRLEN},
|
||||
{"strcmp", SYS_STRCMP},
|
||||
{"strcpy", SYS_STRCPY},
|
||||
{"strcat", SYS_STRCAT},
|
||||
{"memset", SYS_MEMSET},
|
||||
{"memcpy", SYS_MEMCPY},
|
||||
{"malloc", SYS_MALLOC},
|
||||
{"free", SYS_FREE},
|
||||
{"rand", SYS_RAND},
|
||||
{"srand", SYS_SRAND},
|
||||
{"abs", SYS_ABS},
|
||||
{"min", SYS_MIN},
|
||||
{"max", SYS_MAX},
|
||||
{"pow", SYS_POW},
|
||||
{"sqrt", SYS_SQRT},
|
||||
{"sleep", SYS_SLEEP},
|
||||
{"fopen", SYS_FOPEN},
|
||||
{"fclose", SYS_FCLOSE},
|
||||
{"fread", SYS_FREAD},
|
||||
{"fwrite", SYS_FWRITE},
|
||||
{"fseek", SYS_FSEEK},
|
||||
{"remove", SYS_REMOVE},
|
||||
{"draw_pixel", SYS_DRAW_PIXEL},
|
||||
{"draw_rect", SYS_DRAW_RECT},
|
||||
{"draw_line", SYS_DRAW_LINE},
|
||||
{"draw_text", SYS_DRAW_TEXT},
|
||||
{"get_width", SYS_GET_WIDTH},
|
||||
{"get_height", SYS_GET_HEIGHT},
|
||||
{"get_time", SYS_GET_TIME},
|
||||
{"kb_hit", SYS_KB_HIT},
|
||||
{"mouse_x", SYS_MOUSE_X},
|
||||
{"mouse_y", SYS_MOUSE_Y},
|
||||
{"mouse_state", SYS_MOUSE_STATE},
|
||||
{"play_sound", SYS_PLAY_SOUND},
|
||||
{"atoi", SYS_ATOI},
|
||||
{"itoa", SYS_ITOA},
|
||||
{"peek", SYS_PEEK},
|
||||
{"poke", SYS_POKE},
|
||||
{"exec", SYS_EXEC},
|
||||
{"system", SYS_SYSTEM},
|
||||
{"strchr", SYS_STRCHR},
|
||||
{"memcmp", SYS_MEMCMP},
|
||||
{"isalnum", SYS_ISALNUM},
|
||||
{"isalpha", SYS_ISALPHA},
|
||||
{"isdigit", SYS_ISDIGIT},
|
||||
{"tolower", SYS_TOLOWER},
|
||||
{"toupper", SYS_TOUPPER},
|
||||
{"strncpy", SYS_STRNCPY},
|
||||
{"strncat", SYS_STRNCAT},
|
||||
{"strncmp", SYS_STRNCMP},
|
||||
{"strstr", SYS_STRSTR},
|
||||
{"strrchr", SYS_STRRCHR},
|
||||
{"memmove", SYS_MEMMOVE},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static int find_builtin(const char *name) {
|
||||
for (int i = 0; builtins[i].name != NULL; i++) {
|
||||
if (cli_strcmp(builtins[i].name, name) == 0) {
|
||||
return builtins[i].syscall_id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- Parser & CodeGen ---
|
||||
|
||||
static uint8_t code[CODE_SIZE];
|
||||
static int code_pos = 0;
|
||||
static int cur_token = 0;
|
||||
|
||||
static uint8_t str_pool[STR_POOL_SIZE];
|
||||
static int str_pool_pos = 0;
|
||||
|
||||
// Variables
|
||||
typedef struct {
|
||||
char name[32];
|
||||
int addr; // Address in VM memory
|
||||
} Symbol;
|
||||
|
||||
static Symbol symbols[MAX_VARS];
|
||||
static int symbol_count = 0;
|
||||
|
||||
static int next_var_addr = 4096;
|
||||
|
||||
static int find_symbol(const char *name) {
|
||||
for (int i = 0; i < symbol_count; i++) {
|
||||
if (cli_strcmp(symbols[i].name, name) == 0) return symbols[i].addr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int add_symbol(const char *name) {
|
||||
if (find_symbol(name) != -1) return find_symbol(name);
|
||||
if (symbol_count >= MAX_VARS) return -1;
|
||||
cli_strcpy(symbols[symbol_count].name, name);
|
||||
symbols[symbol_count].addr = next_var_addr;
|
||||
next_var_addr += 4; // 32-bit int
|
||||
return symbol_count++;
|
||||
}
|
||||
|
||||
static void emit(uint8_t b) {
|
||||
if (code_pos < CODE_SIZE) code[code_pos++] = b;
|
||||
else {
|
||||
cmd_write("Error: Code buffer overflow\n");
|
||||
compile_error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void emit32(int v) {
|
||||
emit(v & 0xFF);
|
||||
emit((v >> 8) & 0xFF);
|
||||
emit((v >> 16) & 0xFF);
|
||||
emit((v >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static int add_string(const char *str) {
|
||||
int start = str_pool_pos;
|
||||
int len = cli_strlen(str);
|
||||
if (str_pool_pos + len + 1 >= STR_POOL_SIZE) {
|
||||
cmd_write("Error: String pool overflow\n");
|
||||
compile_error = 1;
|
||||
return 0;
|
||||
}
|
||||
for(int i=0; i<len; i++) str_pool[str_pool_pos++] = str[i];
|
||||
str_pool[str_pool_pos++] = 0;
|
||||
return start;
|
||||
}
|
||||
|
||||
static void match(TokenType t) {
|
||||
if (compile_error) return;
|
||||
if (tokens[cur_token].type == t) {
|
||||
cur_token++;
|
||||
} else {
|
||||
cmd_write("Syntax Error: Expected token ");
|
||||
// Debugging helper
|
||||
cmd_write_int(t);
|
||||
cmd_write(" got ");
|
||||
cmd_write_int(tokens[cur_token].type);
|
||||
cmd_write("\n");
|
||||
compile_error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward decls
|
||||
static void expression();
|
||||
static void statement();
|
||||
static void block();
|
||||
|
||||
static void function_call(int syscall_id) {
|
||||
if (compile_error) return;
|
||||
cur_token++; // ID
|
||||
match(TOK_LPAREN);
|
||||
|
||||
if (tokens[cur_token].type != TOK_RPAREN) {
|
||||
expression();
|
||||
while (tokens[cur_token].type == TOK_COMMA) {
|
||||
cur_token++;
|
||||
expression();
|
||||
}
|
||||
}
|
||||
match(TOK_RPAREN);
|
||||
|
||||
emit(OP_SYSCALL);
|
||||
emit32(syscall_id);
|
||||
}
|
||||
|
||||
static void factor() {
|
||||
if (compile_error) return;
|
||||
if (tokens[cur_token].type == TOK_INT) {
|
||||
emit(OP_IMM);
|
||||
emit32(tokens[cur_token].int_val);
|
||||
cur_token++;
|
||||
} else if (tokens[cur_token].type == TOK_STRING) {
|
||||
int offset = add_string(tokens[cur_token].str_val);
|
||||
emit(OP_PUSH_PTR);
|
||||
emit32(offset);
|
||||
cur_token++;
|
||||
} else if (tokens[cur_token].type == TOK_ID) {
|
||||
int syscall = find_builtin(tokens[cur_token].str_val);
|
||||
if (syscall != -1 && tokens[cur_token+1].type == TOK_LPAREN) {
|
||||
function_call(syscall);
|
||||
} else {
|
||||
int addr = find_symbol(tokens[cur_token].str_val);
|
||||
if (addr == -1) {
|
||||
cmd_write("Error: Undefined variable: ");
|
||||
cmd_write(tokens[cur_token].str_val);
|
||||
cmd_write("\n");
|
||||
compile_error = 1;
|
||||
}
|
||||
emit(OP_LOAD);
|
||||
emit32(addr);
|
||||
cur_token++;
|
||||
}
|
||||
} else if (tokens[cur_token].type == TOK_LPAREN) {
|
||||
cur_token++;
|
||||
expression();
|
||||
match(TOK_RPAREN);
|
||||
} else {
|
||||
cmd_write("Syntax Error: Unexpected token in factor\n");
|
||||
compile_error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void term() {
|
||||
if (compile_error) return;
|
||||
factor();
|
||||
while (tokens[cur_token].type == TOK_MUL || tokens[cur_token].type == TOK_DIV) {
|
||||
TokenType op = tokens[cur_token].type;
|
||||
cur_token++;
|
||||
factor();
|
||||
if (op == TOK_MUL) emit(OP_MUL);
|
||||
else emit(OP_DIV);
|
||||
}
|
||||
}
|
||||
|
||||
static void additive() {
|
||||
if (compile_error) return;
|
||||
term();
|
||||
while (tokens[cur_token].type == TOK_PLUS || tokens[cur_token].type == TOK_MINUS) {
|
||||
TokenType op = tokens[cur_token].type;
|
||||
cur_token++;
|
||||
term();
|
||||
if (op == TOK_PLUS) emit(OP_ADD);
|
||||
else emit(OP_SUB);
|
||||
}
|
||||
}
|
||||
|
||||
static void relation() {
|
||||
if (compile_error) return;
|
||||
additive();
|
||||
if (tokens[cur_token].type >= TOK_EQ && tokens[cur_token].type <= TOK_GE) {
|
||||
TokenType op = tokens[cur_token].type;
|
||||
cur_token++;
|
||||
additive();
|
||||
switch (op) {
|
||||
case TOK_EQ: emit(OP_EQ); break;
|
||||
case TOK_NEQ: emit(OP_NEQ); break;
|
||||
case TOK_LT: emit(OP_LT); break;
|
||||
case TOK_GT: emit(OP_GT); break;
|
||||
case TOK_LE: emit(OP_LE); break;
|
||||
case TOK_GE: emit(OP_GE); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void expression() {
|
||||
if (compile_error) return;
|
||||
relation();
|
||||
}
|
||||
|
||||
static void statement() {
|
||||
if (compile_error) return;
|
||||
if (tokens[cur_token].type == TOK_INT_TYPE || tokens[cur_token].type == TOK_CHAR_TYPE) {
|
||||
// Declaration
|
||||
cur_token++;
|
||||
while (tokens[cur_token].type == TOK_MUL) cur_token++; // Skip pointers *
|
||||
|
||||
if (tokens[cur_token].type == TOK_ID) {
|
||||
add_symbol(tokens[cur_token].str_val);
|
||||
cur_token++;
|
||||
|
||||
// Skip array size [INT]
|
||||
if (tokens[cur_token].type == TOK_LBRACKET) {
|
||||
cur_token++;
|
||||
if (tokens[cur_token].type == TOK_INT) cur_token++;
|
||||
if (tokens[cur_token].type == TOK_RBRACKET) cur_token++;
|
||||
else cmd_write("Error: Expected ]\n");
|
||||
}
|
||||
|
||||
if (tokens[cur_token].type == TOK_ASSIGN) { int addr = find_symbol(tokens[cur_token-1].str_val);
|
||||
cur_token++;
|
||||
expression();
|
||||
emit(OP_STORE);
|
||||
emit32(addr);
|
||||
}
|
||||
match(TOK_SEMI);
|
||||
} else {
|
||||
cmd_write("Syntax Error: Expected identifier\n");
|
||||
compile_error = 1;
|
||||
}
|
||||
} else if (tokens[cur_token].type == TOK_ID) {
|
||||
int syscall = find_builtin(tokens[cur_token].str_val);
|
||||
if (syscall != -1 && tokens[cur_token+1].type == TOK_LPAREN) {
|
||||
function_call(syscall);
|
||||
match(TOK_SEMI);
|
||||
// Drop return value (primitive)
|
||||
emit(OP_POP);
|
||||
} else {
|
||||
// Assignment: x = expr;
|
||||
int addr = find_symbol(tokens[cur_token].str_val);
|
||||
if (addr == -1) {
|
||||
cmd_write("Error: Undefined variable assignment: ");
|
||||
cmd_write(tokens[cur_token].str_val);
|
||||
cmd_write("\n");
|
||||
compile_error = 1;
|
||||
return;
|
||||
}
|
||||
cur_token++;
|
||||
match(TOK_ASSIGN);
|
||||
expression();
|
||||
match(TOK_SEMI);
|
||||
emit(OP_STORE);
|
||||
emit32(addr);
|
||||
}
|
||||
} else if (tokens[cur_token].type == TOK_IF) {
|
||||
cur_token++;
|
||||
match(TOK_LPAREN);
|
||||
expression();
|
||||
match(TOK_RPAREN);
|
||||
|
||||
emit(OP_JZ);
|
||||
int jz_addr_pos = code_pos;
|
||||
emit32(0);
|
||||
|
||||
block();
|
||||
|
||||
if (tokens[cur_token].type == TOK_ELSE) {
|
||||
emit(OP_JMP);
|
||||
int jmp_addr_pos = code_pos;
|
||||
emit32(0);
|
||||
|
||||
int else_start = code_pos;
|
||||
code[jz_addr_pos] = else_start & 0xFF;
|
||||
code[jz_addr_pos+1] = (else_start >> 8) & 0xFF;
|
||||
code[jz_addr_pos+2] = (else_start >> 16) & 0xFF;
|
||||
code[jz_addr_pos+3] = (else_start >> 24) & 0xFF;
|
||||
|
||||
cur_token++;
|
||||
block();
|
||||
|
||||
int end_addr = code_pos;
|
||||
code[jmp_addr_pos] = end_addr & 0xFF;
|
||||
code[jmp_addr_pos+1] = (end_addr >> 8) & 0xFF;
|
||||
code[jmp_addr_pos+2] = (end_addr >> 16) & 0xFF;
|
||||
code[jmp_addr_pos+3] = (end_addr >> 24) & 0xFF;
|
||||
} else {
|
||||
int end_addr = code_pos;
|
||||
code[jz_addr_pos] = end_addr & 0xFF;
|
||||
code[jz_addr_pos+1] = (end_addr >> 8) & 0xFF;
|
||||
code[jz_addr_pos+2] = (end_addr >> 16) & 0xFF;
|
||||
code[jz_addr_pos+3] = (end_addr >> 24) & 0xFF;
|
||||
}
|
||||
} else if (tokens[cur_token].type == TOK_WHILE) {
|
||||
int start_addr = code_pos;
|
||||
cur_token++;
|
||||
match(TOK_LPAREN);
|
||||
expression();
|
||||
match(TOK_RPAREN);
|
||||
|
||||
emit(OP_JZ);
|
||||
int jz_addr_pos = code_pos;
|
||||
emit32(0);
|
||||
|
||||
block();
|
||||
|
||||
emit(OP_JMP);
|
||||
emit32(start_addr);
|
||||
|
||||
int end_addr = code_pos;
|
||||
code[jz_addr_pos] = end_addr & 0xFF;
|
||||
code[jz_addr_pos+1] = (end_addr >> 8) & 0xFF;
|
||||
code[jz_addr_pos+2] = (end_addr >> 16) & 0xFF;
|
||||
code[jz_addr_pos+3] = (end_addr >> 24) & 0xFF;
|
||||
} else {
|
||||
cur_token++;
|
||||
}
|
||||
}
|
||||
|
||||
static void block() {
|
||||
if (compile_error) return;
|
||||
match(TOK_LBRACE);
|
||||
while (tokens[cur_token].type != TOK_RBRACE && tokens[cur_token].type != TOK_EOF && !compile_error) {
|
||||
statement();
|
||||
}
|
||||
match(TOK_RBRACE);
|
||||
}
|
||||
|
||||
static void program() {
|
||||
if (tokens[cur_token].type == TOK_INT_TYPE || tokens[cur_token].type == TOK_VOID_TYPE) cur_token++;
|
||||
if (tokens[cur_token].type == TOK_MAIN) cur_token++;
|
||||
match(TOK_LPAREN);
|
||||
match(TOK_RPAREN);
|
||||
block();
|
||||
emit(OP_HALT);
|
||||
}
|
||||
|
||||
void cli_cmd_cc(char *args) {
|
||||
if (!args || !*args) {
|
||||
cmd_write("Usage: cc <filename.c>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FAT32_FileHandle *fh = fat32_open(args, "r");
|
||||
if (!fh) {
|
||||
cmd_write("Error: Cannot open source file.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char source[MAX_SOURCE];
|
||||
int len = fat32_read(fh, source, MAX_SOURCE - 1);
|
||||
source[len] = 0;
|
||||
fat32_close(fh);
|
||||
|
||||
lexer(source);
|
||||
if (compile_error) return;
|
||||
|
||||
code_pos = 0;
|
||||
symbol_count = 0;
|
||||
cur_token = 0;
|
||||
str_pool_pos = 0;
|
||||
next_var_addr = 4096;
|
||||
|
||||
const char* magic = VM_MAGIC;
|
||||
for(int i=0; i<7; i++) emit(magic[i]);
|
||||
emit(1);
|
||||
|
||||
program();
|
||||
|
||||
if (compile_error) {
|
||||
cmd_write("Compilation Failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Finalize Code
|
||||
int pool_start_addr = code_pos;
|
||||
for(int i=0; i<str_pool_pos; i++) {
|
||||
emit(str_pool[i]);
|
||||
}
|
||||
|
||||
// Fixup OP_PUSH_PTR
|
||||
int pc = 8;
|
||||
while (pc < pool_start_addr) {
|
||||
uint8_t op = code[pc++];
|
||||
switch (op) {
|
||||
case OP_HALT: break;
|
||||
case OP_IMM: pc += 4; break;
|
||||
case OP_LOAD: pc += 4; break;
|
||||
case OP_STORE: pc += 4; break;
|
||||
case OP_LOAD8: pc += 4; break;
|
||||
case OP_STORE8: pc += 4; break;
|
||||
case OP_ADD: break;
|
||||
case OP_SUB: break;
|
||||
case OP_MUL: break;
|
||||
case OP_DIV: break;
|
||||
case OP_PRINT: break;
|
||||
case OP_PRITC: break;
|
||||
case OP_JMP: pc += 4; break;
|
||||
case OP_JZ: pc += 4; break;
|
||||
case OP_EQ: break;
|
||||
case OP_NEQ: break;
|
||||
case OP_LT: break;
|
||||
case OP_GT: break;
|
||||
case OP_LE: break;
|
||||
case OP_GE: break;
|
||||
case OP_SYSCALL: pc += 4; break;
|
||||
case OP_POP: break;
|
||||
case OP_PUSH_PTR: {
|
||||
int offset = 0;
|
||||
offset |= code[pc];
|
||||
offset |= code[pc+1] << 8;
|
||||
offset |= code[pc+2] << 16;
|
||||
offset |= code[pc+3] << 24;
|
||||
|
||||
int abs_addr = pool_start_addr + offset;
|
||||
|
||||
code[pc] = abs_addr & 0xFF;
|
||||
code[pc+1] = (abs_addr >> 8) & 0xFF;
|
||||
code[pc+2] = (abs_addr >> 16) & 0xFF;
|
||||
code[pc+3] = (abs_addr >> 24) & 0xFF;
|
||||
|
||||
pc += 4;
|
||||
code[pc-5] = OP_IMM;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
char out_name[64];
|
||||
int i = 0;
|
||||
while(args[i] && args[i] != '.') {
|
||||
out_name[i] = args[i];
|
||||
i++;
|
||||
}
|
||||
out_name[i] = 0;
|
||||
|
||||
FAT32_FileHandle *out_fh = fat32_open(out_name, "w");
|
||||
if (out_fh) {
|
||||
fat32_write(out_fh, code, code_pos);
|
||||
fat32_close(out_fh);
|
||||
cmd_write("Compilation successful. Output: ");
|
||||
cmd_write(out_name);
|
||||
cmd_write("\n");
|
||||
} else {
|
||||
cmd_write("Error: Cannot write output file.\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -47,4 +47,7 @@ void cli_cmd_udptest(char *args);
|
|||
// PCI commands
|
||||
void cli_cmd_pcilist(char *args);
|
||||
|
||||
// Compiler
|
||||
void cli_cmd_cc(char *args);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "network.h"
|
||||
#include "vm.h"
|
||||
|
||||
#define CMD_COLS 116
|
||||
#define CMD_ROWS 41
|
||||
|
|
@ -476,6 +477,8 @@ static const CommandEntry commands[] = {
|
|||
{"udptest", cli_cmd_udptest},
|
||||
{"PCILIST", cli_cmd_pcilist},
|
||||
{"pcilist", cli_cmd_pcilist},
|
||||
{"COMPC", cli_cmd_cc},
|
||||
{"compc", cli_cmd_cc},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -497,6 +500,31 @@ static void cmd_exec_single(char *cmd) {
|
|||
while (*cmd == ' ') cmd++;
|
||||
if (!*cmd) return;
|
||||
|
||||
// Check for file execution ./filename
|
||||
if (cmd[0] == '.' && cmd[1] == '/') {
|
||||
char *filename = cmd + 2;
|
||||
FAT32_FileHandle *fh = fat32_open(filename, "r");
|
||||
if (fh) {
|
||||
// It's a file, try to execute it
|
||||
// Limit executable size to 4KB for now
|
||||
uint8_t buffer[4096];
|
||||
int size = fat32_read(fh, buffer, 4096);
|
||||
fat32_close(fh);
|
||||
|
||||
if (size > 0) {
|
||||
int res = vm_exec(buffer, size);
|
||||
if (res != 0) {
|
||||
cmd_write("Execution failed.\n");
|
||||
}
|
||||
} else {
|
||||
cmd_write("Error: Empty file.\n");
|
||||
}
|
||||
} else {
|
||||
cmd_write("Error: Command not found or file does not exist.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Split cmd and args
|
||||
char *args = cmd;
|
||||
while (*args && *args != ' ') args++;
|
||||
|
|
@ -882,6 +910,7 @@ static void create_test_files(void) {
|
|||
fat32_mkdir("Documents");
|
||||
fat32_mkdir("Projects");
|
||||
fat32_mkdir("Documents/Important");
|
||||
fat32_mkdir("Apps");
|
||||
|
||||
FAT32_FileHandle *fh = fat32_open("README.md", "w");
|
||||
if (fh) {
|
||||
|
|
@ -981,13 +1010,6 @@ static void create_test_files(void) {
|
|||
|
||||
write_license_file();
|
||||
|
||||
fh = fat32_open("Documents/notes.txt", "w");
|
||||
if (fh) {
|
||||
const char *content = "My Notes\n\n- First note\n- Second note\n";
|
||||
fat32_write(fh, (void *)content, 39);
|
||||
fat32_close(fh);
|
||||
}
|
||||
|
||||
fh = fat32_open("Documents/notes.txt", "w");
|
||||
if (fh) {
|
||||
const char *content = "My Notes\n\n- First note\n- Second note\n";
|
||||
|
|
@ -1001,6 +1023,19 @@ static void create_test_files(void) {
|
|||
fat32_write(fh, (void *)content, 32);
|
||||
fat32_close(fh);
|
||||
}
|
||||
|
||||
fh = fat32_open("Apps/wordofgod.c", "w");
|
||||
if (fh) {
|
||||
const char *content =
|
||||
"int main() {\n"
|
||||
" // Word of god (inspired by TempleOS (RIP Terry Davis)\n "
|
||||
" puts(\"Word of God:\\n\");\n"
|
||||
" print_int(rand());\n"
|
||||
" puts(\"\\n\");\n"
|
||||
"}\n";
|
||||
fat32_write(fh, (void *)content, cmd_strlen(content));
|
||||
fat32_close(fh);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_init(void) {
|
||||
|
|
|
|||
|
|
@ -8,4 +8,10 @@ extern Window win_cmd;
|
|||
void cmd_init(void);
|
||||
void cmd_reset(void);
|
||||
|
||||
// IO Functions
|
||||
void cmd_write(const char *str);
|
||||
void cmd_putchar(char c);
|
||||
void cmd_write_int(int n);
|
||||
void cmd_screen_clear(void);
|
||||
|
||||
#endif
|
||||
611
src/kernel/vm.c
Normal file
611
src/kernel/vm.c
Normal file
|
|
@ -0,0 +1,611 @@
|
|||
#include "vm.h"
|
||||
#include "cmd.h"
|
||||
#include "memory_manager.h"
|
||||
#include "graphics.h"
|
||||
#include "wm.h"
|
||||
#include "fat32.h"
|
||||
#include "rtc.h"
|
||||
#include "ps2.h"
|
||||
#include "cli_apps/cli_utils.h"
|
||||
#include "io.h"
|
||||
|
||||
// --- Scancode Map (Set 1) ---
|
||||
static char vm_scancode_map[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0,
|
||||
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*',
|
||||
0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
// VM State
|
||||
static int stack[VM_STACK_SIZE];
|
||||
static unsigned long int rand_next = 1;
|
||||
static int sp = 0;
|
||||
static uint8_t memory[VM_MEMORY_SIZE]; // 64KB Linear RAM
|
||||
|
||||
// --- Graphics Overlay Support ---
|
||||
typedef struct {
|
||||
int x, y, w, h;
|
||||
uint32_t color;
|
||||
} VMRect;
|
||||
|
||||
#define MAX_VM_RECTS 256
|
||||
static VMRect vm_rects[MAX_VM_RECTS];
|
||||
static int vm_rect_count = 0;
|
||||
|
||||
static void vm_paint_overlay(void) {
|
||||
for (int i = 0; i < vm_rect_count; i++) {
|
||||
draw_rect(vm_rects[i].x, vm_rects[i].y, vm_rects[i].w, vm_rects[i].h, vm_rects[i].color);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to access memory as int
|
||||
static int mem_read32(int addr) {
|
||||
if (addr < 0 || addr > VM_MEMORY_SIZE - 4) return 0;
|
||||
int val = 0;
|
||||
val |= memory[addr];
|
||||
val |= memory[addr+1] << 8;
|
||||
val |= memory[addr+2] << 16;
|
||||
val |= memory[addr+3] << 24;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void mem_write32(int addr, int val) {
|
||||
if (addr < 0 || addr > VM_MEMORY_SIZE - 4) return;
|
||||
memory[addr] = val & 0xFF;
|
||||
memory[addr+1] = (val >> 8) & 0xFF;
|
||||
memory[addr+2] = (val >> 16) & 0xFF;
|
||||
memory[addr+3] = (val >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
static void vm_reset(void) {
|
||||
sp = 0;
|
||||
cli_memset(memory, 0, VM_MEMORY_SIZE);
|
||||
}
|
||||
|
||||
static void push(int val) {
|
||||
if (sp < VM_STACK_SIZE) {
|
||||
stack[sp++] = val;
|
||||
} else {
|
||||
cmd_write("VM Error: Stack Overflow\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int pop(void) {
|
||||
if (sp > 0) {
|
||||
return stack[--sp];
|
||||
}
|
||||
cmd_write("VM Error: Stack Underflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Syscall Implementations
|
||||
static void vm_syscall(int id) {
|
||||
switch (id) {
|
||||
case SYS_EXIT:
|
||||
// Handled by return code in main loop usually, but here just do nothing or treat as halt
|
||||
push(0);
|
||||
break;
|
||||
case SYS_PRINT_INT:
|
||||
cmd_write_int(pop());
|
||||
push(0);
|
||||
break;
|
||||
case SYS_PRINT_CHAR: {
|
||||
char c = (char)pop();
|
||||
char s[2] = {c, 0};
|
||||
cmd_write(s);
|
||||
push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_PRINT_STR: {
|
||||
int addr = pop();
|
||||
if (addr >= 0 && addr < VM_MEMORY_SIZE) {
|
||||
cmd_write((char*)&memory[addr]);
|
||||
}
|
||||
push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_NL:
|
||||
cmd_write("\n");
|
||||
push(0);
|
||||
break;
|
||||
case SYS_CLS:
|
||||
cmd_screen_clear();
|
||||
push(0);
|
||||
break;
|
||||
case SYS_GETCHAR: {
|
||||
int c = 0;
|
||||
// Blocking read for a valid key press
|
||||
while (1) {
|
||||
if ((inb(0x64) & 1)) { // Data available
|
||||
uint8_t sc = inb(0x60);
|
||||
if (!(sc & 0x80)) { // Key press
|
||||
if (sc < 128) {
|
||||
c = vm_scancode_map[sc];
|
||||
if (c) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
push(c);
|
||||
break;
|
||||
}
|
||||
case SYS_KB_HIT:
|
||||
// Simple check if data is waiting in keyboard controller
|
||||
push((inb(0x64) & 1) ? 1 : 0);
|
||||
break;
|
||||
case SYS_STRLEN: {
|
||||
int addr = pop();
|
||||
if (addr >= 0 && addr < VM_MEMORY_SIZE) {
|
||||
push(cli_strlen((char*)&memory[addr]));
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRCMP: {
|
||||
int a2 = pop();
|
||||
int a1 = pop();
|
||||
if (a1 >= 0 && a1 < VM_MEMORY_SIZE && a2 >= 0 && a2 < VM_MEMORY_SIZE) {
|
||||
push(cli_strcmp((char*)&memory[a1], (char*)&memory[a2]));
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRCPY: {
|
||||
int src = pop();
|
||||
int dest = pop();
|
||||
if (dest >= 0 && dest < VM_MEMORY_SIZE && src >= 0 && src < VM_MEMORY_SIZE) {
|
||||
cli_strcpy((char*)&memory[dest], (char*)&memory[src]);
|
||||
push(dest);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRCAT: {
|
||||
// Not implemented in cli_utils, skip
|
||||
pop(); pop(); push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_MEMSET: {
|
||||
int n = pop();
|
||||
int val = pop();
|
||||
int ptr = pop();
|
||||
if (ptr >= 0 && ptr + n <= VM_MEMORY_SIZE) {
|
||||
cli_memset(&memory[ptr], val, n);
|
||||
push(ptr);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_MEMCPY: {
|
||||
int n = pop();
|
||||
int src = pop();
|
||||
int dest = pop();
|
||||
if (dest >= 0 && dest+n <= VM_MEMORY_SIZE && src >= 0 && src+n <= VM_MEMORY_SIZE) {
|
||||
for(int i=0; i<n; i++) memory[dest+i] = memory[src+i];
|
||||
push(dest);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
// Simplified Heap (using top of memory growing down?)
|
||||
// For now, static allocation or mapped.
|
||||
// Let's implement a dummy malloc that returns an index into memory
|
||||
// Starting at 1024 (reserve first 1K for globals)
|
||||
case SYS_MALLOC: {
|
||||
static int heap_ptr = 1024;
|
||||
int size = pop();
|
||||
int res = heap_ptr;
|
||||
heap_ptr += size;
|
||||
if (heap_ptr >= VM_MEMORY_SIZE) {
|
||||
push(0); // OOM
|
||||
} else {
|
||||
push(res);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SYS_FREE:
|
||||
pop(); // No-op
|
||||
push(0);
|
||||
break;
|
||||
case SYS_RAND: {
|
||||
rand_next = rand_next * 1103515245 + 12345;
|
||||
push((unsigned int)(rand_next/65536) % 32768);
|
||||
break;
|
||||
}
|
||||
case SYS_SRAND: {
|
||||
rand_next = pop();
|
||||
push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_ABS: {
|
||||
int x = pop();
|
||||
push(x < 0 ? -x : x);
|
||||
break;
|
||||
}
|
||||
case SYS_MIN: {
|
||||
int b = pop();
|
||||
int a = pop();
|
||||
push(a < b ? a : b);
|
||||
break;
|
||||
}
|
||||
case SYS_MAX: {
|
||||
int b = pop();
|
||||
int a = pop();
|
||||
push(a > b ? a : b);
|
||||
break;
|
||||
}
|
||||
case SYS_POW: {
|
||||
int exp = pop();
|
||||
int base = pop();
|
||||
int res = 1;
|
||||
for(int i=0; i<exp; i++) res *= base;
|
||||
push(res);
|
||||
break;
|
||||
}
|
||||
case SYS_SQRT: {
|
||||
int n = pop();
|
||||
int res = 0;
|
||||
while ((res*res) <= n) res++;
|
||||
push(res - 1);
|
||||
break;
|
||||
}
|
||||
case SYS_SLEEP:
|
||||
cli_delay(pop() * 100000);
|
||||
push(0);
|
||||
break;
|
||||
// File IO - Not supported yet as FILE* cannot be easily passed to VM
|
||||
case SYS_FOPEN: pop(); pop(); push(0); break;
|
||||
case SYS_FCLOSE: pop(); push(0); break;
|
||||
case SYS_FREAD: pop(); pop(); pop(); pop(); push(0); break;
|
||||
case SYS_FWRITE: pop(); pop(); pop(); pop(); push(0); break;
|
||||
case SYS_FSEEK: pop(); pop(); pop(); push(0); break;
|
||||
case SYS_REMOVE: pop(); push(0); break;
|
||||
|
||||
case SYS_DRAW_PIXEL: {
|
||||
int color = pop();
|
||||
int y = pop();
|
||||
int x = pop();
|
||||
put_pixel(x, y, color);
|
||||
push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_DRAW_RECT: {
|
||||
int color = pop();
|
||||
int h = pop();
|
||||
int w = pop();
|
||||
int y = pop();
|
||||
int x = pop();
|
||||
|
||||
// Store for overlay
|
||||
if (vm_rect_count < MAX_VM_RECTS) {
|
||||
vm_rects[vm_rect_count].x = x;
|
||||
vm_rects[vm_rect_count].y = y;
|
||||
vm_rects[vm_rect_count].w = w;
|
||||
vm_rects[vm_rect_count].h = h;
|
||||
vm_rects[vm_rect_count].color = color;
|
||||
vm_rect_count++;
|
||||
}
|
||||
|
||||
// Trigger repaint
|
||||
wm_mark_dirty(x, y, w, h);
|
||||
|
||||
push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_GET_WIDTH: push(get_screen_width()); break;
|
||||
case SYS_GET_HEIGHT: push(get_screen_height()); break;
|
||||
|
||||
case SYS_ATOI: {
|
||||
int addr = pop();
|
||||
if (addr >= 0 && addr < VM_MEMORY_SIZE) {
|
||||
push(cli_atoi((char*)&memory[addr]));
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_ITOA: {
|
||||
int addr = pop();
|
||||
int val = pop();
|
||||
if (addr >= 0 && addr < VM_MEMORY_SIZE) {
|
||||
cli_itoa(val, (char*)&memory[addr]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SYS_PEEK: push(mem_read32(pop())); break;
|
||||
case SYS_POKE: {
|
||||
int val = pop();
|
||||
int addr = pop();
|
||||
mem_write32(addr, val);
|
||||
break;
|
||||
}
|
||||
case SYS_EXEC: pop(); push(-1); break; // Not impl
|
||||
case SYS_SYSTEM: pop(); push(-1); break; // Not impl
|
||||
|
||||
// --- New Builtins ---
|
||||
case SYS_ISALNUM: {
|
||||
int c = pop();
|
||||
push(((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')));
|
||||
break;
|
||||
}
|
||||
case SYS_ISALPHA: {
|
||||
int c = pop();
|
||||
push(((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')));
|
||||
break;
|
||||
}
|
||||
case SYS_ISDIGIT: {
|
||||
int c = pop();
|
||||
push((c >= '0' && c <= '9'));
|
||||
break;
|
||||
}
|
||||
case SYS_TOLOWER: {
|
||||
int c = pop();
|
||||
if (c >= 'A' && c <= 'Z') push(c + 32);
|
||||
else push(c);
|
||||
break;
|
||||
}
|
||||
case SYS_TOUPPER: {
|
||||
int c = pop();
|
||||
if (c >= 'a' && c <= 'z') push(c - 32);
|
||||
else push(c);
|
||||
break;
|
||||
}
|
||||
case SYS_STRNCPY: {
|
||||
int n = pop();
|
||||
int src = pop();
|
||||
int dest = pop();
|
||||
if (dest >= 0 && dest+n <= VM_MEMORY_SIZE && src >= 0 && src+n <= VM_MEMORY_SIZE) {
|
||||
char *d = (char*)&memory[dest];
|
||||
char *s = (char*)&memory[src];
|
||||
int i;
|
||||
for (i = 0; i < n && s[i] != '\0'; i++) d[i] = s[i];
|
||||
for ( ; i < n; i++) d[i] = '\0';
|
||||
push(dest);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRNCAT: {
|
||||
int n = pop();
|
||||
int src = pop();
|
||||
int dest = pop();
|
||||
if (dest >= 0 && dest < VM_MEMORY_SIZE && src >= 0 && src < VM_MEMORY_SIZE) {
|
||||
char *d = (char*)&memory[dest];
|
||||
char *s = (char*)&memory[src];
|
||||
int d_len = 0; while (d[d_len]) d_len++;
|
||||
int i;
|
||||
for (i = 0; i < n && s[i] != '\0'; i++) {
|
||||
if (dest + d_len + i < VM_MEMORY_SIZE) d[d_len + i] = s[i];
|
||||
}
|
||||
if (dest + d_len + i < VM_MEMORY_SIZE) d[d_len + i] = '\0';
|
||||
push(dest);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRNCMP: {
|
||||
int n = pop();
|
||||
int s2 = pop();
|
||||
int s1 = pop();
|
||||
if (s1 >= 0 && s1 < VM_MEMORY_SIZE && s2 >= 0 && s2 < VM_MEMORY_SIZE) {
|
||||
char *p1 = (char*)&memory[s1];
|
||||
char *p2 = (char*)&memory[s2];
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i] || p1[i] == '\0' || p2[i] == '\0') {
|
||||
res = (unsigned char)p1[i] - (unsigned char)p2[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
push(res);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRSTR: {
|
||||
int needle = pop();
|
||||
int haystack = pop();
|
||||
if (haystack >= 0 && haystack < VM_MEMORY_SIZE && needle >= 0 && needle < VM_MEMORY_SIZE) {
|
||||
char *h = (char*)&memory[haystack];
|
||||
char *n = (char*)&memory[needle];
|
||||
int h_len = 0; while(h[h_len]) h_len++;
|
||||
int n_len = 0; while(n[n_len]) n_len++;
|
||||
int found_idx = -1;
|
||||
for (int i = 0; i <= h_len - n_len; i++) {
|
||||
bool match = true;
|
||||
for (int j = 0; j < n_len; j++) {
|
||||
if (h[i+j] != n[j]) { match = false; break; }
|
||||
}
|
||||
if (match) { found_idx = haystack + i; break; }
|
||||
}
|
||||
push(found_idx != -1 ? found_idx : 0); // Return ptr or null (0)
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_STRRCHR: {
|
||||
int c = pop();
|
||||
int s = pop();
|
||||
if (s >= 0 && s < VM_MEMORY_SIZE) {
|
||||
char *str = (char*)&memory[s];
|
||||
int len = 0; while(str[len]) len++;
|
||||
int found_idx = 0;
|
||||
for (int i = len; i >= 0; i--) {
|
||||
if (str[i] == c) {
|
||||
found_idx = s + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
push(found_idx);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
case SYS_MEMMOVE: {
|
||||
int n = pop();
|
||||
int src = pop();
|
||||
int dest = pop();
|
||||
if (dest >= 0 && dest+n <= VM_MEMORY_SIZE && src >= 0 && src+n <= VM_MEMORY_SIZE) {
|
||||
uint8_t *d = &memory[dest];
|
||||
uint8_t *s = &memory[src];
|
||||
if (d < s) {
|
||||
for (int i = 0; i < n; i++) d[i] = s[i];
|
||||
} else {
|
||||
for (int i = n - 1; i >= 0; i--) d[i] = s[i];
|
||||
}
|
||||
push(dest);
|
||||
} else push(0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
cmd_write("VM: Unknown Syscall\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int vm_exec(const uint8_t *code, int code_size) {
|
||||
if (code_size < 8) return -1;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
if (code[i] != VM_MAGIC[i]) return -1;
|
||||
}
|
||||
|
||||
vm_reset();
|
||||
|
||||
// Reset Graphics Overlay
|
||||
vm_rect_count = 0;
|
||||
wm_custom_paint_hook = vm_paint_overlay;
|
||||
|
||||
// Copy data segment if any?
|
||||
// For this simple VM, we will copy the ENTIRE bytecode to the start of memory
|
||||
// So code and data share the same space (Von Neumann)
|
||||
// But we need to be careful not to overwrite running code if we write to it
|
||||
|
||||
// Safety check
|
||||
if (code_size > VM_MEMORY_SIZE) {
|
||||
cmd_write("VM Error: Binary too large\n");
|
||||
wm_custom_paint_hook = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Load program into memory at address 0
|
||||
cli_memset(memory, 0, VM_MEMORY_SIZE);
|
||||
for(int i=0; i<code_size; i++) memory[i] = code[i];
|
||||
|
||||
int pc = 8; // Skip header
|
||||
|
||||
while (pc < code_size) {
|
||||
uint8_t op = memory[pc++];
|
||||
|
||||
// DEBUGGING
|
||||
/*
|
||||
cmd_write("PC:"); cmd_write_int(pc-1);
|
||||
cmd_write(" OP:"); cmd_write_int(op);
|
||||
cmd_write(" SP:"); cmd_write_int(sp);
|
||||
cmd_write("\n");
|
||||
*/
|
||||
|
||||
switch (op) {
|
||||
case OP_HALT:
|
||||
wm_custom_paint_hook = NULL;
|
||||
return 0;
|
||||
case OP_IMM: {
|
||||
int val = 0;
|
||||
val |= memory[pc++];
|
||||
val |= memory[pc++] << 8;
|
||||
val |= memory[pc++] << 16;
|
||||
val |= memory[pc++] << 24;
|
||||
push(val);
|
||||
break;
|
||||
}
|
||||
case OP_LOAD: { // Read 32-bit int from absolute address
|
||||
int addr = 0;
|
||||
addr |= memory[pc++];
|
||||
addr |= memory[pc++] << 8;
|
||||
addr |= memory[pc++] << 16;
|
||||
addr |= memory[pc++] << 24;
|
||||
push(mem_read32(addr));
|
||||
break;
|
||||
}
|
||||
case OP_STORE: {
|
||||
int addr = 0;
|
||||
addr |= memory[pc++];
|
||||
addr |= memory[pc++] << 8;
|
||||
addr |= memory[pc++] << 16;
|
||||
addr |= memory[pc++] << 24;
|
||||
mem_write32(addr, pop());
|
||||
break;
|
||||
}
|
||||
case OP_LOAD8: { // Read byte
|
||||
int addr = 0;
|
||||
addr |= memory[pc++];
|
||||
addr |= memory[pc++] << 8;
|
||||
addr |= memory[pc++] << 16;
|
||||
addr |= memory[pc++] << 24;
|
||||
if (addr >= 0 && addr < VM_MEMORY_SIZE) push(memory[addr]);
|
||||
else push(0);
|
||||
break;
|
||||
}
|
||||
case OP_STORE8: {
|
||||
int addr = 0;
|
||||
addr |= memory[pc++];
|
||||
addr |= memory[pc++] << 8;
|
||||
addr |= memory[pc++] << 16;
|
||||
addr |= memory[pc++] << 24;
|
||||
int val = pop();
|
||||
if (addr >= 0 && addr < VM_MEMORY_SIZE) memory[addr] = (uint8_t)val;
|
||||
break;
|
||||
}
|
||||
case OP_ADD: push(pop() + pop()); break;
|
||||
case OP_SUB: { int b=pop(); int a=pop(); push(a-b); } break;
|
||||
case OP_MUL: push(pop() * pop()); break;
|
||||
case OP_DIV: { int b=pop(); int a=pop(); push(b==0?0:a/b); } break;
|
||||
case OP_PRINT: cmd_write_int(pop()); cmd_write("\n"); break;
|
||||
case OP_PRITC: { char c=(char)pop(); char s[2]={c,0}; cmd_write(s); } break;
|
||||
case OP_JMP: {
|
||||
int addr = 0;
|
||||
addr |= memory[pc++];
|
||||
addr |= memory[pc++] << 8;
|
||||
addr |= memory[pc++] << 16;
|
||||
addr |= memory[pc++] << 24;
|
||||
pc = addr;
|
||||
break;
|
||||
}
|
||||
case OP_JZ: {
|
||||
int addr = 0;
|
||||
addr |= memory[pc++];
|
||||
addr |= memory[pc++] << 8;
|
||||
addr |= memory[pc++] << 16;
|
||||
addr |= memory[pc++] << 24;
|
||||
if (pop() == 0) pc = addr;
|
||||
break;
|
||||
}
|
||||
case OP_EQ: push(pop() == pop()); break;
|
||||
case OP_NEQ: push(pop() != pop()); break;
|
||||
case OP_LT: { int b=pop(); int a=pop(); push(a<b); } break;
|
||||
case OP_GT: { int b=pop(); int a=pop(); push(a>b); } break;
|
||||
case OP_LE: { int b=pop(); int a=pop(); push(a<=b); } break;
|
||||
case OP_GE: { int b=pop(); int a=pop(); push(a>=b); } break;
|
||||
|
||||
case OP_SYSCALL: {
|
||||
int id = 0;
|
||||
id |= memory[pc++];
|
||||
id |= memory[pc++] << 8;
|
||||
id |= memory[pc++] << 16;
|
||||
id |= memory[pc++] << 24;
|
||||
vm_syscall(id);
|
||||
break;
|
||||
}
|
||||
case OP_PUSH_PTR: {
|
||||
// Push immediate value (pointer)
|
||||
// This is same as IMM but semantically distinct for future use
|
||||
int val = 0;
|
||||
val |= memory[pc++];
|
||||
val |= memory[pc++] << 8;
|
||||
val |= memory[pc++] << 16;
|
||||
val |= memory[pc++] << 24;
|
||||
push(val);
|
||||
break;
|
||||
}
|
||||
case OP_POP:
|
||||
// cmd_write("DEBUG: POP\n");
|
||||
pop();
|
||||
break;
|
||||
default:
|
||||
wm_custom_paint_hook = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
wm_custom_paint_hook = NULL;
|
||||
return 0;
|
||||
}
|
||||
108
src/kernel/vm.h
Normal file
108
src/kernel/vm.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#ifndef VM_H
|
||||
#define VM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Simple Stack-Based VM
|
||||
// Header: "BREWEXE" (7 bytes) + Version (1 byte)
|
||||
|
||||
#define VM_MAGIC "BREWEXE"
|
||||
#define VM_STACK_SIZE 256
|
||||
#define VM_MEMORY_SIZE (64 * 1024) // 64KB RAM
|
||||
|
||||
typedef enum {
|
||||
OP_HALT = 0,
|
||||
OP_IMM, // Push immediate (int32)
|
||||
OP_LOAD, // Load from memory (addr) - int32
|
||||
OP_STORE, // Store to memory (addr) - int32
|
||||
OP_ADD, // +
|
||||
OP_SUB, // -
|
||||
OP_MUL, // *
|
||||
OP_DIV, // /
|
||||
OP_PRINT, // Deprecated
|
||||
OP_PRITC, // Deprecated
|
||||
OP_JMP, // Jump (addr)
|
||||
OP_JZ, // Jump if zero
|
||||
OP_EQ, // ==
|
||||
OP_NEQ, // !=
|
||||
OP_LT, // <
|
||||
OP_GT, // >
|
||||
OP_LE, // <=
|
||||
OP_GE, // >=
|
||||
OP_SYSCALL, // Call system function (id)
|
||||
OP_LOAD8, // Load byte
|
||||
OP_STORE8, // Store byte
|
||||
OP_PUSH_PTR, // Push pointer to data segment (relative to start of mem)
|
||||
OP_POP // Pop and discard top of stack
|
||||
} OpCode;
|
||||
|
||||
// Syscall IDs
|
||||
typedef enum {
|
||||
SYS_EXIT = 0,
|
||||
SYS_PRINT_INT,
|
||||
SYS_PRINT_CHAR,
|
||||
SYS_PRINT_STR,
|
||||
SYS_NL,
|
||||
SYS_CLS,
|
||||
SYS_GETCHAR,
|
||||
SYS_STRLEN,
|
||||
SYS_STRCMP,
|
||||
SYS_STRCPY,
|
||||
SYS_STRCAT,
|
||||
SYS_MEMSET,
|
||||
SYS_MEMCPY,
|
||||
SYS_MALLOC,
|
||||
SYS_FREE,
|
||||
SYS_RAND,
|
||||
SYS_SRAND,
|
||||
SYS_ABS,
|
||||
SYS_MIN,
|
||||
SYS_MAX,
|
||||
SYS_POW,
|
||||
SYS_SQRT,
|
||||
SYS_SLEEP,
|
||||
SYS_FOPEN,
|
||||
SYS_FCLOSE,
|
||||
SYS_FREAD,
|
||||
SYS_FWRITE,
|
||||
SYS_FSEEK,
|
||||
SYS_REMOVE,
|
||||
SYS_DRAW_PIXEL,
|
||||
SYS_DRAW_RECT,
|
||||
SYS_DRAW_LINE,
|
||||
SYS_DRAW_TEXT,
|
||||
SYS_GET_WIDTH,
|
||||
SYS_GET_HEIGHT,
|
||||
SYS_GET_TIME,
|
||||
SYS_KB_HIT,
|
||||
SYS_MOUSE_X,
|
||||
SYS_MOUSE_Y,
|
||||
SYS_MOUSE_STATE,
|
||||
SYS_PLAY_SOUND,
|
||||
SYS_ATOI,
|
||||
SYS_ITOA,
|
||||
SYS_PEEK,
|
||||
SYS_POKE,
|
||||
SYS_EXEC,
|
||||
SYS_SYSTEM,
|
||||
SYS_STRCHR,
|
||||
SYS_MEMCMP,
|
||||
SYS_GET_DATE,
|
||||
// New Builtins
|
||||
SYS_ISALNUM,
|
||||
SYS_ISALPHA,
|
||||
SYS_ISDIGIT,
|
||||
SYS_TOLOWER,
|
||||
SYS_TOUPPER,
|
||||
SYS_STRNCPY,
|
||||
SYS_STRNCAT,
|
||||
SYS_STRNCMP,
|
||||
SYS_STRSTR,
|
||||
SYS_STRRCHR,
|
||||
SYS_MEMMOVE
|
||||
} SyscallID;
|
||||
|
||||
int vm_exec(const uint8_t *code, int code_size);
|
||||
|
||||
#endif
|
||||
|
|
@ -19,6 +19,9 @@ static int mx = 400, my = 300; // Mouse Pos
|
|||
static int prev_mx = 400, prev_my = 300; // Previous mouse position
|
||||
static bool start_menu_open = false;
|
||||
|
||||
// Hook definition
|
||||
void (*wm_custom_paint_hook)(void) = NULL;
|
||||
|
||||
// Dragging State
|
||||
static bool is_dragging = false;
|
||||
static Window *drag_window = NULL;
|
||||
|
|
@ -330,6 +333,11 @@ void wm_paint(void) {
|
|||
draw_string(8, menu_y + 195, "Restart", COLOR_BLACK);
|
||||
}
|
||||
|
||||
// Custom Overlay (VM Graphics)
|
||||
if (wm_custom_paint_hook) {
|
||||
wm_custom_paint_hook();
|
||||
}
|
||||
|
||||
// 7. Mouse cursor (draw last so it's on top)
|
||||
draw_cursor(mx, my);
|
||||
last_cursor_x = mx;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ void wm_refresh(void);
|
|||
void wm_paint(void);
|
||||
void wm_timer_tick(void);
|
||||
|
||||
// Hook for external rendering (e.g. VM overlay)
|
||||
extern void (*wm_custom_paint_hook)(void);
|
||||
|
||||
// Drawing helpers
|
||||
void draw_bevel_rect(int x, int y, int w, int h, bool sunken);
|
||||
void draw_button(int x, int y, int w, int h, const char *text, bool pressed);
|
||||
|
|
|
|||
Loading…
Reference in a new issue