jsbeeb 1.1.1 → 1.3.2

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 (450) hide show
  1. package/package.json +25 -12
  2. package/src/6502.js +51 -41
  3. package/src/app/app.js +99 -8
  4. package/src/app/electron.js +47 -5
  5. package/src/app/preload.js +11 -1
  6. package/src/config.js +9 -2
  7. package/src/disc.js +2 -2
  8. package/src/filestore.js +1 -4
  9. package/src/gamepad-source.js +1 -1
  10. package/src/machine-session.js +396 -0
  11. package/src/main.js +36 -7
  12. package/src/music5000-worklet.js +1 -0
  13. package/src/music5000.js +1 -9
  14. package/src/sth.js +3 -1
  15. package/src/utils.js +2 -2
  16. package/src/web/audio-renderer.js +1 -1
  17. package/src/web/debug.js +2 -2
  18. package/tests/test-machine.js +82 -1
  19. package/.editorconfig +0 -15
  20. package/.git-blame-ignore-revs +0 -3
  21. package/.github/copilot-instructions.md +0 -94
  22. package/.github/workflows/claude-issue-triage.yml +0 -105
  23. package/.github/workflows/claude.yml +0 -63
  24. package/.github/workflows/release-please.yml +0 -75
  25. package/.github/workflows/test-and-deploy.yml +0 -86
  26. package/.gitmodules +0 -6
  27. package/.husky/pre-commit +0 -1
  28. package/.idea/codeStyleSettings.xml +0 -9
  29. package/.idea/codeStyles/Project.xml +0 -62
  30. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  31. package/.idea/compiler.xml +0 -22
  32. package/.idea/copyright/profiles_settings.xml +0 -3
  33. package/.idea/encodings.xml +0 -6
  34. package/.idea/inspectionProfiles/Project_Default.xml +0 -7
  35. package/.idea/jsLibraryMappings.xml +0 -6
  36. package/.idea/jsLinters/jshint.xml +0 -85
  37. package/.idea/jsLinters/jslint.xml +0 -15
  38. package/.idea/jsbeeb.iml +0 -11
  39. package/.idea/misc.xml +0 -6
  40. package/.idea/modules.xml +0 -8
  41. package/.idea/prettier.xml +0 -7
  42. package/.idea/runConfigurations/Debug.xml +0 -5
  43. package/.idea/scopes/scope_settings.xml +0 -5
  44. package/.idea/vcs.xml +0 -8
  45. package/.prettierignore +0 -4
  46. package/.prettierrc.json +0 -1
  47. package/.release-please-manifest.json +0 -3
  48. package/.vscode/launch.json +0 -14
  49. package/.vscode/settings.json +0 -6
  50. package/CHANGELOG.md +0 -32
  51. package/CLAUDE.md +0 -136
  52. package/Dockerfile +0 -22
  53. package/Makefile +0 -30
  54. package/docker/nginx-default.conf +0 -10
  55. package/docs/pal-comb-filter-research.md +0 -129
  56. package/docs/pal-simulation-design.md +0 -368
  57. package/eslint.config.js +0 -35
  58. package/index.html +0 -954
  59. package/jsconfig.json +0 -10
  60. package/public/discs/README.Irq-Timing +0 -3
  61. package/public/discs/README.bcdtest +0 -5
  62. package/public/discs/README.elite +0 -6
  63. package/public/discs/README.eng_test +0 -3
  64. package/public/discs/README.protection +0 -7
  65. package/public/favicon.ico +0 -0
  66. package/public/images/botbar.png +0 -0
  67. package/public/images/cub-monitor.png +0 -0
  68. package/public/images/jsbeeb-example.png +0 -0
  69. package/public/images/placeholder.png +0 -0
  70. package/public/images/red-off-16.png +0 -0
  71. package/public/images/red-on-16.png +0 -0
  72. package/public/images/sb/CD-left.jpg +0 -0
  73. package/public/images/sb/CD-right.jpg +0 -0
  74. package/public/images/sidebar.png +0 -0
  75. package/public/images/tv.png +0 -0
  76. package/public/images/yellow-off-16.png +0 -0
  77. package/public/images/yellow-on-16.png +0 -0
  78. package/public/jsbeeb-icon.png +0 -0
  79. package/public/robots.txt +0 -3
  80. package/public/roms/ADFS1-53.rom +0 -0
  81. package/public/roms/BASIC.ROM +0 -0
  82. package/public/roms/README +0 -4
  83. package/public/roms/a01/BASIC1.rom +0 -0
  84. package/public/roms/ample.rom +0 -0
  85. package/public/roms/ats-3.0.rom +0 -0
  86. package/public/roms/b/DFS-0.9.rom +0 -0
  87. package/public/roms/b/DFS-1.2.rom +0 -0
  88. package/public/roms/b1770/dfs1770.rom +0 -0
  89. package/public/roms/b1770/zADFS.ROM +0 -0
  90. package/public/roms/bp/dfs.rom +0 -0
  91. package/public/roms/bp/zADFS.ROM +0 -0
  92. package/public/roms/bpos.rom +0 -0
  93. package/public/roms/compact/adfs210.rom +0 -0
  94. package/public/roms/compact/basic48.rom +0 -0
  95. package/public/roms/compact/basic486.rom +0 -0
  96. package/public/roms/compact/os51.rom +0 -0
  97. package/public/roms/compact/utils.rom +0 -0
  98. package/public/roms/deos.rom +0 -0
  99. package/public/roms/master/anfs-4.25.rom +0 -0
  100. package/public/roms/master/mos.txt +0 -68819
  101. package/public/roms/master/mos3.20 +0 -0
  102. package/public/roms/os.rom +0 -0
  103. package/public/roms/os01.rom +0 -0
  104. package/public/roms/tube/6502Tube.rom +0 -0
  105. package/public/roms/tube/ARMeval_100.rom +0 -0
  106. package/public/roms/tube/BIOS.ROM +0 -0
  107. package/public/roms/tube/ReCo6502ROM_816 +0 -0
  108. package/public/roms/tube/Z80_120.rom +0 -0
  109. package/public/roms/us/USBASIC.rom +0 -0
  110. package/public/roms/us/USDNFS.rom +0 -0
  111. package/public/roms/usmos.rom +0 -0
  112. package/public/sounds/disc525/motor.wav +0 -0
  113. package/public/sounds/disc525/motoroff.wav +0 -0
  114. package/public/sounds/disc525/motoron.wav +0 -0
  115. package/public/sounds/disc525/seek.wav +0 -0
  116. package/public/sounds/disc525/seek2.wav +0 -0
  117. package/public/sounds/disc525/seek3.wav +0 -0
  118. package/public/sounds/disc525/step.wav +0 -0
  119. package/public/teletext/txt0.dat +0 -0
  120. package/public/teletext/txt1.dat +0 -0
  121. package/public/teletext/txt2.dat +0 -0
  122. package/public/teletext/txt3.dat +0 -0
  123. package/release-please-config.json +0 -13
  124. package/run-container.sh +0 -92
  125. package/tests/integration/RmwX.asm +0 -47
  126. package/tests/integration/TestInstructionsSource +0 -27
  127. package/tests/integration/TestTimingsResults +0 -27
  128. package/tests/integration/TestTimingsSource +0 -61
  129. package/tests/integration/bcd.js +0 -23
  130. package/tests/integration/dormann.js +0 -101
  131. package/tests/integration/dp111timing.js +0 -42
  132. package/tests/integration/ensure-submodules.js +0 -25
  133. package/tests/integration/nops.bas +0 -119
  134. package/tests/integration/nops.js +0 -24
  135. package/tests/integration/protection.js +0 -26
  136. package/tests/integration/rmw.js +0 -69
  137. package/tests/integration/teletext/expected_bug_469.png +0 -0
  138. package/tests/integration/teletext/expected_flash_0.png +0 -0
  139. package/tests/integration/teletext/expected_flash_1.png +0 -0
  140. package/tests/integration/teletext/expected_hoglet_held_char.png +0 -0
  141. package/tests/integration/teletext/expected_reveal_flash_0.png +0 -0
  142. package/tests/integration/teletext/expected_reveal_flash_1.png +0 -0
  143. package/tests/integration/teletext.js +0 -126
  144. package/tests/integration/timings.js +0 -56
  145. package/tests/integration/via.js +0 -1125
  146. package/tests/suite/README.md +0 -7
  147. package/tests/suite/Test Suite 2.15.txt +0 -373
  148. package/tests/suite/bin/ start +0 -0
  149. package/tests/suite/bin/adca +0 -0
  150. package/tests/suite/bin/adcax +0 -0
  151. package/tests/suite/bin/adcay +0 -0
  152. package/tests/suite/bin/adcb +0 -0
  153. package/tests/suite/bin/adcix +0 -0
  154. package/tests/suite/bin/adciy +0 -0
  155. package/tests/suite/bin/adcz +0 -0
  156. package/tests/suite/bin/adczx +0 -0
  157. package/tests/suite/bin/alrb +0 -0
  158. package/tests/suite/bin/ancb +0 -0
  159. package/tests/suite/bin/anda +0 -0
  160. package/tests/suite/bin/andax +0 -0
  161. package/tests/suite/bin/anday +0 -0
  162. package/tests/suite/bin/andb +0 -0
  163. package/tests/suite/bin/andix +0 -0
  164. package/tests/suite/bin/andiy +0 -0
  165. package/tests/suite/bin/andz +0 -0
  166. package/tests/suite/bin/andzx +0 -0
  167. package/tests/suite/bin/aneb +0 -0
  168. package/tests/suite/bin/arrb +0 -0
  169. package/tests/suite/bin/asla +0 -0
  170. package/tests/suite/bin/aslax +0 -0
  171. package/tests/suite/bin/asln +0 -0
  172. package/tests/suite/bin/aslz +0 -0
  173. package/tests/suite/bin/aslzx +0 -0
  174. package/tests/suite/bin/asoa +0 -0
  175. package/tests/suite/bin/asoax +0 -0
  176. package/tests/suite/bin/asoay +0 -0
  177. package/tests/suite/bin/asoix +0 -0
  178. package/tests/suite/bin/asoiy +0 -0
  179. package/tests/suite/bin/asoz +0 -0
  180. package/tests/suite/bin/asozx +0 -0
  181. package/tests/suite/bin/axsa +0 -0
  182. package/tests/suite/bin/axsix +0 -0
  183. package/tests/suite/bin/axsz +0 -0
  184. package/tests/suite/bin/axszy +0 -0
  185. package/tests/suite/bin/bccr +0 -0
  186. package/tests/suite/bin/bcsr +0 -0
  187. package/tests/suite/bin/beqr +0 -0
  188. package/tests/suite/bin/bita +0 -0
  189. package/tests/suite/bin/bitz +0 -0
  190. package/tests/suite/bin/bmir +0 -0
  191. package/tests/suite/bin/bner +0 -0
  192. package/tests/suite/bin/bplr +0 -0
  193. package/tests/suite/bin/branchwrap +0 -0
  194. package/tests/suite/bin/brkn +0 -0
  195. package/tests/suite/bin/bvcr +0 -0
  196. package/tests/suite/bin/bvsr +0 -0
  197. package/tests/suite/bin/cia1pb6 +0 -0
  198. package/tests/suite/bin/cia1pb7 +0 -0
  199. package/tests/suite/bin/cia1ta +0 -0
  200. package/tests/suite/bin/cia1tab +0 -0
  201. package/tests/suite/bin/cia1tb +0 -0
  202. package/tests/suite/bin/cia1tb123 +0 -0
  203. package/tests/suite/bin/cia2pb6 +0 -0
  204. package/tests/suite/bin/cia2pb7 +0 -0
  205. package/tests/suite/bin/cia2ta +0 -0
  206. package/tests/suite/bin/cia2tb +0 -0
  207. package/tests/suite/bin/cia2tb123 +0 -0
  208. package/tests/suite/bin/clcn +0 -0
  209. package/tests/suite/bin/cldn +0 -0
  210. package/tests/suite/bin/clin +0 -0
  211. package/tests/suite/bin/clvn +0 -0
  212. package/tests/suite/bin/cmpa +0 -0
  213. package/tests/suite/bin/cmpax +0 -0
  214. package/tests/suite/bin/cmpay +0 -0
  215. package/tests/suite/bin/cmpb +0 -0
  216. package/tests/suite/bin/cmpix +0 -0
  217. package/tests/suite/bin/cmpiy +0 -0
  218. package/tests/suite/bin/cmpz +0 -0
  219. package/tests/suite/bin/cmpzx +0 -0
  220. package/tests/suite/bin/cntdef +0 -0
  221. package/tests/suite/bin/cnto2 +0 -0
  222. package/tests/suite/bin/cpuport +0 -0
  223. package/tests/suite/bin/cputiming +0 -0
  224. package/tests/suite/bin/cpxa +0 -0
  225. package/tests/suite/bin/cpxb +0 -0
  226. package/tests/suite/bin/cpxz +0 -0
  227. package/tests/suite/bin/cpya +0 -0
  228. package/tests/suite/bin/cpyb +0 -0
  229. package/tests/suite/bin/cpyz +0 -0
  230. package/tests/suite/bin/dcma +0 -0
  231. package/tests/suite/bin/dcmax +0 -0
  232. package/tests/suite/bin/dcmay +0 -0
  233. package/tests/suite/bin/dcmix +0 -0
  234. package/tests/suite/bin/dcmiy +0 -0
  235. package/tests/suite/bin/dcmz +0 -0
  236. package/tests/suite/bin/dcmzx +0 -0
  237. package/tests/suite/bin/deca +0 -0
  238. package/tests/suite/bin/decax +0 -0
  239. package/tests/suite/bin/decz +0 -0
  240. package/tests/suite/bin/deczx +0 -0
  241. package/tests/suite/bin/dexn +0 -0
  242. package/tests/suite/bin/deyn +0 -0
  243. package/tests/suite/bin/eora +0 -0
  244. package/tests/suite/bin/eorax +0 -0
  245. package/tests/suite/bin/eoray +0 -0
  246. package/tests/suite/bin/eorb +0 -0
  247. package/tests/suite/bin/eorix +0 -0
  248. package/tests/suite/bin/eoriy +0 -0
  249. package/tests/suite/bin/eorz +0 -0
  250. package/tests/suite/bin/eorzx +0 -0
  251. package/tests/suite/bin/finish +0 -0
  252. package/tests/suite/bin/flipos +0 -0
  253. package/tests/suite/bin/icr01 +0 -0
  254. package/tests/suite/bin/imr +0 -0
  255. package/tests/suite/bin/inca +0 -0
  256. package/tests/suite/bin/incax +0 -0
  257. package/tests/suite/bin/incz +0 -0
  258. package/tests/suite/bin/inczx +0 -0
  259. package/tests/suite/bin/insa +0 -0
  260. package/tests/suite/bin/insax +0 -0
  261. package/tests/suite/bin/insay +0 -0
  262. package/tests/suite/bin/insix +0 -0
  263. package/tests/suite/bin/insiy +0 -0
  264. package/tests/suite/bin/insz +0 -0
  265. package/tests/suite/bin/inszx +0 -0
  266. package/tests/suite/bin/inxn +0 -0
  267. package/tests/suite/bin/inyn +0 -0
  268. package/tests/suite/bin/irq +0 -0
  269. package/tests/suite/bin/jmpi +0 -0
  270. package/tests/suite/bin/jmpw +0 -0
  271. package/tests/suite/bin/jsrw +0 -0
  272. package/tests/suite/bin/lasay +0 -0
  273. package/tests/suite/bin/laxa +0 -0
  274. package/tests/suite/bin/laxay +0 -0
  275. package/tests/suite/bin/laxix +0 -0
  276. package/tests/suite/bin/laxiy +0 -0
  277. package/tests/suite/bin/laxz +0 -0
  278. package/tests/suite/bin/laxzy +0 -0
  279. package/tests/suite/bin/ldaa +0 -0
  280. package/tests/suite/bin/ldaax +0 -0
  281. package/tests/suite/bin/ldaay +0 -0
  282. package/tests/suite/bin/ldab +0 -0
  283. package/tests/suite/bin/ldaix +0 -0
  284. package/tests/suite/bin/ldaiy +0 -0
  285. package/tests/suite/bin/ldaz +0 -0
  286. package/tests/suite/bin/ldazx +0 -0
  287. package/tests/suite/bin/ldxa +0 -0
  288. package/tests/suite/bin/ldxay +0 -0
  289. package/tests/suite/bin/ldxb +0 -0
  290. package/tests/suite/bin/ldxz +0 -0
  291. package/tests/suite/bin/ldxzy +0 -0
  292. package/tests/suite/bin/ldya +0 -0
  293. package/tests/suite/bin/ldyax +0 -0
  294. package/tests/suite/bin/ldyb +0 -0
  295. package/tests/suite/bin/ldyz +0 -0
  296. package/tests/suite/bin/ldyzx +0 -0
  297. package/tests/suite/bin/loadth +0 -0
  298. package/tests/suite/bin/lsea +0 -0
  299. package/tests/suite/bin/lseax +0 -0
  300. package/tests/suite/bin/lseay +0 -0
  301. package/tests/suite/bin/lseix +0 -0
  302. package/tests/suite/bin/lseiy +0 -0
  303. package/tests/suite/bin/lsez +0 -0
  304. package/tests/suite/bin/lsezx +0 -0
  305. package/tests/suite/bin/lsra +0 -0
  306. package/tests/suite/bin/lsrax +0 -0
  307. package/tests/suite/bin/lsrn +0 -0
  308. package/tests/suite/bin/lsrz +0 -0
  309. package/tests/suite/bin/lsrzx +0 -0
  310. package/tests/suite/bin/lxab +0 -0
  311. package/tests/suite/bin/mmu +0 -0
  312. package/tests/suite/bin/mmufetch +0 -0
  313. package/tests/suite/bin/nmi +0 -0
  314. package/tests/suite/bin/nopa +0 -0
  315. package/tests/suite/bin/nopax +0 -0
  316. package/tests/suite/bin/nopb +0 -0
  317. package/tests/suite/bin/nopn +0 -0
  318. package/tests/suite/bin/nopz +0 -0
  319. package/tests/suite/bin/nopzx +0 -0
  320. package/tests/suite/bin/oneshot +0 -0
  321. package/tests/suite/bin/oraa +0 -0
  322. package/tests/suite/bin/oraax +0 -0
  323. package/tests/suite/bin/oraay +0 -0
  324. package/tests/suite/bin/orab +0 -0
  325. package/tests/suite/bin/oraix +0 -0
  326. package/tests/suite/bin/oraiy +0 -0
  327. package/tests/suite/bin/oraz +0 -0
  328. package/tests/suite/bin/orazx +0 -0
  329. package/tests/suite/bin/phan +0 -0
  330. package/tests/suite/bin/phpn +0 -0
  331. package/tests/suite/bin/plan +0 -0
  332. package/tests/suite/bin/plpn +0 -0
  333. package/tests/suite/bin/rlaa +0 -0
  334. package/tests/suite/bin/rlaax +0 -0
  335. package/tests/suite/bin/rlaay +0 -0
  336. package/tests/suite/bin/rlaix +0 -0
  337. package/tests/suite/bin/rlaiy +0 -0
  338. package/tests/suite/bin/rlaz +0 -0
  339. package/tests/suite/bin/rlazx +0 -0
  340. package/tests/suite/bin/rola +0 -0
  341. package/tests/suite/bin/rolax +0 -0
  342. package/tests/suite/bin/roln +0 -0
  343. package/tests/suite/bin/rolz +0 -0
  344. package/tests/suite/bin/rolzx +0 -0
  345. package/tests/suite/bin/rora +0 -0
  346. package/tests/suite/bin/rorax +0 -0
  347. package/tests/suite/bin/rorn +0 -0
  348. package/tests/suite/bin/rorz +0 -0
  349. package/tests/suite/bin/rorzx +0 -0
  350. package/tests/suite/bin/rraa +0 -0
  351. package/tests/suite/bin/rraax +0 -0
  352. package/tests/suite/bin/rraay +0 -0
  353. package/tests/suite/bin/rraix +0 -0
  354. package/tests/suite/bin/rraiy +0 -0
  355. package/tests/suite/bin/rraz +0 -0
  356. package/tests/suite/bin/rrazx +0 -0
  357. package/tests/suite/bin/rtin +0 -0
  358. package/tests/suite/bin/rtsn +0 -0
  359. package/tests/suite/bin/sbca +0 -0
  360. package/tests/suite/bin/sbcax +0 -0
  361. package/tests/suite/bin/sbcay +0 -0
  362. package/tests/suite/bin/sbcb +0 -0
  363. package/tests/suite/bin/sbcb(eb) +0 -0
  364. package/tests/suite/bin/sbcix +0 -0
  365. package/tests/suite/bin/sbciy +0 -0
  366. package/tests/suite/bin/sbcz +0 -0
  367. package/tests/suite/bin/sbczx +0 -0
  368. package/tests/suite/bin/sbxb +0 -0
  369. package/tests/suite/bin/secn +0 -0
  370. package/tests/suite/bin/sedn +0 -0
  371. package/tests/suite/bin/sein +0 -0
  372. package/tests/suite/bin/shaay +0 -0
  373. package/tests/suite/bin/shaiy +0 -0
  374. package/tests/suite/bin/shsay +0 -0
  375. package/tests/suite/bin/shxay +0 -0
  376. package/tests/suite/bin/shyax +0 -0
  377. package/tests/suite/bin/staa +0 -0
  378. package/tests/suite/bin/staax +0 -0
  379. package/tests/suite/bin/staay +0 -0
  380. package/tests/suite/bin/staix +0 -0
  381. package/tests/suite/bin/staiy +0 -0
  382. package/tests/suite/bin/staz +0 -0
  383. package/tests/suite/bin/stazx +0 -0
  384. package/tests/suite/bin/stxa +0 -0
  385. package/tests/suite/bin/stxz +0 -0
  386. package/tests/suite/bin/stxzy +0 -0
  387. package/tests/suite/bin/stya +0 -0
  388. package/tests/suite/bin/styz +0 -0
  389. package/tests/suite/bin/styzx +0 -0
  390. package/tests/suite/bin/taxn +0 -0
  391. package/tests/suite/bin/tayn +0 -0
  392. package/tests/suite/bin/trap1 +0 -0
  393. package/tests/suite/bin/trap10 +0 -0
  394. package/tests/suite/bin/trap11 +0 -0
  395. package/tests/suite/bin/trap12 +0 -0
  396. package/tests/suite/bin/trap13 +0 -0
  397. package/tests/suite/bin/trap14 +0 -0
  398. package/tests/suite/bin/trap15 +0 -0
  399. package/tests/suite/bin/trap16 +0 -0
  400. package/tests/suite/bin/trap17 +0 -0
  401. package/tests/suite/bin/trap2 +0 -0
  402. package/tests/suite/bin/trap3 +0 -0
  403. package/tests/suite/bin/trap4 +0 -0
  404. package/tests/suite/bin/trap5 +0 -0
  405. package/tests/suite/bin/trap6 +0 -0
  406. package/tests/suite/bin/trap7 +0 -0
  407. package/tests/suite/bin/trap8 +0 -0
  408. package/tests/suite/bin/trap9 +0 -0
  409. package/tests/suite/bin/tsxn +0 -0
  410. package/tests/suite/bin/txan +0 -0
  411. package/tests/suite/bin/txsn +0 -0
  412. package/tests/suite/bin/tyan +0 -0
  413. package/tests/suite/cbm-hackers-post.html +0 -178
  414. package/tests/suite/cbm-hackers-post.md +0 -78
  415. package/tests/test-suite.js +0 -147
  416. package/tests/test.css +0 -7
  417. package/tests/unit/gzip/test-1 +0 -0
  418. package/tests/unit/gzip/test-1.gz +0 -0
  419. package/tests/unit/gzip/test-2 +0 -0
  420. package/tests/unit/gzip/test-2.gz +0 -0
  421. package/tests/unit/gzip/test-3 +0 -0
  422. package/tests/unit/gzip/test-3.gz +0 -0
  423. package/tests/unit/gzip/test-4 +0 -0
  424. package/tests/unit/gzip/test-4.gz +0 -0
  425. package/tests/unit/test-adc.js +0 -307
  426. package/tests/unit/test-bcd.js +0 -30
  427. package/tests/unit/test-cmos.js +0 -266
  428. package/tests/unit/test-disc-drive.js +0 -85
  429. package/tests/unit/test-disc-hfe.js +0 -347
  430. package/tests/unit/test-disc.js +0 -232
  431. package/tests/unit/test-fifo.js +0 -35
  432. package/tests/unit/test-gamepad-source.js +0 -67
  433. package/tests/unit/test-gzip.js +0 -22
  434. package/tests/unit/test-intel-fdc.js +0 -93
  435. package/tests/unit/test-keyboard.js +0 -410
  436. package/tests/unit/test-mouse-joystick-source.js +0 -128
  437. package/tests/unit/test-scheduler.js +0 -190
  438. package/tests/unit/test-serial.js +0 -154
  439. package/tests/unit/test-teletext-adaptor.js +0 -359
  440. package/tests/unit/test-tokenise.js +0 -65
  441. package/tests/unit/test-url-params.js +0 -398
  442. package/tests/unit/test-utils.js +0 -276
  443. package/tests/unit/test-video.js +0 -498
  444. package/tests/unit/test-zip.js +0 -56
  445. package/tests/unit/zip/test-mixed.zip +0 -0
  446. package/tests/unit/zip/test-rom.zip +0 -0
  447. package/tests/unit/zip/test-ssd.zip +0 -0
  448. package/tools/fir-generator.js +0 -80
  449. package/tools/vite-plugin-fir-shader.js +0 -131
  450. package/vite.config.js +0 -34
@@ -0,0 +1,396 @@
1
+ /**
2
+ * MachineSession - wraps jsbeeb's TestMachine with:
3
+ * - real Video framebuffer (so screenshots work)
4
+ * - accumulated text output between calls
5
+ * - clean lifecycle (boot, interact, screenshot, destroy)
6
+ */
7
+
8
+ import { readFileSync } from "fs";
9
+ import { fileURLToPath } from "url";
10
+ import path from "path";
11
+ import { TestMachine } from "../tests/test-machine.js";
12
+
13
+ // Resolve the jsbeeb package root at module load time using our own location
14
+ // (src/machine-session.js → go up one level). We chdir here during
15
+ // initialise() so jsbeeb's ROM loader ("roms/os.rom" etc.) always resolves
16
+ // correctly, regardless of where the calling process was started from.
17
+ const _jsbeebRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
18
+ import * as fdc from "./fdc.js";
19
+ import { Video } from "./video.js";
20
+ import { findModel } from "./models.js";
21
+ import sharp from "sharp";
22
+
23
+ // BBC framebuffer is 1024×625 pixels
24
+ const FB_WIDTH = 1024;
25
+ const FB_HEIGHT = 625;
26
+
27
+ export class MachineSession {
28
+ /**
29
+ * @param {string} modelName - e.g. "B-DFS1.2", "Master"
30
+ */
31
+ constructor(modelName = "B-DFS1.2") {
32
+ this.modelName = modelName;
33
+
34
+ // Raw RGBA framebuffer — the Video chip renders into _fb32 (cleared each frame).
35
+ // _completeFb8 is a snapshot taken at paint time (the equivalent of the browser canvas)
36
+ // and is what screenshot() reads from — always a complete frame, never mid-render.
37
+ this._fb8 = new Uint8Array(FB_WIDTH * FB_HEIGHT * 4);
38
+ this._fb32 = new Uint32Array(this._fb8.buffer);
39
+ this._completeFb8 = new Uint8Array(FB_WIDTH * FB_HEIGHT * 4);
40
+ this._lastPaint = { minx: 0, miny: 0, maxx: FB_WIDTH, maxy: FB_HEIGHT };
41
+ this._frameDirty = false;
42
+
43
+ // Create a real Video instance so we get pixel output
44
+ const modelObj = findModel(modelName);
45
+ this._video = new Video(modelObj.isMaster, this._fb32, (minx, miny, maxx, maxy) => {
46
+ this._lastPaint = { minx, miny, maxx, maxy };
47
+ this._frameDirty = true;
48
+ // Snapshot the complete frame now, before clearPaintBuffer() wipes _fb32.
49
+ // This mirrors what the browser does: paint_ext fires → canvas updated → fb32 cleared.
50
+ this._completeFb8.set(this._fb8);
51
+ });
52
+
53
+ // TestMachine forwards opts.video to fake6502, which uses it instead of FakeVideo
54
+ this._machine = new TestMachine(modelName, { video: this._video });
55
+
56
+ // Accumulated VDU text output — drained by callers
57
+ this._pendingOutput = [];
58
+ }
59
+
60
+ /** Load ROMs and hardware — call once before anything else */
61
+ async initialise() {
62
+ // jsbeeb's ROM loader uses paths relative to cwd ("roms/os.rom" etc.).
63
+ // Chdir to the package root so it works regardless of where the
64
+ // calling process was launched from.
65
+ process.chdir(_jsbeebRoot);
66
+ await this._machine.initialise();
67
+ this._installCaptureHook();
68
+ }
69
+
70
+ /**
71
+ * Boot the machine (run until the BASIC prompt).
72
+ * Returns captured boot-screen text (the OS banner etc.).
73
+ */
74
+ async boot(timeoutSecs = 30) {
75
+ await this._machine.runUntilInput(timeoutSecs);
76
+ return this.drainOutput();
77
+ }
78
+
79
+ /**
80
+ * Install the VDU character-output capture hook.
81
+ *
82
+ * WRCHV discovery: RAM at 0x20E/0x20F initialises to 0xFFFF before the
83
+ * OS runs. We read directly from cpu.ramRomOs (two array lookups — no
84
+ * readmem() dispatch overhead) on every instruction, waiting for the
85
+ * value to change from its initial 0xFFFF. Once the OS installs a real
86
+ * handler we use that address for the lifetime of the session. Programs
87
+ * that later install a custom VDU driver are handled seamlessly because
88
+ * we always re-read from the live memory.
89
+ *
90
+ * Text elements: { x, y, text, foreground, background, mode }
91
+ * Screenshots (via the real Video chip) are the right tool for anything
92
+ * visual; this capture is a lightweight aid for text-mode output only.
93
+ */
94
+ _installCaptureHook() {
95
+ const cpu = this._machine.processor;
96
+ const ram = cpu.ramRomOs; // direct Uint8Array — no dispatch overhead
97
+ const initialWrchv = ram[0x20e] | (ram[0x20f] << 8); // 0xFFFF pre-boot
98
+
99
+ const attributes = { x: 0, y: 0, text: "", foreground: 7, background: 0, mode: 7 };
100
+ let currentText = "";
101
+ let params = [];
102
+ let nextN = 0;
103
+ let vduProc = null;
104
+
105
+ const onElement = (elem) => this._pendingOutput.push({ ...elem });
106
+
107
+ function flush() {
108
+ if (currentText.length) {
109
+ attributes.text = currentText;
110
+ onElement({ ...attributes });
111
+ attributes.x += currentText.length;
112
+ }
113
+ currentText = "";
114
+ }
115
+
116
+ function onChar(c) {
117
+ if (nextN) {
118
+ params.push(c);
119
+ if (--nextN === 0) {
120
+ if (vduProc) vduProc(params);
121
+ params = [];
122
+ vduProc = null;
123
+ }
124
+ return;
125
+ }
126
+ switch (c) {
127
+ case 1: // next char to printer only
128
+ nextN = 1;
129
+ break;
130
+ case 10: // LF
131
+ flush();
132
+ attributes.y++;
133
+ break;
134
+ case 12: // CLS
135
+ flush();
136
+ attributes.x = 0;
137
+ attributes.y = 0;
138
+ break;
139
+ case 13: // CR
140
+ flush();
141
+ attributes.x = 0;
142
+ break;
143
+ case 17: // COLOUR n
144
+ nextN = 1;
145
+ vduProc = (p) => {
146
+ if (p[0] & 0x80) attributes.background = p[0] & 0xf;
147
+ else attributes.foreground = p[0] & 0xf;
148
+ };
149
+ break;
150
+ case 18: // GCOL
151
+ nextN = 2;
152
+ break;
153
+ case 19: // define logical colour
154
+ nextN = 5;
155
+ break;
156
+ case 22: // MODE n
157
+ nextN = 1;
158
+ vduProc = (p) => {
159
+ flush();
160
+ attributes.mode = p[0];
161
+ attributes.x = 0;
162
+ attributes.y = 0;
163
+ attributes.foreground = 7;
164
+ attributes.background = 0;
165
+ };
166
+ break;
167
+ case 25: // PLOT
168
+ nextN = 5;
169
+ break;
170
+ case 28: // define text window
171
+ nextN = 4;
172
+ break;
173
+ case 29: // define graphics origin
174
+ nextN = 4;
175
+ break;
176
+ case 31: // TAB(x,y)
177
+ nextN = 2;
178
+ vduProc = (p) => {
179
+ flush();
180
+ attributes.x = p[0];
181
+ attributes.y = p[1];
182
+ };
183
+ break;
184
+ default:
185
+ if (c >= 32 && c < 0x7f) {
186
+ currentText += String.fromCharCode(c);
187
+ } else {
188
+ flush();
189
+ }
190
+ break;
191
+ }
192
+ }
193
+
194
+ cpu.debugInstruction.add((addr) => {
195
+ // Two direct array reads — no function-call dispatch overhead.
196
+ // Once the OS sets WRCHV (it changes from 0xFFFF), we start
197
+ // capturing. Programs that install a custom VDU driver mid-run
198
+ // are handled transparently because we re-read on every call.
199
+ const wrchv = ram[0x20e] | (ram[0x20f] << 8);
200
+ if (wrchv !== initialWrchv && addr === wrchv) {
201
+ onChar(cpu.a);
202
+ }
203
+ return false;
204
+ });
205
+ }
206
+
207
+ /** Tokenise BBC BASIC source and write it into PAGE */
208
+ async loadBasic(source) {
209
+ await this._machine.loadBasic(source);
210
+ }
211
+
212
+ /**
213
+ * Simulate keypresses. Note: each character needs a short run to be
214
+ * picked up by the OS, so this is internally async and slow-ish (by
215
+ * emulated-time).
216
+ */
217
+ async type(text) {
218
+ await this._machine.type(text);
219
+ }
220
+
221
+ /**
222
+ * Run the emulator until the OS is waiting at the keyboard prompt, or
223
+ * until timeoutSecs of emulated time elapses. Returns captured output.
224
+ *
225
+ * @param {number} [timeoutSecs=60]
226
+ * @param {Object} [opts]
227
+ * @param {boolean} [opts.clear=true] - Whether to clear the output buffer after returning it.
228
+ */
229
+ async runUntilPrompt(timeoutSecs = 60, { clear = true } = {}) {
230
+ await this._machine.runUntilInput(timeoutSecs);
231
+ return this.drainOutput({ clear });
232
+ }
233
+
234
+ /**
235
+ * Run for an exact number of emulated CPU cycles.
236
+ * Useful for timing-sensitive code.
237
+ */
238
+ async runFor(cycles) {
239
+ await this._machine.runFor(cycles);
240
+ }
241
+
242
+ /**
243
+ * Run until PC reaches targetAddr (like a breakpoint), or timeout.
244
+ */
245
+ async runUntilAddress(addr, timeoutSecs = 30) {
246
+ await this._machine.runUntilAddress(addr, timeoutSecs);
247
+ }
248
+
249
+ /**
250
+ * Load a disc image (absolute or relative path to an .ssd or .dsd file).
251
+ *
252
+ * We read the file ourselves rather than delegating to TestMachine.loadDisc,
253
+ * which goes through utils.loadData and mangles absolute paths by prepending "./".
254
+ */
255
+ loadDisc(imagePath) {
256
+ const data = new Uint8Array(readFileSync(imagePath));
257
+ this._machine.processor.fdc.loadDisc(0, fdc.discFor(this._machine.processor.fdc, imagePath, data));
258
+ }
259
+
260
+ /**
261
+ * Return all VDU text elements captured so far.
262
+ *
263
+ * @param {Object} [opts]
264
+ * @param {boolean} [opts.clear=true] - If true (default), clear the buffer
265
+ * after returning it. Pass false to peek without consuming — the same
266
+ * elements will be returned again on the next call.
267
+ *
268
+ * Each element: { x, y, text, foreground, background, mode }
269
+ * Also includes a flat `screenText` reconstruction.
270
+ */
271
+ drainOutput({ clear = true } = {}) {
272
+ const elements = clear ? this._pendingOutput.splice(0) : [...this._pendingOutput];
273
+ return {
274
+ elements,
275
+ screenText: reconstructScreenText(elements),
276
+ };
277
+ }
278
+
279
+ /** Read `length` bytes from emulator memory starting at `address` */
280
+ readMemory(address, length = 16) {
281
+ const bytes = [];
282
+ for (let i = 0; i < length; i++) {
283
+ bytes.push(this._machine.readbyte(address + i));
284
+ }
285
+ return bytes;
286
+ }
287
+
288
+ /** Write an array of byte values into emulator memory at `address` */
289
+ writeMemory(address, bytes) {
290
+ for (let i = 0; i < bytes.length; i++) {
291
+ this._machine.writebyte(address + i, bytes[i]);
292
+ }
293
+ }
294
+
295
+ /** Read the current 6502 CPU registers */
296
+ registers() {
297
+ const cpu = this._machine.processor;
298
+ return {
299
+ pc: cpu.pc,
300
+ a: cpu.a,
301
+ x: cpu.x,
302
+ y: cpu.y,
303
+ s: cpu.s, // stack pointer
304
+ p: cpu.p, // processor status
305
+ pcHex: `0x${cpu.pc.toString(16).toUpperCase().padStart(4, "0")}`,
306
+ aHex: `0x${cpu.a.toString(16).toUpperCase().padStart(2, "0")}`,
307
+ xHex: `0x${cpu.x.toString(16).toUpperCase().padStart(2, "0")}`,
308
+ yHex: `0x${cpu.y.toString(16).toUpperCase().padStart(2, "0")}`,
309
+ };
310
+ }
311
+
312
+ /**
313
+ * Capture the current screen as a PNG.
314
+ * Returns a Buffer containing a 1024×625 PNG (the full emulated display,
315
+ * including borders — matches what the browser renders).
316
+ *
317
+ * The active display area is roughly:
318
+ * x: leftBorder .. 1024-rightBorder
319
+ * y: topBorder .. 625-bottomBorder
320
+ */
321
+ async screenshot() {
322
+ // Read from _completeFb8 — the last fully-painted frame snapshotted in paint_ext.
323
+ // _fb8/_fb32 is the live render buffer (cleared and partially refilled each frame).
324
+ return sharp(Buffer.from(this._completeFb8.buffer), {
325
+ raw: { width: FB_WIDTH, height: FB_HEIGHT, channels: 4 },
326
+ })
327
+ .png()
328
+ .toBuffer();
329
+ }
330
+
331
+ /**
332
+ * Capture only the active display area (no overscan borders), scaled to
333
+ * a more sensible pixel density. Returns a PNG Buffer.
334
+ *
335
+ * @param {Object} [opts]
336
+ * @param {number} [opts.scale=2] - integer scale factor
337
+ */
338
+ async screenshotActive(opts = {}) {
339
+ const scale = opts.scale ?? 2;
340
+ const v = this._video;
341
+ const left = v.leftBorder;
342
+ const top = v.topBorder;
343
+ const right = v.rightBorder;
344
+ const bottom = v.bottomBorder;
345
+ const w = FB_WIDTH - left - right;
346
+ const h = FB_HEIGHT - top - bottom;
347
+
348
+ return sharp(Buffer.from(this._completeFb8.buffer), {
349
+ raw: { width: FB_WIDTH, height: FB_HEIGHT, channels: 4 },
350
+ })
351
+ .extract({ left, top, width: w, height: h })
352
+ .resize(w * scale, h * scale, { kernel: "nearest" }) // nearest-neighbour keeps pixels crisp
353
+ .png()
354
+ .toBuffer();
355
+ }
356
+
357
+ /** Free any resources (currently just clears the framebuffer) */
358
+ destroy() {
359
+ this._fb8.fill(0);
360
+ this._pendingOutput = [];
361
+ }
362
+ }
363
+
364
+ // ---------------------------------------------------------------------------
365
+ // Helpers
366
+ // ---------------------------------------------------------------------------
367
+
368
+ /**
369
+ * Reconstruct a flat text representation from the captured VDU elements.
370
+ * Builds a 2D grid of characters and returns it as a newline-separated string.
371
+ */
372
+ function reconstructScreenText(elements) {
373
+ if (elements.length === 0) return "";
374
+
375
+ // Find bounds
376
+ let maxX = 0;
377
+ let maxY = 0;
378
+ for (const el of elements) {
379
+ const endX = el.x + el.text.length;
380
+ if (endX > maxX) maxX = endX;
381
+ if (el.y > maxY) maxY = el.y;
382
+ }
383
+
384
+ // Fill grid
385
+ const rows = Array.from({ length: maxY + 1 }, () => Array(maxX + 1).fill(" "));
386
+ for (const el of elements) {
387
+ for (let i = 0; i < el.text.length; i++) {
388
+ const col = el.x + i;
389
+ if (col < rows[el.y].length) {
390
+ rows[el.y][col] = el.text[i];
391
+ }
392
+ }
393
+ }
394
+
395
+ return rows.map((r) => r.join("").trimEnd()).join("\n");
396
+ }
package/src/main.js CHANGED
@@ -129,7 +129,7 @@ const emuKeyHandlers = {};
129
129
  let cpuMultiplier = 1;
130
130
  let fastAsPossible = false;
131
131
  let fastTape = false;
132
- let noSeek = false;
132
+ let noSeek;
133
133
  let audioFilterFreq = 7000;
134
134
  let audioFilterQ = 5;
135
135
  let stationId = 101;
@@ -287,7 +287,7 @@ function sbBind(div, url, onload) {
287
287
  const img = div.find("img");
288
288
  img.hide();
289
289
  if (!url) return;
290
- img.attr("src", url).bind("load", function () {
290
+ img.attr("src", url).on("load", function () {
291
291
  onload(div, img);
292
292
  img.show();
293
293
  });
@@ -348,7 +348,7 @@ function swapCanvas(newFilterClass) {
348
348
  };
349
349
  canvas = newCanvas;
350
350
  displayModeFilter = newFilterClass;
351
- window.setTimeout(() => window.onresize(), 1);
351
+ window.setTimeout(() => window.dispatchEvent(new Event("resize")), 1);
352
352
  }
353
353
 
354
354
  let canvas = createCanvasForFilter(displayModeFilter);
@@ -766,6 +766,13 @@ function setDisc1Image(name) {
766
766
  delete parsedQuery.disc;
767
767
  parsedQuery.disc1 = name;
768
768
  updateUrl();
769
+ config.emit("media-changed", { disc1: name });
770
+ }
771
+
772
+ function setTapeImage(name) {
773
+ parsedQuery.tape = name;
774
+ updateUrl();
775
+ config.emit("media-changed", { tape: name });
769
776
  }
770
777
 
771
778
  function sthClearList() {
@@ -804,8 +811,7 @@ async function discSthClick(item) {
804
811
 
805
812
  async function tapeSthClick(item) {
806
813
  utils.noteEvent("sth", "clickTape", item);
807
- parsedQuery.tape = "sth:" + item;
808
- updateUrl();
814
+ setTapeImage("sth:" + item);
809
815
 
810
816
  popupLoading("Loading " + item);
811
817
  try {
@@ -1027,7 +1033,8 @@ async function loadTapeImage(tapeImage) {
1027
1033
  }
1028
1034
 
1029
1035
  case "http":
1030
- case "https": {
1036
+ case "https":
1037
+ case "file": {
1031
1038
  const asUrl = `${schema}://${tapeImage}`;
1032
1039
  // url may end in query params etc, which can upset file handling
1033
1040
  tapeImage = new URL(asUrl).pathname;
@@ -1750,7 +1757,29 @@ window.m7dump = function () {
1750
1757
  };
1751
1758
 
1752
1759
  // Hooks for electron.
1753
- electron({ loadDiscImage, processor });
1760
+ electron({
1761
+ loadDiscImage,
1762
+ loadTapeImage,
1763
+ processor,
1764
+ config,
1765
+ modals: {
1766
+ show: (modalId, sthType) => {
1767
+ if (modalId === "sth" && sthType) {
1768
+ if (sthType === "discs") discSth.populate();
1769
+ else if (sthType === "tapes") tapeSth.populate();
1770
+ }
1771
+ const modalEl = document.getElementById(modalId);
1772
+ if (modalEl) {
1773
+ const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
1774
+ modal.show();
1775
+ }
1776
+ },
1777
+ },
1778
+ actions: {
1779
+ "soft-reset": () => processor.reset(false),
1780
+ "hard-reset": () => processor.reset(true),
1781
+ },
1782
+ });
1754
1783
 
1755
1784
  // Display version in About dialog
1756
1785
  const versionElement = document.getElementById("jsbeeb-version");
@@ -1,3 +1,4 @@
1
+ /* global registerProcessor, AudioWorkletProcessor */
1
2
  const BUFFER_SIZE = 65536;
2
3
 
3
4
  registerProcessor(
package/src/music5000.js CHANGED
@@ -112,15 +112,7 @@ export class Music5000 {
112
112
  }
113
113
 
114
114
  polltime(cycles) {
115
- let c4d,
116
- freq,
117
- offset,
118
- wavetable,
119
- amplitude,
120
- control,
121
- data,
122
- sign,
123
- pos = 0 >>> 0;
115
+ let c4d, freq, offset, wavetable, amplitude, control, data, sign, pos;
124
116
 
125
117
  // Convert 2MHz 6502 cycles to 6MHz Music5000 cycles
126
118
  this.cycleCount += cycles * 3;
package/src/sth.js CHANGED
@@ -23,7 +23,9 @@ async function _fetchAndParseCatalog(url) {
23
23
 
24
24
  export class StairwayToHell {
25
25
  constructor(onStart, onCat, onError, tape) {
26
- this._baseUrl = `${document.location.protocol}//${sthArchive}/${tape ? "tape" : "disk"}images/`;
26
+ // Use https explicitly - document.location.protocol is 'file:' in Electron
27
+ const protocol = document.location.protocol === "file:" ? "https:" : document.location.protocol;
28
+ this._baseUrl = `${protocol}//${sthArchive}/${tape ? "tape" : "disk"}images/`;
27
29
  this._catalog = [];
28
30
  this._onStart = onStart;
29
31
  this._onCat = onCat;
package/src/utils.js CHANGED
@@ -890,7 +890,7 @@ export function ungzip(data) {
890
890
  try {
891
891
  return pakoUngzip(data);
892
892
  } catch (e) {
893
- throw new Error("Unable to ungzip: " + e.message);
893
+ throw new Error("Unable to ungzip: " + e.message, { cause: e });
894
894
  }
895
895
  }
896
896
 
@@ -1000,7 +1000,7 @@ function unzipImage(data, knownExtensions) {
1000
1000
  try {
1001
1001
  files = unzipSync(data instanceof Uint8Array ? data : new Uint8Array(data));
1002
1002
  } catch (e) {
1003
- throw new Error("Error unzipping " + e.message);
1003
+ throw new Error("Error unzipping " + e.message, { cause: e });
1004
1004
  }
1005
1005
 
1006
1006
  let uncompressed = null;
@@ -1,4 +1,4 @@
1
- /*global sampleRate, currentTime */
1
+ /* global sampleRate, currentTime, registerProcessor, AudioWorkletProcessor */
2
2
 
3
3
  const lowPassFilterFreq = sampleRate / 2;
4
4
  const RC = 1 / (2 * Math.PI * lowPassFilterFreq);
package/src/web/debug.js CHANGED
@@ -22,7 +22,7 @@ class MemoryView {
22
22
  }
23
23
  template.remove();
24
24
 
25
- widget.bind("wheel", (evt) => {
25
+ widget.on("wheel", (evt) => {
26
26
  const deltaY = evt.originalEvent.deltaY;
27
27
  if (deltaY === 0) return;
28
28
  const steps = (deltaY / 20) | 0;
@@ -111,7 +111,7 @@ export class Debugger {
111
111
 
112
112
  this.disass.find(".bp_gutter").click(this.bpClick.bind(this));
113
113
 
114
- this.disass.bind("wheel", (evt) => {
114
+ this.disass.on("wheel", (evt) => {
115
115
  let deltaY = evt.originalEvent.deltaY;
116
116
  if (deltaY === 0) return;
117
117
  let addr = this.disassPc;
@@ -119,9 +119,12 @@ export class TestMachine {
119
119
 
120
120
  const typeChar = (ch) => {
121
121
  let shift = false;
122
+ // Map printable ASCII characters to BBC Micro key codes (UK layout).
123
+ // Characters not listed here fall through to the default case which
124
+ // uses toUpperCase().charCodeAt(0) — valid for A–Z and 0–9 only.
122
125
  switch (ch) {
123
126
  case '"':
124
- ch = 50;
127
+ ch = utils.keyCodes.K2;
125
128
  shift = true;
126
129
  break;
127
130
  case "*":
@@ -148,7 +151,85 @@ export class TestMachine {
148
151
  ch = utils.keyCodes.K6;
149
152
  shift = true;
150
153
  break;
154
+ case " ":
155
+ ch = utils.keyCodes.SPACE;
156
+ break;
157
+ case "-":
158
+ ch = utils.keyCodes.MINUS;
159
+ break;
160
+ case "=":
161
+ ch = utils.keyCodes.MINUS;
162
+ shift = true;
163
+ break;
164
+ case "+":
165
+ ch = utils.keyCodes.SEMICOLON;
166
+ shift = true;
167
+ break;
168
+ case "^":
169
+ ch = utils.keyCodes.EQUALS;
170
+ break;
171
+ case "~":
172
+ ch = utils.keyCodes.EQUALS;
173
+ shift = true;
174
+ break;
175
+ case "[":
176
+ ch = utils.keyCodes.LEFT_SQUARE_BRACKET;
177
+ break;
178
+ case "]":
179
+ ch = utils.keyCodes.RIGHT_SQUARE_BRACKET;
180
+ break;
181
+ case "{":
182
+ ch = utils.keyCodes.LEFT_SQUARE_BRACKET;
183
+ shift = true;
184
+ break;
185
+ case "}":
186
+ ch = utils.keyCodes.HASH;
187
+ shift = true;
188
+ break;
189
+ case "\\":
190
+ ch = utils.keyCodes.BACKSLASH;
191
+ break;
192
+ case "/":
193
+ ch = utils.keyCodes.SLASH;
194
+ break;
195
+ case "?":
196
+ ch = utils.keyCodes.SLASH;
197
+ shift = true;
198
+ break;
199
+ case "<":
200
+ ch = utils.keyCodes.COMMA;
201
+ shift = true;
202
+ break;
203
+ case ">":
204
+ ch = utils.keyCodes.PERIOD;
205
+ shift = true;
206
+ break;
207
+ case "(":
208
+ ch = utils.keyCodes.K8;
209
+ shift = true;
210
+ break;
211
+ case ")":
212
+ ch = utils.keyCodes.K9;
213
+ shift = true;
214
+ break;
215
+ case "@":
216
+ ch = utils.keyCodes.BACK_QUOTE;
217
+ break;
218
+ case "#":
219
+ ch = utils.keyCodes.K3;
220
+ shift = true;
221
+ break;
222
+ case "$":
223
+ ch = utils.keyCodes.K4;
224
+ shift = true;
225
+ break;
226
+ case "%":
227
+ ch = utils.keyCodes.K5;
228
+ shift = true;
229
+ break;
151
230
  default:
231
+ // A-Z and 0-9: ASCII value == DOM keyCode — works as-is.
232
+ // Anything else (e.g. '_', '£', '`') is not yet mapped.
152
233
  ch = ch.toUpperCase().charCodeAt(0);
153
234
  break;
154
235
  }
package/.editorconfig DELETED
@@ -1,15 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- end_of_line = lf
5
- insert_final_newline = true
6
- indent_style = space
7
- indent_size = 2
8
- max_line_length = 120
9
-
10
- [{*.js, *.html}]
11
- indent_size = 4
12
-
13
- [Makefile]
14
- indent_style = tab
15
-
@@ -1,3 +0,0 @@
1
- # .git-blame-ignore-revs
2
- # Prettier reformat all the things
3
- 9bfb8e402dec1addd8b86e19f84a8bcb52e0c595