web-a2e 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clangd +5 -0
- package/.mcp.json +12 -0
- package/CLAUDE.md +362 -0
- package/CMakeLists.txt +774 -0
- package/LICENSE +21 -0
- package/README.md +392 -0
- package/build-wasm/generated/roms.cpp +2447 -0
- package/docker-compose.staging.yml +9 -0
- package/docs/basic-rom-disassembly.md +6663 -0
- package/docs/softswitch-comparison.md +273 -0
- package/docs/thunderclock-debug.md +89 -0
- package/examples/cube.bas +72 -0
- package/examples/hello.s +55 -0
- package/examples/scroll.s +140 -0
- package/package.json +18 -0
- package/public/assets/apple-logo-old.png +0 -0
- package/public/assets/apple-logo.png +0 -0
- package/public/assets/drive-closed-light-on.png +0 -0
- package/public/assets/drive-closed.png +0 -0
- package/public/assets/drive-open-light-on.png +0 -0
- package/public/assets/drive-open.png +0 -0
- package/public/audio-worklet.js +82 -0
- package/public/disks/Apple DOS 3.3 January 1983.dsk +0 -0
- package/public/disks/ProDOS 2.4.3.po +0 -0
- package/public/disks/h32mb.2mg +0 -0
- package/public/disks/library.json +26 -0
- package/public/docs/llms/llm-assembler.txt +90 -0
- package/public/docs/llms/llm-basic-program.txt +256 -0
- package/public/docs/llms/llm-disk-drives.txt +72 -0
- package/public/docs/llms/llm-file-explorer.txt +50 -0
- package/public/docs/llms/llm-hard-drives.txt +80 -0
- package/public/docs/llms/llm-main.txt +51 -0
- package/public/docs/llms/llm-slot-configuration.txt +66 -0
- package/public/icons/icon-192.svg +4 -0
- package/public/icons/icon-512.svg +4 -0
- package/public/index.html +661 -0
- package/public/llms.txt +49 -0
- package/public/manifest.json +29 -0
- package/public/shaders/burnin.glsl +22 -0
- package/public/shaders/crt.glsl +706 -0
- package/public/shaders/edge.glsl +109 -0
- package/public/shaders/vertex.glsl +8 -0
- package/public/sw.js +186 -0
- package/roms/341-0027.bin +0 -0
- package/roms/341-0160-A-US-UK.bin +0 -0
- package/roms/341-0160-A.bin +0 -0
- package/roms/342-0273-A-US-UK.bin +0 -0
- package/roms/342-0349-B-C0-FF.bin +0 -0
- package/roms/Apple Mouse Interface Card ROM - 342-0270-C.bin +0 -0
- package/roms/Thunderclock Plus ROM.bin +0 -0
- package/scripts/generate_roms.sh +69 -0
- package/src/bindings/wasm_interface.cpp +1940 -0
- package/src/core/assembler/assembler.cpp +1239 -0
- package/src/core/assembler/assembler.hpp +115 -0
- package/src/core/audio/audio.cpp +160 -0
- package/src/core/audio/audio.hpp +81 -0
- package/src/core/basic/basic_detokenizer.cpp +436 -0
- package/src/core/basic/basic_detokenizer.hpp +41 -0
- package/src/core/basic/basic_tokenizer.cpp +286 -0
- package/src/core/basic/basic_tokenizer.hpp +26 -0
- package/src/core/basic/basic_tokens.hpp +295 -0
- package/src/core/cards/disk2_card.cpp +568 -0
- package/src/core/cards/disk2_card.hpp +316 -0
- package/src/core/cards/expansion_card.hpp +185 -0
- package/src/core/cards/mockingboard/ay8910.cpp +616 -0
- package/src/core/cards/mockingboard/ay8910.hpp +159 -0
- package/src/core/cards/mockingboard/via6522.cpp +530 -0
- package/src/core/cards/mockingboard/via6522.hpp +163 -0
- package/src/core/cards/mockingboard_card.cpp +312 -0
- package/src/core/cards/mockingboard_card.hpp +159 -0
- package/src/core/cards/mouse_card.cpp +654 -0
- package/src/core/cards/mouse_card.hpp +190 -0
- package/src/core/cards/smartport/block_device.cpp +202 -0
- package/src/core/cards/smartport/block_device.hpp +60 -0
- package/src/core/cards/smartport/smartport_card.cpp +603 -0
- package/src/core/cards/smartport/smartport_card.hpp +120 -0
- package/src/core/cards/thunderclock_card.cpp +237 -0
- package/src/core/cards/thunderclock_card.hpp +122 -0
- package/src/core/cpu/cpu6502.cpp +1609 -0
- package/src/core/cpu/cpu6502.hpp +203 -0
- package/src/core/debug/condition_evaluator.cpp +470 -0
- package/src/core/debug/condition_evaluator.hpp +87 -0
- package/src/core/disassembler/disassembler.cpp +552 -0
- package/src/core/disassembler/disassembler.hpp +171 -0
- package/src/core/disk-image/disk_image.hpp +267 -0
- package/src/core/disk-image/dsk_disk_image.cpp +827 -0
- package/src/core/disk-image/dsk_disk_image.hpp +204 -0
- package/src/core/disk-image/gcr_encoding.cpp +147 -0
- package/src/core/disk-image/gcr_encoding.hpp +78 -0
- package/src/core/disk-image/woz_disk_image.cpp +1049 -0
- package/src/core/disk-image/woz_disk_image.hpp +343 -0
- package/src/core/emulator.cpp +2126 -0
- package/src/core/emulator.hpp +434 -0
- package/src/core/filesystem/dos33.cpp +178 -0
- package/src/core/filesystem/dos33.hpp +66 -0
- package/src/core/filesystem/pascal.cpp +262 -0
- package/src/core/filesystem/pascal.hpp +87 -0
- package/src/core/filesystem/prodos.cpp +369 -0
- package/src/core/filesystem/prodos.hpp +119 -0
- package/src/core/input/keyboard.cpp +227 -0
- package/src/core/input/keyboard.hpp +111 -0
- package/src/core/mmu/mmu.cpp +1387 -0
- package/src/core/mmu/mmu.hpp +236 -0
- package/src/core/types.hpp +196 -0
- package/src/core/video/video.cpp +680 -0
- package/src/core/video/video.hpp +156 -0
- package/src/css/assembler-editor.css +1617 -0
- package/src/css/base.css +470 -0
- package/src/css/basic-debugger.css +791 -0
- package/src/css/basic-editor.css +792 -0
- package/src/css/controls.css +783 -0
- package/src/css/cpu-debugger.css +1413 -0
- package/src/css/debug-base.css +160 -0
- package/src/css/debug-windows.css +6455 -0
- package/src/css/disk-drives.css +406 -0
- package/src/css/documentation.css +392 -0
- package/src/css/file-explorer.css +867 -0
- package/src/css/hard-drive.css +180 -0
- package/src/css/layout.css +217 -0
- package/src/css/memory-windows.css +798 -0
- package/src/css/modals.css +510 -0
- package/src/css/monitor.css +425 -0
- package/src/css/release-notes.css +101 -0
- package/src/css/responsive.css +400 -0
- package/src/css/rule-builder.css +340 -0
- package/src/css/save-states.css +201 -0
- package/src/css/settings-windows.css +1231 -0
- package/src/css/window-switcher.css +150 -0
- package/src/js/agent/agent-manager.js +643 -0
- package/src/js/agent/agent-tools.js +293 -0
- package/src/js/agent/agent-version-tools.js +131 -0
- package/src/js/agent/assembler-tools.js +357 -0
- package/src/js/agent/basic-program-tools.js +894 -0
- package/src/js/agent/disk-tools.js +417 -0
- package/src/js/agent/file-explorer-tools.js +269 -0
- package/src/js/agent/index.js +13 -0
- package/src/js/agent/main-tools.js +222 -0
- package/src/js/agent/slot-tools.js +303 -0
- package/src/js/agent/smartport-tools.js +257 -0
- package/src/js/agent/window-tools.js +80 -0
- package/src/js/audio/audio-driver.js +417 -0
- package/src/js/audio/audio-worklet.js +85 -0
- package/src/js/audio/index.js +8 -0
- package/src/js/config/default-layout.js +34 -0
- package/src/js/config/version.js +8 -0
- package/src/js/data/apple2-rom-routines.js +577 -0
- package/src/js/debug/assembler-editor-window.js +2993 -0
- package/src/js/debug/basic-breakpoint-manager.js +529 -0
- package/src/js/debug/basic-program-parser.js +436 -0
- package/src/js/debug/basic-program-window.js +2594 -0
- package/src/js/debug/basic-variable-inspector.js +447 -0
- package/src/js/debug/breakpoint-manager.js +472 -0
- package/src/js/debug/cpu-debugger-window.js +2396 -0
- package/src/js/debug/index.js +22 -0
- package/src/js/debug/label-manager.js +238 -0
- package/src/js/debug/memory-browser-window.js +416 -0
- package/src/js/debug/memory-heat-map-window.js +481 -0
- package/src/js/debug/memory-map-window.js +206 -0
- package/src/js/debug/mockingboard-window.js +882 -0
- package/src/js/debug/mouse-card-window.js +355 -0
- package/src/js/debug/rule-builder-window.js +648 -0
- package/src/js/debug/soft-switch-window.js +458 -0
- package/src/js/debug/stack-viewer-window.js +221 -0
- package/src/js/debug/symbols.js +416 -0
- package/src/js/debug/trace-panel.js +291 -0
- package/src/js/debug/zero-page-watch-window.js +297 -0
- package/src/js/disk-manager/disk-drives-window.js +212 -0
- package/src/js/disk-manager/disk-operations.js +284 -0
- package/src/js/disk-manager/disk-persistence.js +301 -0
- package/src/js/disk-manager/disk-surface-renderer.js +388 -0
- package/src/js/disk-manager/drive-sounds.js +139 -0
- package/src/js/disk-manager/hard-drive-manager.js +481 -0
- package/src/js/disk-manager/hard-drive-persistence.js +187 -0
- package/src/js/disk-manager/hard-drive-window.js +57 -0
- package/src/js/disk-manager/index.js +890 -0
- package/src/js/display/display-settings-window.js +383 -0
- package/src/js/display/index.js +10 -0
- package/src/js/display/screen-window.js +342 -0
- package/src/js/display/webgl-renderer.js +705 -0
- package/src/js/file-explorer/disassembler.js +574 -0
- package/src/js/file-explorer/dos33.js +266 -0
- package/src/js/file-explorer/file-viewer.js +359 -0
- package/src/js/file-explorer/index.js +1261 -0
- package/src/js/file-explorer/prodos.js +549 -0
- package/src/js/file-explorer/utils.js +67 -0
- package/src/js/help/documentation-window.js +1096 -0
- package/src/js/help/index.js +10 -0
- package/src/js/help/release-notes-window.js +85 -0
- package/src/js/help/release-notes.js +612 -0
- package/src/js/input/gamepad-handler.js +176 -0
- package/src/js/input/index.js +12 -0
- package/src/js/input/input-handler.js +396 -0
- package/src/js/input/joystick-window.js +404 -0
- package/src/js/input/mouse-handler.js +99 -0
- package/src/js/input/text-selection.js +462 -0
- package/src/js/main.js +653 -0
- package/src/js/state/index.js +15 -0
- package/src/js/state/save-states-window.js +393 -0
- package/src/js/state/state-manager.js +409 -0
- package/src/js/state/state-persistence.js +218 -0
- package/src/js/ui/confirm.js +43 -0
- package/src/js/ui/disk-drive-positioner.js +347 -0
- package/src/js/ui/reminder-controller.js +129 -0
- package/src/js/ui/slot-configuration-window.js +560 -0
- package/src/js/ui/theme-manager.js +61 -0
- package/src/js/ui/toast.js +44 -0
- package/src/js/ui/ui-controller.js +897 -0
- package/src/js/ui/window-switcher.js +275 -0
- package/src/js/utils/basic-autocomplete.js +832 -0
- package/src/js/utils/basic-highlighting.js +473 -0
- package/src/js/utils/basic-tokenizer.js +153 -0
- package/src/js/utils/basic-tokens.js +117 -0
- package/src/js/utils/constants.js +28 -0
- package/src/js/utils/indexeddb-helper.js +225 -0
- package/src/js/utils/merlin-editor-support.js +905 -0
- package/src/js/utils/merlin-highlighting.js +551 -0
- package/src/js/utils/storage.js +125 -0
- package/src/js/utils/string-utils.js +19 -0
- package/src/js/utils/wasm-memory.js +54 -0
- package/src/js/windows/base-window.js +690 -0
- package/src/js/windows/index.js +9 -0
- package/src/js/windows/window-manager.js +375 -0
- package/tests/catch2/catch.hpp +17976 -0
- package/tests/common/basic_program_builder.cpp +119 -0
- package/tests/common/basic_program_builder.hpp +209 -0
- package/tests/common/disk_image_builder.cpp +444 -0
- package/tests/common/disk_image_builder.hpp +141 -0
- package/tests/common/test_helpers.hpp +118 -0
- package/tests/gcr/gcr-test.cpp +142 -0
- package/tests/integration/check-rom.js +70 -0
- package/tests/integration/compare-boot.js +239 -0
- package/tests/integration/crash-trace.js +102 -0
- package/tests/integration/disk-boot-test.js +264 -0
- package/tests/integration/memory-crash.js +108 -0
- package/tests/integration/nibble-read-test.js +249 -0
- package/tests/integration/phase-test.js +159 -0
- package/tests/integration/test_emulator.cpp +291 -0
- package/tests/integration/test_emulator_basic.cpp +91 -0
- package/tests/integration/test_emulator_debug.cpp +344 -0
- package/tests/integration/test_emulator_disk.cpp +153 -0
- package/tests/integration/test_emulator_state.cpp +163 -0
- package/tests/klaus/6502_functional_test.bin +0 -0
- package/tests/klaus/65C02_extended_opcodes_test.bin +0 -0
- package/tests/klaus/klaus_6502_test.cpp +184 -0
- package/tests/klaus/klaus_65c02_test.cpp +197 -0
- package/tests/thunderclock/thunderclock_mmu_test.cpp +304 -0
- package/tests/thunderclock/thunderclock_test.cpp +550 -0
- package/tests/unit/test_assembler.cpp +521 -0
- package/tests/unit/test_audio.cpp +196 -0
- package/tests/unit/test_ay8910.cpp +311 -0
- package/tests/unit/test_basic_detokenizer.cpp +265 -0
- package/tests/unit/test_basic_tokenizer.cpp +382 -0
- package/tests/unit/test_block_device.cpp +259 -0
- package/tests/unit/test_condition_evaluator.cpp +219 -0
- package/tests/unit/test_cpu6502.cpp +1301 -0
- package/tests/unit/test_cpu_addressing.cpp +361 -0
- package/tests/unit/test_cpu_cycle_counts.cpp +409 -0
- package/tests/unit/test_cpu_decimal.cpp +166 -0
- package/tests/unit/test_cpu_interrupts.cpp +285 -0
- package/tests/unit/test_disassembler.cpp +323 -0
- package/tests/unit/test_disk2_card.cpp +330 -0
- package/tests/unit/test_dos33.cpp +273 -0
- package/tests/unit/test_dsk_disk_image.cpp +315 -0
- package/tests/unit/test_expansion_card.cpp +178 -0
- package/tests/unit/test_gcr_encoding.cpp +232 -0
- package/tests/unit/test_keyboard.cpp +262 -0
- package/tests/unit/test_mmu.cpp +555 -0
- package/tests/unit/test_mmu_slots.cpp +323 -0
- package/tests/unit/test_mockingboard.cpp +352 -0
- package/tests/unit/test_mouse_card.cpp +386 -0
- package/tests/unit/test_pascal.cpp +248 -0
- package/tests/unit/test_prodos.cpp +259 -0
- package/tests/unit/test_smartport_card.cpp +321 -0
- package/tests/unit/test_thunderclock.cpp +354 -0
- package/tests/unit/test_via6522.cpp +323 -0
- package/tests/unit/test_video.cpp +319 -0
- package/tests/unit/test_woz_disk_image.cpp +257 -0
- package/vite.config.js +96 -0
- package/wiki/AI-Agent.md +372 -0
- package/wiki/Architecture-Overview.md +303 -0
- package/wiki/Audio-System.md +449 -0
- package/wiki/CPU-Emulation.md +477 -0
- package/wiki/Debugger.md +516 -0
- package/wiki/Disk-Drives.md +161 -0
- package/wiki/Disk-System-Internals.md +547 -0
- package/wiki/Display-Settings.md +88 -0
- package/wiki/Expansion-Slots.md +187 -0
- package/wiki/File-Explorer.md +259 -0
- package/wiki/Getting-Started.md +156 -0
- package/wiki/Home.md +69 -0
- package/wiki/Input-Devices.md +183 -0
- package/wiki/Keyboard-Shortcuts.md +158 -0
- package/wiki/Memory-System.md +364 -0
- package/wiki/Save-States.md +172 -0
- package/wiki/Video-Rendering.md +658 -0
package/src/js/main.js
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* main.js - Main entry point and AppleIIeEmulator class
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// CSS imports - bundled by Vite with content hashes for cache busting
|
|
9
|
+
import "../css/base.css";
|
|
10
|
+
import "../css/layout.css";
|
|
11
|
+
import "../css/monitor.css";
|
|
12
|
+
import "../css/disk-drives.css";
|
|
13
|
+
import "../css/hard-drive.css";
|
|
14
|
+
import "../css/controls.css";
|
|
15
|
+
import "../css/modals.css";
|
|
16
|
+
import "../css/debug-base.css";
|
|
17
|
+
import "../css/cpu-debugger.css";
|
|
18
|
+
import "../css/memory-windows.css";
|
|
19
|
+
import "../css/settings-windows.css";
|
|
20
|
+
import "../css/save-states.css";
|
|
21
|
+
import "../css/rule-builder.css";
|
|
22
|
+
import "../css/basic-editor.css";
|
|
23
|
+
import "../css/basic-debugger.css";
|
|
24
|
+
import "../css/assembler-editor.css";
|
|
25
|
+
import "../css/release-notes.css";
|
|
26
|
+
import "../css/file-explorer.css";
|
|
27
|
+
import "../css/documentation.css";
|
|
28
|
+
import "../css/window-switcher.css";
|
|
29
|
+
import "../css/responsive.css";
|
|
30
|
+
|
|
31
|
+
import { VERSION } from "./config/version.js";
|
|
32
|
+
import { DEFAULT_LAYOUT } from "./config/default-layout.js";
|
|
33
|
+
import { WebGLRenderer } from "./display/webgl-renderer.js";
|
|
34
|
+
import { AudioDriver } from "./audio/audio-driver.js";
|
|
35
|
+
import { InputHandler, TextSelection, JoystickWindow, MouseHandler, GamepadHandler } from "./input/index.js";
|
|
36
|
+
import { DiskManager } from "./disk-manager/index.js";
|
|
37
|
+
import { DiskDrivesWindow } from "./disk-manager/disk-drives-window.js";
|
|
38
|
+
import { HardDriveManager } from "./disk-manager/hard-drive-manager.js";
|
|
39
|
+
import { HardDriveWindow } from "./disk-manager/hard-drive-window.js";
|
|
40
|
+
import { FileExplorerWindow } from "./file-explorer/index.js";
|
|
41
|
+
import { DisplaySettingsWindow, ScreenWindow } from "./display/index.js";
|
|
42
|
+
import { DocumentationWindow, ReleaseNotesWindow } from "./help/index.js";
|
|
43
|
+
import { ReminderController } from "./ui/reminder-controller.js";
|
|
44
|
+
import { UIController } from "./ui/ui-controller.js";
|
|
45
|
+
import { ThemeManager } from "./ui/theme-manager.js";
|
|
46
|
+
import { showToast } from "./ui/toast.js";
|
|
47
|
+
import { SlotConfigurationWindow } from "./ui/slot-configuration-window.js";
|
|
48
|
+
import { WindowSwitcher } from "./ui/window-switcher.js";
|
|
49
|
+
import { StateManager } from "./state/state-manager.js";
|
|
50
|
+
import { SaveStatesWindow } from "./state/save-states-window.js";
|
|
51
|
+
import { AgentManager } from "./agent/index.js";
|
|
52
|
+
import {
|
|
53
|
+
WindowManager,
|
|
54
|
+
CPUDebuggerWindow,
|
|
55
|
+
SoftSwitchWindow,
|
|
56
|
+
MemoryBrowserWindow,
|
|
57
|
+
MemoryHeatMapWindow,
|
|
58
|
+
MemoryMapWindow,
|
|
59
|
+
StackViewerWindow,
|
|
60
|
+
ZeroPageWatchWindow,
|
|
61
|
+
MockingboardWindow,
|
|
62
|
+
MouseCardWindow,
|
|
63
|
+
BasicProgramWindow,
|
|
64
|
+
RuleBuilderWindow,
|
|
65
|
+
AssemblerEditorWindow,
|
|
66
|
+
TracePanelWindow,
|
|
67
|
+
} from "./debug/index.js";
|
|
68
|
+
|
|
69
|
+
class AppleIIeEmulator {
|
|
70
|
+
constructor() {
|
|
71
|
+
this.wasmModule = null;
|
|
72
|
+
this.renderer = null;
|
|
73
|
+
this.audioDriver = null;
|
|
74
|
+
this.inputHandler = null;
|
|
75
|
+
this.diskManager = null;
|
|
76
|
+
this.hardDriveManager = null;
|
|
77
|
+
this.fileExplorer = null;
|
|
78
|
+
this.windowManager = null;
|
|
79
|
+
this.displaySettings = null;
|
|
80
|
+
this.textSelection = null;
|
|
81
|
+
this.reminderController = null;
|
|
82
|
+
this.documentationWindow = null;
|
|
83
|
+
this.uiController = null;
|
|
84
|
+
this.stateManager = null;
|
|
85
|
+
this.mouseHandler = null;
|
|
86
|
+
this.themeManager = null;
|
|
87
|
+
this.agentManager = null;
|
|
88
|
+
|
|
89
|
+
this.running = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async init() {
|
|
93
|
+
// Apply theme before any rendering to prevent flash of wrong theme
|
|
94
|
+
this.themeManager = new ThemeManager();
|
|
95
|
+
|
|
96
|
+
this.showLoading(true);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// Load WASM module - use global function loaded via script tag
|
|
100
|
+
this.wasmModule = await window.createA2EModule();
|
|
101
|
+
|
|
102
|
+
// Initialize emulator
|
|
103
|
+
this.wasmModule._init();
|
|
104
|
+
|
|
105
|
+
// Set up renderer
|
|
106
|
+
const canvas = document.getElementById("screen");
|
|
107
|
+
this.renderer = new WebGLRenderer(canvas);
|
|
108
|
+
await this.renderer.init();
|
|
109
|
+
|
|
110
|
+
// Set up audio driver (drives timing)
|
|
111
|
+
this.audioDriver = new AudioDriver(this.wasmModule);
|
|
112
|
+
|
|
113
|
+
// Connect audio-driven frame sync to rendering via rAF flag
|
|
114
|
+
// (avoids doing GPU texture uploads from the audio callback context,
|
|
115
|
+
// which causes stutters in Safari and Brave under heavy I/O)
|
|
116
|
+
this.frameReady = false;
|
|
117
|
+
this.audioDriver.onFrameReady = () => {
|
|
118
|
+
this.frameReady = true;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Set up input handler
|
|
122
|
+
this.inputHandler = new InputHandler(this.wasmModule);
|
|
123
|
+
this.inputHandler.init();
|
|
124
|
+
|
|
125
|
+
// Set up mouse handler for Apple Mouse Interface Card
|
|
126
|
+
this.mouseHandler = new MouseHandler(this.wasmModule);
|
|
127
|
+
this.mouseHandler.init();
|
|
128
|
+
|
|
129
|
+
// Set up window manager
|
|
130
|
+
this.windowManager = new WindowManager();
|
|
131
|
+
|
|
132
|
+
// Set up file explorer (registered with window manager for proper z-index/focus)
|
|
133
|
+
this.fileExplorer = new FileExplorerWindow(this.wasmModule);
|
|
134
|
+
this.fileExplorer.create();
|
|
135
|
+
this.windowManager.register(this.fileExplorer);
|
|
136
|
+
|
|
137
|
+
// Create disk drives window first so DiskManager can find its DOM elements
|
|
138
|
+
const diskDrivesWindow = new DiskDrivesWindow();
|
|
139
|
+
diskDrivesWindow.create();
|
|
140
|
+
this.windowManager.register(diskDrivesWindow);
|
|
141
|
+
|
|
142
|
+
// Set up disk manager (must be after disk drives window is created)
|
|
143
|
+
this.diskManager = new DiskManager(this.wasmModule);
|
|
144
|
+
this.diskManager.init();
|
|
145
|
+
this.diskManager.onDiskLoaded = () => {
|
|
146
|
+
this.reminderController.dismissBasicReminder();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Create hard drive window and manager
|
|
150
|
+
const hardDriveWindow = new HardDriveWindow();
|
|
151
|
+
hardDriveWindow.create();
|
|
152
|
+
this.windowManager.register(hardDriveWindow);
|
|
153
|
+
|
|
154
|
+
this.diskManager.fileExplorer = this.fileExplorer;
|
|
155
|
+
|
|
156
|
+
this.hardDriveManager = new HardDriveManager(this.wasmModule);
|
|
157
|
+
this.hardDriveManager.fileExplorer = this.fileExplorer;
|
|
158
|
+
this.hardDriveManager.init();
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
const cpuWindow = new CPUDebuggerWindow(this.wasmModule, () => this.isRunning());
|
|
162
|
+
cpuWindow.create();
|
|
163
|
+
this.windowManager.register(cpuWindow);
|
|
164
|
+
this.cpuDebuggerWindow = cpuWindow;
|
|
165
|
+
|
|
166
|
+
const ruleBuilderWindow = new RuleBuilderWindow();
|
|
167
|
+
ruleBuilderWindow.create();
|
|
168
|
+
this.windowManager.register(ruleBuilderWindow);
|
|
169
|
+
|
|
170
|
+
ruleBuilderWindow.onApply = (addr, condStr, rules) => {
|
|
171
|
+
cpuWindow.bpManager.setCondition(addr, condStr);
|
|
172
|
+
cpuWindow.bpManager.setConditionRules(addr, rules);
|
|
173
|
+
};
|
|
174
|
+
cpuWindow.setRuleBuilder(ruleBuilderWindow);
|
|
175
|
+
|
|
176
|
+
// BASIC breakpoint condition callback - will be wired after basicProgramWindow is created
|
|
177
|
+
ruleBuilderWindow.onApplyBasic = (key, condStr, rules) => {
|
|
178
|
+
if (this.basicProgramWindow) {
|
|
179
|
+
const bpMgr = this.basicProgramWindow.breakpointManager;
|
|
180
|
+
if (key === "__new_rule__") {
|
|
181
|
+
// New condition-only rule
|
|
182
|
+
bpMgr.addConditionRule(condStr, rules);
|
|
183
|
+
} else {
|
|
184
|
+
const [lineStr, stmtStr] = key.split(":");
|
|
185
|
+
const lineNum = parseInt(lineStr, 10);
|
|
186
|
+
const stmtIdx = parseInt(stmtStr, 10);
|
|
187
|
+
bpMgr.setCondition(lineNum, stmtIdx, condStr);
|
|
188
|
+
bpMgr.setConditionRules(lineNum, stmtIdx, rules);
|
|
189
|
+
}
|
|
190
|
+
this.basicProgramWindow.renderBreakpointList();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const switchWindow = new SoftSwitchWindow(this.wasmModule);
|
|
195
|
+
switchWindow.create();
|
|
196
|
+
this.windowManager.register(switchWindow);
|
|
197
|
+
|
|
198
|
+
// Set up display settings window (pass renderer for shader control, wasmModule for video settings)
|
|
199
|
+
this.displaySettings = new DisplaySettingsWindow(
|
|
200
|
+
this.renderer,
|
|
201
|
+
this.wasmModule,
|
|
202
|
+
);
|
|
203
|
+
this.displaySettings.create();
|
|
204
|
+
this.windowManager.register(this.displaySettings);
|
|
205
|
+
|
|
206
|
+
// Set up screen window (hosts the emulator canvas)
|
|
207
|
+
this.screenWindow = new ScreenWindow(this.renderer, null); // textSelection set later
|
|
208
|
+
this.screenWindow.create();
|
|
209
|
+
this.windowManager.register(this.screenWindow);
|
|
210
|
+
|
|
211
|
+
// Set up new debug windows
|
|
212
|
+
const memBrowserWindow = new MemoryBrowserWindow(this.wasmModule);
|
|
213
|
+
memBrowserWindow.create();
|
|
214
|
+
this.windowManager.register(memBrowserWindow);
|
|
215
|
+
|
|
216
|
+
const memHeatMapWindow = new MemoryHeatMapWindow(this.wasmModule);
|
|
217
|
+
memHeatMapWindow.create();
|
|
218
|
+
this.windowManager.register(memHeatMapWindow);
|
|
219
|
+
|
|
220
|
+
// Connect heat map to memory browser for click-to-jump
|
|
221
|
+
memHeatMapWindow.setJumpCallback((addr) => {
|
|
222
|
+
memBrowserWindow.jumpToAddress(addr);
|
|
223
|
+
this.windowManager.showWindow("memory-browser");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const memMapWindow = new MemoryMapWindow(this.wasmModule);
|
|
227
|
+
memMapWindow.create();
|
|
228
|
+
this.windowManager.register(memMapWindow);
|
|
229
|
+
|
|
230
|
+
const stackWindow = new StackViewerWindow(this.wasmModule);
|
|
231
|
+
stackWindow.create();
|
|
232
|
+
this.windowManager.register(stackWindow);
|
|
233
|
+
|
|
234
|
+
const zpWatchWindow = new ZeroPageWatchWindow(this.wasmModule);
|
|
235
|
+
zpWatchWindow.create();
|
|
236
|
+
this.windowManager.register(zpWatchWindow);
|
|
237
|
+
|
|
238
|
+
const joystickWindow = new JoystickWindow(this.wasmModule);
|
|
239
|
+
joystickWindow.create();
|
|
240
|
+
this.windowManager.register(joystickWindow);
|
|
241
|
+
|
|
242
|
+
this.gamepadHandler = new GamepadHandler(this.wasmModule, joystickWindow);
|
|
243
|
+
joystickWindow.gamepadHandler = this.gamepadHandler;
|
|
244
|
+
|
|
245
|
+
const mockingboardWindow = new MockingboardWindow(this.wasmModule);
|
|
246
|
+
mockingboardWindow.create();
|
|
247
|
+
this.windowManager.register(mockingboardWindow);
|
|
248
|
+
|
|
249
|
+
const mouseCardWindow = new MouseCardWindow(this.wasmModule);
|
|
250
|
+
mouseCardWindow.create();
|
|
251
|
+
this.windowManager.register(mouseCardWindow);
|
|
252
|
+
|
|
253
|
+
const tracePanelWindow = new TracePanelWindow(this.wasmModule);
|
|
254
|
+
tracePanelWindow.create();
|
|
255
|
+
this.windowManager.register(tracePanelWindow);
|
|
256
|
+
|
|
257
|
+
const basicProgramWindow = new BasicProgramWindow(
|
|
258
|
+
this.wasmModule,
|
|
259
|
+
this.inputHandler,
|
|
260
|
+
() => this.isRunning(),
|
|
261
|
+
);
|
|
262
|
+
basicProgramWindow.create();
|
|
263
|
+
this.windowManager.register(basicProgramWindow);
|
|
264
|
+
this.basicProgramWindow = basicProgramWindow;
|
|
265
|
+
basicProgramWindow.setRuleBuilder(ruleBuilderWindow);
|
|
266
|
+
|
|
267
|
+
const assemblerWindow = new AssemblerEditorWindow(this.wasmModule, cpuWindow.bpManager, () => this.isRunning(), cpuWindow);
|
|
268
|
+
assemblerWindow.create();
|
|
269
|
+
this.windowManager.register(assemblerWindow);
|
|
270
|
+
|
|
271
|
+
// Slot configuration window
|
|
272
|
+
const slotConfigWindow = new SlotConfigurationWindow(
|
|
273
|
+
this.wasmModule,
|
|
274
|
+
() => {
|
|
275
|
+
this.wasmModule._reset();
|
|
276
|
+
this.updateMouseHandlerState();
|
|
277
|
+
if (this.hardDriveManager) {
|
|
278
|
+
this.hardDriveManager.syncWithEmulatorState();
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
);
|
|
282
|
+
slotConfigWindow.create();
|
|
283
|
+
this.windowManager.register(slotConfigWindow);
|
|
284
|
+
|
|
285
|
+
// Release notes window
|
|
286
|
+
this.releaseNotesWindow = new ReleaseNotesWindow();
|
|
287
|
+
this.releaseNotesWindow.create();
|
|
288
|
+
this.windowManager.register(this.releaseNotesWindow);
|
|
289
|
+
|
|
290
|
+
// Release notes button in footer
|
|
291
|
+
const releaseNotesBtn = document.getElementById("btn-release-notes");
|
|
292
|
+
if (releaseNotesBtn) {
|
|
293
|
+
releaseNotesBtn.addEventListener("click", () => {
|
|
294
|
+
this.windowManager.toggleWindow("release-notes");
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Set up documentation window
|
|
299
|
+
this.documentationWindow = new DocumentationWindow();
|
|
300
|
+
this.documentationWindow.create();
|
|
301
|
+
this.windowManager.register(this.documentationWindow);
|
|
302
|
+
|
|
303
|
+
// Load saved window states (must be after all windows are registered)
|
|
304
|
+
this.windowManager.loadState();
|
|
305
|
+
|
|
306
|
+
// Save window states when page is closed
|
|
307
|
+
window.addEventListener("beforeunload", () => {
|
|
308
|
+
if (this.windowManager) {
|
|
309
|
+
this.windowManager.saveState();
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Start with TV static "no signal" since emulator is off
|
|
314
|
+
this.renderer.setNoSignal(true);
|
|
315
|
+
|
|
316
|
+
// Set up text selection for copying screen contents
|
|
317
|
+
this.textSelection = new TextSelection(canvas, this.wasmModule, this.renderer);
|
|
318
|
+
|
|
319
|
+
// Wire textSelection into screen window
|
|
320
|
+
this.screenWindow.textSelection = this.textSelection;
|
|
321
|
+
|
|
322
|
+
// Set up reminder controller
|
|
323
|
+
this.reminderController = new ReminderController();
|
|
324
|
+
|
|
325
|
+
// Apply display settings
|
|
326
|
+
this.displaySettings.applyAllSettings();
|
|
327
|
+
|
|
328
|
+
// Ensure ScreenWindow is visible (loadState may have already shown it)
|
|
329
|
+
if (!this.screenWindow.isVisible) {
|
|
330
|
+
this.screenWindow.show();
|
|
331
|
+
}
|
|
332
|
+
this.screenWindow.attachCanvas();
|
|
333
|
+
|
|
334
|
+
// Position/size windows for first-time users (no saved state)
|
|
335
|
+
this.windowManager.applyDefaultLayout(DEFAULT_LAYOUT);
|
|
336
|
+
|
|
337
|
+
// Set up window switcher (Ctrl+`)
|
|
338
|
+
this.windowSwitcher = new WindowSwitcher(this.windowManager);
|
|
339
|
+
this.windowSwitcher.create();
|
|
340
|
+
|
|
341
|
+
// Set up agent manager for MCP server connection
|
|
342
|
+
window.emulator = this;
|
|
343
|
+
this.agentManager = new AgentManager();
|
|
344
|
+
|
|
345
|
+
// Set up UI controller
|
|
346
|
+
this.uiController = new UIController({
|
|
347
|
+
emulator: this,
|
|
348
|
+
wasmModule: this.wasmModule,
|
|
349
|
+
audioDriver: this.audioDriver,
|
|
350
|
+
diskManager: this.diskManager,
|
|
351
|
+
fileExplorer: this.fileExplorer,
|
|
352
|
+
windowManager: this.windowManager,
|
|
353
|
+
screenWindow: this.screenWindow,
|
|
354
|
+
reminderController: this.reminderController,
|
|
355
|
+
inputHandler: this.inputHandler,
|
|
356
|
+
themeManager: this.themeManager,
|
|
357
|
+
windowSwitcher: this.windowSwitcher,
|
|
358
|
+
});
|
|
359
|
+
this.uiController.init();
|
|
360
|
+
|
|
361
|
+
// Set up state manager
|
|
362
|
+
this.stateManager = new StateManager({
|
|
363
|
+
emulator: this,
|
|
364
|
+
wasmModule: this.wasmModule,
|
|
365
|
+
uiController: this.uiController,
|
|
366
|
+
diskManager: this.diskManager,
|
|
367
|
+
reminderController: this.reminderController,
|
|
368
|
+
cpuDebuggerWindow: cpuWindow,
|
|
369
|
+
basicProgramWindow: this.basicProgramWindow,
|
|
370
|
+
});
|
|
371
|
+
this.stateManager.init();
|
|
372
|
+
|
|
373
|
+
// Save States window
|
|
374
|
+
const saveStatesWindow = new SaveStatesWindow(this.stateManager, this.uiController);
|
|
375
|
+
saveStatesWindow.create();
|
|
376
|
+
this.windowManager.register(saveStatesWindow);
|
|
377
|
+
|
|
378
|
+
// Keep autosave row current when the window is open
|
|
379
|
+
this.stateManager.onAutosave = () => {
|
|
380
|
+
if (saveStatesWindow.isVisible) {
|
|
381
|
+
saveStatesWindow.refreshAutosaveRow();
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// Enable mouse handler if a mouse card is configured
|
|
386
|
+
this.updateMouseHandlerState();
|
|
387
|
+
|
|
388
|
+
// Start render loop
|
|
389
|
+
this.startRenderLoop();
|
|
390
|
+
|
|
391
|
+
this.showLoading(false);
|
|
392
|
+
this.reminderController.showPowerReminder(true);
|
|
393
|
+
|
|
394
|
+
console.log("Apple //e Emulator initialized");
|
|
395
|
+
} catch (error) {
|
|
396
|
+
console.error("Failed to initialize emulator:", error);
|
|
397
|
+
this.showLoading(false);
|
|
398
|
+
showToast("Failed to initialize emulator: " + error.message, "error");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Check slot configuration and enable/disable mouse handler accordingly
|
|
404
|
+
*/
|
|
405
|
+
updateMouseHandlerState() {
|
|
406
|
+
if (!this.mouseHandler || !this.wasmModule._getSlotCard) return;
|
|
407
|
+
let mousePresent = false;
|
|
408
|
+
for (let slot = 1; slot <= 7; slot++) {
|
|
409
|
+
const ptr = this.wasmModule._getSlotCard(slot);
|
|
410
|
+
if (ptr) {
|
|
411
|
+
const name = this.wasmModule.UTF8ToString(ptr);
|
|
412
|
+
if (name === "mouse") {
|
|
413
|
+
mousePresent = true;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (mousePresent) {
|
|
419
|
+
this.mouseHandler.enable();
|
|
420
|
+
} else {
|
|
421
|
+
this.mouseHandler.disable();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Check if the emulator is running
|
|
427
|
+
* @returns {boolean}
|
|
428
|
+
*/
|
|
429
|
+
isRunning() {
|
|
430
|
+
return this.running;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
start() {
|
|
434
|
+
if (this.running) return;
|
|
435
|
+
|
|
436
|
+
if (this.inputHandler) this.inputHandler.cancelPaste();
|
|
437
|
+
this.wasmModule._reset();
|
|
438
|
+
this.running = true;
|
|
439
|
+
this.renderer.setNoSignal(false);
|
|
440
|
+
this.audioDriver.start();
|
|
441
|
+
if (this.uiController) {
|
|
442
|
+
this.uiController.updatePowerButton(true);
|
|
443
|
+
}
|
|
444
|
+
console.log("Emulator powered on");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
stop() {
|
|
448
|
+
if (!this.running) return;
|
|
449
|
+
|
|
450
|
+
this.running = false;
|
|
451
|
+
this.audioDriver.stop();
|
|
452
|
+
|
|
453
|
+
if (this.wasmModule._stopDiskMotor) {
|
|
454
|
+
this.wasmModule._stopDiskMotor();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
this.renderer.setNoSignal(true);
|
|
458
|
+
if (this.uiController) {
|
|
459
|
+
this.uiController.updatePowerButton(false);
|
|
460
|
+
}
|
|
461
|
+
console.log("Emulator powered off");
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
renderFrame() {
|
|
465
|
+
const fbPtr = this.wasmModule._getFramebuffer();
|
|
466
|
+
const fbSize = this.wasmModule._getFramebufferSize();
|
|
467
|
+
const framebuffer = new Uint8Array(
|
|
468
|
+
this.wasmModule.HEAPU8.buffer,
|
|
469
|
+
fbPtr,
|
|
470
|
+
fbSize,
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
this.renderer.updateTexture(framebuffer);
|
|
474
|
+
this.renderer.draw();
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
startRenderLoop() {
|
|
478
|
+
const render = () => {
|
|
479
|
+
this.windowManager.updateAll(this.wasmModule);
|
|
480
|
+
this.diskManager.drivesWindowVisible = this.windowManager.isWindowVisible('disk-drives');
|
|
481
|
+
this.diskManager.updateLEDs();
|
|
482
|
+
if (this.hardDriveManager) {
|
|
483
|
+
this.hardDriveManager.updateLEDs();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Beam crosshair overlay — only when CPU debugger is open and CPU is paused
|
|
487
|
+
const isPaused = this.running && this.wasmModule._isPaused();
|
|
488
|
+
if (isPaused && this.cpuDebuggerWindow && this.cpuDebuggerWindow.isVisible) {
|
|
489
|
+
const scanline = this.wasmModule._getBeamScanline();
|
|
490
|
+
const hPos = this.wasmModule._getBeamHPos();
|
|
491
|
+
// Map beam Y to center of scanline band (0–191 visible, ≥192 is VBL)
|
|
492
|
+
this.renderer.setParam("beamY", scanline < 192 ? (scanline + 0.5) / 192.0 : -1.0);
|
|
493
|
+
// Map beam X to leading edge of column (hPos 25–64 → columns 0–39)
|
|
494
|
+
this.renderer.setParam("beamX", hPos >= 25 ? (hPos - 25) / 40.0 : -1.0);
|
|
495
|
+
} else {
|
|
496
|
+
this.renderer.setParam("beamY", -1.0);
|
|
497
|
+
this.renderer.setParam("beamX", -1.0);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (this.frameReady) {
|
|
501
|
+
this.frameReady = false;
|
|
502
|
+
this.renderFrame();
|
|
503
|
+
} else if (!this.running || isPaused) {
|
|
504
|
+
// Force a complete re-render of the framebuffer from current video
|
|
505
|
+
// memory so the display shows the full screen after stepping/pausing,
|
|
506
|
+
// not a partial frame based on beam position.
|
|
507
|
+
if (isPaused) {
|
|
508
|
+
this.wasmModule._forceRenderFrame();
|
|
509
|
+
this.renderFrame();
|
|
510
|
+
} else {
|
|
511
|
+
this.renderer.draw();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
requestAnimationFrame(render);
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
requestAnimationFrame(render);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
showLoading(show) {
|
|
522
|
+
const loading = document.getElementById("loading");
|
|
523
|
+
if (show) {
|
|
524
|
+
loading.classList.remove("hidden");
|
|
525
|
+
} else {
|
|
526
|
+
loading.classList.add("hidden");
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Clean up resources and remove event listeners.
|
|
532
|
+
*/
|
|
533
|
+
destroy() {
|
|
534
|
+
if (this.running) {
|
|
535
|
+
this.stop();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (this.stateManager) {
|
|
539
|
+
this.stateManager.destroy();
|
|
540
|
+
this.stateManager = null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (this.textSelection) {
|
|
544
|
+
this.textSelection.destroy();
|
|
545
|
+
this.textSelection = null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (this.windowManager) {
|
|
549
|
+
this.windowManager.saveState();
|
|
550
|
+
this.windowManager = null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (this.audioDriver) {
|
|
554
|
+
this.audioDriver.stop();
|
|
555
|
+
this.audioDriver = null;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (this.themeManager) {
|
|
559
|
+
this.themeManager.destroy();
|
|
560
|
+
this.themeManager = null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (this.agentManager) {
|
|
564
|
+
this.agentManager.disconnect();
|
|
565
|
+
this.agentManager = null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (this.gamepadHandler) {
|
|
569
|
+
this.gamepadHandler.destroy();
|
|
570
|
+
this.gamepadHandler = null;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
this.renderer = null;
|
|
574
|
+
this.diskManager = null;
|
|
575
|
+
this.hardDriveManager = null;
|
|
576
|
+
if (this.fileExplorer) {
|
|
577
|
+
this.fileExplorer.destroy();
|
|
578
|
+
this.fileExplorer = null;
|
|
579
|
+
}
|
|
580
|
+
this.inputHandler = null;
|
|
581
|
+
this.reminderController = null;
|
|
582
|
+
this.uiController = null;
|
|
583
|
+
|
|
584
|
+
console.log("Apple //e Emulator destroyed");
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Register service worker for offline support only when installed as PWA
|
|
589
|
+
const isInstalled = window.matchMedia("(display-mode: standalone)").matches || navigator.standalone;
|
|
590
|
+
if ("serviceWorker" in navigator && isInstalled) {
|
|
591
|
+
window.addEventListener("load", () => {
|
|
592
|
+
navigator.serviceWorker
|
|
593
|
+
.register("/sw.js")
|
|
594
|
+
.then((registration) => {
|
|
595
|
+
console.log("Service Worker registered:", registration.scope);
|
|
596
|
+
|
|
597
|
+
// Check for updates immediately on load
|
|
598
|
+
registration.update().catch((err) => {
|
|
599
|
+
console.log("Service Worker update check failed:", err);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// Handle new service worker installation
|
|
603
|
+
registration.addEventListener("updatefound", () => {
|
|
604
|
+
const newWorker = registration.installing;
|
|
605
|
+
if (newWorker) {
|
|
606
|
+
newWorker.addEventListener("statechange", () => {
|
|
607
|
+
if (newWorker.state === "installed") {
|
|
608
|
+
if (navigator.serviceWorker.controller) {
|
|
609
|
+
// New version available - show badge on Help button
|
|
610
|
+
console.log("New version available - badge shown on Help button");
|
|
611
|
+
const helpBtn = document.getElementById("btn-help-menu");
|
|
612
|
+
if (helpBtn) {
|
|
613
|
+
helpBtn.classList.add("update-available");
|
|
614
|
+
}
|
|
615
|
+
} else {
|
|
616
|
+
// First install - no reload needed
|
|
617
|
+
console.log("App cached for offline use");
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
})
|
|
624
|
+
.catch((error) => {
|
|
625
|
+
console.log("Service Worker registration failed:", error);
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Initialize when DOM is ready
|
|
631
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
632
|
+
// Display version in header
|
|
633
|
+
const versionEl = document.getElementById("app-version");
|
|
634
|
+
if (versionEl) {
|
|
635
|
+
versionEl.textContent = `v${VERSION}`;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const emulator = new AppleIIeEmulator();
|
|
639
|
+
emulator.init();
|
|
640
|
+
|
|
641
|
+
// Make emulator accessible globally for debugging
|
|
642
|
+
window.a2e = emulator;
|
|
643
|
+
|
|
644
|
+
// Helper to toggle Mockingboard debug logging from console
|
|
645
|
+
window.mbDebug = (enabled = true) => {
|
|
646
|
+
if (emulator.wasmModule && emulator.wasmModule._setMockingboardDebugLogging) {
|
|
647
|
+
emulator.wasmModule._setMockingboardDebugLogging(enabled);
|
|
648
|
+
console.log(`Mockingboard debug logging ${enabled ? "enabled" : "disabled"}`);
|
|
649
|
+
} else {
|
|
650
|
+
console.log("Mockingboard debug logging not available");
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* index.js - State subsystem initialization and exports
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { StateManager } from "./state-manager.js";
|
|
9
|
+
export {
|
|
10
|
+
saveStateToStorage,
|
|
11
|
+
loadStateFromStorage,
|
|
12
|
+
clearStateFromStorage,
|
|
13
|
+
hasSavedState,
|
|
14
|
+
getSavedStateTimestamp,
|
|
15
|
+
} from "./state-persistence.js";
|