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,149 @@
1
+ import { AnalogueSource } from "./analogue-source.js";
2
+
3
+ /**
4
+ * Provides microphone input as an analogue source for the BBC Micro's ADC
5
+ * Used for software like MicroMike that uses the analogue port for sound input
6
+ */
7
+ export class MicrophoneInput extends AnalogueSource {
8
+ /**
9
+ * Create a new MicrophoneInput
10
+ */
11
+ constructor() {
12
+ super();
13
+ this.audioContext = null;
14
+ this.microphoneStream = null;
15
+ this.microphoneSource = null;
16
+ this.microphoneAnalyser = null;
17
+ this.microphoneDataArray = null;
18
+ this.errorCallback = null;
19
+ this.errorMessage = null;
20
+ }
21
+
22
+ /**
23
+ * Set error callback function
24
+ * @param {Function} callback - Function to call with error messages
25
+ */
26
+ setErrorCallback(callback) {
27
+ this.errorCallback = callback;
28
+ }
29
+
30
+ /**
31
+ * Initialise microphone access
32
+ * @returns {Promise<boolean>} True if initialiation was successful
33
+ */
34
+ async initialise() {
35
+ console.log("MicrophoneInput: Initialising microphone input");
36
+
37
+ // Create audio context if needed
38
+ if (!this.audioContext) {
39
+ try {
40
+ console.log("MicrophoneInput: Creating audio context");
41
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
42
+ console.log("MicrophoneInput: Audio context created:", this.audioContext.state);
43
+ } catch (error) {
44
+ console.error("MicrophoneInput: Error creating audio context:", error);
45
+ this.errorMessage = `Could not create audio context: ${error.message}`;
46
+ if (this.errorCallback) this.errorCallback(this.errorMessage);
47
+ return false;
48
+ }
49
+ }
50
+
51
+ try {
52
+ console.log("MicrophoneInput: Requesting microphone access");
53
+ // Request microphone access
54
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
55
+ console.log("MicrophoneInput: Microphone access granted");
56
+
57
+ // Store the stream so we can stop it if needed
58
+ this.microphoneStream = stream;
59
+
60
+ // Create analyser node
61
+ console.log("MicrophoneInput: Creating audio analyser");
62
+ this.microphoneAnalyser = this.audioContext.createAnalyser();
63
+ this.microphoneAnalyser.fftSize = 1024; // Larger FFT size for better resolution
64
+ this.microphoneAnalyser.smoothingTimeConstant = 0.2; // Less smoothing for more responsive input
65
+
66
+ // Create buffer for analyser data
67
+ this.microphoneDataArray = new Uint8Array(this.microphoneAnalyser.frequencyBinCount);
68
+ console.log("MicrophoneInput: Created data buffer with", this.microphoneDataArray.length, "samples");
69
+
70
+ // Create media stream source from microphone
71
+ console.log("MicrophoneInput: Creating media stream source");
72
+ this.microphoneSource = this.audioContext.createMediaStreamSource(stream);
73
+
74
+ // Connect microphone to analyser
75
+ console.log("MicrophoneInput: Connecting microphone to analyser");
76
+ this.microphoneSource.connect(this.microphoneAnalyser);
77
+
78
+ this.errorMessage = null;
79
+ console.log("MicrophoneInput: Initialisation complete");
80
+ return true;
81
+ } catch (error) {
82
+ console.error("MicrophoneInput: Error accessing microphone:", error);
83
+ this.errorMessage = `Error accessing microphone: ${error.message}`;
84
+ if (this.errorCallback) this.errorCallback(this.errorMessage);
85
+ return false;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Get the last error message if any
91
+ * @returns {string|null} The last error message or null
92
+ */
93
+ getErrorMessage() {
94
+ return this.errorMessage;
95
+ }
96
+
97
+ /**
98
+ * Get analog value from microphone for the specified channel
99
+ * @param {number} _channel - The ADC channel (0-3)
100
+ * @returns {number} A value between 0 and 0xffff
101
+ */
102
+ getValue(_channel) {
103
+ if (!this.microphoneAnalyser || !this.microphoneDataArray) {
104
+ throw new Error("MicrophoneInput: getValue called but analyser not initialised");
105
+ }
106
+
107
+ // Get time domain data (waveform)
108
+ this.microphoneAnalyser.getByteTimeDomainData(this.microphoneDataArray);
109
+
110
+ // Calculate volume as average deviation from the center (128)
111
+ let sum = 0;
112
+ for (let i = 0; i < this.microphoneDataArray.length; i++) {
113
+ sum += Math.abs(this.microphoneDataArray[i] - 128);
114
+ }
115
+
116
+ const average = sum / this.microphoneDataArray.length;
117
+
118
+ // Scale up the signal using a configurable scaling factor
119
+ // This can be adjusted based on testing
120
+ const scaleFactor = 800; // Amplify the signal
121
+ const scaledValue = average * scaleFactor;
122
+
123
+ // Map to 16-bit range (0-0xFFFF)
124
+ return Math.min(0xffff, scaledValue) | 0;
125
+ }
126
+
127
+ /**
128
+ * Clean up resources when no longer needed
129
+ */
130
+ dispose() {
131
+ if (this.microphoneStream) {
132
+ this.microphoneStream.getTracks().forEach((track) => track.stop());
133
+ this.microphoneStream = null;
134
+ }
135
+
136
+ if (this.microphoneSource) {
137
+ this.microphoneSource.disconnect();
138
+ this.microphoneSource = null;
139
+ }
140
+
141
+ this.microphoneAnalyser = null;
142
+ this.microphoneDataArray = null;
143
+
144
+ if (this.audioContext && this.audioContext.state !== "closed") {
145
+ this.audioContext.close().catch((err) => console.error("Error closing audio context:", err));
146
+ }
147
+ this.audioContext = null;
148
+ }
149
+ }
package/src/models.js ADDED
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+
3
+ import { NoiseAwareWdFdc } from "./wd-fdc.js";
4
+ import { NoiseAwareIntelFdc } from "./intel-fdc.js";
5
+ import * as opcodes from "./6502.opcodes.js";
6
+
7
+ const CpuModel = Object.freeze({
8
+ MOS6502: 0,
9
+ CMOS65C02: 1,
10
+ CMOS65C12: 2,
11
+ });
12
+
13
+ class Model {
14
+ constructor(name, synonyms, os, cpuModel, isMaster, swram, fdc, tube, cmosOverride) {
15
+ this.name = name;
16
+ this.synonyms = synonyms;
17
+ this.os = os;
18
+ this._cpuModel = cpuModel;
19
+ this.isMaster = isMaster;
20
+ this.Fdc = fdc;
21
+ this.swram = swram;
22
+ this.isTest = false;
23
+ this.tube = tube;
24
+ this.cmosOverride = cmosOverride;
25
+ this.hasEconet = false;
26
+ this.hasMusic5000 = false;
27
+ }
28
+
29
+ get nmos() {
30
+ return this._cpuModel === CpuModel.MOS6502;
31
+ }
32
+
33
+ get opcodesFactory() {
34
+ switch (this._cpuModel) {
35
+ case CpuModel.MOS6502:
36
+ return opcodes.Cpu6502;
37
+ case CpuModel.CMOS65C02:
38
+ return opcodes.Cpu65c02;
39
+ case CpuModel.CMOS65C12:
40
+ return opcodes.Cpu65c12;
41
+ }
42
+ throw new Error("Unknown CPU model");
43
+ }
44
+ }
45
+
46
+ function pickAdfs(cmos) {
47
+ cmos[19] = (cmos[19] & 0xf0) | 13;
48
+ return cmos;
49
+ }
50
+
51
+ function pickAnfs(cmos) {
52
+ cmos[19] = (cmos[19] & 0xf0) | 8;
53
+ return cmos;
54
+ }
55
+
56
+ function pickDfs(cmos) {
57
+ cmos[19] = (cmos[19] & 0xf0) | 9;
58
+ return cmos;
59
+ }
60
+
61
+ // TODO: semi-bplus-style to get swram for exile hardcoded here
62
+ const beebSwram = [
63
+ true,
64
+ true,
65
+ true,
66
+ true, // Dunjunz variants. Exile (not picky).
67
+ true,
68
+ true,
69
+ true,
70
+ true, // Crazee Rider.
71
+ false,
72
+ false,
73
+ false,
74
+ false,
75
+ false,
76
+ false,
77
+ false,
78
+ false,
79
+ ];
80
+ const masterSwram = [
81
+ false,
82
+ false,
83
+ false,
84
+ false,
85
+ true,
86
+ true,
87
+ true,
88
+ true,
89
+ false,
90
+ false,
91
+ false,
92
+ false,
93
+ false,
94
+ false,
95
+ false,
96
+ false,
97
+ ];
98
+
99
+ export const allModels = [
100
+ new Model(
101
+ "BBC B with DFS 1.2",
102
+ ["B-DFS1.2"],
103
+ ["os.rom", "BASIC.ROM", "b/DFS-1.2.rom"],
104
+ CpuModel.MOS6502,
105
+ false,
106
+ beebSwram,
107
+ NoiseAwareIntelFdc,
108
+ ),
109
+ new Model(
110
+ "BBC B with DFS 0.9",
111
+ ["B-DFS0.9", "B"],
112
+ ["os.rom", "BASIC.ROM", "b/DFS-0.9.rom"],
113
+ CpuModel.MOS6502,
114
+ false,
115
+ beebSwram,
116
+ NoiseAwareIntelFdc,
117
+ ),
118
+ new Model(
119
+ "BBC B with 1770 (DFS)",
120
+ ["B1770"],
121
+ ["os.rom", "BASIC.ROM", "b1770/dfs1770.rom", "b1770/zADFS.ROM"],
122
+ CpuModel.MOS6502,
123
+ false,
124
+ beebSwram,
125
+ NoiseAwareWdFdc,
126
+ ),
127
+ // putting ADFS in a higher ROM slot gives it priority
128
+ new Model(
129
+ "BBC B with 1770 (ADFS)",
130
+ ["B1770A"],
131
+ ["os.rom", "BASIC.ROM", "b1770/zADFS.ROM", "b1770/dfs1770.rom"],
132
+ CpuModel.MOS6502,
133
+ false,
134
+ beebSwram,
135
+ NoiseAwareWdFdc,
136
+ ),
137
+ new Model(
138
+ "BBC Master 128 (DFS)",
139
+ ["Master"],
140
+ ["master/mos3.20"],
141
+ CpuModel.CMOS65C12,
142
+ true,
143
+ masterSwram,
144
+ NoiseAwareWdFdc,
145
+ null,
146
+ pickDfs,
147
+ ),
148
+ new Model(
149
+ "BBC Master 128 (ADFS)",
150
+ ["MasterADFS"],
151
+ ["master/mos3.20"],
152
+ CpuModel.CMOS65C12,
153
+ true,
154
+ masterSwram,
155
+ NoiseAwareWdFdc,
156
+ null,
157
+ pickAdfs,
158
+ ),
159
+ new Model(
160
+ "BBC Master 128 (ANFS)",
161
+ ["MasterANFS"],
162
+ ["master/mos3.20"],
163
+ CpuModel.CMOS65C12,
164
+ true,
165
+ masterSwram,
166
+ NoiseAwareWdFdc,
167
+ null,
168
+ pickAnfs,
169
+ ),
170
+ new Model("Tube65C02", [], ["tube/6502Tube.rom"], CpuModel.CMOS65C02, false), // Although this can not be explicitly selected as a model, it is required by the configuration builder later
171
+ ];
172
+
173
+ export function findModel(name) {
174
+ name = name.toLowerCase();
175
+ for (let i = 0; i < allModels.length; ++i) {
176
+ const model = allModels[i];
177
+ if (model.name.toLowerCase() === name) return model;
178
+ for (let j = 0; j < model.synonyms.length; ++j) {
179
+ if (model.synonyms[j].toLowerCase() === name) return model;
180
+ }
181
+ }
182
+ return null;
183
+ }
184
+
185
+ export const TEST_6502 = new Model("TEST", ["TEST"], [], CpuModel.MOS6502, false, beebSwram, NoiseAwareIntelFdc);
186
+ TEST_6502.isTest = true;
187
+ export const TEST_65C02 = new Model("TEST", ["TEST"], [], CpuModel.CMOS65C02, false, masterSwram, NoiseAwareIntelFdc);
188
+ TEST_65C02.isTest = true;
189
+ export const TEST_65C12 = new Model("TEST", ["TEST"], [], CpuModel.CMOS65C12, false, masterSwram, NoiseAwareIntelFdc);
190
+ TEST_65C12.isTest = true;
191
+
192
+ export const basicOnly = new Model(
193
+ "Basic only",
194
+ ["Basic only"],
195
+ ["master/mos3.20"],
196
+ CpuModel.CMOS65C12,
197
+ true,
198
+ masterSwram,
199
+ NoiseAwareWdFdc,
200
+ );
@@ -0,0 +1,107 @@
1
+ import { AnalogueSource } from "./analogue-source.js";
2
+
3
+ /**
4
+ * Mouse-based joystick implementation of AnalogueSource
5
+ * Maps mouse position relative to BBC display center to ADC channels
6
+ */
7
+ export class MouseJoystickSource extends AnalogueSource {
8
+ /**
9
+ * Create a new MouseJoystickSource
10
+ * @param {HTMLCanvasElement} canvas - The BBC display canvas element
11
+ */
12
+ constructor(canvas) {
13
+ super();
14
+ this.canvas = canvas;
15
+ this.mouseX = 0.5; // Normalized position (0-1)
16
+ this.mouseY = 0.5; // Normalized position (0-1)
17
+ this.isActive = false;
18
+ this.via = null; // Will be set later
19
+ }
20
+
21
+ /**
22
+ * Set the VIA reference for button handling
23
+ * @param {object} via - The system VIA
24
+ */
25
+ setVia(via) {
26
+ this.via = via;
27
+ }
28
+
29
+ /**
30
+ * Handle mouse movement event from external handler
31
+ * @param {number} x - Normalized X position (0-1)
32
+ * @param {number} y - Normalized Y position (0-1)
33
+ */
34
+ onMouseMove(x, y) {
35
+ this.mouseX = Math.max(0, Math.min(1, x));
36
+ this.mouseY = Math.max(0, Math.min(1, y));
37
+ this.isActive = true;
38
+ }
39
+
40
+ /**
41
+ * Handle mouse button press from external handler
42
+ * @param {number} button - Mouse button number (0 = left, 1 = middle, 2 = right)
43
+ */
44
+ onMouseDown(button) {
45
+ if (!this.via) return;
46
+
47
+ // Only handle left mouse button (button 0)
48
+ if (button === 0) {
49
+ this.via.setJoystickButton(0, true);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Handle mouse button release from external handler
55
+ * @param {number} button - Mouse button number (0 = left, 1 = middle, 2 = right)
56
+ */
57
+ onMouseUp(button) {
58
+ if (!this.via) return;
59
+
60
+ // Only handle left mouse button (button 0)
61
+ if (button === 0) {
62
+ this.via.setJoystickButton(0, false);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Check if mouse joystick is enabled and ready
68
+ * @returns {boolean} True if mouse joystick can handle events
69
+ */
70
+ isEnabled() {
71
+ return !!this.via;
72
+ }
73
+
74
+ /**
75
+ * Get analog value from mouse position for the specified channel
76
+ * @param {number} channel - The ADC channel (0-3)
77
+ * @returns {number} A value between 0 and 0xffff
78
+ */
79
+ getValue(channel) {
80
+ switch (channel) {
81
+ case 0:
82
+ // X axis for joystick 1
83
+ // BBC Micro: left=65535, right=0
84
+ return Math.floor((1 - this.mouseX) * 0xffff);
85
+ case 1:
86
+ // Y axis for joystick 1
87
+ // BBC Micro: up=65535, down=0
88
+ return Math.floor((1 - this.mouseY) * 0xffff);
89
+ case 2:
90
+ case 3:
91
+ // Joystick 2 axes (not used for mouse)
92
+ return 0x8000;
93
+ default:
94
+ return 0x8000;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Clean up when source is no longer needed
100
+ * Called by ADC when switching to a different source
101
+ */
102
+ dispose() {
103
+ // Reset state
104
+ this.via = null;
105
+ this.isActive = false;
106
+ }
107
+ }
@@ -0,0 +1,43 @@
1
+ const BUFFER_SIZE = 65536;
2
+
3
+ registerProcessor(
4
+ "music5000",
5
+ class extends AudioWorkletProcessor {
6
+ constructor() {
7
+ super();
8
+ this.port.onmessage = this.onmessage.bind(this);
9
+
10
+ this.sampleBuffer = new Float32Array(BUFFER_SIZE);
11
+ this.readPosition = 0;
12
+ this.writePosition = 0;
13
+ }
14
+
15
+ onmessage(event) {
16
+ // Receive a new 128-byte sample from the audio processor and write to the FIFO buffer
17
+ const { data } = event;
18
+ const sample = new Float32Array(data);
19
+
20
+ if (this.writePosition === BUFFER_SIZE) {
21
+ this.writePosition = 0;
22
+ }
23
+
24
+ for (let i = 0; i < data.length; i++) {
25
+ this.sampleBuffer[this.writePosition++] = sample[i] / 32768.0; // Conversion to a range -1 to +1
26
+ }
27
+ }
28
+
29
+ process(inputs, outputs) {
30
+ // Playback
31
+ if (this.readPosition === BUFFER_SIZE) {
32
+ this.readPosition = 0;
33
+ }
34
+
35
+ for (let i = 0; i < outputs[0][0].length; i++) {
36
+ outputs[0][0][i] = this.sampleBuffer[this.readPosition++];
37
+ outputs[0][1][i] = this.sampleBuffer[this.readPosition++];
38
+ }
39
+
40
+ return true;
41
+ }
42
+ },
43
+ );
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+
3
+ // Code ported from Beebem (C to .js) by Jason Robson
4
+ const AUDIO_BUFFER_SIZE = 256;
5
+
6
+ const RAM_SIZE = 2048;
7
+ const WAVE_TABLE_SIZE = 128;
8
+ const WAVE_TABLES = 14;
9
+ const NUM_CHANNELS = 16;
10
+ const CHANNEL_REG_OFFSET = WAVE_TABLES * WAVE_TABLE_SIZE;
11
+ const CHANNEL_ROW_SIZE = 128;
12
+
13
+ // Control register bits
14
+ const CTRL_STEREO_POS = 0xf;
15
+ const CTRL_INVERT_WAVE = 1 << 4;
16
+ const CTRL_MODULATE_ADJ = 1 << 5;
17
+
18
+ // Data bits
19
+ const DATA_SIGN = 1 << 7;
20
+ const DATA_VALUE = 0x7f;
21
+ const FREQ_DISABLE = 1 << 0;
22
+ const NOT_FREQ_DISABLE = 0xfe;
23
+
24
+ // Channel register sets
25
+ const REG_SET_NORMAL = 0;
26
+ const REG_SET_ALT = 1;
27
+
28
+ export class Music5000 {
29
+ constructor(onBuffer) {
30
+ this._onBufferMusic5000 = onBuffer;
31
+
32
+ this.waveRam = new Uint8Array(RAM_SIZE);
33
+ this.phaseRam = new Uint32Array(NUM_CHANNELS);
34
+ this.cycleCount = 0;
35
+ this.curCh = 0;
36
+ this.activeRegSet = REG_SET_NORMAL;
37
+ this.sampleLeft = 0;
38
+ this.sampleRight = 0;
39
+
40
+ this.chordBase = [0, 8.25, 24.75, 57.75, 123.75, 255.75, 519.75, 1047.75];
41
+ this.stepInc = [0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0];
42
+
43
+ this.stereoLeft = [0, 0, 0, 0, 0, 0, 0, 0, 100, 100, 100, 83, 67, 50, 33, 17];
44
+ this.stereoRight = [100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 17, 33, 50, 67, 83];
45
+
46
+ this.D2ATable = new Uint16Array(128);
47
+
48
+ this.sampleBuffer = new Float64Array(AUDIO_BUFFER_SIZE);
49
+ this.position = 0;
50
+
51
+ // Helper functions to access the register set and wavetable
52
+ this.cReg_freqLow = function (channel, regSet) {
53
+ return this.waveRam[CHANNEL_REG_OFFSET + regSet * CHANNEL_ROW_SIZE + 0 * NUM_CHANNELS + channel];
54
+ };
55
+ this.cReg_freqMedium = function (channel, regSet) {
56
+ return this.waveRam[CHANNEL_REG_OFFSET + regSet * CHANNEL_ROW_SIZE + 1 * NUM_CHANNELS + channel];
57
+ };
58
+ this.cReg_freqHigh = function (channel, regSet) {
59
+ return this.waveRam[CHANNEL_REG_OFFSET + regSet * CHANNEL_ROW_SIZE + 2 * NUM_CHANNELS + channel];
60
+ };
61
+ this.cReg_waveformReg = function (channel, regSet) {
62
+ return this.waveRam[CHANNEL_REG_OFFSET + regSet * CHANNEL_ROW_SIZE + 5 * NUM_CHANNELS + channel];
63
+ };
64
+ this.cReg_amplitudeReg = function (channel, regSet) {
65
+ return this.waveRam[CHANNEL_REG_OFFSET + regSet * CHANNEL_ROW_SIZE + 6 * NUM_CHANNELS + channel];
66
+ };
67
+ this.cReg_controlReg = function (channel, regSet) {
68
+ return this.waveRam[CHANNEL_REG_OFFSET + regSet * CHANNEL_ROW_SIZE + 7 * NUM_CHANNELS + channel];
69
+ };
70
+
71
+ this.waveTableVal = function (table, b) {
72
+ return this.waveRam[table * WAVE_TABLE_SIZE + b];
73
+ };
74
+ }
75
+
76
+ reset(hard) {
77
+ if (hard) {
78
+ console.log("Music 5000: initialisation");
79
+
80
+ // Build the D2A table
81
+ let i = 0;
82
+ for (let chord = 0; chord < 8; chord++) {
83
+ let val = this.chordBase[chord];
84
+ for (let step = 0; step < 16; step++) {
85
+ this.D2ATable[i] = Math.floor(val * 4); // Multiply up to get an integer
86
+ val += this.stepInc[chord];
87
+ i++;
88
+ }
89
+ }
90
+ }
91
+
92
+ // Clear RAM
93
+ for (let w = 0; w < RAM_SIZE; w++) {
94
+ this.waveRam[w] = 0;
95
+ }
96
+
97
+ for (let p = 0; p < NUM_CHANNELS; p++) {
98
+ this.phaseRam[p] = 0;
99
+ }
100
+ }
101
+
102
+ read(page, addr) {
103
+ // Bit0 unused
104
+ const offset = ((page & 0x0e) << 7) + addr;
105
+ return this.waveRam[offset];
106
+ }
107
+
108
+ write(page, addr, value) {
109
+ // Bit0 unused
110
+ const offset = ((page & 0x0e) << 7) + (addr & 0xff);
111
+ this.waveRam[offset] = value;
112
+ }
113
+
114
+ polltime(cycles) {
115
+ let c4d,
116
+ freq,
117
+ offset,
118
+ wavetable,
119
+ amplitude,
120
+ control,
121
+ data,
122
+ sign,
123
+ pos = 0 >>> 0;
124
+
125
+ // Convert 2MHz 6502 cycles to 6MHz Music5000 cycles
126
+ this.cycleCount += cycles * 3;
127
+
128
+ // Need 8 cycles to update a channel
129
+ while (this.cycleCount >= 8) {
130
+ // Update phase for active register set
131
+ if (this.cReg_freqLow(this.curCh, this.activeRegSet) & FREQ_DISABLE) {
132
+ this.phaseRam[this.curCh] = 0;
133
+ c4d = 0;
134
+ } else {
135
+ freq =
136
+ (this.cReg_freqHigh(this.curCh, this.activeRegSet) << 16) +
137
+ (this.cReg_freqMedium(this.curCh, this.activeRegSet) << 8) +
138
+ (this.cReg_freqLow(this.curCh, this.activeRegSet) & NOT_FREQ_DISABLE);
139
+ this.phaseRam[this.curCh] += freq;
140
+ c4d = this.phaseRam[this.curCh] & (1 << 24);
141
+ this.phaseRam[this.curCh] &= 0xffffff;
142
+ }
143
+
144
+ // Pull wave sample out for the active register set
145
+ offset = (this.phaseRam[this.curCh] >> 17) & 0x7f;
146
+ wavetable = this.cReg_waveformReg(this.curCh, this.activeRegSet) >> 4;
147
+ data = this.waveTableVal(wavetable, offset);
148
+ amplitude = this.cReg_amplitudeReg(this.curCh, this.activeRegSet);
149
+ control = this.cReg_controlReg(this.curCh, this.activeRegSet);
150
+ sign = data & DATA_SIGN;
151
+ data &= DATA_VALUE;
152
+
153
+ // Modulate the next channel?
154
+ if (control & CTRL_MODULATE_ADJ && (sign || c4d)) this.activeRegSet = REG_SET_ALT;
155
+ else this.activeRegSet = REG_SET_NORMAL;
156
+
157
+ if (amplitude > 0x80) amplitude = 0x80;
158
+ data = (data * amplitude) / 0x80;
159
+
160
+ let sample = this.D2ATable[parseInt(data)];
161
+ if (control & CTRL_INVERT_WAVE) sign ^= DATA_SIGN;
162
+ if (sign) sample = -sample;
163
+
164
+ // Stereo
165
+ pos = control & CTRL_STEREO_POS;
166
+ this.sampleLeft += (sample * this.stereoLeft[pos]) / 100;
167
+ this.sampleRight += (sample * this.stereoRight[pos]) / 100;
168
+
169
+ this.curCh++;
170
+ if (this.curCh === NUM_CHANNELS) {
171
+ this.curCh = 0;
172
+ this.activeRegSet = REG_SET_NORMAL;
173
+
174
+ // Range check
175
+ if (this.sampleLeft < -32768) this.sampleLeft = -32768;
176
+ else if (this.sampleLeft > 32767) this.sampleLeft = 32767;
177
+
178
+ if (this.sampleRight < -32768) this.sampleRight = -32768;
179
+ else if (this.sampleRight > 32767) this.sampleRight = 32767;
180
+
181
+ this.sampleBuffer[this.position++] = this.sampleLeft;
182
+ this.sampleBuffer[this.position++] = this.sampleRight;
183
+
184
+ if (this.position === AUDIO_BUFFER_SIZE) {
185
+ this._onBufferMusic5000(this.sampleBuffer);
186
+ this.position = 0;
187
+ }
188
+
189
+ this.sampleLeft = 0;
190
+ this.sampleRight = 0;
191
+ }
192
+
193
+ this.cycleCount -= 8;
194
+ }
195
+ }
196
+ }
197
+
198
+ export class FakeMusic5000 {
199
+ constructor() {
200
+ this.reset = function () {};
201
+ this.polltime = function () {};
202
+ this.read = function () {
203
+ return 0;
204
+ };
205
+ this.write = function () {};
206
+ }
207
+ }