- GDScript 41.6%
- JavaScript 35.4%
- C++ 14%
- Shell 9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| core | ||
| godot | ||
| scripts | ||
| tests | ||
| web | ||
| .gitignore | ||
| README.md | ||
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::vectorcannot 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()latchesStopReason::StepBudgetintocpu.stop_reason, andstep()refuses to execute while any stop reason is set, so callingrun()again after a budget end does nothing. The core clears StepBudget between slices.- Snapshots serialise the console UART's
tx_capturering but not itstx_totalcounter, so after a restoretx_total < tx_capture.size(). Anything that watchestx_totalfor new output (console_driver.cpp does) sees no growth until the counter catches up. The core normalises it.