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,316 @@
1
+ /*
2
+ * disk2_card.hpp - Disk II controller 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 "../disk-image/disk_image.hpp"
12
+ #include <array>
13
+ #include <cstdint>
14
+ #include <functional>
15
+ #include <memory>
16
+ #include <string>
17
+
18
+ namespace a2e {
19
+
20
+ /**
21
+ * Disk2Card - Disk II Controller Card
22
+ *
23
+ * Complete implementation of the Disk II controller card for Apple IIe.
24
+ * The Disk II typically occupies slot 6, providing:
25
+ * - I/O space: $C0E0-$C0EF (16 soft switches for disk control)
26
+ * - ROM space: $C600-$C6FF (256 byte bootstrap ROM, P5A 341-0027)
27
+ *
28
+ * The card does not use expansion ROM ($C800-$CFFF).
29
+ *
30
+ * Soft switch addresses (offset from slot base):
31
+ * $00 - Phase 0 off $01 - Phase 0 on
32
+ * $02 - Phase 1 off $03 - Phase 1 on
33
+ * $04 - Phase 2 off $05 - Phase 2 on
34
+ * $06 - Phase 3 off $07 - Phase 3 on
35
+ * $08 - Motor off $09 - Motor on
36
+ * $0A - Drive 1 select $0B - Drive 2 select
37
+ * $0C - Q6L (read) $0D - Q6H (WP sense/write load)
38
+ * $0E - Q7L (read mode) $0F - Q7H (write mode)
39
+ */
40
+ class Disk2Card : public ExpansionCard {
41
+ public:
42
+ Disk2Card();
43
+ explicit Disk2Card(const uint8_t* rom, size_t romSize);
44
+ ~Disk2Card() override = default;
45
+
46
+ // Delete copy (disk images are non-copyable)
47
+ Disk2Card(const Disk2Card&) = delete;
48
+ Disk2Card& operator=(const Disk2Card&) = delete;
49
+
50
+ // Allow move
51
+ Disk2Card(Disk2Card&&) = default;
52
+ Disk2Card& operator=(Disk2Card&&) = default;
53
+
54
+ // ===== ExpansionCard Interface =====
55
+
56
+ uint8_t readIO(uint8_t offset) override;
57
+ void writeIO(uint8_t offset, uint8_t value) override;
58
+ uint8_t peekIO(uint8_t offset) const override;
59
+
60
+ uint8_t readROM(uint8_t offset) override;
61
+ bool hasROM() const override { return true; }
62
+
63
+ bool hasExpansionROM() const override { return false; }
64
+
65
+ void reset() override;
66
+ void update(int cycles) override;
67
+
68
+ void setCycleCallback(CycleCallback callback) override;
69
+
70
+ size_t getStateSize() const override;
71
+ size_t serialize(uint8_t* buffer, size_t maxSize) const override;
72
+ size_t deserialize(const uint8_t* buffer, size_t size) override;
73
+
74
+ const char* getName() const override { return "Disk II"; }
75
+ uint8_t getPreferredSlot() const override { return 6; }
76
+
77
+ // ===== Disk II Specific Methods =====
78
+
79
+ /**
80
+ * Load the P5A ROM (341-0027)
81
+ * @param rom ROM data
82
+ * @param size ROM size (should be 256 bytes)
83
+ */
84
+ void loadROM(const uint8_t* rom, size_t size);
85
+
86
+ // ===== Disk Operations =====
87
+
88
+ /**
89
+ * Insert a disk image into a drive
90
+ * @param drive Drive number (0 or 1)
91
+ * @param data Pointer to disk image data
92
+ * @param size Size of the data
93
+ * @param filename Original filename (for format detection)
94
+ * @return true on success
95
+ */
96
+ bool insertDisk(int drive, const uint8_t* data, size_t size, const std::string& filename);
97
+
98
+ /**
99
+ * Insert a blank, unformatted disk into a drive
100
+ * @param drive Drive number (0 or 1)
101
+ * @return true on success
102
+ */
103
+ bool insertBlankDisk(int drive);
104
+
105
+ /**
106
+ * Eject disk from a drive
107
+ * @param drive Drive number (0 or 1)
108
+ */
109
+ void ejectDisk(int drive);
110
+
111
+ /**
112
+ * Check if a drive has a disk inserted
113
+ * @param drive Drive number (0 or 1)
114
+ * @return true if disk is inserted
115
+ */
116
+ bool hasDisk(int drive) const;
117
+
118
+ /**
119
+ * Get the disk data for saving (DSK format)
120
+ * @param drive Drive number (0 or 1)
121
+ * @param size Output: size of the data
122
+ * @return Pointer to disk data, or nullptr if no disk
123
+ */
124
+ const uint8_t* getDiskData(int drive, size_t* size) const;
125
+
126
+ /**
127
+ * Export disk data in its native format for saving
128
+ * This works for both DSK and WOZ formats.
129
+ * @param drive Drive number (0 or 1)
130
+ * @param size Output: size of the exported data
131
+ * @return Pointer to exported data, or nullptr if no disk
132
+ */
133
+ const uint8_t* exportDiskData(int drive, size_t* size);
134
+
135
+ /**
136
+ * Get the disk image for a drive (for UI display)
137
+ * @param drive Drive number (0 or 1)
138
+ * @return Pointer to disk image, or nullptr if no disk
139
+ */
140
+ const DiskImage* getDiskImage(int drive) const;
141
+
142
+ /**
143
+ * Get mutable disk image for a drive (for state restoration)
144
+ * @param drive Drive number (0 or 1)
145
+ * @return Pointer to disk image, or nullptr if no disk
146
+ */
147
+ DiskImage* getMutableDiskImage(int drive);
148
+
149
+ /**
150
+ * Check if motor is currently on
151
+ * @return true if motor is running
152
+ */
153
+ bool isMotorOn() const;
154
+
155
+ /**
156
+ * Stop the motor immediately (for warm reset)
157
+ * Does not reset other controller state like track position
158
+ */
159
+ void stopMotor();
160
+
161
+ /**
162
+ * Get currently selected drive (0 or 1)
163
+ * @return Selected drive number
164
+ */
165
+ int getSelectedDrive() const { return selectedDrive_; }
166
+
167
+ /**
168
+ * Get the current track position from the selected drive's disk image
169
+ * @return Track number (0-34), or -1 if no disk
170
+ */
171
+ int getCurrentTrack() const;
172
+
173
+ /**
174
+ * Get the current quarter-track position from the selected drive's disk image
175
+ * @return Quarter-track number (0-139), or -1 if no disk
176
+ */
177
+ int getQuarterTrack() const;
178
+
179
+ /**
180
+ * Get the current phase magnet states
181
+ * @return Bit field where bit 0-3 represent phases 0-3 (1 = on, 0 = off)
182
+ */
183
+ uint8_t getPhaseStates() const { return phaseStates_; }
184
+
185
+ /**
186
+ * Get Q6 latch state
187
+ * @return true if Q6 is high, false if low
188
+ */
189
+ bool getQ6() const { return q6_; }
190
+
191
+ /**
192
+ * Get Q7 latch state
193
+ * @return true if Q7 is high (write mode), false if low (read mode)
194
+ */
195
+ bool getQ7() const { return q7_; }
196
+
197
+ /**
198
+ * Get the data register value (LSS shift register)
199
+ * @return Current data register value
200
+ */
201
+ uint8_t getDataLatch() const { return dataRegister_; }
202
+
203
+ /**
204
+ * Get the sequencer state (4-bit, 0-15)
205
+ */
206
+ uint8_t getSequencerState() const { return sequencerState_; }
207
+
208
+ /**
209
+ * Get the bus data value (last value written by CPU)
210
+ */
211
+ uint8_t getBusData() const { return busData_; }
212
+
213
+ // ===== State Restoration Methods =====
214
+
215
+ void setSelectedDrive(int drive) { selectedDrive_ = (drive == 0) ? 0 : 1; }
216
+ void setQ6(bool q6) { q6_ = q6; }
217
+ void setQ7(bool q7) { q7_ = q7; }
218
+ void setPhaseStates(uint8_t states) { phaseStates_ = states; }
219
+ void setDataLatch(uint8_t latch) { dataRegister_ = latch; }
220
+ void setMotorOn(bool on) { motorOn_ = on; }
221
+ void setSequencerState(uint8_t s) { sequencerState_ = s & 0x0F; }
222
+ void setBusData(uint8_t d) { busData_ = d; }
223
+ uint8_t getLSSClock() const { return lssClock_; }
224
+ void setLSSClock(uint8_t c) { lssClock_ = c & 0x07; }
225
+
226
+ private:
227
+ // Soft switch offsets
228
+ static constexpr uint8_t PHASE0_OFF = 0x00;
229
+ static constexpr uint8_t PHASE0_ON = 0x01;
230
+ static constexpr uint8_t PHASE1_OFF = 0x02;
231
+ static constexpr uint8_t PHASE1_ON = 0x03;
232
+ static constexpr uint8_t PHASE2_OFF = 0x04;
233
+ static constexpr uint8_t PHASE2_ON = 0x05;
234
+ static constexpr uint8_t PHASE3_OFF = 0x06;
235
+ static constexpr uint8_t PHASE3_ON = 0x07;
236
+ static constexpr uint8_t MOTOR_OFF = 0x08;
237
+ static constexpr uint8_t MOTOR_ON = 0x09;
238
+ static constexpr uint8_t DRIVE1_SELECT = 0x0A;
239
+ static constexpr uint8_t DRIVE2_SELECT = 0x0B;
240
+ static constexpr uint8_t Q6L = 0x0C;
241
+ static constexpr uint8_t Q6H = 0x0D;
242
+ static constexpr uint8_t Q7L = 0x0E;
243
+ static constexpr uint8_t Q7H = 0x0F;
244
+
245
+ // Motor timeout: ~1 second at 1.023 MHz
246
+ static constexpr uint64_t MOTOR_OFF_DELAY_CYCLES = 1023000;
247
+
248
+ // LSS timing: 4 CPU cycles per bit cell
249
+ static constexpr int CYCLES_PER_BIT = 4;
250
+
251
+ // Maximum catch-up: ~one disk revolution (~51200 bits for standard track)
252
+ static constexpr uint32_t MAX_CATCHUP_BITS = 53000;
253
+
254
+ // P6 sequencer ROM (341-0028, 256x4 bits, de-scrambled to logical format)
255
+ static const uint8_t P6_ROM[256];
256
+
257
+ // P5A ROM (256 bytes)
258
+ std::array<uint8_t, 256> rom_;
259
+
260
+ // Controller state
261
+ mutable bool motorOn_ = false;
262
+ mutable uint64_t motorOffCycle_ = 0;
263
+ int selectedDrive_ = 0;
264
+ bool q6_ = false;
265
+ bool q7_ = false;
266
+ uint8_t phaseStates_ = 0;
267
+
268
+ // Timing state
269
+ uint64_t totalCycles_ = 0;
270
+
271
+ // LSS state (P6 ROM clocked at 2x CPU rate = 8 ticks per bit cell)
272
+ uint8_t sequencerState_ = 0; // 4-bit state (0-15)
273
+ uint8_t dataRegister_ = 0; // 8-bit shift register
274
+ uint64_t lastLSSCycle_ = 0; // Last cycle LSS was clocked
275
+ uint8_t busData_ = 0; // CPU bus data for LOAD operations
276
+ uint8_t lssClock_ = 0; // 8-phase clock (0-7), disk I/O at phase 4
277
+ uint8_t writeLevel_ = 0; // Previous write amplifier level for transition encoding
278
+
279
+ // Disk images for each drive
280
+ std::unique_ptr<DiskImage> diskImages_[2];
281
+
282
+ // Cycle callback
283
+ CycleCallback cycleCallback_;
284
+
285
+ /**
286
+ * Get current cycle count, using callback if available
287
+ */
288
+ uint64_t getCycles() const {
289
+ if (cycleCallback_) {
290
+ return cycleCallback_();
291
+ }
292
+ return totalCycles_;
293
+ }
294
+
295
+ /**
296
+ * Handle soft switch access
297
+ * @param offset Offset (0x00-0x0F)
298
+ * @param isWrite true if write access, false if read
299
+ * @return byte value for reads
300
+ */
301
+ uint8_t handleSoftSwitch(uint8_t offset, bool isWrite);
302
+
303
+ /**
304
+ * Clock the Logic State Sequencer by one tick (runs at 2x CPU rate).
305
+ * 8 ticks = 1 bit cell. Disk read/write occurs at phase 4 only.
306
+ */
307
+ void clockLSS();
308
+
309
+ /**
310
+ * Advance the LSS to the current CPU cycle
311
+ * @param currentCycle Current total CPU cycle count
312
+ */
313
+ void catchUpLSS(uint64_t currentCycle);
314
+ };
315
+
316
+ } // namespace a2e
@@ -0,0 +1,185 @@
1
+ /*
2
+ * expansion_card.hpp - Base class for expansion cards
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 <functional>
13
+
14
+ namespace a2e {
15
+
16
+ /**
17
+ * ExpansionCard - Abstract interface for Apple IIe expansion slot cards
18
+ *
19
+ * The Apple IIe has 7 expansion slots (1-7), each with:
20
+ * - I/O space: $C0n0-$C0nF (16 bytes, where n = slot + 8)
21
+ * - ROM space: $Cn00-$CnFF (256 bytes)
22
+ * - Expansion ROM: $C800-$CFFF (shared 2KB, active when card's ROM is accessed)
23
+ *
24
+ * Slot assignments by convention:
25
+ * - Slot 1: Printer cards
26
+ * - Slot 2: Serial/modem cards
27
+ * - Slot 3: 80-column card (built-in on //e, controlled by SLOTC3ROM)
28
+ * - Slot 4: Mockingboard / other sound cards
29
+ * - Slot 5: Hard drive / accelerator cards
30
+ * - Slot 6: Disk II controller
31
+ * - Slot 7: ProDOS RAM disk / other cards
32
+ */
33
+ class ExpansionCard {
34
+ public:
35
+ using IRQCallback = std::function<void()>;
36
+ using CycleCallback = std::function<uint64_t()>;
37
+
38
+ virtual ~ExpansionCard() = default;
39
+
40
+ // ===== I/O Space Access ($C0n0-$C0nF) =====
41
+
42
+ /**
43
+ * Read from the card's I/O space
44
+ * @param offset Offset within slot I/O (0-15)
45
+ * @return Byte value
46
+ */
47
+ virtual uint8_t readIO(uint8_t offset) = 0;
48
+
49
+ /**
50
+ * Write to the card's I/O space
51
+ * @param offset Offset within slot I/O (0-15)
52
+ * @param value Byte value
53
+ */
54
+ virtual void writeIO(uint8_t offset, uint8_t value) = 0;
55
+
56
+ /**
57
+ * Peek at I/O without side effects (for debugger)
58
+ * @param offset Offset within slot I/O (0-15)
59
+ * @return Byte value
60
+ */
61
+ virtual uint8_t peekIO(uint8_t offset) const { return 0xFF; }
62
+
63
+ // ===== ROM Space Access ($Cn00-$CnFF) =====
64
+
65
+ /**
66
+ * Read from the card's ROM space
67
+ * @param offset Offset within slot ROM (0-255)
68
+ * @return Byte value
69
+ */
70
+ virtual uint8_t readROM(uint8_t offset) = 0;
71
+
72
+ /**
73
+ * Check if this card has ROM (most cards do)
74
+ * @return true if card provides ROM at $Cn00-$CnFF
75
+ */
76
+ virtual bool hasROM() const { return true; }
77
+
78
+ /**
79
+ * Write to the card's ROM space (unusual - used by cards like Mockingboard
80
+ * that put I/O registers in ROM space instead of I/O space)
81
+ * @param offset Offset within slot ROM (0-255)
82
+ * @param value Byte value
83
+ */
84
+ virtual void writeROM(uint8_t offset, uint8_t value) {}
85
+
86
+ // ===== Expansion ROM Space ($C800-$CFFF) =====
87
+
88
+ /**
89
+ * Check if this card has expansion ROM
90
+ * @return true if card provides expansion ROM at $C800-$CFFF
91
+ */
92
+ virtual bool hasExpansionROM() const { return false; }
93
+
94
+ /**
95
+ * Read from the card's expansion ROM space
96
+ * @param offset Offset within expansion ROM (0-2047)
97
+ * @return Byte value
98
+ */
99
+ virtual uint8_t readExpansionROM(uint16_t offset) { return 0xFF; }
100
+
101
+ // ===== Lifecycle =====
102
+
103
+ /**
104
+ * Reset the card to power-on state
105
+ */
106
+ virtual void reset() = 0;
107
+
108
+ /**
109
+ * Update the card's internal state (call each CPU cycle)
110
+ * @param cycles Number of CPU cycles elapsed
111
+ */
112
+ virtual void update(int cycles) {}
113
+
114
+ // ===== Callbacks =====
115
+
116
+ /**
117
+ * Set callback for generating IRQ
118
+ * @param callback Function to call when IRQ is asserted
119
+ */
120
+ virtual void setIRQCallback(IRQCallback callback) {}
121
+
122
+ /**
123
+ * Set callback for getting current CPU cycle count
124
+ * @param callback Function that returns current cycle count
125
+ */
126
+ virtual void setCycleCallback(CycleCallback callback) {}
127
+
128
+ /**
129
+ * Check if IRQ is currently active
130
+ * @return true if IRQ line is asserted
131
+ */
132
+ virtual bool isIRQActive() const { return false; }
133
+
134
+ // ===== State Serialization =====
135
+
136
+ /**
137
+ * Get the size of the serialized state
138
+ * @return Size in bytes
139
+ */
140
+ virtual size_t getStateSize() const { return 0; }
141
+
142
+ /**
143
+ * Serialize card state for save
144
+ * @param buffer Output buffer
145
+ * @param maxSize Maximum bytes to write
146
+ * @return Bytes written
147
+ */
148
+ virtual size_t serialize(uint8_t* buffer, size_t maxSize) const { return 0; }
149
+
150
+ /**
151
+ * Deserialize card state from save
152
+ * @param buffer Input buffer
153
+ * @param size Size of input data
154
+ * @return Bytes read
155
+ */
156
+ virtual size_t deserialize(const uint8_t* buffer, size_t size) { return 0; }
157
+
158
+ // ===== Card Information =====
159
+
160
+ /**
161
+ * Get the card's name for display
162
+ * @return Human-readable name
163
+ */
164
+ virtual const char* getName() const = 0;
165
+
166
+ /**
167
+ * Get the card's preferred slot (0 = any)
168
+ * @return Preferred slot number (1-7) or 0 for no preference
169
+ */
170
+ virtual uint8_t getPreferredSlot() const { return 0; }
171
+
172
+ /**
173
+ * Check if card is currently enabled
174
+ * @return true if card is active
175
+ */
176
+ virtual bool isEnabled() const { return true; }
177
+
178
+ /**
179
+ * Enable or disable the card
180
+ * @param enabled true to enable
181
+ */
182
+ virtual void setEnabled(bool enabled) {}
183
+ };
184
+
185
+ } // namespace a2e