aboutsummaryrefslogtreecommitdiffstats
path: root/Makefile
diff options
context:
space:
mode:
authorgbc dev <gbc@localhost>2026-09-22 15:57:06 +0200
committergbc dev <gbc@localhost>2026-09-22 15:57:06 +0200
commit6fb4fbbce99c08bd535d1b7e0f356864fb0c38a0 (patch)
tree7f0b249f4cfe640d80a7848d9012dc695d42d5c3 /Makefile
parentcore: make the machine loadable without a filesystem (diff)
downloadsl0pboy-6fb4fbbce99c08bd535d1b7e0f356864fb0c38a0.tar.gz
sl0pboy-6fb4fbbce99c08bd535d1b7e0f356864fb0c38a0.tar.xz
sl0pboy-6fb4fbbce99c08bd535d1b7e0f356864fb0c38a0.zip
wasm: run the emulator in a browser
`make wasm` builds the core to WebAssembly (~44KB stripped) and web/ is a front-end for it: drag-drop a ROM, pixelated canvas at any zoom, keyboard mapped like the native build, battery saves in localStorage. Built with zig cc, which carries its own wasm backend, linker and wasi-libc - so no emscripten dependency, just zig on PATH. Only the portable core goes in; render/kitty/control/main are terminal- and POSIX-bound and stay out. src/wasm.c is the browser's whole host layer: no terminal, no sockets, no files, and no frame pacing (the page's requestAnimationFrame owns that). The machine is static, the RGB888 -> RGBA conversion for putImageData happens in wasm, and nothing allocates per frame. The entire host interface is four WASI fd_* imports, pulled in by the cart info banner's fprintf; the page shims them to console.log in ~20 lines. Cartridge RAM *is* the save file here, so it's exported for the page to persist per cart title (every 5s and on unload). Because both builds now advance on the same frame boundary, the port is checked byte-for-byte against the native binary rather than by eyeballing a canvas - tools/wasm_check.mjs runs N frames in node and diffs the framebuffer against a --shot PPM: cgb-acid2 @60, dmg-acid2 @60, cpu_instrs @600, mem_timing-2 @700, pky.gbc @400 with an imported .sav, cgb-acid2 @120 through the full CGB BIOS boot animation -> all pixel-identical Also smoke-tested in headless chromium over CDP: core instantiates, ROM loads through the file input, canvas animates (7 distinct frames over 5.6s), keyboard reaches the joypad, 60fps, and a 32KB save lands in localStorage. No audio: the APU is still stubbed.
Diffstat (limited to 'Makefile')
-rw-r--r--Makefile26
1 files changed, 23 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 0223819..8b34825 100644
--- a/Makefile
+++ b/Makefile
@@ -3,14 +3,34 @@ CFLAGS := -std=c11 -O2 -Wall -Wextra -Wno-unused-parameter -g -D_POSIX_C_SOURCE
LDFLAGS :=
SRCDIR := src
BUILD := build
-SRCS := $(wildcard $(SRCDIR)/*.c)
+# The native binary is every .c except the wasm host layer.
+SRCS := $(filter-out $(SRCDIR)/wasm.c,$(wildcard $(SRCDIR)/*.c))
OBJS := $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.o,$(SRCS))
BIN := $(BUILD)/sl0pboy
-.PHONY: all clean run
+# ---- wasm ----
+# Only the portable core plus the wasm host layer: render/kitty/control/main
+# are terminal-and-POSIX bound and have no meaning in a browser.
+# zig cc carries its own wasm backend, linker and wasi-libc, so there is no
+# emscripten dependency. Reactor model = no main(), exports stay callable.
+WASM_SRCS := $(addprefix $(SRCDIR)/,cpu.c ppu.c timer.c gb.c cart.c wasm.c)
+WASM_OUT := web/sl0pboy.wasm
+WASM_CC := zig cc
+WASM_FLAGS := -target wasm32-wasi -mexec-model=reactor -O2 -std=c11 \
+ -Wall -Wextra -Wno-unused-parameter \
+ -Wl,--no-entry -Wl,--export-dynamic -Wl,--strip-all
+
+.PHONY: all clean run wasm
all: $(BIN)
+wasm: $(WASM_OUT)
+
+$(WASM_OUT): $(WASM_SRCS) $(wildcard $(SRCDIR)/*.h)
+ @mkdir -p $(dir $@)
+ $(WASM_CC) $(WASM_FLAGS) $(WASM_SRCS) -o $@
+ @echo "built $@ ($$(du -h $@ | cut -f1))"
+
$(BIN): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS)
@@ -21,7 +41,7 @@ $(BUILD):
mkdir -p $(BUILD)
clean:
- rm -rf $(BUILD)
+ rm -rf $(BUILD) $(WASM_OUT)
run: $(BIN)
$(BIN) $(ROM)