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,87 @@
1
+ /*
2
+ * condition_evaluator.hpp - Expression parser for debugger breakpoint conditions and watches
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <cstdint>
11
+ #include <cstring>
12
+
13
+ namespace a2e {
14
+
15
+ class Emulator;
16
+
17
+ /**
18
+ * Evaluates breakpoint condition expressions.
19
+ *
20
+ * Syntax:
21
+ * Registers: A, X, Y, SP, PC, P
22
+ * Flags: C, Z, I, D, B, V, N (return 0 or 1)
23
+ * Memory: PEEK($addr), DEEK($addr) (16-bit little-endian)
24
+ * Hex literals: #$FF, $FFFF
25
+ * Decimal literals: 42
26
+ * BASIC vars: BV(b1,b2) - simple variable, BA(b1,b2,idx) - array element
27
+ * b1,b2 are the encoded name bytes; type bits in high bits
28
+ * Comparisons: ==, !=, >=, <=, >, <
29
+ * Arithmetic: +, -, *
30
+ * Logic: &&, ||
31
+ * Grouping: ( )
32
+ */
33
+ class ConditionEvaluator {
34
+ public:
35
+ /**
36
+ * Evaluate a condition expression as a boolean.
37
+ * Returns true if the condition is satisfied.
38
+ */
39
+ static bool evaluate(const char* expr, const Emulator& emu);
40
+
41
+ /**
42
+ * Evaluate an expression and return the raw numeric value.
43
+ * Used for watch expressions.
44
+ */
45
+ static int32_t evaluateNumeric(const char* expr, const Emulator& emu);
46
+
47
+ /**
48
+ * Get the last error message (empty string if no error).
49
+ */
50
+ static const char* getLastError();
51
+
52
+ private:
53
+ // Token types
54
+ enum TokenType : uint8_t {
55
+ TOK_NUM, // Numeric literal
56
+ TOK_ID, // Identifier (register, PEEK, etc.)
57
+ TOK_OP2, // Two-character operator (==, !=, >=, <=, &&, ||)
58
+ TOK_OP1, // Single-character operator (<, >, +, -, *, (, ))
59
+ TOK_END // End of tokens
60
+ };
61
+
62
+ struct Token {
63
+ TokenType type;
64
+ int32_t numVal; // For TOK_NUM
65
+ char strVal[8]; // For TOK_ID or TOK_OP (null-terminated)
66
+ };
67
+
68
+ static constexpr int MAX_TOKENS = 128;
69
+
70
+ struct ParseState {
71
+ Token tokens[MAX_TOKENS];
72
+ int count;
73
+ int pos;
74
+ const Emulator* emu;
75
+ };
76
+
77
+ static int tokenize(const char* expr, Token* tokens, int maxTokens);
78
+ static bool parseOr(ParseState& s);
79
+ static bool parseAnd(ParseState& s);
80
+ static bool parseComparison(ParseState& s);
81
+ static int32_t parseExpr(ParseState& s);
82
+ static int32_t parseAtom(ParseState& s);
83
+
84
+ static char errorBuf_[128];
85
+ };
86
+
87
+ } // namespace a2e
@@ -0,0 +1,552 @@
1
+ /*
2
+ * disassembler.cpp - 65C02 instruction disassembler with flow analysis
3
+ *
4
+ * Written by
5
+ * Mike Daley <michael_daley@icloud.com>
6
+ */
7
+
8
+ #include "disassembler.hpp"
9
+ #include <algorithm>
10
+ #include <queue>
11
+ #include <unordered_set>
12
+
13
+ namespace a2e {
14
+
15
+ // Internal addressing mode enum for opcode table
16
+ enum AddrModeInt {
17
+ IMP = 0, ACC, IMM, ZP, ZPX, ZPY, ABS, ABX, ABY, IND, IZX, IZY, REL, ZPI, AIX, ZPR
18
+ };
19
+
20
+ // Internal category enum for opcode table
21
+ enum CategoryInt {
22
+ CAT_BRANCH = 0, CAT_LOAD, CAT_MATH, CAT_STACK, CAT_FLAG, CAT_UNKNOWN
23
+ };
24
+
25
+ // OpcodeInfo is defined in disassembler.hpp
26
+
27
+ // Mnemonic table - index corresponds to mnemonicIndex in DisasmInstruction
28
+ static const char* MNEMONICS[] = {
29
+ "???", // 0 - unknown
30
+ "ADC", // 1
31
+ "AND", // 2
32
+ "ASL", // 3
33
+ "BBR0", // 4
34
+ "BBR1", // 5
35
+ "BBR2", // 6
36
+ "BBR3", // 7
37
+ "BBR4", // 8
38
+ "BBR5", // 9
39
+ "BBR6", // 10
40
+ "BBR7", // 11
41
+ "BBS0", // 12
42
+ "BBS1", // 13
43
+ "BBS2", // 14
44
+ "BBS3", // 15
45
+ "BBS4", // 16
46
+ "BBS5", // 17
47
+ "BBS6", // 18
48
+ "BBS7", // 19
49
+ "BCC", // 20
50
+ "BCS", // 21
51
+ "BEQ", // 22
52
+ "BIT", // 23
53
+ "BMI", // 24
54
+ "BNE", // 25
55
+ "BPL", // 26
56
+ "BRA", // 27
57
+ "BRK", // 28
58
+ "BVC", // 29
59
+ "BVS", // 30
60
+ "CLC", // 31
61
+ "CLD", // 32
62
+ "CLI", // 33
63
+ "CLV", // 34
64
+ "CMP", // 35
65
+ "CPX", // 36
66
+ "CPY", // 37
67
+ "DEC", // 38
68
+ "DEX", // 39
69
+ "DEY", // 40
70
+ "EOR", // 41
71
+ "INC", // 42
72
+ "INX", // 43
73
+ "INY", // 44
74
+ "JMP", // 45
75
+ "JSR", // 46
76
+ "LDA", // 47
77
+ "LDX", // 48
78
+ "LDY", // 49
79
+ "LSR", // 50
80
+ "NOP", // 51
81
+ "ORA", // 52
82
+ "PHA", // 53
83
+ "PHP", // 54
84
+ "PHX", // 55
85
+ "PHY", // 56
86
+ "PLA", // 57
87
+ "PLP", // 58
88
+ "PLX", // 59
89
+ "PLY", // 60
90
+ "RMB0", // 61
91
+ "RMB1", // 62
92
+ "RMB2", // 63
93
+ "RMB3", // 64
94
+ "RMB4", // 65
95
+ "RMB5", // 66
96
+ "RMB6", // 67
97
+ "RMB7", // 68
98
+ "ROL", // 69
99
+ "ROR", // 70
100
+ "RTI", // 71
101
+ "RTS", // 72
102
+ "SBC", // 73
103
+ "SEC", // 74
104
+ "SED", // 75
105
+ "SEI", // 76
106
+ "SMB0", // 77
107
+ "SMB1", // 78
108
+ "SMB2", // 79
109
+ "SMB3", // 80
110
+ "SMB4", // 81
111
+ "SMB5", // 82
112
+ "SMB6", // 83
113
+ "SMB7", // 84
114
+ "STA", // 85
115
+ "STP", // 86
116
+ "STX", // 87
117
+ "STY", // 88
118
+ "STZ", // 89
119
+ "TAX", // 90
120
+ "TAY", // 91
121
+ "TRB", // 92
122
+ "TSB", // 93
123
+ "TSX", // 94
124
+ "TXA", // 95
125
+ "TXS", // 96
126
+ "TYA", // 97
127
+ "WAI", // 98
128
+ };
129
+
130
+ // Mnemonic indices
131
+ enum MnemIdx {
132
+ M_UNK = 0,
133
+ M_ADC = 1, M_AND, M_ASL,
134
+ M_BBR0, M_BBR1, M_BBR2, M_BBR3, M_BBR4, M_BBR5, M_BBR6, M_BBR7,
135
+ M_BBS0, M_BBS1, M_BBS2, M_BBS3, M_BBS4, M_BBS5, M_BBS6, M_BBS7,
136
+ M_BCC, M_BCS, M_BEQ, M_BIT, M_BMI, M_BNE, M_BPL, M_BRA, M_BRK, M_BVC, M_BVS,
137
+ M_CLC, M_CLD, M_CLI, M_CLV, M_CMP, M_CPX, M_CPY,
138
+ M_DEC, M_DEX, M_DEY,
139
+ M_EOR,
140
+ M_INC, M_INX, M_INY,
141
+ M_JMP, M_JSR,
142
+ M_LDA, M_LDX, M_LDY, M_LSR,
143
+ M_NOP,
144
+ M_ORA,
145
+ M_PHA, M_PHP, M_PHX, M_PHY, M_PLA, M_PLP, M_PLX, M_PLY,
146
+ M_RMB0, M_RMB1, M_RMB2, M_RMB3, M_RMB4, M_RMB5, M_RMB6, M_RMB7,
147
+ M_ROL, M_ROR, M_RTI, M_RTS,
148
+ M_SBC, M_SEC, M_SED, M_SEI,
149
+ M_SMB0, M_SMB1, M_SMB2, M_SMB3, M_SMB4, M_SMB5, M_SMB6, M_SMB7,
150
+ M_STA, M_STP, M_STX, M_STY, M_STZ,
151
+ M_TAX, M_TAY, M_TRB, M_TSB, M_TSX, M_TXA, M_TXS, M_TYA,
152
+ M_WAI
153
+ };
154
+
155
+ // Full 65C02 opcode table: {mnemonicIndex, mode, category}
156
+ static const OpcodeInfo opcodes[256] = {
157
+ // 0x00-0x0F
158
+ {M_BRK, IMP, CAT_STACK}, {M_ORA, IZX, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
159
+ {M_TSB, ZP, CAT_MATH}, {M_ORA, ZP, CAT_MATH}, {M_ASL, ZP, CAT_MATH}, {M_RMB0, ZP, CAT_MATH},
160
+ {M_PHP, IMP, CAT_STACK}, {M_ORA, IMM, CAT_MATH}, {M_ASL, ACC, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
161
+ {M_TSB, ABS, CAT_MATH}, {M_ORA, ABS, CAT_MATH}, {M_ASL, ABS, CAT_MATH}, {M_BBR0, ZPR, CAT_BRANCH},
162
+ // 0x10-0x1F
163
+ {M_BPL, REL, CAT_BRANCH}, {M_ORA, IZY, CAT_MATH}, {M_ORA, ZPI, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
164
+ {M_TRB, ZP, CAT_MATH}, {M_ORA, ZPX, CAT_MATH}, {M_ASL, ZPX, CAT_MATH}, {M_RMB1, ZP, CAT_MATH},
165
+ {M_CLC, IMP, CAT_FLAG}, {M_ORA, ABY, CAT_MATH}, {M_INC, ACC, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
166
+ {M_TRB, ABS, CAT_MATH}, {M_ORA, ABX, CAT_MATH}, {M_ASL, ABX, CAT_MATH}, {M_BBR1, ZPR, CAT_BRANCH},
167
+ // 0x20-0x2F
168
+ {M_JSR, ABS, CAT_BRANCH}, {M_AND, IZX, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
169
+ {M_BIT, ZP, CAT_MATH}, {M_AND, ZP, CAT_MATH}, {M_ROL, ZP, CAT_MATH}, {M_RMB2, ZP, CAT_MATH},
170
+ {M_PLP, IMP, CAT_STACK}, {M_AND, IMM, CAT_MATH}, {M_ROL, ACC, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
171
+ {M_BIT, ABS, CAT_MATH}, {M_AND, ABS, CAT_MATH}, {M_ROL, ABS, CAT_MATH}, {M_BBR2, ZPR, CAT_BRANCH},
172
+ // 0x30-0x3F
173
+ {M_BMI, REL, CAT_BRANCH}, {M_AND, IZY, CAT_MATH}, {M_AND, ZPI, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
174
+ {M_BIT, ZPX, CAT_MATH}, {M_AND, ZPX, CAT_MATH}, {M_ROL, ZPX, CAT_MATH}, {M_RMB3, ZP, CAT_MATH},
175
+ {M_SEC, IMP, CAT_FLAG}, {M_AND, ABY, CAT_MATH}, {M_DEC, ACC, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
176
+ {M_BIT, ABX, CAT_MATH}, {M_AND, ABX, CAT_MATH}, {M_ROL, ABX, CAT_MATH}, {M_BBR3, ZPR, CAT_BRANCH},
177
+ // 0x40-0x4F
178
+ {M_RTI, IMP, CAT_BRANCH}, {M_EOR, IZX, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
179
+ {M_UNK, IMP, CAT_UNKNOWN},{M_EOR, ZP, CAT_MATH}, {M_LSR, ZP, CAT_MATH}, {M_RMB4, ZP, CAT_MATH},
180
+ {M_PHA, IMP, CAT_STACK}, {M_EOR, IMM, CAT_MATH}, {M_LSR, ACC, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
181
+ {M_JMP, ABS, CAT_BRANCH}, {M_EOR, ABS, CAT_MATH}, {M_LSR, ABS, CAT_MATH}, {M_BBR4, ZPR, CAT_BRANCH},
182
+ // 0x50-0x5F
183
+ {M_BVC, REL, CAT_BRANCH}, {M_EOR, IZY, CAT_MATH}, {M_EOR, ZPI, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
184
+ {M_UNK, IMP, CAT_UNKNOWN},{M_EOR, ZPX, CAT_MATH}, {M_LSR, ZPX, CAT_MATH}, {M_RMB5, ZP, CAT_MATH},
185
+ {M_CLI, IMP, CAT_FLAG}, {M_EOR, ABY, CAT_MATH}, {M_PHY, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
186
+ {M_UNK, IMP, CAT_UNKNOWN},{M_EOR, ABX, CAT_MATH}, {M_LSR, ABX, CAT_MATH}, {M_BBR5, ZPR, CAT_BRANCH},
187
+ // 0x60-0x6F
188
+ {M_RTS, IMP, CAT_BRANCH}, {M_ADC, IZX, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
189
+ {M_STZ, ZP, CAT_LOAD}, {M_ADC, ZP, CAT_MATH}, {M_ROR, ZP, CAT_MATH}, {M_RMB6, ZP, CAT_MATH},
190
+ {M_PLA, IMP, CAT_STACK}, {M_ADC, IMM, CAT_MATH}, {M_ROR, ACC, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
191
+ {M_JMP, IND, CAT_BRANCH}, {M_ADC, ABS, CAT_MATH}, {M_ROR, ABS, CAT_MATH}, {M_BBR6, ZPR, CAT_BRANCH},
192
+ // 0x70-0x7F
193
+ {M_BVS, REL, CAT_BRANCH}, {M_ADC, IZY, CAT_MATH}, {M_ADC, ZPI, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
194
+ {M_STZ, ZPX, CAT_LOAD}, {M_ADC, ZPX, CAT_MATH}, {M_ROR, ZPX, CAT_MATH}, {M_RMB7, ZP, CAT_MATH},
195
+ {M_SEI, IMP, CAT_FLAG}, {M_ADC, ABY, CAT_MATH}, {M_PLY, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
196
+ {M_JMP, AIX, CAT_BRANCH}, {M_ADC, ABX, CAT_MATH}, {M_ROR, ABX, CAT_MATH}, {M_BBR7, ZPR, CAT_BRANCH},
197
+ // 0x80-0x8F
198
+ {M_BRA, REL, CAT_BRANCH}, {M_STA, IZX, CAT_LOAD}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
199
+ {M_STY, ZP, CAT_LOAD}, {M_STA, ZP, CAT_LOAD}, {M_STX, ZP, CAT_LOAD}, {M_SMB0, ZP, CAT_MATH},
200
+ {M_DEY, IMP, CAT_STACK}, {M_BIT, IMM, CAT_MATH}, {M_TXA, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
201
+ {M_STY, ABS, CAT_LOAD}, {M_STA, ABS, CAT_LOAD}, {M_STX, ABS, CAT_LOAD}, {M_BBS0, ZPR, CAT_BRANCH},
202
+ // 0x90-0x9F
203
+ {M_BCC, REL, CAT_BRANCH}, {M_STA, IZY, CAT_LOAD}, {M_STA, ZPI, CAT_LOAD}, {M_UNK, IMP, CAT_UNKNOWN},
204
+ {M_STY, ZPX, CAT_LOAD}, {M_STA, ZPX, CAT_LOAD}, {M_STX, ZPY, CAT_LOAD}, {M_SMB1, ZP, CAT_MATH},
205
+ {M_TYA, IMP, CAT_STACK}, {M_STA, ABY, CAT_LOAD}, {M_TXS, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
206
+ {M_STZ, ABS, CAT_LOAD}, {M_STA, ABX, CAT_LOAD}, {M_STZ, ABX, CAT_LOAD}, {M_BBS1, ZPR, CAT_BRANCH},
207
+ // 0xA0-0xAF
208
+ {M_LDY, IMM, CAT_LOAD}, {M_LDA, IZX, CAT_LOAD}, {M_LDX, IMM, CAT_LOAD}, {M_UNK, IMP, CAT_UNKNOWN},
209
+ {M_LDY, ZP, CAT_LOAD}, {M_LDA, ZP, CAT_LOAD}, {M_LDX, ZP, CAT_LOAD}, {M_SMB2, ZP, CAT_MATH},
210
+ {M_TAY, IMP, CAT_STACK}, {M_LDA, IMM, CAT_LOAD}, {M_TAX, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
211
+ {M_LDY, ABS, CAT_LOAD}, {M_LDA, ABS, CAT_LOAD}, {M_LDX, ABS, CAT_LOAD}, {M_BBS2, ZPR, CAT_BRANCH},
212
+ // 0xB0-0xBF
213
+ {M_BCS, REL, CAT_BRANCH}, {M_LDA, IZY, CAT_LOAD}, {M_LDA, ZPI, CAT_LOAD}, {M_UNK, IMP, CAT_UNKNOWN},
214
+ {M_LDY, ZPX, CAT_LOAD}, {M_LDA, ZPX, CAT_LOAD}, {M_LDX, ZPY, CAT_LOAD}, {M_SMB3, ZP, CAT_MATH},
215
+ {M_CLV, IMP, CAT_FLAG}, {M_LDA, ABY, CAT_LOAD}, {M_TSX, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
216
+ {M_LDY, ABX, CAT_LOAD}, {M_LDA, ABX, CAT_LOAD}, {M_LDX, ABY, CAT_LOAD}, {M_BBS3, ZPR, CAT_BRANCH},
217
+ // 0xC0-0xCF
218
+ {M_CPY, IMM, CAT_MATH}, {M_CMP, IZX, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
219
+ {M_CPY, ZP, CAT_MATH}, {M_CMP, ZP, CAT_MATH}, {M_DEC, ZP, CAT_MATH}, {M_SMB4, ZP, CAT_MATH},
220
+ {M_INY, IMP, CAT_STACK}, {M_CMP, IMM, CAT_MATH}, {M_DEX, IMP, CAT_STACK}, {M_WAI, IMP, CAT_STACK},
221
+ {M_CPY, ABS, CAT_MATH}, {M_CMP, ABS, CAT_MATH}, {M_DEC, ABS, CAT_MATH}, {M_BBS4, ZPR, CAT_BRANCH},
222
+ // 0xD0-0xDF
223
+ {M_BNE, REL, CAT_BRANCH}, {M_CMP, IZY, CAT_MATH}, {M_CMP, ZPI, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
224
+ {M_UNK, IMP, CAT_UNKNOWN},{M_CMP, ZPX, CAT_MATH}, {M_DEC, ZPX, CAT_MATH}, {M_SMB5, ZP, CAT_MATH},
225
+ {M_CLD, IMP, CAT_FLAG}, {M_CMP, ABY, CAT_MATH}, {M_PHX, IMP, CAT_STACK}, {M_STP, IMP, CAT_STACK},
226
+ {M_UNK, IMP, CAT_UNKNOWN},{M_CMP, ABX, CAT_MATH}, {M_DEC, ABX, CAT_MATH}, {M_BBS5, ZPR, CAT_BRANCH},
227
+ // 0xE0-0xEF
228
+ {M_CPX, IMM, CAT_MATH}, {M_SBC, IZX, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN}, {M_UNK, IMP, CAT_UNKNOWN},
229
+ {M_CPX, ZP, CAT_MATH}, {M_SBC, ZP, CAT_MATH}, {M_INC, ZP, CAT_MATH}, {M_SMB6, ZP, CAT_MATH},
230
+ {M_INX, IMP, CAT_STACK}, {M_SBC, IMM, CAT_MATH}, {M_NOP, IMP, CAT_FLAG}, {M_UNK, IMP, CAT_UNKNOWN},
231
+ {M_CPX, ABS, CAT_MATH}, {M_SBC, ABS, CAT_MATH}, {M_INC, ABS, CAT_MATH}, {M_BBS6, ZPR, CAT_BRANCH},
232
+ // 0xF0-0xFF
233
+ {M_BEQ, REL, CAT_BRANCH}, {M_SBC, IZY, CAT_MATH}, {M_SBC, ZPI, CAT_MATH}, {M_UNK, IMP, CAT_UNKNOWN},
234
+ {M_UNK, IMP, CAT_UNKNOWN},{M_SBC, ZPX, CAT_MATH}, {M_INC, ZPX, CAT_MATH}, {M_SMB7, ZP, CAT_MATH},
235
+ {M_SED, IMP, CAT_FLAG}, {M_SBC, ABY, CAT_MATH}, {M_PLX, IMP, CAT_STACK}, {M_UNK, IMP, CAT_UNKNOWN},
236
+ {M_UNK, IMP, CAT_UNKNOWN},{M_SBC, ABX, CAT_MATH}, {M_INC, ABX, CAT_MATH}, {M_BBS7, ZPR, CAT_BRANCH}
237
+ };
238
+
239
+ int getInstructionLength(uint8_t opcode) {
240
+ switch (opcodes[opcode].mode) {
241
+ case IMP:
242
+ case ACC:
243
+ return 1;
244
+ case IMM:
245
+ case ZP:
246
+ case ZPX:
247
+ case ZPY:
248
+ case IZX:
249
+ case IZY:
250
+ case ZPI:
251
+ case REL:
252
+ return 2;
253
+ case ABS:
254
+ case ABX:
255
+ case ABY:
256
+ case IND:
257
+ case AIX:
258
+ case ZPR:
259
+ return 3;
260
+ }
261
+ return 1;
262
+ }
263
+
264
+ const char* getMnemonic(uint8_t opcode) {
265
+ return MNEMONICS[opcodes[opcode].mnemonicIndex];
266
+ }
267
+
268
+ const char* getMnemonicByIndex(uint8_t index) {
269
+ if (index >= sizeof(MNEMONICS) / sizeof(MNEMONICS[0])) {
270
+ return "???";
271
+ }
272
+ return MNEMONICS[index];
273
+ }
274
+
275
+ AddrMode getAddressingMode(uint8_t opcode) {
276
+ return static_cast<AddrMode>(opcodes[opcode].mode);
277
+ }
278
+
279
+ InstrCategory getInstructionCategory(uint8_t opcode) {
280
+ return static_cast<InstrCategory>(opcodes[opcode].category);
281
+ }
282
+
283
+ const OpcodeInfo* getOpcodeTable() {
284
+ return opcodes;
285
+ }
286
+
287
+ int getMnemonicCount() {
288
+ return static_cast<int>(sizeof(MNEMONICS) / sizeof(MNEMONICS[0]));
289
+ }
290
+
291
+ DisasmInstruction disassembleInstruction(const uint8_t *data, size_t size,
292
+ uint16_t address) {
293
+ DisasmInstruction instr = {};
294
+ instr.address = address;
295
+
296
+ if (size == 0 || data == nullptr) {
297
+ instr.length = 0;
298
+ instr.mnemonic[0] = '?';
299
+ instr.mnemonic[1] = '?';
300
+ instr.mnemonic[2] = '?';
301
+ instr.mnemonic[3] = '\0';
302
+ return instr;
303
+ }
304
+
305
+ uint8_t opcode = data[0];
306
+ const OpcodeInfo &info = opcodes[opcode];
307
+ int instrLen = getInstructionLength(opcode);
308
+
309
+ instr.opcode = opcode;
310
+ instr.length = static_cast<uint8_t>(instrLen);
311
+ instr.mode = info.mode;
312
+ instr.category = info.category;
313
+
314
+ // Read operand bytes (if available)
315
+ instr.operand1 = (size > 1 && instrLen >= 2) ? data[1] : 0;
316
+ instr.operand2 = (size > 2 && instrLen >= 3) ? data[2] : 0;
317
+
318
+ // Calculate target address for branches and jumps
319
+ instr.target = 0;
320
+ switch (info.mode) {
321
+ case REL: {
322
+ int8_t offset = static_cast<int8_t>(instr.operand1);
323
+ instr.target = static_cast<uint16_t>(address + 2 + offset);
324
+ break;
325
+ }
326
+ case ZPR: {
327
+ int8_t offset = static_cast<int8_t>(instr.operand2);
328
+ instr.target = static_cast<uint16_t>(address + 3 + offset);
329
+ break;
330
+ }
331
+ case ABS:
332
+ case ABX:
333
+ case ABY:
334
+ case IND:
335
+ case AIX:
336
+ instr.target = static_cast<uint16_t>(instr.operand1 | (instr.operand2 << 8));
337
+ break;
338
+ default:
339
+ break;
340
+ }
341
+
342
+ // Copy mnemonic string (max 4 chars, null-padded)
343
+ const char* mnem = MNEMONICS[info.mnemonicIndex];
344
+ for (int i = 0; i < 4; i++) {
345
+ instr.mnemonic[i] = mnem[i];
346
+ if (mnem[i] == '\0') {
347
+ // Null-pad remainder
348
+ for (int j = i + 1; j < 4; j++) {
349
+ instr.mnemonic[j] = '\0';
350
+ }
351
+ break;
352
+ }
353
+ }
354
+
355
+ return instr;
356
+ }
357
+
358
+ DisasmResult disassembleBlock(const uint8_t *data, size_t size,
359
+ uint16_t baseAddress) {
360
+ DisasmResult result;
361
+
362
+ if (size == 0 || data == nullptr) {
363
+ return result;
364
+ }
365
+
366
+ // Reserve approximate space
367
+ result.instructions.reserve(size / 2);
368
+
369
+ size_t offset = 0;
370
+ while (offset < size) {
371
+ DisasmInstruction instr = disassembleInstruction(
372
+ data + offset, size - offset,
373
+ static_cast<uint16_t>(baseAddress + offset));
374
+
375
+ if (instr.length == 0) {
376
+ break;
377
+ }
378
+
379
+ result.instructions.push_back(instr);
380
+ offset += instr.length;
381
+ }
382
+
383
+ return result;
384
+ }
385
+
386
+ FlowType getFlowType(uint8_t opcode) {
387
+ const OpcodeInfo &info = opcodes[opcode];
388
+ uint8_t mnem = info.mnemonicIndex;
389
+
390
+ // Return instructions - stop tracing this path
391
+ if (mnem == M_RTS || mnem == M_RTI) {
392
+ return FlowType::RETURN;
393
+ }
394
+
395
+ // Halt instructions - stop tracing
396
+ if (mnem == M_BRK || mnem == M_STP || mnem == M_WAI) {
397
+ return FlowType::HALT;
398
+ }
399
+
400
+ // JSR - subroutine call, will return
401
+ if (mnem == M_JSR) {
402
+ return FlowType::CALL;
403
+ }
404
+
405
+ // JMP - depends on addressing mode
406
+ if (mnem == M_JMP) {
407
+ // Indirect jumps can't be traced statically
408
+ if (info.mode == IND || info.mode == AIX) {
409
+ return FlowType::INDIRECT;
410
+ }
411
+ // Absolute JMP is unconditional
412
+ return FlowType::UNCONDITIONAL;
413
+ }
414
+
415
+ // BRA (65C02) - unconditional branch
416
+ if (mnem == M_BRA) {
417
+ return FlowType::UNCONDITIONAL;
418
+ }
419
+
420
+ // Conditional branches - Bxx instructions
421
+ if (mnem == M_BPL || mnem == M_BMI || mnem == M_BVC || mnem == M_BVS ||
422
+ mnem == M_BCC || mnem == M_BCS || mnem == M_BEQ || mnem == M_BNE) {
423
+ return FlowType::CONDITIONAL;
424
+ }
425
+
426
+ // BBR/BBS (65C02) - conditional branches
427
+ if ((mnem >= M_BBR0 && mnem <= M_BBR7) ||
428
+ (mnem >= M_BBS0 && mnem <= M_BBS7)) {
429
+ return FlowType::CONDITIONAL;
430
+ }
431
+
432
+ // Everything else is sequential
433
+ return FlowType::SEQUENTIAL;
434
+ }
435
+
436
+ DisasmResult disassembleWithFlowAnalysis(const uint8_t *data, size_t size,
437
+ uint16_t baseAddress,
438
+ const std::vector<uint16_t> &entryPoints) {
439
+ DisasmResult result;
440
+
441
+ if (size == 0 || data == nullptr || entryPoints.empty()) {
442
+ return result;
443
+ }
444
+
445
+ // Calculate valid address range
446
+ uint16_t endAddress = static_cast<uint16_t>(baseAddress + size);
447
+
448
+ // Track visited addresses to avoid re-processing
449
+ std::unordered_set<uint16_t> visited;
450
+
451
+ // Work queue of addresses to visit
452
+ std::queue<uint16_t> toVisit;
453
+
454
+ // Initialize with entry points
455
+ for (uint16_t entry : entryPoints) {
456
+ if (entry >= baseAddress && entry < endAddress) {
457
+ toVisit.push(entry);
458
+ }
459
+ }
460
+
461
+ // Reserve approximate space
462
+ result.instructions.reserve(size / 2);
463
+
464
+ // Process addresses until queue is empty
465
+ while (!toVisit.empty()) {
466
+ uint16_t addr = toVisit.front();
467
+ toVisit.pop();
468
+
469
+ // Skip if already visited or out of range
470
+ if (visited.count(addr) || addr < baseAddress || addr >= endAddress) {
471
+ continue;
472
+ }
473
+
474
+ // Mark as visited
475
+ visited.insert(addr);
476
+
477
+ // Calculate offset into data buffer
478
+ size_t offset = addr - baseAddress;
479
+
480
+ // Disassemble instruction at this address
481
+ DisasmInstruction instr = disassembleInstruction(
482
+ data + offset, size - offset, addr);
483
+
484
+ if (instr.length == 0) {
485
+ continue;
486
+ }
487
+
488
+ result.instructions.push_back(instr);
489
+
490
+ // Determine what addresses to visit next based on flow type
491
+ FlowType flow = getFlowType(instr.opcode);
492
+ uint16_t nextAddr = static_cast<uint16_t>(addr + instr.length);
493
+
494
+ switch (flow) {
495
+ case FlowType::SEQUENTIAL:
496
+ // Continue to next instruction
497
+ if (nextAddr >= baseAddress && nextAddr < endAddress) {
498
+ toVisit.push(nextAddr);
499
+ }
500
+ break;
501
+
502
+ case FlowType::CONDITIONAL:
503
+ // Add both target and fall-through
504
+ if (nextAddr >= baseAddress && nextAddr < endAddress) {
505
+ toVisit.push(nextAddr);
506
+ }
507
+ if (instr.target >= baseAddress && instr.target < endAddress) {
508
+ toVisit.push(instr.target);
509
+ }
510
+ break;
511
+
512
+ case FlowType::CALL:
513
+ // JSR: add target AND return address (next instruction)
514
+ if (nextAddr >= baseAddress && nextAddr < endAddress) {
515
+ toVisit.push(nextAddr);
516
+ }
517
+ if (instr.target >= baseAddress && instr.target < endAddress) {
518
+ toVisit.push(instr.target);
519
+ }
520
+ break;
521
+
522
+ case FlowType::UNCONDITIONAL:
523
+ // JMP/BRA: only add target, don't fall through
524
+ if (instr.target >= baseAddress && instr.target < endAddress) {
525
+ toVisit.push(instr.target);
526
+ }
527
+ break;
528
+
529
+ case FlowType::RETURN:
530
+ case FlowType::INDIRECT:
531
+ case FlowType::HALT:
532
+ // Stop tracing this path
533
+ break;
534
+ }
535
+ }
536
+
537
+ // Sort instructions by address for consistent output
538
+ std::sort(result.instructions.begin(), result.instructions.end(),
539
+ [](const DisasmInstruction &a, const DisasmInstruction &b) {
540
+ return a.address < b.address;
541
+ });
542
+
543
+ return result;
544
+ }
545
+
546
+ DisasmResult disassembleWithFlowAnalysis(const uint8_t *data, size_t size,
547
+ uint16_t baseAddress) {
548
+ std::vector<uint16_t> entryPoints = {baseAddress};
549
+ return disassembleWithFlowAnalysis(data, size, baseAddress, entryPoints);
550
+ }
551
+
552
+ } // namespace a2e