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,142 @@
1
+ // Simple GCR encoding/decoding test
2
+ // Compile: g++ -std=c++17 -I../src/core/disk -o gcr-test gcr-test.cpp ../src/core/disk/gcr_encoding.cpp
3
+
4
+ #include <iostream>
5
+ #include <iomanip>
6
+ #include <cstring>
7
+ #include "../src/core/disk/gcr_encoding.hpp"
8
+
9
+ using namespace a2e;
10
+
11
+ // 6-and-2 decoding table (reverse of ENCODE_6_AND_2)
12
+ static constexpr std::array<int8_t, 256> DECODE_6_AND_2 = []() {
13
+ std::array<int8_t, 256> table{};
14
+ for (int i = 0; i < 256; i++) {
15
+ table[i] = -1; // Invalid by default
16
+ }
17
+ // Fill in valid mappings from the encode table
18
+ for (int i = 0; i < 64; i++) {
19
+ table[GCR::ENCODE_6_AND_2[i]] = static_cast<int8_t>(i);
20
+ }
21
+ return table;
22
+ }();
23
+
24
+ bool decode6and2(const uint8_t *encoded, uint8_t *output) {
25
+ // Decode 343 nibbles back to 256 bytes
26
+ uint8_t buffer[342];
27
+
28
+ // XOR decode (reverse of encode)
29
+ uint8_t prev = 0;
30
+ for (int i = 0; i < 342; i++) {
31
+ int8_t decoded = DECODE_6_AND_2[encoded[i]];
32
+ if (decoded < 0) {
33
+ std::cerr << "Invalid nibble at position " << i << ": 0x"
34
+ << std::hex << (int)encoded[i] << std::dec << std::endl;
35
+ return false;
36
+ }
37
+ buffer[i] = decoded ^ prev;
38
+ prev = buffer[i];
39
+ }
40
+
41
+ // Verify checksum
42
+ int8_t checksum_decoded = DECODE_6_AND_2[encoded[342]];
43
+ if (checksum_decoded < 0) {
44
+ std::cerr << "Invalid checksum nibble: 0x" << std::hex << (int)encoded[342] << std::dec << std::endl;
45
+ } else if ((prev & 0x3F) != (checksum_decoded & 0x3F)) {
46
+ std::cerr << "Checksum mismatch: expected 0x" << std::hex << (int)(prev & 0x3F)
47
+ << " got 0x" << (int)(checksum_decoded & 0x3F) << std::dec << std::endl;
48
+ }
49
+
50
+ // Reconstruct 256 bytes from auxiliary (86) and primary (256) buffers
51
+ for (int i = 0; i < 256; i++) {
52
+ // High 6 bits from primary buffer
53
+ uint8_t high = buffer[86 + i] << 2;
54
+
55
+ // Low 2 bits from auxiliary buffer
56
+ // NOTE: encode swaps bits 0,1, so decode must unswap
57
+ uint8_t aux_byte = buffer[i % 86];
58
+ int shift = (i / 86) * 2;
59
+ uint8_t raw = (aux_byte >> shift) & 0x03;
60
+ // Unswap: bit 0 <-> bit 1
61
+ uint8_t low = ((raw & 0x01) << 1) | ((raw & 0x02) >> 1);
62
+
63
+ output[i] = high | low;
64
+ }
65
+
66
+ return true;
67
+ }
68
+
69
+ int main() {
70
+ // Create a test sector with known data
71
+ uint8_t sector_data[256];
72
+ for (int i = 0; i < 256; i++) {
73
+ sector_data[i] = i; // Simple pattern: 0, 1, 2, ..., 255
74
+ }
75
+
76
+ std::cout << "Original sector data (first 32 bytes):" << std::endl;
77
+ for (int i = 0; i < 32; i++) {
78
+ std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sector_data[i] << " ";
79
+ if ((i + 1) % 16 == 0) std::cout << std::endl;
80
+ }
81
+ std::cout << std::dec << std::endl;
82
+
83
+ // Encode using 6-and-2
84
+ std::vector<uint8_t> encoded = GCR::encode6and2(sector_data);
85
+
86
+ std::cout << "Encoded nibbles: " << encoded.size() << " bytes" << std::endl;
87
+ std::cout << "First 48 nibbles:" << std::endl;
88
+ for (int i = 0; i < 48; i++) {
89
+ std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)encoded[i] << " ";
90
+ if ((i + 1) % 16 == 0) std::cout << std::endl;
91
+ }
92
+ std::cout << std::dec << std::endl;
93
+
94
+ // Verify all nibbles have bit 7 set
95
+ int invalid_count = 0;
96
+ for (size_t i = 0; i < encoded.size(); i++) {
97
+ if (!(encoded[i] & 0x80)) {
98
+ std::cout << "Warning: nibble at position " << i << " doesn't have bit 7 set: 0x"
99
+ << std::hex << (int)encoded[i] << std::dec << std::endl;
100
+ invalid_count++;
101
+ }
102
+ }
103
+ if (invalid_count == 0) {
104
+ std::cout << "All nibbles have bit 7 set (good)" << std::endl;
105
+ }
106
+ std::cout << std::endl;
107
+
108
+ // Decode
109
+ uint8_t decoded[256];
110
+ if (!decode6and2(encoded.data(), decoded)) {
111
+ std::cerr << "Decode failed!" << std::endl;
112
+ return 1;
113
+ }
114
+
115
+ std::cout << "Decoded sector data (first 32 bytes):" << std::endl;
116
+ for (int i = 0; i < 32; i++) {
117
+ std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)decoded[i] << " ";
118
+ if ((i + 1) % 16 == 0) std::cout << std::endl;
119
+ }
120
+ std::cout << std::dec << std::endl;
121
+
122
+ // Compare
123
+ int mismatches = 0;
124
+ for (int i = 0; i < 256; i++) {
125
+ if (sector_data[i] != decoded[i]) {
126
+ if (mismatches < 10) {
127
+ std::cout << "Mismatch at position " << i << ": expected 0x"
128
+ << std::hex << (int)sector_data[i] << " got 0x" << (int)decoded[i]
129
+ << std::dec << std::endl;
130
+ }
131
+ mismatches++;
132
+ }
133
+ }
134
+
135
+ if (mismatches == 0) {
136
+ std::cout << "SUCCESS: All 256 bytes match!" << std::endl;
137
+ return 0;
138
+ } else {
139
+ std::cout << "FAILED: " << mismatches << " mismatches" << std::endl;
140
+ return 1;
141
+ }
142
+ }
@@ -0,0 +1,70 @@
1
+ import { readFileSync } from 'fs';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import { createContext, runInContext } from 'vm';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+
9
+ async function loadWasmModule() {
10
+ const wasmJsPath = join(__dirname, '..', 'public', 'a2e.js');
11
+ const wasmDir = dirname(wasmJsPath);
12
+ const code = readFileSync(wasmJsPath, 'utf8');
13
+ const context = {
14
+ module: { exports: {} },
15
+ exports: {},
16
+ globalThis: global,
17
+ console: console,
18
+ require: (await import('module')).createRequire(import.meta.url),
19
+ __dirname: wasmDir,
20
+ __filename: wasmJsPath,
21
+ process: process,
22
+ WebAssembly: WebAssembly,
23
+ URL: URL,
24
+ TextDecoder: TextDecoder,
25
+ setTimeout: setTimeout,
26
+ clearTimeout: clearTimeout,
27
+ performance: { now: () => Date.now() }
28
+ };
29
+ createContext(context);
30
+ runInContext(code, context);
31
+ return context.module.exports;
32
+ }
33
+
34
+ async function runTest() {
35
+ const createA2EModule = await loadWasmModule();
36
+ const Module = await createA2EModule();
37
+ Module._init();
38
+
39
+ // Check ROM at FE89 and surrounding area
40
+ console.log('ROM at $FE80-$FE9F:');
41
+ for (let row = 0; row < 2; row++) {
42
+ const addr = 0xFE80 + row * 16;
43
+ let line = '$' + addr.toString(16).toUpperCase() + ': ';
44
+ for (let col = 0; col < 16; col++) {
45
+ line += Module._readMemory(addr + col).toString(16).padStart(2, '0').toUpperCase() + ' ';
46
+ }
47
+ console.log(line);
48
+ }
49
+
50
+ // Check IRQ/BRK vectors
51
+ console.log('\nVectors:');
52
+ const nmilo = Module._readMemory(0xFFFA);
53
+ const nmihi = Module._readMemory(0xFFFB);
54
+ console.log('NMI ($FFFA): $' + ((nmihi << 8) | nmilo).toString(16).toUpperCase());
55
+
56
+ const resetlo = Module._readMemory(0xFFFC);
57
+ const resethi = Module._readMemory(0xFFFD);
58
+ console.log('RESET ($FFFC): $' + ((resethi << 8) | resetlo).toString(16).toUpperCase());
59
+
60
+ const irqlo = Module._readMemory(0xFFFE);
61
+ const irqhi = Module._readMemory(0xFFFF);
62
+ console.log('IRQ/BRK ($FFFE): $' + ((irqhi << 8) | irqlo).toString(16).toUpperCase());
63
+
64
+ // What should be at FE89 in real Apple IIe ROM?
65
+ console.log('\n$FE89 contains: $' + Module._readMemory(0xFE89).toString(16).toUpperCase());
66
+ console.log('$FE8A contains: $' + Module._readMemory(0xFE8A).toString(16).toUpperCase());
67
+ console.log('$FE8B contains: $' + Module._readMemory(0xFE8B).toString(16).toUpperCase());
68
+ }
69
+
70
+ runTest().catch(err => { console.error(err); process.exit(1); });
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Compare boot sector loading between disk image and memory
3
+ */
4
+ import { readFileSync } from 'fs';
5
+ import { createContext, runInContext } from 'vm';
6
+
7
+ async function loadWasmModule() {
8
+ const wasmJsPath = '/Users/michaeldaley/Source/web-a2e/public/a2e.js';
9
+
10
+ const code = readFileSync(wasmJsPath, 'utf8');
11
+
12
+ const context = {
13
+ module: { exports: {} },
14
+ exports: {},
15
+ globalThis: global,
16
+ console: console,
17
+ require: (await import('module')).createRequire(import.meta.url),
18
+ __dirname: '/Users/michaeldaley/Source/web-a2e/public',
19
+ __filename: wasmJsPath,
20
+ process: process,
21
+ WebAssembly: WebAssembly,
22
+ URL: URL,
23
+ TextDecoder: TextDecoder,
24
+ setTimeout: setTimeout,
25
+ clearTimeout: clearTimeout,
26
+ performance: { now: () => Date.now() }
27
+ };
28
+
29
+ createContext(context);
30
+ runInContext(code, context);
31
+
32
+ return context.module.exports;
33
+ }
34
+
35
+ async function main() {
36
+ console.log('Loading WASM module...');
37
+ const createA2EModule = await loadWasmModule();
38
+ const Module = await createA2EModule();
39
+
40
+ console.log('Initializing emulator...');
41
+ Module._init();
42
+
43
+ // Load the disk image
44
+ const diskPath = '/Users/michaeldaley/Source/web-a2e/public/Apple DOS 3.3 August 1980.dsk';
45
+ const diskData = readFileSync(diskPath);
46
+
47
+ // Allocate memory for disk data
48
+ const diskPtr = Module._malloc(diskData.length);
49
+ Module.HEAPU8.set(diskData, diskPtr);
50
+
51
+ // Allocate memory for filename
52
+ const filename = 'Apple DOS 3.3 August 1980.dsk';
53
+ const filenamePtr = Module._malloc(filename.length + 1);
54
+ Module.stringToUTF8(filename, filenamePtr, filename.length + 1);
55
+
56
+ // Insert the disk
57
+ const inserted = Module._insertDisk(0, diskPtr, diskData.length, filenamePtr);
58
+ Module._free(filenamePtr);
59
+
60
+ if (!inserted) {
61
+ console.error('Failed to insert disk!');
62
+ process.exit(1);
63
+ }
64
+
65
+ // Run boot cycles in batches to see progress
66
+ console.log('Running boot cycles...');
67
+
68
+ // Run first to get to the interesting part
69
+ Module._runCycles(4000000);
70
+ console.log(`At 4M cycles: PC=$${Module._getPC().toString(16).toUpperCase().padStart(4, '0')}`);
71
+
72
+ // Now run in smaller batches to catch the crash
73
+ for (let batch = 0; batch < 20; batch++) {
74
+ Module._runCycles(50000);
75
+ const pc = Module._getPC();
76
+ const track = Module._getDiskTrack(0);
77
+ const cycles = Module._getTotalCycles();
78
+ console.log(`${cycles} cycles: PC=$${pc.toString(16).toUpperCase().padStart(4, '0')}, Track=${track}`);
79
+
80
+ // Check if crashed
81
+ if (pc === 0) {
82
+ console.log('CRASHED at $0000!');
83
+
84
+ // Dump memory around $3900-$397F (boot stage 2 code)
85
+ console.log('\nBoot stage 2 code ($3900-$397F):');
86
+ let line = '';
87
+ for (let i = 0; i < 128; i++) {
88
+ const byte = Module._readMemory(0x3900 + i);
89
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
90
+ if ((i + 1) % 16 === 0) {
91
+ console.log('$' + (0x3900 + i - 15).toString(16).toUpperCase() + ': ' + line);
92
+ line = '';
93
+ }
94
+ }
95
+
96
+ // Dump memory around where crash happened ($3E50-$3EAF)
97
+ console.log('\nCode near crash location ($3E50-$3EAF):');
98
+ line = '';
99
+ for (let i = 0; i < 96; i++) {
100
+ const byte = Module._readMemory(0x3E50 + i);
101
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
102
+ if ((i + 1) % 16 === 0) {
103
+ console.log('$' + (0x3E50 + i - 15).toString(16).toUpperCase() + ': ' + line);
104
+ line = '';
105
+ }
106
+ }
107
+
108
+ // Dump stack area
109
+ console.log('\nStack area ($0100-$01FF):');
110
+ const sp = Module._getSP();
111
+ console.log(`SP = $${sp.toString(16).toUpperCase().padStart(2, '0')}`);
112
+ line = '';
113
+ for (let i = sp; i <= 0xFF; i++) {
114
+ const byte = Module._readMemory(0x0100 + i);
115
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
116
+ }
117
+ console.log('Stack: ' + line);
118
+
119
+ // Check what's at $0236 (the return address on the stack)
120
+ console.log('\nMemory at $0230-$024F:');
121
+ line = '';
122
+ for (let i = 0; i < 32; i++) {
123
+ const byte = Module._readMemory(0x0230 + i);
124
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
125
+ if ((i + 1) % 16 === 0) {
126
+ console.log('$' + (0x0230 + i - 15).toString(16).toUpperCase() + ': ' + line);
127
+ line = '';
128
+ }
129
+ }
130
+
131
+ // Check what's at $0000 (where we crashed)
132
+ console.log('\nMemory at $0000-$001F:');
133
+ line = '';
134
+ for (let i = 0; i < 32; i++) {
135
+ const byte = Module._readMemory(i);
136
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
137
+ if ((i + 1) % 16 === 0) {
138
+ console.log('$' + (i - 15).toString(16).toUpperCase() + ': ' + line);
139
+ line = '';
140
+ }
141
+ }
142
+
143
+ break;
144
+ }
145
+ }
146
+
147
+ // Read memory at $0800 to see if boot sector loaded
148
+ console.log('\nMemory at $0800 (boot sector destination):');
149
+ let line = '';
150
+ for (let i = 0; i < 32; i++) {
151
+ const byte = Module._readMemory(0x0800 + i);
152
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
153
+ if ((i + 1) % 16 === 0) {
154
+ console.log(line);
155
+ line = '';
156
+ }
157
+ }
158
+
159
+ // Check the raw DSK data for sector 0, track 0
160
+ console.log('\nRaw DSK file sector 0, track 0:');
161
+ line = '';
162
+ for (let i = 0; i < 32; i++) {
163
+ line += diskData[i].toString(16).padStart(2, '0').toUpperCase() + ' ';
164
+ if ((i + 1) % 16 === 0) {
165
+ console.log(line);
166
+ line = '';
167
+ }
168
+ }
169
+
170
+ // They should match if boot sector loaded correctly
171
+ console.log('\nComparing boot sector (256 bytes)...');
172
+ let match = true;
173
+ let mismatches = [];
174
+ for (let i = 0; i < 256; i++) {
175
+ const mem = Module._readMemory(0x0800 + i);
176
+ const disk = diskData[i];
177
+ if (mem !== disk) {
178
+ mismatches.push({
179
+ addr: 0x0800 + i,
180
+ mem: mem,
181
+ disk: disk
182
+ });
183
+ match = false;
184
+ }
185
+ }
186
+
187
+ if (match) {
188
+ console.log('Boot sector matches DSK data! Loading is correct.');
189
+ } else {
190
+ console.log('Boot sector MOSTLY matches DSK data!');
191
+ console.log('Mismatches (likely runtime modifications):');
192
+ mismatches.forEach(m => {
193
+ const addr = m.addr.toString(16).toUpperCase().padStart(4, '0');
194
+ const mem = m.mem.toString(16).toUpperCase().padStart(2, '0');
195
+ const disk = m.disk.toString(16).toUpperCase().padStart(2, '0');
196
+ console.log(` $${addr}: memory=${mem} disk=${disk}`);
197
+ });
198
+ }
199
+
200
+ // Check CPU state
201
+ console.log('\nCPU State:');
202
+ console.log(`PC: $${Module._getPC().toString(16).toUpperCase().padStart(4, '0')}`);
203
+ console.log(`A: $${Module._getA().toString(16).toUpperCase().padStart(2, '0')}`);
204
+ console.log(`X: $${Module._getX().toString(16).toUpperCase().padStart(2, '0')}`);
205
+ console.log(`Y: $${Module._getY().toString(16).toUpperCase().padStart(2, '0')}`);
206
+ console.log(`SP: $${Module._getSP().toString(16).toUpperCase().padStart(2, '0')}`);
207
+ console.log(`Total cycles: ${Module._getTotalCycles()}`);
208
+
209
+ // Check disk state
210
+ console.log('\nDisk State:');
211
+ console.log(`Track: ${Module._getDiskTrack(0)}`);
212
+ console.log(`Motor: ${Module._getDiskMotorOn(0)}`);
213
+
214
+ // Check what DOS loads next - it should load sectors 1-9 of track 0 to RWTS area
215
+ // Let's check a few key memory locations
216
+ console.log('\nKey memory locations:');
217
+ console.log('$B600-$B60F (RWTS entry):');
218
+ line = '';
219
+ for (let i = 0; i < 16; i++) {
220
+ const byte = Module._readMemory(0xB600 + i);
221
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
222
+ }
223
+ console.log(line);
224
+
225
+ console.log('$9D00-$9D0F (DOS start):');
226
+ line = '';
227
+ for (let i = 0; i < 16; i++) {
228
+ const byte = Module._readMemory(0x9D00 + i);
229
+ line += byte.toString(16).padStart(2, '0').toUpperCase() + ' ';
230
+ }
231
+ console.log(line);
232
+
233
+ Module._free(diskPtr);
234
+ }
235
+
236
+ main().catch(err => {
237
+ console.error('Error:', err);
238
+ process.exit(1);
239
+ });
@@ -0,0 +1,102 @@
1
+ import { readFileSync } from 'fs';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import { createContext, runInContext } from 'vm';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+
9
+ async function loadWasmModule() {
10
+ const wasmJsPath = join(__dirname, '..', 'public', 'a2e.js');
11
+ const wasmDir = dirname(wasmJsPath);
12
+ const code = readFileSync(wasmJsPath, 'utf8');
13
+ const context = {
14
+ module: { exports: {} },
15
+ exports: {},
16
+ globalThis: global,
17
+ console: console,
18
+ require: (await import('module')).createRequire(import.meta.url),
19
+ __dirname: wasmDir,
20
+ __filename: wasmJsPath,
21
+ process: process,
22
+ WebAssembly: WebAssembly,
23
+ URL: URL,
24
+ TextDecoder: TextDecoder,
25
+ setTimeout: setTimeout,
26
+ clearTimeout: clearTimeout,
27
+ performance: { now: () => Date.now() }
28
+ };
29
+ createContext(context);
30
+ runInContext(code, context);
31
+ return context.module.exports;
32
+ }
33
+
34
+ async function runTest() {
35
+ console.log('Loading...');
36
+ const createA2EModule = await loadWasmModule();
37
+ const Module = await createA2EModule();
38
+ Module._init();
39
+
40
+ const diskPath = join(__dirname, '..', 'public', 'Apple DOS 3.3 August 1980.dsk');
41
+ const diskData = readFileSync(diskPath);
42
+ const diskPtr = Module._malloc(diskData.length);
43
+ Module.HEAPU8.set(diskData, diskPtr);
44
+ const filename = 'test.dsk';
45
+ const filenamePtr = Module._malloc(filename.length + 1);
46
+ Module.stringToUTF8(filename, filenamePtr, filename.length + 1);
47
+ Module._insertDisk(0, diskPtr, diskData.length, filenamePtr);
48
+ Module._free(filenamePtr);
49
+
50
+ // Run to earlier point
51
+ console.log('Running to 4,050,000 cycles...');
52
+ while (Module._getTotalCycles() < 4050000) {
53
+ Module._runCycles(1000);
54
+ }
55
+ console.log('At cycle ' + Module._getTotalCycles());
56
+
57
+ // Capture last 200 instructions before crash
58
+ const history = [];
59
+
60
+ for (let i = 0; i < 500000; i++) {
61
+ const pc = Module._getPC();
62
+ const sp = Module._getSP();
63
+ const a = Module._getA();
64
+ const x = Module._getX();
65
+ const y = Module._getY();
66
+ const op = Module._readMemory(pc);
67
+
68
+ history.push({pc, sp, a, x, y, op});
69
+ if (history.length > 200) history.shift();
70
+
71
+ Module._stepInstruction();
72
+
73
+ const newPC = Module._getPC();
74
+ if (newPC === 0) {
75
+ console.log('\n*** CRASH at cycle ' + Module._getTotalCycles() + ' ***');
76
+ console.log('Last 100 instructions:');
77
+ for (let j = Math.max(0, history.length - 100); j < history.length; j++) {
78
+ const h = history[j];
79
+ console.log('$' + h.pc.toString(16).toUpperCase().padStart(4,'0') +
80
+ ' [' + h.op.toString(16).padStart(2,'0') + ']' +
81
+ ' A=' + h.a.toString(16).padStart(2,'0') +
82
+ ' X=' + h.x.toString(16).padStart(2,'0') +
83
+ ' Y=' + h.y.toString(16).padStart(2,'0') +
84
+ ' SP=' + h.sp.toString(16).padStart(2,'0'));
85
+ }
86
+
87
+ console.log('\nStack ($01C0-$01FF):');
88
+ for (let row = 0; row < 4; row++) {
89
+ let line = '$' + (0x01C0 + row*16).toString(16).toUpperCase() + ': ';
90
+ for (let col = 0; col < 16; col++) {
91
+ line += Module._readMemory(0x01C0 + row*16 + col).toString(16).padStart(2,'0') + ' ';
92
+ }
93
+ console.log(line);
94
+ }
95
+ break;
96
+ }
97
+ }
98
+
99
+ Module._free(diskPtr);
100
+ }
101
+
102
+ runTest().catch(err => { console.error(err); process.exit(1); });