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
package/src/utils.js ADDED
@@ -0,0 +1,1090 @@
1
+ "use strict";
2
+ import { ungzip as pakoUngzip } from "pako";
3
+ import { unzipSync } from "fflate";
4
+
5
+ export const runningInNode = typeof window === "undefined";
6
+
7
+ export function isFirefox() {
8
+ // With thanks to http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
9
+ // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
10
+ return typeof InstallTrigger !== "undefined"; // Firefox 1.0+
11
+ }
12
+
13
+ export function parseAddr(s) {
14
+ if (s[0] === "$" || s[0] === "&") return parseInt(s.substring(1), 16);
15
+ if (s.indexOf("0x") === 0) return parseInt(s.substring(2), 16);
16
+ return parseInt(s, 16);
17
+ }
18
+
19
+ export const userKeymap = [];
20
+
21
+ export const BBC = {
22
+ SEMICOLON_PLUS: [7, 5],
23
+ MINUS: [7, 1],
24
+ LEFT_SQUARE_BRACKET: [8, 3],
25
+ RIGHT_SQUARE_BRACKET: [8, 5],
26
+ COMMA: [6, 6],
27
+ PERIOD: [7, 6],
28
+ SLASH: [8, 6],
29
+ SHIFTLOCK: [0, 5],
30
+ TAB: [0, 6],
31
+ RETURN: [9, 4],
32
+ DELETE: [9, 5],
33
+ COPY: [9, 6],
34
+ SHIFT: [0, 0],
35
+ ESCAPE: [0, 7],
36
+ CTRL: [1, 0],
37
+ CAPSLOCK: [0, 4],
38
+ LEFT: [9, 1],
39
+ UP: [9, 3],
40
+ RIGHT: [9, 7],
41
+ DOWN: [9, 2],
42
+ K0: [7, 2],
43
+ K1: [0, 3],
44
+ K2: [1, 3],
45
+ K3: [1, 1],
46
+ K4: [2, 1],
47
+ K5: [3, 1],
48
+ K6: [4, 3],
49
+ K7: [4, 2],
50
+ K8: [5, 1],
51
+ K9: [6, 2],
52
+
53
+ Q: [0, 1],
54
+ W: [1, 2],
55
+ E: [2, 2],
56
+ R: [3, 3],
57
+ T: [3, 2],
58
+ Y: [4, 4],
59
+ U: [5, 3],
60
+ I: [5, 2],
61
+ O: [6, 3],
62
+ P: [7, 3],
63
+
64
+ A: [1, 4],
65
+ S: [1, 5],
66
+ D: [2, 3],
67
+ F: [3, 4],
68
+ G: [3, 5],
69
+ H: [4, 5],
70
+ J: [5, 4],
71
+ K: [6, 4],
72
+ L: [6, 5],
73
+
74
+ Z: [1, 6],
75
+ X: [2, 4],
76
+ C: [2, 5],
77
+ V: [3, 6],
78
+ B: [4, 6],
79
+ N: [5, 5],
80
+ M: [5, 6],
81
+
82
+ F0: [0, 2],
83
+ F1: [1, 7],
84
+ F2: [2, 7],
85
+ F3: [3, 7],
86
+ F4: [4, 1],
87
+ F5: [4, 7],
88
+ F6: [5, 7],
89
+ F7: [6, 1],
90
+ F8: [6, 7],
91
+ F9: [7, 7],
92
+
93
+ SPACE: [2, 6],
94
+
95
+ UNDERSCORE_POUND: [8, 2],
96
+ AT: [7, 4],
97
+ COLON_STAR: [8, 4],
98
+ PIPE_BACKSLASH: [8, 7],
99
+ HAT_TILDE: [8, 1],
100
+
101
+ // row 1
102
+ NUMPADPLUS: [10, 3],
103
+ NUMPADMINUS: [11, 3],
104
+ NUMPADSLASH: [10, 4],
105
+ NUMPADASTERISK: [11, 5],
106
+
107
+ // row 2
108
+ NUMPAD7: [11, 1],
109
+ NUMPAD8: [10, 2],
110
+ NUMPAD9: [11, 2],
111
+ NUMPADHASH: [10, 5],
112
+ // row 3
113
+ NUMPAD4: [10, 7],
114
+ NUMPAD5: [11, 7],
115
+ NUMPAD6: [10, 1],
116
+ NUMPAD_DELETE: [11, 4],
117
+ //row4
118
+ NUMPAD1: [11, 6],
119
+ NUMPAD2: [12, 7],
120
+ NUMPAD3: [12, 6],
121
+ NUMPADCOMMA: [12, 5],
122
+
123
+ //row 5
124
+ NUMPAD0: [10, 6],
125
+ NUMPAD_DECIMAL_POINT: [12, 4],
126
+ NUMPADENTER: [12, 3],
127
+ };
128
+
129
+ export function stringToBBCKeys(str) {
130
+ const array = [];
131
+ let shiftState = false;
132
+ let capsLockState = true;
133
+ for (let i = 0; i < str.length; ++i) {
134
+ const c = str.charCodeAt(i);
135
+ let charStr = str.charAt(i);
136
+ let bbcKey = null;
137
+ let needsShift = false;
138
+ let needsCapsLock = true;
139
+ if (c >= 65 && c <= 90) {
140
+ // A-Z
141
+ bbcKey = BBC[charStr];
142
+ } else if (c >= 97 && c <= 122) {
143
+ // a-z
144
+ charStr = String.fromCharCode(c - 32);
145
+ bbcKey = BBC[charStr];
146
+ needsCapsLock = false;
147
+ } else if (c >= 48 && c <= 57) {
148
+ // 0-9
149
+ bbcKey = BBC["K" + charStr];
150
+ } else if (c >= 33 && c <= 41) {
151
+ // ! to )
152
+ charStr = String.fromCharCode(c + 16);
153
+ bbcKey = BBC["K" + charStr];
154
+ needsShift = true;
155
+ } else {
156
+ switch (charStr) {
157
+ case "\n":
158
+ bbcKey = BBC.RETURN;
159
+ break;
160
+ case "\t":
161
+ bbcKey = BBC.TAB;
162
+ break;
163
+ case " ":
164
+ bbcKey = BBC.SPACE;
165
+ break;
166
+ case "-":
167
+ bbcKey = BBC.MINUS;
168
+ break;
169
+ case "=":
170
+ bbcKey = BBC.MINUS;
171
+ needsShift = true;
172
+ break;
173
+ case "^":
174
+ bbcKey = BBC.HAT_TILDE;
175
+ break;
176
+ case "~":
177
+ bbcKey = BBC.HAT_TILDE;
178
+ needsShift = true;
179
+ break;
180
+ case "\\":
181
+ bbcKey = BBC.PIPE_BACKSLASH;
182
+ break;
183
+ case "|":
184
+ bbcKey = BBC.PIPE_BACKSLASH;
185
+ needsShift = true;
186
+ break;
187
+ case "@":
188
+ bbcKey = BBC.AT;
189
+ break;
190
+ case "[":
191
+ bbcKey = BBC.LEFT_SQUARE_BRACKET;
192
+ break;
193
+ case "{":
194
+ bbcKey = BBC.LEFT_SQUARE_BRACKET;
195
+ needsShift = true;
196
+ break;
197
+ case "_":
198
+ bbcKey = BBC.UNDERSCORE_POUND;
199
+ break;
200
+ case ";":
201
+ bbcKey = BBC.SEMICOLON_PLUS;
202
+ break;
203
+ case "+":
204
+ bbcKey = BBC.SEMICOLON_PLUS;
205
+ needsShift = true;
206
+ break;
207
+ case ":":
208
+ bbcKey = BBC.COLON_STAR;
209
+ break;
210
+ case "*":
211
+ bbcKey = BBC.COLON_STAR;
212
+ needsShift = true;
213
+ break;
214
+ case "]":
215
+ bbcKey = BBC.RIGHT_SQUARE_BRACKET;
216
+ break;
217
+ case "}":
218
+ bbcKey = BBC.RIGHT_SQUARE_BRACKET;
219
+ needsShift = true;
220
+ break;
221
+ case ",":
222
+ bbcKey = BBC.COMMA;
223
+ break;
224
+ case "<":
225
+ bbcKey = BBC.COMMA;
226
+ needsShift = true;
227
+ break;
228
+ case ".":
229
+ bbcKey = BBC.PERIOD;
230
+ break;
231
+ case ">":
232
+ bbcKey = BBC.PERIOD;
233
+ needsShift = true;
234
+ break;
235
+ case "/":
236
+ bbcKey = BBC.SLASH;
237
+ break;
238
+ case "?":
239
+ bbcKey = BBC.SLASH;
240
+ needsShift = true;
241
+ break;
242
+ }
243
+ }
244
+
245
+ if (!bbcKey) continue;
246
+
247
+ if ((needsShift && !shiftState) || (!needsShift && shiftState)) {
248
+ array.push(BBC.SHIFT);
249
+ shiftState = !shiftState;
250
+ }
251
+ if ((needsCapsLock && !capsLockState) || (!needsCapsLock && capsLockState)) {
252
+ array.push(BBC.CAPSLOCK);
253
+ capsLockState = !capsLockState;
254
+ }
255
+ array.push(bbcKey);
256
+ }
257
+
258
+ if (shiftState) array.push(BBC.SHIFT);
259
+ if (!capsLockState) array.push(BBC.CAPSLOCK);
260
+ return array;
261
+ }
262
+
263
+ /**
264
+ * Useful references:
265
+ * http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
266
+ * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode
267
+ */
268
+ export const keyCodes = {
269
+ UNDEFINED: 0,
270
+ BACKSPACE: 8,
271
+ TAB: 9,
272
+ CLEAR: 12,
273
+ ENTER: 13,
274
+ SHIFT: 16,
275
+ CTRL: 17,
276
+ ALT: 18,
277
+ BREAK: 19,
278
+ CAPSLOCK: 20,
279
+ ESCAPE: 27,
280
+ SPACE: 32,
281
+ PAGEUP: 33,
282
+ PAGEDOWN: 34,
283
+ END: 35,
284
+ HOME: 36,
285
+ LEFT: 37,
286
+ UP: 38,
287
+ RIGHT: 39,
288
+ DOWN: 40,
289
+ PRINTSCREEN: 44,
290
+ INSERT: 45,
291
+ DELETE: 46,
292
+ K0: 48,
293
+ K1: 49,
294
+ K2: 50,
295
+ K3: 51,
296
+ K4: 52,
297
+ K5: 53,
298
+ K6: 54,
299
+ K7: 55,
300
+ K8: 56,
301
+ K9: 57,
302
+ A: 65,
303
+ B: 66,
304
+ C: 67,
305
+ D: 68,
306
+ E: 69,
307
+ F: 70,
308
+ G: 71,
309
+ H: 72,
310
+ I: 73,
311
+ J: 74,
312
+ K: 75,
313
+ L: 76,
314
+ M: 77,
315
+ N: 78,
316
+ O: 79,
317
+ P: 80,
318
+ Q: 81,
319
+ R: 82,
320
+ S: 83,
321
+ T: 84,
322
+ U: 85,
323
+ V: 86,
324
+ W: 87,
325
+ X: 88,
326
+ Y: 89,
327
+ Z: 90,
328
+ /* also META on Mac */
329
+ WINDOWS: 91,
330
+ MENU: 93,
331
+ NUMPAD0: 96,
332
+ NUMPAD1: 97,
333
+ NUMPAD2: 98,
334
+ NUMPAD3: 99,
335
+ NUMPAD4: 100,
336
+ NUMPAD5: 101,
337
+ NUMPAD6: 102,
338
+ NUMPAD7: 103,
339
+ NUMPAD8: 104,
340
+ NUMPAD9: 105,
341
+ NUMPADASTERISK: 106,
342
+ NUMPADPLUS: 107,
343
+ /* on numeric keypad in eg Germany*/
344
+ NUMPAD_DECIMAL_COMMA: 108,
345
+ NUMPADMINUS: 109,
346
+ /* on numeric keypad */
347
+ NUMPAD_DECIMAL_POINT: 110,
348
+ NUMPADSLASH: 111,
349
+ F1: 112,
350
+ F2: 113,
351
+ F3: 114,
352
+ F4: 115,
353
+ F5: 116,
354
+ F6: 117,
355
+ F7: 118,
356
+ F8: 119,
357
+ F9: 120,
358
+ F10: 121,
359
+ F11: 122,
360
+ F12: 123,
361
+ NUMLOCK: 144,
362
+ SCROLL_LOCK: 145,
363
+ VOLUMEUP: 174,
364
+ VOLUMEDOWN: 175,
365
+ FASTFORWARD: 176,
366
+ FASTREWIND: 177,
367
+ PLAYPAUSE: 179,
368
+ COMMA: 188,
369
+ PERIOD: 190,
370
+ SLASH: 191,
371
+ LEFT_SQUARE_BRACKET: 219,
372
+ BACKSLASH: 220,
373
+ RIGHT_SQUARE_BRACKET: 221,
374
+ NUMPADENTER: 255, // hack, jsbeeb only
375
+ SHIFT_LEFT: 256, // hack, jsbeeb only
376
+ SHIFT_RIGHT: 257, // hack, jsbeeb only
377
+ ALT_LEFT: 258, // hack, jsbeeb only
378
+ ALT_RIGHT: 259, // hack, jsbeeb only
379
+ CTRL_LEFT: 260, // hack, jsbeeb only
380
+ CTRL_RIGHT: 261, // hack, jsbeeb only
381
+ };
382
+
383
+ function detectKeyboardLayout() {
384
+ if (runningInNode) {
385
+ return "UK";
386
+ }
387
+ if (localStorage.keyboardLayout) {
388
+ return localStorage.keyboardLayout === "US" ? "US" : "UK";
389
+ }
390
+ if (navigator.language) {
391
+ if (navigator.language.toLowerCase() === "en-gb") return "UK";
392
+ if (navigator.language.toLowerCase() === "en-us") return "US";
393
+ }
394
+ return "UK"; // Default guess of UK
395
+ }
396
+
397
+ const isUKlayout = detectKeyboardLayout() === "UK";
398
+
399
+ if (isFirefox()) {
400
+ keyCodes.SEMICOLON = 59;
401
+ // #~ key (not on US keyboard)
402
+ keyCodes.HASH = 163;
403
+ keyCodes.APOSTROPHE = 222;
404
+ keyCodes.BACK_QUOTE = 192;
405
+ // Firefox doesn't return a keycode for this
406
+ keyCodes.MUTE = -1;
407
+ keyCodes.MINUS = 173;
408
+ keyCodes.EQUALS = 61;
409
+ } else {
410
+ // Chrome
411
+ // TODO: check other browsers
412
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode
413
+ keyCodes.SEMICOLON = 186;
414
+ // #~ key (not on US keyboard)
415
+ keyCodes.HASH = isUKlayout ? 222 : 223;
416
+ keyCodes.APOSTROPHE = isUKlayout ? 192 : 222;
417
+ keyCodes.MUTE = 173;
418
+ keyCodes.MINUS = 189;
419
+ keyCodes.EQUALS = 187;
420
+ keyCodes.BACK_QUOTE = isUKlayout ? 223 : 192;
421
+ }
422
+
423
+ // Swap APOSTROPHE and BACK_QUOTE keys around for Mac users. They are the opposite to what jsbeeb expects.
424
+ // Swap them to what jsbeeb expects, and tidy up the hash key to prevent duplicate key mappings.
425
+ if (!runningInNode && window.navigator.userAgent.indexOf("Mac") !== -1) {
426
+ keyCodes.BACK_QUOTE = 192;
427
+ keyCodes.APOSTROPHE = 222;
428
+ keyCodes.HASH = 223;
429
+ }
430
+
431
+ export function getKeyMap(keyLayout) {
432
+ const keys2 = [];
433
+
434
+ // shift pressed
435
+ keys2[true] = {};
436
+
437
+ // shift not pressed
438
+ keys2[false] = {};
439
+
440
+ // shiftDown MUST be true or false (not undefined)
441
+ function doMap(s, colRow, shiftDown) {
442
+ if (keys2[shiftDown][s] && keys2[shiftDown][s] !== colRow) {
443
+ console.log(
444
+ "Warning: duplicate binding for key",
445
+ (shiftDown ? "<SHIFT>" : "") + s,
446
+ colRow,
447
+ keys2[shiftDown][s],
448
+ );
449
+ }
450
+ keys2[shiftDown][s] = colRow;
451
+ }
452
+
453
+ // shiftDown undefined -> map both
454
+ function map(s, colRow, shiftDown) {
455
+ if ((!s && s !== 0) || !colRow) {
456
+ console.log("error binding key", s, colRow);
457
+ }
458
+ if (typeof s === "string") {
459
+ s = s.charCodeAt(0);
460
+ }
461
+
462
+ if (shiftDown === undefined) {
463
+ doMap(s, colRow, true);
464
+ doMap(s, colRow, false);
465
+ } else {
466
+ doMap(s, colRow, shiftDown);
467
+ }
468
+ }
469
+
470
+ map(keyCodes.Q, BBC.Q);
471
+ map(keyCodes.W, BBC.W);
472
+ map(keyCodes.E, BBC.E);
473
+ map(keyCodes.R, BBC.R);
474
+ map(keyCodes.T, BBC.T);
475
+ map(keyCodes.Y, BBC.Y);
476
+ map(keyCodes.U, BBC.U);
477
+ map(keyCodes.I, BBC.I);
478
+ map(keyCodes.O, BBC.O);
479
+ map(keyCodes.P, BBC.P);
480
+
481
+ map(keyCodes.A, BBC.A);
482
+ map(keyCodes.S, BBC.S);
483
+ map(keyCodes.D, BBC.D);
484
+ map(keyCodes.F, BBC.F);
485
+ map(keyCodes.G, BBC.G);
486
+ map(keyCodes.H, BBC.H);
487
+ map(keyCodes.J, BBC.J);
488
+ map(keyCodes.K, BBC.K);
489
+ map(keyCodes.L, BBC.L);
490
+
491
+ map(keyCodes.Z, BBC.Z);
492
+ map(keyCodes.X, BBC.X);
493
+ map(keyCodes.C, BBC.C);
494
+ map(keyCodes.V, BBC.V);
495
+ map(keyCodes.B, BBC.B);
496
+ map(keyCodes.N, BBC.N);
497
+ map(keyCodes.M, BBC.M);
498
+
499
+ map(keyCodes.F10, BBC.F0); // F0 (mapped to F10)
500
+ map(keyCodes.F1, BBC.F1);
501
+ map(keyCodes.F2, BBC.F2);
502
+ map(keyCodes.F3, BBC.F3);
503
+ map(keyCodes.F4, BBC.F4);
504
+ map(keyCodes.F5, BBC.F5);
505
+ map(keyCodes.F6, BBC.F6);
506
+ map(keyCodes.F7, BBC.F7);
507
+ map(keyCodes.F8, BBC.F8);
508
+ map(keyCodes.F9, BBC.F9);
509
+
510
+ // these keys are in the same place on PC and BBC keyboards
511
+ // including shifted characters
512
+ // so can be the same for "natural" and "gaming"
513
+ map(keyCodes.COMMA, BBC.COMMA);
514
+ map(keyCodes.PERIOD, BBC.PERIOD);
515
+ map(keyCodes.SLASH, BBC.SLASH);
516
+ map(keyCodes.SPACE, BBC.SPACE);
517
+ map(keyCodes.TAB, BBC.TAB);
518
+ map(keyCodes.ENTER, BBC.RETURN);
519
+
520
+ map(keyCodes.SHIFT, BBC.SHIFT);
521
+ // see later map(keyCodes.SHIFT_LEFT, BBC.SHIFT_LEFT);
522
+ map(keyCodes.SHIFT_RIGHT, BBC.SHIFT);
523
+
524
+ // other keys to map to these in "game" layout too
525
+ map(keyCodes.LEFT, BBC.LEFT);
526
+ map(keyCodes.UP, BBC.UP);
527
+ map(keyCodes.RIGHT, BBC.RIGHT);
528
+ map(keyCodes.DOWN, BBC.DOWN);
529
+
530
+ if (keyLayout === "natural") {
531
+ // "natural" keyboard
532
+
533
+ map(keyCodes.SHIFT_LEFT, BBC.SHIFT);
534
+
535
+ // US Keyboard: has Tilde on <Shift>BACK_QUOTE
536
+ map(keyCodes.BACK_QUOTE, isUKlayout ? BBC.UNDERSCORE_POUND : BBC.HAT_TILDE);
537
+ map(keyCodes.APOSTROPHE, isUKlayout ? BBC.AT : BBC.K2, true);
538
+ map(keyCodes.K2, isUKlayout ? BBC.K2 : BBC.AT, true);
539
+
540
+ // 1st row
541
+ map(keyCodes.K3, BBC.UNDERSCORE_POUND, true);
542
+ map(keyCodes.K7, BBC.K6, true);
543
+ map(keyCodes.K8, BBC.COLON_STAR, true);
544
+ map(keyCodes.K9, BBC.K8, true);
545
+ map(keyCodes.K0, BBC.K9, true);
546
+
547
+ map(keyCodes.K2, BBC.K2, false);
548
+ map(keyCodes.K3, BBC.K3, false);
549
+ map(keyCodes.K7, BBC.K7, false);
550
+ map(keyCodes.K8, BBC.K8, false);
551
+ map(keyCodes.K9, BBC.K9, false);
552
+ map(keyCodes.K0, BBC.K0, false);
553
+
554
+ map(keyCodes.K1, BBC.K1);
555
+ map(keyCodes.K4, BBC.K4);
556
+ map(keyCodes.K5, BBC.K5);
557
+ map(keyCodes.K6, BBC.K6);
558
+
559
+ map(keyCodes.MINUS, BBC.MINUS);
560
+
561
+ // 2nd row
562
+ map(keyCodes.LEFT_SQUARE_BRACKET, BBC.LEFT_SQUARE_BRACKET);
563
+
564
+ map(keyCodes.RIGHT_SQUARE_BRACKET, BBC.RIGHT_SQUARE_BRACKET);
565
+
566
+ // 3rd row
567
+
568
+ map(keyCodes.SEMICOLON, BBC.SEMICOLON_PLUS);
569
+
570
+ map(keyCodes.APOSTROPHE, BBC.COLON_STAR, false);
571
+
572
+ map(keyCodes.HASH, BBC.HAT_TILDE); // OK for <Shift> at least
573
+
574
+ map(keyCodes.EQUALS, BBC.SEMICOLON_PLUS); // OK for <Shift> at least
575
+
576
+ map(keyCodes.WINDOWS, BBC.SHIFTLOCK);
577
+
578
+ map(keyCodes.END, BBC.COPY);
579
+
580
+ map(keyCodes.F11, BBC.COPY);
581
+
582
+ map(keyCodes.ESCAPE, BBC.ESCAPE);
583
+
584
+ map(keyCodes.CTRL, BBC.CTRL);
585
+ map(keyCodes.CTRL_LEFT, BBC.CTRL);
586
+ map(keyCodes.CTRL_RIGHT, BBC.CTRL);
587
+
588
+ map(keyCodes.CAPSLOCK, BBC.CAPSLOCK);
589
+
590
+ map(keyCodes.DELETE, BBC.DELETE);
591
+
592
+ map(keyCodes.BACKSPACE, BBC.DELETE);
593
+
594
+ map(keyCodes.BACKSLASH, BBC.PIPE_BACKSLASH);
595
+ } else if (keyLayout === "gaming") {
596
+ // gaming keyboard
597
+
598
+ // 1st row
599
+ map(keyCodes.ESCAPE, BBC.F0);
600
+
601
+ // 2nd row
602
+ map(keyCodes.BACK_QUOTE, BBC.ESCAPE);
603
+ map(keyCodes.K1, BBC.K1);
604
+ map(keyCodes.K2, BBC.K2);
605
+ map(keyCodes.K3, BBC.K3);
606
+ map(keyCodes.K4, BBC.K4);
607
+ map(keyCodes.K5, BBC.K5);
608
+ map(keyCodes.K6, BBC.K6);
609
+ map(keyCodes.K7, BBC.K7);
610
+ map(keyCodes.K8, BBC.K8);
611
+ map(keyCodes.K9, BBC.K9);
612
+ map(keyCodes.K0, BBC.K0);
613
+ map(keyCodes.MINUS, BBC.MINUS);
614
+ map(keyCodes.EQUALS, BBC.HAT_TILDE);
615
+ map(keyCodes.BACKSPACE, BBC.PIPE_BACKSLASH);
616
+ map(keyCodes.INSERT, BBC.LEFT);
617
+ map(keyCodes.HOME, BBC.RIGHT);
618
+
619
+ // 3rd row
620
+ map(keyCodes.LEFT_SQUARE_BRACKET, BBC.AT);
621
+ map(keyCodes.RIGHT_SQUARE_BRACKET, BBC.LEFT_SQUARE_BRACKET);
622
+ // no key for BBC.UNDERSCORE_POUND in UK
623
+ // see 4th row for US mapping keyCodes.BACKSLASH
624
+ map(keyCodes.DELETE, BBC.UP);
625
+ map(keyCodes.END, BBC.DOWN);
626
+
627
+ // 4th row
628
+ // no key for BBC.CAPSLOCK (mapped to CTRL_LEFT below)
629
+ map(keyCodes.CAPSLOCK, BBC.CTRL);
630
+ map(keyCodes.SEMICOLON, BBC.SEMICOLON_PLUS);
631
+ map(keyCodes.APOSTROPHE, BBC.COLON_STAR);
632
+ // UK keyboard (key missing on US)
633
+ map(keyCodes.HASH, BBC.RIGHT_SQUARE_BRACKET);
634
+
635
+ // UK has extra key \| for SHIFT
636
+ map(keyCodes.SHIFT_LEFT, isUKlayout ? BBC.SHIFTLOCK : BBC.SHIFT);
637
+ // UK: key is between SHIFT and Z
638
+ // US: key is above ENTER
639
+ map(keyCodes.BACKSLASH, isUKlayout ? BBC.SHIFT : BBC.UNDERSCORE_POUND);
640
+
641
+ // 5th row
642
+
643
+ // for Zalaga
644
+ map(keyCodes.CTRL_LEFT, BBC.CAPSLOCK);
645
+ map(keyCodes.ALT_LEFT, BBC.CTRL);
646
+
647
+ // should be 4th row, not enough keys
648
+ map(keyCodes.MENU, BBC.DELETE);
649
+ map(keyCodes.CTRL_RIGHT, BBC.COPY);
650
+
651
+ // not in correct location
652
+ map(keyCodes.ALT_RIGHT, BBC.SHIFTLOCK);
653
+ map(keyCodes.WINDOWS, BBC.SHIFTLOCK);
654
+ } else {
655
+ // Physical, and default
656
+ map(keyCodes.K1, BBC.K1);
657
+ map(keyCodes.K2, BBC.K2);
658
+ map(keyCodes.K3, BBC.K3);
659
+ map(keyCodes.K4, BBC.K4);
660
+ map(keyCodes.K5, BBC.K5);
661
+ map(keyCodes.K6, BBC.K6);
662
+ map(keyCodes.K7, BBC.K7);
663
+ map(keyCodes.K8, BBC.K8);
664
+ map(keyCodes.K9, BBC.K9);
665
+ map(keyCodes.K0, BBC.K0);
666
+ map(keyCodes.SHIFT_LEFT, BBC.SHIFT);
667
+ map(keyCodes.EQUALS, BBC.HAT_TILDE); // ^~ on +/=
668
+ map(keyCodes.SEMICOLON, BBC.SEMICOLON_PLUS); // ';' / '+'
669
+ map(keyCodes.MINUS, BBC.MINUS); // '-' / '=' mapped to underscore
670
+ map(keyCodes.LEFT_SQUARE_BRACKET, BBC.LEFT_SQUARE_BRACKET); // maps to [{
671
+ map(keyCodes.RIGHT_SQUARE_BRACKET, BBC.RIGHT_SQUARE_BRACKET); // maps to ]}
672
+ map(keyCodes.COMMA, BBC.COMMA); // ',' / '<'
673
+ map(keyCodes.PERIOD, BBC.PERIOD); // '.' / '>'
674
+ map(keyCodes.SLASH, BBC.SLASH); // '/' / '?'
675
+ map(keyCodes.WINDOWS, BBC.SHIFTLOCK); // shift lock mapped to "windows" key
676
+ map(keyCodes.TAB, BBC.TAB); // tab
677
+ map(keyCodes.ENTER, BBC.RETURN); // return
678
+ map(keyCodes.DELETE, BBC.DELETE); // delete
679
+ map(keyCodes.BACKSPACE, BBC.DELETE); // delete
680
+ map(keyCodes.END, BBC.COPY); // copy key is end
681
+ map(keyCodes.F11, BBC.COPY); // copy key is end for Apple
682
+ map(keyCodes.SHIFT, BBC.SHIFT); // shift
683
+ map(keyCodes.ESCAPE, BBC.ESCAPE); // escape
684
+ map(keyCodes.CTRL, BBC.CTRL);
685
+ map(keyCodes.CTRL_LEFT, BBC.CTRL);
686
+ map(keyCodes.CTRL_RIGHT, BBC.CTRL);
687
+ map(keyCodes.CAPSLOCK, BBC.CAPSLOCK); // caps (on Rich's/Mike's computer)
688
+ map(keyCodes.LEFT, BBC.LEFT); // arrow left
689
+ map(keyCodes.UP, BBC.UP); // arrow up
690
+ map(keyCodes.RIGHT, BBC.RIGHT); // arrow right
691
+ map(keyCodes.DOWN, BBC.DOWN); // arrow down
692
+ map(keyCodes.APOSTROPHE, BBC.COLON_STAR);
693
+ map(keyCodes.HASH, BBC.RIGHT_SQUARE_BRACKET);
694
+
695
+ // None of this last group in great locations.
696
+ // But better to have them mapped at least somewhere.
697
+ map(keyCodes.BACK_QUOTE, BBC.AT);
698
+ map(keyCodes.BACKSLASH, BBC.PIPE_BACKSLASH);
699
+ map(keyCodes.PAGEUP, BBC.UNDERSCORE_POUND);
700
+ }
701
+
702
+ // Master
703
+ map(keyCodes.NUMPAD0, BBC.NUMPAD0);
704
+ map(keyCodes.NUMPAD1, BBC.NUMPAD1);
705
+ map(keyCodes.NUMPAD2, BBC.NUMPAD2);
706
+ map(keyCodes.NUMPAD3, BBC.NUMPAD3);
707
+ map(keyCodes.NUMPAD4, BBC.NUMPAD4);
708
+ map(keyCodes.NUMPAD5, BBC.NUMPAD5);
709
+ map(keyCodes.NUMPAD6, BBC.NUMPAD6);
710
+ map(keyCodes.NUMPAD7, BBC.NUMPAD7);
711
+ map(keyCodes.NUMPAD8, BBC.NUMPAD8);
712
+ map(keyCodes.NUMPAD9, BBC.NUMPAD9);
713
+ // small hack in main.js/keyCode() to make this work
714
+ map(keyCodes.NUMPAD_DECIMAL_POINT, BBC.NUMPAD_DECIMAL_POINT);
715
+
716
+ // "natural" mapping
717
+ map(keyCodes.NUMPADPLUS, BBC.NUMPADPLUS);
718
+ map(keyCodes.NUMPADMINUS, BBC.NUMPADMINUS);
719
+ map(keyCodes.NUMPADSLASH, BBC.NUMPADSLASH);
720
+ map(keyCodes.NUMPADASTERISK, BBC.NUMPADASTERISK);
721
+ //map(???, BBC.NUMPADCOMMA);
722
+ //map(???, BBC.NUMPADHASH);
723
+ // no keycode for NUMPADENTER, small hack in main.js/keyCode()
724
+ map(keyCodes.NUMPADENTER, BBC.NUMPADENTER);
725
+
726
+ // TODO: "game" mapping
727
+ // eg Master Dunjunz needs # Del 3 , * Enter
728
+ // https://web.archive.org/web/20080305042238/http://bbc.nvg.org/doc/games/Dunjunz-docs.txt
729
+
730
+ // user keymapping
731
+ // do last (to override defaults)
732
+ while (userKeymap.length > 0) {
733
+ const mapping = userKeymap.pop();
734
+ map(keyCodes[mapping.native], BBC[mapping.bbc]);
735
+ }
736
+
737
+ return keys2;
738
+ }
739
+
740
+ export function hexbyte(value) {
741
+ return ((value >>> 4) & 0xf).toString(16) + (value & 0xf).toString(16);
742
+ }
743
+
744
+ export function hexword(value) {
745
+ return hexbyte(value >>> 8) + hexbyte(value & 0xff);
746
+ }
747
+
748
+ export function hd(reader, start, end, opts) {
749
+ opts = opts || {};
750
+ const width = opts.width || 16;
751
+ const gap = opts.gap === undefined ? 8 : opts.gap;
752
+ const res = [];
753
+ let str = "";
754
+ let j = 0;
755
+ for (let i = start; i < end; ++i) {
756
+ str += " ";
757
+ str += hexbyte(reader(i));
758
+ if (++j === gap) str += " ";
759
+ if (j === width) {
760
+ res.push(str);
761
+ str = "";
762
+ j = 0;
763
+ }
764
+ }
765
+ if (str) res.push(str);
766
+ let joined = "";
767
+ for (let i = 0; i < res.length; ++i) {
768
+ joined += hexword(start + i * width) + " :" + res[i] + "\n";
769
+ }
770
+ return joined;
771
+ }
772
+
773
+ export function signExtend(val) {
774
+ return val >= 128 ? val - 256 : val;
775
+ }
776
+
777
+ export function noop() {}
778
+
779
+ export function bench() {
780
+ for (let j = 0; j < 10; ++j) {
781
+ let res = 0;
782
+ const start = Date.now();
783
+ for (let i = 0; i < 4096 * 1024; ++i) {
784
+ res += signExtend(i & 0xff);
785
+ }
786
+ const tt = Date.now() - start;
787
+ console.log(res, tt);
788
+ }
789
+ }
790
+
791
+ export function noteEvent(category, type, label) {
792
+ if (
793
+ !runningInNode &&
794
+ (window.location.host.endsWith(".godbolt.org") || window.location.host.endsWith(".xania.org"))
795
+ ) {
796
+ // Only note events on the public site
797
+ /*global gtag*/
798
+ gtag("event", category, { type, label });
799
+ }
800
+ console.log("event noted:", category, type, label);
801
+ }
802
+
803
+ let baseUrl = "";
804
+
805
+ export function setBaseUrl(url) {
806
+ baseUrl = url;
807
+ }
808
+
809
+ export function uint8ArrayToString(array) {
810
+ let str = "";
811
+ for (let i = 0; i < array.length; ++i) str += String.fromCharCode(array[i]);
812
+ return str;
813
+ }
814
+
815
+ export function stringToUint8Array(str) {
816
+ if (str instanceof Uint8Array) return str;
817
+ const len = str.length;
818
+ const array = new Uint8Array(len);
819
+ for (let i = 0; i < len; ++i) array[i] = str.charCodeAt(i) & 0xff;
820
+ return array;
821
+ }
822
+
823
+ function loadDataHttp(url) {
824
+ return new Promise(function (resolve, reject) {
825
+ const request = new XMLHttpRequest();
826
+ request.open("GET", baseUrl + url, true);
827
+ request.overrideMimeType("text/plain; charset=x-user-defined");
828
+ request.onload = function () {
829
+ if (request.status !== 200) reject(new Error("Unable to load " + url + ", http code " + request.status));
830
+ if (typeof request.response !== "string") {
831
+ resolve(request.response);
832
+ } else {
833
+ resolve(stringToUint8Array(request.response));
834
+ }
835
+ };
836
+ request.onerror = function () {
837
+ reject(new Error("A network error occurred loading " + url));
838
+ };
839
+ request.send(null);
840
+ });
841
+ }
842
+
843
+ async function loadDataNode(url) {
844
+ if (typeof readbuffer !== "undefined") {
845
+ // d8 shell
846
+ /*global readbuffer*/
847
+ return new Uint8Array(readbuffer(url));
848
+ } else if (typeof read !== "undefined") {
849
+ // SpiderMonkey shell
850
+ /*global read*/
851
+ return read(url, "binary");
852
+ } else {
853
+ // Node
854
+ const fs = await import("fs");
855
+ if (url[0] === "/") url = "." + url;
856
+ if (fs.existsSync("public/" + url)) return fs.readFileSync("public/" + url);
857
+ return fs.readFileSync(url);
858
+ }
859
+ }
860
+
861
+ export function loadData(url) {
862
+ if (runningInNode) {
863
+ return loadDataNode(url);
864
+ } else {
865
+ return loadDataHttp(url);
866
+ }
867
+ }
868
+
869
+ export function readInt32(data, offset) {
870
+ return (data[offset + 3] << 24) | (data[offset + 2] << 16) | (data[offset + 1] << 8) | data[offset + 0];
871
+ }
872
+
873
+ export function readInt16(data, offset) {
874
+ return (data[offset + 1] << 8) | data[offset + 0];
875
+ }
876
+
877
+ const tempBuf = new ArrayBuffer(4);
878
+ const tempBuf8 = new Uint8Array(tempBuf);
879
+ const tempBufF32 = new Float32Array(tempBuf);
880
+
881
+ export function readFloat32(data, offset) {
882
+ tempBuf8[0] = data[offset];
883
+ tempBuf8[1] = data[offset + 1];
884
+ tempBuf8[2] = data[offset + 2];
885
+ tempBuf8[3] = data[offset + 3];
886
+ return tempBufF32[0];
887
+ }
888
+
889
+ export function ungzip(data) {
890
+ try {
891
+ return pakoUngzip(data);
892
+ } catch (e) {
893
+ throw new Error("Unable to ungzip: " + e.message);
894
+ }
895
+ }
896
+
897
+ export class DataStream {
898
+ constructor(name, data, dontUnzip) {
899
+ this.name = name;
900
+ this.pos = 0;
901
+ this.data = stringToUint8Array(data);
902
+ if (!dontUnzip && this.data && this.data.length > 4 && this.data[0] === 0x1f && this.data[1] === 0x8b) {
903
+ console.log("Ungzipping " + name);
904
+ this.data = ungzip(this.data);
905
+ }
906
+ if (!this.data) {
907
+ throw new Error("No data in " + name);
908
+ }
909
+ this.end = this.data.length;
910
+ }
911
+
912
+ bytesLeft() {
913
+ return this.end - this.pos;
914
+ }
915
+
916
+ eof() {
917
+ return this.bytesLeft() === 0;
918
+ }
919
+
920
+ advance(distance) {
921
+ if (this.bytesLeft() < distance) throw new RangeError("EOF in " + this.name);
922
+ this.pos += distance;
923
+ return this.pos - distance;
924
+ }
925
+
926
+ readFloat32(pos) {
927
+ if (pos === undefined) pos = this.advance(4);
928
+ return readFloat32(this.data, pos);
929
+ }
930
+
931
+ readInt32(pos) {
932
+ if (pos === undefined) pos = this.advance(4);
933
+ return readInt32(this.data, pos);
934
+ }
935
+
936
+ readInt16(pos) {
937
+ if (pos === undefined) pos = this.advance(2);
938
+ return readInt16(this.data, pos);
939
+ }
940
+
941
+ readByte(pos) {
942
+ if (pos === undefined) pos = this.advance(1);
943
+ return this.data[pos];
944
+ }
945
+
946
+ readNulString(pos, maxLength) {
947
+ if (!maxLength) maxLength = 1024;
948
+ let posToUse = pos === undefined ? this.pos : pos;
949
+ let result = "";
950
+ let c;
951
+ while ((c = this.readByte(posToUse++)) !== 0 && --maxLength) {
952
+ result += String.fromCharCode(c);
953
+ }
954
+ if (maxLength === 0) return "";
955
+ if (pos === undefined) this.pos = posToUse;
956
+ return result;
957
+ }
958
+
959
+ substream(posOrLength, length) {
960
+ let pos;
961
+ if (length === undefined) {
962
+ length = posOrLength;
963
+ pos = this.advance(length);
964
+ } else {
965
+ pos = posOrLength;
966
+ if (pos + length >= this.end) throw new RangeError("EOF in " + this.name);
967
+ }
968
+ return new DataStream(this.name + ".sub", this.data.subarray(pos, pos + length));
969
+ }
970
+
971
+ seek(to) {
972
+ if (to >= this.end) throw new RangeError("Seek out of range in " + this.name);
973
+ this.pos = to;
974
+ }
975
+ }
976
+
977
+ export function makeFast32(u32) {
978
+ // Firefox is ~5% faster with signed 32-bit arrays. Chrome is the same speed
979
+ // either way, so here we unconditionally wrap all u32 buffers as i32.
980
+ // Having a function do this makes it easy to test u32 vs i32, and means we
981
+ // keep the rest of the code using u32 (which makes more sense to me).
982
+ return new Int32Array(u32.buffer);
983
+ }
984
+
985
+ const knownDiscExtensions = {
986
+ uef: true,
987
+ ssd: true,
988
+ dsd: true,
989
+ adl: true,
990
+ };
991
+
992
+ const knownRomExtensions = {
993
+ rom: true,
994
+ };
995
+
996
+ function unzipImage(data, knownExtensions) {
997
+ console.log("Attempting to unzip");
998
+
999
+ let files;
1000
+ try {
1001
+ files = unzipSync(data instanceof Uint8Array ? data : new Uint8Array(data));
1002
+ } catch (e) {
1003
+ throw new Error("Error unzipping " + e.message);
1004
+ }
1005
+
1006
+ let uncompressed = null;
1007
+ let loadedFile;
1008
+
1009
+ for (const [filename, fileData] of Object.entries(files)) {
1010
+ const match = filename.match(/.*\.([a-z]+)/i);
1011
+ if (!match || !knownExtensions[match[1].toLowerCase()]) {
1012
+ console.log("Skipping file", filename);
1013
+ continue;
1014
+ }
1015
+ if (uncompressed) {
1016
+ console.log("Ignoring", filename, "as already found a file");
1017
+ continue;
1018
+ }
1019
+ loadedFile = filename;
1020
+ uncompressed = fileData;
1021
+ }
1022
+
1023
+ if (!uncompressed) {
1024
+ throw new Error("Couldn't find any compatible files in the archive");
1025
+ }
1026
+
1027
+ console.log("Unzipped '" + loadedFile + "'");
1028
+ return { data: uncompressed, name: loadedFile };
1029
+ }
1030
+
1031
+ export function unzipDiscImage(data) {
1032
+ return unzipImage(data, knownDiscExtensions);
1033
+ }
1034
+
1035
+ export function unzipRomImage(data) {
1036
+ return unzipImage(data, knownRomExtensions);
1037
+ }
1038
+
1039
+ export function resizeUint8Array(array, byteSize) {
1040
+ const newArray = new Uint8Array(byteSize);
1041
+ newArray.set(array.subarray(0, byteSize));
1042
+ return newArray;
1043
+ }
1044
+
1045
+ export class Fifo {
1046
+ constructor(capacity) {
1047
+ this._buffer = new Uint8Array(capacity);
1048
+ this._size = 0;
1049
+ this._wPtr = 0;
1050
+ this._rPtr = 0;
1051
+ }
1052
+
1053
+ /** @returns {number} */
1054
+ get size() {
1055
+ return this._size;
1056
+ }
1057
+
1058
+ /** @returns {boolean} */
1059
+ get full() {
1060
+ return this._size === this._buffer.length;
1061
+ }
1062
+
1063
+ /** @returns {boolean} */
1064
+ get empty() {
1065
+ return this._size === 0;
1066
+ }
1067
+
1068
+ clear() {
1069
+ this._size = 0;
1070
+ this._wPtr = 0;
1071
+ this._rPtr = 0;
1072
+ }
1073
+
1074
+ /** @type {Number} b */
1075
+ put(b) {
1076
+ if (this.full) return;
1077
+ this._buffer[this._wPtr % this._buffer.length] = b;
1078
+ this._wPtr++;
1079
+ this._size++;
1080
+ }
1081
+
1082
+ /** @returns {Number} */
1083
+ get() {
1084
+ if (this.empty) return;
1085
+ const res = this._buffer[this._rPtr % this._buffer.length];
1086
+ this._rPtr++;
1087
+ this._size--;
1088
+ return res;
1089
+ }
1090
+ }