diff --git a/Makefile b/Makefile index 0ead99f..8401064 100644 --- a/Makefile +++ b/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 diff --git a/README.md b/README.md index 49a3fac..fbeb795 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/brewos.iso b/brewos.iso index 777e497..303cf04 100644 Binary files a/brewos.iso and b/brewos.iso differ diff --git a/build/brewos.elf b/build/brewos.elf index b5e8b88..a339972 100755 Binary files a/build/brewos.elf and b/build/brewos.elf differ diff --git a/build/cli_apps/cc.o b/build/cli_apps/cc.o new file mode 100644 index 0000000..4d79d89 Binary files /dev/null and b/build/cli_apps/cc.o differ diff --git a/build/cmd.o b/build/cmd.o index f34cd16..3a4dbc7 100644 Binary files a/build/cmd.o and b/build/cmd.o differ diff --git a/build/graphics.o b/build/graphics.o index 1ae1932..e063cc7 100644 Binary files a/build/graphics.o and b/build/graphics.o differ diff --git a/build/vm.o b/build/vm.o new file mode 100644 index 0000000..7a0fd7a Binary files /dev/null and b/build/vm.o differ diff --git a/build/wm.o b/build/wm.o index 0e2afa5..6f0cdd1 100644 Binary files a/build/wm.o and b/build/wm.o differ diff --git a/iso_root/brewos.elf b/iso_root/brewos.elf index b5e8b88..a339972 100755 Binary files a/iso_root/brewos.elf and b/iso_root/brewos.elf differ diff --git a/src/kernel/about.c b/src/kernel/about.c index b8f82bb..1086782 100644 --- a/src/kernel/about.c +++ b/src/kernel/about.c @@ -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); diff --git a/src/kernel/cli_apps/about.c b/src/kernel/cli_apps/about.c index 668a770..b051ccb 100644 --- a/src/kernel/cli_apps/about.c +++ b/src/kernel/cli_apps/about.c @@ -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"); } diff --git a/src/kernel/cli_apps/cc.c b/src/kernel/cli_apps/cc.c new file mode 100644 index 0000000..3106fbf --- /dev/null +++ b/src/kernel/cli_apps/cc.c @@ -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= 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 \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> 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"); + } +} \ No newline at end of file diff --git a/src/kernel/cli_apps/cli_apps.h b/src/kernel/cli_apps/cli_apps.h index 129c823..ecbceb0 100644 --- a/src/kernel/cli_apps/cli_apps.h +++ b/src/kernel/cli_apps/cli_apps.h @@ -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 diff --git a/src/kernel/cmd.c b/src/kernel/cmd.c index 8934eeb..ede96ee 100644 --- a/src/kernel/cmd.c +++ b/src/kernel/cmd.c @@ -14,6 +14,7 @@ #include #include #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) { diff --git a/src/kernel/cmd.h b/src/kernel/cmd.h index c495792..ddd8684 100644 --- a/src/kernel/cmd.h +++ b/src/kernel/cmd.h @@ -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 \ No newline at end of file diff --git a/src/kernel/vm.c b/src/kernel/vm.c new file mode 100644 index 0000000..057073c --- /dev/null +++ b/src/kernel/vm.c @@ -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= 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= 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= 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(ab); } 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; +} \ No newline at end of file diff --git a/src/kernel/vm.h b/src/kernel/vm.h new file mode 100644 index 0000000..f7de263 --- /dev/null +++ b/src/kernel/vm.h @@ -0,0 +1,108 @@ +#ifndef VM_H +#define VM_H + +#include +#include + +// 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 \ No newline at end of file diff --git a/src/kernel/wm.c b/src/kernel/wm.c index e704837..ea0684d 100644 --- a/src/kernel/wm.c +++ b/src/kernel/wm.c @@ -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; diff --git a/src/kernel/wm.h b/src/kernel/wm.h index 04073f9..13bf266 100644 --- a/src/kernel/wm.h +++ b/src/kernel/wm.h @@ -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);