#include "../../vm/device.h" #include "../../vm/vm.h" #include "devices.h" #include #include #include #include VM vm = {0}; static DeviceOps screen_ops = {.open = screen_open, .read = screen_read, .write = screen_write, .close = screen_close, .ioctl = screen_ioctl}; static DeviceOps mouse_ops = {.open = mouse_open, .read = mouse_read, .write = mouse_write, .close = mouse_close, .ioctl = nil}; static DeviceOps console_device_ops = { .open = console_open, .read = console_read, .write = console_write, .close = console_close, .ioctl = console_ioctl, }; static ScreenDeviceData screen_data = {0}; static MouseDeviceData mouse_data = {0}; const char *opcode_to_string(Opcode op) { static const char *names[] = {[OP_HALT] = "halt", [OP_JMP] = "jump", [OP_JMPF] = "jump-if-flag", [OP_CALL] = "call", [OP_RETURN] = "return", [OP_LOAD_IMM] = "load-immediate", [OP_GET_8] = "get-8", [OP_GET_16] = "get-16", [OP_GET_32] = "get", [OP_LOAD_8] = "load-8", [OP_LOAD_16] = "load-16", [OP_LOAD_32] = "load", [OP_STORE_8] = "store-8", [OP_STORE_16] = "store-16", [OP_STORE_32] = "store", [OP_PUT_8] = "put-8", [OP_PUT_16] = "put-16", [OP_PUT_32] = "put", [OP_MALLOC] = "malloc", [OP_MEMSET_8] = "memset-8", [OP_MEMSET_16] = "memset-16", [OP_MEMSET_32] = "memset-32", [OP_PUSH] = "push", [OP_POP] = "pop", [OP_REG_MOV] = "register-move", [OP_SYSCALL] = "syscall", [OP_SLL] = "bit-shift-left", [OP_SRL] = "bit-shift-right", [OP_SRE] = "bit-shift-re", [OP_BAND] = "bit-and", [OP_BOR] = "bit-or", [OP_BXOR] = "bit-xor", [OP_ADD_INT] = "add-int", [OP_SUB_INT] = "sub-int", [OP_MUL_INT] = "mul-int", [OP_DIV_INT] = "div-int", [OP_ADD_UINT] = "add-nat", [OP_SUB_UINT] = "sub-nat", [OP_MUL_UINT] = "mul-nat", [OP_DIV_UINT] = "div-nat", [OP_ADD_REAL] = "add-real", [OP_SUB_REAL] = "sub-real", [OP_MUL_REAL] = "mul-real", [OP_DIV_REAL] = "div-real", [OP_INT_TO_REAL] = "int-to-real", [OP_UINT_TO_REAL] = "nat-to-real", [OP_REAL_TO_INT] = "real-to-int", [OP_REAL_TO_UINT] = "real-to-nat", [OP_JEQ_INT] = "jump-eq-int", [OP_JNEQ_INT] = "jump-neq-int", [OP_JGT_INT] = "jump-gt-int", [OP_JLT_INT] = "jump-lt-int", [OP_JLE_INT] = "jump-le-int", [OP_JGE_INT] = "jump-ge-int", [OP_JEQ_UINT] = "jump-eq-nat", [OP_JNEQ_UINT] = "jump-neq-nat", [OP_JGT_UINT] = "jump-gt-nat", [OP_JLT_UINT] = "jump-lt-nat", [OP_JLE_UINT] = "jump-le-nat", [OP_JGE_UINT] = "jump-ge-nat", [OP_JEQ_REAL] = "jump-eq-real", [OP_JNEQ_REAL] = "jump-neq-real", [OP_JGE_REAL] = "jump-ge-real", [OP_JGT_REAL] = "jump-gt-real", [OP_JLT_REAL] = "jump-lt-real", [OP_JLE_REAL] = "jump-le-real", [OP_STRLEN] = "string-length", [OP_STREQ] = "string-eq", [OP_STRCAT] = "string-concat", [OP_STR_GET_CHAR] = "string-get-char", [OP_STR_FIND_CHAR] = "string-find-char", [OP_STR_SLICE] = "string-slice", [OP_INT_TO_STRING] = "int-to-string", [OP_UINT_TO_STRING] = "nat-to-string", [OP_REAL_TO_STRING] = "real-to-string", [OP_STRING_TO_INT] = "string-to-int", [OP_STRING_TO_UINT] = "string-to-nat", [OP_STRING_TO_REAL] = "string-to-real"}; if (op < 0 || op >= (int)(sizeof(names) / sizeof(names[0]))) { return ""; } const char *name = names[op]; return name ? name : ""; } void mainloop() { SDL_Event event; SDL_PumpEvents(); while (SDL_PollEvent(&event)) { switch (event.type) { // Mouse events case SDL_MOUSEMOTION: mouse_data.x = event.motion.x; mouse_data.y = event.motion.y; break; case SDL_MOUSEBUTTONDOWN: if (event.button.button == SDL_BUTTON_LEFT) mouse_data.btn1 = 1; if (event.button.button == SDL_BUTTON_RIGHT) mouse_data.btn2 = 1; if (event.button.button == SDL_BUTTON_MIDDLE) mouse_data.btn3 = 1; if (event.button.button == SDL_BUTTON_X1) mouse_data.btn4 = 1; break; case SDL_MOUSEBUTTONUP: if (event.button.button == SDL_BUTTON_LEFT) mouse_data.btn1 = 0; if (event.button.button == SDL_BUTTON_RIGHT) mouse_data.btn2 = 0; if (event.button.button == SDL_BUTTON_MIDDLE) mouse_data.btn3 = 0; if (event.button.button == SDL_BUTTON_X1) mouse_data.btn4 = 0; break; // Touch events (map to mouse_data as left-click equivalent) case SDL_FINGERMOTION: case SDL_FINGERDOWN: case SDL_FINGERUP: { float x = event.tfinger.x * 640; float y = event.tfinger.y * 480; mouse_data.x = (int)x; mouse_data.y = (int)y; // Only treat the first finger as mouse input (ignore multi-touch beyond 1 // finger) if (event.tfinger.fingerId == 0) { if (event.type == SDL_FINGERDOWN || event.type == SDL_FINGERMOTION) { mouse_data.btn1 = 1; } else if (event.type == SDL_FINGERUP) { mouse_data.btn1 = 0; } } break; } } } // Run VM for a fixed number of cycles or a time slice int cycles_this_frame = 0; int max_cycles_per_frame = 1000; // Adjust this value while (cycles_this_frame < max_cycles_per_frame) { // printf("%s\n", opcode_to_string(vm.code[vm.pc])); // REMOVE THIS LINE if (!step_vm(&vm)) { emscripten_cancel_main_loop(); return; } cycles_this_frame++; } // Render only if the screen buffer was updated AND at a reasonable rate if (screen_data.update) { if (screen_data.renderer && screen_data.texture) { SDL_RenderCopy(screen_data.renderer, screen_data.texture, NULL, NULL); SDL_RenderPresent(screen_data.renderer); } screen_data.update = false; // Reset flag after rendering } } bool loadVM(const char *filename, VM *vm) { FILE *file = fopen(filename, "rb"); if (!file) { printf("Failed to open ROM file: %s\n", filename); return false; } // Read VM state if (fread(&vm->pc, sizeof(u32), 1, file) != 1 || fread(&vm->cp, sizeof(u32), 1, file) != 1 || fread(&vm->fp, sizeof(u32), 1, file) != 1 || fread(&vm->sp, sizeof(u32), 1, file) != 1 || fread(&vm->rp, sizeof(u32), 1, file) != 1 || fread(&vm->mp, sizeof(u32), 1, file) != 1 || fread(&vm->dc, sizeof(u32), 1, file) != 1 || fread(&vm->flag, sizeof(i32), 1, file) != 1) { printf("Failed to read VM state\n"); fclose(file); return false; } // Read code section if (fread(vm->code, 1, vm->cp, file) != vm->cp) { printf("Failed to read code section\n"); fclose(file); return false; } // Read memory section if (fread(vm->memory, 1, vm->mp, file) != vm->mp) { printf("Failed to read memory section\n"); fclose(file); return false; } fclose(file); return true; } int main(int argc, char **argv) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL initialization failed: %s\n", SDL_GetError()); return 1; } SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); #ifdef __EMSCRIPTEN__ emscripten_set_canvas_element_size("#canvas", 640, 480); #endif loadVM("paint.rom", &vm); printf("VM loaded successfully\n"); // Initialize device data screen_data.width = 640; screen_data.height = 480; screen_data.size = 640 * 480; screen_data.window = NULL; screen_data.renderer = NULL; screen_data.texture = NULL; mouse_data.x = 0; mouse_data.y = 0; mouse_data.btn1 = 0; mouse_data.btn2 = 0; mouse_data.btn3 = 0; mouse_data.btn4 = 0; mouse_data.size = 12; // Register devices vm_register_device(&vm, "/dev/screen/0", "screen", &screen_data, &screen_ops); vm_register_device(&vm, "/dev/mouse/0", "mouse", &mouse_data, &mouse_ops); vm_register_device(&vm, "/dev/term/0", "terminal", NULL, &console_device_ops); // Set up main loop emscripten_set_main_loop(mainloop, 0, 1); return 0; }