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
package/src/6502.js ADDED
@@ -0,0 +1,1347 @@
1
+ "use strict";
2
+ import * as utils from "./utils.js";
3
+ import * as via from "./via.js";
4
+ import { Acia } from "./acia.js";
5
+ import { Serial } from "./serial.js";
6
+ import { Tube } from "./tube.js";
7
+ import { Adc } from "./adc.js";
8
+ import { Scheduler } from "./scheduler.js";
9
+ import { TouchScreen } from "./touchscreen.js";
10
+ import { TeletextAdaptor } from "./teletext_adaptor.js";
11
+ import { Filestore } from "./filestore.js";
12
+
13
+ const signExtend = utils.signExtend;
14
+
15
+ function _set(byte, mask, set) {
16
+ return (byte & ~mask) | (set ? mask : 0);
17
+ }
18
+
19
+ class Flags {
20
+ constructor() {
21
+ this._byte = 0x30;
22
+ }
23
+
24
+ get c() {
25
+ return !!(this._byte & 0x01);
26
+ }
27
+
28
+ set c(val) {
29
+ this._byte = _set(this._byte, 0x01, val);
30
+ }
31
+
32
+ get z() {
33
+ return !!(this._byte & 0x02);
34
+ }
35
+
36
+ set z(val) {
37
+ this._byte = _set(this._byte, 0x02, val);
38
+ }
39
+
40
+ get i() {
41
+ return !!(this._byte & 0x04);
42
+ }
43
+
44
+ set i(val) {
45
+ this._byte = _set(this._byte, 0x04, val);
46
+ }
47
+
48
+ get d() {
49
+ return !!(this._byte & 0x08);
50
+ }
51
+
52
+ set d(val) {
53
+ this._byte = _set(this._byte, 0x08, val);
54
+ }
55
+
56
+ get v() {
57
+ return !!(this._byte & 0x40);
58
+ }
59
+
60
+ set v(val) {
61
+ this._byte = _set(this._byte, 0x40, val);
62
+ }
63
+
64
+ get n() {
65
+ return !!(this._byte & 0x80);
66
+ }
67
+
68
+ set n(val) {
69
+ this._byte = _set(this._byte, 0x80, val);
70
+ }
71
+
72
+ reset() {
73
+ this._byte = 0x30;
74
+ }
75
+
76
+ debugString() {
77
+ return (
78
+ (this.n ? "N" : "n") +
79
+ (this.v ? "V" : "v") +
80
+ "xx" +
81
+ (this.d ? "D" : "d") +
82
+ (this.i ? "I" : "i") +
83
+ (this.z ? "Z" : "z") +
84
+ (this.c ? "C" : "c")
85
+ );
86
+ }
87
+
88
+ setzn(v) {
89
+ v &= 0xff;
90
+ this._byte = (this._byte & ~(0x02 | 0x80)) | (v & 0x80) | (v === 0 ? 0x02 : 0x00);
91
+ return v | 0;
92
+ }
93
+
94
+ asByte() {
95
+ return this._byte | 0x30;
96
+ }
97
+
98
+ setFromByte(byte) {
99
+ this._byte = byte | 0x30;
100
+ }
101
+ }
102
+
103
+ class Base6502 {
104
+ constructor(model) {
105
+ this.model = model;
106
+ this.a = this.x = this.y = this.s = 0;
107
+ this.p = new Flags();
108
+ this.pc = 0;
109
+ this.opcodes = model.opcodesFactory(this);
110
+ this.disassembler = this.opcodes.disassembler;
111
+ this.forceTracing = false;
112
+ this.runner = this.opcodes.runInstruction;
113
+ this.interrupt = 0;
114
+ this._nmiLevel = false;
115
+ this._nmiEdge = false;
116
+
117
+ if (model.nmos) {
118
+ this.adc = function (addend) {
119
+ if (!this.p.d) {
120
+ this.adcNonBCD(addend);
121
+ } else {
122
+ this.adcBCD(addend);
123
+ }
124
+ };
125
+
126
+ this.sbc = function (subend) {
127
+ if (!this.p.d) {
128
+ this.adcNonBCD(subend ^ 0xff);
129
+ } else {
130
+ this.sbcBCD(subend);
131
+ }
132
+ };
133
+ } else {
134
+ this.adc = function (addend) {
135
+ if (!this.p.d) {
136
+ this.adcNonBCD(addend);
137
+ } else {
138
+ this.adcBCDcmos(addend);
139
+ }
140
+ };
141
+
142
+ this.sbc = function (subend) {
143
+ if (!this.p.d) {
144
+ this.adcNonBCD(subend ^ 0xff);
145
+ } else {
146
+ this.sbcBCDcmos(subend);
147
+ }
148
+ };
149
+ }
150
+ }
151
+
152
+ incpc() {
153
+ this.pc = (this.pc + 1) & 0xffff;
154
+ }
155
+
156
+ getb() {
157
+ const result = this.readmem(this.pc);
158
+ this.incpc();
159
+ return result | 0;
160
+ }
161
+
162
+ getw() {
163
+ let result = this.readmem(this.pc) | 0;
164
+ this.incpc();
165
+ result |= (this.readmem(this.pc) | 0) << 8;
166
+ this.incpc();
167
+ return result | 0;
168
+ }
169
+
170
+ checkInt() {
171
+ this.takeInt = !!(this.interrupt && !this.p.i);
172
+ this.takeInt |= this._nmiEdge;
173
+ }
174
+
175
+ // eslint-disable-next-line no-unused-vars
176
+ writemem(address, value) {
177
+ throw new Error("Must be overridden");
178
+ }
179
+
180
+ writememZpStack(address, value) {
181
+ this.writemem(address, value);
182
+ }
183
+
184
+ // eslint-disable-next-line no-unused-vars
185
+ readmem(address) {
186
+ throw new Error("Must be overridden");
187
+ }
188
+
189
+ readmemZpStack(address) {
190
+ return this.readmem(address);
191
+ }
192
+
193
+ push(v) {
194
+ this.writememZpStack(0x100 + this.s, v);
195
+ this.s = (this.s - 1) & 0xff;
196
+ }
197
+
198
+ pull() {
199
+ this.s = (this.s + 1) & 0xff;
200
+ return this.readmemZpStack(0x100 + this.s);
201
+ }
202
+
203
+ get nmi() {
204
+ return this._nmiLevel;
205
+ }
206
+
207
+ NMI(nmi) {
208
+ const prevLevel = this._nmiLevel;
209
+ this._nmiLevel = !!nmi;
210
+ if (this._nmiLevel && !prevLevel) this._nmiEdge = true;
211
+ }
212
+
213
+ polltime() {
214
+ throw new Error("Must be overridden");
215
+ }
216
+
217
+ brk(isIrq) {
218
+ // Behavior here generally discovered via Visual 6502 analysis.
219
+ // 6502 has a quirky BRK; it was sanitized in 65c12.
220
+ // See also https://wiki.nesdev.com/w/index.php/CPU_interrupts
221
+ let pushAddr = this.pc;
222
+ if (!isIrq) pushAddr = (pushAddr + 1) & 0xffff;
223
+ this.readmem(pushAddr);
224
+
225
+ this.push(pushAddr >>> 8);
226
+ this.push(pushAddr & 0xff);
227
+ let pushFlags = this.p.asByte();
228
+ if (isIrq) pushFlags &= ~0x10;
229
+ this.push(pushFlags);
230
+
231
+ // NMI status is determined part way through the BRK / IRQ
232
+ // sequence, and yes, on 6502, an NMI can redirect the vector
233
+ // for a half-way done BRK instruction.
234
+ this.polltime(4);
235
+ let vector = 0xfffe;
236
+ if ((this.model.nmos || isIrq) && this._nmiEdge) {
237
+ vector = 0xfffa;
238
+ this._nmiEdge = false;
239
+ }
240
+ this.takeInt = false;
241
+ this.pc = this.readmem(vector) | (this.readmem(vector + 1) << 8);
242
+ this.p.i = true;
243
+ if (this.model.nmos) {
244
+ this.polltime(3);
245
+ } else {
246
+ this.p.d = false;
247
+ if (isIrq) {
248
+ this.polltime(3);
249
+ } else {
250
+ this.polltime(2);
251
+ // TODO: check 65c12 BRK interrupt poll timing.
252
+ this.checkInt();
253
+ this.polltime(1);
254
+ }
255
+ }
256
+ }
257
+
258
+ branch(taken) {
259
+ const offset = signExtend(this.getb());
260
+ if (!taken) {
261
+ this.polltime(1);
262
+ this.checkInt();
263
+ this.polltime(1);
264
+ return;
265
+ }
266
+ const newPc = (this.pc + offset) & 0xffff;
267
+ const pageCrossed = !!((this.pc & 0xff00) ^ (newPc & 0xff00));
268
+ this.pc = newPc;
269
+ if (!this.model.nmos) {
270
+ this.polltime(2 + pageCrossed);
271
+ this.checkInt();
272
+ this.polltime(1);
273
+ } else if (!pageCrossed) {
274
+ this.polltime(1);
275
+ this.checkInt();
276
+ this.polltime(2);
277
+ } else {
278
+ // 6502 polls twice during a taken branch with page
279
+ // crossing and either is sufficient to trigger IRQ.
280
+ // See https://wiki.nesdev.com/w/index.php/CPU_interrupts
281
+ this.polltime(1);
282
+ this.checkInt();
283
+ const sawInt = this.takeInt;
284
+ this.polltime(2);
285
+ this.checkInt();
286
+ this.takeInt |= sawInt;
287
+ this.polltime(1);
288
+ }
289
+ }
290
+
291
+ adcNonBCD(addend) {
292
+ const result = this.a + addend + (this.p.c ? 1 : 0);
293
+ this.p.v = !!((this.a ^ result) & (addend ^ result) & 0x80);
294
+ this.p.c = !!(result & 0x100);
295
+ this.a = this.p.setzn(result);
296
+ }
297
+
298
+ // For flags and stuff see URLs like:
299
+ // http://www.visual6502.org/JSSim/expert.html?graphics=false&a=0&d=a900f86911eaeaea&steps=16
300
+ adcBCD(addend) {
301
+ let ah = 0;
302
+ const tempb = (this.a + addend + (this.p.c ? 1 : 0)) & 0xff;
303
+ this.p.z = !tempb;
304
+ let al = (this.a & 0xf) + (addend & 0xf) + (this.p.c ? 1 : 0);
305
+ if (al > 9) {
306
+ al -= 10;
307
+ al &= 0xf;
308
+ ah = 1;
309
+ }
310
+ ah += (this.a >>> 4) + (addend >>> 4);
311
+ this.p.n = !!(ah & 8);
312
+ this.p.v = !((this.a ^ addend) & 0x80) && !!((this.a ^ (ah << 4)) & 0x80);
313
+ this.p.c = false;
314
+ if (ah > 9) {
315
+ this.p.c = true;
316
+ ah -= 10;
317
+ ah &= 0xf;
318
+ }
319
+ this.a = ((al & 0xf) | (ah << 4)) & 0xff;
320
+ }
321
+
322
+ // With reference to c64doc: http://vice-emu.sourceforge.net/plain/64doc.txt
323
+ // and http://www.visual6502.org/JSSim/expert.html?graphics=false&a=0&d=a900f8e988eaeaea&steps=18
324
+ sbcBCD(subend) {
325
+ const carry = this.p.c ? 0 : 1;
326
+ let al = (this.a & 0xf) - (subend & 0xf) - carry;
327
+ let ah = (this.a >>> 4) - (subend >>> 4);
328
+ if (al & 0x10) {
329
+ al = (al - 6) & 0xf;
330
+ ah--;
331
+ }
332
+ if (ah & 0x10) {
333
+ ah = (ah - 6) & 0xf;
334
+ }
335
+
336
+ const result = this.a - subend - carry;
337
+ this.p.n = !!(result & 0x80);
338
+ this.p.z = !(result & 0xff);
339
+ this.p.v = !!((this.a ^ result) & (subend ^ this.a) & 0x80);
340
+ this.p.c = !(result & 0x100);
341
+ this.a = al | (ah << 4);
342
+ }
343
+
344
+ adcBCDcmos(addend) {
345
+ this.polltime(1); // One more cycle, apparently
346
+ const carry = this.p.c ? 1 : 0;
347
+ let al = (this.a & 0xf) + (addend & 0xf) + carry;
348
+ let ah = (this.a >>> 4) + (addend >>> 4);
349
+ if (al > 9) {
350
+ al = (al - 10) & 0xf;
351
+ ah++;
352
+ }
353
+ this.p.v = !((this.a ^ addend) & 0x80) && !!((this.a ^ (ah << 4)) & 0x80);
354
+ this.p.c = false;
355
+ if (ah > 9) {
356
+ ah = (ah - 10) & 0xf;
357
+ this.p.c = true;
358
+ }
359
+ this.a = this.p.setzn(al | (ah << 4));
360
+ }
361
+
362
+ sbcBCDcmos(subend) {
363
+ this.polltime(1); // One more cycle, apparently
364
+ const carry = this.p.c ? 0 : 1;
365
+ const al = (this.a & 0xf) - (subend & 0xf) - carry;
366
+ let result = this.a - subend - carry;
367
+ if (result < 0) {
368
+ result -= 0x60;
369
+ }
370
+ if (al < 0) result -= 0x06;
371
+
372
+ this.adcNonBCD(subend ^ 0xff); // For flags
373
+ this.a = this.p.setzn(result);
374
+ }
375
+
376
+ arr(arg) {
377
+ // Insane instruction. I started with b-em source, but ended up using:
378
+ // http://www.6502.org/users/andre/petindex/local/64doc.txt as reference,
379
+ // tidying up as needed and fixing a couple of typos.
380
+ if (this.p.d) {
381
+ const temp = this.a & arg;
382
+
383
+ const ah = temp >>> 4;
384
+ const al = temp & 0x0f;
385
+
386
+ this.p.n = this.p.c;
387
+ this.a = (temp >>> 1) | (this.p.c ? 0x80 : 0x00);
388
+ this.p.z = !this.a;
389
+ this.p.v = (temp ^ this.a) & 0x40;
390
+
391
+ if (al + (al & 1) > 5) this.a = (this.a & 0xf0) | ((this.a + 6) & 0xf);
392
+
393
+ this.p.c = ah + (ah & 1) > 5;
394
+ if (this.p.c) this.a = (this.a + 0x60) & 0xff;
395
+ } else {
396
+ this.a = this.a & arg;
397
+ this.p.v = !!(((this.a >>> 7) ^ (this.a >>> 6)) & 0x01);
398
+ this.a >>>= 1;
399
+ if (this.p.c) this.a |= 0x80;
400
+ this.p.setzn(this.a);
401
+ this.p.c = !!(this.a & 0x40);
402
+ }
403
+ }
404
+ }
405
+
406
+ class Tube6502 extends Base6502 {
407
+ constructor(model, cpu) {
408
+ super(model);
409
+
410
+ this.cycles = 0;
411
+ this.romPaged = true;
412
+ this.memory = new Uint8Array(65536);
413
+ this.rom = new Uint8Array(4096);
414
+
415
+ this.tube = new Tube(cpu, this);
416
+ }
417
+
418
+ reset(hard) {
419
+ this.romPaged = true;
420
+ this.pc = this.readmem(0xfffc) | (this.readmem(0xfffd) << 8);
421
+ this.p.i = true;
422
+ this.tube.reset(hard);
423
+ }
424
+
425
+ readmem(offset) {
426
+ if ((offset & 0xfff8) === 0xfef8) {
427
+ if ((offset & 7) === 0) {
428
+ this.romPaged = false;
429
+ }
430
+ return this.tube.parasiteRead(offset);
431
+ }
432
+ if (this.romPaged && (offset & 0xf000) === 0xf000) {
433
+ return this.rom[offset & 0xfff];
434
+ }
435
+ return this.memory[offset & 0xffff];
436
+ }
437
+
438
+ readmemZpStack(offset) {
439
+ return this.memory[offset & 0xffff];
440
+ }
441
+
442
+ writemem(addr, b) {
443
+ if ((addr & 0xfff8) === 0xfef8) {
444
+ return this.tube.parasiteWrite(addr, b);
445
+ }
446
+ this.memory[addr & 0xffff] = b;
447
+ }
448
+
449
+ writememZpStack(addr, b) {
450
+ this.memory[addr & 0xffff] = b;
451
+ }
452
+
453
+ polltime(cycles) {
454
+ this.cycles -= cycles;
455
+ }
456
+
457
+ polltimeAddr(cycles) {
458
+ this.polltime(cycles);
459
+ }
460
+
461
+ read(addr) {
462
+ return this.tube.hostRead(addr);
463
+ }
464
+
465
+ write(addr, b) {
466
+ this.tube.hostWrite(addr, b);
467
+ }
468
+
469
+ execute(cycles) {
470
+ this.cycles += cycles * 2;
471
+ if (this.cycles < 3) return;
472
+ while (this.cycles > 0) {
473
+ const opcode = this.readmem(this.pc);
474
+ this.incpc();
475
+ this.runner.run(opcode);
476
+ if (this.takeInt) this.brk(true);
477
+ }
478
+ }
479
+
480
+ async loadOs() {
481
+ console.log("Loading tube rom from roms/" + this.model.os);
482
+ const tubeRom = this.rom;
483
+ const data = await utils.loadData("roms/" + this.model.os);
484
+ const len = data.length;
485
+ if (len !== 2048) throw new Error("Broken ROM file (length=" + len + ")");
486
+ for (let i = 0; i < len; ++i) {
487
+ tubeRom[i + 2048] = data[i];
488
+ }
489
+ }
490
+ }
491
+
492
+ class FakeTube {
493
+ read() {
494
+ return 0xfe;
495
+ }
496
+
497
+ write() {}
498
+
499
+ execute() {}
500
+
501
+ reset() {}
502
+ }
503
+
504
+ class FakeUserPort {
505
+ write() {}
506
+
507
+ read() {
508
+ return 0xff;
509
+ }
510
+ }
511
+
512
+ function fixUpConfig(config) {
513
+ if (config === undefined) config = {};
514
+ if (!config.keyLayout) config.keyLayout = "physical";
515
+ if (!config.cpuMultiplier) config.cpuMultiplier = 1;
516
+ if (!config.userPort) config.userPort = new FakeUserPort();
517
+ if (config.printerPort === undefined) config.printerPort = null;
518
+ config.extraRoms = config.extraRoms || [];
519
+ config.debugFlags = config.debugFlags || {};
520
+ return config;
521
+ }
522
+
523
+ class DebugHook {
524
+ constructor(cpu, functionName) {
525
+ this.cpu = cpu;
526
+ this.functionName = functionName;
527
+ this.handlers = [];
528
+ }
529
+
530
+ add(handler) {
531
+ const self = this;
532
+ this.handlers.push(handler);
533
+ if (!this.cpu[this.functionName]) {
534
+ this.cpu[this.functionName] = function () {
535
+ for (let i = 0; i < self.handlers.length; ++i) {
536
+ const handler = self.handlers[i];
537
+ if (handler.apply(handler, arguments)) {
538
+ self.cpu.stop();
539
+ return true;
540
+ }
541
+ }
542
+ return false;
543
+ };
544
+ }
545
+ handler.remove = function () {
546
+ self.remove(handler);
547
+ };
548
+ return handler;
549
+ }
550
+
551
+ remove(handler) {
552
+ const i = this.handlers.indexOf(handler);
553
+ if (i < 0) throw "Unable to find debug hook handler";
554
+ this.handlers = this.handlers.slice(0, i).concat(this.handlers.slice(i + 1));
555
+ if (this.handlers.length === 0) {
556
+ this.cpu[this.functionName] = null;
557
+ }
558
+ }
559
+
560
+ clear() {
561
+ this.handlers = [];
562
+ this.cpu[this.functionName] = null;
563
+ }
564
+ }
565
+
566
+ function is1MHzAccess(addr) {
567
+ const FEslowdown = [true, false, true, true, false, false, true, false];
568
+ return addr >= 0xfc00 && addr < 0xff00 && (addr < 0xfe00 || FEslowdown[(addr >>> 5) & 7]);
569
+ }
570
+
571
+ export class Cpu6502 extends Base6502 {
572
+ constructor(model, dbgr, video_, soundChip_, ddNoise_, music5000_, cmos, config, econet_) {
573
+ super(model);
574
+ this.config = fixUpConfig(config);
575
+ this.debugFlags = this.config.debugFlags;
576
+ this.cmos = cmos;
577
+ this.debugger = dbgr;
578
+
579
+ this.video = video_;
580
+ this.crtc = this.video.crtc;
581
+ this.ula = this.video.ula;
582
+ this.soundChip = soundChip_;
583
+ this.music5000 = music5000_;
584
+ this.ddNoise = ddNoise_;
585
+ this.memStatOffsetByIFetchBank = 0;
586
+ this.memStatOffset = 0;
587
+ this.memStat = new Uint8Array(512);
588
+ this.memLook = new Int32Array(512); // Cannot be unsigned as we use negative offsets
589
+ this.ramRomOs = new Uint8Array(128 * 1024 + 17 * 16 * 16384);
590
+ this.romOffset = 128 * 1024;
591
+ this.osOffset = this.romOffset + 16 * 16 * 1024;
592
+ this.romsel = 0;
593
+ this.acccon = 0;
594
+ this.oldPcArray = new Uint16Array(256);
595
+ this.oldAArray = new Uint8Array(256);
596
+ this.oldXArray = new Uint8Array(256);
597
+ this.oldYArray = new Uint8Array(256);
598
+ this.oldPcIndex = 0;
599
+ this.resetLine = true;
600
+ this.cpuMultiplier = this.config.cpuMultiplier;
601
+ this.videoCyclesBatch = this.config.videoCyclesBatch | 0;
602
+ this.peripheralCyclesPerSecond = 2 * 1000 * 1000;
603
+ this.tube = model.tube ? new Tube6502(model.tube, this) : new FakeTube();
604
+ this.music5000PageSel = 0;
605
+ this.econet = econet_;
606
+
607
+ this.peripheralCycles = 0;
608
+ this.videoCycles = 0;
609
+
610
+ if (this.cpuMultiplier === 1 && this.videoCyclesBatch === 0) {
611
+ this.polltime = this.polltimeFast;
612
+ } else {
613
+ this.polltime = this.polltimeSlow;
614
+ }
615
+
616
+ this._debugRead = this._debugWrite = this._debugInstruction = null;
617
+ this.debugInstruction = new DebugHook(this, "_debugInstruction");
618
+ this.debugRead = new DebugHook(this, "_debugRead");
619
+ this.debugWrite = new DebugHook(this, "_debugWrite");
620
+
621
+ this.scheduler = new Scheduler();
622
+ this.sysvia = new via.SysVia(
623
+ this,
624
+ this.scheduler,
625
+ this.video,
626
+ this.soundChip,
627
+ this.cmos,
628
+ this.model.isMaster,
629
+ this.config.keyLayout,
630
+ this.config.getGamepads,
631
+ );
632
+ this.uservia = new via.UserVia(this, this.scheduler, this.model.isMaster, this.config.userPort);
633
+ this.acia = new Acia(this, this.soundChip.toneGenerator, this.scheduler, this.touchScreen);
634
+ this.serial = new Serial(this.acia);
635
+ this.adconverter = new Adc(this.sysvia, this.scheduler);
636
+ this.soundChip.setScheduler(this.scheduler);
637
+ this.fdc = new this.model.Fdc(this, this.ddNoise, this.scheduler, this.debugFlags);
638
+ }
639
+
640
+ getPrevPc(index) {
641
+ return this.oldPcArray[(this.oldPcIndex - index) & 0xff];
642
+ }
643
+
644
+ // BBC Master memory map (within ramRomOs array):
645
+ // 00000 - 08000 -> base 32KB RAM
646
+ // 08000 - 09000 -> ANDY - 4KB
647
+ // 09000 - 0b000 -> HAZEL - 8KB
648
+ // 0b000 - 10000 -> LYNNE - 20KB
649
+ romSelect(b) {
650
+ this.romsel = b;
651
+ const bankOffset = ((b & 15) << 14) + this.romOffset;
652
+ const offset = bankOffset - 0x8000;
653
+ for (let c = 128; c < 192; ++c) this.memLook[c] = this.memLook[256 + c] = offset;
654
+ const swram = this.model.swram[b & 15] ? 1 : 2;
655
+ for (let c = 128; c < 192; ++c) this.memStat[c] = this.memStat[256 + c] = swram;
656
+ if (this.model.isMaster && b & 0x80) {
657
+ // 4Kb RAM (private RAM - ANDY)
658
+ // Zero offset as 0x8000 mapped to 0x8000
659
+ for (let c = 128; c < 144; ++c) {
660
+ this.memLook[c] = this.memLook[256 + c] = 0;
661
+ this.memStat[c] = this.memStat[256 + c] = 1;
662
+ }
663
+ }
664
+ }
665
+
666
+ writeAcccon(b) {
667
+ this.acccon = b;
668
+ // ACCCON is
669
+ // IRR TST IJF ITU Y X E D
670
+ // 7 6 5 4 3 2 1 0
671
+ // Video offset (to LYNNE) is controlled by the "D" bit of ACCCON.
672
+ // LYNNE lives at 0xb000 in our map, but the offset we use here is 0x8000
673
+ // as the video circuitry will already be looking at 0x3000 or so above
674
+ // the offset.
675
+ this.videoDisplayPage = b & 1 ? 0x8000 : 0x0000;
676
+
677
+ const bitE = !!(b & 2);
678
+ const bitX = !!(b & 4);
679
+ const bitY = !!(b & 8);
680
+ // The "X" bit controls the "illegal" paging 20KB region overlay of LYNNE.
681
+ // This loop rewires which paged RAM 0x3000 - 0x7fff hits.
682
+ for (let i = 48; i < 128; ++i) {
683
+ // For "normal" access, it's simple: shadow or not.
684
+ this.memLook[i] = bitX ? 0x8000 : 0;
685
+ // For special Master opcode access at 0xc000 - 0xdfff,
686
+ // it's more involved.
687
+ if (bitY) {
688
+ // If 0xc000 is mapped as RAM, the Master opcode access
689
+ // is disabled; follow what normal access does.
690
+ this.memLook[i + 256] = this.memLook[i];
691
+ } else {
692
+ // Master opcode access enabled; bit E determines whether
693
+ // it hits shadow RAM or normal RAM. This is independent
694
+ // of bit X.
695
+ this.memLook[i + 256] = bitE ? 0x8000 : 0;
696
+ }
697
+ }
698
+ // The "Y" bit pages in HAZEL at c000->dfff. HAZEL is mapped in our RAM
699
+ // at 0x9000, so (0x9000 - 0xc000) = -0x3000 is needed as an offset.
700
+ const hazelRAM = bitY ? 1 : 2;
701
+ const hazelOff = bitY ? -0x3000 : this.osOffset - 0xc000;
702
+ for (let i = 192; i < 224; ++i) {
703
+ this.memLook[i] = this.memLook[i + 256] = hazelOff;
704
+ this.memStat[i] = this.memStat[i + 256] = hazelRAM;
705
+ }
706
+ }
707
+
708
+ // Works for unpaged RAM only (ie stack and zp)
709
+ readmemZpStack(addr) {
710
+ addr &= 0xffff;
711
+ const res = this.ramRomOs[addr];
712
+ if (this._debugRead) this._debugRead(addr, 0, res);
713
+ return res | 0;
714
+ }
715
+
716
+ writememZpStack(addr, b) {
717
+ addr &= 0xffff;
718
+ b |= 0;
719
+ if (this._debugWrite) this._debugWrite(addr, b);
720
+ this.ramRomOs[addr] = b;
721
+ }
722
+
723
+ // Handy debug function to read a string zero or \n terminated.
724
+ readString(addr) {
725
+ let s = "";
726
+ for (;;) {
727
+ const b = this.readmem(addr);
728
+ addr++;
729
+ if (b === 0 || b === 13) break;
730
+ s += String.fromCharCode(b);
731
+ }
732
+ return s;
733
+ }
734
+
735
+ findString(string, addr) {
736
+ addr = addr | 0;
737
+ for (; addr < 0xffff; ++addr) {
738
+ let i;
739
+ for (i = 0; i < string.length; ++i) {
740
+ if (this.readmem(addr + i) !== string.charCodeAt(i)) break;
741
+ }
742
+ if (i === string.length) {
743
+ return addr;
744
+ }
745
+ }
746
+ return null;
747
+ }
748
+
749
+ readArea(addr, len) {
750
+ let str = "";
751
+ for (let i = 0; i < len; ++i) {
752
+ str += utils.hexbyte(this.readmem(addr + i));
753
+ }
754
+ return str;
755
+ }
756
+
757
+ handleEconetStationId() {
758
+ if (!this.econet) return 0xff;
759
+ this.econet.econetNMIEnabled = false;
760
+ return this.econet.stationId;
761
+ }
762
+
763
+ handleEconetNMIEnable() {
764
+ if (this.econet && !this.econet.econetNMIEnabled) {
765
+ // was off
766
+ this.econet.econetNMIEnabled = true;
767
+ if (this.econet.ADLC.status1 & 128) {
768
+ // irq pending
769
+ this.NMI(true); // delayed NMI asserted
770
+ }
771
+ }
772
+ return 0xff;
773
+ }
774
+
775
+ readDevice(addr) {
776
+ if (this.model.isMaster && this.acccon & 0x40) {
777
+ // TST bit of ACCCON
778
+ return this.ramRomOs[this.osOffset + (addr & 0x3fff)];
779
+ }
780
+ addr &= 0xffff;
781
+
782
+ switch (addr & ~0x0003) {
783
+ case 0xfc10:
784
+ if (this.model.hasTeletextAdaptor) return this.teletextAdaptor.read(addr - 0xfc10);
785
+ break;
786
+ case 0xfc20:
787
+ case 0xfc24:
788
+ case 0xfc28:
789
+ case 0xfc2c:
790
+ case 0xfc30:
791
+ case 0xfc34:
792
+ case 0xfc38:
793
+ case 0xfc3c:
794
+ // SID Chip.
795
+ break;
796
+ case 0xfc40:
797
+ case 0xfc44:
798
+ case 0xfc48:
799
+ case 0xfc4c:
800
+ case 0xfc50:
801
+ case 0xfc54:
802
+ case 0xfc58:
803
+ case 0xfc5c:
804
+ // IDE
805
+ break;
806
+ case 0xfcfc:
807
+ if (addr === 0xfcff && this.model.hasMusic5000) return this.music5000PageSel;
808
+ break;
809
+ case 0xfe00:
810
+ case 0xfe04:
811
+ return this.crtc.read(addr);
812
+ case 0xfe08:
813
+ case 0xfe0c:
814
+ return this.acia.read(addr);
815
+ case 0xfe10:
816
+ case 0xfe14:
817
+ return this.serial.read(addr);
818
+ case 0xfe18:
819
+ return this.model.isMaster ? this.adconverter.read(addr) : this.handleEconetStationId();
820
+ case 0xfe20:
821
+ if (!this.model.isMaster) return this.handleEconetNMIEnable();
822
+ break;
823
+ case 0xfe24:
824
+ case 0xfe28:
825
+ if (this.model.isMaster) return this.fdc.read(addr);
826
+ break;
827
+ case 0xfe30:
828
+ if (this.model.isMaster) return this.romsel & 0x8f;
829
+ break;
830
+ case 0xfe34:
831
+ if (this.model.isMaster) return this.acccon;
832
+ break;
833
+ case 0xfe38:
834
+ if (this.model.isMaster) return this.handleEconetStationId();
835
+ break;
836
+ case 0xfe3c:
837
+ if (this.model.isMaster) return this.handleEconetNMIEnable();
838
+ break;
839
+ case 0xfe40:
840
+ case 0xfe44:
841
+ case 0xfe48:
842
+ case 0xfe4c:
843
+ case 0xfe50:
844
+ case 0xfe54:
845
+ case 0xfe58:
846
+ case 0xfe5c:
847
+ return this.sysvia.read(addr);
848
+ case 0xfe60:
849
+ case 0xfe64:
850
+ case 0xfe68:
851
+ case 0xfe6c:
852
+ case 0xfe70:
853
+ case 0xfe74:
854
+ case 0xfe78:
855
+ case 0xfe7c:
856
+ return this.uservia.read(addr);
857
+ case 0xfe80:
858
+ case 0xfe84:
859
+ case 0xfe88:
860
+ case 0xfe8c:
861
+ case 0xfe90:
862
+ case 0xfe94:
863
+ case 0xfe98:
864
+ case 0xfe9c:
865
+ if (!this.model.isMaster) return this.fdc.read(addr);
866
+ break;
867
+ case 0xfea0:
868
+ // Econet status register
869
+ if (this.econet) {
870
+ return this.econet.readRegister(addr & 3);
871
+ }
872
+ break;
873
+ case 0xfec0:
874
+ case 0xfec4:
875
+ case 0xfec8:
876
+ case 0xfecc:
877
+ case 0xfed0:
878
+ case 0xfed4:
879
+ case 0xfed8:
880
+ case 0xfedc:
881
+ if (!this.model.isMaster) return this.adconverter.read(addr);
882
+ break;
883
+ case 0xfee0:
884
+ case 0xfee4:
885
+ case 0xfee8:
886
+ case 0xfeec:
887
+ case 0xfef0:
888
+ case 0xfef4:
889
+ case 0xfef8:
890
+ case 0xfefc:
891
+ return this.tube.read(addr);
892
+ }
893
+
894
+ if (this.model.hasMusic5000) {
895
+ if ((this.music5000PageSel & 0xf0) === 0x30 && (addr & 0xff00) === 0xfd00) {
896
+ return this.music5000.read(this.music5000PageSel, addr);
897
+ }
898
+ }
899
+
900
+ if (addr >= 0xfc00 && addr < 0xfe00) return 0xff;
901
+ return addr >>> 8;
902
+ }
903
+
904
+ videoRead(addr) {
905
+ return this.ramRomOs[addr | this.videoDisplayPage] | 0;
906
+ }
907
+
908
+ readmem(addr) {
909
+ addr &= 0xffff;
910
+ const statOffset = this.memStatOffset + (addr >>> 8);
911
+ if (this.memStat[statOffset]) {
912
+ const offset = this.memLook[statOffset];
913
+ const res = this.ramRomOs[offset + addr];
914
+ if (this._debugRead) this._debugRead(addr, res, offset);
915
+ return res | 0;
916
+ } else {
917
+ const res = this.readDevice(addr);
918
+ if (this._debugRead) this._debugRead(addr, res, 0);
919
+ return res | 0;
920
+ }
921
+ }
922
+
923
+ peekmem(addr) {
924
+ const statOffset = this.memStatOffset + (addr >>> 8);
925
+ if (this.memStat[statOffset]) {
926
+ const offset = this.memLook[statOffset];
927
+ return this.ramRomOs[offset + addr];
928
+ } else {
929
+ return 0xff; // TODO; peekDevice -- this.peekDevice(addr);
930
+ }
931
+ }
932
+
933
+ writemem(addr, b) {
934
+ addr &= 0xffff;
935
+ b |= 0;
936
+ if (this._debugWrite) this._debugWrite(addr, b);
937
+ const statOffset = this.memStatOffset + (addr >>> 8);
938
+ if (this.memStat[statOffset] === 1) {
939
+ const offset = this.memLook[statOffset];
940
+ this.ramRomOs[offset + addr] = b;
941
+ return;
942
+ }
943
+ if (addr < 0xfc00 || addr >= 0xff00) return;
944
+ this.writeDevice(addr, b);
945
+ }
946
+
947
+ writeDevice(addr, b) {
948
+ addr &= 0xffff;
949
+ b |= 0;
950
+
951
+ if (this.model.hasMusic5000 && (addr & 0xff00) === 0xfd00 && (this.music5000PageSel & 0xf0) === 0x30) {
952
+ this.music5000.write(this.music5000PageSel, addr, b);
953
+ return;
954
+ }
955
+
956
+ switch (addr & ~0x0003) {
957
+ case 0xfc10:
958
+ if (this.model.hasTeletextAdaptor) return this.teletextAdaptor.write(addr - 0xfc10, b);
959
+ break;
960
+ case 0xfc20:
961
+ case 0xfc24:
962
+ case 0xfc28:
963
+ case 0xfc2c:
964
+ case 0xfc30:
965
+ case 0xfc34:
966
+ case 0xfc38:
967
+ case 0xfc3c:
968
+ // SID chip
969
+ break;
970
+ case 0xfc40:
971
+ case 0xfc44:
972
+ case 0xfc48:
973
+ case 0xfc4c:
974
+ case 0xfc50:
975
+ case 0xfc54:
976
+ case 0xfc58:
977
+ case 0xfc5c:
978
+ // IDE
979
+ break;
980
+ case 0xfcfc:
981
+ if (addr === 0xfcff && this.model.hasMusic5000) {
982
+ this.music5000PageSel = b;
983
+ }
984
+ break;
985
+ case 0xfe00:
986
+ case 0xfe04:
987
+ return this.crtc.write(addr, b);
988
+ case 0xfe08:
989
+ case 0xfe0c:
990
+ return this.acia.write(addr, b);
991
+ case 0xfe10:
992
+ case 0xfe14:
993
+ return this.serial.write(addr, b);
994
+ case 0xfe18:
995
+ if (this.model.isMaster) return this.adconverter.write(addr, b);
996
+ if (!this.model.isMaster && this.econet) this.econet.econetNMIEnabled = false;
997
+ break;
998
+ case 0xfe20:
999
+ return this.ula.write(addr, b);
1000
+ case 0xfe24:
1001
+ case 0xfe28:
1002
+ if (this.model.isMaster) {
1003
+ return this.fdc.write(addr, b);
1004
+ }
1005
+ return this.ula.write(addr, b);
1006
+ case 0xfe2c:
1007
+ if (!this.model.isMaster) {
1008
+ return this.ula.write(addr, b);
1009
+ }
1010
+ break;
1011
+ case 0xfe30:
1012
+ return this.romSelect(b);
1013
+ case 0xfe34:
1014
+ if (this.model.isMaster) {
1015
+ return this.writeAcccon(b);
1016
+ }
1017
+ return this.romSelect(b);
1018
+ case 0xfe38:
1019
+ if (this.model.isMaster && this.econet) this.econet.econetNMIEnabled = false;
1020
+ break;
1021
+ case 0xfe3c:
1022
+ if (!this.model.isMaster) {
1023
+ return this.romSelect(b);
1024
+ }
1025
+ break;
1026
+ case 0xfe40:
1027
+ case 0xfe44:
1028
+ case 0xfe48:
1029
+ case 0xfe4c:
1030
+ case 0xfe50:
1031
+ case 0xfe54:
1032
+ case 0xfe58:
1033
+ case 0xfe5c:
1034
+ return this.sysvia.write(addr, b);
1035
+ case 0xfe60:
1036
+ case 0xfe64:
1037
+ case 0xfe68:
1038
+ case 0xfe6c:
1039
+ case 0xfe70:
1040
+ case 0xfe74:
1041
+ case 0xfe78:
1042
+ case 0xfe7c:
1043
+ return this.uservia.write(addr, b);
1044
+ case 0xfe80:
1045
+ case 0xfe84:
1046
+ case 0xfe88:
1047
+ case 0xfe8c:
1048
+ case 0xfe90:
1049
+ case 0xfe94:
1050
+ case 0xfe98:
1051
+ case 0xfe9c:
1052
+ if (!this.model.isMaster) return this.fdc.write(addr, b);
1053
+ break;
1054
+
1055
+ case 0xfea0:
1056
+ case 0xfea4:
1057
+ case 0xfea8:
1058
+ case 0xfeac:
1059
+ case 0xfeb0:
1060
+ case 0xfeb4:
1061
+ case 0xfeb8:
1062
+ case 0xfebc:
1063
+ if (this.econet) this.econet.writeRegister(addr & 3, b);
1064
+ break;
1065
+ case 0xfec0:
1066
+ case 0xfec4:
1067
+ case 0xfec8:
1068
+ case 0xfecc:
1069
+ case 0xfed0:
1070
+ case 0xfed4:
1071
+ case 0xfed8:
1072
+ case 0xfedc:
1073
+ if (!this.model.isMaster) return this.adconverter.write(addr, b);
1074
+ break;
1075
+ case 0xfee0:
1076
+ case 0xfee4:
1077
+ case 0xfee8:
1078
+ case 0xfeec:
1079
+ case 0xfef0:
1080
+ case 0xfef4:
1081
+ case 0xfef8:
1082
+ case 0xfefc:
1083
+ return this.tube.write(addr, b);
1084
+ }
1085
+ }
1086
+
1087
+ async loadRom(name, offset) {
1088
+ if (name.indexOf("http") !== 0) name = "roms/" + name;
1089
+ console.log("Loading ROM from " + name);
1090
+ const ramRomOs = this.ramRomOs;
1091
+ let data = await utils.loadData(name);
1092
+ if (/\.zip/i.test(name)) {
1093
+ data = utils.unzipRomImage(data).data;
1094
+ }
1095
+ ramRomOs.set(data, offset);
1096
+ }
1097
+
1098
+ async loadOs(os) {
1099
+ const extraRoms = Array.prototype.slice.call(arguments, 1).concat(this.config.extraRoms);
1100
+ os = "roms/" + os;
1101
+ console.log(`Loading OS from ${os}`);
1102
+ const ramRomOs = this.ramRomOs;
1103
+ const data = await utils.loadData(os);
1104
+ const len = data.length;
1105
+ if (len < 16384 || len & 16383) throw new Error(`Broken OS ROM file (length=${len})`);
1106
+ ramRomOs.set(data, this.osOffset);
1107
+ const numExtraBanks = (len - 16384) / 16384;
1108
+ let romIndex = 16 - numExtraBanks;
1109
+ for (let i_1 = 0; i_1 < numExtraBanks; ++i_1) {
1110
+ const srcBase = 16384 + 16384 * i_1;
1111
+ const destBase = this.romOffset + (romIndex + i_1) * 16384;
1112
+ ramRomOs.set(data.subarray(srcBase, srcBase + 16384), destBase);
1113
+ }
1114
+ const awaiting = [];
1115
+ for (let i_2 = 0; i_2 < extraRoms.length; ++i_2) {
1116
+ // Skip over banks 4-7 (sideways RAM on a Master)
1117
+ romIndex--;
1118
+ while (this.model.swram[romIndex]) {
1119
+ romIndex--;
1120
+ }
1121
+
1122
+ awaiting.push(this.loadRom(extraRoms[i_2], this.romOffset + romIndex * 16384));
1123
+ }
1124
+ return await Promise.all(awaiting);
1125
+ }
1126
+
1127
+ setReset(resetOn) {
1128
+ this.resetLine = !resetOn;
1129
+ }
1130
+
1131
+ reset(hard) {
1132
+ if (hard) {
1133
+ // On the Master, opcodes executing from 0xc000 - 0xdfff can optionally have their memory accesses
1134
+ // redirected to shadow RAM.
1135
+ this.memStatOffsetByIFetchBank = this.model.isMaster ? (1 << 0xc) | (1 << 0xd) : 0x0000;
1136
+ if (!this.model.isTest) {
1137
+ for (let i = 0; i < 128; ++i) this.memStat[i] = this.memStat[256 + i] = 1;
1138
+ for (let i = 128; i < 256; ++i) this.memStat[i] = this.memStat[256 + i] = 2;
1139
+ for (let i = 0; i < 128; ++i) this.memLook[i] = this.memLook[256 + i] = 0;
1140
+ for (let i = 128; i < 192; ++i) this.memLook[i] = this.memLook[256 + i] = this.romOffset - 0x8000;
1141
+ for (let i = 192; i < 256; ++i) this.memLook[i] = this.memLook[256 + i] = this.osOffset - 0xc000;
1142
+
1143
+ for (let i = 0xfc; i < 0xff; ++i) this.memStat[i] = this.memStat[256 + i] = 0;
1144
+ } else {
1145
+ // Test sets everything as RAM.
1146
+ for (let i = 0; i < 256; ++i) {
1147
+ this.memStat[i] = this.memStat[256 + i] = 1;
1148
+ this.memLook[i] = this.memLook[256 + i] = 0;
1149
+ }
1150
+ }
1151
+ // DRAM content is not guaranteed to contain any particular
1152
+ // value on start up, so we choose values that help avoid
1153
+ // bugs in various games.
1154
+ for (let i = 0; i < this.romOffset; ++i) {
1155
+ if (i < 0x100) {
1156
+ // For Clogger.
1157
+ this.ramRomOs[i] = 0x00;
1158
+ } else {
1159
+ // For Eagle Empire.
1160
+ this.ramRomOs[i] = 0xff;
1161
+ }
1162
+ }
1163
+ this.videoDisplayPage = 0;
1164
+ if (this.config.printerPort) this.uservia.ca2changecallback = this.config.printerPort.outputStrobe;
1165
+
1166
+ this.sysvia.reset();
1167
+ this.uservia.reset();
1168
+ this.acia.reset();
1169
+ this.serial.reset();
1170
+ this.ddNoise.spinDown();
1171
+ this.fdc.powerOnReset();
1172
+ this.adconverter.reset();
1173
+
1174
+ this.touchScreen = new TouchScreen(this.scheduler);
1175
+ if (this.model.hasTeletextAdaptor) this.teletextAdaptor = new TeletextAdaptor(this);
1176
+ if (this.econet) this.filestore = new Filestore(this, this.econet);
1177
+ } else {
1178
+ this.fdc.reset();
1179
+ }
1180
+ this.tube.reset(hard);
1181
+ if (hard) {
1182
+ this.targetCycles = 0;
1183
+ this.currentCycles = 0;
1184
+ this.cycleSeconds = 0;
1185
+ }
1186
+ this.pc = this.readmem(0xfffc) | (this.readmem(0xfffd) << 8);
1187
+ this.p.i = true;
1188
+ this._nmiEdge = false;
1189
+ this._nmiLevel = false;
1190
+ this.halted = false;
1191
+ this.music5000PageSel = 0;
1192
+ this.video.reset(this, this.sysvia, hard);
1193
+ this.soundChip.reset(hard);
1194
+ if (this.teletextAdaptor) this.teletextAdaptor.reset(hard);
1195
+ if (this.music5000) this.music5000.reset(hard);
1196
+ if (hard && this.econet) {
1197
+ this.econet.reset();
1198
+ this.filestore.reset();
1199
+ }
1200
+ }
1201
+
1202
+ updateKeyLayout() {
1203
+ this.sysvia.setKeyLayout(this.config.keyLayout);
1204
+ }
1205
+
1206
+ polltimeAddr(cycles, addr) {
1207
+ cycles = cycles | 0;
1208
+ if (is1MHzAccess(addr)) {
1209
+ cycles += 1 + ((cycles ^ this.currentCycles) & 1);
1210
+ }
1211
+ this.polltime(cycles);
1212
+ }
1213
+
1214
+ // Common between polltimeSlow and polltimeFast
1215
+ polltimeCommon(cycles) {
1216
+ this.scheduler.polltime(cycles);
1217
+ this.tube.execute(cycles);
1218
+ if (this.teletextAdaptor) this.teletextAdaptor.polltime(cycles);
1219
+ if (this.music5000) this.music5000.polltime(cycles);
1220
+ if (this.econet) {
1221
+ const donmi = this.econet.polltime(cycles);
1222
+ if (donmi && this.econet.econetNMIEnabled) {
1223
+ this.NMI(true);
1224
+ }
1225
+ this.filestore.polltime(cycles);
1226
+ }
1227
+ }
1228
+
1229
+ // Slow version allows video batching and cpu multipliers
1230
+ polltimeSlow(cycles) {
1231
+ cycles |= 0;
1232
+ this.currentCycles += cycles;
1233
+ this.peripheralCycles += cycles;
1234
+ this.videoCycles += cycles;
1235
+ cycles = (this.videoCycles / this.cpuMultiplier) | 0;
1236
+ if (cycles > this.videoCyclesBatch) {
1237
+ this.video.polltime(cycles);
1238
+ this.videoCycles -= (cycles * this.cpuMultiplier) | 0;
1239
+ }
1240
+ cycles = (this.peripheralCycles / this.cpuMultiplier) | 0;
1241
+ if (!cycles) return;
1242
+ this.peripheralCycles -= (cycles * this.cpuMultiplier) | 0;
1243
+ this.polltimeCommon(cycles);
1244
+ }
1245
+
1246
+ // Faster, but more limited version
1247
+ polltimeFast(cycles) {
1248
+ cycles |= 0;
1249
+ this.currentCycles += cycles;
1250
+ this.video.polltime(cycles);
1251
+ this.polltimeCommon(cycles);
1252
+ }
1253
+
1254
+ execute(numCyclesToRun) {
1255
+ this.halted = false;
1256
+ this.targetCycles += numCyclesToRun;
1257
+ // To prevent issues with wrapping around / overflowing the accuracy that poxy Javascript numbers have,
1258
+ // find the smaller of the target and current cycles, and if that's over one second's worth; subtract
1259
+ // that from both, to keep the domain low (while accumulating seconds). Take care to preserve the bottom
1260
+ // bit though; as that encodes whether we're on an even or odd bus cycle.
1261
+ const smaller = Math.min(this.targetCycles, this.currentCycles) & 0xfffffffe;
1262
+ if (smaller >= 2 * 1000 * 1000) {
1263
+ this.targetCycles -= 2 * 1000 * 1000;
1264
+ this.currentCycles -= 2 * 1000 * 1000;
1265
+ this.cycleSeconds++;
1266
+ }
1267
+ // Any tracing or debugging means we need to run the potentially slower version: the debug read or
1268
+ // debug write might change tracing or other debugging while we're running.
1269
+ if (this.forceTracing || this._debugInstruction || this._debugRead || this._debugWrite) {
1270
+ return this.executeInternal();
1271
+ } else {
1272
+ return this.executeInternalFast();
1273
+ }
1274
+ }
1275
+
1276
+ executeInternal() {
1277
+ let first = true;
1278
+ while (!this.halted && this.currentCycles < this.targetCycles) {
1279
+ this.oldPcIndex = (this.oldPcIndex + 1) & 0xff;
1280
+ this.oldPcArray[this.oldPcIndex] = this.pc;
1281
+ this.memStatOffset = this.memStatOffsetByIFetchBank & (1 << (this.pc >>> 12)) ? 256 : 0;
1282
+ const opcode = this.readmem(this.pc);
1283
+ if (this._debugInstruction && !first && this._debugInstruction(this.pc, opcode)) {
1284
+ return false;
1285
+ }
1286
+ first = false;
1287
+ this.incpc();
1288
+ this.runner.run(opcode);
1289
+ this.oldAArray[this.oldPcIndex] = this.a;
1290
+ this.oldXArray[this.oldPcIndex] = this.x;
1291
+ this.oldYArray[this.oldPcIndex] = this.y;
1292
+ if (this.takeInt) this.brk(true);
1293
+ if (!this.resetLine) this.reset(false);
1294
+ }
1295
+ return !this.halted;
1296
+ }
1297
+
1298
+ executeInternalFast() {
1299
+ while (!this.halted && this.currentCycles < this.targetCycles) {
1300
+ this.memStatOffset = this.memStatOffsetByIFetchBank & (1 << (this.pc >>> 12)) ? 256 : 0;
1301
+ const opcode = this.readmem(this.pc);
1302
+ this.incpc();
1303
+ this.runner.run(opcode);
1304
+ if (this.takeInt) this.brk(true);
1305
+ if (!this.resetLine) this.reset(false);
1306
+ }
1307
+ return !this.halted;
1308
+ }
1309
+
1310
+ stop() {
1311
+ this.halted = true;
1312
+ }
1313
+
1314
+ dumpTrace(maxToShow, func) {
1315
+ if (!maxToShow) maxToShow = 256;
1316
+ if (maxToShow > 256) maxToShow = 256;
1317
+ const disassembler = this.disassembler;
1318
+ func =
1319
+ func ||
1320
+ function (pc, a, x, y) {
1321
+ const dis = disassembler.disassemble(pc, true)[0];
1322
+ console.log(
1323
+ utils.hexword(pc),
1324
+ (dis + " ").substring(0, 15),
1325
+ utils.hexbyte(a),
1326
+ utils.hexbyte(x),
1327
+ utils.hexbyte(y),
1328
+ );
1329
+ };
1330
+ for (let i = maxToShow - 2; i >= 0; --i) {
1331
+ const j = (this.oldPcIndex - i) & 255;
1332
+ func(this.oldPcArray[j], this.oldAArray[j], this.oldXArray[j], this.oldYArray[j]);
1333
+ }
1334
+ func(this.pc, this.a, this.x, this.y);
1335
+ }
1336
+
1337
+ async initialise() {
1338
+ if (this.model.os.length) {
1339
+ await this.loadOs.apply(this, this.model.os);
1340
+ }
1341
+ if (this.model.tube) {
1342
+ await this.tube.loadOs();
1343
+ }
1344
+ this.reset(true);
1345
+ this.debugger.setCpu(this);
1346
+ }
1347
+ }