diff options
| author | gbc dev <gbc@localhost> | 2026-09-22 15:57:06 +0200 |
|---|---|---|
| committer | gbc dev <gbc@localhost> | 2026-09-22 15:57:06 +0200 |
| commit | 6fb4fbbce99c08bd535d1b7e0f356864fb0c38a0 (patch) | |
| tree | 7f0b249f4cfe640d80a7848d9012dc695d42d5c3 /Makefile | |
| parent | core: make the machine loadable without a filesystem (diff) | |
| download | sl0pboy-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-- | Makefile | 26 |
1 files changed, 23 insertions, 3 deletions
@@ -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) |
