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,434 @@
1
+ /*
2
+ * emulator.hpp - Core emulator coordinator tying together CPU, memory, video, audio, and peripherals
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include "audio/audio.hpp"
11
+ #include "cpu/cpu6502.hpp"
12
+ #include "cards/disk2_card.hpp"
13
+ #include "cards/expansion_card.hpp"
14
+ #include "input/keyboard.hpp"
15
+ #include "cards/mockingboard_card.hpp"
16
+ #include "cards/mouse_card.hpp"
17
+ #include "cards/smartport/smartport_card.hpp"
18
+ #include "mmu/mmu.hpp"
19
+ #include "types.hpp"
20
+ #include "video/video.hpp"
21
+ #include <cstdint>
22
+ #include <memory>
23
+ #include <set>
24
+ #include <string>
25
+ #include <vector>
26
+ #include <array>
27
+ #include <unordered_map>
28
+
29
+ namespace a2e {
30
+
31
+ class Emulator {
32
+ public:
33
+ // BASIC stepping modes
34
+ enum class BasicStepMode { None, Line, Statement };
35
+
36
+ Emulator();
37
+ ~Emulator();
38
+
39
+ // Initialization
40
+ void init();
41
+ void reset(); // Cold reset - clears memory
42
+ void warmReset(); // Warm reset - CPU only, preserves memory
43
+
44
+ // Execution
45
+ void runCycles(int cycles);
46
+ int generateStereoAudioSamples(float *buffer, int sampleCount);
47
+
48
+ // Audio-driven frame synchronization
49
+ // Returns number of complete frames worth of samples generated since last
50
+ // call
51
+ int consumeFrameSamples();
52
+
53
+ // Frame management
54
+ bool isFrameReady() const { return frameReady_; }
55
+ void clearFrameReady() { frameReady_ = false; }
56
+ const uint8_t *getFramebuffer() const;
57
+ size_t getFramebufferSize() const { return FRAMEBUFFER_SIZE; }
58
+
59
+ // Input - raw browser keycodes (preferred)
60
+ int handleRawKeyDown(int browserKeycode, bool shift, bool ctrl, bool alt,
61
+ bool meta, bool capsLock);
62
+ void handleRawKeyUp(int browserKeycode, bool shift, bool ctrl, bool alt,
63
+ bool meta);
64
+
65
+ // Input - direct Apple II keycodes (for paste functionality)
66
+ void keyDown(int keycode);
67
+ void keyUp(int keycode);
68
+ void setButton(int button, bool pressed); // Set button state (0=Open Apple, 1=Closed Apple, 2=Button2)
69
+ void setPaddleValue(int paddle, int value); // Set paddle value (0-3, value 0-255)
70
+ int getPaddleValue(int paddle) const; // Get paddle value (0-3)
71
+
72
+ // Mouse input
73
+ void mouseMove(int dx, int dy);
74
+ void mouseButton(bool pressed);
75
+ bool isKeyboardReady() const { return (keyboardLatch_ & 0x80) == 0; } // True if strobe cleared
76
+
77
+ // Disk management
78
+ bool insertDisk(int drive, const uint8_t *data, size_t size,
79
+ const char *filename);
80
+ bool insertBlankDisk(int drive);
81
+ void ejectDisk(int drive);
82
+ const uint8_t *getDiskData(int drive, size_t *size) const;
83
+ const uint8_t *exportDiskData(int drive, size_t *size);
84
+ const char *getDiskFilename(int drive) const;
85
+
86
+ // Debugger interface
87
+ void addBreakpoint(uint16_t address);
88
+ void removeBreakpoint(uint16_t address);
89
+ void enableBreakpoint(uint16_t address, bool enabled);
90
+ bool isBreakpointHit() const { return breakpointHit_; }
91
+ uint16_t getBreakpointAddress() const { return breakpointAddress_; }
92
+
93
+ // BASIC line and statement breakpoints
94
+ void addBasicBreakpoint(uint16_t lineNumber, int statementIndex);
95
+ void removeBasicBreakpoint(uint16_t lineNumber, int statementIndex);
96
+ void clearBasicBreakpoints();
97
+ void clearBasicBreakpointHit(); // Clear hit state for fresh run
98
+ bool hasBasicBreakpoints() const { return !basicBreakpoints_.empty(); }
99
+ bool isBasicBreakpointHit() const { return basicBreakpointHit_; }
100
+ uint16_t getBasicBreakLine() const { return basicBreakLine_; }
101
+
102
+ // BASIC condition-only rules: evaluated in C++ at every $D820
103
+ void addBasicConditionRule(int id, const char* expression);
104
+ void removeBasicConditionRule(int id);
105
+ void clearBasicConditionRules();
106
+ int getBasicConditionRuleHitId() const { return basicConditionRuleHitId_; }
107
+
108
+ // BASIC stepping - execute current line/statement and stop at next
109
+ void stepBasicLine();
110
+ void stepBasicStatement();
111
+ bool isBasicStepping() const { return basicStepMode_ != BasicStepMode::None; }
112
+ bool isBasicProgramRunning() const { return basicProgramRunning_; }
113
+
114
+ // BASIC runtime error state
115
+ bool isBasicErrorHit() const { return basicErrorHit_; }
116
+ uint16_t getBasicErrorLine() const { return basicErrorLine_; }
117
+ uint16_t getBasicErrorTxtptr() const { return basicErrorTxtptr_; }
118
+ uint8_t getBasicErrorCode() const { return basicErrorCode_; }
119
+ void clearBasicError() { basicErrorHit_ = false; }
120
+ uint16_t getBasicTxtptr() const; // Get current TXTPTR for statement highlighting
121
+ int getBasicStatementIndex(); // Get current statement index (0-based)
122
+
123
+ // BASIC line heat map
124
+ void setBasicHeatMapEnabled(bool enabled) { basicHeatMapEnabled_ = enabled; }
125
+ bool isBasicHeatMapEnabled() const { return basicHeatMapEnabled_; }
126
+ void clearBasicHeatMap() { basicHeatMap_.clear(); }
127
+ const std::unordered_map<uint16_t, uint32_t>& getBasicHeatMap() const { return basicHeatMap_; }
128
+ int getBasicHeatMapSize() const { return static_cast<int>(basicHeatMap_.size()); }
129
+ // Copy heat map data into caller-provided buffers (lines[], counts[]), returns entry count
130
+ int getBasicHeatMapData(uint16_t* lines, uint32_t* counts, int maxEntries) const;
131
+
132
+ // Beam position (derived from cycle count)
133
+ int getFrameCycle() const;
134
+ int getBeamScanline() const;
135
+ int getBeamHPos() const;
136
+ int getBeamColumn() const;
137
+ bool isInVBL() const;
138
+ bool isInHBLANK() const;
139
+
140
+ // Step Over / Step Out
141
+ uint16_t stepOver(); // Returns temp breakpoint address, or 0 if single-stepped
142
+ uint16_t stepOut(); // Returns temp breakpoint address, or 0 if invalid
143
+ void clearTempBreakpoint();
144
+ bool isTempBreakpointHit() const { return tempBreakpointHit_; }
145
+
146
+ // CPU state access
147
+ uint16_t getPC() const { return cpu_->getPC(); }
148
+ uint8_t getSP() const { return cpu_->getSP(); }
149
+ uint8_t getA() const { return cpu_->getA(); }
150
+ uint8_t getX() const { return cpu_->getX(); }
151
+ uint8_t getY() const { return cpu_->getY(); }
152
+ uint8_t getP() const { return cpu_->getP(); }
153
+ uint64_t getTotalCycles() const { return cpu_->getTotalCycles(); }
154
+ bool isIRQPending() const { return cpu_->isIRQPending(); }
155
+ bool isNMIPending() const { return cpu_->isNMIPending(); }
156
+ bool isNMIEdge() const { return cpu_->isNMIEdge(); }
157
+
158
+ // CPU state setters (for debugger register editing)
159
+ void setPC(uint16_t v) { cpu_->setPC(v); }
160
+ void setSP(uint8_t v) { cpu_->setSP(v); }
161
+ void setA(uint8_t v) { cpu_->setA(v); }
162
+ void setX(uint8_t v) { cpu_->setX(v); }
163
+ void setY(uint8_t v) { cpu_->setY(v); }
164
+ void setP(uint8_t v) { cpu_->setP(v); }
165
+
166
+ // Watchpoints
167
+ enum WatchpointType : uint8_t { WP_READ = 1, WP_WRITE = 2, WP_READWRITE = 3 };
168
+ void addWatchpoint(uint16_t startAddr, uint16_t endAddr, WatchpointType type);
169
+ void removeWatchpoint(uint16_t startAddr);
170
+ void clearWatchpoints();
171
+ bool isWatchpointHit() const { return watchpointHit_; }
172
+ uint16_t getWatchpointAddress() const { return watchpointAddress_; }
173
+ uint8_t getWatchpointValue() const { return watchpointValue_; }
174
+ bool isWatchpointWrite() const { return watchpointIsWrite_; }
175
+
176
+ // Beam breakpoints
177
+ int32_t addBeamBreakpoint(int16_t scanline, int16_t hPos); // returns ID, -1 if full
178
+ void removeBeamBreakpoint(int32_t id);
179
+ void enableBeamBreakpoint(int32_t id, bool enabled);
180
+ void clearAllBeamBreakpoints();
181
+ bool isBeamBreakpointHit() const { return beamBreakHit_; }
182
+ int32_t getBeamBreakpointHitId() const { return beamBreakHitId_; }
183
+ int16_t getBeamBreakScanline() const { return beamBreakHitScanline_; }
184
+ int16_t getBeamBreakHPos() const { return beamBreakHitHPos_; }
185
+
186
+ // Trace log
187
+ struct TraceEntry {
188
+ uint16_t pc;
189
+ uint8_t opcode, a, x, y, sp, p;
190
+ uint8_t operand1, operand2, instrLen;
191
+ uint8_t padding;
192
+ uint32_t cycle;
193
+ };
194
+ void setTraceEnabled(bool enabled) { traceEnabled_ = enabled; }
195
+ bool isTraceEnabled() const { return traceEnabled_; }
196
+ void clearTrace() { traceHead_ = 0; traceCount_ = 0; }
197
+ size_t getTraceCount() const { return traceCount_; }
198
+ size_t getTraceHead() const { return traceHead_; }
199
+ const TraceEntry* getTraceBuffer() const { return traceBuffer_.data(); }
200
+ size_t getTraceCapacity() const { return traceBuffer_.size(); }
201
+
202
+ // Cycle profiling
203
+ void setProfileEnabled(bool enabled) { profileEnabled_ = enabled; }
204
+ bool isProfileEnabled() const { return profileEnabled_; }
205
+ void clearProfile() { profileCycles_.fill(0); }
206
+ const uint32_t* getProfileCycles() const { return profileCycles_.data(); }
207
+
208
+ // Speed control
209
+ void setSpeedMultiplier(int multiplier);
210
+ int getSpeedMultiplier() const { return speedMultiplier_; }
211
+
212
+ // Pause/resume
213
+ bool isPaused() const { return paused_; }
214
+ void setPaused(bool paused);
215
+
216
+ // Single step
217
+ void stepInstruction();
218
+
219
+ // Memory access
220
+ uint8_t readMemory(uint16_t address) const;
221
+ uint8_t peekMemory(uint16_t address) const; // Non-side-effecting read for debugger
222
+ void writeMemory(uint16_t address, uint8_t value);
223
+
224
+ // Disassembly
225
+ const char *disassembleAt(uint16_t address);
226
+
227
+ // Soft switch state (64-bit packed state)
228
+ uint64_t getSoftSwitchState() const;
229
+
230
+ // Screen text extraction (for text selection / copy)
231
+ static int screenCodeToAscii(uint8_t code);
232
+ const char* readScreenText(int startRow, int startCol, int endRow, int endCol);
233
+
234
+ // State serialization for save/restore
235
+ // Returns pointer to state data and sets size. Caller does not own the pointer.
236
+ const uint8_t *exportState(size_t *size);
237
+ // Restores state from data. Returns true on success.
238
+ bool importState(const uint8_t *data, size_t size);
239
+
240
+ // Components access
241
+ MMU &getMMU() { return *mmu_; }
242
+ Video &getVideo() { return *video_; }
243
+ Audio &getAudio() { return *audio_; }
244
+ Disk2Card &getDisk() { return *disk_; }
245
+ MockingboardCard &getMockingboard() { return *mockingboard_; }
246
+ MouseCard* getMouseCard() { return mouse_; }
247
+ SmartPortCard* getSmartPortCard() { return smartport_; }
248
+
249
+ // SmartPort hard drive management
250
+ bool insertSmartPortImage(int device, const uint8_t* data, size_t size, const char* filename);
251
+ void ejectSmartPortImage(int device);
252
+ bool isSmartPortImageInserted(int device) const;
253
+ const char* getSmartPortImageFilename(int device) const;
254
+ bool isSmartPortImageModified(int device) const;
255
+ const uint8_t* exportSmartPortImageData(int device, size_t* size) const;
256
+ const uint8_t* getSmartPortBlockData(int device, size_t* size) const;
257
+ bool isSmartPortCardInstalled() const { return smartport_ != nullptr; }
258
+
259
+ // Slot management
260
+ const char* getSlotCardName(uint8_t slot) const;
261
+ bool setSlotCard(uint8_t slot, const char* cardId);
262
+ bool isSlotEmpty(uint8_t slot) const;
263
+
264
+ private:
265
+ // Memory callbacks for CPU
266
+ uint8_t cpuRead(uint16_t address);
267
+ void cpuWrite(uint16_t address, uint8_t value);
268
+
269
+ // Keyboard handling
270
+ uint8_t getKeyboardData();
271
+ void clearKeyboardStrobe();
272
+
273
+ // Speaker callback
274
+ void toggleSpeaker();
275
+
276
+ // Components
277
+ std::unique_ptr<MMU> mmu_;
278
+ std::unique_ptr<CPU6502> cpu_;
279
+ std::unique_ptr<Video> video_;
280
+ std::unique_ptr<Audio> audio_;
281
+ std::unique_ptr<Keyboard> keyboard_;
282
+
283
+ // Non-owning pointers to cards (owned by MMU slot system)
284
+ Disk2Card* disk_ = nullptr;
285
+ MockingboardCard* mockingboard_ = nullptr;
286
+ MouseCard* mouse_ = nullptr;
287
+ SmartPortCard* smartport_ = nullptr;
288
+
289
+ // Storage for cards when removed from slots
290
+ std::unique_ptr<ExpansionCard> diskStorage_;
291
+ std::unique_ptr<ExpansionCard> mbStorage_;
292
+
293
+ // Keyboard state
294
+ uint8_t keyboardLatch_ = 0;
295
+ bool keyDown_ = false;
296
+
297
+ // Button state (Open Apple, Closed Apple, Button 2)
298
+ bool buttonState_[3] = {false, false, false};
299
+ uint8_t getButtonState(int button);
300
+
301
+ // Speed control
302
+ int speedMultiplier_ = 1;
303
+
304
+ // Frame timing
305
+ uint64_t lastFrameCycle_ = 0;
306
+ bool frameReady_ = false;
307
+
308
+ // Audio-driven frame sync
309
+ static constexpr int SAMPLES_PER_FRAME = 800; // 48000 Hz / 60 Hz
310
+ int samplesGenerated_ = 0;
311
+
312
+ // Debugger state
313
+ std::set<uint16_t> breakpoints_;
314
+ std::set<uint16_t> disabledBreakpoints_;
315
+ bool breakpointHit_ = false;
316
+ uint16_t breakpointAddress_ = 0;
317
+ bool paused_ = false;
318
+ bool skipBreakpointOnce_ = false;
319
+
320
+ // BASIC breakpoints - supports whole-line and statement-level
321
+ struct BasicBreakpoint {
322
+ uint16_t lineNumber;
323
+ int8_t statementIndex; // -1 = whole line, 0+ = specific statement
324
+ bool operator<(const BasicBreakpoint& o) const {
325
+ if (lineNumber != o.lineNumber) return lineNumber < o.lineNumber;
326
+ return statementIndex < o.statementIndex;
327
+ }
328
+ };
329
+ std::set<BasicBreakpoint> basicBreakpoints_;
330
+ bool basicBreakpointHit_ = false;
331
+ struct BasicConditionRule {
332
+ int id;
333
+ std::string expression;
334
+ bool enabled;
335
+ };
336
+ std::vector<BasicConditionRule> basicConditionRules_;
337
+ int basicConditionRuleHitId_ = -1;
338
+ uint16_t basicBreakLine_ = 0;
339
+ uint16_t skipBasicBreakpointLine_ = 0xFFFF; // Line to skip (0xFFFF = none)
340
+ int8_t skipBasicBreakpointStmt_ = -1; // Statement to skip (-1 = whole line)
341
+
342
+ // BASIC program execution tracking - set by monitoring ROM entry points
343
+ // $D912 (RUN) sets true, $D43C (RESTART/] prompt) sets false
344
+ bool basicProgramRunning_ = false;
345
+
346
+ // BASIC runtime error tracking - captured at $D412 (ERROR handler entry)
347
+ bool basicErrorHit_ = false;
348
+ uint16_t basicErrorLine_ = 0; // CURLIN at error time
349
+ uint16_t basicErrorTxtptr_ = 0; // TXTPTR at error time
350
+ uint8_t basicErrorCode_ = 0; // X register (error message table offset)
351
+
352
+ // BASIC stepping
353
+ BasicStepMode basicStepMode_ = BasicStepMode::None;
354
+ uint16_t basicStepFromLine_ = 0xFFFF;
355
+ uint16_t basicStepFromTxtptr_ = 0;
356
+ int basicStepFromStmtIndex_ = 0;
357
+ uint16_t basicStepLineStart_ = 0; // Address where current line's tokens start
358
+ uint16_t basicStepNextColon_ = 0; // Address of next colon after starting position (0 if none)
359
+ bool basicStepSkipFirst_ = false; // Skip first EXECUTE_STATEMENT hit when already there
360
+
361
+ // BASIC line heat map - counts execution hits per line at $D820
362
+ bool basicHeatMapEnabled_ = false;
363
+ std::unordered_map<uint16_t, uint32_t> basicHeatMap_;
364
+
365
+ // Helper to count colons (statement separators) between lineStart and txtptr
366
+ int countColonsBetween(uint16_t lineStart, uint16_t txtptr);
367
+ // Helper to find the start address of tokenized text for a BASIC line
368
+ uint16_t findCurrentLineStart(uint16_t lineNumber);
369
+ // Helper to find the next colon address after a given position within a line
370
+ uint16_t findNextColonAfter(uint16_t lineStart, uint16_t afterPos);
371
+
372
+ // Temp breakpoint for step over / step out
373
+ uint16_t tempBreakpoint_ = 0;
374
+ bool tempBreakpointActive_ = false;
375
+ bool tempBreakpointHit_ = false;
376
+
377
+ // Watchpoints
378
+ struct Watchpoint {
379
+ uint16_t startAddr;
380
+ uint16_t endAddr;
381
+ WatchpointType type;
382
+ bool enabled;
383
+ };
384
+ std::vector<Watchpoint> watchpoints_;
385
+ bool watchpointsActive_ = false;
386
+ bool watchpointHit_ = false;
387
+ uint16_t watchpointAddress_ = 0;
388
+ uint8_t watchpointValue_ = 0;
389
+ bool watchpointIsWrite_ = false;
390
+
391
+ // Beam breakpoints
392
+ struct BeamBreakpoint {
393
+ int16_t scanline; // -1 = any
394
+ int16_t hPos; // -1 = any (raw 0-64)
395
+ bool enabled;
396
+ int32_t id;
397
+ uint64_t lastFireFrame; // per-breakpoint re-fire prevention
398
+ int16_t lastFireScanline; // for wildcard-scanline breakpoints (fire once per scanline)
399
+ };
400
+ std::vector<BeamBreakpoint> beamBreakpoints_;
401
+ int32_t beamBreakNextId_ = 1;
402
+ static constexpr size_t MAX_BEAM_BREAKPOINTS = 16;
403
+ bool beamBreakHit_ = false;
404
+ int32_t beamBreakHitId_ = -1;
405
+ int16_t beamBreakHitScanline_ = -1; // Scanline where break occurred (for display)
406
+ int16_t beamBreakHitHPos_ = -1; // hPos where break occurred (for display)
407
+
408
+ // Watchpoint callback for MMU
409
+ void onWatchpointRead(uint16_t address, uint8_t value);
410
+ void onWatchpointWrite(uint16_t address, uint8_t value);
411
+
412
+ // Trace log
413
+ std::vector<TraceEntry> traceBuffer_;
414
+ size_t traceHead_ = 0;
415
+ size_t traceCount_ = 0;
416
+ bool traceEnabled_ = false;
417
+
418
+ void recordTrace();
419
+
420
+ // Cycle profiling
421
+ bool profileEnabled_ = false;
422
+ std::array<uint32_t, 65536> profileCycles_{};
423
+
424
+ // Disassembly buffer
425
+ mutable std::string disasmBuffer_;
426
+
427
+ // Screen text extraction buffer
428
+ mutable std::string screenTextBuffer_;
429
+
430
+ // State serialization buffer
431
+ mutable std::vector<uint8_t> stateBuffer_;
432
+ };
433
+
434
+ } // namespace a2e
@@ -0,0 +1,178 @@
1
+ /*
2
+ * dos33.cpp - DOS 3.3 filesystem reader for disk image browsing
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #include "dos33.hpp"
9
+ #include <cstring>
10
+
11
+ namespace a2e {
12
+
13
+ int DOS33::getSectorOffset(int track, int sector) {
14
+ return (track * SECTORS_PER_TRACK + sector) * BYTES_PER_SECTOR;
15
+ }
16
+
17
+ const uint8_t* DOS33::readSector(const uint8_t* data, size_t size, int track, int sector) {
18
+ int offset = getSectorOffset(track, sector);
19
+ if (offset + BYTES_PER_SECTOR > static_cast<int>(size)) return nullptr;
20
+ return data + offset;
21
+ }
22
+
23
+ void DOS33::parseFilename(const uint8_t* bytes, char* out, int maxLen) {
24
+ // DOS 3.3 filenames are 30 bytes, high bit set, space-padded
25
+ int len = 0;
26
+ int lastNonSpace = -1;
27
+ for (int i = 0; i < 30 && len < maxLen - 1; i++) {
28
+ char c = bytes[i] & 0x7F; // Strip high bit
29
+ out[len] = c;
30
+ if (c != ' ') lastNonSpace = len;
31
+ len++;
32
+ }
33
+ // Trim trailing spaces
34
+ out[lastNonSpace + 1] = '\0';
35
+ }
36
+
37
+ const char* DOS33::getFileTypeName(uint8_t fileType) {
38
+ switch (fileType) {
39
+ case 0x00: return "T";
40
+ case 0x01: return "I";
41
+ case 0x02: return "A";
42
+ case 0x04: return "B";
43
+ case 0x08: return "S";
44
+ case 0x10: return "R";
45
+ case 0x20: return "a";
46
+ case 0x40: return "b";
47
+ default: return "?";
48
+ }
49
+ }
50
+
51
+ bool DOS33::isDOS33(const uint8_t* data, size_t size) {
52
+ if (size < static_cast<size_t>(DISK_SIZE)) return false;
53
+
54
+ const uint8_t* vtoc = readSector(data, size, 17, 0);
55
+ if (!vtoc) return false;
56
+
57
+ uint8_t catalogTrack = vtoc[0x01];
58
+ uint8_t dosVersion = vtoc[0x03];
59
+
60
+ return (catalogTrack == 0x11 && dosVersion == 0x03);
61
+ }
62
+
63
+ int DOS33::readCatalog(const uint8_t* data, size_t size,
64
+ DOS33CatalogEntry* entries, int maxEntries) {
65
+ if (size < static_cast<size_t>(DISK_SIZE)) return 0;
66
+
67
+ const uint8_t* vtoc = readSector(data, size, 17, 0);
68
+ if (!vtoc) return 0;
69
+
70
+ uint8_t catalogTrack = vtoc[0x01];
71
+ uint8_t dosVersion = vtoc[0x03];
72
+ if (catalogTrack != 0x11 || dosVersion != 0x03) return 0;
73
+
74
+ int count = 0;
75
+ int track = vtoc[0x01];
76
+ int sector = vtoc[0x02];
77
+
78
+ // Visited set for cycle detection (max 560 sectors on a 35-track disk)
79
+ bool visited[35 * 16] = {};
80
+
81
+ while (track != 0 && sector != 0 && count < maxEntries) {
82
+ int key = track * 16 + sector;
83
+ if (key < 0 || key >= 35 * 16 || visited[key]) break;
84
+ visited[key] = true;
85
+
86
+ const uint8_t* catSector = readSector(data, size, track, sector);
87
+ if (!catSector) break;
88
+
89
+ // Parse entries (7 entries per sector, starting at offset 0x0B)
90
+ for (int i = 0; i < 7 && count < maxEntries; i++) {
91
+ int entryOff = 0x0B + (i * 35);
92
+ uint8_t firstTrack = catSector[entryOff + 0x00];
93
+ uint8_t firstSector = catSector[entryOff + 0x01];
94
+
95
+ if (firstTrack == 0xFF || firstTrack == 0x00) continue;
96
+
97
+ uint8_t typeAndFlags = catSector[entryOff + 0x02];
98
+ uint8_t fileType = typeAndFlags & 0x7F;
99
+ bool isLocked = (typeAndFlags & 0x80) != 0;
100
+
101
+ DOS33CatalogEntry& entry = entries[count];
102
+ parseFilename(catSector + entryOff + 0x03, entry.filename, sizeof(entry.filename));
103
+ entry.fileType = fileType;
104
+ const char* typeName = getFileTypeName(fileType);
105
+ strncpy(entry.fileTypeName, typeName, sizeof(entry.fileTypeName) - 1);
106
+ entry.fileTypeName[sizeof(entry.fileTypeName) - 1] = '\0';
107
+ entry.isLocked = isLocked;
108
+ entry.sectorCount = catSector[entryOff + 0x21] | (catSector[entryOff + 0x22] << 8);
109
+ entry.firstTrack = firstTrack;
110
+ entry.firstSector = firstSector;
111
+
112
+ count++;
113
+ }
114
+
115
+ // Next catalog sector
116
+ track = catSector[0x01];
117
+ sector = catSector[0x02];
118
+ }
119
+
120
+ return count;
121
+ }
122
+
123
+ int DOS33::readFile(const uint8_t* data, size_t size,
124
+ uint8_t firstTrack, uint8_t firstSector,
125
+ uint8_t* outBuf, int outMax) {
126
+ if (size < static_cast<size_t>(DISK_SIZE)) return 0;
127
+
128
+ int bytesWritten = 0;
129
+ int track = firstTrack;
130
+ int sector = firstSector;
131
+
132
+ bool visited[35 * 16] = {};
133
+
134
+ // Follow T/S list chain
135
+ while (track != 0) {
136
+ int key = track * 16 + sector;
137
+ if (key < 0 || key >= 35 * 16 || visited[key]) break;
138
+ visited[key] = true;
139
+
140
+ const uint8_t* tsList = readSector(data, size, track, sector);
141
+ if (!tsList) break;
142
+
143
+ // Read sector pairs from T/S list (starting at offset 0x0C)
144
+ for (int i = 0x0C; i < 0x100; i += 2) {
145
+ int t = tsList[i];
146
+ int s = tsList[i + 1];
147
+ if (t == 0 && s == 0) break;
148
+
149
+ const uint8_t* sectorData = readSector(data, size, t, s);
150
+ if (!sectorData) continue;
151
+
152
+ int bytesToCopy = BYTES_PER_SECTOR;
153
+ if (bytesWritten + bytesToCopy > outMax) {
154
+ bytesToCopy = outMax - bytesWritten;
155
+ }
156
+ if (bytesToCopy > 0) {
157
+ memcpy(outBuf + bytesWritten, sectorData, bytesToCopy);
158
+ bytesWritten += bytesToCopy;
159
+ }
160
+ }
161
+
162
+ // Next T/S list sector
163
+ track = tsList[0x01];
164
+ sector = tsList[0x02];
165
+ }
166
+
167
+ return bytesWritten;
168
+ }
169
+
170
+ bool DOS33::getBinaryFileInfo(const uint8_t* fileData, size_t size,
171
+ uint16_t* address, uint16_t* length) {
172
+ if (size < 4) return false;
173
+ *address = fileData[0] | (fileData[1] << 8);
174
+ *length = fileData[2] | (fileData[3] << 8);
175
+ return true;
176
+ }
177
+
178
+ } // namespace a2e
@@ -0,0 +1,66 @@
1
+ /*
2
+ * dos33.hpp - DOS 3.3 filesystem reader for disk image browsing
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <cstdint>
11
+ #include <cstddef>
12
+
13
+ namespace a2e {
14
+
15
+ struct DOS33CatalogEntry {
16
+ char filename[31]; // Null-terminated filename (max 30 chars)
17
+ uint8_t fileType; // Raw file type byte (0x00=T, 0x01=I, 0x02=A, 0x04=B, etc.)
18
+ char fileTypeName[4]; // Short type name ("T", "I", "A", "B", etc.)
19
+ bool isLocked;
20
+ uint16_t sectorCount;
21
+ uint8_t firstTrack;
22
+ uint8_t firstSector;
23
+ };
24
+
25
+ class DOS33 {
26
+ public:
27
+ static constexpr int TRACKS = 35;
28
+ static constexpr int SECTORS_PER_TRACK = 16;
29
+ static constexpr int BYTES_PER_SECTOR = 256;
30
+ static constexpr int DISK_SIZE = TRACKS * SECTORS_PER_TRACK * BYTES_PER_SECTOR; // 143360
31
+
32
+ /**
33
+ * Check if disk data is DOS 3.3 format
34
+ */
35
+ static bool isDOS33(const uint8_t* data, size_t size);
36
+
37
+ /**
38
+ * Read the catalog from a DOS 3.3 disk image.
39
+ * Returns number of entries written to entries array.
40
+ */
41
+ static int readCatalog(const uint8_t* data, size_t size,
42
+ DOS33CatalogEntry* entries, int maxEntries);
43
+
44
+ /**
45
+ * Read a file's raw data (all sectors concatenated).
46
+ * outBuf must be large enough. Returns number of bytes written.
47
+ */
48
+ static int readFile(const uint8_t* data, size_t size,
49
+ uint8_t firstTrack, uint8_t firstSector,
50
+ uint8_t* outBuf, int outMax);
51
+
52
+ /**
53
+ * Get binary file header info (load address and length).
54
+ * Returns false if fileData is too small.
55
+ */
56
+ static bool getBinaryFileInfo(const uint8_t* fileData, size_t size,
57
+ uint16_t* address, uint16_t* length);
58
+
59
+ private:
60
+ static int getSectorOffset(int track, int sector);
61
+ static const uint8_t* readSector(const uint8_t* data, size_t size, int track, int sector);
62
+ static void parseFilename(const uint8_t* bytes, char* out, int maxLen);
63
+ static const char* getFileTypeName(uint8_t fileType);
64
+ };
65
+
66
+ } // namespace a2e