browser: recover cleanly from a rejected boot ROM#193
Merged
Conversation
boot() assigned emu before load_rom could throw, so a bad ROM image left a half-initialized machine behind: the ROM pickers then took the live-swap branch, which never updated the bootRom stash, and every Boot retry re-fed the rejected image. Once a boot had failed there was no way back short of reloading the page, even after picking a good ROM. boot() now fits the ROM into a fresh WebEmu before anything else and assigns emu only after the whole boot has succeeded, so a rejected image leaves the page in its pre-boot state. The picker and drag-drop paths share a new fitRom() helper that updates the stash on a successful live swap too, so a later reboot fits the ROM chosen last; a rejected image throws before the stash is touched. The frame loop's error path re-arms the boot button and boot() closes the previous audio stack, so a machine that hit an emulator error can be rebooted cleanly.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a browser-side boot flow bug where a rejected Kickstart ROM could leave the page in a half-booted state, causing subsequent ROM selections/retries to keep reusing a stale rejected image. It restructures the ROM-fit and boot logic in try.js to make boot atomic and to keep the “boot ROM” stash consistent across live swaps and reboots.
Changes:
- Add a shared
fitRom()helper to unify ROM picker and drag-drop behavior, and update thebootRomstash on successful live swaps. - Make
boot()construct/load a freshWebEmufirst and assignemuonly after the overall boot succeeds. - Re-arm the boot UI after main-loop emulator errors and close/rebuild the audio stack on reboot.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
247
to
+251
| running = false; | ||
| setLoadStatus(`emulator error: ${e.message ?? e}`); | ||
| overlay.style.display = ''; | ||
| // Re-arm the boot button: a fresh boot replaces the wedged machine. | ||
| refreshBootButton(); |
Review follow-up: tick()'s error path re-armed the boot button but left emu set, so the pickers still took the live-swap branch into the crashed instance (which may have panicked) instead of stashing for the fresh boot. Null it there, and guard the two deferred touch timers (long-press and tap-release) that can fire after the machine is gone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A field report from /try: with a bad ROM image (a TOSEC
[b]Kickstart 2.04 dump, 528509 bytes - a byte-exact LF-to-CRLF ASCII-transfer casualty, the clean image contains exactly 4221 LFs), the page wedged in ways that looked order-dependent:boot()assignedemubeforeload_romcould throw, so a failed boot left a half-initialized, never-started machine behind. The ROM pickers then took theif (emu)live-swap branch, which never updated thebootRomstash - so every Boot retry re-fed the rejected image, whatever was picked since.(The reporter's third symptom - a black-instead-of-blue Kickstart 1.3 insert-disk screen after a live swap from 2.04 - was chased into the core and ruled out:
reload_rom+power_on_resetrenders pixel-identical to a fresh boot, both at current main and at the deployed wasm's commit, for 512K and mirrored 256K images alike. Their 1.3 image is suspect; verification pending.)The fix
JS-only change in
crates/copperline-web/www/try.js:boot()fits the ROM into a freshWebEmubefore anything else and assignsemuonly once the whole boot has succeeded, so a rejected image aborts with the page still in its pre-boot state and the pickers keep working.fitRom()helper: on a successful live swap it also updates thebootRomstash, so a later reboot fits the ROM chosen last; a rejected image throws before the stash is touched.boot()closes the previous audio stack, so a machine that hit an emulator error can be rebooted cleanly instead of wedging behind a disabled button.Testing
Verified end-to-end in Chrome against the deployed wasm bundle (the fix uses no new WebEmu API), with a bad ROM generated the same way the reporter's was corrupted:
reload_romvalidates before mutating)Rust untouched, so
cargo fmt/clippy/testare unaffected by this diff.