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,396 @@
1
+ // HFE format implementation for disc image loading and saving
2
+ // Translated from beebjit by Chris Evans and expanded by Matt Godbolt.
3
+
4
+ import { IbmDiscFormat } from "./disc.js";
5
+
6
+ // HFE format constants
7
+ const HfeHeaderV1 = "HXCPICFE";
8
+ const HfeHeaderV3 = "HXCHFEV3";
9
+ const HfeV3OpcodeMask = 0xf0;
10
+ const HfeV3OpcodeNop = 0xf0;
11
+ const HfeV3OpcodeSetIndex = 0xf1;
12
+ const HfeV3OpcodeSetBitrate = 0xf2;
13
+ const HfeV3OpcodeSkipBits = 0xf3;
14
+ const HfeV3OpcodeRand = 0xf4;
15
+ const HfeBlockSideSize = 256;
16
+ const HfeBlockSize = HfeBlockSideSize * 2;
17
+ const HfeShugartDdFloppyMode = 7;
18
+ const HfeIsoIbmFmEncoding = 2;
19
+
20
+ // 250kbit bitrate value per HFE format specification
21
+ const Bitrate250k = 72;
22
+
23
+ /**
24
+ * Flip the bits in a byte for HFE encoding
25
+ * @param {number} val - Byte value to flip
26
+ * @returns {number} - Flipped byte value
27
+ */
28
+ function hfeByteFlip(val) {
29
+ let ret = 0;
30
+ if (val & 0x80) ret |= 0x01;
31
+ if (val & 0x40) ret |= 0x02;
32
+ if (val & 0x20) ret |= 0x04;
33
+ if (val & 0x10) ret |= 0x08;
34
+ if (val & 0x08) ret |= 0x10;
35
+ if (val & 0x04) ret |= 0x20;
36
+ if (val & 0x02) ret |= 0x40;
37
+ if (val & 0x01) ret |= 0x80;
38
+
39
+ return ret;
40
+ }
41
+
42
+ /**
43
+ * Get the track offset and length from HFE metadata
44
+ * @param {Uint8Array} metadata - HFE lookup table metadata
45
+ * @param {number} track - Track number
46
+ * @returns {{offset: number, length: number}} - Track offset and length
47
+ */
48
+ function hfeGetTrackOffsetAndLength(metadata, track) {
49
+ const index = track << 2;
50
+ const offset = HfeBlockSize * (metadata[index] + (metadata[index + 1] << 8));
51
+ const length = metadata[index + 2] + (metadata[index + 3] << 8);
52
+ return { offset, length };
53
+ }
54
+
55
+ /**
56
+ * Load a disc image in HFE format (v1 or v3)
57
+ * @param {import("./disc.js").Disc} disc - The disc object to load into
58
+ * @param {Uint8Array} data - The HFE file data
59
+ * @param {function(Uint8Array): void} onChange - Optional callback when disc content changes
60
+ * @returns {import("./disc.js").Disc} - The loaded disc object
61
+ */
62
+ export function loadHfe(disc, data, onChange) {
63
+ if (data.length < HfeBlockSize) throw new Error("HFE file missing header");
64
+ const header = new TextDecoder("ascii").decode(data.slice(0, 8));
65
+ let isV3 = false;
66
+ let hfeVersion = 1;
67
+
68
+ if (header === HfeHeaderV1) {
69
+ // HFE v1 format
70
+ } else if (header === HfeHeaderV3) {
71
+ hfeVersion = 3;
72
+ isV3 = true;
73
+ } else {
74
+ throw new Error(`HFE file bad header '${header}'`);
75
+ }
76
+ if (data[8] !== 0) throw new Error("HFE file revision not 0");
77
+ if (data[11] !== 2 && data[11] !== 0) {
78
+ if (data[11] === 0xff) {
79
+ console.log(`Unknown HFE encoding ${data[11]}, trying anyway`);
80
+ } else {
81
+ throw new Error(`HFE encoding not ISOIBM_(M)FM_ENCODING: ${data[11]}`);
82
+ }
83
+ }
84
+ const numSides = data[10];
85
+ if (numSides < 1 || numSides > 2) throw new Error(`Invalid number of sides: ${numSides}`);
86
+
87
+ const numTracks = data[9];
88
+ if (numTracks > IbmDiscFormat.tracksPerDisc) throw new Error(`Too many tracks: ${numTracks}`);
89
+ let expandShift = 0;
90
+ if (disc.config.expandTo80 && numTracks * 2 <= IbmDiscFormat.tracksPerDisc) {
91
+ expandShift = 1;
92
+ console.log("Expanding 40 tracks to 80");
93
+ }
94
+
95
+ console.log(`HFE v${hfeVersion} loading ${numSides} sides, ${numTracks} tracks`);
96
+
97
+ const lutOffset = HfeBlockSize * (data[18] + (data[19] << 8));
98
+ if (lutOffset + HfeBlockSize > data.length) throw new Error("HFE LUT doesn't fit");
99
+
100
+ const metadata = data.slice(lutOffset, lutOffset + HfeBlockSize);
101
+
102
+ for (let trackNum = 0; trackNum < numTracks; ++trackNum) {
103
+ let actualTrackNum = trackNum;
104
+ if (disc.config.isSkipOddTracks) {
105
+ if (trackNum & 1) continue;
106
+ actualTrackNum = trackNum >>> 1;
107
+ }
108
+ actualTrackNum = actualTrackNum << expandShift;
109
+ const { offset, length } = hfeGetTrackOffsetAndLength(metadata, trackNum);
110
+ if (offset + length > data.length)
111
+ throw new Error(
112
+ `HFE track ${trackNum} doesn't fit (length ${length} offset ${offset} file length ${data.length})`,
113
+ );
114
+ const trackData = data.slice(offset, offset + length);
115
+
116
+ for (let sideNum = 0; sideNum < numSides; ++sideNum) {
117
+ const bufLen = length >> 1;
118
+ let bytesWritten = 0;
119
+ if (disc.config.isSkipUpperSide && sideNum === 1) continue;
120
+
121
+ let isSetBitRate = false;
122
+ let isSkipBits = false;
123
+ let skipBitsLength = 0;
124
+ let pulses = 0;
125
+ let shiftCounter = 0;
126
+
127
+ const trackObj = disc.getTrack(sideNum === 1, actualTrackNum);
128
+ disc.setTrackUsed(sideNum === 1, actualTrackNum);
129
+ const rawPulses = trackObj.pulses2Us;
130
+ for (let byteIndex = 0; byteIndex < bufLen; ++byteIndex) {
131
+ if (bytesWritten === rawPulses.length) {
132
+ throw new Error(`HFE track ${trackNum} truncated`);
133
+ }
134
+ const index = ((byteIndex >>> 8) << 9) + (sideNum << 8) + (byteIndex & 0xff);
135
+ let byte = hfeByteFlip(trackData[index]);
136
+ let numBits = 8;
137
+
138
+ if (isSetBitRate) {
139
+ isSetBitRate = false;
140
+ if (byte < 64 || byte > 80) {
141
+ console.log(`HFE v3 SETBITRATE wild (72=250kbit) track: ${trackNum} ${byte}`);
142
+ }
143
+ continue;
144
+ } else if (isSkipBits) {
145
+ isSkipBits = false;
146
+ if (byte === 0 || byte >= 8) {
147
+ throw new Error(`HFE v3 invalid skipbits ${byte}`);
148
+ }
149
+ skipBitsLength = byte;
150
+ continue;
151
+ } else if (skipBitsLength) {
152
+ byte = (byte << (8 - skipBitsLength)) & 0xff;
153
+ numBits = skipBitsLength;
154
+ skipBitsLength = 0;
155
+ } else if (isV3 && (byte & HfeV3OpcodeMask) === HfeV3OpcodeMask) {
156
+ switch (byte) {
157
+ case HfeV3OpcodeNop:
158
+ continue; // NB continue
159
+ case HfeV3OpcodeSetIndex:
160
+ if (bytesWritten !== 0)
161
+ console.log(`HFEv3 SETINDEX not at byte 0, track ${trackNum}: ${bytesWritten}`);
162
+ continue; // NB continue
163
+ case HfeV3OpcodeSetBitrate:
164
+ isSetBitRate = true;
165
+ continue; // NB continue
166
+ case HfeV3OpcodeSkipBits:
167
+ isSkipBits = true;
168
+ continue; // NB continue
169
+ case HfeV3OpcodeRand:
170
+ // internally we represent weak bits on disc as a no flux area.
171
+ byte = 0;
172
+ break; // NB a break
173
+ default:
174
+ throw new Error(`Unknown HFE v3 opcode ${byte}`);
175
+ }
176
+ }
177
+
178
+ for (let bitIndex = 0; bitIndex < numBits; ++bitIndex) {
179
+ pulses = ((pulses << 1) & 0xffffffff) | (byte & 0x80 ? 1 : 0);
180
+ byte = (byte << 1) & 0xff;
181
+ if (++shiftCounter === 32) {
182
+ rawPulses[bytesWritten] = pulses;
183
+ bytesWritten++;
184
+ pulses = 0;
185
+ shiftCounter = 0;
186
+ }
187
+ }
188
+ }
189
+ trackObj.length = bytesWritten;
190
+ }
191
+ }
192
+
193
+ // Set up write track callback if onChange is provided
194
+ if (onChange) {
195
+ disc.setWriteTrackCallback((_side, _trackNum, _trackObj) => {
196
+ // Generate a complete HFE image from the current disc state
197
+ const hfeData = toHfe(disc);
198
+ // Call the onChange handler with the updated HFE data
199
+ onChange(hfeData);
200
+ });
201
+ }
202
+
203
+ return disc;
204
+ }
205
+
206
+ /**
207
+ * Check if a byte could be mistakenly interpreted as a v3 opcode
208
+ * @param {number} byte - The byte to check after bit flipping
209
+ * @returns {boolean} - True if the byte could be interpreted as a v3 opcode
210
+ */
211
+ function isHfeV3OpcodeCollision(byte) {
212
+ // After bit flipping, check if the lower 4 bits are all 1s (0x0F)
213
+ return (byte & 0x0f) === 0x0f;
214
+ }
215
+
216
+ /**
217
+ * Encode a single 32-bit pulse value to 4 bytes in HFE v3 format
218
+ * Handles opcode collisions by replacing with RAND opcode if needed
219
+ * @param {number} pulsesWord - The 32-bit pulse value
220
+ * @returns {Uint8Array} - 4 bytes of HFE v3 encoded data
221
+ */
222
+ function encodeHfeV3Pulses(pulsesWord) {
223
+ const result = new Uint8Array(4);
224
+
225
+ for (let i = 0; i < 4; i++) {
226
+ const byte = (pulsesWord >>> 24) & 0xff;
227
+ const flippedByte = hfeByteFlip(byte);
228
+
229
+ // Check if the byte could be mistakenly interpreted as a v3 opcode
230
+ if (isHfeV3OpcodeCollision(flippedByte)) {
231
+ // Replace with bit-flipped RAND opcode
232
+ result[i] = hfeByteFlip(HfeV3OpcodeRand);
233
+ } else {
234
+ result[i] = flippedByte;
235
+ }
236
+
237
+ pulsesWord <<= 8;
238
+ }
239
+
240
+ return result;
241
+ }
242
+
243
+ /**
244
+ * Convert track data to HFE v3 format, handling weak pulses and opcode collisions
245
+ * @param {Uint32Array|Array<number>} pulses - Array of 32-bit pulse values for the track
246
+ * @returns {Uint8Array} - The track data in HFE v3 format
247
+ */
248
+ export function convertTrackToHfeV3(pulses) {
249
+ // Calculate the max potential size (4 bytes per pulse value + 3 bytes header)
250
+ const buffer = new Uint8Array(pulses.length * 4 + 3);
251
+ let bufferIndex = 0;
252
+
253
+ // Add HFE v3 header opcodes (SETINDEX, SETBITRATE, Bitrate250k)
254
+ buffer[bufferIndex++] = hfeByteFlip(HfeV3OpcodeSetIndex);
255
+ buffer[bufferIndex++] = hfeByteFlip(HfeV3OpcodeSetBitrate);
256
+ buffer[bufferIndex++] = hfeByteFlip(Bitrate250k);
257
+
258
+ // Process each pulse
259
+ for (let i = 0; i < pulses.length; i++) {
260
+ const pulse = pulses[i];
261
+
262
+ if (pulse === 0) {
263
+ // Handle weak bits explicitly in HFEv3 with RAND opcode
264
+ const randOpcode = hfeByteFlip(HfeV3OpcodeRand);
265
+ buffer.fill(randOpcode, bufferIndex, bufferIndex + 4);
266
+ } else {
267
+ // Encode normal pulses, handling potential opcode collisions
268
+ const encodedPulses = encodeHfeV3Pulses(pulse);
269
+ buffer.set(encodedPulses, bufferIndex);
270
+ }
271
+
272
+ bufferIndex += 4;
273
+ }
274
+
275
+ // Return only the used part of the buffer
276
+ return buffer.slice(0, bufferIndex);
277
+ }
278
+
279
+ /**
280
+ * Convert disc to HFE v3 format
281
+ * HFE is a format used by the HxC Floppy Emulator for storing disk images.
282
+ * This implementation supports variable track lengths without truncation.
283
+ * Reference: https://hxc2001.com/download/floppy_drive_emulator/SDCard_HxC_Floppy_Emulator_HFE_file_format.pdf
284
+ * @param {import("./disc.js").Disc} disc - The disc to convert to HFE
285
+ * @returns {Uint8Array} - The HFE file data
286
+ */
287
+ export function toHfe(disc) {
288
+ const numSides = disc.isDoubleSided ? 2 : 1;
289
+ const numTracks = disc.tracksUsed;
290
+
291
+ // Pre-calculate sizes for each track to properly support variable track lengths
292
+ const trackSizes = [];
293
+ const trackOffsetDeltas = [];
294
+ for (let trackNum = 0; trackNum < numTracks; trackNum++) {
295
+ const track0 = disc.getTrack(false, trackNum);
296
+ const track1 = numSides > 1 ? disc.getTrack(true, trackNum) : { length: 0 };
297
+
298
+ // Calculate the actual size needed for this specific track
299
+ // From C code: 4 bytes per 32-bit word, 3 "header" HFEv3 bytes, 2 sides
300
+ const trackLen = Math.max(track0.length, track1.length);
301
+ const hfeTrackLen = (trackLen * 4 + 3) * 2;
302
+ const hfeOffsetDelta = Math.floor(hfeTrackLen / HfeBlockSize) + 1;
303
+
304
+ trackSizes.push(hfeTrackLen);
305
+ trackOffsetDeltas.push(hfeOffsetDelta);
306
+ }
307
+
308
+ // Build header
309
+ const header = new Uint8Array(512);
310
+ header.fill(0xff);
311
+
312
+ // Write HFE v3 signature
313
+ const encoder = new TextEncoder();
314
+ header.set(encoder.encode("HXCHFEV3"), 0);
315
+ header[8] = 0; // Revision 0
316
+ header[9] = numTracks;
317
+ header[10] = numSides;
318
+ header[11] = HfeIsoIbmFmEncoding; // IBM FM/MFM encoding
319
+ header[12] = 250; // Bit rate (nb we use the set bitrate command per track)
320
+ header[13] = 0;
321
+ header[14] = 0; // RPM (unused)
322
+ header[15] = 0;
323
+ header[16] = HfeShugartDdFloppyMode; // Mode: Shugart DD
324
+ header[17] = 0xff; // Unused
325
+ header[18] = 1; // LUT offset at block 1 (512 bytes)
326
+ header[19] = 0;
327
+ header[20] = 0xff; // Write allowed
328
+ header[21] = 0xff; // Single step
329
+ header[22] = 0xff; // No alternate track options
330
+ header[23] = 0xff;
331
+ header[24] = 0xff;
332
+ header[25] = 0xff;
333
+
334
+ const headerSize = HfeBlockSize;
335
+ // Assume everything will fit into one block.
336
+ const lutSize = 1 * HfeBlockSize;
337
+
338
+ // Build LUT (Lookup Table) for track offsets
339
+ const lut = new Uint8Array(lutSize);
340
+ const lutDataView = new DataView(lut.buffer);
341
+ let hfeOffset = 2; // Start at block 2 (after header and LUT)
342
+
343
+ // Calculate total file size and build LUT
344
+ let totalSize = headerSize + lutSize;
345
+
346
+ for (let trackNum = 0; trackNum < numTracks; trackNum++) {
347
+ const byteOffset = trackNum * 4;
348
+ lutDataView.setUint16(byteOffset, hfeOffset, true); // Offset in 512-byte blocks
349
+ lutDataView.setUint16(byteOffset + 2, trackSizes[trackNum], true); // Track-specific length in bytes
350
+
351
+ // Add this track's size to the total and advance offset
352
+ totalSize += trackOffsetDeltas[trackNum] * HfeBlockSize;
353
+ hfeOffset += trackOffsetDeltas[trackNum];
354
+ }
355
+ const hfeData = new Uint8Array(totalSize);
356
+
357
+ // Write header
358
+ hfeData.set(header, 0);
359
+ // Write LUT
360
+ hfeData.set(lut, headerSize);
361
+
362
+ // Write track data
363
+ let trackOffset = 1024; // Start after header and LUT
364
+ for (let trackNum = 0; trackNum < numTracks; trackNum++) {
365
+ // Process each side separately
366
+ for (let side = 0; side < numSides; side++) {
367
+ const track = disc.getTrack(side === 1, trackNum);
368
+ const pulses = track.pulses2Us;
369
+
370
+ const trackBuffer = convertTrackToHfeV3(pulses.slice(0, track.length));
371
+ let bufferIndex = trackBuffer.length;
372
+
373
+ // Write track data in per-side sized chunks, interleaved according to HFE format
374
+ let writePos = trackOffset + (side === 1 ? HfeBlockSideSize : 0);
375
+ let iByte = 0;
376
+
377
+ while (iByte < bufferIndex) {
378
+ const chunkLen = Math.min(HfeBlockSideSize, bufferIndex - iByte);
379
+ const chunk = new Uint8Array(HfeBlockSideSize);
380
+
381
+ if (chunkLen > 0) {
382
+ chunk.set(trackBuffer.slice(iByte, iByte + chunkLen));
383
+ }
384
+
385
+ hfeData.set(chunk, writePos);
386
+ writePos += HfeBlockSize;
387
+ iByte += chunkLen;
388
+ }
389
+ }
390
+
391
+ // Move to the next track after processing all sides
392
+ trackOffset += trackOffsetDeltas[trackNum] * HfeBlockSize;
393
+ }
394
+
395
+ return hfeData;
396
+ }