No description
  • GDScript 41.6%
  • JavaScript 35.4%
  • C++ 14%
  • Shell 9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
zclaude 8da178fa40 browser test: SITE_URL runs it against a deployed site
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 08:23:59 -04:00
core softsparc-web: Godot browser front end for softsparc + rv100 2026-10-26 01:37:03 -04:00
godot softsparc-web: Godot browser front end for softsparc + rv100 2026-10-26 01:37:03 -04:00
scripts softsparc-web: Godot browser front end for softsparc + rv100 2026-10-26 01:37:03 -04:00
tests browser test: SITE_URL runs it against a deployed site 2026-10-26 08:23:59 -04:00
web softsparc-web: Godot browser front end for softsparc + rv100 2026-10-26 01:37:03 -04:00
.gitignore softsparc-web: Godot browser front end for softsparc + rv100 2026-10-26 01:37:03 -04:00
README.md softsparc-web: Godot browser front end for softsparc + rv100 2026-10-26 01:37:03 -04:00

softsparc-web

A browser front end for softsparc, the architecturally exact UltraSPARC IIIi emulator, booting the real Sun Ultra 25 OpenBoot PROM (4.25.9), optionally with the rv100 model of the Sun XVR-100 (ATI Radeon 7000). The UI is a Godot 4 web export; the emulator is compiled to WebAssembly and runs in a Web Worker.

Live: https://apps.wizardhat.info/softsparc-web/

preset (URL ?preset=) what you get
ok instant: a snapshot of OBP sitting at ok
obsd instant: OpenBSD 7.9's miniroot booted to the installer prompt
obsd-boot OBP from reset, then boot disk into OpenBSD 7.9 (~6e9 instructions, ~8 min)
post OBP from the reset vector to ok, headless (~9e8 instructions, ~1.5 min)
gpu from reset with an XVR-100; at ok the console moves to the card at 1152x900
Custom XVR-100 on/off, your own boot image (read lazily, never uploaded), a blank or imaged read-write disk 1, start from reset / the ok snapshot / a saved state

The machine is the same one softsparc's own gates test: 2 GB RAM, LSI SAS1064 with the boot image on target 0, console on ttya. Keyboard input always goes to ttya (the emulated machine has no keyboard), including in graphics mode.

Layout

core/web_core.cpp     C API over softsparc + rv100 (init, restore/save, run a slice,
                      serial in/out, scanout). One machine per worker.
web/emu_worker.js     Web Worker: loads the core, runs it in ~30 ms slices, posts
                      console text, frames and stats.
web/emu_bridge.js     page side: window.ssBridge, polled by Godot through
                      JavaScriptBridge.eval.
godot/                the front end (main.gd: UI and polling; terminal.gd: VT100 subset)
scripts/              build-core.sh, fetch-media.sh, make-snapshots.sh, build-site.sh
tests/core_harness.mjs   drives the core under node (boots, types, snapshots, screenshots)
tests/browser_smoke.mjs  the built site in headless Chromium, keyboard included

Build

Needs emsdk (~/emsdk), Godot 4.7 with web export templates, checkouts of softsparc and rv100, and the two ROM dumps.

SOFTSPARC_DIR=~/sparcemulation/softsparc RV100_DIR=~/rv100 ./scripts/build-core.sh
./scripts/make-snapshots.sh       # ~8 min; writes build/snap/*.snap
ULTRA25_ROM=~/dumping/ultra25/ultra25-obp.rom XVR100_ROM=~/dumping/ultra25/xvr100.rom \
    ./scripts/build-site.sh        # -> build/site

build/site/core/versions.txt records the softsparc, rv100 and emscripten versions the site was built from. The snapshots must be regenerated whenever the core changes: softsparc's snapshot format guards on sizeof(CpuState) and the ROM hash, and rejects a mismatch rather than misloading it.

Test

node tests/core_harness.mjs boot --rom ~/dumping/ultra25/ultra25-obp.rom --max-steps 200000000
PLAYWRIGHT_CORE=<dir containing node_modules/playwright-core> \
  node tests/browser_smoke.mjs --preset ok --step 'ok ' --type banner \
       --step 'Sun Ultra 25 Workstation' --shot build/shot.png

The wasm core's console transcript over a 200M-instruction OBP boot is byte-identical to the native softsparc CLI's (--dynarec --trace-ring 0).

Why it is built the way it is

  • wasm64 (MEMORY64). The Ultra 25 has 2 GB of RAM and a wasm32 std::vector cannot hold 2^31 bytes, and running OBP with less RAM misconfigures the machine rather than shrinking it. Needs Chrome/Edge 133+ or Firefox 134+; Safari does not support memory64 yet.
  • No threads. The page needs no COOP/COEP headers; the emulator gets its own thread by being a Web Worker, and Godot uses the no-threads template.
  • Reset = a new worker. softsparc finds the XVR-100 through a process global, so a machine is never re-initialised in place.
  • The graphics preset cold-boots. softsparc's snapshots do not cover the rv100 state, so there is no instant graphics machine.
  • No network. The RTL8139 and slirp are left out; nothing in a browser could carry raw guest packets.
  • Interpreter + softsparc's portable block cache (--dynarec), ~13 MIPS in Chrome. The x86-64 JIT is not available in wasm.

Notes on softsparc found while building this

  • run() latches StopReason::StepBudget into cpu.stop_reason, and step() refuses to execute while any stop reason is set, so calling run() again after a budget end does nothing. The core clears StepBudget between slices.
  • Snapshots serialise the console UART's tx_capture ring but not its tx_total counter, so after a restore tx_total < tx_capture.size(). Anything that watches tx_total for new output (console_driver.cpp does) sees no growth until the counter catches up. The core normalises it.