#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 static int vm_heap_ptr = 8192; // --- 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); vm_heap_ptr = 8192; } 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]); } push(0); break; } case SYS_PEEK: push(mem_read32(pop())); break; case SYS_POKE: { int val = pop(); int addr = pop(); mem_write32(addr, val); push(0); 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; }