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,307 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { Adc } from "../../src/adc.js";
3
+ import { AnalogueSource } from "../../src/analogue-source.js";
4
+
5
+ // Create a mock gamepad source for testing
6
+ class MockGamepadSource extends AnalogueSource {
7
+ constructor() {
8
+ super();
9
+ this.mockValues = {
10
+ 0: 0x3fff, // Channel 0: halfway (0.5) -> 0x3fff
11
+ 1: 0xffff, // Channel 1: max (-1.0) -> 0xffff
12
+ 2: 0x5fff, // Channel 2: quarter (0.25) -> 0x5fff
13
+ 3: 0xdfff, // Channel 3: three quarters (-0.75) -> 0xdfff
14
+ };
15
+ }
16
+
17
+ getValue(channel) {
18
+ return this.mockValues[channel] || 0x8000;
19
+ }
20
+ }
21
+
22
+ describe("ADC", () => {
23
+ // Mock dependencies
24
+ const mockSysvia = {
25
+ setcb1: vi.fn(),
26
+ };
27
+
28
+ const mockScheduler = {
29
+ newTask: vi.fn().mockImplementation((callback) => ({
30
+ schedule: vi.fn(),
31
+ cancel: vi.fn(),
32
+ callback,
33
+ })),
34
+ };
35
+
36
+ let adc;
37
+ let mockTask;
38
+ let mockGamepadSource;
39
+
40
+ beforeEach(() => {
41
+ // Reset mocks
42
+ vi.resetAllMocks();
43
+
44
+ // Create a mock task that stores the callback
45
+ mockTask = {
46
+ schedule: vi.fn(),
47
+ cancel: vi.fn(),
48
+ };
49
+ mockScheduler.newTask.mockReturnValue(mockTask);
50
+
51
+ // Create a fresh ADC instance
52
+ adc = new Adc(mockSysvia, mockScheduler);
53
+
54
+ mockGamepadSource = new MockGamepadSource();
55
+ adc.setChannelSource(0, mockGamepadSource);
56
+ adc.setChannelSource(1, mockGamepadSource);
57
+ adc.setChannelSource(2, mockGamepadSource);
58
+ adc.setChannelSource(3, mockGamepadSource);
59
+ });
60
+
61
+ afterEach(() => {
62
+ vi.resetAllMocks();
63
+ });
64
+
65
+ describe("Initialization", () => {
66
+ it("should initialize with default state", () => {
67
+ expect(adc.status).toBe(0x40);
68
+ expect(adc.low).toBe(0x00);
69
+ expect(adc.high).toBe(0x00);
70
+ expect(mockScheduler.newTask).toHaveBeenCalled();
71
+ });
72
+
73
+ it("should reset to default state", () => {
74
+ // Change state
75
+ adc.status = 0xff;
76
+ adc.low = 0xff;
77
+ adc.high = 0xff;
78
+
79
+ // Reset
80
+ adc.reset();
81
+
82
+ // Check state is back to default
83
+ expect(adc.status).toBe(0x40);
84
+ expect(adc.low).toBe(0x00);
85
+ expect(adc.high).toBe(0x00);
86
+ });
87
+ });
88
+
89
+ describe("Reading registers", () => {
90
+ it("should read status register (addr 0)", () => {
91
+ adc.status = 0x42;
92
+ expect(adc.read(0)).toBe(0x42);
93
+ });
94
+
95
+ it("should read high byte register (addr 1)", () => {
96
+ adc.high = 0x42;
97
+ expect(adc.read(1)).toBe(0x42);
98
+ });
99
+
100
+ it("should read low byte register (addr 2)", () => {
101
+ adc.low = 0x42;
102
+ expect(adc.read(2)).toBe(0x42);
103
+ });
104
+
105
+ it("should return 0x40 for undefined register (addr 3)", () => {
106
+ expect(adc.read(3)).toBe(0x40);
107
+ });
108
+
109
+ it("should correctly mask address when reading", () => {
110
+ adc.status = 0x42;
111
+ adc.high = 0x43;
112
+ adc.low = 0x44;
113
+
114
+ expect(adc.read(4)).toBe(0x42); // 4 & 3 = 0
115
+ expect(adc.read(5)).toBe(0x43); // 5 & 3 = 1
116
+ expect(adc.read(6)).toBe(0x44); // 6 & 3 = 2
117
+ expect(adc.read(7)).toBe(0x40); // 7 & 3 = 3
118
+ });
119
+ });
120
+
121
+ describe("Writing to control register", () => {
122
+ it("should ignore writes to non-control registers", () => {
123
+ // Write to addresses 1, 2, 3
124
+ adc.write(1, 0x42);
125
+ adc.write(2, 0x42);
126
+ adc.write(3, 0x42);
127
+
128
+ // Verify no conversion was started
129
+ expect(mockTask.cancel).not.toHaveBeenCalled();
130
+ expect(mockTask.schedule).not.toHaveBeenCalled();
131
+ expect(mockSysvia.setcb1).not.toHaveBeenCalled();
132
+ });
133
+
134
+ it("should start 8-bit conversion on write to control register", () => {
135
+ adc.write(0, 0x00); // 8-bit conversion (bit 3 not set)
136
+
137
+ expect(mockTask.cancel).toHaveBeenCalled();
138
+ expect(mockTask.schedule).toHaveBeenCalledWith(8000); // 8ms for 8-bit
139
+ expect(adc.status).toBe(0x80); // Busy bit set
140
+ expect(mockSysvia.setcb1).toHaveBeenCalledWith(true);
141
+ });
142
+
143
+ it("should start 10-bit conversion on write to control register", () => {
144
+ adc.write(0, 0x08); // 10-bit conversion (bit 3 set)
145
+
146
+ expect(mockTask.cancel).toHaveBeenCalled();
147
+ expect(mockTask.schedule).toHaveBeenCalledWith(20000); // 20ms for 10-bit
148
+ expect(adc.status).toBe(0x88); // Busy bit set, bit 3 set
149
+ expect(mockSysvia.setcb1).toHaveBeenCalledWith(true);
150
+ });
151
+
152
+ it("should store channel number in status register", () => {
153
+ adc.write(0, 0x02); // Channel 2
154
+ expect(adc.status & 0x0f).toBe(0x02);
155
+
156
+ adc.write(0, 0x03); // Channel 3
157
+ expect(adc.status & 0x0f).toBe(0x03);
158
+ });
159
+
160
+ it("should correctly mask address when writing", () => {
161
+ adc.write(4, 0x01); // 4 & 3 = 0
162
+ expect(adc.status & 0x0f).toBe(0x01);
163
+
164
+ // These should be ignored
165
+ adc.write(5, 0x02); // 5 & 3 = 1
166
+ adc.write(6, 0x03); // 6 & 3 = 2
167
+ adc.write(7, 0x04); // 7 & 3 = 3
168
+
169
+ // Status should still reflect the last valid write
170
+ expect(adc.status & 0x0f).toBe(0x01);
171
+ });
172
+ });
173
+
174
+ describe("Analogue sources", () => {
175
+ it("should set and get a channel source", () => {
176
+ const newSource = new MockGamepadSource();
177
+ const result = adc.setChannelSource(1, newSource);
178
+ expect(result).toBe(true);
179
+ expect(adc.getChannelSource(1)).toBe(newSource);
180
+ });
181
+
182
+ it("should clear a channel source", () => {
183
+ const result = adc.clearChannelSource(2);
184
+ expect(result).toBe(true);
185
+ expect(adc.getChannelSource(2)).toBe(null);
186
+ });
187
+
188
+ it("should clear all sources", () => {
189
+ adc.clearSources();
190
+ for (let i = 0; i < 4; i++) {
191
+ expect(adc.getChannelSource(i)).toBe(null);
192
+ }
193
+ });
194
+ });
195
+
196
+ describe("Conversion completion", () => {
197
+ it("should handle completion with no source for the channel", () => {
198
+ adc.clearChannelSource(1);
199
+
200
+ // Set up a conversion for channel 1
201
+ adc.write(0, 0x01);
202
+
203
+ // Reset mock to clearly see what happens during completion
204
+ mockSysvia.setcb1.mockReset();
205
+
206
+ // Simulate conversion completion
207
+ adc.onComplete();
208
+
209
+ // Check results
210
+ expect(adc.status & 0x80).toBe(0); // Busy bit cleared
211
+ expect(adc.status & 0x40).toBe(0x40); // End of conversion bit set
212
+ expect(adc.low).toBe(0); // Default value low byte
213
+ expect(adc.high).toBe(0x80); // Default value high byte (0x8000 >> 8)
214
+ expect(mockSysvia.setcb1).toHaveBeenCalledWith(false); // Interrupt cleared
215
+ });
216
+
217
+ it("should read from source on channel 0", () => {
218
+ // Set channel 0
219
+ adc.write(0, 0x00);
220
+
221
+ // Simulate conversion completion
222
+ adc.onComplete();
223
+
224
+ // Check that the mock value was used
225
+ const expectedValue = mockGamepadSource.getValue(0);
226
+ expect(adc.low).toBe(expectedValue & 0xff);
227
+ expect(adc.high).toBe((expectedValue >>> 8) & 0xff);
228
+ });
229
+
230
+ it("should read from source on channel 1", () => {
231
+ // Set channel 1
232
+ adc.write(0, 0x01);
233
+
234
+ // Simulate conversion completion
235
+ adc.onComplete();
236
+
237
+ // Check that the mock value was used
238
+ const expectedValue = mockGamepadSource.getValue(1);
239
+ expect(adc.low).toBe(expectedValue & 0xff);
240
+ expect(adc.high).toBe((expectedValue >>> 8) & 0xff);
241
+ });
242
+
243
+ it("should use specific source for each channel", () => {
244
+ // Create a special source for channel 2 with a different value
245
+ const specialSource = new MockGamepadSource();
246
+ specialSource.mockValues[2] = 0x1234; // Different value
247
+
248
+ // Set it as the source for channel 2
249
+ adc.setChannelSource(2, specialSource);
250
+
251
+ // Set channel 2
252
+ adc.write(0, 0x02);
253
+
254
+ // Simulate conversion completion
255
+ adc.onComplete();
256
+
257
+ // Should use the special source for channel 2
258
+ const expectedValue = specialSource.getValue(2);
259
+ expect(adc.low).toBe(expectedValue & 0xff);
260
+ expect(adc.high).toBe((expectedValue >>> 8) & 0xff);
261
+ });
262
+
263
+ it("should switch between different sources by channel", () => {
264
+ // Create two different sources with different values
265
+ const source1 = new MockGamepadSource();
266
+ source1.mockValues[2] = 0x1111;
267
+
268
+ const source2 = new MockGamepadSource();
269
+ source2.mockValues[3] = 0x2222;
270
+
271
+ // Set them for different channels
272
+ adc.setChannelSource(2, source1);
273
+ adc.setChannelSource(3, source2);
274
+
275
+ // Test channel 2 (should use source1)
276
+ adc.write(0, 0x02);
277
+ adc.onComplete();
278
+ expect(adc.low).toBe(0x11); // 0x1111 & 0xff
279
+ expect(adc.high).toBe(0x11); // (0x1111 >>> 8) & 0xff
280
+
281
+ // Test channel 3 (should use source2)
282
+ adc.write(0, 0x03);
283
+ adc.onComplete();
284
+ expect(adc.low).toBe(0x22); // 0x2222 & 0xff
285
+ expect(adc.high).toBe(0x22); // (0x2222 >>> 8) & 0xff
286
+ });
287
+
288
+ it("should update status bits correctly after conversion", () => {
289
+ // Set channel 2
290
+ adc.write(0, 0x02);
291
+
292
+ // Simulate conversion completion
293
+ adc.onComplete();
294
+
295
+ // Get the expected value from our mock source
296
+ const expectedValue = mockGamepadSource.getValue(2);
297
+
298
+ // The status should have:
299
+ // - bits 0-3: channel number (0)
300
+ // - bit 6: end of conversion bit (1)
301
+ // - bits 4-5: ((0x5fff >>> 10) & 0x03) = 0x01 (shifted to bit position)
302
+ const expectedStatus = (0x00 & 0x0f) | 0x40 | ((expectedValue >>> 10) & 0x03);
303
+
304
+ expect(adc.status).toBe(expectedStatus);
305
+ });
306
+ });
307
+ });
@@ -0,0 +1,30 @@
1
+ import { describe, it, expect } from "vitest";
2
+
3
+ import { fake65C12 } from "../../src/fake6502.js";
4
+
5
+ const cpu = fake65C12();
6
+
7
+ describe("BCD tests", function () {
8
+ "use strict";
9
+ it("handles 65c12sbc1", async function () {
10
+ await cpu.initialise();
11
+ cpu.p.reset();
12
+ cpu.p.d = true;
13
+ cpu.a = 0x90;
14
+ cpu.sbc(0x0b);
15
+ expect(cpu.p.v).toBe(false);
16
+ expect(cpu.p.c).toBe(true);
17
+ expect(cpu.a).toBe(126);
18
+ });
19
+
20
+ it("handles 65c12sbc2", async function () {
21
+ await cpu.initialise();
22
+ cpu.p.reset();
23
+ cpu.p.d = true;
24
+ cpu.a = 0x80;
25
+ cpu.sbc(0x01);
26
+ expect(cpu.p.v).toBe(true);
27
+ expect(cpu.p.c).toBe(true);
28
+ expect(cpu.a).toBe(120);
29
+ });
30
+ });
@@ -0,0 +1,266 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { Cmos } from "../../src/cmos.js";
3
+
4
+ describe("CMOS", () => {
5
+ // Mock persistence
6
+ const mockPersistence = {
7
+ load: vi.fn().mockReturnValue(null),
8
+ save: vi.fn(),
9
+ };
10
+
11
+ // Test date (2023-04-15T12:34:56)
12
+ const TEST_DATE = new Date(2023, 3, 15, 12, 34, 56);
13
+
14
+ // CMOS register addresses (from BBC Micro documentation)
15
+ const CMOS_ADDR = {
16
+ SECONDS: 0,
17
+ MINUTES: 2,
18
+ HOURS: 4,
19
+ DAY_OF_WEEK: 6,
20
+ DAY_OF_MONTH: 7,
21
+ MONTH: 8,
22
+ YEAR: 9,
23
+ // Non-RTC addresses for testing
24
+ CONFIG_1: 12,
25
+ CONFIG_2: 13,
26
+ };
27
+
28
+ // Constants from the hardware implementation
29
+ const PORT_B_ENABLE = 0x40; // Bit 6 of port B
30
+ const PORT_B_ADDR_SEL = 0x80; // Bit 7 of port B
31
+ const IC32_READ = 2; // Bit 1 of IC32
32
+ const IC32_DATA_SEL = 4; // Bit 2 of IC32
33
+
34
+ let cmos;
35
+
36
+ beforeEach(() => {
37
+ // Use fake timers for consistent date/time testing
38
+ vi.useFakeTimers();
39
+ vi.setSystemTime(TEST_DATE);
40
+
41
+ // Create a fresh CMOS instance for each test
42
+ cmos = new Cmos(mockPersistence);
43
+ });
44
+
45
+ afterEach(() => {
46
+ vi.useRealTimers();
47
+ vi.resetAllMocks();
48
+ });
49
+
50
+ describe("Initialization", () => {
51
+ it("should initialize with persistence and save default values", () => {
52
+ expect(mockPersistence.save).toHaveBeenCalled();
53
+ });
54
+
55
+ it("should use custom persistence data if available", () => {
56
+ const customData = Array(48).fill(0x42);
57
+ mockPersistence.load.mockReturnValueOnce(customData);
58
+
59
+ const customCmos = new Cmos(mockPersistence);
60
+
61
+ // Reading from a non-RTC location should return our custom data
62
+ // First enable CMOS and set it up for reading
63
+ customCmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, CMOS_ADDR.CONFIG_1, 0);
64
+ customCmos.writeControl(PORT_B_ENABLE, CMOS_ADDR.CONFIG_1, 0);
65
+ customCmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
66
+
67
+ expect(customCmos.read()).toBe(0x42);
68
+ });
69
+
70
+ it("should apply CMOS override when provided", () => {
71
+ const cmosOverride = (store) => {
72
+ const newStore = [...store];
73
+ newStore[CMOS_ADDR.CONFIG_1] = 0x42;
74
+ return newStore;
75
+ };
76
+
77
+ const customCmos = new Cmos(mockPersistence, cmosOverride);
78
+
79
+ // Reading from the overridden location should return our custom value
80
+ // First enable CMOS and set it up for reading
81
+ customCmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, CMOS_ADDR.CONFIG_1, 0);
82
+ customCmos.writeControl(PORT_B_ENABLE, CMOS_ADDR.CONFIG_1, 0);
83
+ customCmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
84
+
85
+ expect(customCmos.read()).toBe(0x42);
86
+ });
87
+
88
+ it("should apply econet settings when provided", () => {
89
+ const econet = { stationId: 0x42 };
90
+ const customCmos = new Cmos(mockPersistence, null, econet);
91
+
92
+ // First read econet station ID (at address 0x0E)
93
+ customCmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, 0x0e, 0);
94
+ customCmos.writeControl(PORT_B_ENABLE, 0x0e, 0);
95
+ customCmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
96
+
97
+ expect(customCmos.read()).toBe(0x42);
98
+
99
+ // Then read FS ID (at address 0x0F)
100
+ customCmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, 0x0f, 0);
101
+ customCmos.writeControl(PORT_B_ENABLE, 0x0f, 0);
102
+ customCmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
103
+
104
+ expect(customCmos.read()).toBe(254);
105
+ });
106
+ });
107
+
108
+ describe("Reading and Writing non-RTC data", () => {
109
+ it("should return 0xFF when CMOS is disabled", () => {
110
+ // Don't enable CMOS (no PORT_B_ENABLE bit)
111
+ expect(cmos.read()).toBe(0xff);
112
+ });
113
+
114
+ it("should write and read from CMOS memory locations", () => {
115
+ // Set address to CONFIG_1 (addr 12)
116
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, CMOS_ADDR.CONFIG_1, 0);
117
+ cmos.writeControl(PORT_B_ENABLE, CMOS_ADDR.CONFIG_1, 0);
118
+
119
+ // Write value 0x42 to CONFIG_1
120
+ cmos.writeControl(PORT_B_ENABLE, 0x42, IC32_DATA_SEL);
121
+ cmos.writeControl(PORT_B_ENABLE, 0x42, 0);
122
+
123
+ // Read it back
124
+ cmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
125
+ expect(cmos.read()).toBe(0x42);
126
+
127
+ // Check persistence was called
128
+ expect(mockPersistence.save).toHaveBeenCalled();
129
+ });
130
+
131
+ it("should only read when properly configured", () => {
132
+ // Set address to CONFIG_2 (different than other tests)
133
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, CMOS_ADDR.CONFIG_2, 0);
134
+ cmos.writeControl(PORT_B_ENABLE, CMOS_ADDR.CONFIG_2, 0);
135
+
136
+ // Write a known test value
137
+ cmos.writeControl(PORT_B_ENABLE, 0x42, IC32_DATA_SEL);
138
+ cmos.writeControl(PORT_B_ENABLE, 0x42, 0);
139
+
140
+ // Without setting the read mode, should return 0xFF
141
+ expect(cmos.read()).toBe(0xff);
142
+
143
+ // With address select high, should return 0xFF
144
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, 0, IC32_READ);
145
+ expect(cmos.read()).toBe(0xff);
146
+
147
+ // With data select low, should return 0xFF
148
+ cmos.writeControl(PORT_B_ENABLE, 0, IC32_READ);
149
+ expect(cmos.read()).toBe(0xff);
150
+
151
+ // Make sure we're still pointing at the right address
152
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, CMOS_ADDR.CONFIG_2, 0);
153
+ cmos.writeControl(PORT_B_ENABLE, CMOS_ADDR.CONFIG_2, 0);
154
+
155
+ // With everything set correctly, should return the value
156
+ cmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
157
+ expect(cmos.read()).toBe(0x42);
158
+ });
159
+ });
160
+
161
+ describe("Reading RTC values", () => {
162
+ // Helper function to read a specific RTC register
163
+ function readRtcRegister(register) {
164
+ // Set address
165
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, register, 0);
166
+ cmos.writeControl(PORT_B_ENABLE, register, 0);
167
+
168
+ // Configure for reading
169
+ cmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
170
+
171
+ return cmos.read();
172
+ }
173
+
174
+ it("should read current time from RTC registers", () => {
175
+ // Helper function for BCD conversion (same as in cmos.js)
176
+ function toBcd(value) {
177
+ return parseInt(value.toString(10), 16);
178
+ }
179
+
180
+ // Test all RTC components
181
+ expect(readRtcRegister(CMOS_ADDR.SECONDS)).toBe(toBcd(TEST_DATE.getSeconds()));
182
+ expect(readRtcRegister(CMOS_ADDR.MINUTES)).toBe(toBcd(TEST_DATE.getMinutes()));
183
+ expect(readRtcRegister(CMOS_ADDR.HOURS)).toBe(toBcd(TEST_DATE.getHours()));
184
+ expect(readRtcRegister(CMOS_ADDR.DAY_OF_WEEK)).toBe(toBcd(TEST_DATE.getDay() + 1));
185
+ expect(readRtcRegister(CMOS_ADDR.DAY_OF_MONTH)).toBe(toBcd(TEST_DATE.getDate()));
186
+ expect(readRtcRegister(CMOS_ADDR.MONTH)).toBe(toBcd(TEST_DATE.getMonth() + 1));
187
+ });
188
+ });
189
+
190
+ describe("Setting RTC values", () => {
191
+ // Helper to read a specific RTC register
192
+ function readRtcRegister(register) {
193
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, register, 0);
194
+ cmos.writeControl(PORT_B_ENABLE, register, 0);
195
+ cmos.writeControl(PORT_B_ENABLE, 0, IC32_READ | IC32_DATA_SEL);
196
+ return cmos.read();
197
+ }
198
+
199
+ // Helper to write to a specific RTC register
200
+ function writeRtcRegister(register, value) {
201
+ cmos.writeControl(PORT_B_ENABLE | PORT_B_ADDR_SEL, register, 0);
202
+ cmos.writeControl(PORT_B_ENABLE, register, 0);
203
+ cmos.writeControl(PORT_B_ENABLE, value, IC32_DATA_SEL);
204
+ cmos.writeControl(PORT_B_ENABLE, value, 0);
205
+ }
206
+
207
+ it("should update RTC values when written", () => {
208
+ // Set hours to 10
209
+ writeRtcRegister(CMOS_ADDR.HOURS, 0x10);
210
+
211
+ // Advance time slightly to ensure changes take effect
212
+ vi.advanceTimersByTime(100);
213
+
214
+ // Read back hours
215
+ expect(readRtcRegister(CMOS_ADDR.HOURS)).toBe(0x10);
216
+
217
+ // Set minutes to 45
218
+ writeRtcRegister(CMOS_ADDR.MINUTES, 0x45);
219
+
220
+ // Advance time slightly
221
+ vi.advanceTimersByTime(100);
222
+
223
+ // Read back minutes
224
+ expect(readRtcRegister(CMOS_ADDR.MINUTES)).toBe(0x45);
225
+ });
226
+ });
227
+
228
+ describe("BCD Conversion Logic", () => {
229
+ it("should correctly convert between decimal and BCD", () => {
230
+ // Helper functions for BCD conversion (same as in cmos.js)
231
+ const toBcd = (value) => parseInt(value.toString(10), 16);
232
+ const fromBcd = (value) => parseInt(value.toString(16), 10);
233
+
234
+ // Test toBcd conversion
235
+ expect(toBcd(0)).toBe(0x00);
236
+ expect(toBcd(9)).toBe(0x09);
237
+ expect(toBcd(10)).toBe(0x10);
238
+ expect(toBcd(42)).toBe(0x42);
239
+ expect(toBcd(99)).toBe(0x99);
240
+
241
+ // Test fromBcd conversion
242
+ expect(fromBcd(0x00)).toBe(0);
243
+ expect(fromBcd(0x09)).toBe(9);
244
+ expect(fromBcd(0x10)).toBe(10);
245
+ expect(fromBcd(0x42)).toBe(42);
246
+ expect(fromBcd(0x99)).toBe(99);
247
+
248
+ // Test round-trips
249
+ for (let i = 0; i < 100; i++) {
250
+ expect(fromBcd(toBcd(i))).toBe(i);
251
+ }
252
+ });
253
+
254
+ it("should handle year century threshold correctly", () => {
255
+ const fromBcd = (value) => parseInt(value.toString(16), 10);
256
+
257
+ // Years 80-99 should use 1900 as base
258
+ expect(fromBcd(0x80) >= 80 ? 1900 : 2000).toBe(1900);
259
+ expect(fromBcd(0x99) >= 80 ? 1900 : 2000).toBe(1900);
260
+
261
+ // Years 00-79 should use 2000 as base
262
+ expect(fromBcd(0x00) >= 80 ? 1900 : 2000).toBe(2000);
263
+ expect(fromBcd(0x79) >= 80 ? 1900 : 2000).toBe(2000);
264
+ });
265
+ });
266
+ });
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from "vitest";
2
+
3
+ import { Disc, IbmDiscFormat } from "../../src/disc.js";
4
+ import { DiscDrive } from "../../src/disc-drive.js";
5
+ import { Scheduler } from "../../src/scheduler.js";
6
+
7
+ describe("Disc drive tests", function () {
8
+ it("starts empty", () => {
9
+ const scheduler = new Scheduler();
10
+ const drive = new DiscDrive(0, scheduler);
11
+ expect(drive.trackLength).toBe(IbmDiscFormat.bytesPerTrack);
12
+ expect(drive.disc).toBeFalsy();
13
+ expect(drive.spinning).toBe(false);
14
+ drive.setPulsesCallback(() => {
15
+ expect.fail("no callbacks expected");
16
+ });
17
+ scheduler.polltime(1000000);
18
+ });
19
+ it("sets a disc", () => {
20
+ const scheduler = new Scheduler();
21
+ const drive = new DiscDrive(0, scheduler);
22
+ const disc = Disc.createBlank();
23
+ drive.setDisc(disc);
24
+ expect(drive.disc).toBe(disc);
25
+ });
26
+ it("calls back with pulses after spinning starts", () => {
27
+ const scheduler = new Scheduler();
28
+ const drive = new DiscDrive(0, scheduler);
29
+ drive.setDisc(0, Disc.createBlank());
30
+ drive.setPulsesCallback(() => {
31
+ expect.fail("no callbacks expected");
32
+ });
33
+ scheduler.polltime(1000000);
34
+ drive.startSpinning();
35
+ let numPulses = 0;
36
+ drive.setPulsesCallback(() => numPulses++);
37
+ scheduler.polltime(500);
38
+ expect(numPulses).toBe(4);
39
+ drive.stopSpinning();
40
+ drive.setPulsesCallback(() => {
41
+ expect.fail("no callbacks expected");
42
+ });
43
+ scheduler.polltime(1000000);
44
+ });
45
+ it("generates quasi random pulses with a blank disc", () => {
46
+ const scheduler = new Scheduler();
47
+ const drive = new DiscDrive(0, scheduler);
48
+ drive.setDisc(Disc.createBlank());
49
+ drive.getQuasiRandomPulses = () => {
50
+ return 0xdeadbeef;
51
+ };
52
+ let called = false;
53
+ drive.setPulsesCallback((pulses, numPulses) => {
54
+ called = true;
55
+ expect(numPulses).toBe(32);
56
+ expect(pulses).toBe(0xdeadbeef);
57
+ });
58
+ drive.startSpinning();
59
+ scheduler.polltime(1000000);
60
+ expect(called).toBe(true);
61
+ });
62
+ it("asserts index all the time with no disc", () => {
63
+ const scheduler = new Scheduler();
64
+ const drive = new DiscDrive(0, scheduler);
65
+ expect(drive.indexPulse).toBe(true);
66
+ });
67
+ it("asserts index periodically with a spinning disc", () => {
68
+ const scheduler = new Scheduler();
69
+ const drive = new DiscDrive(0, scheduler);
70
+ drive.setDisc(Disc.createBlank());
71
+ drive.startSpinning();
72
+ let previousIndex = drive.indexPulse;
73
+ let risingEdges = 0;
74
+ const cyclesPerSecond = 2 * 1000 * 1000;
75
+ const cyclesPerIter = cyclesPerSecond / 60;
76
+ const rpm = 300;
77
+ const testSeconds = 5;
78
+ for (let cycle = 0; cycle < testSeconds * cyclesPerSecond; cycle += cyclesPerIter) {
79
+ scheduler.polltime(cyclesPerIter);
80
+ if (drive.indexPulse && !previousIndex) risingEdges++;
81
+ previousIndex = drive.indexPulse;
82
+ }
83
+ expect(risingEdges).toBe((rpm / 60) * testSeconds);
84
+ });
85
+ });