jsbeeb 1.1.1

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 (498) hide show
  1. package/.editorconfig +15 -0
  2. package/.git-blame-ignore-revs +3 -0
  3. package/.github/copilot-instructions.md +94 -0
  4. package/.github/workflows/claude-issue-triage.yml +105 -0
  5. package/.github/workflows/claude.yml +63 -0
  6. package/.github/workflows/release-please.yml +75 -0
  7. package/.github/workflows/test-and-deploy.yml +86 -0
  8. package/.gitmodules +6 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.idea/codeStyleSettings.xml +9 -0
  11. package/.idea/codeStyles/Project.xml +62 -0
  12. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  13. package/.idea/compiler.xml +22 -0
  14. package/.idea/copyright/profiles_settings.xml +3 -0
  15. package/.idea/encodings.xml +6 -0
  16. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  17. package/.idea/jsLibraryMappings.xml +6 -0
  18. package/.idea/jsLinters/jshint.xml +85 -0
  19. package/.idea/jsLinters/jslint.xml +15 -0
  20. package/.idea/jsbeeb.iml +11 -0
  21. package/.idea/misc.xml +6 -0
  22. package/.idea/modules.xml +8 -0
  23. package/.idea/prettier.xml +7 -0
  24. package/.idea/runConfigurations/Debug.xml +5 -0
  25. package/.idea/scopes/scope_settings.xml +5 -0
  26. package/.idea/vcs.xml +8 -0
  27. package/.prettierignore +4 -0
  28. package/.prettierrc.json +1 -0
  29. package/.release-please-manifest.json +3 -0
  30. package/.vscode/launch.json +14 -0
  31. package/.vscode/settings.json +6 -0
  32. package/CHANGELOG.md +32 -0
  33. package/CLAUDE.md +136 -0
  34. package/COPYING +674 -0
  35. package/Dockerfile +22 -0
  36. package/Makefile +30 -0
  37. package/README.md +259 -0
  38. package/docker/nginx-default.conf +10 -0
  39. package/docs/pal-comb-filter-research.md +129 -0
  40. package/docs/pal-simulation-design.md +368 -0
  41. package/eslint.config.js +35 -0
  42. package/index.html +954 -0
  43. package/jsconfig.json +10 -0
  44. package/package.json +102 -0
  45. package/public/discs/README.Irq-Timing +3 -0
  46. package/public/discs/README.bcdtest +5 -0
  47. package/public/discs/README.elite +6 -0
  48. package/public/discs/README.eng_test +3 -0
  49. package/public/discs/README.protection +7 -0
  50. package/public/favicon.ico +0 -0
  51. package/public/images/botbar.png +0 -0
  52. package/public/images/cub-monitor.png +0 -0
  53. package/public/images/jsbeeb-example.png +0 -0
  54. package/public/images/placeholder.png +0 -0
  55. package/public/images/red-off-16.png +0 -0
  56. package/public/images/red-on-16.png +0 -0
  57. package/public/images/sb/CD-left.jpg +0 -0
  58. package/public/images/sb/CD-right.jpg +0 -0
  59. package/public/images/sidebar.png +0 -0
  60. package/public/images/tv.png +0 -0
  61. package/public/images/yellow-off-16.png +0 -0
  62. package/public/images/yellow-on-16.png +0 -0
  63. package/public/jsbeeb-icon.png +0 -0
  64. package/public/robots.txt +3 -0
  65. package/public/roms/ADFS1-53.rom +0 -0
  66. package/public/roms/BASIC.ROM +0 -0
  67. package/public/roms/README +4 -0
  68. package/public/roms/a01/BASIC1.rom +0 -0
  69. package/public/roms/ample.rom +0 -0
  70. package/public/roms/ats-3.0.rom +0 -0
  71. package/public/roms/b/DFS-0.9.rom +0 -0
  72. package/public/roms/b/DFS-1.2.rom +0 -0
  73. package/public/roms/b1770/dfs1770.rom +0 -0
  74. package/public/roms/b1770/zADFS.ROM +0 -0
  75. package/public/roms/bp/dfs.rom +0 -0
  76. package/public/roms/bp/zADFS.ROM +0 -0
  77. package/public/roms/bpos.rom +0 -0
  78. package/public/roms/compact/adfs210.rom +0 -0
  79. package/public/roms/compact/basic48.rom +0 -0
  80. package/public/roms/compact/basic486.rom +0 -0
  81. package/public/roms/compact/os51.rom +0 -0
  82. package/public/roms/compact/utils.rom +0 -0
  83. package/public/roms/deos.rom +0 -0
  84. package/public/roms/master/anfs-4.25.rom +0 -0
  85. package/public/roms/master/mos.txt +68819 -0
  86. package/public/roms/master/mos3.20 +0 -0
  87. package/public/roms/os.rom +0 -0
  88. package/public/roms/os01.rom +0 -0
  89. package/public/roms/tube/6502Tube.rom +0 -0
  90. package/public/roms/tube/ARMeval_100.rom +0 -0
  91. package/public/roms/tube/BIOS.ROM +0 -0
  92. package/public/roms/tube/ReCo6502ROM_816 +0 -0
  93. package/public/roms/tube/Z80_120.rom +0 -0
  94. package/public/roms/us/USBASIC.rom +0 -0
  95. package/public/roms/us/USDNFS.rom +0 -0
  96. package/public/roms/usmos.rom +0 -0
  97. package/public/sounds/disc525/motor.wav +0 -0
  98. package/public/sounds/disc525/motoroff.wav +0 -0
  99. package/public/sounds/disc525/motoron.wav +0 -0
  100. package/public/sounds/disc525/seek.wav +0 -0
  101. package/public/sounds/disc525/seek2.wav +0 -0
  102. package/public/sounds/disc525/seek3.wav +0 -0
  103. package/public/sounds/disc525/step.wav +0 -0
  104. package/public/teletext/txt0.dat +0 -0
  105. package/public/teletext/txt1.dat +0 -0
  106. package/public/teletext/txt2.dat +0 -0
  107. package/public/teletext/txt3.dat +0 -0
  108. package/release-please-config.json +13 -0
  109. package/run-container.sh +92 -0
  110. package/src/6502.js +1347 -0
  111. package/src/6502.opcodes.js +1411 -0
  112. package/src/acia.js +261 -0
  113. package/src/adc.js +149 -0
  114. package/src/analogue-source.js +21 -0
  115. package/src/app/app.js +175 -0
  116. package/src/app/electron.js +20 -0
  117. package/src/app/preload.js +8 -0
  118. package/src/app-bench.js +33 -0
  119. package/src/basic/multiline-tetris +9 -0
  120. package/src/basic-tokenise.js +104 -0
  121. package/src/canvas.js +177 -0
  122. package/src/cmos.js +141 -0
  123. package/src/config.js +165 -0
  124. package/src/ddnoise.js +138 -0
  125. package/src/disc-drive.js +371 -0
  126. package/src/disc-hfe.js +396 -0
  127. package/src/disc.js +997 -0
  128. package/src/econet/L3FS.dat +0 -0
  129. package/src/econet/scsi.dat +0 -0
  130. package/src/econet.js +714 -0
  131. package/src/fake6502.js +32 -0
  132. package/src/fdc.js +248 -0
  133. package/src/filestore.js +666 -0
  134. package/src/gamepad-source.js +59 -0
  135. package/src/gamepads.js +268 -0
  136. package/src/google-drive.js +160 -0
  137. package/src/intel-fdc.js +1717 -0
  138. package/src/jsbeeb.css +363 -0
  139. package/src/keyboard.js +411 -0
  140. package/src/lib/README +1 -0
  141. package/src/lib/webgl-debug.js +911 -0
  142. package/src/main.js +1759 -0
  143. package/src/microphone-input.js +149 -0
  144. package/src/models.js +200 -0
  145. package/src/mouse-joystick-source.js +107 -0
  146. package/src/music5000-worklet.js +43 -0
  147. package/src/music5000.js +207 -0
  148. package/src/scheduler.js +148 -0
  149. package/src/serial.js +31 -0
  150. package/src/soundchip.js +314 -0
  151. package/src/sth.js +59 -0
  152. package/src/tapes.js +265 -0
  153. package/src/teletext.js +348 -0
  154. package/src/teletext_adaptor.js +172 -0
  155. package/src/teletext_data.js +1064 -0
  156. package/src/touchscreen.js +86 -0
  157. package/src/tube.js +349 -0
  158. package/src/url-params.js +256 -0
  159. package/src/utils.js +1090 -0
  160. package/src/via.js +702 -0
  161. package/src/video-filters/pal-composite.js +94 -0
  162. package/src/video-filters/passthrough-filter.js +70 -0
  163. package/src/video-filters/shaders/pal-composite.frag.glsl +147 -0
  164. package/src/video-filters/shaders/pal-composite.vert.glsl +8 -0
  165. package/src/video-filters/shaders/passthrough.frag.glsl +6 -0
  166. package/src/video-filters/shaders/passthrough.vert.glsl +7 -0
  167. package/src/video.js +794 -0
  168. package/src/wd-fdc.js +1344 -0
  169. package/src/web/audio-handler.js +146 -0
  170. package/src/web/audio-renderer.js +115 -0
  171. package/src/web/debug.js +529 -0
  172. package/tests/integration/RmwX.asm +47 -0
  173. package/tests/integration/TestInstructionsSource +27 -0
  174. package/tests/integration/TestTimingsResults +27 -0
  175. package/tests/integration/TestTimingsSource +61 -0
  176. package/tests/integration/bcd.js +23 -0
  177. package/tests/integration/dormann.js +101 -0
  178. package/tests/integration/dp111timing.js +42 -0
  179. package/tests/integration/ensure-submodules.js +25 -0
  180. package/tests/integration/nops.bas +119 -0
  181. package/tests/integration/nops.js +24 -0
  182. package/tests/integration/protection.js +26 -0
  183. package/tests/integration/rmw.js +69 -0
  184. package/tests/integration/teletext/expected_bug_469.png +0 -0
  185. package/tests/integration/teletext/expected_flash_0.png +0 -0
  186. package/tests/integration/teletext/expected_flash_1.png +0 -0
  187. package/tests/integration/teletext/expected_hoglet_held_char.png +0 -0
  188. package/tests/integration/teletext/expected_reveal_flash_0.png +0 -0
  189. package/tests/integration/teletext/expected_reveal_flash_1.png +0 -0
  190. package/tests/integration/teletext.js +126 -0
  191. package/tests/integration/timings.js +56 -0
  192. package/tests/integration/via.js +1125 -0
  193. package/tests/suite/README.md +7 -0
  194. package/tests/suite/Test Suite 2.15.txt +373 -0
  195. package/tests/suite/bin/ start +0 -0
  196. package/tests/suite/bin/adca +0 -0
  197. package/tests/suite/bin/adcax +0 -0
  198. package/tests/suite/bin/adcay +0 -0
  199. package/tests/suite/bin/adcb +0 -0
  200. package/tests/suite/bin/adcix +0 -0
  201. package/tests/suite/bin/adciy +0 -0
  202. package/tests/suite/bin/adcz +0 -0
  203. package/tests/suite/bin/adczx +0 -0
  204. package/tests/suite/bin/alrb +0 -0
  205. package/tests/suite/bin/ancb +0 -0
  206. package/tests/suite/bin/anda +0 -0
  207. package/tests/suite/bin/andax +0 -0
  208. package/tests/suite/bin/anday +0 -0
  209. package/tests/suite/bin/andb +0 -0
  210. package/tests/suite/bin/andix +0 -0
  211. package/tests/suite/bin/andiy +0 -0
  212. package/tests/suite/bin/andz +0 -0
  213. package/tests/suite/bin/andzx +0 -0
  214. package/tests/suite/bin/aneb +0 -0
  215. package/tests/suite/bin/arrb +0 -0
  216. package/tests/suite/bin/asla +0 -0
  217. package/tests/suite/bin/aslax +0 -0
  218. package/tests/suite/bin/asln +0 -0
  219. package/tests/suite/bin/aslz +0 -0
  220. package/tests/suite/bin/aslzx +0 -0
  221. package/tests/suite/bin/asoa +0 -0
  222. package/tests/suite/bin/asoax +0 -0
  223. package/tests/suite/bin/asoay +0 -0
  224. package/tests/suite/bin/asoix +0 -0
  225. package/tests/suite/bin/asoiy +0 -0
  226. package/tests/suite/bin/asoz +0 -0
  227. package/tests/suite/bin/asozx +0 -0
  228. package/tests/suite/bin/axsa +0 -0
  229. package/tests/suite/bin/axsix +0 -0
  230. package/tests/suite/bin/axsz +0 -0
  231. package/tests/suite/bin/axszy +0 -0
  232. package/tests/suite/bin/bccr +0 -0
  233. package/tests/suite/bin/bcsr +0 -0
  234. package/tests/suite/bin/beqr +0 -0
  235. package/tests/suite/bin/bita +0 -0
  236. package/tests/suite/bin/bitz +0 -0
  237. package/tests/suite/bin/bmir +0 -0
  238. package/tests/suite/bin/bner +0 -0
  239. package/tests/suite/bin/bplr +0 -0
  240. package/tests/suite/bin/branchwrap +0 -0
  241. package/tests/suite/bin/brkn +0 -0
  242. package/tests/suite/bin/bvcr +0 -0
  243. package/tests/suite/bin/bvsr +0 -0
  244. package/tests/suite/bin/cia1pb6 +0 -0
  245. package/tests/suite/bin/cia1pb7 +0 -0
  246. package/tests/suite/bin/cia1ta +0 -0
  247. package/tests/suite/bin/cia1tab +0 -0
  248. package/tests/suite/bin/cia1tb +0 -0
  249. package/tests/suite/bin/cia1tb123 +0 -0
  250. package/tests/suite/bin/cia2pb6 +0 -0
  251. package/tests/suite/bin/cia2pb7 +0 -0
  252. package/tests/suite/bin/cia2ta +0 -0
  253. package/tests/suite/bin/cia2tb +0 -0
  254. package/tests/suite/bin/cia2tb123 +0 -0
  255. package/tests/suite/bin/clcn +0 -0
  256. package/tests/suite/bin/cldn +0 -0
  257. package/tests/suite/bin/clin +0 -0
  258. package/tests/suite/bin/clvn +0 -0
  259. package/tests/suite/bin/cmpa +0 -0
  260. package/tests/suite/bin/cmpax +0 -0
  261. package/tests/suite/bin/cmpay +0 -0
  262. package/tests/suite/bin/cmpb +0 -0
  263. package/tests/suite/bin/cmpix +0 -0
  264. package/tests/suite/bin/cmpiy +0 -0
  265. package/tests/suite/bin/cmpz +0 -0
  266. package/tests/suite/bin/cmpzx +0 -0
  267. package/tests/suite/bin/cntdef +0 -0
  268. package/tests/suite/bin/cnto2 +0 -0
  269. package/tests/suite/bin/cpuport +0 -0
  270. package/tests/suite/bin/cputiming +0 -0
  271. package/tests/suite/bin/cpxa +0 -0
  272. package/tests/suite/bin/cpxb +0 -0
  273. package/tests/suite/bin/cpxz +0 -0
  274. package/tests/suite/bin/cpya +0 -0
  275. package/tests/suite/bin/cpyb +0 -0
  276. package/tests/suite/bin/cpyz +0 -0
  277. package/tests/suite/bin/dcma +0 -0
  278. package/tests/suite/bin/dcmax +0 -0
  279. package/tests/suite/bin/dcmay +0 -0
  280. package/tests/suite/bin/dcmix +0 -0
  281. package/tests/suite/bin/dcmiy +0 -0
  282. package/tests/suite/bin/dcmz +0 -0
  283. package/tests/suite/bin/dcmzx +0 -0
  284. package/tests/suite/bin/deca +0 -0
  285. package/tests/suite/bin/decax +0 -0
  286. package/tests/suite/bin/decz +0 -0
  287. package/tests/suite/bin/deczx +0 -0
  288. package/tests/suite/bin/dexn +0 -0
  289. package/tests/suite/bin/deyn +0 -0
  290. package/tests/suite/bin/eora +0 -0
  291. package/tests/suite/bin/eorax +0 -0
  292. package/tests/suite/bin/eoray +0 -0
  293. package/tests/suite/bin/eorb +0 -0
  294. package/tests/suite/bin/eorix +0 -0
  295. package/tests/suite/bin/eoriy +0 -0
  296. package/tests/suite/bin/eorz +0 -0
  297. package/tests/suite/bin/eorzx +0 -0
  298. package/tests/suite/bin/finish +0 -0
  299. package/tests/suite/bin/flipos +0 -0
  300. package/tests/suite/bin/icr01 +0 -0
  301. package/tests/suite/bin/imr +0 -0
  302. package/tests/suite/bin/inca +0 -0
  303. package/tests/suite/bin/incax +0 -0
  304. package/tests/suite/bin/incz +0 -0
  305. package/tests/suite/bin/inczx +0 -0
  306. package/tests/suite/bin/insa +0 -0
  307. package/tests/suite/bin/insax +0 -0
  308. package/tests/suite/bin/insay +0 -0
  309. package/tests/suite/bin/insix +0 -0
  310. package/tests/suite/bin/insiy +0 -0
  311. package/tests/suite/bin/insz +0 -0
  312. package/tests/suite/bin/inszx +0 -0
  313. package/tests/suite/bin/inxn +0 -0
  314. package/tests/suite/bin/inyn +0 -0
  315. package/tests/suite/bin/irq +0 -0
  316. package/tests/suite/bin/jmpi +0 -0
  317. package/tests/suite/bin/jmpw +0 -0
  318. package/tests/suite/bin/jsrw +0 -0
  319. package/tests/suite/bin/lasay +0 -0
  320. package/tests/suite/bin/laxa +0 -0
  321. package/tests/suite/bin/laxay +0 -0
  322. package/tests/suite/bin/laxix +0 -0
  323. package/tests/suite/bin/laxiy +0 -0
  324. package/tests/suite/bin/laxz +0 -0
  325. package/tests/suite/bin/laxzy +0 -0
  326. package/tests/suite/bin/ldaa +0 -0
  327. package/tests/suite/bin/ldaax +0 -0
  328. package/tests/suite/bin/ldaay +0 -0
  329. package/tests/suite/bin/ldab +0 -0
  330. package/tests/suite/bin/ldaix +0 -0
  331. package/tests/suite/bin/ldaiy +0 -0
  332. package/tests/suite/bin/ldaz +0 -0
  333. package/tests/suite/bin/ldazx +0 -0
  334. package/tests/suite/bin/ldxa +0 -0
  335. package/tests/suite/bin/ldxay +0 -0
  336. package/tests/suite/bin/ldxb +0 -0
  337. package/tests/suite/bin/ldxz +0 -0
  338. package/tests/suite/bin/ldxzy +0 -0
  339. package/tests/suite/bin/ldya +0 -0
  340. package/tests/suite/bin/ldyax +0 -0
  341. package/tests/suite/bin/ldyb +0 -0
  342. package/tests/suite/bin/ldyz +0 -0
  343. package/tests/suite/bin/ldyzx +0 -0
  344. package/tests/suite/bin/loadth +0 -0
  345. package/tests/suite/bin/lsea +0 -0
  346. package/tests/suite/bin/lseax +0 -0
  347. package/tests/suite/bin/lseay +0 -0
  348. package/tests/suite/bin/lseix +0 -0
  349. package/tests/suite/bin/lseiy +0 -0
  350. package/tests/suite/bin/lsez +0 -0
  351. package/tests/suite/bin/lsezx +0 -0
  352. package/tests/suite/bin/lsra +0 -0
  353. package/tests/suite/bin/lsrax +0 -0
  354. package/tests/suite/bin/lsrn +0 -0
  355. package/tests/suite/bin/lsrz +0 -0
  356. package/tests/suite/bin/lsrzx +0 -0
  357. package/tests/suite/bin/lxab +0 -0
  358. package/tests/suite/bin/mmu +0 -0
  359. package/tests/suite/bin/mmufetch +0 -0
  360. package/tests/suite/bin/nmi +0 -0
  361. package/tests/suite/bin/nopa +0 -0
  362. package/tests/suite/bin/nopax +0 -0
  363. package/tests/suite/bin/nopb +0 -0
  364. package/tests/suite/bin/nopn +0 -0
  365. package/tests/suite/bin/nopz +0 -0
  366. package/tests/suite/bin/nopzx +0 -0
  367. package/tests/suite/bin/oneshot +0 -0
  368. package/tests/suite/bin/oraa +0 -0
  369. package/tests/suite/bin/oraax +0 -0
  370. package/tests/suite/bin/oraay +0 -0
  371. package/tests/suite/bin/orab +0 -0
  372. package/tests/suite/bin/oraix +0 -0
  373. package/tests/suite/bin/oraiy +0 -0
  374. package/tests/suite/bin/oraz +0 -0
  375. package/tests/suite/bin/orazx +0 -0
  376. package/tests/suite/bin/phan +0 -0
  377. package/tests/suite/bin/phpn +0 -0
  378. package/tests/suite/bin/plan +0 -0
  379. package/tests/suite/bin/plpn +0 -0
  380. package/tests/suite/bin/rlaa +0 -0
  381. package/tests/suite/bin/rlaax +0 -0
  382. package/tests/suite/bin/rlaay +0 -0
  383. package/tests/suite/bin/rlaix +0 -0
  384. package/tests/suite/bin/rlaiy +0 -0
  385. package/tests/suite/bin/rlaz +0 -0
  386. package/tests/suite/bin/rlazx +0 -0
  387. package/tests/suite/bin/rola +0 -0
  388. package/tests/suite/bin/rolax +0 -0
  389. package/tests/suite/bin/roln +0 -0
  390. package/tests/suite/bin/rolz +0 -0
  391. package/tests/suite/bin/rolzx +0 -0
  392. package/tests/suite/bin/rora +0 -0
  393. package/tests/suite/bin/rorax +0 -0
  394. package/tests/suite/bin/rorn +0 -0
  395. package/tests/suite/bin/rorz +0 -0
  396. package/tests/suite/bin/rorzx +0 -0
  397. package/tests/suite/bin/rraa +0 -0
  398. package/tests/suite/bin/rraax +0 -0
  399. package/tests/suite/bin/rraay +0 -0
  400. package/tests/suite/bin/rraix +0 -0
  401. package/tests/suite/bin/rraiy +0 -0
  402. package/tests/suite/bin/rraz +0 -0
  403. package/tests/suite/bin/rrazx +0 -0
  404. package/tests/suite/bin/rtin +0 -0
  405. package/tests/suite/bin/rtsn +0 -0
  406. package/tests/suite/bin/sbca +0 -0
  407. package/tests/suite/bin/sbcax +0 -0
  408. package/tests/suite/bin/sbcay +0 -0
  409. package/tests/suite/bin/sbcb +0 -0
  410. package/tests/suite/bin/sbcb(eb) +0 -0
  411. package/tests/suite/bin/sbcix +0 -0
  412. package/tests/suite/bin/sbciy +0 -0
  413. package/tests/suite/bin/sbcz +0 -0
  414. package/tests/suite/bin/sbczx +0 -0
  415. package/tests/suite/bin/sbxb +0 -0
  416. package/tests/suite/bin/secn +0 -0
  417. package/tests/suite/bin/sedn +0 -0
  418. package/tests/suite/bin/sein +0 -0
  419. package/tests/suite/bin/shaay +0 -0
  420. package/tests/suite/bin/shaiy +0 -0
  421. package/tests/suite/bin/shsay +0 -0
  422. package/tests/suite/bin/shxay +0 -0
  423. package/tests/suite/bin/shyax +0 -0
  424. package/tests/suite/bin/staa +0 -0
  425. package/tests/suite/bin/staax +0 -0
  426. package/tests/suite/bin/staay +0 -0
  427. package/tests/suite/bin/staix +0 -0
  428. package/tests/suite/bin/staiy +0 -0
  429. package/tests/suite/bin/staz +0 -0
  430. package/tests/suite/bin/stazx +0 -0
  431. package/tests/suite/bin/stxa +0 -0
  432. package/tests/suite/bin/stxz +0 -0
  433. package/tests/suite/bin/stxzy +0 -0
  434. package/tests/suite/bin/stya +0 -0
  435. package/tests/suite/bin/styz +0 -0
  436. package/tests/suite/bin/styzx +0 -0
  437. package/tests/suite/bin/taxn +0 -0
  438. package/tests/suite/bin/tayn +0 -0
  439. package/tests/suite/bin/trap1 +0 -0
  440. package/tests/suite/bin/trap10 +0 -0
  441. package/tests/suite/bin/trap11 +0 -0
  442. package/tests/suite/bin/trap12 +0 -0
  443. package/tests/suite/bin/trap13 +0 -0
  444. package/tests/suite/bin/trap14 +0 -0
  445. package/tests/suite/bin/trap15 +0 -0
  446. package/tests/suite/bin/trap16 +0 -0
  447. package/tests/suite/bin/trap17 +0 -0
  448. package/tests/suite/bin/trap2 +0 -0
  449. package/tests/suite/bin/trap3 +0 -0
  450. package/tests/suite/bin/trap4 +0 -0
  451. package/tests/suite/bin/trap5 +0 -0
  452. package/tests/suite/bin/trap6 +0 -0
  453. package/tests/suite/bin/trap7 +0 -0
  454. package/tests/suite/bin/trap8 +0 -0
  455. package/tests/suite/bin/trap9 +0 -0
  456. package/tests/suite/bin/tsxn +0 -0
  457. package/tests/suite/bin/txan +0 -0
  458. package/tests/suite/bin/txsn +0 -0
  459. package/tests/suite/bin/tyan +0 -0
  460. package/tests/suite/cbm-hackers-post.html +178 -0
  461. package/tests/suite/cbm-hackers-post.md +78 -0
  462. package/tests/test-machine.js +288 -0
  463. package/tests/test-suite.js +147 -0
  464. package/tests/test.css +7 -0
  465. package/tests/unit/gzip/test-1 +0 -0
  466. package/tests/unit/gzip/test-1.gz +0 -0
  467. package/tests/unit/gzip/test-2 +0 -0
  468. package/tests/unit/gzip/test-2.gz +0 -0
  469. package/tests/unit/gzip/test-3 +0 -0
  470. package/tests/unit/gzip/test-3.gz +0 -0
  471. package/tests/unit/gzip/test-4 +0 -0
  472. package/tests/unit/gzip/test-4.gz +0 -0
  473. package/tests/unit/test-adc.js +307 -0
  474. package/tests/unit/test-bcd.js +30 -0
  475. package/tests/unit/test-cmos.js +266 -0
  476. package/tests/unit/test-disc-drive.js +85 -0
  477. package/tests/unit/test-disc-hfe.js +347 -0
  478. package/tests/unit/test-disc.js +232 -0
  479. package/tests/unit/test-fifo.js +35 -0
  480. package/tests/unit/test-gamepad-source.js +67 -0
  481. package/tests/unit/test-gzip.js +22 -0
  482. package/tests/unit/test-intel-fdc.js +93 -0
  483. package/tests/unit/test-keyboard.js +410 -0
  484. package/tests/unit/test-mouse-joystick-source.js +128 -0
  485. package/tests/unit/test-scheduler.js +190 -0
  486. package/tests/unit/test-serial.js +154 -0
  487. package/tests/unit/test-teletext-adaptor.js +359 -0
  488. package/tests/unit/test-tokenise.js +65 -0
  489. package/tests/unit/test-url-params.js +398 -0
  490. package/tests/unit/test-utils.js +276 -0
  491. package/tests/unit/test-video.js +498 -0
  492. package/tests/unit/test-zip.js +56 -0
  493. package/tests/unit/zip/test-mixed.zip +0 -0
  494. package/tests/unit/zip/test-rom.zip +0 -0
  495. package/tests/unit/zip/test-ssd.zip +0 -0
  496. package/tools/fir-generator.js +80 -0
  497. package/tools/vite-plugin-fir-shader.js +131 -0
  498. package/vite.config.js +34 -0
@@ -0,0 +1,22 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import * as fs from "fs";
3
+ import * as utils from "../../src/utils.js";
4
+
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+
10
+ function testOneFile(file) {
11
+ const compressed = new Uint8Array(fs.readFileSync(`${file}.gz`));
12
+ const expected = new Uint8Array(fs.readFileSync(file));
13
+ expect(utils.ungzip(compressed)).toEqual(expected);
14
+ }
15
+
16
+ describe("gzip tests", function () {
17
+ for (let fileIndex = 1; ; fileIndex++) {
18
+ let file = join(__dirname, "gzip", `test-${fileIndex}`);
19
+ if (!fs.existsSync(file)) break;
20
+ it("handles test case " + file, () => testOneFile(file));
21
+ }
22
+ });
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect } from "vitest";
2
+
3
+ import { Scheduler } from "../../src/scheduler.js";
4
+ import { IntelFdc } from "../../src/intel-fdc.js";
5
+ import { fake6502 } from "../../src/fake6502.js";
6
+
7
+ class FakeDrive {
8
+ constructor() {
9
+ this.spinning = false;
10
+ this.pulsesCallback = null;
11
+ this.upperSide = false;
12
+ this.track = 0;
13
+ }
14
+ selectSide(side) {
15
+ this.upperSide = side;
16
+ }
17
+ setPulsesCallback(callback) {
18
+ this.pulsesCallback = callback;
19
+ }
20
+ startSpinning() {
21
+ this.spinning = true;
22
+ }
23
+ stopSpinning() {
24
+ this.spinning = false;
25
+ }
26
+ seekOneTrack(dir) {
27
+ this.track = this.track + dir;
28
+ }
29
+ notifySeek() {}
30
+ }
31
+
32
+ /**
33
+ * @param {IntelFdc} fdc
34
+ * @param {Number} command
35
+ * @param {...Number} params
36
+ */
37
+ function sendCommand(fdc, command, ...params) {
38
+ fdc.write(0, command);
39
+ for (const param of params) fdc.write(1, param);
40
+ }
41
+
42
+ describe("Intel 8271 tests", function () {
43
+ it("should contruct and start out idle", () => {
44
+ const fakeCpu = fake6502();
45
+ const scheduler = new Scheduler();
46
+ const fdc = new IntelFdc(fakeCpu, scheduler);
47
+ expect(fdc.internalStatus).toBe(0);
48
+ expect(scheduler.headroom()).toBe(Scheduler.MaxHeadroom);
49
+ });
50
+
51
+ it("should go busy as soon as a command is registered", () => {
52
+ const fakeCpu = fake6502();
53
+ const scheduler = new Scheduler();
54
+ const fdc = new IntelFdc(fakeCpu, scheduler);
55
+ fdc.write(0, 0x3a);
56
+ expect(fdc.internalStatus).toBe(0x80); // 0x80 = busy
57
+ });
58
+
59
+ const loadHead = 0x08;
60
+ const select1 = 0x40;
61
+ const writeRegCmd = 0x3a;
62
+ const mmioWrite = 0x23;
63
+ const seekCmd = (0x0a << 2) | select1 | 1;
64
+
65
+ it("should spin up when poked", () => {
66
+ const fakeCpu = fake6502();
67
+ const scheduler = new Scheduler();
68
+ const fakeDrive = new FakeDrive();
69
+ const fdc = new IntelFdc(fakeCpu, scheduler, [fakeDrive]);
70
+ expect(fdc._driveOut & loadHead).toBe(0);
71
+ expect(fakeDrive.spinning).toBe(false);
72
+ sendCommand(fdc, writeRegCmd, mmioWrite, loadHead | select1);
73
+ expect(fdc._driveOut & loadHead).toBe(loadHead);
74
+ expect(fakeDrive.spinning).toBe(true);
75
+ });
76
+ it("should seek to a track", () => {
77
+ const fakeCpu = fake6502();
78
+ const scheduler = new Scheduler();
79
+ const fakeDrive = new FakeDrive();
80
+ const fdc = new IntelFdc(fakeCpu, scheduler, [fakeDrive]);
81
+ sendCommand(fdc, writeRegCmd, mmioWrite, loadHead | select1);
82
+ // nb will seek two more due to bad track nonsense
83
+ sendCommand(fdc, seekCmd, 2);
84
+ expect(fakeDrive.track).toBe(1);
85
+ // We should have some 3ms step scheduled
86
+ expect(scheduler.headroom()).toBe(6000);
87
+ scheduler.polltime(6000);
88
+ expect(fakeDrive.track).toBe(2);
89
+ // We should reach and stop at track 4.
90
+ scheduler.polltime(6000 * 10);
91
+ expect(fakeDrive.track).toBe(4);
92
+ });
93
+ });
@@ -0,0 +1,410 @@
1
+ import { expect, describe, test, beforeEach, vi } from "vitest";
2
+ import { Keyboard } from "../../src/keyboard.js";
3
+ import * as utils from "../../src/utils.js";
4
+
5
+ describe("Keyboard", () => {
6
+ let keyboard;
7
+ let mockProcessor;
8
+ let mockSysvia;
9
+ let mockInputEnabledFunction;
10
+
11
+ // Helper function to create an async event tester
12
+ const waitForEvent = (eventName) => {
13
+ return new Promise((resolve) => {
14
+ keyboard.on(eventName, (...args) => {
15
+ resolve(args);
16
+ });
17
+ });
18
+ };
19
+
20
+ // Helper to trigger an event and wait for the result
21
+ const triggerAndWaitForEvent = async (eventName, action) => {
22
+ const eventPromise = waitForEvent(eventName);
23
+ action();
24
+ return await eventPromise;
25
+ };
26
+
27
+ beforeEach(() => {
28
+ mockSysvia = {
29
+ keyDown: vi.fn(),
30
+ keyUp: vi.fn(),
31
+ clearKeys: vi.fn(),
32
+ disableKeyboard: vi.fn(),
33
+ enableKeyboard: vi.fn(),
34
+ keyToggleRaw: vi.fn(),
35
+ setKeyLayout: vi.fn(),
36
+ capsLockLight: false,
37
+ shiftLockLight: false,
38
+ };
39
+
40
+ mockProcessor = {
41
+ sysvia: mockSysvia,
42
+ debugInstruction: {
43
+ add: vi.fn().mockReturnValue({
44
+ remove: vi.fn(),
45
+ }),
46
+ },
47
+ setReset: vi.fn(),
48
+ cpuMultiplier: 1,
49
+ cycleSeconds: 0,
50
+ currentCycles: 0,
51
+ };
52
+
53
+ mockInputEnabledFunction = vi.fn().mockReturnValue(false);
54
+
55
+ keyboard = new Keyboard({
56
+ processor: mockProcessor,
57
+ inputEnabledFunction: mockInputEnabledFunction,
58
+ keyLayout: "physical",
59
+ dbgr: {
60
+ enabled: vi.fn().mockReturnValue(false),
61
+ keyPress: vi.fn(),
62
+ hide: vi.fn(),
63
+ },
64
+ });
65
+ });
66
+
67
+ test("should create a keyboard instance", () => {
68
+ expect(keyboard).toBeDefined();
69
+ });
70
+
71
+ test("keyCode should handle location properly", () => {
72
+ // Test SHIFT key
73
+ const shiftEvent = { which: utils.keyCodes.SHIFT, location: 1 };
74
+ expect(keyboard.keyCode(shiftEvent)).toBe(utils.keyCodes.SHIFT_LEFT);
75
+
76
+ const shiftEvent2 = { which: utils.keyCodes.SHIFT, location: 2 };
77
+ expect(keyboard.keyCode(shiftEvent2)).toBe(utils.keyCodes.SHIFT_RIGHT);
78
+
79
+ // Test CTRL key
80
+ const ctrlEvent = { which: utils.keyCodes.CTRL, location: 1 };
81
+ expect(keyboard.keyCode(ctrlEvent)).toBe(utils.keyCodes.CTRL_LEFT);
82
+
83
+ const ctrlEvent2 = { which: utils.keyCodes.CTRL, location: 2 };
84
+ expect(keyboard.keyCode(ctrlEvent2)).toBe(utils.keyCodes.CTRL_RIGHT);
85
+
86
+ // Test ALT key
87
+ const altEvent = { which: utils.keyCodes.ALT, location: 1 };
88
+ expect(keyboard.keyCode(altEvent)).toBe(utils.keyCodes.ALT_LEFT);
89
+
90
+ const altEvent2 = { which: utils.keyCodes.ALT, location: 2 };
91
+ expect(keyboard.keyCode(altEvent2)).toBe(utils.keyCodes.ALT_RIGHT);
92
+
93
+ // Test numpad
94
+ const enterEvent = { which: utils.keyCodes.ENTER, location: 3 };
95
+ expect(keyboard.keyCode(enterEvent)).toBe(utils.keyCodes.NUMPADENTER);
96
+
97
+ const deleteEvent = { which: utils.keyCodes.DELETE, location: 3 };
98
+ expect(keyboard.keyCode(deleteEvent)).toBe(utils.keyCodes.NUMPAD_DECIMAL_POINT);
99
+
100
+ // Test normal key
101
+ const normalEvent = { which: utils.keyCodes.A, location: 0 };
102
+ expect(keyboard.keyCode(normalEvent)).toBe(utils.keyCodes.A);
103
+ });
104
+
105
+ test("keyCode should remember last modifier key locations", () => {
106
+ // First, set modifier locations to known values
107
+ keyboard.keyCode({ which: utils.keyCodes.SHIFT, location: 1 });
108
+ keyboard.keyCode({ which: utils.keyCodes.CTRL, location: 1 });
109
+ keyboard.keyCode({ which: utils.keyCodes.ALT, location: 1 });
110
+
111
+ // When location = 0 (like in keyUp events), should return based on last location
112
+ expect(keyboard.keyCode({ which: utils.keyCodes.SHIFT, location: 0 })).toBe(utils.keyCodes.SHIFT_LEFT);
113
+ expect(keyboard.keyCode({ which: utils.keyCodes.CTRL, location: 0 })).toBe(utils.keyCodes.CTRL_LEFT);
114
+ expect(keyboard.keyCode({ which: utils.keyCodes.ALT, location: 0 })).toBe(utils.keyCodes.ALT_LEFT);
115
+
116
+ // Change the locations to right side
117
+ keyboard.keyCode({ which: utils.keyCodes.SHIFT, location: 2 });
118
+ keyboard.keyCode({ which: utils.keyCodes.CTRL, location: 2 });
119
+ keyboard.keyCode({ which: utils.keyCodes.ALT, location: 2 });
120
+
121
+ // Should now use the updated locations
122
+ expect(keyboard.keyCode({ which: utils.keyCodes.SHIFT, location: 0 })).toBe(utils.keyCodes.SHIFT_RIGHT);
123
+ expect(keyboard.keyCode({ which: utils.keyCodes.CTRL, location: 0 })).toBe(utils.keyCodes.CTRL_RIGHT);
124
+ expect(keyboard.keyCode({ which: utils.keyCodes.ALT, location: 0 })).toBe(utils.keyCodes.ALT_RIGHT);
125
+ });
126
+
127
+ test("keyDown should handle normal key press", () => {
128
+ const event = {
129
+ which: utils.keyCodes.A,
130
+ location: 0,
131
+ preventDefault: vi.fn(),
132
+ ctrlKey: false,
133
+ altKey: false,
134
+ shiftKey: false,
135
+ };
136
+
137
+ keyboard.setRunning(true);
138
+ keyboard.keyDown(event);
139
+
140
+ expect(mockSysvia.keyDown).toHaveBeenCalledWith(utils.keyCodes.A, false);
141
+ expect(event.preventDefault).toHaveBeenCalled();
142
+ });
143
+
144
+ test("keyDown should not handle keys when not running", () => {
145
+ const event = {
146
+ which: utils.keyCodes.A,
147
+ location: 0,
148
+ preventDefault: vi.fn(),
149
+ ctrlKey: false,
150
+ altKey: false,
151
+ shiftKey: false,
152
+ };
153
+
154
+ keyboard.setRunning(false);
155
+ keyboard.keyDown(event);
156
+
157
+ expect(mockSysvia.keyDown).not.toHaveBeenCalled();
158
+ });
159
+
160
+ test("keyDown should not handle keys when input is enabled", () => {
161
+ const event = {
162
+ which: utils.keyCodes.A,
163
+ location: 0,
164
+ preventDefault: vi.fn(),
165
+ ctrlKey: false,
166
+ altKey: false,
167
+ shiftKey: false,
168
+ };
169
+
170
+ // Set input enabled to true
171
+ mockInputEnabledFunction.mockReturnValueOnce(true);
172
+
173
+ keyboard.setRunning(true);
174
+ keyboard.keyDown(event);
175
+
176
+ expect(mockInputEnabledFunction).toHaveBeenCalled();
177
+ expect(mockSysvia.keyDown).not.toHaveBeenCalled();
178
+ });
179
+
180
+ test("keyDown should handle F12/BREAK and emit break event", async () => {
181
+ const event = {
182
+ which: utils.keyCodes.F12,
183
+ location: 0,
184
+ preventDefault: vi.fn(),
185
+ ctrlKey: false,
186
+ altKey: false,
187
+ shiftKey: false,
188
+ };
189
+
190
+ keyboard.setRunning(true);
191
+
192
+ const [breakState] = await triggerAndWaitForEvent("break", () => {
193
+ keyboard.keyDown(event);
194
+ });
195
+
196
+ expect(mockProcessor.setReset).toHaveBeenCalledWith(true);
197
+ expect(event.preventDefault).toHaveBeenCalled();
198
+ expect(breakState).toBe(true);
199
+ });
200
+
201
+ test("keyUp should call sysvia.keyUp", () => {
202
+ const event = {
203
+ which: utils.keyCodes.A,
204
+ location: 0,
205
+ preventDefault: vi.fn(),
206
+ altKey: false,
207
+ };
208
+
209
+ keyboard.setRunning(true);
210
+ keyboard.keyUp(event);
211
+
212
+ expect(mockSysvia.keyUp).toHaveBeenCalledWith(utils.keyCodes.A);
213
+ expect(event.preventDefault).toHaveBeenCalled();
214
+ });
215
+
216
+ test("keyUp should not proceed when input is enabled", () => {
217
+ const event = {
218
+ which: utils.keyCodes.A,
219
+ location: 0,
220
+ preventDefault: vi.fn(),
221
+ altKey: false,
222
+ };
223
+
224
+ // Set input enabled to true
225
+ mockInputEnabledFunction.mockReturnValueOnce(true);
226
+
227
+ keyboard.setRunning(true);
228
+ keyboard.keyUp(event);
229
+
230
+ expect(mockInputEnabledFunction).toHaveBeenCalled();
231
+ expect(mockSysvia.keyUp).not.toHaveBeenCalled();
232
+ expect(event.preventDefault).not.toHaveBeenCalled();
233
+ });
234
+
235
+ test("keyUp should handle F12/BREAK and emit break event", async () => {
236
+ const event = {
237
+ which: utils.keyCodes.F12,
238
+ location: 0,
239
+ preventDefault: vi.fn(),
240
+ altKey: false,
241
+ };
242
+
243
+ keyboard.setRunning(true);
244
+
245
+ const [breakState] = await triggerAndWaitForEvent("break", () => {
246
+ keyboard.keyUp(event);
247
+ });
248
+
249
+ expect(mockProcessor.setReset).toHaveBeenCalledWith(false);
250
+ expect(event.preventDefault).toHaveBeenCalled();
251
+ expect(breakState).toBe(false);
252
+ });
253
+
254
+ test("clearKeys should call sysvia.clearKeys", () => {
255
+ keyboard.clearKeys();
256
+ expect(mockSysvia.clearKeys).toHaveBeenCalled();
257
+ });
258
+
259
+ test("setKeyLayout should update config and call processor to update layout", () => {
260
+ keyboard.setKeyLayout("gaming");
261
+ expect(mockSysvia.setKeyLayout).toHaveBeenCalledWith("gaming");
262
+ });
263
+
264
+ test("keyPress should not proceed when input is enabled", () => {
265
+ const event = {
266
+ which: 103, // lowercase g key
267
+ location: 0,
268
+ preventDefault: vi.fn(),
269
+ };
270
+
271
+ // Set input enabled to true
272
+ mockInputEnabledFunction.mockReturnValueOnce(true);
273
+
274
+ // Add a resume event listener to check it's not called
275
+ const resumeListener = vi.fn();
276
+ keyboard.on("resume", resumeListener);
277
+
278
+ keyboard.keyPress(event);
279
+
280
+ expect(mockInputEnabledFunction).toHaveBeenCalled();
281
+ // No events should be emitted when input is enabled
282
+ expect(resumeListener).not.toHaveBeenCalled();
283
+ });
284
+
285
+ test("keyPress should emit resume event when lowercase g pressed in pause mode", async () => {
286
+ const event = {
287
+ which: 103, // lowercase g key
288
+ location: 0,
289
+ preventDefault: vi.fn(),
290
+ };
291
+
292
+ keyboard.pauseEmu = true;
293
+
294
+ const eventPromise = waitForEvent("resume");
295
+ keyboard.keyPress(event);
296
+ await eventPromise;
297
+
298
+ expect(keyboard.pauseEmu).toBe(false);
299
+ });
300
+
301
+ test("keyPress should handle debugger g key and emit resume event", async () => {
302
+ const event = {
303
+ which: 103, // lowercase g key
304
+ location: 0,
305
+ preventDefault: vi.fn(),
306
+ };
307
+
308
+ // Mock debugger enabled
309
+ const mockDbgr = {
310
+ enabled: vi.fn().mockReturnValue(true),
311
+ keyPress: vi.fn(),
312
+ hide: vi.fn(),
313
+ };
314
+ keyboard.dbgr = mockDbgr;
315
+
316
+ const eventPromise = waitForEvent("resume");
317
+ keyboard.keyPress(event);
318
+ await eventPromise;
319
+
320
+ expect(mockDbgr.hide).toHaveBeenCalled();
321
+ });
322
+
323
+ test("registerKeyHandler should add a handler for a key with Alt modifier", () => {
324
+ const mockHandler = vi.fn();
325
+ keyboard.registerKeyHandler(utils.keyCodes.Q, mockHandler, { alt: true, ctrl: false });
326
+
327
+ const event = {
328
+ which: utils.keyCodes.Q,
329
+ location: 0,
330
+ preventDefault: vi.fn(),
331
+ ctrlKey: false,
332
+ altKey: true,
333
+ shiftKey: false,
334
+ };
335
+
336
+ keyboard.setRunning(true);
337
+ keyboard.keyDown(event);
338
+
339
+ expect(mockHandler).toHaveBeenCalledWith(true, utils.keyCodes.Q);
340
+ });
341
+
342
+ test("registerKeyHandler should add a handler for a key with Ctrl modifier", () => {
343
+ const mockHandler = vi.fn();
344
+ keyboard.registerKeyHandler(utils.keyCodes.E, mockHandler, { alt: false, ctrl: true });
345
+
346
+ const event = {
347
+ which: utils.keyCodes.E,
348
+ location: 0,
349
+ preventDefault: vi.fn(),
350
+ ctrlKey: true,
351
+ altKey: false,
352
+ shiftKey: false,
353
+ };
354
+
355
+ keyboard.setRunning(true);
356
+ keyboard.keyDown(event);
357
+
358
+ expect(mockHandler).toHaveBeenCalledWith(true, utils.keyCodes.E);
359
+ });
360
+
361
+ test("sendRawKeyboardToBBC should setup the keyboard input", () => {
362
+ keyboard.sendRawKeyboardToBBC([utils.BBC.A], false);
363
+
364
+ expect(mockSysvia.disableKeyboard).toHaveBeenCalled();
365
+ expect(mockProcessor.debugInstruction.add).toHaveBeenCalled();
366
+ });
367
+
368
+ test("postFrameShouldPause should handle single step", () => {
369
+ // Initially should not pause
370
+ expect(keyboard.postFrameShouldPause()).toBe(false);
371
+
372
+ // Set step to true
373
+ keyboard.stepEmuWhenPaused = true;
374
+
375
+ // Should pause and reset flag
376
+ expect(keyboard.postFrameShouldPause()).toBe(true);
377
+
378
+ // Flag should be reset
379
+ expect(keyboard.stepEmuWhenPaused).toBe(false);
380
+
381
+ // Subsequent call should not pause
382
+ expect(keyboard.postFrameShouldPause()).toBe(false);
383
+ });
384
+
385
+ test("requestStep should set the step flag", () => {
386
+ expect(keyboard.stepEmuWhenPaused).toBe(false);
387
+ keyboard.requestStep();
388
+ expect(keyboard.stepEmuWhenPaused).toBe(true);
389
+ });
390
+
391
+ test("pauseEmulation should set pause flag and emit pause event", async () => {
392
+ const eventPromise = waitForEvent("pause");
393
+ keyboard.pauseEmulation();
394
+ await eventPromise;
395
+
396
+ expect(keyboard.pauseEmu).toBe(true);
397
+ });
398
+
399
+ test("resumeEmulation should clear pause flag and emit resume event", async () => {
400
+ keyboard.pauseEmu = true;
401
+
402
+ const eventPromise = waitForEvent("resume");
403
+ keyboard.resumeEmulation();
404
+ await eventPromise;
405
+
406
+ expect(keyboard.pauseEmu).toBe(false);
407
+ });
408
+
409
+ // We don't test Mac-specific code as it requires global mocking
410
+ });
@@ -0,0 +1,128 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { MouseJoystickSource } from "../../src/mouse-joystick-source.js";
3
+
4
+ describe("MouseJoystickSource", () => {
5
+ let canvas, source, mockVia;
6
+
7
+ beforeEach(() => {
8
+ // Create a mock canvas element (simpler since we don't use event listeners)
9
+ canvas = {
10
+ addEventListener: vi.fn(),
11
+ removeEventListener: vi.fn(),
12
+ };
13
+
14
+ mockVia = {
15
+ setJoystickButton: vi.fn(),
16
+ };
17
+
18
+ source = new MouseJoystickSource(canvas);
19
+ });
20
+
21
+ afterEach(() => {
22
+ vi.restoreAllMocks();
23
+ });
24
+
25
+ it("should initialize with center position", () => {
26
+ expect(source.mouseX).toBe(0.5);
27
+ expect(source.mouseY).toBe(0.5);
28
+ expect(source.isActive).toBe(false);
29
+ });
30
+
31
+ it("should return center value initially", () => {
32
+ // Mouse starts at center position (0.5, 0.5)
33
+ // Math.floor((1 - 0.5) * 0xffff) = Math.floor(0.5 * 0xffff) = 32767
34
+ expect(source.getValue(0)).toBe(32767);
35
+ expect(source.getValue(1)).toBe(32767);
36
+ });
37
+
38
+ it("should handle mouse position tracking via API", () => {
39
+ // Use the API method to set mouse position
40
+ source.onMouseMove(0, 0); // top-left corner
41
+ expect(source.isActive).toBe(true);
42
+ expect(source.mouseX).toBe(0);
43
+ expect(source.mouseY).toBe(0);
44
+ expect(source.getValue(0)).toBe(0xffff); // X channel (left = max)
45
+ expect(source.getValue(1)).toBe(0xffff); // Y channel (top = max)
46
+
47
+ // Use the API method to move to bottom-right corner
48
+ source.onMouseMove(1, 1); // bottom-right corner
49
+ expect(source.mouseX).toBe(1);
50
+ expect(source.mouseY).toBe(1);
51
+ expect(source.getValue(0)).toBe(0); // X channel (right = min)
52
+ expect(source.getValue(1)).toBe(0); // Y channel (bottom = min)
53
+ });
54
+
55
+ it("should track active state via API", () => {
56
+ expect(source.isActive).toBe(false);
57
+
58
+ // Using the API method sets the source as active
59
+ source.onMouseMove(0.8, 0.3);
60
+ expect(source.isActive).toBe(true);
61
+ expect(source.mouseX).toBe(0.8);
62
+ expect(source.mouseY).toBe(0.3);
63
+ });
64
+
65
+ it("should handle fire button clicks via API", () => {
66
+ source.setVia(mockVia);
67
+
68
+ // Use API method for mouse button down
69
+ source.onMouseDown(0); // left button
70
+ expect(mockVia.setJoystickButton).toHaveBeenCalledWith(0, true);
71
+
72
+ // Use API method for mouse button up
73
+ source.onMouseUp(0); // left button
74
+ expect(mockVia.setJoystickButton).toHaveBeenCalledWith(0, false);
75
+ });
76
+
77
+ it("should ignore non-left mouse buttons via API", () => {
78
+ source.setVia(mockVia);
79
+
80
+ // Use API method with right button (button 2)
81
+ source.onMouseDown(2);
82
+ expect(mockVia.setJoystickButton).not.toHaveBeenCalled();
83
+ });
84
+
85
+ it("should return correct values for all channels", () => {
86
+ // Use API method to set position
87
+ source.onMouseMove(0.25, 0.75);
88
+
89
+ expect(source.getValue(0)).toBe(Math.floor((1 - 0.25) * 0xffff)); // X channel (inverted)
90
+ expect(source.getValue(1)).toBe(Math.floor((1 - 0.75) * 0xffff)); // Y channel (inverted)
91
+ expect(source.getValue(2)).toBe(0x8000); // Unused channel
92
+ expect(source.getValue(3)).toBe(0x8000); // Unused channel
93
+ expect(source.getValue(99)).toBe(0x8000); // Invalid channel
94
+ });
95
+
96
+ it("should validate input range via API", () => {
97
+ // Test boundary clamping in API method
98
+ source.onMouseMove(-0.5, 1.5); // Out of bounds values
99
+
100
+ // Should be clamped to valid range [0, 1]
101
+ expect(source.mouseX).toBe(0);
102
+ expect(source.mouseY).toBe(1);
103
+ expect(source.getValue(0)).toBe(0xffff); // X channel (left = max)
104
+ expect(source.getValue(1)).toBe(0); // Y channel (bottom = min)
105
+ });
106
+
107
+ it("should reset state on dispose", () => {
108
+ source.setVia(mockVia);
109
+ source.onMouseMove(0.8, 0.3);
110
+
111
+ source.dispose();
112
+
113
+ expect(source.via).toBe(null);
114
+ expect(source.isActive).toBe(false);
115
+ // No event listeners to remove since we don't register any
116
+ expect(canvas.removeEventListener).not.toHaveBeenCalled();
117
+ });
118
+
119
+ it("should report enabled state correctly", () => {
120
+ expect(source.isEnabled()).toBe(false);
121
+
122
+ source.setVia(mockVia);
123
+ expect(source.isEnabled()).toBe(true);
124
+
125
+ source.setVia(null);
126
+ expect(source.isEnabled()).toBe(false);
127
+ });
128
+ });