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.
Files changed (295) hide show
  1. package/.clangd +5 -0
  2. package/.mcp.json +12 -0
  3. package/CLAUDE.md +362 -0
  4. package/CMakeLists.txt +774 -0
  5. package/LICENSE +21 -0
  6. package/README.md +392 -0
  7. package/build-wasm/generated/roms.cpp +2447 -0
  8. package/docker-compose.staging.yml +9 -0
  9. package/docs/basic-rom-disassembly.md +6663 -0
  10. package/docs/softswitch-comparison.md +273 -0
  11. package/docs/thunderclock-debug.md +89 -0
  12. package/examples/cube.bas +72 -0
  13. package/examples/hello.s +55 -0
  14. package/examples/scroll.s +140 -0
  15. package/package.json +18 -0
  16. package/public/assets/apple-logo-old.png +0 -0
  17. package/public/assets/apple-logo.png +0 -0
  18. package/public/assets/drive-closed-light-on.png +0 -0
  19. package/public/assets/drive-closed.png +0 -0
  20. package/public/assets/drive-open-light-on.png +0 -0
  21. package/public/assets/drive-open.png +0 -0
  22. package/public/audio-worklet.js +82 -0
  23. package/public/disks/Apple DOS 3.3 January 1983.dsk +0 -0
  24. package/public/disks/ProDOS 2.4.3.po +0 -0
  25. package/public/disks/h32mb.2mg +0 -0
  26. package/public/disks/library.json +26 -0
  27. package/public/docs/llms/llm-assembler.txt +90 -0
  28. package/public/docs/llms/llm-basic-program.txt +256 -0
  29. package/public/docs/llms/llm-disk-drives.txt +72 -0
  30. package/public/docs/llms/llm-file-explorer.txt +50 -0
  31. package/public/docs/llms/llm-hard-drives.txt +80 -0
  32. package/public/docs/llms/llm-main.txt +51 -0
  33. package/public/docs/llms/llm-slot-configuration.txt +66 -0
  34. package/public/icons/icon-192.svg +4 -0
  35. package/public/icons/icon-512.svg +4 -0
  36. package/public/index.html +661 -0
  37. package/public/llms.txt +49 -0
  38. package/public/manifest.json +29 -0
  39. package/public/shaders/burnin.glsl +22 -0
  40. package/public/shaders/crt.glsl +706 -0
  41. package/public/shaders/edge.glsl +109 -0
  42. package/public/shaders/vertex.glsl +8 -0
  43. package/public/sw.js +186 -0
  44. package/roms/341-0027.bin +0 -0
  45. package/roms/341-0160-A-US-UK.bin +0 -0
  46. package/roms/341-0160-A.bin +0 -0
  47. package/roms/342-0273-A-US-UK.bin +0 -0
  48. package/roms/342-0349-B-C0-FF.bin +0 -0
  49. package/roms/Apple Mouse Interface Card ROM - 342-0270-C.bin +0 -0
  50. package/roms/Thunderclock Plus ROM.bin +0 -0
  51. package/scripts/generate_roms.sh +69 -0
  52. package/src/bindings/wasm_interface.cpp +1940 -0
  53. package/src/core/assembler/assembler.cpp +1239 -0
  54. package/src/core/assembler/assembler.hpp +115 -0
  55. package/src/core/audio/audio.cpp +160 -0
  56. package/src/core/audio/audio.hpp +81 -0
  57. package/src/core/basic/basic_detokenizer.cpp +436 -0
  58. package/src/core/basic/basic_detokenizer.hpp +41 -0
  59. package/src/core/basic/basic_tokenizer.cpp +286 -0
  60. package/src/core/basic/basic_tokenizer.hpp +26 -0
  61. package/src/core/basic/basic_tokens.hpp +295 -0
  62. package/src/core/cards/disk2_card.cpp +568 -0
  63. package/src/core/cards/disk2_card.hpp +316 -0
  64. package/src/core/cards/expansion_card.hpp +185 -0
  65. package/src/core/cards/mockingboard/ay8910.cpp +616 -0
  66. package/src/core/cards/mockingboard/ay8910.hpp +159 -0
  67. package/src/core/cards/mockingboard/via6522.cpp +530 -0
  68. package/src/core/cards/mockingboard/via6522.hpp +163 -0
  69. package/src/core/cards/mockingboard_card.cpp +312 -0
  70. package/src/core/cards/mockingboard_card.hpp +159 -0
  71. package/src/core/cards/mouse_card.cpp +654 -0
  72. package/src/core/cards/mouse_card.hpp +190 -0
  73. package/src/core/cards/smartport/block_device.cpp +202 -0
  74. package/src/core/cards/smartport/block_device.hpp +60 -0
  75. package/src/core/cards/smartport/smartport_card.cpp +603 -0
  76. package/src/core/cards/smartport/smartport_card.hpp +120 -0
  77. package/src/core/cards/thunderclock_card.cpp +237 -0
  78. package/src/core/cards/thunderclock_card.hpp +122 -0
  79. package/src/core/cpu/cpu6502.cpp +1609 -0
  80. package/src/core/cpu/cpu6502.hpp +203 -0
  81. package/src/core/debug/condition_evaluator.cpp +470 -0
  82. package/src/core/debug/condition_evaluator.hpp +87 -0
  83. package/src/core/disassembler/disassembler.cpp +552 -0
  84. package/src/core/disassembler/disassembler.hpp +171 -0
  85. package/src/core/disk-image/disk_image.hpp +267 -0
  86. package/src/core/disk-image/dsk_disk_image.cpp +827 -0
  87. package/src/core/disk-image/dsk_disk_image.hpp +204 -0
  88. package/src/core/disk-image/gcr_encoding.cpp +147 -0
  89. package/src/core/disk-image/gcr_encoding.hpp +78 -0
  90. package/src/core/disk-image/woz_disk_image.cpp +1049 -0
  91. package/src/core/disk-image/woz_disk_image.hpp +343 -0
  92. package/src/core/emulator.cpp +2126 -0
  93. package/src/core/emulator.hpp +434 -0
  94. package/src/core/filesystem/dos33.cpp +178 -0
  95. package/src/core/filesystem/dos33.hpp +66 -0
  96. package/src/core/filesystem/pascal.cpp +262 -0
  97. package/src/core/filesystem/pascal.hpp +87 -0
  98. package/src/core/filesystem/prodos.cpp +369 -0
  99. package/src/core/filesystem/prodos.hpp +119 -0
  100. package/src/core/input/keyboard.cpp +227 -0
  101. package/src/core/input/keyboard.hpp +111 -0
  102. package/src/core/mmu/mmu.cpp +1387 -0
  103. package/src/core/mmu/mmu.hpp +236 -0
  104. package/src/core/types.hpp +196 -0
  105. package/src/core/video/video.cpp +680 -0
  106. package/src/core/video/video.hpp +156 -0
  107. package/src/css/assembler-editor.css +1617 -0
  108. package/src/css/base.css +470 -0
  109. package/src/css/basic-debugger.css +791 -0
  110. package/src/css/basic-editor.css +792 -0
  111. package/src/css/controls.css +783 -0
  112. package/src/css/cpu-debugger.css +1413 -0
  113. package/src/css/debug-base.css +160 -0
  114. package/src/css/debug-windows.css +6455 -0
  115. package/src/css/disk-drives.css +406 -0
  116. package/src/css/documentation.css +392 -0
  117. package/src/css/file-explorer.css +867 -0
  118. package/src/css/hard-drive.css +180 -0
  119. package/src/css/layout.css +217 -0
  120. package/src/css/memory-windows.css +798 -0
  121. package/src/css/modals.css +510 -0
  122. package/src/css/monitor.css +425 -0
  123. package/src/css/release-notes.css +101 -0
  124. package/src/css/responsive.css +400 -0
  125. package/src/css/rule-builder.css +340 -0
  126. package/src/css/save-states.css +201 -0
  127. package/src/css/settings-windows.css +1231 -0
  128. package/src/css/window-switcher.css +150 -0
  129. package/src/js/agent/agent-manager.js +643 -0
  130. package/src/js/agent/agent-tools.js +293 -0
  131. package/src/js/agent/agent-version-tools.js +131 -0
  132. package/src/js/agent/assembler-tools.js +357 -0
  133. package/src/js/agent/basic-program-tools.js +894 -0
  134. package/src/js/agent/disk-tools.js +417 -0
  135. package/src/js/agent/file-explorer-tools.js +269 -0
  136. package/src/js/agent/index.js +13 -0
  137. package/src/js/agent/main-tools.js +222 -0
  138. package/src/js/agent/slot-tools.js +303 -0
  139. package/src/js/agent/smartport-tools.js +257 -0
  140. package/src/js/agent/window-tools.js +80 -0
  141. package/src/js/audio/audio-driver.js +417 -0
  142. package/src/js/audio/audio-worklet.js +85 -0
  143. package/src/js/audio/index.js +8 -0
  144. package/src/js/config/default-layout.js +34 -0
  145. package/src/js/config/version.js +8 -0
  146. package/src/js/data/apple2-rom-routines.js +577 -0
  147. package/src/js/debug/assembler-editor-window.js +2993 -0
  148. package/src/js/debug/basic-breakpoint-manager.js +529 -0
  149. package/src/js/debug/basic-program-parser.js +436 -0
  150. package/src/js/debug/basic-program-window.js +2594 -0
  151. package/src/js/debug/basic-variable-inspector.js +447 -0
  152. package/src/js/debug/breakpoint-manager.js +472 -0
  153. package/src/js/debug/cpu-debugger-window.js +2396 -0
  154. package/src/js/debug/index.js +22 -0
  155. package/src/js/debug/label-manager.js +238 -0
  156. package/src/js/debug/memory-browser-window.js +416 -0
  157. package/src/js/debug/memory-heat-map-window.js +481 -0
  158. package/src/js/debug/memory-map-window.js +206 -0
  159. package/src/js/debug/mockingboard-window.js +882 -0
  160. package/src/js/debug/mouse-card-window.js +355 -0
  161. package/src/js/debug/rule-builder-window.js +648 -0
  162. package/src/js/debug/soft-switch-window.js +458 -0
  163. package/src/js/debug/stack-viewer-window.js +221 -0
  164. package/src/js/debug/symbols.js +416 -0
  165. package/src/js/debug/trace-panel.js +291 -0
  166. package/src/js/debug/zero-page-watch-window.js +297 -0
  167. package/src/js/disk-manager/disk-drives-window.js +212 -0
  168. package/src/js/disk-manager/disk-operations.js +284 -0
  169. package/src/js/disk-manager/disk-persistence.js +301 -0
  170. package/src/js/disk-manager/disk-surface-renderer.js +388 -0
  171. package/src/js/disk-manager/drive-sounds.js +139 -0
  172. package/src/js/disk-manager/hard-drive-manager.js +481 -0
  173. package/src/js/disk-manager/hard-drive-persistence.js +187 -0
  174. package/src/js/disk-manager/hard-drive-window.js +57 -0
  175. package/src/js/disk-manager/index.js +890 -0
  176. package/src/js/display/display-settings-window.js +383 -0
  177. package/src/js/display/index.js +10 -0
  178. package/src/js/display/screen-window.js +342 -0
  179. package/src/js/display/webgl-renderer.js +705 -0
  180. package/src/js/file-explorer/disassembler.js +574 -0
  181. package/src/js/file-explorer/dos33.js +266 -0
  182. package/src/js/file-explorer/file-viewer.js +359 -0
  183. package/src/js/file-explorer/index.js +1261 -0
  184. package/src/js/file-explorer/prodos.js +549 -0
  185. package/src/js/file-explorer/utils.js +67 -0
  186. package/src/js/help/documentation-window.js +1096 -0
  187. package/src/js/help/index.js +10 -0
  188. package/src/js/help/release-notes-window.js +85 -0
  189. package/src/js/help/release-notes.js +612 -0
  190. package/src/js/input/gamepad-handler.js +176 -0
  191. package/src/js/input/index.js +12 -0
  192. package/src/js/input/input-handler.js +396 -0
  193. package/src/js/input/joystick-window.js +404 -0
  194. package/src/js/input/mouse-handler.js +99 -0
  195. package/src/js/input/text-selection.js +462 -0
  196. package/src/js/main.js +653 -0
  197. package/src/js/state/index.js +15 -0
  198. package/src/js/state/save-states-window.js +393 -0
  199. package/src/js/state/state-manager.js +409 -0
  200. package/src/js/state/state-persistence.js +218 -0
  201. package/src/js/ui/confirm.js +43 -0
  202. package/src/js/ui/disk-drive-positioner.js +347 -0
  203. package/src/js/ui/reminder-controller.js +129 -0
  204. package/src/js/ui/slot-configuration-window.js +560 -0
  205. package/src/js/ui/theme-manager.js +61 -0
  206. package/src/js/ui/toast.js +44 -0
  207. package/src/js/ui/ui-controller.js +897 -0
  208. package/src/js/ui/window-switcher.js +275 -0
  209. package/src/js/utils/basic-autocomplete.js +832 -0
  210. package/src/js/utils/basic-highlighting.js +473 -0
  211. package/src/js/utils/basic-tokenizer.js +153 -0
  212. package/src/js/utils/basic-tokens.js +117 -0
  213. package/src/js/utils/constants.js +28 -0
  214. package/src/js/utils/indexeddb-helper.js +225 -0
  215. package/src/js/utils/merlin-editor-support.js +905 -0
  216. package/src/js/utils/merlin-highlighting.js +551 -0
  217. package/src/js/utils/storage.js +125 -0
  218. package/src/js/utils/string-utils.js +19 -0
  219. package/src/js/utils/wasm-memory.js +54 -0
  220. package/src/js/windows/base-window.js +690 -0
  221. package/src/js/windows/index.js +9 -0
  222. package/src/js/windows/window-manager.js +375 -0
  223. package/tests/catch2/catch.hpp +17976 -0
  224. package/tests/common/basic_program_builder.cpp +119 -0
  225. package/tests/common/basic_program_builder.hpp +209 -0
  226. package/tests/common/disk_image_builder.cpp +444 -0
  227. package/tests/common/disk_image_builder.hpp +141 -0
  228. package/tests/common/test_helpers.hpp +118 -0
  229. package/tests/gcr/gcr-test.cpp +142 -0
  230. package/tests/integration/check-rom.js +70 -0
  231. package/tests/integration/compare-boot.js +239 -0
  232. package/tests/integration/crash-trace.js +102 -0
  233. package/tests/integration/disk-boot-test.js +264 -0
  234. package/tests/integration/memory-crash.js +108 -0
  235. package/tests/integration/nibble-read-test.js +249 -0
  236. package/tests/integration/phase-test.js +159 -0
  237. package/tests/integration/test_emulator.cpp +291 -0
  238. package/tests/integration/test_emulator_basic.cpp +91 -0
  239. package/tests/integration/test_emulator_debug.cpp +344 -0
  240. package/tests/integration/test_emulator_disk.cpp +153 -0
  241. package/tests/integration/test_emulator_state.cpp +163 -0
  242. package/tests/klaus/6502_functional_test.bin +0 -0
  243. package/tests/klaus/65C02_extended_opcodes_test.bin +0 -0
  244. package/tests/klaus/klaus_6502_test.cpp +184 -0
  245. package/tests/klaus/klaus_65c02_test.cpp +197 -0
  246. package/tests/thunderclock/thunderclock_mmu_test.cpp +304 -0
  247. package/tests/thunderclock/thunderclock_test.cpp +550 -0
  248. package/tests/unit/test_assembler.cpp +521 -0
  249. package/tests/unit/test_audio.cpp +196 -0
  250. package/tests/unit/test_ay8910.cpp +311 -0
  251. package/tests/unit/test_basic_detokenizer.cpp +265 -0
  252. package/tests/unit/test_basic_tokenizer.cpp +382 -0
  253. package/tests/unit/test_block_device.cpp +259 -0
  254. package/tests/unit/test_condition_evaluator.cpp +219 -0
  255. package/tests/unit/test_cpu6502.cpp +1301 -0
  256. package/tests/unit/test_cpu_addressing.cpp +361 -0
  257. package/tests/unit/test_cpu_cycle_counts.cpp +409 -0
  258. package/tests/unit/test_cpu_decimal.cpp +166 -0
  259. package/tests/unit/test_cpu_interrupts.cpp +285 -0
  260. package/tests/unit/test_disassembler.cpp +323 -0
  261. package/tests/unit/test_disk2_card.cpp +330 -0
  262. package/tests/unit/test_dos33.cpp +273 -0
  263. package/tests/unit/test_dsk_disk_image.cpp +315 -0
  264. package/tests/unit/test_expansion_card.cpp +178 -0
  265. package/tests/unit/test_gcr_encoding.cpp +232 -0
  266. package/tests/unit/test_keyboard.cpp +262 -0
  267. package/tests/unit/test_mmu.cpp +555 -0
  268. package/tests/unit/test_mmu_slots.cpp +323 -0
  269. package/tests/unit/test_mockingboard.cpp +352 -0
  270. package/tests/unit/test_mouse_card.cpp +386 -0
  271. package/tests/unit/test_pascal.cpp +248 -0
  272. package/tests/unit/test_prodos.cpp +259 -0
  273. package/tests/unit/test_smartport_card.cpp +321 -0
  274. package/tests/unit/test_thunderclock.cpp +354 -0
  275. package/tests/unit/test_via6522.cpp +323 -0
  276. package/tests/unit/test_video.cpp +319 -0
  277. package/tests/unit/test_woz_disk_image.cpp +257 -0
  278. package/vite.config.js +96 -0
  279. package/wiki/AI-Agent.md +372 -0
  280. package/wiki/Architecture-Overview.md +303 -0
  281. package/wiki/Audio-System.md +449 -0
  282. package/wiki/CPU-Emulation.md +477 -0
  283. package/wiki/Debugger.md +516 -0
  284. package/wiki/Disk-Drives.md +161 -0
  285. package/wiki/Disk-System-Internals.md +547 -0
  286. package/wiki/Display-Settings.md +88 -0
  287. package/wiki/Expansion-Slots.md +187 -0
  288. package/wiki/File-Explorer.md +259 -0
  289. package/wiki/Getting-Started.md +156 -0
  290. package/wiki/Home.md +69 -0
  291. package/wiki/Input-Devices.md +183 -0
  292. package/wiki/Keyboard-Shortcuts.md +158 -0
  293. package/wiki/Memory-System.md +364 -0
  294. package/wiki/Save-States.md +172 -0
  295. package/wiki/Video-Rendering.md +658 -0
package/.clangd ADDED
@@ -0,0 +1,5 @@
1
+ CompileFlags:
2
+ CompilationDatabase: build
3
+ Diagnostics:
4
+ Suppress:
5
+ - unused-includes
package/.mcp.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "appleii-agent": {
4
+ "type": "stdio",
5
+ "command": "bunx",
6
+ "args": [
7
+ "-y",
8
+ "@retrotech71/appleii-agent@latest"
9
+ ]
10
+ }
11
+ }
12
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,362 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Apple //e Browser Based Emulator - A cycle-accurate Apple II Enhanced emulator running in the browser using WebAssembly (C++ backend) and WebGL rendering. No JavaScript frameworks; vanilla ES6 modules with Vite for bundling.
8
+
9
+ ## Build Commands
10
+
11
+ ```bash
12
+ npm install # Install dependencies
13
+ npm run build:wasm # Build WASM module (required first time and after C++ changes)
14
+ npm run dev # Start dev server at localhost:3000 (hot-reload for JS only)
15
+ npm run build # Full production build (WASM + Vite bundle)
16
+ npm run clean # Clean build artifacts
17
+ npm run deploy # Deploy to VPS via rsync
18
+ ```
19
+
20
+ ## Testing
21
+
22
+ ### CPU Compliance Tests
23
+
24
+ Klaus Dormann's 6502/65C02 functional test suites (`tests/klaus/`):
25
+
26
+ ```bash
27
+ mkdir -p build-native && cd build-native
28
+ cmake ..
29
+ make -j$(sysctl -n hw.ncpu)
30
+ ctest --verbose
31
+ ```
32
+
33
+ Test executables: `klaus_6502_test` (NMOS 6502), `klaus_65c02_test` (65C02 extended opcodes)
34
+
35
+ ### Thunderclock Tests
36
+
37
+ Native C++ tests for Thunderclock card emulation (`tests/thunderclock/`), including MMU integration. Built and run via the same native CMake build above.
38
+
39
+ ### GCR Encoding Tests
40
+
41
+ GCR (Group Code Recording) encoding tests (`tests/gcr/`). Native C++ tests for disk encoding logic.
42
+
43
+ ### Integration Tests
44
+
45
+ Ad-hoc JavaScript tests for disk, memory, and boot debugging (`tests/integration/`). Run with Node.js:
46
+
47
+ ```bash
48
+ node tests/integration/disk-boot-test.js
49
+ ```
50
+
51
+ ## Architecture
52
+
53
+ ### Two-Layer Design
54
+
55
+ **C++ Core (src/core/)** - Pure emulation logic compiled to WebAssembly:
56
+
57
+ - `cpu/cpu6502.cpp` - Cycle-accurate 65C02 processor (1.023 MHz)
58
+ - `mmu/mmu.cpp` - 128KB memory management, soft switches ($C000-$CFFF), expansion slots
59
+ - `video/video.cpp` - TEXT/LORES/HIRES/DHIRES per-scanline rendering
60
+ - `audio/audio.cpp` - Speaker emulation from $C030 toggles
61
+ - `disk-image/` - Disk image format support (DSK/DO/PO/NIB/WOZ) with GCR encoding
62
+ - `disassembler/` - 65C02 instruction disassembler
63
+ - `input/keyboard.cpp` - Keyboard input handling
64
+ - `cards/` - Pluggable expansion card system (ExpansionCard interface)
65
+ - `cards/mockingboard/` - AY-3-8910 sound chip + VIA 6522 timer
66
+ - `cards/smartport/` - SmartPort hard drive controller (2 block devices, self-built ROM)
67
+ - `filesystem/` - DOS 3.3 and ProDOS filesystem parsers
68
+ - `basic/` - Applesoft and Integer BASIC detokenizer and tokenizer
69
+ - `debug/` - Condition evaluator for breakpoint expressions (supports BV/BA/BA2 for BASIC variable/array reads)
70
+ - `emulator.cpp` - Core coordinator, state serialization
71
+
72
+ **JavaScript Layer (src/js/)** - Browser integration:
73
+
74
+ - `main.js` - AppleIIeEmulator class orchestrating all subsystems
75
+ - `audio/` - Web Audio API driver and AudioWorklet
76
+ - `display/` - WebGL renderer, CRT shader effects, display settings, screen window
77
+ - `disk-manager/` - Disk drive UI, SmartPort hard drives, persistence, surface rendering, drive sounds
78
+ - `file-explorer/` - DOS 3.3 and ProDOS disk browser with disassembler
79
+ - `debug/` - Debug window implementations (see Debugging section)
80
+ - `help/` - Documentation and release notes windows
81
+ - `input/` - Keyboard input, text selection, joystick, mouse
82
+ - `ui/` - Menu wiring, reminders, slot configuration, custom confirm dialogs
83
+ - `state/` - State serialization and persistence (autosave + 5 manual slots)
84
+ - `config/` - App version
85
+ - `utils/` - Shared utilities (storage, string, BASIC)
86
+ - `windows/` - Base window class and window manager
87
+
88
+ ### Theming
89
+
90
+ Light, dark, and system-follow themes controlled by `ThemeManager` (`src/js/ui/theme-manager.js`). Sets `data-theme` attribute on `<html>` for CSS variable switching. All accent and syntax highlighting colours are derived from the six-stripe Apple rainbow logo palette (Green `#61BB46`, Yellow `#FDB827`, Orange `#F5821F`, Red `#E03A3E`, Purple `#963D97`, Blue `#009DDC`), with brightness adjusted per theme for contrast. Speaker, Mockingboard, and disk drive sound volumes are all wired to a single main volume slider with a unified mute toggle.
91
+
92
+ Control sytles, sizes and layout must be consistent across the entire app.
93
+
94
+ ### Audio-Driven Timing
95
+
96
+ The emulator uses Web Audio API for precise timing:
97
+
98
+ 1. AudioWorklet requests samples at 48kHz
99
+ 2. WASM runs ~21.3 CPU cycles per audio sample
100
+ 3. Frame ready when cycles cross ~17,030 (60Hz)
101
+
102
+ This ensures consistent speed and works when the browser tab is backgrounded.
103
+
104
+ ### WASM Interface Pattern
105
+
106
+ Single global `Emulator` instance in C++ (`wasm_interface.cpp`). JS allocates WASM heap with `_malloc`/`_free`, uses `stringToUTF8()`/`UTF8ToString()` for string conversion. New WASM exports must be added to `CMakeLists.txt` EXPORTED_FUNCTIONS list.
107
+
108
+ ### Key Constants (src/core/types.hpp)
109
+
110
+ - CPU: 1.023 MHz clock
111
+ - Audio: 48kHz sample rate
112
+ - Screen: 560x384 pixels (280x192 doubled)
113
+ - Memory: 64KB main + 64KB aux RAM, 16KB ROM
114
+
115
+ ## Development Workflow
116
+
117
+ **C++ changes** require rebuilding WASM: `npm run build:wasm`
118
+
119
+ **JavaScript changes** auto-reload via Vite dev server
120
+
121
+ **Full build** for production: `npm run build` (outputs to `dist/`)
122
+
123
+ **ROM files** are embedded into WASM at compile time. Place in `roms/` directory before building:
124
+
125
+ - `342-0349-B-C0-FF.bin` (16KB system ROM)
126
+ - `342-0273-A-US-UK.bin` (4KB character ROM, US/UK)
127
+ - `341-0160-A-US-UK.bin` (alternate character ROM variant)
128
+ - `341-0027.bin` (256 bytes Disk II ROM)
129
+ - `Thunderclock Plus ROM.bin` (2KB Thunderclock card ROM)
130
+ - `Apple Mouse Interface Card ROM - 342-0270-C.bin` (2KB Mouse Interface Card ROM)
131
+
132
+ ## Code Organization
133
+
134
+ ```
135
+ src/
136
+ ├── core/ # C++ emulator (namespace a2e::)
137
+ │ ├── cpu/ # 65C02 processor
138
+ │ ├── mmu/ # Memory management and soft switches
139
+ │ ├── video/ # Per-scanline video rendering
140
+ │ ├── audio/ # Speaker audio
141
+ │ ├── disk-image/ # Disk image formats (DSK/DO/PO/NIB/WOZ) and GCR encoding
142
+ │ ├── disassembler/ # 65C02 disassembler
143
+ │ ├── input/ # Keyboard handling
144
+ │ ├── cards/ # Expansion card system
145
+ │ │ ├── mockingboard/ # AY-3-8910 + VIA 6522
146
+ │ │ └── smartport/ # SmartPort hard drive controller
147
+ │ ├── filesystem/ # DOS 3.3 and ProDOS parsers
148
+ │ ├── basic/ # BASIC tokenizer and detokenizer
149
+ │ ├── debug/ # Condition evaluator
150
+ │ ├── emulator.cpp # Core coordinator, state serialization
151
+ │ └── types.hpp # Shared constants and types
152
+ ├── bindings/ # wasm_interface.cpp - WASM export glue
153
+ └── js/ # ES6 modules, no framework
154
+ ├── main.js # Entry point, AppleIIeEmulator class
155
+ ├── audio/ # Web Audio API driver and worklet
156
+ ├── config/ # App version
157
+ ├── debug/ # Debug window implementations
158
+ ├── disk-manager/ # Disk drive operations, persistence, surface rendering, sounds
159
+ ├── display/ # WebGL renderer, CRT shaders, display settings, screen window
160
+ ├── file-explorer/ # DOS 3.3 and ProDOS file browser, disassembler
161
+ ├── help/ # Documentation and release notes
162
+ ├── input/ # Keyboard input, text selection, joystick, mouse
163
+ ├── state/ # Save state manager and persistence
164
+ ├── ui/ # Menu wiring, reminders, slot configuration
165
+ ├── utils/ # Shared utilities (storage, string, BASIC)
166
+ └── windows/ # Base window class and window manager
167
+ ├── css/ # Stylesheets (bundled by Vite)
168
+ public/ # Static assets, built WASM files, shaders
169
+ ├── shaders/ # CRT vertex/fragment shaders
170
+ ├── assets/ # Images and sounds
171
+ └── index.html # Main HTML entry point
172
+ tests/
173
+ ├── klaus/ # Klaus Dormann CPU compliance tests
174
+ ├── thunderclock/ # Thunderclock card tests
175
+ ├── integration/ # JS integration/debug tests
176
+ └── gcr/ # GCR encoding tests
177
+ ```
178
+
179
+ ### File Naming Convention
180
+
181
+ All JavaScript files use **kebab-case** (e.g., `audio-driver.js`, `cpu-debugger-window.js`). Class names remain PascalCase in the code.
182
+
183
+ ## Expansion Card Architecture
184
+
185
+ The MMU supports pluggable expansion cards matching real Apple IIe hardware. Cards implement the `ExpansionCard` interface (`src/core/cards/expansion_card.hpp`).
186
+
187
+ ### Slot Memory Map
188
+
189
+ | Slot | I/O Space | ROM Space | Default Card |
190
+ | ---- | ----------- | ----------- | --------------------------- |
191
+ | 1 | $C090-$C09F | $C100-$C1FF | Empty |
192
+ | 2 | $C0A0-$C0AF | $C200-$C2FF | Empty |
193
+ | 3 | $C0B0-$C0BF | $C300-$C3FF | 80-column (built-in, fixed) |
194
+ | 4 | $C0C0-$C0CF | $C400-$C4FF | Mockingboard |
195
+ | 5 | $C0D0-$C0DF | $C500-$C5FF | Thunderclock |
196
+ | 6 | $C0E0-$C0EF | $C600-$C6FF | Disk II |
197
+ | 7 | $C0F0-$C0FF | $C700-$C7FF | Empty |
198
+
199
+ ### Card Interface Methods
200
+
201
+ ```cpp
202
+ class ExpansionCard {
203
+ virtual uint8_t readIO(uint8_t offset); // I/O space ($C0x0-$C0xF)
204
+ virtual void writeIO(uint8_t offset, uint8_t value);
205
+ virtual uint8_t readROM(uint8_t offset); // ROM space ($Cx00-$CxFF)
206
+ virtual void writeROM(uint8_t offset, uint8_t value);
207
+ virtual void reset();
208
+ virtual void update(int cycles);
209
+ // ... serialization, IRQ callbacks, etc.
210
+ };
211
+ ```
212
+
213
+ ### Available Cards
214
+
215
+ - `Disk2Card` - Wraps Disk2Controller (slot 6)
216
+ - `MockingboardCard` - Dual AY-3-8910 + VIA 6522, stereo output (slot 4)
217
+ - `MouseCard` - Apple Mouse Interface Card via MC6821 PIA command protocol (slot 4)
218
+ - `SmartPortCard` - SmartPort hard drive controller, 2 block devices, self-built ROM (user-configurable slot)
219
+ - `ThunderclockCard` - ProDOS-compatible real-time clock (slots 5, 7)
220
+
221
+ ## State Serialization
222
+
223
+ Binary format with versioned header. Includes CPU state, 128KB RAM, Language Card (16KB), soft switches, disk images with modifications, filenames, and debugger state. Autosave slot plus 5 manual save slots. Stored in browser IndexedDB. Window option state (toggles, view modes, mute states) is persisted separately via localStorage.
224
+
225
+ ## Release Process
226
+
227
+ When the user says "release", perform all of the following steps:
228
+
229
+ 1. **Review git log** since the last release notes entry to identify all changes
230
+ 2. **Bump version** in `src/js/config/version.js`
231
+ 3. **Update release notes** in both `src/js/help/release-notes.js` and `src/js/config/release-notes.json`
232
+ 4. **Update README.md** to reflect any new features, changed commands, or updated project information
233
+ 5. **Update CLAUDE.md** to reflect any architectural changes, new files/directories, new build steps, new expansion cards, new debug windows, or other structural changes to the codebase
234
+
235
+ ## Debugging
236
+
237
+ Built-in debug windows accessible via Debug menu:
238
+
239
+ - CPU Debugger: registers (REGS, FLAGS, TIMING, BEAM sections), breakpoints, stepping, disassembly with symbols
240
+ - Memory Browser: hex/ASCII view of 128KB address space with search
241
+ - Memory Heat Map: real-time memory access visualization (read/write/combined modes)
242
+ - Memory Map: address space layout overview
243
+ - Stack Viewer: live stack contents
244
+ - Zero Page Watch: monitor zero page locations with predefined and custom watches
245
+ - Soft Switch Monitor: Apple II switch states ($C000-$C0FF)
246
+ - Mockingboard: unified channel-centric view with AY-3-8910 and VIA registers, inline waveforms, level meters, and per-channel mute controls
247
+ - Mouse Card: PIA registers, position, mode, interrupt state, protocol activity
248
+ - BASIC Program Viewer: view, load, and tokenize BASIC programs from memory, line heat map, trace toggle, statement-level breakpoints, conditional breakpoints on variables/arrays, condition-only rules, variable inspector, run/stop/pause/step controls
249
+ - Rule Builder: complex conditional breakpoints with C-style expressions, supports CPU registers/memory and BASIC variables/arrays as subjects
250
+
251
+ ## Keyboard Shortcuts
252
+
253
+ | Shortcut | Action |
254
+ | ---------------- | ------------------------ |
255
+ | F1 | Open/close Help window |
256
+ | Ctrl+Escape | Exit full page mode |
257
+ | Ctrl+V | Paste text into emulator |
258
+ | Ctrl+` | Open window switcher |
259
+ | Option+Tab | Cycle to next window |
260
+ | Option+Shift+Tab | Cycle to previous window |
261
+ | F5 | Run / Continue execution |
262
+ | F10 | Step Over |
263
+ | F11 | Step Into |
264
+ | Shift+F11 | Step Out |
265
+
266
+ ## Agent / MCP Integration
267
+
268
+ The emulator exposes an AI agent interface via the Model Context Protocol (MCP) and AG-UI event protocol. This allows AI agents (including Claude Code) to fully control the emulator programmatically.
269
+
270
+ ### Architecture
271
+
272
+ Two coordinated components:
273
+
274
+ - **MCP Server** (`mcp/appleii-agent/`) — Node.js process providing MCP tools over stdio + an HTTP/HTTPS server (port 3033) implementing the AG-UI event protocol (SSE)
275
+ - **Frontend Agent Manager** (`src/js/agent/agent-manager.js`) — Browser-side AG-UI client that connects to the server, receives tool calls via SSE, executes them against the emulator, and returns results
276
+
277
+ ### Configuration
278
+
279
+ - `.mcp.json` (repo root) — MCP client config for running the agent
280
+ - Recommended: `bunx -y @retrotech71/appleii-agent` (auto-installs with Bun)
281
+ - Development: `node /path/to/appleii-agent/src/index.js` (local source)
282
+ - Environment variables: `PORT` (default 3033), `HTTPS=true` for TLS mode
283
+
284
+ ### MCP Server Tools (`mcp/appleii-agent/src/tools/`)
285
+
286
+ | Tool | Description |
287
+ | ---- | ----------- |
288
+ | `emma_command` | Generic command wrapper — delegates to any frontend tool |
289
+ | `server_control` | Start/stop/restart the agent server |
290
+ | `set_https` | Enable/disable HTTPS mode |
291
+ | `set_debug` | Set debug logging level |
292
+ | `get_state` | Return current emulator state |
293
+ | `show_window` / `hide_window` / `focus_window` | Window management |
294
+ | `load_disk_image` | Load disk image (.dsk/.do/.po/.nib/.woz) from filesystem, returns base64 |
295
+ | `load_file` | Load arbitrary file from filesystem |
296
+ | `save_basic_file` | Save BASIC program to disk |
297
+ | `save_asm_file` | Save assembler source to disk |
298
+ | `save_disk_file` | Save extracted disk file to filesystem |
299
+ | `load_smartport_image` | Load hard drive image (.hdv/.po/.2mg) from filesystem |
300
+
301
+ ### Frontend Agent Tools (`src/js/agent/`)
302
+
303
+ Registered in `agent-tools.js`, organized by category:
304
+
305
+ **Emulator Control** (`main-tools.js`)
306
+ - `emulatorPower` — on/off/toggle
307
+ - `emulatorCtrlReset` — warm reset (Ctrl+Reset)
308
+ - `emulatorReboot` — cold reset
309
+ - `directLoadBinaryAt` — load base64 data to memory address
310
+ - `directSaveBinaryRangeTo` — read memory range as base64
311
+
312
+ **BASIC Program** (`basic-program-tools.js`)
313
+ - `directReadBasic` / `directWriteBasic` / `directRunBasic` / `directNewBasic` — direct memory operations
314
+ - `basicProgramLoadFromMemory` / `basicProgramLoadIntoEmulator` — transfer between editor and emulator
315
+ - `basicProgramRun` / `basicProgramPause` / `basicProgramNew` / `basicProgramRenumber` / `basicProgramFormat`
316
+ - `basicProgramGet` / `basicProgramSet` / `basicProgramLineCount`
317
+ - `saveBasicInEditorToLocal` — export from editor
318
+
319
+ **Assembler** (`assembler-tools.js`)
320
+ - `asmAssemble` — compile source code
321
+ - `asmWrite` — load assembled code into memory
322
+ - `asmLoadExample` — load template program
323
+ - `asmNew` / `asmGet` / `asmSet` — editor operations
324
+ - `asmGetStatus` — compilation status (origin, size, errors)
325
+ - `directExecuteAssemblyAt` — execute at address with optional return address
326
+
327
+ **Disk Drives** (`disk-tools.js`)
328
+ - `driveInsertDisc` — load disk image (calls MCP `load_disk_image`)
329
+ - `driveRecentsList` / `driveInsertRecent` / `driveLoadRecent` / `drivesClearRecent` — recent disk management
330
+
331
+ **SmartPort Hard Drives** (`smartport-tools.js`)
332
+ - `smartportInsertImage` — load hard drive image (calls MCP `load_smartport_image`)
333
+ - `smartportRecentsList` / `smartportInsertRecent` / `smartportClearRecent` — recent image management
334
+ - Validates SmartPort card is installed before operations
335
+
336
+ **File Explorer** (`file-explorer-tools.js`)
337
+ - `listDiskFiles` — enumerate DOS 3.3/ProDOS catalog (returns filename, type, size, locked status)
338
+ - `getDiskFileContent` — read file from disk (base64 for binary, plaintext for text)
339
+
340
+ **Window Management** (`window-tools.js`)
341
+ - `showWindow` / `hideWindow` / `focusWindow`
342
+
343
+ **Expansion Slots** (`slot-tools.js`)
344
+ - `slotsListAll` — list all slots with current cards and available options
345
+ - `slotsInstallCard` / `slotsRemoveCard` / `slotsMoveCard` — card management
346
+ - Persists to localStorage, triggers emulator reset after changes
347
+
348
+ ### WASM APIs Used by Agent Tools
349
+
350
+ The frontend tools hook into these WASM exports (changes to these require updating agent tools):
351
+
352
+ - **CPU/Execution**: `_isPaused()`, `_setPaused(bool)`, `_getPC()`, `_setRegPC()`, `_getA/X/Y/SP()`, `_setRegA/X/Y/SP()`, `_getTotalCycles()`, `_reset()`, `_warmReset()`
353
+ - **Memory**: `_readMemory(addr)`, `_writeMemory(addr, val)`, `_peekMemory(addr)`, `_malloc()`, `_free()`
354
+ - **Disk**: `_isDiskInserted(drive)`, `_getDiskSectorData()`, `_isDOS33Format()`, `_isProDOSFormat()`, `_getDOS33Catalog()`, `_getProDOSCatalog()`, `_readDOS33File()`, `_readProDOSFile()`, `_getDOS33FileBuffer()`, `_getProDOSFileBuffer()`
355
+ - **Slots**: `_getSlotCard(slot)`, `_setSlotCard(slot, cardId)`, `_isSmartPortCardInstalled()`
356
+ - **Strings**: `stringToUTF8()`, `UTF8ToString()`
357
+
358
+ ### Data Flow
359
+
360
+ 1. Agent calls MCP tool (e.g., `load_disk_image`) → MCP server reads file from filesystem → returns base64
361
+ 2. Agent calls frontend tool (e.g., `driveInsertDisc`) via `emma_command` → AG-UI SSE delivers tool call to browser
362
+ 3. Frontend decodes data, calls disk manager / WASM APIs → emulator state updates → result returned via `/tool-result` POST