aboutsummaryrefslogtreecommitdiffstats
path: root/src/wasm.c (follow)
Commit message (Collapse)AuthorAge
* wasm: expose the link port as host-drained byte queuesgbc dev2026-09-22
| | | | | | | | | | | | | | | | | | | | | | A browser has no file descriptors, so the wasm build's link port was inert. Give it a pair of ring buffers instead: the guest's transmitted bytes pile up in one, the host pushes received bytes into the other, and the external-clock RX path pops from it (staying pending when empty, exactly like a quiet link). gbw_serial_drain / gbw_serial_push hand them to JS through a fixed staging buffer, so nothing allocates per frame. That is enough to be a real link. SLIP-framed bytes are IP packets; unframed bytes are console I/O - the same split tools/gbhub makes natively, which is what lets a page type into the link port and reach the shell's stdin. One machine per wasm instance, so the queues are plain statics: several Game Boys on a page are several instantiations of the one compiled module, each with its own linear memory. The native link is untouched: the fd paths sit in the #else and, preprocessed for a native target, gb.c comes out byte-identical to the previous commit (verified with gcc -E). blargg cpu_instrs still passes and the golden-frame tests are still pixel-exact.
* wasm: run the emulator in a browsergbc dev2026-09-22
`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.