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,190 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Scheduler } from "../../src/scheduler.js";
3
+
4
+ describe("Scheduler tests", function () {
5
+ "use strict";
6
+ it("creates", function () {
7
+ new Scheduler();
8
+ });
9
+
10
+ it("handles simple cases", function () {
11
+ const s = new Scheduler();
12
+ let called = false;
13
+ const t = s.newTask(function () {
14
+ expect(called).toBe(false);
15
+ called = true;
16
+ });
17
+ t.schedule(2);
18
+ expect(called).toBe(false);
19
+ s.polltime(1);
20
+ expect(called).toBe(false);
21
+ s.polltime(1);
22
+ expect(called).toBe(true);
23
+ s.polltime(1);
24
+ });
25
+
26
+ it("handles simple cases with a big step", function () {
27
+ const s = new Scheduler();
28
+ let called = false;
29
+ const t = s.newTask(function () {
30
+ expect(called).toBe(false);
31
+ called = true;
32
+ });
33
+ t.schedule(2);
34
+ expect(called).toBe(false);
35
+ s.polltime(2);
36
+ expect(called).toBe(true);
37
+ s.polltime(2);
38
+ });
39
+
40
+ it("handles simple cases with a big step past", function () {
41
+ const s = new Scheduler();
42
+ let called = false;
43
+ const t = s.newTask(function () {
44
+ expect(called).toBe(false);
45
+ called = true;
46
+ });
47
+ t.schedule(2);
48
+ expect(called).toBe(false);
49
+ s.polltime(3);
50
+ expect(called).toBe(true);
51
+ s.polltime(3);
52
+ });
53
+
54
+ it("calls callbacks in the order registered when occurring on same cycle", function () {
55
+ const s = new Scheduler();
56
+ let called = "";
57
+ s.newTask(function () {
58
+ called += "a";
59
+ }).schedule(2);
60
+ s.newTask(function () {
61
+ called += "b";
62
+ }).schedule(2);
63
+ s.newTask(function () {
64
+ called += "c";
65
+ }).schedule(2);
66
+ expect(called).toBe("");
67
+ s.polltime(1);
68
+ expect(called).toBe("");
69
+ s.polltime(1);
70
+ expect(called).toBe("abc");
71
+ s.polltime(1);
72
+ });
73
+
74
+ it("cancels first occurring event", function () {
75
+ const s = new Scheduler();
76
+ let called = "";
77
+ const a = s.newTask(function () {
78
+ called += "a";
79
+ });
80
+ a.schedule(2);
81
+ s.newTask(function () {
82
+ called += "b";
83
+ }).schedule(2);
84
+ s.newTask(function () {
85
+ called += "c";
86
+ }).schedule(2);
87
+ a.cancel();
88
+ s.polltime(2);
89
+ expect(called).toBe("bc");
90
+ });
91
+
92
+ it("cancels middle occurring event", function () {
93
+ const s = new Scheduler();
94
+ let called = "";
95
+ s.newTask(function () {
96
+ called += "a";
97
+ }).schedule(2);
98
+ const b = s.newTask(function () {
99
+ called += "b";
100
+ });
101
+ b.schedule(2);
102
+ s.newTask(function () {
103
+ called += "c";
104
+ }).schedule(2);
105
+ b.cancel();
106
+ s.polltime(2);
107
+ expect(called).toBe("ac");
108
+ });
109
+
110
+ it("cancels last occurring event", function () {
111
+ const s = new Scheduler();
112
+ let called = "";
113
+ s.newTask(function () {
114
+ called += "a";
115
+ }).schedule(2);
116
+ s.newTask(function () {
117
+ called += "b";
118
+ }).schedule(2);
119
+ const c = s.newTask(function () {
120
+ called += "c";
121
+ });
122
+ c.schedule(2);
123
+ c.cancel();
124
+ s.polltime(2);
125
+ expect(called).toBe("ab");
126
+ });
127
+
128
+ it("handle events registered in reverse (CBA) order", function () {
129
+ const s = new Scheduler();
130
+ let called = "";
131
+ s.newTask(function () {
132
+ called += "a";
133
+ }).schedule(4);
134
+ s.newTask(function () {
135
+ called += "b";
136
+ }).schedule(3);
137
+ s.newTask(function () {
138
+ called += "c";
139
+ }).schedule(2);
140
+ s.polltime(10);
141
+ expect(called).toBe("cba");
142
+ });
143
+
144
+ it("handle events registered in CAB order", function () {
145
+ const s = new Scheduler();
146
+ let called = "";
147
+ s.newTask(function () {
148
+ called += "a";
149
+ }).schedule(3);
150
+ s.newTask(function () {
151
+ called += "b";
152
+ }).schedule(4);
153
+ s.newTask(function () {
154
+ called += "c";
155
+ }).schedule(2);
156
+ s.polltime(10);
157
+ expect(called).toBe("cab");
158
+ });
159
+
160
+ it("works properly with epochs", function () {
161
+ const s = new Scheduler();
162
+ s.polltime(12346);
163
+ const epochBefore = s.epoch;
164
+ let epochAtCall = 0;
165
+ s.newTask(function () {
166
+ epochAtCall = s.epoch;
167
+ }).schedule(4);
168
+ s.polltime(9974);
169
+ expect(epochAtCall - epochBefore).toBe(4);
170
+ });
171
+
172
+ it("allows you to reschedule from within a callback", function () {
173
+ const s = new Scheduler();
174
+ s.polltime(12346);
175
+ const times = [1000, 1000, 100000, 1000];
176
+ const called = [];
177
+ const task = s.newTask(function () {
178
+ called.push(s.epoch);
179
+ const next = times.shift();
180
+ if (next) {
181
+ task.schedule(next);
182
+ }
183
+ });
184
+ task.schedule(10);
185
+ for (let i = 0; i < 500000; ++i) {
186
+ s.polltime(3);
187
+ }
188
+ expect(called).toEqual([12356, 13356, 14356, 114356, 115356]);
189
+ });
190
+ });
@@ -0,0 +1,154 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { Serial } from "../../src/serial.js";
3
+
4
+ describe("Serial", () => {
5
+ // Create mock for the ACIA dependency
6
+ const mockAcia = {
7
+ setSerialReceive: vi.fn(),
8
+ setMotor: vi.fn(),
9
+ selectRs423: vi.fn(),
10
+ };
11
+
12
+ // The baud rate table as defined in serial.js
13
+ const baudRateTable = [19200, 9600, 4800, 2400, 1200, 300, 150, 75];
14
+
15
+ let serial;
16
+
17
+ beforeEach(() => {
18
+ // Reset mocks before each test
19
+ vi.resetAllMocks();
20
+
21
+ // Create a fresh Serial instance
22
+ serial = new Serial(mockAcia);
23
+ });
24
+
25
+ describe("Initialization", () => {
26
+ it("should initialize with default state", () => {
27
+ expect(serial.reg).toBe(0);
28
+ expect(serial.transmitRate).toBe(0);
29
+ expect(serial.receiveRate).toBe(0);
30
+ });
31
+
32
+ it("should reset to default state", () => {
33
+ // Change state
34
+ serial.reg = 0xff;
35
+ serial.transmitRate = 7;
36
+ serial.receiveRate = 7;
37
+
38
+ // Reset
39
+ serial.reset();
40
+
41
+ // Check state is back to default
42
+ expect(serial.reg).toBe(0);
43
+ expect(serial.transmitRate).toBe(0);
44
+ expect(serial.receiveRate).toBe(0);
45
+ });
46
+ });
47
+
48
+ describe("Register write operation", () => {
49
+ it("should store value and update rates on write", () => {
50
+ // Write value 0x2A to register (00101010)
51
+ // - transmitRate = 010 = 2
52
+ // - receiveRate = 101 = 5
53
+ serial.write(0, 0x2a);
54
+
55
+ expect(serial.reg).toBe(0x2a);
56
+ expect(serial.transmitRate).toBe(2);
57
+ expect(serial.receiveRate).toBe(5);
58
+ });
59
+
60
+ it("should mask value to 8 bits on write", () => {
61
+ // Write value 0x1FF to register, should be masked to 0xFF
62
+ serial.write(0, 0x1ff);
63
+
64
+ expect(serial.reg).toBe(0xff);
65
+ });
66
+
67
+ it("should set correct receive baud rate", () => {
68
+ // Test all possible receive rate values
69
+ for (let i = 0; i < 8; i++) {
70
+ // Create a value where receiveRate = i (bits 3-5)
71
+ const val = i << 3;
72
+ serial.write(0, val);
73
+
74
+ // Check rate is set correctly
75
+ expect(serial.receiveRate).toBe(i);
76
+ // Check correct baud rate was passed to ACIA
77
+ expect(mockAcia.setSerialReceive).toHaveBeenLastCalledWith(baudRateTable[i]);
78
+ }
79
+ });
80
+
81
+ it("should set motor state based on bit 7", () => {
82
+ // Test with bit 7 = 0
83
+ serial.write(0, 0x00);
84
+ expect(mockAcia.setMotor).toHaveBeenLastCalledWith(false);
85
+
86
+ // Test with bit 7 = 1
87
+ serial.write(0, 0x80);
88
+ expect(mockAcia.setMotor).toHaveBeenLastCalledWith(true);
89
+ });
90
+
91
+ it("should select RS-423 based on bit 6", () => {
92
+ // Test with bit 6 = 0
93
+ serial.write(0, 0x00);
94
+ expect(mockAcia.selectRs423).toHaveBeenLastCalledWith(false);
95
+
96
+ // Test with bit 6 = 1
97
+ serial.write(0, 0x40);
98
+ expect(mockAcia.selectRs423).toHaveBeenLastCalledWith(true);
99
+ });
100
+
101
+ it("should handle complex register values", () => {
102
+ // Write value 0xEA to register (11101010)
103
+ // - transmitRate = 010 = 2
104
+ // - receiveRate = 101 = 5
105
+ // - bit 6 = 1 (select RS-423)
106
+ // - bit 7 = 1 (motor on)
107
+ serial.write(0, 0xea);
108
+
109
+ expect(serial.reg).toBe(0xea);
110
+ expect(serial.transmitRate).toBe(2);
111
+ expect(serial.receiveRate).toBe(5);
112
+ expect(mockAcia.setSerialReceive).toHaveBeenCalledWith(baudRateTable[5]);
113
+ expect(mockAcia.setMotor).toHaveBeenCalledWith(true);
114
+ expect(mockAcia.selectRs423).toHaveBeenCalledWith(true);
115
+ });
116
+ });
117
+
118
+ describe("Register read operation", () => {
119
+ it("should reset register to 0xFE on read", () => {
120
+ // Set initial state
121
+ serial.write(0, 0x2a);
122
+
123
+ // Read should reset to 0xFE
124
+ const result = serial.read();
125
+
126
+ // Check result is 0
127
+ expect(result).toBe(0);
128
+
129
+ // Check register was updated
130
+ expect(serial.reg).toBe(0xfe);
131
+ // Check rates were updated
132
+ expect(serial.transmitRate).toBe(6); // 0xFE & 0x07 = 6
133
+ expect(serial.receiveRate).toBe(7); // (0xFE >>> 3) & 0x07 = 7
134
+ // Check ACIA was updated
135
+ expect(mockAcia.setSerialReceive).toHaveBeenLastCalledWith(baudRateTable[7]);
136
+ expect(mockAcia.setMotor).toHaveBeenLastCalledWith(true);
137
+ expect(mockAcia.selectRs423).toHaveBeenLastCalledWith(true);
138
+ });
139
+ });
140
+
141
+ describe("Edge cases", () => {
142
+ it("should ignore address in write", () => {
143
+ // Write to different addresses should have same effect
144
+ serial.write(0, 0x2a);
145
+ expect(serial.reg).toBe(0x2a);
146
+
147
+ serial.write(1, 0x3b);
148
+ expect(serial.reg).toBe(0x3b);
149
+
150
+ serial.write(0xff, 0x4c);
151
+ expect(serial.reg).toBe(0x4c);
152
+ });
153
+ });
154
+ });
@@ -0,0 +1,359 @@
1
+ import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
2
+ import { TeletextAdaptor } from "../../src/teletext_adaptor.js";
3
+
4
+ describe("TeletextAdaptor", () => {
5
+ // Constants
6
+ const TELETEXT_IRQ = 5;
7
+
8
+ // Mock CPU
9
+ const mockCpu = {
10
+ interrupt: 0,
11
+ resetLine: true,
12
+ };
13
+
14
+ let teletext;
15
+
16
+ beforeEach(() => {
17
+ // Reset mocks
18
+ vi.clearAllMocks();
19
+ mockCpu.interrupt = 0;
20
+ mockCpu.resetLine = true;
21
+
22
+ // Create fresh teletext adaptor
23
+ teletext = new TeletextAdaptor(mockCpu);
24
+
25
+ // Silence console logs during tests
26
+ vi.spyOn(console, "log").mockImplementation(() => {});
27
+
28
+ // Override loadChannelStream to avoid actual network calls
29
+ teletext.loadChannelStream = vi.fn();
30
+ });
31
+
32
+ afterEach(() => {
33
+ vi.restoreAllMocks();
34
+ });
35
+
36
+ describe("Initialization", () => {
37
+ it("should initialize with default state", () => {
38
+ expect(teletext.teletextStatus).toBe(0x0f);
39
+ expect(teletext.teletextInts).toBe(false);
40
+ expect(teletext.teletextEnable).toBe(false);
41
+ expect(teletext.channel).toBe(0);
42
+ expect(teletext.currentFrame).toBe(0);
43
+ expect(teletext.totalFrames).toBe(0);
44
+ expect(teletext.rowPtr).toBe(0);
45
+ expect(teletext.colPtr).toBe(0);
46
+ expect(teletext.frameBuffer.length).toBe(16);
47
+ expect(teletext.frameBuffer[0].length).toBe(64);
48
+ expect(teletext.streamData).toBe(null);
49
+ expect(teletext.pollCount).toBe(0);
50
+ });
51
+
52
+ it("should call loadChannelStream on hard reset", () => {
53
+ // Hard reset
54
+ teletext.reset(true);
55
+
56
+ // Check if loadChannelStream was called with channel 0
57
+ expect(teletext.loadChannelStream).toHaveBeenCalledWith(0);
58
+ });
59
+
60
+ it("should not call loadChannelStream on soft reset", () => {
61
+ // Soft reset
62
+ teletext.reset(false);
63
+
64
+ // Check if loadChannelStream was not called
65
+ expect(teletext.loadChannelStream).not.toHaveBeenCalled();
66
+ });
67
+ });
68
+
69
+ describe("Register operations", () => {
70
+ describe("Read operations", () => {
71
+ it("should read status register (addr 0)", () => {
72
+ teletext.teletextStatus = 0x42;
73
+ expect(teletext.read(0)).toBe(0x42);
74
+ });
75
+
76
+ it("should read row register (addr 1)", () => {
77
+ // Row register reads always return 0
78
+ expect(teletext.read(1)).toBe(0);
79
+ });
80
+
81
+ it("should read from frame buffer and increment column pointer (addr 2)", () => {
82
+ // Set up known values in frame buffer
83
+ teletext.rowPtr = 5;
84
+ teletext.colPtr = 10;
85
+ teletext.frameBuffer[5][10] = 0xaa;
86
+ teletext.frameBuffer[5][11] = 0xbb;
87
+
88
+ // First read should return value at current position and increment column
89
+ expect(teletext.read(2)).toBe(0xaa);
90
+ expect(teletext.colPtr).toBe(11);
91
+
92
+ // Second read should return next value
93
+ expect(teletext.read(2)).toBe(0xbb);
94
+ expect(teletext.colPtr).toBe(12);
95
+ });
96
+
97
+ it("should clear status and interrupt on addr 3 read", () => {
98
+ // Set status and interrupt
99
+ teletext.teletextStatus = 0xff;
100
+ mockCpu.interrupt = 1 << TELETEXT_IRQ;
101
+
102
+ // Read from addr 3
103
+ teletext.read(3);
104
+
105
+ // Status should be cleared (INT, DOR, and FSYN latches)
106
+ expect(teletext.teletextStatus & 0xd0).toBe(0);
107
+
108
+ // Interrupt should be cleared
109
+ expect(mockCpu.interrupt & (1 << TELETEXT_IRQ)).toBe(0);
110
+ });
111
+ });
112
+
113
+ describe("Write operations", () => {
114
+ it("should update control bits on status register write (addr 0)", () => {
115
+ // Write with teletext enabled, interrupts enabled, channel 2
116
+ teletext.write(0, 0x0c | 2); // 0x0E
117
+
118
+ expect(teletext.teletextEnable).toBe(true);
119
+ expect(teletext.teletextInts).toBe(true);
120
+ expect(teletext.channel).toBe(2);
121
+
122
+ // Check if loadChannelStream was called
123
+ expect(teletext.loadChannelStream).toHaveBeenCalledWith(2);
124
+ });
125
+
126
+ it("should not reload channel if channel doesn't change", () => {
127
+ // Set initial state
128
+ teletext.channel = 1;
129
+ teletext.teletextEnable = true;
130
+
131
+ // Write same channel
132
+ teletext.write(0, 0x0c | 1); // 0x0D
133
+
134
+ // Check loadChannelStream wasn't called
135
+ expect(teletext.loadChannelStream).not.toHaveBeenCalled();
136
+ });
137
+
138
+ it("should not change channel or load channel if teletext not enabled", () => {
139
+ // Set initial values
140
+ teletext.channel = 1;
141
+ teletext.loadChannelStream.mockClear();
142
+
143
+ // Write with teletext disabled, channel 2
144
+ teletext.write(0, 2); // 0x02
145
+
146
+ // Teletext should be disabled
147
+ expect(teletext.teletextEnable).toBe(false);
148
+
149
+ // Channel should remain unchanged since teletext is disabled
150
+ // According to the implementation in write method, channel is only updated
151
+ // if teletext is enabled: if ((value & 0x03) !== this.channel && this.teletextEnable)
152
+ expect(teletext.channel).toBe(1);
153
+
154
+ // Check loadChannelStream wasn't called
155
+ expect(teletext.loadChannelStream).not.toHaveBeenCalled();
156
+ });
157
+
158
+ it("should set interrupt flag if INT and interrupts enabled", () => {
159
+ // Set INT latch
160
+ teletext.teletextStatus = 0x80;
161
+
162
+ // Enable interrupts
163
+ teletext.write(0, 0x08);
164
+
165
+ // Check if interrupt was set
166
+ expect(mockCpu.interrupt & (1 << TELETEXT_IRQ)).toBe(1 << TELETEXT_IRQ);
167
+ });
168
+
169
+ it("should clear interrupt flag if interrupts disabled", () => {
170
+ // Set interrupt
171
+ mockCpu.interrupt = 1 << TELETEXT_IRQ;
172
+
173
+ // Disable interrupts
174
+ teletext.write(0, 0x00);
175
+
176
+ // Check if interrupt was cleared
177
+ expect(mockCpu.interrupt & (1 << TELETEXT_IRQ)).toBe(0);
178
+ });
179
+
180
+ it("should update row pointer and reset column pointer (addr 1)", () => {
181
+ // Set initial state
182
+ teletext.rowPtr = 0;
183
+ teletext.colPtr = 10;
184
+
185
+ // Write to row register
186
+ teletext.write(1, 5);
187
+
188
+ expect(teletext.rowPtr).toBe(5);
189
+ expect(teletext.colPtr).toBe(0);
190
+ });
191
+
192
+ it("should write to frame buffer and increment column (addr 2)", () => {
193
+ // Set initial position
194
+ teletext.rowPtr = 3;
195
+ teletext.colPtr = 7;
196
+
197
+ // Write to data register
198
+ teletext.write(2, 0xaa);
199
+
200
+ // Check if value was written and column incremented
201
+ expect(teletext.frameBuffer[3][7]).toBe(0xaa);
202
+ expect(teletext.colPtr).toBe(8);
203
+ });
204
+
205
+ it("should clear status and interrupt on addr 3 write", () => {
206
+ // Set status and interrupt
207
+ teletext.teletextStatus = 0xff;
208
+ mockCpu.interrupt = 1 << TELETEXT_IRQ;
209
+
210
+ // Write to addr 3
211
+ teletext.write(3, 0);
212
+
213
+ // Status should be cleared (INT, DOR, and FSYN latches)
214
+ expect(teletext.teletextStatus & 0xd0).toBe(0);
215
+
216
+ // Interrupt should be cleared
217
+ expect(mockCpu.interrupt & (1 << TELETEXT_IRQ)).toBe(0);
218
+ });
219
+ });
220
+ });
221
+
222
+ describe("Update and polling", () => {
223
+ // Create a mock implementation of update() that doesn't access streamData
224
+ // This avoids trying to access the null streamData property
225
+ let originalUpdate;
226
+
227
+ beforeEach(() => {
228
+ // Save original update method
229
+ originalUpdate = teletext.update;
230
+
231
+ // Replace with mock that only updates the status and frame counter
232
+ teletext.update = function () {
233
+ // Set status latches
234
+ this.teletextStatus &= 0x0f;
235
+ this.teletextStatus |= 0xd0;
236
+
237
+ // Increment frame counter
238
+ if (this.currentFrame >= this.totalFrames - 1) {
239
+ this.currentFrame = 0;
240
+ } else {
241
+ this.currentFrame++;
242
+ }
243
+
244
+ // Reset pointers
245
+ this.rowPtr = 0;
246
+ this.colPtr = 0;
247
+
248
+ // Set interrupt if enabled
249
+ if (this.teletextInts) {
250
+ this.cpu.interrupt |= 1 << TELETEXT_IRQ;
251
+ }
252
+ };
253
+ });
254
+
255
+ afterEach(() => {
256
+ // Restore original update method
257
+ teletext.update = originalUpdate;
258
+ });
259
+
260
+ it("should update status and frame counter on update()", () => {
261
+ // Set initial state
262
+ teletext.teletextEnable = true;
263
+ teletext.currentFrame = 2;
264
+ teletext.totalFrames = 5;
265
+
266
+ // Call update
267
+ teletext.update();
268
+
269
+ // Check status latches were set
270
+ expect(teletext.teletextStatus & 0xd0).toBe(0xd0);
271
+
272
+ // Check frame was incremented
273
+ expect(teletext.currentFrame).toBe(3);
274
+
275
+ // Check pointers were reset
276
+ expect(teletext.rowPtr).toBe(0);
277
+ expect(teletext.colPtr).toBe(0);
278
+ });
279
+
280
+ it("should wrap to frame 0 when reaching end of frames", () => {
281
+ // Set to last frame
282
+ teletext.currentFrame = 4;
283
+ teletext.totalFrames = 5;
284
+
285
+ // Call update
286
+ teletext.update();
287
+
288
+ // Check frame wrapped to 0
289
+ expect(teletext.currentFrame).toBe(0);
290
+ });
291
+
292
+ it("should generate interrupt if interrupts enabled", () => {
293
+ // Enable interrupts
294
+ teletext.teletextInts = true;
295
+
296
+ // Clear interrupt
297
+ mockCpu.interrupt = 0;
298
+
299
+ // Call update
300
+ teletext.update();
301
+
302
+ // Check interrupt was generated
303
+ expect(mockCpu.interrupt & (1 << TELETEXT_IRQ)).toBe(1 << TELETEXT_IRQ);
304
+ });
305
+
306
+ it("should not generate interrupt if interrupts disabled", () => {
307
+ // Disable interrupts
308
+ teletext.teletextInts = false;
309
+
310
+ // Clear interrupt
311
+ mockCpu.interrupt = 0;
312
+
313
+ // Call update
314
+ teletext.update();
315
+
316
+ // Check interrupt wasn't generated
317
+ expect(mockCpu.interrupt & (1 << TELETEXT_IRQ)).toBe(0);
318
+ });
319
+
320
+ it("should trigger update when poll cycles exceed threshold", () => {
321
+ // Mock update method
322
+ const updateSpy = vi.spyOn(teletext, "update");
323
+
324
+ // Poll with cycles just below threshold (50000)
325
+ teletext.pollCount = 0;
326
+ teletext.polltime(49999);
327
+
328
+ // Check update wasn't called
329
+ expect(updateSpy).not.toHaveBeenCalled();
330
+
331
+ // Poll with cycles to exceed threshold
332
+ teletext.polltime(2);
333
+
334
+ // Check update was called
335
+ expect(updateSpy).toHaveBeenCalled();
336
+
337
+ // Check poll count was reset
338
+ expect(teletext.pollCount).toBe(0);
339
+ });
340
+
341
+ it("should not update when CPU reset line is low", () => {
342
+ // Set CPU reset line low
343
+ mockCpu.resetLine = false;
344
+
345
+ // Mock update method
346
+ const updateSpy = vi.spyOn(teletext, "update");
347
+
348
+ // Poll with cycles to exceed threshold
349
+ teletext.pollCount = 0;
350
+ teletext.polltime(50001);
351
+
352
+ // Check update wasn't called
353
+ expect(updateSpy).not.toHaveBeenCalled();
354
+
355
+ // Check poll count was set to negative value
356
+ expect(teletext.pollCount).toBeLessThan(0);
357
+ });
358
+ });
359
+ });