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,190 @@
1
+ /*
2
+ * mouse_card.hpp - Apple Mouse Interface card
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include "expansion_card.hpp"
11
+ #include <cstdint>
12
+
13
+ namespace a2e {
14
+
15
+ /**
16
+ * MouseCard - Apple Mouse Interface Card emulation
17
+ *
18
+ * Emulates the Apple Mouse Interface Card (342-0270-C ROM) using the
19
+ * AppleWin-style PIA command protocol. The real ROM firmware executes
20
+ * as native 6502 code on the CPU while our C++ code emulates the
21
+ * "MCU side" of the MC6821 PIA — receiving commands and providing
22
+ * response data through Port A/B transitions.
23
+ *
24
+ * Protocol overview:
25
+ * 6502 CPU <-> ROM firmware (runs natively) <-> MC6821 PIA <-> Our "MCU" code
26
+ *
27
+ * The firmware writes command bytes to PIA Port A and toggles Port B
28
+ * control signals. Our code detects Port B transitions, buffers the
29
+ * command/data, and places response data on Port A input pins for the
30
+ * firmware to read back.
31
+ *
32
+ * Mouse state is communicated through "screen holes" by the firmware
33
+ * itself — we only provide position/status data via the PIA when asked.
34
+ *
35
+ * The 6821 PIA provides 4 registers at I/O offsets 0-3 and the
36
+ * real ROM is served for card identification and firmware execution.
37
+ * Port B bits 1-3 select the 256-byte ROM page (2KB total).
38
+ *
39
+ * VBL (Vertical Blank) interrupt generation:
40
+ * When the mouse mode has bit 3 set (VBL interrupt enable), an IRQ
41
+ * is generated at the start of each vertical blanking period.
42
+ *
43
+ * Available in slots 2, 4, and 7.
44
+ */
45
+ class MouseCard : public ExpansionCard {
46
+ public:
47
+ MouseCard();
48
+ ~MouseCard() override = default;
49
+
50
+ // Delete copy
51
+ MouseCard(const MouseCard&) = delete;
52
+ MouseCard& operator=(const MouseCard&) = delete;
53
+
54
+ // Allow move
55
+ MouseCard(MouseCard&&) = default;
56
+ MouseCard& operator=(MouseCard&&) = default;
57
+
58
+ // ===== ExpansionCard Interface =====
59
+
60
+ // I/O space ($C0n0-$C0nF)
61
+ uint8_t readIO(uint8_t offset) override;
62
+ void writeIO(uint8_t offset, uint8_t value) override;
63
+ uint8_t peekIO(uint8_t offset) const override;
64
+
65
+ // ROM space ($Cn00-$CnFF)
66
+ uint8_t readROM(uint8_t offset) override;
67
+ bool hasROM() const override { return true; }
68
+
69
+ // No expansion ROM - the mouse card uses banked slot ROM instead
70
+ bool hasExpansionROM() const override { return false; }
71
+ uint8_t readExpansionROM(uint16_t offset) override;
72
+
73
+ void reset() override;
74
+ void update(int cycles) override;
75
+
76
+ const char* getName() const override { return "Mouse"; }
77
+ uint8_t getPreferredSlot() const override { return 4; }
78
+
79
+ // IRQ support
80
+ void setIRQCallback(IRQCallback callback) override { irqCallback_ = callback; }
81
+ void setCycleCallback(CycleCallback callback) override { cycleCallback_ = callback; }
82
+ bool isIRQActive() const override { return irqActive_; }
83
+
84
+ // State serialization
85
+ static constexpr size_t STATE_SIZE = 36;
86
+ size_t getStateSize() const override { return STATE_SIZE; }
87
+ size_t serialize(uint8_t* buffer, size_t maxSize) const override;
88
+ size_t deserialize(const uint8_t* buffer, size_t size) override;
89
+
90
+ // Mouse input from browser
91
+ void setSlotNumber(uint8_t slot) { slotNum_ = slot; }
92
+ void addDelta(int dx, int dy);
93
+ void setMouseButton(bool pressed);
94
+
95
+ // Debug accessors
96
+ uint8_t getSlotNumber() const { return slotNum_; }
97
+ int16_t getMouseX() const { return mouseX_; }
98
+ int16_t getMouseY() const { return mouseY_; }
99
+ bool getMouseButton() const { return mouseButton_; }
100
+ bool getMoved() const { return moved_; }
101
+ bool getButtonChanged() const { return buttonChanged_; }
102
+ int16_t getClampMinX() const { return clampMinX_; }
103
+ int16_t getClampMaxX() const { return clampMaxX_; }
104
+ int16_t getClampMinY() const { return clampMinY_; }
105
+ int16_t getClampMaxY() const { return clampMaxY_; }
106
+ uint8_t getDDRA() const { return ddra_; }
107
+ uint8_t getDDRB() const { return ddrb_; }
108
+ uint8_t getORA() const { return ora_; }
109
+ uint8_t getORB() const { return orb_; }
110
+ uint8_t getIRA() const { return ira_; }
111
+ uint8_t getIRB() const { return irb_; }
112
+ uint8_t getCRA() const { return cra_; }
113
+ uint8_t getCRB() const { return crb_; }
114
+ bool getVBLInterruptPending() const { return vblInterruptPending_; }
115
+ bool getMoveInterruptPending() const { return moveInterruptPending_; }
116
+ bool getButtonInterruptPending() const { return buttonInterruptPending_; }
117
+ uint8_t getLastCommand() const { return lastCommand_; }
118
+ uint8_t getResponseState() const { return byState_; }
119
+ bool getWasInVBL() const { return wasInVBL_; }
120
+ uint8_t getMode() const { return mode_; }
121
+
122
+ private:
123
+ // ROM data
124
+ const uint8_t* rom_;
125
+ size_t romSize_;
126
+
127
+ // Slot number (needed for screen hole addressing)
128
+ uint8_t slotNum_ = 4;
129
+
130
+ // 6821 PIA registers
131
+ uint8_t ddra_ = 0; // Data Direction Register A (1=output, 0=input)
132
+ uint8_t ddrb_ = 0; // Data Direction Register B
133
+ uint8_t ora_ = 0; // Output Register A
134
+ uint8_t orb_ = 0; // Output Register B
135
+ uint8_t ira_ = 0; // Input Register A (peripheral side)
136
+ uint8_t irb_ = 0; // Input Register B (peripheral side)
137
+ uint8_t cra_ = 0; // Control Register A
138
+ uint8_t crb_ = 0; // Control Register B
139
+
140
+ // Mouse state
141
+ int16_t mouseX_ = 0;
142
+ int16_t mouseY_ = 0;
143
+ bool mouseButton_ = false;
144
+ bool lastButton_ = false;
145
+ bool moved_ = false;
146
+ bool buttonChanged_ = false;
147
+
148
+ // Mouse mode (set by SetMouse command)
149
+ uint8_t mode_ = 0;
150
+
151
+ // Mouse clamping
152
+ int16_t clampMinX_ = 0;
153
+ int16_t clampMaxX_ = 1023;
154
+ int16_t clampMinY_ = 0;
155
+ int16_t clampMaxY_ = 1023;
156
+
157
+ // Interrupt state
158
+ bool irqActive_ = false;
159
+ bool vblInterruptPending_ = false;
160
+ bool moveInterruptPending_ = false;
161
+ bool buttonInterruptPending_ = false;
162
+
163
+ // VBL tracking
164
+ bool wasInVBL_ = false;
165
+
166
+ // IRQ/cycle callbacks
167
+ IRQCallback irqCallback_;
168
+ CycleCallback cycleCallback_;
169
+
170
+ // PIA command protocol state machine (AppleWin-style)
171
+ uint8_t byState_ = 0; // Protocol state (0=idle, 1=writing, 2=reading)
172
+ uint8_t byBuff_[8] = {}; // Command/response data buffer
173
+ uint8_t nBuffPos_ = 0; // Current buffer position
174
+ uint8_t nDataLen_ = 0; // Expected data length for current operation
175
+ uint8_t by6821B_ = 0; // Previous Port B value for transition detection
176
+ uint8_t lastCommand_ = 0; // Last command byte (for debug display)
177
+
178
+ // Position snapshot (set on ReadMouse, served to firmware)
179
+ int16_t snapX_ = 0;
180
+ int16_t snapY_ = 0;
181
+
182
+ void updateIRQState();
183
+
184
+ // PIA command protocol handlers
185
+ void on6821_B(uint8_t byData); // Port B transition handler
186
+ void onCommand(); // Command dispatch
187
+ void onWrite(); // Multi-byte write dispatch
188
+ };
189
+
190
+ } // namespace a2e
@@ -0,0 +1,202 @@
1
+ /*
2
+ * block_device.cpp - Block-oriented storage device for SmartPort
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #include "block_device.hpp"
9
+ #include <cstring>
10
+ #include <algorithm>
11
+
12
+ namespace a2e {
13
+
14
+ bool BlockDevice::load(const uint8_t* data, size_t size, const std::string& filename) {
15
+ if (!data || size == 0) return false;
16
+
17
+ filename_ = filename;
18
+ modified_ = false;
19
+ writeProtected_ = false;
20
+ dataOffset_ = 0;
21
+
22
+ // Check for 2IMG format (64-byte header with "2IMG" magic)
23
+ if (size > 64 && data[0] == '2' && data[1] == 'I' && data[2] == 'M' && data[3] == 'G') {
24
+ // Parse 2IMG header
25
+ uint32_t headerSize = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
26
+ uint32_t dataOffset = data[24] | (data[25] << 8) | (data[26] << 16) | (data[27] << 24);
27
+ uint32_t dataLength = data[28] | (data[29] << 8) | (data[30] << 16) | (data[31] << 24);
28
+ uint32_t flags = data[16] | (data[17] << 8) | (data[18] << 16) | (data[19] << 24);
29
+
30
+ // Validate
31
+ if (headerSize < 64 || dataOffset >= size) return false;
32
+ if (dataOffset + dataLength > size) return false;
33
+
34
+ // Image format in header byte 12: 0=DOS order, 1=ProDOS order, 2=nibble
35
+ uint32_t imageFormat = data[12] | (data[13] << 8) | (data[14] << 16) | (data[15] << 24);
36
+ if (imageFormat == 2) return false; // nibble format not supported as block device
37
+
38
+ writeProtected_ = (flags & 0x80000000) != 0;
39
+
40
+ // Block count from header
41
+ uint32_t blockCount = data[20] | (data[21] << 8) | (data[22] << 16) | (data[23] << 24);
42
+ if (blockCount == 0 && dataLength > 0) {
43
+ blockCount = dataLength / BLOCK_SIZE;
44
+ }
45
+ if (blockCount > MAX_BLOCKS) blockCount = MAX_BLOCKS;
46
+
47
+ dataOffset_ = dataOffset;
48
+ totalBlocks_ = static_cast<uint16_t>(blockCount);
49
+
50
+ // Store the entire file (header + data) so we can export it back
51
+ data_.assign(data, data + size);
52
+ return true;
53
+ }
54
+
55
+ // Raw ProDOS order (.hdv, .po) - just raw blocks
56
+ if (size < BLOCK_SIZE) return false;
57
+
58
+ uint32_t blockCount = size / BLOCK_SIZE;
59
+ if (blockCount > MAX_BLOCKS) blockCount = MAX_BLOCKS;
60
+
61
+ totalBlocks_ = static_cast<uint16_t>(blockCount);
62
+ dataOffset_ = 0;
63
+ data_.assign(data, data + size);
64
+ return true;
65
+ }
66
+
67
+ bool BlockDevice::readBlock(uint16_t blockNum, uint8_t* buffer) const {
68
+ if (!buffer || blockNum >= totalBlocks_) return false;
69
+
70
+ size_t offset = dataOffset_ + static_cast<size_t>(blockNum) * BLOCK_SIZE;
71
+ if (offset + BLOCK_SIZE > data_.size()) return false;
72
+
73
+ std::memcpy(buffer, data_.data() + offset, BLOCK_SIZE);
74
+ return true;
75
+ }
76
+
77
+ bool BlockDevice::writeBlock(uint16_t blockNum, const uint8_t* buffer) {
78
+ if (!buffer || blockNum >= totalBlocks_ || writeProtected_) return false;
79
+
80
+ size_t offset = dataOffset_ + static_cast<size_t>(blockNum) * BLOCK_SIZE;
81
+ if (offset + BLOCK_SIZE > data_.size()) return false;
82
+
83
+ std::memcpy(data_.data() + offset, buffer, BLOCK_SIZE);
84
+ modified_ = true;
85
+ return true;
86
+ }
87
+
88
+ const uint8_t* BlockDevice::exportData(size_t* size) const {
89
+ if (data_.empty()) {
90
+ if (size) *size = 0;
91
+ return nullptr;
92
+ }
93
+ if (size) *size = data_.size();
94
+ return data_.data();
95
+ }
96
+
97
+ const uint8_t* BlockDevice::getBlockData(size_t* size) const {
98
+ if (data_.empty()) {
99
+ if (size) *size = 0;
100
+ return nullptr;
101
+ }
102
+ if (size) *size = data_.size() - dataOffset_;
103
+ return data_.data() + dataOffset_;
104
+ }
105
+
106
+ size_t BlockDevice::getStateSize() const {
107
+ // flags(1) + totalBlocks(2) + dataOffset(4) + filenameLen(2) + filename + dataSize(4) + data
108
+ return 1 + 2 + 4 + 2 + filename_.size() + 4 + data_.size();
109
+ }
110
+
111
+ size_t BlockDevice::serialize(uint8_t* buffer, size_t maxSize) const {
112
+ size_t needed = getStateSize();
113
+ if (maxSize < needed) return 0;
114
+
115
+ size_t offset = 0;
116
+
117
+ // Flags: bit 0 = loaded, bit 1 = writeProtected, bit 2 = modified
118
+ uint8_t flags = 0;
119
+ if (!data_.empty()) flags |= 0x01;
120
+ if (writeProtected_) flags |= 0x02;
121
+ if (modified_) flags |= 0x04;
122
+ buffer[offset++] = flags;
123
+
124
+ // Total blocks (LE16)
125
+ buffer[offset++] = totalBlocks_ & 0xFF;
126
+ buffer[offset++] = (totalBlocks_ >> 8) & 0xFF;
127
+
128
+ // Data offset (LE32)
129
+ uint32_t doff = static_cast<uint32_t>(dataOffset_);
130
+ buffer[offset++] = doff & 0xFF;
131
+ buffer[offset++] = (doff >> 8) & 0xFF;
132
+ buffer[offset++] = (doff >> 16) & 0xFF;
133
+ buffer[offset++] = (doff >> 24) & 0xFF;
134
+
135
+ // Filename
136
+ uint16_t fnLen = static_cast<uint16_t>(filename_.size());
137
+ buffer[offset++] = fnLen & 0xFF;
138
+ buffer[offset++] = (fnLen >> 8) & 0xFF;
139
+ if (fnLen > 0) {
140
+ std::memcpy(buffer + offset, filename_.data(), fnLen);
141
+ offset += fnLen;
142
+ }
143
+
144
+ // Data
145
+ uint32_t dataSize = static_cast<uint32_t>(data_.size());
146
+ buffer[offset++] = dataSize & 0xFF;
147
+ buffer[offset++] = (dataSize >> 8) & 0xFF;
148
+ buffer[offset++] = (dataSize >> 16) & 0xFF;
149
+ buffer[offset++] = (dataSize >> 24) & 0xFF;
150
+ if (dataSize > 0) {
151
+ std::memcpy(buffer + offset, data_.data(), dataSize);
152
+ offset += dataSize;
153
+ }
154
+
155
+ return offset;
156
+ }
157
+
158
+ size_t BlockDevice::deserialize(const uint8_t* buffer, size_t size) {
159
+ if (size < 13) return 0; // minimum: flags + blocks + offset + fnLen + dataSize
160
+
161
+ size_t offset = 0;
162
+
163
+ uint8_t flags = buffer[offset++];
164
+ bool loaded = (flags & 0x01) != 0;
165
+ writeProtected_ = (flags & 0x02) != 0;
166
+ modified_ = (flags & 0x04) != 0;
167
+
168
+ totalBlocks_ = buffer[offset] | (buffer[offset + 1] << 8);
169
+ offset += 2;
170
+
171
+ dataOffset_ = buffer[offset] | (buffer[offset + 1] << 8) |
172
+ (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
173
+ offset += 4;
174
+
175
+ uint16_t fnLen = buffer[offset] | (buffer[offset + 1] << 8);
176
+ offset += 2;
177
+ if (offset + fnLen > size) return 0;
178
+ if (fnLen > 0) {
179
+ filename_.assign(reinterpret_cast<const char*>(buffer + offset), fnLen);
180
+ offset += fnLen;
181
+ } else {
182
+ filename_.clear();
183
+ }
184
+
185
+ if (offset + 4 > size) return 0;
186
+ uint32_t dataSize = buffer[offset] | (buffer[offset + 1] << 8) |
187
+ (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
188
+ offset += 4;
189
+
190
+ if (loaded && dataSize > 0) {
191
+ if (offset + dataSize > size) return 0;
192
+ data_.assign(buffer + offset, buffer + offset + dataSize);
193
+ offset += dataSize;
194
+ } else {
195
+ data_.clear();
196
+ totalBlocks_ = 0;
197
+ }
198
+
199
+ return offset;
200
+ }
201
+
202
+ } // namespace a2e
@@ -0,0 +1,60 @@
1
+ /*
2
+ * block_device.hpp - Block-oriented storage device for SmartPort
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <cstdint>
11
+ #include <cstddef>
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ namespace a2e {
16
+
17
+ class BlockDevice {
18
+ public:
19
+ static constexpr size_t BLOCK_SIZE = 512;
20
+ static constexpr size_t MAX_BLOCKS = 65535;
21
+
22
+ BlockDevice() = default;
23
+ ~BlockDevice() = default;
24
+
25
+ bool load(const uint8_t* data, size_t size, const std::string& filename);
26
+ bool readBlock(uint16_t blockNum, uint8_t* buffer) const;
27
+ bool writeBlock(uint16_t blockNum, const uint8_t* buffer);
28
+
29
+ uint16_t getTotalBlocks() const { return totalBlocks_; }
30
+ bool isWriteProtected() const { return writeProtected_; }
31
+ bool isModified() const { return modified_; }
32
+ bool isLoaded() const { return !data_.empty(); }
33
+ const std::string& getFilename() const { return filename_; }
34
+
35
+ const uint8_t* exportData(size_t* size) const;
36
+ const uint8_t* getBlockData(size_t* size) const;
37
+
38
+ size_t getStateSize() const;
39
+ size_t serialize(uint8_t* buffer, size_t maxSize) const;
40
+ size_t deserialize(const uint8_t* buffer, size_t size);
41
+
42
+ void eject() {
43
+ data_.clear();
44
+ totalBlocks_ = 0;
45
+ modified_ = false;
46
+ writeProtected_ = false;
47
+ filename_.clear();
48
+ dataOffset_ = 0;
49
+ }
50
+
51
+ private:
52
+ std::vector<uint8_t> data_;
53
+ uint16_t totalBlocks_ = 0;
54
+ bool writeProtected_ = false;
55
+ bool modified_ = false;
56
+ std::string filename_;
57
+ size_t dataOffset_ = 0; // offset to block data (non-zero for 2IMG)
58
+ };
59
+
60
+ } // namespace a2e