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
@@ -0,0 +1,481 @@
1
+ /*
2
+ * memory-heat-map-window.js - Real-time memory access heat map visualization
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ import { BaseWindow } from "../windows/base-window.js";
9
+
10
+ // Memory region labels for main memory
11
+ const MAIN_MEMORY_REGIONS = [
12
+ { name: "Zero Page", start: 0x0000, end: 0x00ff },
13
+ { name: "Stack", start: 0x0100, end: 0x01ff },
14
+ { name: "Input Buffer", start: 0x0200, end: 0x02ff },
15
+ { name: "Vectors/Data", start: 0x0300, end: 0x03ff },
16
+ { name: "Text Page 1", start: 0x0400, end: 0x07ff },
17
+ { name: "Text Page 2", start: 0x0800, end: 0x0bff },
18
+ { name: "Free RAM", start: 0x0c00, end: 0x1fff },
19
+ { name: "HiRes Page 1", start: 0x2000, end: 0x3fff },
20
+ { name: "HiRes Page 2", start: 0x4000, end: 0x5fff },
21
+ { name: "Free RAM", start: 0x6000, end: 0x95ff },
22
+ { name: "DOS 3.3", start: 0x9600, end: 0xbfff },
23
+ { name: "I/O Space", start: 0xc000, end: 0xc0ff },
24
+ { name: "Slot ROMs", start: 0xc100, end: 0xcfff },
25
+ { name: "ROM/LC RAM", start: 0xd000, end: 0xffff },
26
+ ];
27
+
28
+ // Memory region labels for auxiliary memory
29
+ const AUX_MEMORY_REGIONS = [
30
+ { name: "Aux Zero Page", start: 0x0000, end: 0x00ff },
31
+ { name: "Aux Stack", start: 0x0100, end: 0x01ff },
32
+ { name: "Aux $0200", start: 0x0200, end: 0x03ff },
33
+ { name: "Aux Text Page 1", start: 0x0400, end: 0x07ff },
34
+ { name: "Aux Text Page 2", start: 0x0800, end: 0x0bff },
35
+ { name: "Aux RAM", start: 0x0c00, end: 0x1fff },
36
+ { name: "Aux HiRes Page 1", start: 0x2000, end: 0x3fff },
37
+ { name: "Aux HiRes Page 2", start: 0x4000, end: 0x5fff },
38
+ { name: "Aux RAM", start: 0x6000, end: 0xbfff },
39
+ { name: "Aux $C000-$FFFF", start: 0xc000, end: 0xffff },
40
+ ];
41
+
42
+ export class MemoryHeatMapWindow extends BaseWindow {
43
+ constructor(wasmModule) {
44
+ super({
45
+ id: "memory-heatmap",
46
+ title: "Memory Heat Map",
47
+ defaultWidth: 580,
48
+ defaultHeight: 435,
49
+ minWidth: 580,
50
+ minHeight: 435,
51
+ maxWidth: 580,
52
+ maxHeight: 435,
53
+
54
+ });
55
+ this.wasmModule = wasmModule;
56
+ this.isTracking = false;
57
+ this.viewMode = "combined"; // combined, reads, writes
58
+ this.decayEnabled = false;
59
+ this.onJumpToAddress = null; // Callback for Memory Browser integration
60
+ this.activeCanvas = "main"; // Which canvas is being hovered
61
+ }
62
+
63
+ renderContent() {
64
+ return `
65
+ <div class="heatmap-toolbar">
66
+ <button class="heatmap-toggle-btn">Start</button>
67
+ <button class="heatmap-clear-btn">Clear</button>
68
+ <select class="heatmap-mode-select">
69
+ <option value="combined">Combined</option>
70
+ <option value="reads">Reads Only</option>
71
+ <option value="writes">Writes Only</option>
72
+ </select>
73
+ <label class="heatmap-decay-label">
74
+ <input type="checkbox" class="heatmap-decay-check" />
75
+ Decay
76
+ </label>
77
+ </div>
78
+ <div class="heatmap-dual-container">
79
+ <div class="heatmap-panel">
80
+ <div class="heatmap-panel-title">Main RAM + ROM</div>
81
+ <div class="heatmap-canvas-container">
82
+ <canvas class="heatmap-canvas heatmap-canvas-main" width="256" height="256"></canvas>
83
+ </div>
84
+ </div>
85
+ <div class="heatmap-panel">
86
+ <div class="heatmap-panel-title">Auxiliary RAM</div>
87
+ <div class="heatmap-canvas-container">
88
+ <canvas class="heatmap-canvas heatmap-canvas-aux" width="256" height="256"></canvas>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ <div class="heatmap-legend">
93
+ <span class="heatmap-legend-item"><span class="legend-color reads"></span> Reads</span>
94
+ <span class="heatmap-legend-item"><span class="legend-color writes"></span> Writes</span>
95
+ <span class="heatmap-legend-item"><span class="legend-color both"></span> Both</span>
96
+ </div>
97
+ <div class="heatmap-info">
98
+ <span class="heatmap-addr">$0000</span>
99
+ <span class="heatmap-region">Zero Page</span>
100
+ <span class="heatmap-counts">R: 0 W: 0</span>
101
+ </div>
102
+ `;
103
+ }
104
+
105
+ onContentRendered() {
106
+ this.canvasMain = this.contentElement.querySelector(".heatmap-canvas-main");
107
+ this.canvasAux = this.contentElement.querySelector(".heatmap-canvas-aux");
108
+ this.ctxMain = this.canvasMain.getContext("2d");
109
+ this.ctxAux = this.canvasAux.getContext("2d");
110
+ this.toggleBtn = this.contentElement.querySelector(".heatmap-toggle-btn");
111
+ this.modeSelect = this.contentElement.querySelector(".heatmap-mode-select");
112
+ this.decayCheck = this.contentElement.querySelector(".heatmap-decay-check");
113
+ this.addrSpan = this.contentElement.querySelector(".heatmap-addr");
114
+ this.regionSpan = this.contentElement.querySelector(".heatmap-region");
115
+ this.countsSpan = this.contentElement.querySelector(".heatmap-counts");
116
+
117
+ // Pre-allocate image data for both canvases
118
+ this.imageDataMain = this.ctxMain.createImageData(256, 256);
119
+ this.imageDataAux = this.ctxAux.createImageData(256, 256);
120
+
121
+ this.setupHeatmapEventListeners();
122
+
123
+ // Apply restored state to UI elements
124
+ this.modeSelect.value = this.viewMode;
125
+ this.decayCheck.checked = this.decayEnabled;
126
+ }
127
+
128
+ setupHeatmapEventListeners() {
129
+ // Toggle tracking
130
+ this.toggleBtn.addEventListener("click", () => {
131
+ this.isTracking = !this.isTracking;
132
+ this.toggleBtn.textContent = this.isTracking ? "Stop" : "Start";
133
+ this.toggleBtn.classList.toggle("active", this.isTracking);
134
+ this.wasmModule._enableMemoryTracking(this.isTracking);
135
+ });
136
+
137
+ // Clear tracking data
138
+ this.contentElement
139
+ .querySelector(".heatmap-clear-btn")
140
+ .addEventListener("click", () => {
141
+ this.wasmModule._clearMemoryTracking();
142
+ });
143
+
144
+ // View mode selection
145
+ this.modeSelect.addEventListener("change", (e) => {
146
+ this.viewMode = e.target.value;
147
+ if (this.onStateChange) this.onStateChange();
148
+ });
149
+
150
+ // Decay toggle
151
+ this.decayCheck.addEventListener("change", (e) => {
152
+ this.decayEnabled = e.target.checked;
153
+ if (this.onStateChange) this.onStateChange();
154
+ });
155
+
156
+ // Canvas hover for main memory
157
+ this.canvasMain.addEventListener("mousemove", (e) => {
158
+ this.activeCanvas = "main";
159
+ const addr = this.getAddressFromEvent(e, this.canvasMain);
160
+ this.showAddressInfo(addr, MAIN_MEMORY_REGIONS, "Main");
161
+ });
162
+
163
+ // Canvas hover for aux memory
164
+ this.canvasAux.addEventListener("mousemove", (e) => {
165
+ this.activeCanvas = "aux";
166
+ const addr = this.getAddressFromEvent(e, this.canvasAux);
167
+ this.showAddressInfo(addr, AUX_MEMORY_REGIONS, "Aux");
168
+ });
169
+
170
+ this.canvasMain.addEventListener("mouseleave", () =>
171
+ this.clearAddressInfo(),
172
+ );
173
+ this.canvasAux.addEventListener("mouseleave", () =>
174
+ this.clearAddressInfo(),
175
+ );
176
+
177
+ // Click to jump to Memory Browser
178
+ this.canvasMain.addEventListener("click", (e) => {
179
+ const addr = this.getAddressFromEvent(e, this.canvasMain);
180
+ if (this.onJumpToAddress) {
181
+ this.onJumpToAddress(addr);
182
+ }
183
+ });
184
+
185
+ this.canvasAux.addEventListener("click", (e) => {
186
+ const addr = this.getAddressFromEvent(e, this.canvasAux);
187
+ if (this.onJumpToAddress) {
188
+ this.onJumpToAddress(addr);
189
+ }
190
+ });
191
+ }
192
+
193
+ getAddressFromEvent(e, canvas) {
194
+ const rect = canvas.getBoundingClientRect();
195
+ const x = Math.floor(((e.clientX - rect.left) / rect.width) * 256);
196
+ const y = Math.floor(((e.clientY - rect.top) / rect.height) * 256);
197
+ return y * 256 + x;
198
+ }
199
+
200
+ clearAddressInfo() {
201
+ this.addrSpan.textContent = "$----";
202
+ this.regionSpan.textContent = "";
203
+ this.countsSpan.textContent = "";
204
+ }
205
+
206
+ showAddressInfo(addr, regions, prefix) {
207
+ if (addr < 0 || addr > 0xffff) return;
208
+
209
+ this.addrSpan.textContent = `${prefix} $${this.formatHex(addr, 4)}`;
210
+
211
+ // Find region
212
+ for (const region of regions) {
213
+ if (addr >= region.start && addr <= region.end) {
214
+ this.regionSpan.textContent = region.name;
215
+ break;
216
+ }
217
+ }
218
+
219
+ // Get counts from WASM memory
220
+ const readCountsPtr = this.wasmModule._getMemoryReadCounts();
221
+ const writeCountsPtr = this.wasmModule._getMemoryWriteCounts();
222
+
223
+ if (readCountsPtr && writeCountsPtr) {
224
+ const readCount = this.wasmModule.HEAPU8[readCountsPtr + addr];
225
+ const writeCount = this.wasmModule.HEAPU8[writeCountsPtr + addr];
226
+ this.countsSpan.textContent = `R: ${readCount} W: ${writeCount}`;
227
+ }
228
+ }
229
+
230
+ update(wasmModule) {
231
+ if (!this.isVisible || !this.canvasMain || !this.canvasAux) return;
232
+
233
+ const readCountsPtr = wasmModule._getMemoryReadCounts();
234
+ const writeCountsPtr = wasmModule._getMemoryWriteCounts();
235
+ const mainRAMPtr = wasmModule._getMainRAM();
236
+ const auxRAMPtr = wasmModule._getAuxRAM();
237
+ const systemROMPtr = wasmModule._getSystemROM();
238
+
239
+ if (!readCountsPtr || !writeCountsPtr) return;
240
+
241
+ // Update main memory canvas
242
+ this.updateCanvas(
243
+ this.imageDataMain,
244
+ this.ctxMain,
245
+ readCountsPtr,
246
+ writeCountsPtr,
247
+ mainRAMPtr,
248
+ systemROMPtr,
249
+ );
250
+
251
+ // Update aux memory canvas
252
+ this.updateCanvasAux(
253
+ this.imageDataAux,
254
+ this.ctxAux,
255
+ readCountsPtr,
256
+ writeCountsPtr,
257
+ auxRAMPtr,
258
+ );
259
+
260
+ // Apply decay if enabled
261
+ if (this.decayEnabled && this.isTracking) {
262
+ wasmModule._decayMemoryTracking(2);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Render the memory heat map for main RAM and system ROM.
268
+ *
269
+ * Color encoding scheme:
270
+ * - Background brightness: Based on memory content value (0-255 → visible gray level).
271
+ * This shows the actual memory contents.
272
+ * - Read activity: Blue/cyan channel. Higher read count = brighter blue.
273
+ * - Write activity: Red/orange channel. Higher write count = brighter red.
274
+ * - Combined read+write: Purple/magenta blend.
275
+ *
276
+ * Each pixel represents one memory address. The canvas is 256x256 = 65536 pixels,
277
+ * covering the entire 64KB address space. Addresses are laid out left-to-right,
278
+ * top-to-bottom (address 0 at top-left, address 65535 at bottom-right).
279
+ *
280
+ * Memory regions:
281
+ * - $0000-$BFFF: Main RAM (48KB)
282
+ * - $C000-$CFFF: I/O and soft switches
283
+ * - $D000-$FFFF: ROM or bank-switched RAM
284
+ */
285
+ updateCanvas(
286
+ imageData,
287
+ ctx,
288
+ readCountsPtr,
289
+ writeCountsPtr,
290
+ mainRAMPtr,
291
+ systemROMPtr,
292
+ ) {
293
+ const data = imageData.data;
294
+ const wasmModule = this.wasmModule;
295
+
296
+ for (let addr = 0; addr < 65536; addr++) {
297
+ const readCount = wasmModule.HEAPU8[readCountsPtr + addr];
298
+ const writeCount = wasmModule.HEAPU8[writeCountsPtr + addr];
299
+
300
+ // Get memory content for background brightness
301
+ let memValue = 0;
302
+ if (mainRAMPtr && addr < 0xc000) {
303
+ memValue = wasmModule.HEAPU8[mainRAMPtr + addr];
304
+ } else if (systemROMPtr && addr >= 0xc000) {
305
+ memValue = wasmModule.HEAPU8[systemROMPtr + (addr - 0xc000)];
306
+ }
307
+
308
+ const pixelIndex = addr * 4;
309
+
310
+ // Base background: visible gray based on memory content (20-60 range)
311
+ const bgLevel = 20 + Math.floor(memValue * 0.16);
312
+
313
+ let r, g, b;
314
+
315
+ // Scale activity to be more visible (multiply by 3, cap at 200)
316
+ const readIntensity = Math.min(200, readCount * 3);
317
+ const writeIntensity = Math.min(200, writeCount * 3);
318
+
319
+ switch (this.viewMode) {
320
+ case "reads":
321
+ // Apple Blue (#18ABEA) for reads
322
+ r = Math.min(255, bgLevel + Math.floor(readIntensity * 0.1));
323
+ g = Math.min(255, bgLevel + Math.floor(readIntensity * 0.73));
324
+ b = Math.min(255, bgLevel + readIntensity);
325
+ break;
326
+ case "writes":
327
+ // Apple Orange (#F68D35) for writes
328
+ r = Math.min(255, bgLevel + writeIntensity);
329
+ g = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.57));
330
+ b = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.22));
331
+ break;
332
+ case "combined":
333
+ default:
334
+ if (readCount > 0 && writeCount > 0) {
335
+ // Apple Purple (#B55DB6) for both
336
+ r = Math.min(255, bgLevel + writeIntensity);
337
+ g = Math.min(
338
+ 255,
339
+ bgLevel +
340
+ Math.floor(Math.min(readIntensity, writeIntensity) * 0.51),
341
+ );
342
+ b = Math.min(255, bgLevel + readIntensity);
343
+ } else if (readCount > 0) {
344
+ // Apple Blue (#18ABEA) for reads only
345
+ r = Math.min(255, bgLevel + Math.floor(readIntensity * 0.1));
346
+ g = Math.min(255, bgLevel + Math.floor(readIntensity * 0.73));
347
+ b = Math.min(255, bgLevel + readIntensity);
348
+ } else if (writeCount > 0) {
349
+ // Apple Orange (#F68D35) for writes only
350
+ r = Math.min(255, bgLevel + writeIntensity);
351
+ g = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.57));
352
+ b = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.22));
353
+ } else {
354
+ // No activity - gray background
355
+ r = bgLevel;
356
+ g = bgLevel;
357
+ b = bgLevel;
358
+ }
359
+ break;
360
+ }
361
+
362
+ data[pixelIndex] = r;
363
+ data[pixelIndex + 1] = g;
364
+ data[pixelIndex + 2] = b;
365
+ data[pixelIndex + 3] = 255;
366
+ }
367
+
368
+ ctx.putImageData(imageData, 0, 0);
369
+ }
370
+
371
+ updateCanvasAux(imageData, ctx, readCountsPtr, writeCountsPtr, auxRAMPtr) {
372
+ const data = imageData.data;
373
+ const wasmModule = this.wasmModule;
374
+
375
+ for (let addr = 0; addr < 65536; addr++) {
376
+ // Note: tracking counts are for the main address space
377
+ // Aux memory accesses happen when RAMRD/RAMWRT are set
378
+ // For now, show aux memory content with dimmed tracking overlay
379
+ const readCount = wasmModule.HEAPU8[readCountsPtr + addr];
380
+ const writeCount = wasmModule.HEAPU8[writeCountsPtr + addr];
381
+
382
+ // Get aux memory content
383
+ let memValue = 0;
384
+ if (auxRAMPtr) {
385
+ memValue = wasmModule.HEAPU8[auxRAMPtr + addr];
386
+ }
387
+
388
+ const pixelIndex = addr * 4;
389
+
390
+ // Base background: visible gray based on memory content (20-60 range)
391
+ const bgLevel = 20 + Math.floor(memValue * 0.16);
392
+
393
+ let r, g, b;
394
+
395
+ // Scale activity (dimmed for aux since tracking is address-based)
396
+ const readIntensity = Math.min(200, readCount * 2);
397
+ const writeIntensity = Math.min(200, writeCount * 2);
398
+
399
+ switch (this.viewMode) {
400
+ case "reads":
401
+ // Apple Blue (#18ABEA) for reads
402
+ r = Math.min(255, bgLevel + Math.floor(readIntensity * 0.1));
403
+ g = Math.min(255, bgLevel + Math.floor(readIntensity * 0.73));
404
+ b = Math.min(255, bgLevel + readIntensity);
405
+ break;
406
+ case "writes":
407
+ // Apple Orange (#F68D35) for writes
408
+ r = Math.min(255, bgLevel + writeIntensity);
409
+ g = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.57));
410
+ b = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.22));
411
+ break;
412
+ case "combined":
413
+ default:
414
+ if (readCount > 0 && writeCount > 0) {
415
+ // Apple Purple (#B55DB6) for both
416
+ r = Math.min(255, bgLevel + writeIntensity);
417
+ g = Math.min(
418
+ 255,
419
+ bgLevel +
420
+ Math.floor(Math.min(readIntensity, writeIntensity) * 0.51),
421
+ );
422
+ b = Math.min(255, bgLevel + readIntensity);
423
+ } else if (readCount > 0) {
424
+ // Apple Blue (#18ABEA) for reads only
425
+ r = Math.min(255, bgLevel + Math.floor(readIntensity * 0.1));
426
+ g = Math.min(255, bgLevel + Math.floor(readIntensity * 0.73));
427
+ b = Math.min(255, bgLevel + readIntensity);
428
+ } else if (writeCount > 0) {
429
+ // Apple Orange (#F68D35) for writes only
430
+ r = Math.min(255, bgLevel + writeIntensity);
431
+ g = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.57));
432
+ b = Math.min(255, bgLevel + Math.floor(writeIntensity * 0.22));
433
+ } else {
434
+ // No activity - gray background
435
+ r = bgLevel;
436
+ g = bgLevel;
437
+ b = bgLevel;
438
+ }
439
+ break;
440
+ }
441
+
442
+ data[pixelIndex] = r;
443
+ data[pixelIndex + 1] = g;
444
+ data[pixelIndex + 2] = b;
445
+ data[pixelIndex + 3] = 255;
446
+ }
447
+
448
+ ctx.putImageData(imageData, 0, 0);
449
+ }
450
+
451
+ setJumpCallback(callback) {
452
+ this.onJumpToAddress = callback;
453
+ }
454
+
455
+ getState() {
456
+ const base = super.getState();
457
+ base.viewMode = this.viewMode;
458
+ base.decayEnabled = this.decayEnabled;
459
+ return base;
460
+ }
461
+
462
+ restoreState(state) {
463
+ if (state.viewMode) {
464
+ this.viewMode = state.viewMode;
465
+ }
466
+ if (state.decayEnabled !== undefined) {
467
+ this.decayEnabled = state.decayEnabled;
468
+ }
469
+ super.restoreState(state);
470
+ }
471
+
472
+ hide() {
473
+ if (this.isTracking) {
474
+ this.isTracking = false;
475
+ this.toggleBtn.textContent = "Start";
476
+ this.toggleBtn.classList.remove("active");
477
+ this.wasmModule._enableMemoryTracking(false);
478
+ }
479
+ super.hide();
480
+ }
481
+ }
@@ -0,0 +1,206 @@
1
+ /*
2
+ * memory-map-window.js - Memory bank configuration overview window
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ import { BaseWindow } from "../windows/base-window.js";
9
+
10
+ /**
11
+ * MemoryMapWindow - Visual representation of memory bank configuration
12
+ * Shows which memory banks are currently active based on soft switch states
13
+ */
14
+ export class MemoryMapWindow extends BaseWindow {
15
+ constructor(wasmModule) {
16
+ super({
17
+ id: "memory-map",
18
+ title: "Memory Map",
19
+ minWidth: 260,
20
+ minHeight: 405,
21
+ maxWidth: 260,
22
+ maxHeight: 405,
23
+ defaultWidth: 260,
24
+ defaultHeight: 405,
25
+ });
26
+
27
+ this.wasmModule = wasmModule;
28
+ }
29
+
30
+ renderContent() {
31
+ return `
32
+ <div class="memory-map-content">
33
+ <div class="bank-map">
34
+ <div class="bank-row" id="bank-zp">
35
+ <span class="bank-addr">$0000-$01FF</span>
36
+ <span class="bank-region bank-main" id="bank-zp-main">Main ZP/Stack</span>
37
+ <span class="bank-region bank-aux hidden" id="bank-zp-aux">Aux ZP/Stack</span>
38
+ </div>
39
+ <div class="bank-row" id="bank-0200">
40
+ <span class="bank-addr">$0200-$03FF</span>
41
+ <span class="bank-region bank-main" id="bank-0200-main">Main RAM</span>
42
+ <span class="bank-region bank-aux hidden" id="bank-0200-aux">Aux RAM</span>
43
+ </div>
44
+ <div class="bank-row" id="bank-text1">
45
+ <span class="bank-addr">$0400-$07FF</span>
46
+ <span class="bank-region bank-main" id="bank-text1-main">Main Text 1</span>
47
+ <span class="bank-region bank-aux hidden" id="bank-text1-aux">Aux Text 1</span>
48
+ </div>
49
+ <div class="bank-row" id="bank-0800">
50
+ <span class="bank-addr">$0800-$1FFF</span>
51
+ <span class="bank-region bank-main" id="bank-0800-main">Main RAM</span>
52
+ <span class="bank-region bank-aux hidden" id="bank-0800-aux">Aux RAM</span>
53
+ </div>
54
+ <div class="bank-row" id="bank-hires1">
55
+ <span class="bank-addr">$2000-$3FFF</span>
56
+ <span class="bank-region bank-main" id="bank-hires1-main">Main HiRes 1</span>
57
+ <span class="bank-region bank-aux hidden" id="bank-hires1-aux">Aux HiRes 1</span>
58
+ </div>
59
+ <div class="bank-row" id="bank-hires2">
60
+ <span class="bank-addr">$4000-$5FFF</span>
61
+ <span class="bank-region bank-main" id="bank-hires2-main">Main HiRes 2</span>
62
+ <span class="bank-region bank-aux hidden" id="bank-hires2-aux">Aux HiRes 2</span>
63
+ </div>
64
+ <div class="bank-row" id="bank-6000">
65
+ <span class="bank-addr">$6000-$BFFF</span>
66
+ <span class="bank-region bank-main" id="bank-6000-main">Main RAM</span>
67
+ <span class="bank-region bank-aux hidden" id="bank-6000-aux">Aux RAM</span>
68
+ </div>
69
+ <div class="bank-row" id="bank-c000">
70
+ <span class="bank-addr">$C000-$C0FF</span>
71
+ <span class="bank-region bank-io">I/O Space</span>
72
+ </div>
73
+ <div class="bank-row" id="bank-slot">
74
+ <span class="bank-addr">$C100-$CFFF</span>
75
+ <span class="bank-region bank-rom" id="bank-slot-int">Internal ROM</span>
76
+ <span class="bank-region bank-slot hidden" id="bank-slot-card">Slot ROMs</span>
77
+ </div>
78
+ <div class="bank-row" id="bank-lc">
79
+ <span class="bank-addr">$D000-$FFFF</span>
80
+ <span class="bank-region bank-rom" id="bank-lc-rom">System ROM</span>
81
+ <span class="bank-region bank-ram hidden" id="bank-lc-ram">LC RAM Bnk1</span>
82
+ <span class="bank-region bank-ram hidden" id="bank-lc-ram2">LC RAM Bnk2</span>
83
+ </div>
84
+ </div>
85
+ <div class="bank-legend">
86
+ <span class="legend-item"><span class="legend-box bank-main"></span>Main</span>
87
+ <span class="legend-item"><span class="legend-box bank-aux"></span>Aux</span>
88
+ <span class="legend-item"><span class="legend-box bank-rom"></span>ROM</span>
89
+ <span class="legend-item"><span class="legend-box bank-ram"></span>LC RAM</span>
90
+ <span class="legend-item"><span class="legend-box bank-io"></span>I/O</span>
91
+ </div>
92
+ <div class="bank-status">
93
+ <div class="bank-status-row">
94
+ <span class="bank-status-label">Read:</span>
95
+ <span class="bank-status-value" id="bank-read-status">Main RAM</span>
96
+ </div>
97
+ <div class="bank-status-row">
98
+ <span class="bank-status-label">Write:</span>
99
+ <span class="bank-status-value" id="bank-write-status">Main RAM</span>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ `;
104
+ }
105
+
106
+ /**
107
+ * Update the memory bank map visualization
108
+ */
109
+ update(wasmModule) {
110
+ this.wasmModule = wasmModule;
111
+
112
+ // Get soft switch states
113
+ const stateLow = wasmModule._getSoftSwitchState();
114
+ const stateHigh = wasmModule._getSoftSwitchStateHigh
115
+ ? wasmModule._getSoftSwitchStateHigh()
116
+ : 0;
117
+
118
+ // Bit positions for relevant switches
119
+ const ALTZP = 10;
120
+ const STORE80 = 6;
121
+ const PAGE2 = 2;
122
+ const HIRES = 3;
123
+ const RAMRD = 7;
124
+ const RAMWRT = 8;
125
+ const INTCXROM = 9;
126
+ const LCRAM = 13;
127
+ const LCBANK2 = 14;
128
+ const LCWRITE = 15;
129
+
130
+ const altzp = (stateLow & (1 << ALTZP)) !== 0;
131
+ const store80 = (stateLow & (1 << STORE80)) !== 0;
132
+ const page2 = (stateLow & (1 << PAGE2)) !== 0;
133
+ const hires = (stateLow & (1 << HIRES)) !== 0;
134
+ const ramrd = (stateLow & (1 << RAMRD)) !== 0;
135
+ const ramwrt = (stateLow & (1 << RAMWRT)) !== 0;
136
+ const intcxrom = (stateLow & (1 << INTCXROM)) !== 0;
137
+ const lcram = (stateLow & (1 << LCRAM)) !== 0;
138
+ const lcbank2 = (stateLow & (1 << LCBANK2)) !== 0;
139
+ const lcwrite = (stateLow & (1 << LCWRITE)) !== 0;
140
+
141
+ // Zero Page / Stack: ALTZP controls
142
+ this.toggleBankRegion("bank-zp-main", !altzp);
143
+ this.toggleBankRegion("bank-zp-aux", altzp);
144
+
145
+ // $0200-$03FF: RAMRD/RAMWRT controls (not affected by 80STORE)
146
+ this.toggleBankRegion("bank-0200-main", !ramrd);
147
+ this.toggleBankRegion("bank-0200-aux", ramrd);
148
+
149
+ // Text Page 1 ($0400-$07FF): 80STORE + PAGE2 or RAMRD controls
150
+ const text1Aux = store80 ? page2 : ramrd;
151
+ this.toggleBankRegion("bank-text1-main", !text1Aux);
152
+ this.toggleBankRegion("bank-text1-aux", text1Aux);
153
+
154
+ // $0800-$1FFF: RAMRD/RAMWRT controls
155
+ this.toggleBankRegion("bank-0800-main", !ramrd);
156
+ this.toggleBankRegion("bank-0800-aux", ramrd);
157
+
158
+ // HiRes Page 1 ($2000-$3FFF): 80STORE + HIRES + PAGE2 or RAMRD controls
159
+ const hires1Aux = store80 && hires ? page2 : ramrd;
160
+ this.toggleBankRegion("bank-hires1-main", !hires1Aux);
161
+ this.toggleBankRegion("bank-hires1-aux", hires1Aux);
162
+
163
+ // HiRes Page 2 ($4000-$5FFF): RAMRD/RAMWRT controls
164
+ this.toggleBankRegion("bank-hires2-main", !ramrd);
165
+ this.toggleBankRegion("bank-hires2-aux", ramrd);
166
+
167
+ // $6000-$BFFF: RAMRD/RAMWRT controls
168
+ this.toggleBankRegion("bank-6000-main", !ramrd);
169
+ this.toggleBankRegion("bank-6000-aux", ramrd);
170
+
171
+ // Slot ROM ($C100-$CFFF): INTCXROM controls
172
+ this.toggleBankRegion("bank-slot-int", intcxrom);
173
+ this.toggleBankRegion("bank-slot-card", !intcxrom);
174
+
175
+ // Language Card ($D000-$FFFF): LCRAM and LCBANK2 control
176
+ this.toggleBankRegion("bank-lc-rom", !lcram);
177
+ this.toggleBankRegion("bank-lc-ram", lcram && !lcbank2);
178
+ this.toggleBankRegion("bank-lc-ram2", lcram && lcbank2);
179
+
180
+ // Update status display
181
+ const readStatus = this.contentElement.querySelector("#bank-read-status");
182
+ const writeStatus = this.contentElement.querySelector("#bank-write-status");
183
+
184
+ if (readStatus) {
185
+ let readBank = ramrd ? "Aux RAM" : "Main RAM";
186
+ if (altzp) readBank += " (Aux ZP)";
187
+ readStatus.textContent = readBank;
188
+ }
189
+
190
+ if (writeStatus) {
191
+ let writeBank = ramwrt ? "Aux RAM" : "Main RAM";
192
+ if (lcwrite) writeBank += " + LC";
193
+ writeStatus.textContent = writeBank;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Toggle visibility of a bank region element
199
+ */
200
+ toggleBankRegion(id, show) {
201
+ const el = this.contentElement.querySelector(`#${id}`);
202
+ if (el) {
203
+ el.classList.toggle("hidden", !show);
204
+ }
205
+ }
206
+ }