Tachibana

Documentation › Build navigateur

Build WebAssembly (navigateur)

Phosphoric compile en WebAssembly via Emscripten : l'émulateur complet (CPU 6502, mémoire, VIA, PSG, ULA, clavier, cassette, disque) tourne dans un onglet, rendu sur un <canvas>, audio via Web Audio, clavier via le DOM — en réutilisant le chemin SDL2 existant (port SDL d'Emscripten).

Prérequis

Emscripten SDK actif (emcc dans le PATH) :

git clone https://github.com/emscripten-core/emsdk
cd emsdk && ./emsdk install latest && ./emsdk activate latest
source ./emsdk_env.sh

Construire et lancer

make wasm
(cd web && python3 -m http.server 8000)
# ouvrir http://localhost:8000/phosphoric.html

make wasm produit dans web/ : phosphoric.html (page + canvas), phosphoric.js, phosphoric.wasm, et phosphoric.data (les ROM de roms/ préchargées dans le système de fichiers virtuel). La page démarre l'Atmos (-r /roms/basic11b.rom) ; cliquez l'écran pour le focus clavier et l'audio.

Déploiement : assets requis + CSP

La page charge sa logique (définition de Module, UI, clavier, glisser-déposer) depuis web/shell.js — un fichier externe qu'il faut déployer à côté de phosphoric.html/.js/.wasm/.data. shell.js est versionné (source) et référencé par phosphoric.html via <script src="shell.js">.

Cette externalisation rend le bundle compatible Content-Security-Policy stricte. Un hôte qui sert la page sous script-src 'self' 'wasm-unsafe-eval' bloquerait un <script> inline (script-src-elem) → Module jamais défini → Module.canvas undefined → erreur fatale au createContext WebGL. En externalisant tout le JS (et en remplaçant l'attribut inline oncontextmenu du canvas par un écouteur DOM), phosphoric.html n'a plus aucun script/gestionnaire inline : il tourne sous CSP stricte comme sous une politique permissive (GitHub Pages).

CSP minimale requise

Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'

⚠️ Ne pas retirer 'wasm-unsafe-eval' : sous script-src 'self' nu, la compilation WebAssembly est refusée et l'émulateur ne démarre pas. Après le fix d'externalisation, ni 'unsafe-inline' ni hash/nonce ne sont nécessaires — seul le token WASM l'est.

Un blocage script-src-elem dont la source est sandbox eval code (et non phosphoric.html) provient d'une extension navigateur, pas de Phosphoric : la page n'injecte aucun script inline ni eval. Sans effet sur l'émulateur.

Interface (page web)

La page présente un rail d'icônes vertical à gauche (façon JOric) et le clavier ORIC en overlay :

CTRL+T et autres chords : le navigateur réserve certains raccourcis (CTRL+T = nouvel onglet) au niveau de l'OS. Utilisez la touche CTRL du clavier virtuel : elle écrit la matrice ORIC via un appel C direct, donc le navigateur ne l'intercepte pas.

Servez les fichiers par HTTP (pas file://) : le navigateur refuse de charger un .wasm depuis le système de fichiers local.

Détails techniques

Fidélité — vérifié

La sortie WASM est byte-identique au build natif pour des entrées identiques : un boot Atmos headless compilé en WASM et exécuté sous Node.js produit la même capture PPM exacte que le binaire natif (testé à 2M et 5M cycles). Le déterminisme cycle-exact du cœur est préservé à travers la compilation WebAssembly.

Le rendu navigateur a aussi été validé : la page chargée dans Chromium headless affiche l'écran de boot Atmos correct, et la saisie clavier fonctionne (taper PRINT 6*7 + RETURN affiche 42) — boot ROM → injection clavier → exécution BASIC → rendu, entièrement dans le navigateur.

Reproduit depuis docs/wasm.md (version de référence à jour sur GitHub).

← Documentation · Ouvrir l'émulateur →