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,285 @@
1
+ /*
2
+ * test_cpu_interrupts.cpp - 65C02 interrupt handling tests
3
+ *
4
+ * Tests BRK, IRQ, NMI behavior, RTI, and level-triggered IRQ
5
+ * via the IRQ status callback mechanism.
6
+ */
7
+
8
+ #define CATCH_CONFIG_MAIN
9
+ #include "catch.hpp"
10
+ #include "test_helpers.hpp"
11
+
12
+ // ============================================================================
13
+ // BRK Instruction
14
+ // ============================================================================
15
+
16
+ TEST_CASE("BRK instruction behavior", "[cpu][interrupt]") {
17
+
18
+ SECTION("BRK pushes PC+2 onto stack") {
19
+ test::CPUTestFixture f;
20
+ f.mem.setIRQVector(0x1000);
21
+ // NOP at handler to prevent undefined behavior
22
+ f.mem.loadProgram(0x1000, {0xEA});
23
+ f.loadAndReset(0x0400, {0x00, 0xEA}); // BRK, padding
24
+ uint8_t spBefore = f.cpu->getSP();
25
+ f.cpu->executeInstruction();
26
+
27
+ // BRK pushes PC+2 (address after BRK + signature byte)
28
+ uint8_t retHi = f.mem[0x0100 + spBefore];
29
+ uint8_t retLo = f.mem[0x0100 + spBefore - 1];
30
+ uint16_t retAddr = (retHi << 8) | retLo;
31
+ REQUIRE(retAddr == 0x0402);
32
+ }
33
+
34
+ SECTION("BRK pushes P with B flag set") {
35
+ test::CPUTestFixture f;
36
+ f.mem.setIRQVector(0x1000);
37
+ f.mem.loadProgram(0x1000, {0xEA});
38
+ f.loadAndReset(0x0400, {0x00, 0xEA});
39
+ uint8_t spBefore = f.cpu->getSP();
40
+ f.cpu->executeInstruction();
41
+
42
+ // P is pushed after the two PC bytes, at SP+3 from final SP
43
+ uint8_t pushedP = f.mem[0x0100 + spBefore - 2];
44
+ REQUIRE((pushedP & a2e::FLAG_B) != 0); // B flag set in pushed P
45
+ REQUIRE((pushedP & a2e::FLAG_U) != 0); // U flag always set
46
+ }
47
+
48
+ SECTION("BRK jumps to IRQ vector") {
49
+ test::CPUTestFixture f;
50
+ f.mem.setIRQVector(0x1000);
51
+ f.mem.loadProgram(0x1000, {0xEA});
52
+ f.loadAndReset(0x0400, {0x00, 0xEA});
53
+ f.cpu->executeInstruction();
54
+ REQUIRE(f.cpu->getPC() == 0x1000);
55
+ }
56
+
57
+ SECTION("BRK sets I flag") {
58
+ test::CPUTestFixture f;
59
+ f.mem.setIRQVector(0x1000);
60
+ f.mem.loadProgram(0x1000, {0xEA});
61
+ f.loadAndReset(0x0400, {0x00, 0xEA});
62
+ f.cpu->setFlag(a2e::FLAG_I, false);
63
+ f.cpu->executeInstruction();
64
+ REQUIRE(f.cpu->getFlag(a2e::FLAG_I) == true);
65
+ }
66
+
67
+ SECTION("BRK clears D flag on 65C02") {
68
+ test::CPUTestFixture f;
69
+ f.mem.setIRQVector(0x1000);
70
+ f.mem.loadProgram(0x1000, {0xEA});
71
+ // SED; BRK
72
+ f.loadAndReset(0x0400, {0xF8, 0x00, 0xEA});
73
+ test::runInstructions(*f.cpu, 2);
74
+ // On 65C02, BRK clears D flag
75
+ REQUIRE(f.cpu->getFlag(a2e::FLAG_D) == false);
76
+ }
77
+ }
78
+
79
+ // ============================================================================
80
+ // IRQ - Maskable Interrupt
81
+ // ============================================================================
82
+
83
+ TEST_CASE("IRQ behavior", "[cpu][interrupt]") {
84
+
85
+ SECTION("IRQ fires when I flag is clear") {
86
+ test::CPUTestFixture f;
87
+ f.mem.setIRQVector(0x2000);
88
+ f.mem.loadProgram(0x2000, {0xEA}); // NOP at handler
89
+ // CLI; NOP; NOP
90
+ f.loadAndReset(0x0400, {0x58, 0xEA, 0xEA});
91
+ f.cpu->executeInstruction(); // CLI
92
+
93
+ // Trigger IRQ
94
+ f.cpu->irq();
95
+ f.cpu->executeInstruction(); // This NOP should complete, then IRQ fires
96
+ f.cpu->executeInstruction(); // Process the IRQ
97
+
98
+ REQUIRE(f.cpu->getPC() == 0x2001); // One instruction into handler (NOP executed)
99
+ }
100
+
101
+ SECTION("IRQ does not fire when I flag is set") {
102
+ test::CPUTestFixture f;
103
+ f.mem.setIRQVector(0x2000);
104
+ f.mem.loadProgram(0x2000, {0xEA});
105
+ // SEI; NOP; NOP
106
+ f.loadAndReset(0x0400, {0x78, 0xEA, 0xEA});
107
+ f.cpu->executeInstruction(); // SEI
108
+
109
+ f.cpu->irq();
110
+ f.cpu->executeInstruction(); // NOP at $0401
111
+ f.cpu->executeInstruction(); // NOP at $0402
112
+
113
+ // Should still be executing main code, not at handler
114
+ REQUIRE(f.cpu->getPC() == 0x0403);
115
+ }
116
+
117
+ SECTION("IRQ pushes PC and P with B flag clear") {
118
+ test::CPUTestFixture f;
119
+ f.mem.setIRQVector(0x2000);
120
+ f.mem.loadProgram(0x2000, {0xEA, 0xEA});
121
+ // CLI; NOP (will be interrupted after this)
122
+ f.loadAndReset(0x0400, {0x58, 0xEA, 0xEA});
123
+ f.cpu->executeInstruction(); // CLI
124
+ f.cpu->irq();
125
+ uint8_t spBefore = f.cpu->getSP();
126
+ f.cpu->executeInstruction(); // NOP completes
127
+ f.cpu->executeInstruction(); // IRQ serviced
128
+
129
+ // P pushed during IRQ should have B flag CLEAR (unlike BRK)
130
+ uint8_t pushedP = f.mem[0x0100 + spBefore - 2];
131
+ REQUIRE((pushedP & a2e::FLAG_B) == 0);
132
+ }
133
+
134
+ SECTION("IRQ sets I flag") {
135
+ test::CPUTestFixture f;
136
+ f.mem.setIRQVector(0x2000);
137
+ f.mem.loadProgram(0x2000, {0xEA});
138
+ f.loadAndReset(0x0400, {0x58, 0xEA}); // CLI; NOP
139
+ f.cpu->executeInstruction(); // CLI
140
+ f.cpu->irq();
141
+ f.cpu->executeInstruction(); // NOP
142
+ f.cpu->executeInstruction(); // IRQ serviced
143
+
144
+ REQUIRE(f.cpu->getFlag(a2e::FLAG_I) == true);
145
+ }
146
+ }
147
+
148
+ // ============================================================================
149
+ // NMI - Non-Maskable Interrupt
150
+ // ============================================================================
151
+
152
+ TEST_CASE("NMI behavior", "[cpu][interrupt]") {
153
+
154
+ SECTION("NMI fires regardless of I flag") {
155
+ test::CPUTestFixture f;
156
+ f.mem.setNMIVector(0x3000);
157
+ f.mem.loadProgram(0x3000, {0xEA, 0xEA});
158
+ // SEI; NOP (NMI should still fire even with I set)
159
+ f.loadAndReset(0x0400, {0x78, 0xEA, 0xEA});
160
+ f.cpu->executeInstruction(); // SEI
161
+ f.cpu->nmi();
162
+ f.cpu->executeInstruction(); // NOP completes
163
+ f.cpu->executeInstruction(); // NMI serviced
164
+
165
+ // Should be at NMI handler
166
+ REQUIRE(f.cpu->getPC() == 0x3001);
167
+ }
168
+
169
+ SECTION("NMI pushes PC and P") {
170
+ test::CPUTestFixture f;
171
+ f.mem.setNMIVector(0x3000);
172
+ f.mem.loadProgram(0x3000, {0xEA, 0xEA});
173
+ f.loadAndReset(0x0400, {0xEA, 0xEA}); // NOP; NOP
174
+ uint8_t spBefore = f.cpu->getSP();
175
+ f.cpu->nmi();
176
+ f.cpu->executeInstruction(); // NOP completes
177
+ f.cpu->executeInstruction(); // NMI serviced
178
+
179
+ // SP should have moved down by 3 (PCH, PCL, P)
180
+ REQUIRE(f.cpu->getSP() == static_cast<uint8_t>(spBefore - 3));
181
+ }
182
+
183
+ SECTION("NMI jumps to NMI vector") {
184
+ test::CPUTestFixture f;
185
+ f.mem.setNMIVector(0x3000);
186
+ f.mem.loadProgram(0x3000, {0xEA});
187
+ f.loadAndReset(0x0400, {0xEA}); // NOP
188
+ f.cpu->nmi();
189
+ f.cpu->executeInstruction(); // NOP completes
190
+ f.cpu->executeInstruction(); // NMI serviced
191
+ REQUIRE(f.cpu->getPC() == 0x3001); // After executing NOP at $3000
192
+ }
193
+ }
194
+
195
+ // ============================================================================
196
+ // IRQ Status Callback (Level-Triggered IRQs)
197
+ // ============================================================================
198
+
199
+ TEST_CASE("IRQ status callback for level-triggered IRQs", "[cpu][interrupt]") {
200
+
201
+ SECTION("IRQ status callback triggers repeated interrupts while active") {
202
+ test::CPUTestFixture f;
203
+ bool irqActive = true;
204
+
205
+ f.mem.setIRQVector(0x2000);
206
+ // Handler: increment counter memory location, RTI
207
+ // LDA $10; CLC; ADC #$01; STA $10; RTI
208
+ f.mem.loadProgram(0x2000, {0xA5, 0x10, 0x18, 0x69, 0x01, 0x85, 0x10, 0x40});
209
+ f.mem[0x10] = 0x00;
210
+
211
+ // Main: CLI; NOP loop
212
+ f.loadAndReset(0x0400, {0x58, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA,
213
+ 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA});
214
+
215
+ f.cpu->setIRQStatusCallback([&irqActive]() { return irqActive; });
216
+
217
+ // Run CLI
218
+ f.cpu->executeInstruction();
219
+
220
+ // Trigger IRQ and run enough instructions for it to be serviced and return
221
+ f.cpu->irq();
222
+ test::runInstructions(*f.cpu, 20);
223
+
224
+ // Counter should have been incremented at least once
225
+ REQUIRE(f.mem[0x10] >= 1);
226
+ }
227
+ }
228
+
229
+ // ============================================================================
230
+ // RTI - Return from Interrupt
231
+ // ============================================================================
232
+
233
+ TEST_CASE("RTI instruction", "[cpu][interrupt]") {
234
+
235
+ SECTION("RTI restores P and PC from stack") {
236
+ test::CPUTestFixture f;
237
+ f.mem.setIRQVector(0x2000);
238
+ // Handler: LDA #$42; RTI
239
+ f.mem.loadProgram(0x2000, {0xA9, 0x42, 0x40});
240
+
241
+ // Main: CLC; CLI; NOP; LDX #$99
242
+ f.loadAndReset(0x0400, {0x18, 0x58, 0xEA, 0xA2, 0x99});
243
+ f.cpu->executeInstruction(); // CLC
244
+ f.cpu->executeInstruction(); // CLI
245
+
246
+ uint8_t pBeforeIRQ = f.cpu->getP();
247
+ f.cpu->irq();
248
+ f.cpu->executeInstruction(); // NOP completes
249
+ f.cpu->executeInstruction(); // IRQ serviced, now at handler
250
+
251
+ // Execute handler: LDA #$42
252
+ f.cpu->executeInstruction();
253
+ REQUIRE(f.cpu->getA() == 0x42);
254
+
255
+ // RTI should restore PC and P
256
+ f.cpu->executeInstruction(); // RTI
257
+ // PC should be back to where we were interrupted
258
+ // After RTI, should execute LDX #$99
259
+ f.cpu->executeInstruction(); // LDX #$99
260
+ REQUIRE(f.cpu->getX() == 0x99);
261
+ }
262
+
263
+ SECTION("RTI restores flags correctly") {
264
+ test::CPUTestFixture f;
265
+ f.mem.setIRQVector(0x2000);
266
+ // Handler: SEC; RTI (modifies carry, but RTI restores original flags)
267
+ f.mem.loadProgram(0x2000, {0x38, 0x40});
268
+
269
+ // Main: CLC; CLI; NOP
270
+ f.loadAndReset(0x0400, {0x18, 0x58, 0xEA, 0xEA});
271
+ f.cpu->executeInstruction(); // CLC -> C=0
272
+ f.cpu->executeInstruction(); // CLI
273
+
274
+ f.cpu->irq();
275
+ // IRQ is serviced at the start of the next executeInstruction call,
276
+ // before fetching any opcode. So the IRQ fires immediately.
277
+ f.cpu->executeInstruction(); // IRQ serviced (pushes PC and P, jumps to handler)
278
+
279
+ f.cpu->executeInstruction(); // SEC -> C=1 in handler
280
+ REQUIRE(f.cpu->getFlag(a2e::FLAG_C) == true);
281
+
282
+ f.cpu->executeInstruction(); // RTI -> restores original P with C=0
283
+ REQUIRE(f.cpu->getFlag(a2e::FLAG_C) == false);
284
+ }
285
+ }
@@ -0,0 +1,323 @@
1
+ /*
2
+ * test_disassembler.cpp - Unit tests for 65C02 disassembler
3
+ */
4
+
5
+ #define CATCH_CONFIG_MAIN
6
+ #include "catch.hpp"
7
+
8
+ #include "disassembler.hpp"
9
+
10
+ #include <cstring>
11
+
12
+ using namespace a2e;
13
+
14
+ // ============================================================================
15
+ // disassembleInstruction tests
16
+ // ============================================================================
17
+
18
+ TEST_CASE("disassembleInstruction NOP (0xEA)", "[disasm][single]") {
19
+ uint8_t data[] = {0xEA};
20
+ auto instr = disassembleInstruction(data, sizeof(data), 0x1000);
21
+
22
+ CHECK(instr.address == 0x1000);
23
+ CHECK(instr.length == 1);
24
+ CHECK(instr.opcode == 0xEA);
25
+ CHECK(std::string(instr.mnemonic) == "NOP");
26
+ CHECK(instr.mode == static_cast<uint8_t>(AddrMode::IMP));
27
+ }
28
+
29
+ TEST_CASE("disassembleInstruction LDA immediate (0xA9)", "[disasm][single]") {
30
+ uint8_t data[] = {0xA9, 0x42};
31
+ auto instr = disassembleInstruction(data, sizeof(data), 0x2000);
32
+
33
+ CHECK(instr.address == 0x2000);
34
+ CHECK(instr.length == 2);
35
+ CHECK(instr.opcode == 0xA9);
36
+ CHECK(instr.operand1 == 0x42);
37
+ CHECK(std::string(instr.mnemonic) == "LDA");
38
+ CHECK(instr.mode == static_cast<uint8_t>(AddrMode::IMM));
39
+ }
40
+
41
+ TEST_CASE("disassembleInstruction JMP absolute (0x4C)", "[disasm][single]") {
42
+ uint8_t data[] = {0x4C, 0x00, 0xC0};
43
+ auto instr = disassembleInstruction(data, sizeof(data), 0x0300);
44
+
45
+ CHECK(instr.address == 0x0300);
46
+ CHECK(instr.length == 3);
47
+ CHECK(instr.opcode == 0x4C);
48
+ CHECK(instr.operand1 == 0x00);
49
+ CHECK(instr.operand2 == 0xC0);
50
+ CHECK(instr.target == 0xC000);
51
+ CHECK(std::string(instr.mnemonic) == "JMP");
52
+ CHECK(instr.mode == static_cast<uint8_t>(AddrMode::ABS));
53
+ }
54
+
55
+ TEST_CASE("disassembleInstruction BRK (0x00)", "[disasm][single]") {
56
+ uint8_t data[] = {0x00};
57
+ auto instr = disassembleInstruction(data, sizeof(data), 0x0400);
58
+
59
+ CHECK(instr.address == 0x0400);
60
+ CHECK(instr.length == 1);
61
+ CHECK(instr.opcode == 0x00);
62
+ CHECK(std::string(instr.mnemonic) == "BRK");
63
+ }
64
+
65
+ TEST_CASE("disassembleInstruction branch instruction calculates target", "[disasm][single]") {
66
+ // BNE with forward branch (+5)
67
+ uint8_t data[] = {0xD0, 0x05};
68
+ auto instr = disassembleInstruction(data, sizeof(data), 0x1000);
69
+
70
+ CHECK(instr.length == 2);
71
+ CHECK(std::string(instr.mnemonic) == "BNE");
72
+ // Target = address + 2 + offset = 0x1000 + 2 + 5 = 0x1007
73
+ CHECK(instr.target == 0x1007);
74
+ }
75
+
76
+ TEST_CASE("disassembleInstruction branch with negative offset", "[disasm][single]") {
77
+ // BEQ with backward branch (-10 = 0xF6)
78
+ uint8_t data[] = {0xF0, 0xF6};
79
+ auto instr = disassembleInstruction(data, sizeof(data), 0x1000);
80
+
81
+ CHECK(instr.length == 2);
82
+ CHECK(std::string(instr.mnemonic) == "BEQ");
83
+ // Target = 0x1000 + 2 + (-10) = 0x0FF8
84
+ CHECK(instr.target == 0x0FF8);
85
+ }
86
+
87
+ TEST_CASE("disassembleInstruction null data returns zero-length", "[disasm][single]") {
88
+ auto instr = disassembleInstruction(nullptr, 0, 0x0000);
89
+ CHECK(instr.length == 0);
90
+ }
91
+
92
+ // ============================================================================
93
+ // disassembleBlock tests
94
+ // ============================================================================
95
+
96
+ TEST_CASE("disassembleBlock multiple instructions", "[disasm][block]") {
97
+ // NOP; LDA #$42; RTS
98
+ uint8_t data[] = {0xEA, 0xA9, 0x42, 0x60};
99
+ auto result = disassembleBlock(data, sizeof(data), 0x0800);
100
+
101
+ REQUIRE(result.instructions.size() == 3);
102
+
103
+ CHECK(std::string(result.instructions[0].mnemonic) == "NOP");
104
+ CHECK(result.instructions[0].address == 0x0800);
105
+ CHECK(result.instructions[0].length == 1);
106
+
107
+ CHECK(std::string(result.instructions[1].mnemonic) == "LDA");
108
+ CHECK(result.instructions[1].address == 0x0801);
109
+ CHECK(result.instructions[1].length == 2);
110
+
111
+ CHECK(std::string(result.instructions[2].mnemonic) == "RTS");
112
+ CHECK(result.instructions[2].address == 0x0803);
113
+ CHECK(result.instructions[2].length == 1);
114
+ }
115
+
116
+ TEST_CASE("disassembleBlock empty data returns empty result", "[disasm][block]") {
117
+ auto result = disassembleBlock(nullptr, 0, 0x0000);
118
+ CHECK(result.instructions.empty());
119
+ }
120
+
121
+ // ============================================================================
122
+ // disassembleWithFlowAnalysis tests
123
+ // ============================================================================
124
+
125
+ TEST_CASE("disassembleWithFlowAnalysis traces code paths", "[disasm][flow]") {
126
+ // Simple subroutine:
127
+ // 0x1000: LDA #$01 (A9 01)
128
+ // 0x1002: BNE $1005 (D0 01)
129
+ // 0x1004: NOP (EA)
130
+ // 0x1005: RTS (60)
131
+ uint8_t data[] = {0xA9, 0x01, 0xD0, 0x01, 0xEA, 0x60};
132
+ std::vector<uint16_t> entries = {0x1000};
133
+
134
+ auto result = disassembleWithFlowAnalysis(data, sizeof(data), 0x1000, entries);
135
+
136
+ // Flow analysis should find all reachable instructions
137
+ REQUIRE(result.instructions.size() >= 3);
138
+
139
+ // Check that we traced both the branch-taken and fall-through paths
140
+ bool foundLDA = false, foundRTS = false, foundBNE = false;
141
+ for (const auto& instr : result.instructions) {
142
+ if (std::string(instr.mnemonic) == "LDA") foundLDA = true;
143
+ if (std::string(instr.mnemonic) == "BNE") foundBNE = true;
144
+ if (std::string(instr.mnemonic) == "RTS") foundRTS = true;
145
+ }
146
+ CHECK(foundLDA);
147
+ CHECK(foundBNE);
148
+ CHECK(foundRTS);
149
+ }
150
+
151
+ TEST_CASE("disassembleWithFlowAnalysis convenience overload", "[disasm][flow]") {
152
+ uint8_t data[] = {0xEA, 0x60}; // NOP; RTS
153
+ auto result = disassembleWithFlowAnalysis(data, sizeof(data), 0x0800);
154
+
155
+ REQUIRE(result.instructions.size() == 2);
156
+ CHECK(std::string(result.instructions[0].mnemonic) == "NOP");
157
+ CHECK(std::string(result.instructions[1].mnemonic) == "RTS");
158
+ }
159
+
160
+ // ============================================================================
161
+ // getInstructionLength tests
162
+ // ============================================================================
163
+
164
+ TEST_CASE("getInstructionLength implied is 1", "[disasm][length]") {
165
+ // NOP (0xEA) is implied
166
+ CHECK(getInstructionLength(0xEA) == 1);
167
+ // RTS (0x60) is implied
168
+ CHECK(getInstructionLength(0x60) == 1);
169
+ // BRK (0x00) is implied
170
+ CHECK(getInstructionLength(0x00) == 1);
171
+ // INX (0xE8) is implied
172
+ CHECK(getInstructionLength(0xE8) == 1);
173
+ }
174
+
175
+ TEST_CASE("getInstructionLength immediate is 2", "[disasm][length]") {
176
+ // LDA # (0xA9)
177
+ CHECK(getInstructionLength(0xA9) == 2);
178
+ // LDX # (0xA2)
179
+ CHECK(getInstructionLength(0xA2) == 2);
180
+ // CMP # (0xC9)
181
+ CHECK(getInstructionLength(0xC9) == 2);
182
+ }
183
+
184
+ TEST_CASE("getInstructionLength absolute is 3", "[disasm][length]") {
185
+ // JMP abs (0x4C)
186
+ CHECK(getInstructionLength(0x4C) == 3);
187
+ // JSR abs (0x20)
188
+ CHECK(getInstructionLength(0x20) == 3);
189
+ // LDA abs (0xAD)
190
+ CHECK(getInstructionLength(0xAD) == 3);
191
+ // STA abs (0x8D)
192
+ CHECK(getInstructionLength(0x8D) == 3);
193
+ }
194
+
195
+ TEST_CASE("getInstructionLength relative (branches) is 2", "[disasm][length]") {
196
+ // BNE (0xD0)
197
+ CHECK(getInstructionLength(0xD0) == 2);
198
+ // BEQ (0xF0)
199
+ CHECK(getInstructionLength(0xF0) == 2);
200
+ // BCC (0x90)
201
+ CHECK(getInstructionLength(0x90) == 2);
202
+ }
203
+
204
+ TEST_CASE("getInstructionLength zero page is 2", "[disasm][length]") {
205
+ // LDA zp (0xA5)
206
+ CHECK(getInstructionLength(0xA5) == 2);
207
+ // STA zp (0x85)
208
+ CHECK(getInstructionLength(0x85) == 2);
209
+ }
210
+
211
+ // ============================================================================
212
+ // getMnemonic tests
213
+ // ============================================================================
214
+
215
+ TEST_CASE("getMnemonic returns correct mnemonic strings", "[disasm][mnemonic]") {
216
+ CHECK(std::string(getMnemonic(0xEA)) == "NOP");
217
+ CHECK(std::string(getMnemonic(0xA9)) == "LDA");
218
+ CHECK(std::string(getMnemonic(0x4C)) == "JMP");
219
+ CHECK(std::string(getMnemonic(0x20)) == "JSR");
220
+ CHECK(std::string(getMnemonic(0x60)) == "RTS");
221
+ CHECK(std::string(getMnemonic(0x00)) == "BRK");
222
+ CHECK(std::string(getMnemonic(0xE8)) == "INX");
223
+ CHECK(std::string(getMnemonic(0xC8)) == "INY");
224
+ CHECK(std::string(getMnemonic(0x48)) == "PHA");
225
+ CHECK(std::string(getMnemonic(0x68)) == "PLA");
226
+ }
227
+
228
+ // ============================================================================
229
+ // getAddressingMode tests
230
+ // ============================================================================
231
+
232
+ TEST_CASE("getAddressingMode returns correct modes", "[disasm][addrmode]") {
233
+ CHECK(getAddressingMode(0xEA) == AddrMode::IMP); // NOP - implied
234
+ CHECK(getAddressingMode(0xA9) == AddrMode::IMM); // LDA # - immediate
235
+ CHECK(getAddressingMode(0xA5) == AddrMode::ZP); // LDA zp - zero page
236
+ CHECK(getAddressingMode(0xAD) == AddrMode::ABS); // LDA abs - absolute
237
+ CHECK(getAddressingMode(0xB5) == AddrMode::ZPX); // LDA zp,X
238
+ CHECK(getAddressingMode(0xBD) == AddrMode::ABX); // LDA abs,X
239
+ CHECK(getAddressingMode(0xB9) == AddrMode::ABY); // LDA abs,Y
240
+ CHECK(getAddressingMode(0xA1) == AddrMode::IZX); // LDA (zp,X)
241
+ CHECK(getAddressingMode(0xB1) == AddrMode::IZY); // LDA (zp),Y
242
+ CHECK(getAddressingMode(0x6C) == AddrMode::IND); // JMP (abs)
243
+ CHECK(getAddressingMode(0xD0) == AddrMode::REL); // BNE rel
244
+ CHECK(getAddressingMode(0x0A) == AddrMode::ACC); // ASL A
245
+ }
246
+
247
+ // ============================================================================
248
+ // getInstructionCategory tests
249
+ // ============================================================================
250
+
251
+ TEST_CASE("getInstructionCategory returns correct categories", "[disasm][category]") {
252
+ // Branch instructions
253
+ CHECK(getInstructionCategory(0x4C) == InstrCategory::BRANCH); // JMP
254
+ CHECK(getInstructionCategory(0x20) == InstrCategory::BRANCH); // JSR
255
+ CHECK(getInstructionCategory(0xD0) == InstrCategory::BRANCH); // BNE
256
+ CHECK(getInstructionCategory(0x60) == InstrCategory::BRANCH); // RTS
257
+
258
+ // Load/store instructions
259
+ CHECK(getInstructionCategory(0xA9) == InstrCategory::LOAD); // LDA #
260
+ CHECK(getInstructionCategory(0x85) == InstrCategory::LOAD); // STA zp
261
+ CHECK(getInstructionCategory(0xA2) == InstrCategory::LOAD); // LDX #
262
+
263
+ // Math/logic instructions
264
+ CHECK(getInstructionCategory(0x69) == InstrCategory::MATH); // ADC
265
+ CHECK(getInstructionCategory(0x29) == InstrCategory::MATH); // AND #
266
+ CHECK(getInstructionCategory(0xC9) == InstrCategory::MATH); // CMP #
267
+
268
+ // Stack instructions
269
+ CHECK(getInstructionCategory(0x48) == InstrCategory::STACK); // PHA
270
+ CHECK(getInstructionCategory(0x68) == InstrCategory::STACK); // PLA
271
+ CHECK(getInstructionCategory(0x00) == InstrCategory::STACK); // BRK
272
+
273
+ // Flag instructions
274
+ CHECK(getInstructionCategory(0x18) == InstrCategory::FLAG); // CLC
275
+ CHECK(getInstructionCategory(0x38) == InstrCategory::FLAG); // SEC
276
+ CHECK(getInstructionCategory(0x78) == InstrCategory::FLAG); // SEI
277
+ }
278
+
279
+ // ============================================================================
280
+ // getFlowType tests
281
+ // ============================================================================
282
+
283
+ TEST_CASE("getFlowType SEQUENTIAL for normal instructions", "[disasm][flow]") {
284
+ CHECK(getFlowType(0xEA) == FlowType::SEQUENTIAL); // NOP
285
+ CHECK(getFlowType(0xA9) == FlowType::SEQUENTIAL); // LDA #
286
+ CHECK(getFlowType(0x85) == FlowType::SEQUENTIAL); // STA zp
287
+ CHECK(getFlowType(0xE8) == FlowType::SEQUENTIAL); // INX
288
+ CHECK(getFlowType(0x48) == FlowType::SEQUENTIAL); // PHA
289
+ }
290
+
291
+ TEST_CASE("getFlowType CONDITIONAL for branches", "[disasm][flow]") {
292
+ CHECK(getFlowType(0xD0) == FlowType::CONDITIONAL); // BNE
293
+ CHECK(getFlowType(0xF0) == FlowType::CONDITIONAL); // BEQ
294
+ CHECK(getFlowType(0x90) == FlowType::CONDITIONAL); // BCC
295
+ CHECK(getFlowType(0xB0) == FlowType::CONDITIONAL); // BCS
296
+ CHECK(getFlowType(0x10) == FlowType::CONDITIONAL); // BPL
297
+ CHECK(getFlowType(0x30) == FlowType::CONDITIONAL); // BMI
298
+ }
299
+
300
+ TEST_CASE("getFlowType UNCONDITIONAL for JMP absolute and BRA", "[disasm][flow]") {
301
+ CHECK(getFlowType(0x4C) == FlowType::UNCONDITIONAL); // JMP abs
302
+ CHECK(getFlowType(0x80) == FlowType::UNCONDITIONAL); // BRA (65C02)
303
+ }
304
+
305
+ TEST_CASE("getFlowType CALL for JSR", "[disasm][flow]") {
306
+ CHECK(getFlowType(0x20) == FlowType::CALL); // JSR
307
+ }
308
+
309
+ TEST_CASE("getFlowType RETURN for RTS and RTI", "[disasm][flow]") {
310
+ CHECK(getFlowType(0x60) == FlowType::RETURN); // RTS
311
+ CHECK(getFlowType(0x40) == FlowType::RETURN); // RTI
312
+ }
313
+
314
+ TEST_CASE("getFlowType HALT for BRK, STP, WAI", "[disasm][flow]") {
315
+ CHECK(getFlowType(0x00) == FlowType::HALT); // BRK
316
+ CHECK(getFlowType(0xDB) == FlowType::HALT); // STP
317
+ CHECK(getFlowType(0xCB) == FlowType::HALT); // WAI
318
+ }
319
+
320
+ TEST_CASE("getFlowType INDIRECT for JMP indirect", "[disasm][flow]") {
321
+ CHECK(getFlowType(0x6C) == FlowType::INDIRECT); // JMP (abs)
322
+ CHECK(getFlowType(0x7C) == FlowType::INDIRECT); // JMP (abs,X) - 65C02
323
+ }