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,203 @@
1
+ /*
2
+ * cpu6502.hpp - Cycle-accurate 65C02 CPU emulation
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <array>
11
+ #include <cstdint>
12
+ #include <functional>
13
+
14
+ namespace a2e {
15
+
16
+ // CPU variant types
17
+ enum class CPUVariant { NMOS_6502, CMOS_65C02 };
18
+
19
+ // Status flag bits
20
+ enum StatusFlag : uint8_t {
21
+ FLAG_C = 0x01, // Carry
22
+ FLAG_Z = 0x02, // Zero
23
+ FLAG_I = 0x04, // Interrupt disable
24
+ FLAG_D = 0x08, // Decimal mode
25
+ FLAG_B = 0x10, // Break
26
+ FLAG_U = 0x20, // Unused (always 1)
27
+ FLAG_V = 0x40, // Overflow
28
+ FLAG_N = 0x80 // Negative
29
+ };
30
+
31
+ class CPU6502 {
32
+ public:
33
+ using ReadCallback = std::function<uint8_t(uint16_t)>;
34
+ using WriteCallback = std::function<void(uint16_t, uint8_t)>;
35
+ using IRQStatusCallback = std::function<bool()>;
36
+
37
+ CPU6502(ReadCallback read, WriteCallback write,
38
+ CPUVariant variant = CPUVariant::CMOS_65C02);
39
+
40
+ // Execution
41
+ void reset();
42
+ void executeInstruction();
43
+ void step(); // Single cycle step
44
+ bool isInstructionComplete() const { return cycleCount_ == 0; }
45
+
46
+ // Interrupts
47
+ void irq();
48
+ void nmi();
49
+
50
+ // Set callback for polling IRQ status (level-triggered IRQs like VIA)
51
+ void setIRQStatusCallback(IRQStatusCallback cb) { irqStatusCallback_ = std::move(cb); }
52
+
53
+ // Register access
54
+ uint8_t getA() const { return a_; }
55
+ uint8_t getX() const { return x_; }
56
+ uint8_t getY() const { return y_; }
57
+ uint8_t getSP() const { return sp_; }
58
+ uint16_t getPC() const { return pc_; }
59
+ uint8_t getP() const { return p_; }
60
+
61
+ void setA(uint8_t v) { a_ = v; }
62
+ void setX(uint8_t v) { x_ = v; }
63
+ void setY(uint8_t v) { y_ = v; }
64
+ void setSP(uint8_t v) { sp_ = v; }
65
+ void setPC(uint16_t v) { pc_ = v; }
66
+ void setP(uint8_t v) { p_ = v; }
67
+
68
+ // Flag access
69
+ bool getFlag(StatusFlag flag) const { return (p_ & flag) != 0; }
70
+ void setFlag(StatusFlag flag, bool value) {
71
+ if (value)
72
+ p_ |= flag;
73
+ else
74
+ p_ &= ~flag;
75
+ }
76
+
77
+ // Cycle counting
78
+ // During instruction execution (cycleCount_ > 0), return the cycle of
79
+ // the last bus access — this matches when the 6502 performs the effective
80
+ // memory read/write that triggers soft switch callbacks.
81
+ // Between instructions (cycleCount_ == 0), return the plain total.
82
+ uint64_t getTotalCycles() const {
83
+ return cycleCount_ > 0 ? totalCycles_ + cycleCount_ - 1 : totalCycles_;
84
+ }
85
+ void resetCycleCount() { totalCycles_ = 0; }
86
+ void setTotalCycles(uint64_t cycles) { totalCycles_ = cycles; }
87
+
88
+ // Interrupt state access
89
+ bool isIRQPending() const { return irqPending_; }
90
+ bool isNMIPending() const { return nmiPending_; }
91
+ bool isNMIEdge() const { return nmiEdge_; }
92
+
93
+ // Debugging
94
+ std::string disassembleAt(uint16_t address) const;
95
+ uint8_t peekMemory(uint16_t address) const { return read_(address); }
96
+
97
+ private:
98
+ // Memory access
99
+ uint8_t read(uint16_t address);
100
+ void write(uint16_t address, uint8_t value);
101
+ uint8_t fetch();
102
+ uint16_t fetchWord();
103
+
104
+ // Stack operations
105
+ void push(uint8_t value);
106
+ uint8_t pop();
107
+ void pushWord(uint16_t value);
108
+ uint16_t popWord();
109
+
110
+ // Flag helpers
111
+ void updateNZ(uint8_t value);
112
+ void compare(uint8_t reg, uint8_t value);
113
+
114
+ // Addressing modes - return effective address
115
+ uint16_t addrImmediate();
116
+ uint16_t addrZeroPage();
117
+ uint16_t addrZeroPageX();
118
+ uint16_t addrZeroPageY();
119
+ uint16_t addrAbsolute();
120
+ uint16_t addrAbsoluteX(bool checkPage = true);
121
+ uint16_t addrAbsoluteY(bool checkPage = true);
122
+ uint16_t addrIndirect();
123
+ uint16_t addrIndexedIndirect(); // (zp,X)
124
+ uint16_t addrIndirectIndexed(bool checkPage = true); // (zp),Y
125
+ uint16_t addrIndirectZP(); // 65C02: (zp)
126
+
127
+ // Instruction implementations
128
+ void opADC(uint8_t value);
129
+ void opSBC(uint8_t value);
130
+ void opAND(uint8_t value);
131
+ void opORA(uint8_t value);
132
+ void opEOR(uint8_t value);
133
+ void opCMP(uint8_t value);
134
+ void opCPX(uint8_t value);
135
+ void opCPY(uint8_t value);
136
+ void opBIT(uint8_t value);
137
+ void opASL_A();
138
+ uint8_t opASL(uint8_t value);
139
+ void opLSR_A();
140
+ uint8_t opLSR(uint8_t value);
141
+ void opROL_A();
142
+ uint8_t opROL(uint8_t value);
143
+ void opROR_A();
144
+ uint8_t opROR(uint8_t value);
145
+ uint8_t opINC(uint8_t value);
146
+ uint8_t opDEC(uint8_t value);
147
+
148
+ // Branch helper
149
+ void branch(bool condition);
150
+
151
+ // Execute the current opcode
152
+ void executeOpcode(uint8_t opcode);
153
+
154
+ // Registers
155
+ uint8_t a_ = 0; // Accumulator
156
+ uint8_t x_ = 0; // X index
157
+ uint8_t y_ = 0; // Y index
158
+ uint8_t sp_ = 0xFF; // Stack pointer
159
+ uint16_t pc_ = 0; // Program counter
160
+ uint8_t p_ = 0x24; // Status register (U always set, I set on reset)
161
+
162
+ // Cycle tracking
163
+ int cycleCount_ = 0;
164
+ uint64_t totalCycles_ = 0;
165
+ bool pageCrossed_ = false;
166
+
167
+ // Interrupt state
168
+ bool irqPending_ = false;
169
+ bool nmiPending_ = false;
170
+ bool nmiEdge_ = false;
171
+
172
+ // Memory callbacks
173
+ ReadCallback read_;
174
+ WriteCallback write_;
175
+
176
+ // IRQ status callback for level-triggered IRQs (VIA)
177
+ IRQStatusCallback irqStatusCallback_;
178
+
179
+ // CPU variant
180
+ CPUVariant variant_;
181
+
182
+ // Opcode table for cycle counts
183
+ static constexpr std::array<uint8_t, 256> CYCLE_TABLE = {{
184
+ 7, 6, 2, 2, 5, 3, 5, 5, 3, 2, 2, 2, 6, 4, 6, 5, // 00-0F
185
+ 2, 5, 5, 2, 5, 4, 6, 5, 2, 4, 2, 2, 6, 4, 6, 5, // 10-1F
186
+ 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 5, // 20-2F
187
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 2, 2, 4, 4, 6, 5, // 30-3F
188
+ 6, 6, 2, 2, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 5, // 40-4F
189
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 8, 4, 6, 5, // 50-5F
190
+ 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 5, // 60-6F
191
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 6, 4, 6, 5, // 70-7F
192
+ 3, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5, // 80-8F
193
+ 2, 6, 5, 2, 4, 4, 4, 5, 2, 5, 2, 2, 4, 5, 5, 5, // 90-9F
194
+ 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5, // A0-AF
195
+ 2, 5, 5, 2, 4, 4, 4, 5, 2, 4, 2, 2, 4, 4, 4, 5, // B0-BF
196
+ 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 5, // C0-CF
197
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 3, 4, 4, 7, 5, // D0-DF
198
+ 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 5, // E0-EF
199
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 4, 4, 7, 5 // F0-FF
200
+ }};
201
+ };
202
+
203
+ } // namespace a2e
@@ -0,0 +1,470 @@
1
+ /*
2
+ * condition_evaluator.cpp - Expression parser for debugger breakpoint conditions and watches
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #include "condition_evaluator.hpp"
9
+ #include "../emulator.hpp"
10
+ #include <cctype>
11
+ #include <cmath>
12
+ #include <cstdio>
13
+
14
+ namespace a2e {
15
+
16
+ // Decode 5-byte Applesoft float to int32_t (truncated)
17
+ static int32_t decodeApplesoftFloat(const Emulator& emu, uint16_t addr) {
18
+ uint8_t exp = emu.peekMemory(addr);
19
+ if (exp == 0) return 0;
20
+
21
+ int sign = (emu.peekMemory(addr + 1) & 0x80) ? -1 : 1;
22
+ double mantissa = 1.0;
23
+ mantissa += (emu.peekMemory(addr + 1) & 0x7F) / 128.0;
24
+ mantissa += emu.peekMemory(addr + 2) / 32768.0;
25
+ mantissa += emu.peekMemory(addr + 3) / 8388608.0;
26
+ mantissa += emu.peekMemory(addr + 4) / 2147483648.0;
27
+
28
+ int actualExp = exp - 129;
29
+ double value = sign * mantissa * pow(2.0, actualExp);
30
+ return static_cast<int32_t>(value);
31
+ }
32
+
33
+ // Read a BASIC simple variable value by its encoded name bytes
34
+ // Walks VARTAB to ARYTAB looking for matching name
35
+ static int32_t readBasicVariable(const Emulator& emu, uint8_t nameB1, uint8_t nameB2) {
36
+ uint16_t vartab = emu.peekMemory(0x69) | (emu.peekMemory(0x6A) << 8);
37
+ uint16_t arytab = emu.peekMemory(0x6B) | (emu.peekMemory(0x6C) << 8);
38
+
39
+ if (vartab == 0 || arytab == 0 || vartab >= arytab) return 0;
40
+
41
+ uint16_t addr = vartab;
42
+ while (addr < arytab) {
43
+ uint8_t b1 = emu.peekMemory(addr);
44
+ uint8_t b2 = emu.peekMemory(addr + 1);
45
+
46
+ if (b1 == nameB1 && b2 == nameB2) {
47
+ // Found it - determine type from high bits
48
+ bool isInteger = (b1 & 0x80) && (b2 & 0x80);
49
+ if (isInteger) {
50
+ uint8_t high = emu.peekMemory(addr + 2);
51
+ uint8_t low = emu.peekMemory(addr + 3);
52
+ int16_t val = static_cast<int16_t>((high << 8) | low);
53
+ return static_cast<int32_t>(val);
54
+ }
55
+ // Real number (5-byte float)
56
+ return decodeApplesoftFloat(emu, addr + 2);
57
+ }
58
+ addr += 7; // Each simple var is 7 bytes
59
+ }
60
+ return 0; // Variable not found
61
+ }
62
+
63
+ // Read a BASIC array element by encoded name bytes and flat index
64
+ static int32_t readBasicArrayElement(const Emulator& emu, uint8_t nameB1, uint8_t nameB2, int32_t flatIndex) {
65
+ uint16_t arytab = emu.peekMemory(0x6B) | (emu.peekMemory(0x6C) << 8);
66
+ uint16_t strend = emu.peekMemory(0x6D) | (emu.peekMemory(0x6E) << 8);
67
+
68
+ if (arytab == 0 || strend == 0 || arytab >= strend) return 0;
69
+
70
+ uint16_t addr = arytab;
71
+ while (addr < strend) {
72
+ uint8_t b1 = emu.peekMemory(addr);
73
+ uint8_t b2 = emu.peekMemory(addr + 1);
74
+ uint16_t totalSize = emu.peekMemory(addr + 2) | (emu.peekMemory(addr + 3) << 8);
75
+
76
+ if (b1 == nameB1 && b2 == nameB2) {
77
+ uint8_t numDims = emu.peekMemory(addr + 4);
78
+ uint16_t dataStart = addr + 5 + numDims * 2;
79
+
80
+ bool isInteger = (b1 & 0x80) && (b2 & 0x80);
81
+ int elementSize = isInteger ? 2 : 5;
82
+
83
+ uint16_t elemAddr = dataStart + flatIndex * elementSize;
84
+ if (isInteger) {
85
+ uint8_t high = emu.peekMemory(elemAddr);
86
+ uint8_t low = emu.peekMemory(elemAddr + 1);
87
+ int16_t val = static_cast<int16_t>((high << 8) | low);
88
+ return static_cast<int32_t>(val);
89
+ }
90
+ return decodeApplesoftFloat(emu, elemAddr);
91
+ }
92
+ addr += totalSize;
93
+ }
94
+ return 0;
95
+ }
96
+
97
+ // Read a BASIC 2D array element by encoded name bytes and two indices
98
+ // Applesoft stores dimensions in reverse order: DIM A(M,N) stores [N+1, M+1]
99
+ // Element A(i,j) flat index = i * dim2size + j (dim2size is the FIRST stored dimension)
100
+ static int32_t readBasicArrayElement2D(const Emulator& emu, uint8_t nameB1, uint8_t nameB2, int32_t idx1, int32_t idx2) {
101
+ uint16_t arytab = emu.peekMemory(0x6B) | (emu.peekMemory(0x6C) << 8);
102
+ uint16_t strend = emu.peekMemory(0x6D) | (emu.peekMemory(0x6E) << 8);
103
+
104
+ if (arytab == 0 || strend == 0 || arytab >= strend) return 0;
105
+
106
+ uint16_t addr = arytab;
107
+ while (addr < strend) {
108
+ uint8_t b1 = emu.peekMemory(addr);
109
+ uint8_t b2 = emu.peekMemory(addr + 1);
110
+ uint16_t totalSize = emu.peekMemory(addr + 2) | (emu.peekMemory(addr + 3) << 8);
111
+
112
+ if (b1 == nameB1 && b2 == nameB2) {
113
+ uint8_t numDims = emu.peekMemory(addr + 4);
114
+ if (numDims < 2) return 0;
115
+
116
+ // Applesoft stores arrays in column-major order with reversed dimensions.
117
+ // For DIM A(M,N): dims[0]=N+1, dims[1]=M+1
118
+ // Flat index for A(i,j) = j * dims[1] + i
119
+ uint16_t dim1Size = (emu.peekMemory(addr + 7) << 8) | emu.peekMemory(addr + 8);
120
+
121
+ int32_t flatIndex = idx2 * dim1Size + idx1;
122
+ uint16_t dataStart = addr + 5 + numDims * 2;
123
+
124
+ bool isInteger = (b1 & 0x80) && (b2 & 0x80);
125
+ int elementSize = isInteger ? 2 : 5;
126
+
127
+ uint16_t elemAddr = dataStart + flatIndex * elementSize;
128
+ if (isInteger) {
129
+ uint8_t high = emu.peekMemory(elemAddr);
130
+ uint8_t low = emu.peekMemory(elemAddr + 1);
131
+ int16_t val = static_cast<int16_t>((high << 8) | low);
132
+ return static_cast<int32_t>(val);
133
+ }
134
+ return decodeApplesoftFloat(emu, elemAddr);
135
+ }
136
+ addr += totalSize;
137
+ }
138
+ return 0;
139
+ }
140
+
141
+ char ConditionEvaluator::errorBuf_[128] = "";
142
+
143
+ int ConditionEvaluator::tokenize(const char* expr, Token* tokens, int maxTokens) {
144
+ int count = 0;
145
+ int i = 0;
146
+ int len = static_cast<int>(strlen(expr));
147
+
148
+ while (i < len && count < maxTokens - 1) {
149
+ // Skip whitespace
150
+ if (expr[i] == ' ' || expr[i] == '\t') {
151
+ i++;
152
+ continue;
153
+ }
154
+
155
+ // Two-char operators
156
+ if (i + 1 < len) {
157
+ char c0 = expr[i], c1 = expr[i + 1];
158
+ if ((c0 == '=' && c1 == '=') || (c0 == '!' && c1 == '=') ||
159
+ (c0 == '>' && c1 == '=') || (c0 == '<' && c1 == '=') ||
160
+ (c0 == '&' && c1 == '&') || (c0 == '|' && c1 == '|')) {
161
+ tokens[count].type = TOK_OP2;
162
+ tokens[count].strVal[0] = c0;
163
+ tokens[count].strVal[1] = c1;
164
+ tokens[count].strVal[2] = '\0';
165
+ count++;
166
+ i += 2;
167
+ continue;
168
+ }
169
+ }
170
+
171
+ // Single-char operators (including comma for function args)
172
+ char ch = expr[i];
173
+ if (ch == '<' || ch == '>' || ch == '(' || ch == ')' ||
174
+ ch == '+' || ch == '-' || ch == '*' || ch == ',') {
175
+ tokens[count].type = TOK_OP1;
176
+ tokens[count].strVal[0] = ch;
177
+ tokens[count].strVal[1] = '\0';
178
+ count++;
179
+ i++;
180
+ continue;
181
+ }
182
+
183
+ // Hex literal: #$XX or $XXXX
184
+ if (ch == '#' && i + 1 < len && expr[i + 1] == '$') {
185
+ i += 2;
186
+ int32_t val = 0;
187
+ while (i < len && isxdigit(static_cast<unsigned char>(expr[i]))) {
188
+ int digit;
189
+ char c = expr[i];
190
+ if (c >= '0' && c <= '9') digit = c - '0';
191
+ else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
192
+ else digit = c - 'A' + 10;
193
+ val = (val << 4) | digit;
194
+ i++;
195
+ }
196
+ tokens[count].type = TOK_NUM;
197
+ tokens[count].numVal = val;
198
+ count++;
199
+ continue;
200
+ }
201
+
202
+ if (ch == '$') {
203
+ i++;
204
+ int32_t val = 0;
205
+ while (i < len && isxdigit(static_cast<unsigned char>(expr[i]))) {
206
+ int digit;
207
+ char c = expr[i];
208
+ if (c >= '0' && c <= '9') digit = c - '0';
209
+ else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
210
+ else digit = c - 'A' + 10;
211
+ val = (val << 4) | digit;
212
+ i++;
213
+ }
214
+ tokens[count].type = TOK_NUM;
215
+ tokens[count].numVal = val;
216
+ count++;
217
+ continue;
218
+ }
219
+
220
+ // Decimal literal
221
+ if (isdigit(static_cast<unsigned char>(ch))) {
222
+ int32_t val = 0;
223
+ while (i < len && isdigit(static_cast<unsigned char>(expr[i]))) {
224
+ val = val * 10 + (expr[i] - '0');
225
+ i++;
226
+ }
227
+ tokens[count].type = TOK_NUM;
228
+ tokens[count].numVal = val;
229
+ count++;
230
+ continue;
231
+ }
232
+
233
+ // Identifiers
234
+ if (isalpha(static_cast<unsigned char>(ch)) || ch == '_') {
235
+ int start = i;
236
+ while (i < len && (isalnum(static_cast<unsigned char>(expr[i])) || expr[i] == '_')) {
237
+ i++;
238
+ }
239
+ int idLen = i - start;
240
+ if (idLen > 7) idLen = 7; // Truncate to fit strVal
241
+ tokens[count].type = TOK_ID;
242
+ for (int j = 0; j < idLen; j++) {
243
+ tokens[count].strVal[j] = toupper(static_cast<unsigned char>(expr[start + j]));
244
+ }
245
+ tokens[count].strVal[idLen] = '\0';
246
+ count++;
247
+ continue;
248
+ }
249
+
250
+ // Unknown char, skip
251
+ i++;
252
+ }
253
+
254
+ // End sentinel
255
+ tokens[count].type = TOK_END;
256
+ return count;
257
+ }
258
+
259
+ bool ConditionEvaluator::evaluate(const char* expr, const Emulator& emu) {
260
+ errorBuf_[0] = '\0';
261
+ ParseState s;
262
+ s.count = tokenize(expr, s.tokens, MAX_TOKENS);
263
+ s.pos = 0;
264
+ s.emu = &emu;
265
+
266
+ bool result = parseOr(s);
267
+ return result;
268
+ }
269
+
270
+ int32_t ConditionEvaluator::evaluateNumeric(const char* expr, const Emulator& emu) {
271
+ errorBuf_[0] = '\0';
272
+ ParseState s;
273
+ s.count = tokenize(expr, s.tokens, MAX_TOKENS);
274
+ s.pos = 0;
275
+ s.emu = &emu;
276
+
277
+ return parseExpr(s);
278
+ }
279
+
280
+ const char* ConditionEvaluator::getLastError() {
281
+ return errorBuf_;
282
+ }
283
+
284
+ bool ConditionEvaluator::parseOr(ParseState& s) {
285
+ bool left = parseAnd(s);
286
+ while (s.pos < s.count && s.tokens[s.pos].type == TOK_OP2 &&
287
+ s.tokens[s.pos].strVal[0] == '|') {
288
+ s.pos++;
289
+ bool right = parseAnd(s);
290
+ left = left || right;
291
+ }
292
+ return left;
293
+ }
294
+
295
+ bool ConditionEvaluator::parseAnd(ParseState& s) {
296
+ bool left = parseComparison(s);
297
+ while (s.pos < s.count && s.tokens[s.pos].type == TOK_OP2 &&
298
+ s.tokens[s.pos].strVal[0] == '&') {
299
+ s.pos++;
300
+ bool right = parseComparison(s);
301
+ left = left && right;
302
+ }
303
+ return left;
304
+ }
305
+
306
+ bool ConditionEvaluator::parseComparison(ParseState& s) {
307
+ int32_t left = parseExpr(s);
308
+
309
+ if (s.pos < s.count) {
310
+ const Token& tok = s.tokens[s.pos];
311
+ if (tok.type == TOK_OP2 || tok.type == TOK_OP1) {
312
+ const char* op = tok.strVal;
313
+ if (strcmp(op, "==") == 0) { s.pos++; return left == parseExpr(s); }
314
+ if (strcmp(op, "!=") == 0) { s.pos++; return left != parseExpr(s); }
315
+ if (strcmp(op, ">=") == 0) { s.pos++; return left >= parseExpr(s); }
316
+ if (strcmp(op, "<=") == 0) { s.pos++; return left <= parseExpr(s); }
317
+ if (strcmp(op, ">") == 0) { s.pos++; return left > parseExpr(s); }
318
+ if (strcmp(op, "<") == 0) { s.pos++; return left < parseExpr(s); }
319
+ }
320
+ }
321
+
322
+ return left != 0; // Truthy if no comparison
323
+ }
324
+
325
+ int32_t ConditionEvaluator::parseExpr(ParseState& s) {
326
+ int32_t val = parseAtom(s);
327
+
328
+ while (s.pos < s.count) {
329
+ const Token& tok = s.tokens[s.pos];
330
+ if (tok.type == TOK_OP1) {
331
+ char op = tok.strVal[0];
332
+ if (op == '+') { s.pos++; val += parseAtom(s); }
333
+ else if (op == '-') { s.pos++; val -= parseAtom(s); }
334
+ else if (op == '*') { s.pos++; val *= parseAtom(s); }
335
+ else break;
336
+ } else break;
337
+ }
338
+
339
+ return val;
340
+ }
341
+
342
+ int32_t ConditionEvaluator::parseAtom(ParseState& s) {
343
+ if (s.pos >= s.count) return 0;
344
+
345
+ const Token& t = s.tokens[s.pos];
346
+
347
+ // Number literal
348
+ if (t.type == TOK_NUM) {
349
+ s.pos++;
350
+ return t.numVal;
351
+ }
352
+
353
+ // Parenthesized expression
354
+ if (t.type == TOK_OP1 && t.strVal[0] == '(') {
355
+ s.pos++;
356
+ int32_t val = static_cast<int32_t>(parseOr(s));
357
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
358
+ s.tokens[s.pos].strVal[0] == ')') {
359
+ s.pos++;
360
+ }
361
+ return val;
362
+ }
363
+
364
+ // Identifier
365
+ if (t.type == TOK_ID) {
366
+ s.pos++;
367
+ const char* id = t.strVal;
368
+ const Emulator& emu = *s.emu;
369
+
370
+ // BV(b1,b2) - read BASIC simple variable value by encoded name bytes
371
+ if (strcmp(id, "BV") == 0 && s.pos < s.count &&
372
+ s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
373
+ s.pos++; // skip '('
374
+ int32_t b1 = parseExpr(s);
375
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
376
+ int32_t b2 = parseExpr(s);
377
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
378
+ s.tokens[s.pos].strVal[0] == ')') {
379
+ s.pos++;
380
+ }
381
+ return readBasicVariable(emu, static_cast<uint8_t>(b1), static_cast<uint8_t>(b2));
382
+ }
383
+
384
+ // BA(b1,b2,idx) - read BASIC array element by encoded name bytes and flat index
385
+ if (strcmp(id, "BA") == 0 && s.pos < s.count &&
386
+ s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
387
+ s.pos++; // skip '('
388
+ int32_t b1 = parseExpr(s);
389
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
390
+ int32_t b2 = parseExpr(s);
391
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
392
+ int32_t idx = parseExpr(s);
393
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
394
+ s.tokens[s.pos].strVal[0] == ')') {
395
+ s.pos++;
396
+ }
397
+ return readBasicArrayElement(emu, static_cast<uint8_t>(b1), static_cast<uint8_t>(b2), idx);
398
+ }
399
+
400
+ // BA2(b1,b2,i1,i2) - read BASIC 2D array element
401
+ if (strcmp(id, "BA2") == 0 && s.pos < s.count &&
402
+ s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
403
+ s.pos++; // skip '('
404
+ int32_t b1 = parseExpr(s);
405
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
406
+ int32_t b2 = parseExpr(s);
407
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
408
+ int32_t i1 = parseExpr(s);
409
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
410
+ int32_t i2 = parseExpr(s);
411
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
412
+ s.tokens[s.pos].strVal[0] == ')') {
413
+ s.pos++;
414
+ }
415
+ return readBasicArrayElement2D(emu, static_cast<uint8_t>(b1), static_cast<uint8_t>(b2), i1, i2);
416
+ }
417
+
418
+ // PEEK(addr) - read byte
419
+ if (strcmp(id, "PEEK") == 0 && s.pos < s.count &&
420
+ s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
421
+ s.pos++;
422
+ int32_t addr = parseExpr(s);
423
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
424
+ s.tokens[s.pos].strVal[0] == ')') {
425
+ s.pos++;
426
+ }
427
+ return emu.peekMemory(static_cast<uint16_t>(addr & 0xFFFF));
428
+ }
429
+
430
+ // DEEK(addr) - read 16-bit word (little-endian)
431
+ if (strcmp(id, "DEEK") == 0 && s.pos < s.count &&
432
+ s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
433
+ s.pos++;
434
+ int32_t addr = parseExpr(s);
435
+ if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
436
+ s.tokens[s.pos].strVal[0] == ')') {
437
+ s.pos++;
438
+ }
439
+ uint8_t lo = emu.peekMemory(static_cast<uint16_t>(addr & 0xFFFF));
440
+ uint8_t hi = emu.peekMemory(static_cast<uint16_t>((addr + 1) & 0xFFFF));
441
+ return (hi << 8) | lo;
442
+ }
443
+
444
+ // Registers
445
+ if (strcmp(id, "A") == 0) return emu.getA();
446
+ if (strcmp(id, "X") == 0) return emu.getX();
447
+ if (strcmp(id, "Y") == 0) return emu.getY();
448
+ if (strcmp(id, "SP") == 0) return emu.getSP();
449
+ if (strcmp(id, "PC") == 0) return emu.getPC();
450
+ if (strcmp(id, "P") == 0) return emu.getP();
451
+
452
+ // Individual flags
453
+ if (strcmp(id, "C") == 0) return (emu.getP() & 0x01) ? 1 : 0;
454
+ if (strcmp(id, "Z") == 0) return (emu.getP() & 0x02) ? 1 : 0;
455
+ if (strcmp(id, "I") == 0) return (emu.getP() & 0x04) ? 1 : 0;
456
+ if (strcmp(id, "D") == 0) return (emu.getP() & 0x08) ? 1 : 0;
457
+ if (strcmp(id, "B") == 0) return (emu.getP() & 0x10) ? 1 : 0;
458
+ if (strcmp(id, "V") == 0) return (emu.getP() & 0x40) ? 1 : 0;
459
+ if (strcmp(id, "N") == 0) return (emu.getP() & 0x80) ? 1 : 0;
460
+
461
+ snprintf(errorBuf_, sizeof(errorBuf_), "Unknown identifier: %s", id);
462
+ return 0;
463
+ }
464
+
465
+ // Fallback
466
+ s.pos++;
467
+ return 0;
468
+ }
469
+
470
+ } // namespace a2e