Dokumentation › Browser-Build
WebAssembly-(Browser-)Build
Phosphoric kompiliert über Emscripten zu WebAssembly: Der vollständige Emulator (6502-CPU, Speicher,
VIA, PSG, ULA, Tastatur, Kassette, Diskette) läuft in einem Tab, gerendert auf einem <canvas>, Audio
über Web Audio, Tastatur über das DOM — unter Wiederverwendung des bestehenden SDL2-Pfads (Emscriptens SDL-Port).
Voraussetzungen
Ein aktives Emscripten SDK (emcc im
PATH):
git clone https://github.com/emscripten-core/emsdk
cd emsdk && ./emsdk install latest && ./emsdk activate latest
source ./emsdk_env.sh
Bauen und ausführen
make wasm
(cd web && python3 -m http.server 8000)
# http://localhost:8000/phosphoric.html öffnen
make wasm erzeugt in web/: phosphoric.html (Seite + Canvas),
phosphoric.js, phosphoric.wasm und phosphoric.data (die roms/-ROMs,
vorgeladen ins virtuelle Dateisystem). Die Seite bootet den Atmos (-r /roms/basic11b.rom); klicke auf den
Bildschirm für Tastaturfokus und Audio.
Bereitstellung: benötigte Assets + CSP
Die Seite lädt ihre Logik (die Module-Definition, UI, Tastatur, Drag-and-drop) aus
web/shell.js — einer externen Datei, die neben
phosphoric.html/.js/.wasm/.data bereitgestellt werden muss.
shell.js steht unter Versionskontrolle (Quelle) und wird von phosphoric.html über
<script src="shell.js"> referenziert.
Diese Auslagerung macht das Bundle mit einer strengen Content-Security-Policy kompatibel. Ein Host,
der die Seite unter script-src 'self' 'wasm-unsafe-eval' ausliefert, würde ein Inline-<script>
blockieren (script-src-elem) → Module nie definiert → Module.canvas
undefined → fataler Fehler beim WebGL-createContext. Durch Auslagern des gesamten JS (und
Ersetzen des Inline-oncontextmenu-Attributs des Canvas durch einen DOM-Listener) hat
phosphoric.html kein einziges Inline-Skript/-Handler: Es läuft unter einer strengen CSP
genauso wie unter einer permissiven (GitHub Pages).
Minimal erforderliche CSP
Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'
'self'— erlaubtshell.jsundphosphoric.js(beide extern).'wasm-unsafe-eval'— erforderlich:phosphoric.jskompiliert das Modul überWebAssembly.instantiateStreaming/instantiate, unter reinemscript-src 'self'blockiert. Es ist das WASM-Subtoken (≠'unsafe-eval', weit breiter) — sicher.
⚠️ 'wasm-unsafe-eval' nicht entfernen: Unter einem nackten
script-src 'self' wird die WebAssembly-Kompilierung verweigert und der Emulator startet nicht. Nach dem
Auslagerungs-Fix sind weder 'unsafe-inline' noch Hash/Nonce nötig — nur das WASM-Token.
Eine script-src-elem-Blockade mit Quelle sandbox eval code (nicht
phosphoric.html) stammt von einer Browser-Erweiterung, nicht von Phosphoric: Die Seite
injiziert kein Inline-Skript und kein eval. Keine Auswirkung auf den Emulator.
Oberfläche (Webseite)
Die Seite zeigt eine vertikale Icon-Leiste links (JOric-Stil) und die ORIC-Tastatur als Overlay:
- MODEL — wechselt ORIC-1 / Atmos (Badge
1/A, Kaltstart mit dem gewählten ROM). - LOAD + Drag-and-drop einer
.tap/.dskauf den Bildschirm: Die Datei wird eingelegt und die Maschine startet darauf neu. EJECT zum Entfernen. - Deeplinks (URL-Parameter) —
?rom=oric1|atmoswählt die Maschine und?media=<Datei>lädt beim ersten Laden ein Medium. Der Typ wird aus der Erweiterung abgeleitet:.tap→ Kassette,.dsk→ Diskette (der Microdisc-Controller wird beim Booten aktiviert). Die Zieldatei muss als binär ausgeliefert werden. - RESET — Kaltstart unter Beibehaltung von ROM und Medium.
- KEYS — zeigt/verbirgt die virtuelle Tastatur.
- FULL — Vollbild (Canvas zentriert, Verhältnis 240/224 beibehalten).
- CRT — Scanline- + Vignette-Filter (Zustand gemerkt).
- SAVE / REST — speichert den Zustand in eine
.ost(heruntergeladen) / stellt eine.ostim Betrieb wieder her, ohne Neustart. - TAPE- / DISK-LEDs — leuchten während eines CLOAD (Kassette) oder eines Diskzugriffs (WD1793 BUSY).
- Getreue virtuelle ORIC-Tastatur, halbtransparentes Overlay: echtes Layout (ESC, CTRL, FUNCT, 2× SHIFT, RETURN, DEL, SPACE, Pfeiltasten) mit klebrigen CTRL / FUNCT / SHIFT-Modifikatoren. Beim ORIC-1 fehlt die FUNCT-Taste (nur Atmos).
CTRL+T und andere Chords: Der Browser reserviert manche Kürzel (CTRL+T = neuer Tab) auf OS-Ebene. Verwende die CTRL-Taste der virtuellen Tastatur: Sie schreibt die ORIC-Matrix über einen direkten C-Aufruf, sodass der Browser sie nicht abfängt.
Liefere Dateien über HTTP aus (nicht file://): Der Browser weigert sich,
eine .wasm vom lokalen Dateisystem zu laden.
Technische Details
- Hauptschleife: Im Browser gibt die C-
while-Schleife pro Frame die Kontrolle an die Ereignisschleife zurück, über Asyncify (-sASYNCIFY) +emscripten_sleep()im Frame-Limiter — der auch auf ~50 Hz taktet. - Stack:
emulator_tist groß (Framebuffer + Speicher) und liegt auf dem Stack vonmain(); der Build erzwingt-sSTACK_SIZE=8MB. - Netzwerk: Funktionen, die native Sockets/Threads erfordern (TCP/PTY/COM-seriell, GDB-Stub, Cast-Server, TLS), werden im Browser als No-Ops gelinkt — der Maschinenkern, Video, Audio, Tastatur, Kassette und Diskette funktionieren.
Treue — verifiziert
Die WASM-Ausgabe ist bei identischen Eingaben byte-identisch zum nativen Build: Ein Headless-Atmos-Boot, zu WASM kompiliert und unter Node.js ausgeführt, erzeugt exakt denselben PPM-Screenshot wie das native Binary (getestet bei 2 Mio. und 5 Mio. Takten). Der taktgenaue Determinismus des Kerns bleibt über die WebAssembly-Kompilierung erhalten.
Das Browser-Rendering wurde ebenfalls validiert: Die in Headless-Chromium geladene Seite zeigt den korrekten
Atmos-Bootscreen, und die Tastatureingabe funktioniert (Eingabe von PRINT 6*7 + RETURN zeigt 42) —
ROM-Boot → Tastaturinjektion → BASIC-Ausführung → Rendering, vollständig im Browser.
Reproduziert aus docs/wasm.md (maßgebliche, aktuelle Version auf GitHub).