Emscripten Paint Works!!!!!!

This commit is contained in:
zongor 2025-10-14 15:27:29 -07:00
parent dcf348f577
commit 0c72525755
11 changed files with 336 additions and 145 deletions

View File

@ -55,16 +55,15 @@ endif
# --- EMSCRIPTEN-SPECIFIC --- # --- EMSCRIPTEN-SPECIFIC ---
ifeq ($(PLATFORM), emscripten) ifeq ($(PLATFORM), emscripten)
LDFLAGS += -s USE_SDL=2 -s WASM=1 \ LDFLAGS += -s USE_SDL=2\
-s ASYNCIFY \
-s ALLOW_MEMORY_GROWTH=1 \ -s ALLOW_MEMORY_GROWTH=1 \
-s MAX_WEBGL_VERSION=2 \ --preload-file test/paint.rom@paint.rom
--preload-file test/paint-bw.rom@paint.rom \
-s INITIAL_MEMORY=16MB
# For release: optimize, strip debug, minimize size # For release: optimize, strip debug, minimize size
ifeq ($(BUILD_MODE), release) ifeq ($(BUILD_MODE), release)
PLATFORM_CFLAGS += -O2 -flto PLATFORM_CFLAGS += -O2
LDFLAGS += -O2 -flto LDFLAGS += -O2
endif endif
# Output: HTML + JS + WASM # Output: HTML + JS + WASM

View File

@ -121,29 +121,13 @@ i32 screen_write(void *data, const u8 *buffer, u32 size) {
return -1; return -1;
} }
if (!screen->screen_buffer) {
screen->screen_buffer = (u8*)buffer;
}
// Update texture with new frame data // Update texture with new frame data
SDL_UpdateTexture(screen->texture, NULL, buffer, screen->width); SDL_UpdateTexture(screen->texture, NULL, buffer, screen->width);
screen->update = true;
// Clear and render
SDL_RenderClear(screen->renderer);
SDL_Rect output_rect;
SDL_RenderGetViewport(screen->renderer, &output_rect);
// Calculate aspect ratio preserving scaling
float scale_x = (float)output_rect.w / screen->width;
float scale_y = (float)output_rect.h / screen->height;
float scale = SDL_min(scale_x, scale_y);
SDL_Rect dstrect = {
(i32)((output_rect.w - screen->width * scale) / 2),
(i32)((output_rect.h - screen->height * scale) / 2),
(i32)(screen->width * scale),
(i32)(screen->height * scale)
};
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &dstrect);
SDL_RenderPresent(screen->renderer);
return 0; return 0;
} }
@ -210,54 +194,6 @@ i32 mouse_read(void *data, u8 *buffer, u32 size) {
if (size < 12) return -1; if (size < 12) return -1;
SDL_PumpEvents();
SDL_Event event;
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;
}
}
}
u8 *info = (u8 *)buffer; u8 *info = (u8 *)buffer;
memcpy(&info[0], &mouse_data->x, sizeof(u32)); memcpy(&info[0], &mouse_data->x, sizeof(u32));
memcpy(&info[4], &mouse_data->y, sizeof(u32)); memcpy(&info[4], &mouse_data->y, sizeof(u32));

View File

@ -8,6 +8,8 @@ typedef struct screen_device_data_s {
u32 height; u32 height;
u32 pos; u32 pos;
u32 size; u32 size;
u8 *screen_buffer;
bool update;
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Texture *texture; SDL_Texture *texture;

View File

@ -1,5 +1,5 @@
#include "../../vm/vm.h"
#include "../../vm/device.h" #include "../../vm/device.h"
#include "../../vm/vm.h"
#include "devices.h" #include "devices.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <emscripten.h> #include <emscripten.h>
@ -31,10 +31,171 @@ static DeviceOps console_device_ops = {
static ScreenDeviceData screen_data = {0}; static ScreenDeviceData screen_data = {0};
static MouseDeviceData mouse_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 "<invalid-opcode>";
}
const char *name = names[op];
return name ? name : "<unknown-opcode>";
}
void mainloop() { 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)) { if (!step_vm(&vm)) {
emscripten_cancel_main_loop(); emscripten_cancel_main_loop();
printf("VM execution completed\n"); 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
} }
} }

View File

@ -89,29 +89,13 @@ i32 screen_write(void *data, const u8 *buffer, u32 size) {
if (size > screen->size * sizeof(u8)) { if (size > screen->size * sizeof(u8)) {
return -1; return -1;
} }
if (!screen->screen_buffer) {
screen->screen_buffer = (u8*)buffer;
}
// Update texture with new frame data // Update texture with new frame data
SDL_UpdateTexture(screen->texture, NULL, buffer, screen->width); SDL_UpdateTexture(screen->texture, NULL, buffer, screen->width);
screen->update = true;
// Clear and render
SDL_RenderClear(screen->renderer);
SDL_Rect output_rect;
SDL_RenderGetViewport(screen->renderer, &output_rect);
// Calculate aspect ratio preserving scaling
float scale_x = (float)output_rect.w / screen->width;
float scale_y = (float)output_rect.h / screen->height;
float scale = SDL_min(scale_x, scale_y);
SDL_Rect dstrect = {
(i32)((output_rect.w - screen->width * scale) / 2),
(i32)((output_rect.h - screen->height * scale) / 2),
(i32)(screen->width * scale),
(i32)(screen->height * scale)
};
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &dstrect);
SDL_RenderPresent(screen->renderer);
return 0; return 0;
} }

View File

@ -10,6 +10,8 @@ typedef struct screen_device_data_s {
u32 height; u32 height;
u32 pos; u32 pos;
u32 size; u32 size;
u8 *screen_buffer;
u32 update;
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Texture *texture; SDL_Texture *texture;

View File

@ -394,8 +394,8 @@ i32 main(i32 argc, char *argv[]) {
screen_data.height = 480; screen_data.height = 480;
screen_data.size = screen_data.width * screen_data.height; screen_data.size = screen_data.width * screen_data.height;
vm_register_device(&vm, "/dev/screen/0", "screen", vm_register_device(&vm, "/dev/screen/0", "screen", &screen_data,
&screen_data, &screen_ops); &screen_ops);
mouse_data.x = 0; mouse_data.x = 0;
mouse_data.y = 0; mouse_data.y = 0;
@ -411,11 +411,104 @@ i32 main(i32 argc, char *argv[]) {
vm_register_device(&vm, "/dev/keyboard/0", "keyboard", &keyboard_data, vm_register_device(&vm, "/dev/keyboard/0", "keyboard", &keyboard_data,
&keyboard_ops); &keyboard_ops);
SDL_Event event;
bool running = true; bool running = true;
SDL_PumpEvents();
while (running) { while (running) {
if (!step_vm(&vm)) { while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false; running = false;
break; break;
// 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) {
if (!step_vm(&vm)) {
running = false;
}
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) {
// Clear and render
SDL_RenderClear(screen_data.renderer);
SDL_Rect output_rect;
SDL_RenderGetViewport(screen_data.renderer, &output_rect);
// Calculate aspect ratio preserving scaling
float scale_x = (float)output_rect.w / screen_data.width;
float scale_y = (float)output_rect.h / screen_data.height;
float scale = SDL_min(scale_x, scale_y);
SDL_Rect dstrect = {
(i32)((output_rect.w - screen_data.width * scale) / 2),
(i32)((output_rect.h - screen_data.height * scale) / 2),
(i32)(screen_data.width * scale),
(i32)(screen_data.height * scale)};
SDL_RenderCopy(screen_data.renderer, screen_data.texture, NULL,
&dstrect);
SDL_RenderPresent(screen_data.renderer);
}
screen_data.update = false; // Reset flag after rendering
} }
} }
} else { } else {

View File

@ -1,52 +1,68 @@
((code ((code
(label main (label main
(load-immediate $30 4) ; fat ptr size
; Open screen ; Open screen
; use load immediate because it is a pointer to a string, not a value ; use load immediate because it is a pointer to a string, not a value
(load-immediate $0 &screen-namespace) (load-immediate $0 &screen-namespace)
(load-immediate $11 0) (load-immediate $11 0)
(syscall OPEN $0 $11) (syscall OPEN $0 $0 $11)
(load-immediate $16 1) ; device info call (load-immediate $16 1) ; device info call
(load-immediate $17 16) ; sizeof screen device info (load-immediate $17 16) ; sizeof screen device info
(malloc $18 $17) (malloc $18 $17)
(syscall IOCTL $0 $16 $18) (syscall IOCTL $0 $16 $18)
(load-immediate $1 12) ; offset for width (load-immediate $1 12) ; offset for width
(add-nat $19 $18 $1) (add-nat $19 $18 $1)
(get $20 $19) ; load width (get $20 $19) ; load width
(load-immediate $1 8) ; offset for size (load-immediate $1 8) ; offset for size
(add-nat $19 $18 $1) (add-nat $19 $18 $1)
(get $22 $19) ; load size (get $22 $19) ; load size
(malloc $21 $22) ; malloc frame buffer (malloc $21 $22) ; malloc frame buffer
(load-immediate $16 &mouse-namespace) (load-immediate $16 &mouse-namespace)
(load-immediate $3 12) ; malloc sizeof mouse data (load-immediate $3 12) ; malloc sizeof mouse data
(malloc $4 $3) (malloc $4 $3)
(syscall OPEN $16 $16 $4)
(load-immediate $14 20) ; box size (push $21)
(push $20)
(load $1 &BLACK)
(push $1)
(load-immediate $12 1)
(push $12)
(load-immediate $13 1)
(push $13)
(call &draw-outlined-swatch)
(push $21)
(push $20)
(load $1 &WHITE)
(push $1)
(load-immediate $12 21)
(push $12)
(load-immediate $13 1)
(push $13)
(call &draw-outlined-swatch)
(syscall WRITE $0 $21 $22) (syscall WRITE $0 $21 $22)
(label draw-loop (label draw-loop
; load mouse click data ; load mouse click data
(syscall READ $16 $2 $3 $4) (syscall READ $16 $2 $3 $4)
(load-immediate $5 4) ; offset for x
(add-nat $6 $5 $2)
(get $7 $6) ; load x
(load-immediate $5 8) ; offset for y
(add-nat $6 $5 $2)
(get $8 $6) ; load y
(load-immediate $5 12) ; offset for btn1 (load-immediate $5 12) ; offset for btn1
(add-nat $6 $5 $2) (add-nat $6 $5 $2)
(get-8 $9 $6) ; load btn1 pressed (get-8 $9 $6) ; load btn1 pressed
(load-immediate $14 20) ; box size
(jump-eq-nat &draw-loop $9 $11)
(load-immediate $5 4) ; offset for x
(add-nat $6 $5 $2)
(get $7 $6) ; load x
(load-immediate $5 8) ; offset for y
(add-nat $6 $5 $2)
(get $8 $6) ; load y
(load-immediate $5 13) ; offset for btn2 (load-immediate $5 13) ; offset for btn2
(add-nat $6 $5 $2) (add-nat $6 $5 $2)
(get-8 $10 $6) ; load btn2 pressed (get-8 $10 $6) ; load btn2 pressed
@ -61,7 +77,6 @@
(load-immediate $13 1) (load-immediate $13 1)
(push $13) (push $13)
(call &draw-outlined-swatch) (call &draw-outlined-swatch)
(syscall WRITE $0 $21 $22)
(push $14) ; box_size (20) (push $14) ; box_size (20)
(push $13) ; box_y (push $13) ; box_y
@ -93,14 +108,17 @@
(syscall WRITE $0 $21 $22) (syscall WRITE $0 $21 $22)
(jump-eq-nat &draw-loop $9 $11)
(mul-nat $15 $8 $20) ; $15 = y * width
(add-nat $15 $15 $7) ; $15 += x
(add-nat $15 $21 $15) ; $15 = base + pixel_offset
(add-nat $15 $15 $30)
(load $22 &SELECTED-COLOR) ; color (load $22 &SELECTED-COLOR) ; color
(store-8 $15 $22) ; draw color at screen [x,y] (load-immediate $1 5) ; size of brush
(push $21) ;base
(push $20) ;width
(push $22) ; color
(push $7) ;x
(push $8) ;y
(push $1)
(push $1)
(call &draw-box)
(jump-eq-nat &draw-loop $10 $11)) (jump-eq-nat &draw-loop $10 $11))
@ -208,9 +226,7 @@
(label draw-box-outer (label draw-box-outer
(add-int $6 $4 $12) ; $6 = row end = current + width (add-int $6 $4 $12) ; $6 = row end = current + width
(register-move $7 $4) ; $7 = pixel pointer (register-move $7 $4) ; $7 = pixel pointer
(memset-8 $7 $3 $12) ; draw row
(memset-8 $7 $3 $12)
(add-int $4 $4 $2) ; next row (+= 640) (add-int $4 $4 $2) ; next row (+= 640)
(sub-int $5 $5 $1) ; decrement row count (sub-int $5 $5 $1) ; decrement row count
(jump-gt-int &draw-box-outer $5 0)) (jump-gt-int &draw-box-outer $5 0))
@ -222,4 +238,5 @@
(label BLACK 0) (label BLACK 0)
(label WHITE 255) (label WHITE 255)
(label DARK-GRAY 73) (label DARK-GRAY 73)
(label GRAY 146))) (label GRAY 146)
(label LIGHT-GRAY 182)))

Binary file not shown.

View File

@ -36,9 +36,6 @@
(load-immediate $5 12) ; offset for btn1 (load-immediate $5 12) ; offset for btn1
(add-nat $6 $5 $2) (add-nat $6 $5 $2)
(get-8 $9 $6) ; load btn1 pressed (get-8 $9 $6) ; load btn1 pressed
(load-immediate $5 13) ; offset for btn2
(add-nat $6 $5 $2)
(get-8 $10 $6) ; load btn2 pressed
(load-immediate $14 20) ; box size (load-immediate $14 20) ; box size
@ -253,7 +250,7 @@
(push $1) (push $1)
(call &draw-box) (call &draw-box)
(jump-eq-nat &draw-loop $10 $11)) (jump-eq-nat &draw-loop $11 $11))
; Flush and halt ; Flush and halt
(halt)) (halt))

Binary file not shown.