-
Notifications
You must be signed in to change notification settings - Fork 0
Emulator XRoar CoCo
XRoar was the first emulator in GenX-DOS where the upstream's HTML wrapper turned out to be doing a lot more than it looked. It became the cautionary tale that every emscripten-based emulator after it got measured against.
Upstream: 6809.org.uk/xroar/online/ — Ciaran Anscomb's XRoar, a long-running Dragon/Tandy CoCo emulator written in C. Ciaran ships an emscripten-WASM build alongside the native binaries, served from his own site. It's GPL-2, covers the whole Dragon/CoCo family, and runs cleanly in a browser. There wasn't really a competitor worth evaluating — XRoar is the CoCo emulator.
We dropped xroar.js and xroar.wasm into emulators/xroar/, wrote a thin play.html with a <canvas> and a <script> tag, and… nothing. Black screen. No errors on the page. Just a dead canvas.
DevTools told the real story: Uncaught ReferenceError: ui_menu_clear is not defined. The WASM was halting mid-init because it expected a pile of JS functions to exist for things like menu population and status updates — things the upstream HTML wraps a real UI around, but which we'd thrown away when we stripped the page down to the essentials.
The fix was to grep upstream's HTML for every function ui_ definition and stub each one as a no-op. For XRoar v1.10 that's fifteen of them:
function ui_done_initialising() {}
function ui_menu_clear() {}
function ui_menu_add() {}
function ui_menu_add_str() {}
function ui_menu_select() {}
function ui_menu_select_str() {}
function ui_set_checkbox() {}
function ui_set_fullscreen() {}
function ui_set_html() {}
function ui_set_value() {}
function ui_update_ccr() {}
function ui_update_disk_info() {}
function ui_update_hd_filename() {}
function ui_update_tape_input_filename() {}
function ui_update_tape_playing() {}We refreshed. Still a black screen.
This time the console had a different complaint: (unpopulated) CRC32 INVALID repeating for several ROM names. XRoar's docs mention "free open-source replacement ROMs built in" — easy to read as "no ROMs needed." That's not quite what they meant. The replacement ROMs exist, but they're not baked into the WASM; they're served alongside it from the upstream deploy directory.
A quick probe of upstream confirmed it:
curl -sIo /dev/null -w "%{http_code}\n" https://www.6809.org.uk/xroar/online/bas13.rom200 OK. We mirrored the lot — bas13.rom, extbas11.rom, d64_1.rom, d64_2.rom, dplus49b.rom, coco3.rom, coco3p.rom, disk11.rom, mc10.rom — into the bundle directory.
Refreshed. Still black screen.
Now the console showed WARNING: Error fetching '/d64_1.rom'. Note the leading slash. XRoar was requesting ROMs by absolute path, which under emscripten's default locateFile resolved to the web root — and our web root had no ROMs, only index.html. The files were sitting right next to xroar.js, but the runtime was looking for them three directories up.
The fix was a one-liner:
Module.locateFile = function(path) { return path.replace(/^\/+/, ''); };That strips the leading slash so requests become page-relative. After this, the boot log went green and the CoCo prompt appeared.
wasm_set_machine_cart('coco', …) called from onRuntimeInitialized doesn't reliably switch from the default dragon64 machine. By the time the callback fires, init has already locked in the wrong target. We worked around it by passing -machine coco in Module.arguments at the very start, so XRoar picks the right machine during its own init pass instead of being switched after the fact.
Each game is a row in games.json describing the cartridge / tape / disk file and the machine variant. play.html reads ?game=<key> from the URL, looks the entry up, and calls wasm_set_machine_cart + wasm_load_file with the right arguments. Cartridge games autorun instantly; tape games wait 2 seconds so the BASIC prompt finishes drawing before CLOAD fires.
Every new emscripten emulator now runs through the same loop before we declare it working:
chromium --headless --no-sandbox --enable-logging=stderr \
--log-level=0 --virtual-time-budget=10000 \
http://127.0.0.1:8765/emulators/xroar/play.html?game=foo \
2>&1 | grep CONSOLEIf the log shows valid CRC32 lines and the expected machine banner ([part:coco] Tandy | Colour Computer), it'll render. Headless can't verify pixels without a GPU, but a clean boot log is a strong proxy.
emulators/xroar/
├── play.html
├── xroar.js
├── xroar.wasm
├── games.json
├── controls.html
├── bas13.rom, extbas11.rom, d64_1.rom, d64_2.rom,
│ dplus49b.rom, coco3.rom, coco3p.rom, disk11.rom, mc10.rom
└── roms/ ← cartridge / tape / disk images
- Emulators — index of all engines
- File-Structure — where bundles sit in the repo
-
Virtual-Filesystem — how
.batlaunchers reachplay.html