Documentatie › Browserbuild
WebAssembly-build (browser)
Phosphoric compileert naar WebAssembly via Emscripten: de volledige emulator (6502-CPU, geheugen,
VIA, PSG, ULA, toetsenbord, cassette, schijf) draait in een tabblad, gerenderd op een <canvas>, audio
via Web Audio, toetsenbord via de DOM — met hergebruik van het bestaande SDL2-pad (Emscriptens SDL-port).
Vereisten
Een actieve Emscripten SDK (emcc in het
PATH):
git clone https://github.com/emscripten-core/emsdk
cd emsdk && ./emsdk install latest && ./emsdk activate latest
source ./emsdk_env.sh
Bouwen en uitvoeren
make wasm
(cd web && python3 -m http.server 8000)
# open http://localhost:8000/phosphoric.html
make wasm produceert in web/: phosphoric.html (pagina + canvas),
phosphoric.js, phosphoric.wasm en phosphoric.data (de roms/-ROM's
voorgeladen in het virtuele bestandssysteem). De pagina start de Atmos (-r /roms/basic11b.rom); klik op
het scherm voor toetsenbordfocus en audio.
Implementatie: vereiste assets + CSP
De pagina laadt haar logica (de Module-definitie, UI, toetsenbord, drag-and-drop) uit
web/shell.js — een extern bestand dat naast
phosphoric.html/.js/.wasm/.data moet worden geïmplementeerd.
shell.js staat onder versiebeheer (bron) en wordt door phosphoric.html gerefereerd via
<script src="shell.js">.
Deze externalisatie maakt de bundel compatibel met een strikte Content-Security-Policy. Een host
die de pagina serveert onder script-src 'self' 'wasm-unsafe-eval' zou een inline <script>
blokkeren (script-src-elem) → Module nooit gedefinieerd → Module.canvas
undefined → fatale fout bij de WebGL-createContext. Door alle JS te externaliseren (en het
inline oncontextmenu-attribuut van het canvas te vervangen door een DOM-listener), heeft
phosphoric.html geen enkel inline-script/handler: het draait onder een strikte CSP net
als onder een permissieve (GitHub Pages).
Minimaal vereiste CSP
Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'
'self'— staatshell.jsenphosphoric.jstoe (beide extern).'wasm-unsafe-eval'— vereist:phosphoric.jscompileert de module viaWebAssembly.instantiateStreaming/instantiate, geblokkeerd onder alleenscript-src 'self'. Het is het WASM-subtoken (≠'unsafe-eval', veel breder) — veilig.
⚠️ Verwijder 'wasm-unsafe-eval' niet: onder een kaal script-src 'self'
wordt WebAssembly-compilatie geweigerd en start de emulator niet. Na de externalisatiefix zijn noch 'unsafe-inline'
noch hash/nonce nodig — alleen het WASM-token.
Een script-src-elem-blokkade met bron sandbox eval code (niet
phosphoric.html) komt van een browserextensie, niet van Phosphoric: de pagina injecteert
geen inline-script of eval. Geen effect op de emulator.
Interface (webpagina)
De pagina toont een verticale iconenrail links (JOric-stijl) en het ORIC-toetsenbord als overlay:
- MODEL — schakelt ORIC-1 / Atmos (badge
1/A, koude herstart met de gekozen ROM). - LOAD + drag-and-drop van een
.tap/.dskop het scherm: het bestand wordt ingevoerd en de machine herstart erop. EJECT om het te verwijderen. - Deeplinks (URL-parameters) —
?rom=oric1|atmoskiest de machine en?media=<bestand>laadt media bij de eerste keer laden. Het type wordt afgeleid uit de extensie:.tap→ cassette,.dsk→ diskette (de Microdisc-controller wordt bij het opstarten ingeschakeld). Het doelbestand moet als binair worden geserveerd. - RESET — koude herstart met behoud van ROM en media.
- KEYS — toont/verbergt het virtuele toetsenbord.
- FULL — volledig scherm (canvas gecentreerd, verhouding 240/224 behouden).
- CRT — scanline- + vignetfilter (staat onthouden).
- SAVE / REST — slaat de staat op in een
.ost(gedownload) / herstelt een.ostlive, zonder herstart. - TAPE / DISK-LED's — lichten op tijdens een CLOAD (cassette) of een schijftoegang (WD1793 BUSY).
- Getrouw virtueel ORIC-toetsenbord, semitransparante overlay: echte indeling (ESC, CTRL, FUNCT, 2× SHIFT, RETURN, DEL, SPACE, pijltjes) met sticky CTRL / FUNCT / SHIFT-modifiers. Op ORIC-1 ontbreekt de FUNCT-toets (alleen Atmos).
CTRL+T en andere chords: de browser reserveert sommige sneltoetsen (CTRL+T = nieuw tabblad) op OS-niveau. Gebruik de CTRL-toets van het virtuele toetsenbord: die schrijft de ORIC-matrix via een directe C-aanroep, zodat de browser hem niet onderschept.
Serveer bestanden via HTTP (niet file://): de browser weigert een
.wasm vanaf het lokale bestandssysteem te laden.
Technische details
- Hoofdlus: in de browser geeft de C-
while-lus elke frame de controle terug aan de event-lus, via Asyncify (-sASYNCIFY) +emscripten_sleep()in de frame-limiter — die ook op ~50 Hz klokt. - Stack:
emulator_tis groot (framebuffer + geheugen) en staat op de stack vanmain(); de build forceert-sSTACK_SIZE=8MB. - Netwerk: functies die native sockets/threads vereisen (TCP/PTY/COM-serieel, GDB-stub, Cast-server, TLS) linken als no-ops in de browser — de machinekern, video, audio, toetsenbord, cassette en schijf werken.
Getrouwheid — geverifieerd
De WASM-uitvoer is byte-identiek aan de native build voor identieke invoer: een headless Atmos-boot gecompileerd naar WASM en uitgevoerd onder Node.js produceert exact dezelfde PPM-schermafbeelding als het native binary (getest bij 2M en 5M cycli). Het cyclusnauwkeurige determinisme van de kern blijft behouden door de WebAssembly-compilatie.
Browserrendering is ook gevalideerd: de pagina geladen in headless Chromium toont het juiste Atmos-bootscherm, en
toetsenbordinvoer werkt (typen van PRINT 6*7 + RETURN toont 42) — ROM-boot →
toetsenbordinjectie → BASIC-uitvoering → rendering, geheel in de browser.
Gereproduceerd uit docs/wasm.md (gezaghebbende, actuele versie op GitHub).