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.
- package/.editorconfig +15 -0
- package/.git-blame-ignore-revs +3 -0
- package/.github/copilot-instructions.md +94 -0
- package/.github/workflows/claude-issue-triage.yml +105 -0
- package/.github/workflows/claude.yml +63 -0
- package/.github/workflows/release-please.yml +75 -0
- package/.github/workflows/test-and-deploy.yml +86 -0
- package/.gitmodules +6 -0
- package/.husky/pre-commit +1 -0
- package/.idea/codeStyleSettings.xml +9 -0
- package/.idea/codeStyles/Project.xml +62 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/compiler.xml +22 -0
- package/.idea/copyright/profiles_settings.xml +3 -0
- package/.idea/encodings.xml +6 -0
- package/.idea/inspectionProfiles/Project_Default.xml +7 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/jsLinters/jshint.xml +85 -0
- package/.idea/jsLinters/jslint.xml +15 -0
- package/.idea/jsbeeb.iml +11 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/prettier.xml +7 -0
- package/.idea/runConfigurations/Debug.xml +5 -0
- package/.idea/scopes/scope_settings.xml +5 -0
- package/.idea/vcs.xml +8 -0
- package/.prettierignore +4 -0
- package/.prettierrc.json +1 -0
- package/.release-please-manifest.json +3 -0
- package/.vscode/launch.json +14 -0
- package/.vscode/settings.json +6 -0
- package/CHANGELOG.md +32 -0
- package/CLAUDE.md +136 -0
- package/COPYING +674 -0
- package/Dockerfile +22 -0
- package/Makefile +30 -0
- package/README.md +259 -0
- package/docker/nginx-default.conf +10 -0
- package/docs/pal-comb-filter-research.md +129 -0
- package/docs/pal-simulation-design.md +368 -0
- package/eslint.config.js +35 -0
- package/index.html +954 -0
- package/jsconfig.json +10 -0
- package/package.json +102 -0
- package/public/discs/README.Irq-Timing +3 -0
- package/public/discs/README.bcdtest +5 -0
- package/public/discs/README.elite +6 -0
- package/public/discs/README.eng_test +3 -0
- package/public/discs/README.protection +7 -0
- package/public/favicon.ico +0 -0
- package/public/images/botbar.png +0 -0
- package/public/images/cub-monitor.png +0 -0
- package/public/images/jsbeeb-example.png +0 -0
- package/public/images/placeholder.png +0 -0
- package/public/images/red-off-16.png +0 -0
- package/public/images/red-on-16.png +0 -0
- package/public/images/sb/CD-left.jpg +0 -0
- package/public/images/sb/CD-right.jpg +0 -0
- package/public/images/sidebar.png +0 -0
- package/public/images/tv.png +0 -0
- package/public/images/yellow-off-16.png +0 -0
- package/public/images/yellow-on-16.png +0 -0
- package/public/jsbeeb-icon.png +0 -0
- package/public/robots.txt +3 -0
- package/public/roms/ADFS1-53.rom +0 -0
- package/public/roms/BASIC.ROM +0 -0
- package/public/roms/README +4 -0
- package/public/roms/a01/BASIC1.rom +0 -0
- package/public/roms/ample.rom +0 -0
- package/public/roms/ats-3.0.rom +0 -0
- package/public/roms/b/DFS-0.9.rom +0 -0
- package/public/roms/b/DFS-1.2.rom +0 -0
- package/public/roms/b1770/dfs1770.rom +0 -0
- package/public/roms/b1770/zADFS.ROM +0 -0
- package/public/roms/bp/dfs.rom +0 -0
- package/public/roms/bp/zADFS.ROM +0 -0
- package/public/roms/bpos.rom +0 -0
- package/public/roms/compact/adfs210.rom +0 -0
- package/public/roms/compact/basic48.rom +0 -0
- package/public/roms/compact/basic486.rom +0 -0
- package/public/roms/compact/os51.rom +0 -0
- package/public/roms/compact/utils.rom +0 -0
- package/public/roms/deos.rom +0 -0
- package/public/roms/master/anfs-4.25.rom +0 -0
- package/public/roms/master/mos.txt +68819 -0
- package/public/roms/master/mos3.20 +0 -0
- package/public/roms/os.rom +0 -0
- package/public/roms/os01.rom +0 -0
- package/public/roms/tube/6502Tube.rom +0 -0
- package/public/roms/tube/ARMeval_100.rom +0 -0
- package/public/roms/tube/BIOS.ROM +0 -0
- package/public/roms/tube/ReCo6502ROM_816 +0 -0
- package/public/roms/tube/Z80_120.rom +0 -0
- package/public/roms/us/USBASIC.rom +0 -0
- package/public/roms/us/USDNFS.rom +0 -0
- package/public/roms/usmos.rom +0 -0
- package/public/sounds/disc525/motor.wav +0 -0
- package/public/sounds/disc525/motoroff.wav +0 -0
- package/public/sounds/disc525/motoron.wav +0 -0
- package/public/sounds/disc525/seek.wav +0 -0
- package/public/sounds/disc525/seek2.wav +0 -0
- package/public/sounds/disc525/seek3.wav +0 -0
- package/public/sounds/disc525/step.wav +0 -0
- package/public/teletext/txt0.dat +0 -0
- package/public/teletext/txt1.dat +0 -0
- package/public/teletext/txt2.dat +0 -0
- package/public/teletext/txt3.dat +0 -0
- package/release-please-config.json +13 -0
- package/run-container.sh +92 -0
- package/src/6502.js +1347 -0
- package/src/6502.opcodes.js +1411 -0
- package/src/acia.js +261 -0
- package/src/adc.js +149 -0
- package/src/analogue-source.js +21 -0
- package/src/app/app.js +175 -0
- package/src/app/electron.js +20 -0
- package/src/app/preload.js +8 -0
- package/src/app-bench.js +33 -0
- package/src/basic/multiline-tetris +9 -0
- package/src/basic-tokenise.js +104 -0
- package/src/canvas.js +177 -0
- package/src/cmos.js +141 -0
- package/src/config.js +165 -0
- package/src/ddnoise.js +138 -0
- package/src/disc-drive.js +371 -0
- package/src/disc-hfe.js +396 -0
- package/src/disc.js +997 -0
- package/src/econet/L3FS.dat +0 -0
- package/src/econet/scsi.dat +0 -0
- package/src/econet.js +714 -0
- package/src/fake6502.js +32 -0
- package/src/fdc.js +248 -0
- package/src/filestore.js +666 -0
- package/src/gamepad-source.js +59 -0
- package/src/gamepads.js +268 -0
- package/src/google-drive.js +160 -0
- package/src/intel-fdc.js +1717 -0
- package/src/jsbeeb.css +363 -0
- package/src/keyboard.js +411 -0
- package/src/lib/README +1 -0
- package/src/lib/webgl-debug.js +911 -0
- package/src/main.js +1759 -0
- package/src/microphone-input.js +149 -0
- package/src/models.js +200 -0
- package/src/mouse-joystick-source.js +107 -0
- package/src/music5000-worklet.js +43 -0
- package/src/music5000.js +207 -0
- package/src/scheduler.js +148 -0
- package/src/serial.js +31 -0
- package/src/soundchip.js +314 -0
- package/src/sth.js +59 -0
- package/src/tapes.js +265 -0
- package/src/teletext.js +348 -0
- package/src/teletext_adaptor.js +172 -0
- package/src/teletext_data.js +1064 -0
- package/src/touchscreen.js +86 -0
- package/src/tube.js +349 -0
- package/src/url-params.js +256 -0
- package/src/utils.js +1090 -0
- package/src/via.js +702 -0
- package/src/video-filters/pal-composite.js +94 -0
- package/src/video-filters/passthrough-filter.js +70 -0
- package/src/video-filters/shaders/pal-composite.frag.glsl +147 -0
- package/src/video-filters/shaders/pal-composite.vert.glsl +8 -0
- package/src/video-filters/shaders/passthrough.frag.glsl +6 -0
- package/src/video-filters/shaders/passthrough.vert.glsl +7 -0
- package/src/video.js +794 -0
- package/src/wd-fdc.js +1344 -0
- package/src/web/audio-handler.js +146 -0
- package/src/web/audio-renderer.js +115 -0
- package/src/web/debug.js +529 -0
- package/tests/integration/RmwX.asm +47 -0
- package/tests/integration/TestInstructionsSource +27 -0
- package/tests/integration/TestTimingsResults +27 -0
- package/tests/integration/TestTimingsSource +61 -0
- package/tests/integration/bcd.js +23 -0
- package/tests/integration/dormann.js +101 -0
- package/tests/integration/dp111timing.js +42 -0
- package/tests/integration/ensure-submodules.js +25 -0
- package/tests/integration/nops.bas +119 -0
- package/tests/integration/nops.js +24 -0
- package/tests/integration/protection.js +26 -0
- package/tests/integration/rmw.js +69 -0
- package/tests/integration/teletext/expected_bug_469.png +0 -0
- package/tests/integration/teletext/expected_flash_0.png +0 -0
- package/tests/integration/teletext/expected_flash_1.png +0 -0
- package/tests/integration/teletext/expected_hoglet_held_char.png +0 -0
- package/tests/integration/teletext/expected_reveal_flash_0.png +0 -0
- package/tests/integration/teletext/expected_reveal_flash_1.png +0 -0
- package/tests/integration/teletext.js +126 -0
- package/tests/integration/timings.js +56 -0
- package/tests/integration/via.js +1125 -0
- package/tests/suite/README.md +7 -0
- package/tests/suite/Test Suite 2.15.txt +373 -0
- package/tests/suite/bin/ start +0 -0
- package/tests/suite/bin/adca +0 -0
- package/tests/suite/bin/adcax +0 -0
- package/tests/suite/bin/adcay +0 -0
- package/tests/suite/bin/adcb +0 -0
- package/tests/suite/bin/adcix +0 -0
- package/tests/suite/bin/adciy +0 -0
- package/tests/suite/bin/adcz +0 -0
- package/tests/suite/bin/adczx +0 -0
- package/tests/suite/bin/alrb +0 -0
- package/tests/suite/bin/ancb +0 -0
- package/tests/suite/bin/anda +0 -0
- package/tests/suite/bin/andax +0 -0
- package/tests/suite/bin/anday +0 -0
- package/tests/suite/bin/andb +0 -0
- package/tests/suite/bin/andix +0 -0
- package/tests/suite/bin/andiy +0 -0
- package/tests/suite/bin/andz +0 -0
- package/tests/suite/bin/andzx +0 -0
- package/tests/suite/bin/aneb +0 -0
- package/tests/suite/bin/arrb +0 -0
- package/tests/suite/bin/asla +0 -0
- package/tests/suite/bin/aslax +0 -0
- package/tests/suite/bin/asln +0 -0
- package/tests/suite/bin/aslz +0 -0
- package/tests/suite/bin/aslzx +0 -0
- package/tests/suite/bin/asoa +0 -0
- package/tests/suite/bin/asoax +0 -0
- package/tests/suite/bin/asoay +0 -0
- package/tests/suite/bin/asoix +0 -0
- package/tests/suite/bin/asoiy +0 -0
- package/tests/suite/bin/asoz +0 -0
- package/tests/suite/bin/asozx +0 -0
- package/tests/suite/bin/axsa +0 -0
- package/tests/suite/bin/axsix +0 -0
- package/tests/suite/bin/axsz +0 -0
- package/tests/suite/bin/axszy +0 -0
- package/tests/suite/bin/bccr +0 -0
- package/tests/suite/bin/bcsr +0 -0
- package/tests/suite/bin/beqr +0 -0
- package/tests/suite/bin/bita +0 -0
- package/tests/suite/bin/bitz +0 -0
- package/tests/suite/bin/bmir +0 -0
- package/tests/suite/bin/bner +0 -0
- package/tests/suite/bin/bplr +0 -0
- package/tests/suite/bin/branchwrap +0 -0
- package/tests/suite/bin/brkn +0 -0
- package/tests/suite/bin/bvcr +0 -0
- package/tests/suite/bin/bvsr +0 -0
- package/tests/suite/bin/cia1pb6 +0 -0
- package/tests/suite/bin/cia1pb7 +0 -0
- package/tests/suite/bin/cia1ta +0 -0
- package/tests/suite/bin/cia1tab +0 -0
- package/tests/suite/bin/cia1tb +0 -0
- package/tests/suite/bin/cia1tb123 +0 -0
- package/tests/suite/bin/cia2pb6 +0 -0
- package/tests/suite/bin/cia2pb7 +0 -0
- package/tests/suite/bin/cia2ta +0 -0
- package/tests/suite/bin/cia2tb +0 -0
- package/tests/suite/bin/cia2tb123 +0 -0
- package/tests/suite/bin/clcn +0 -0
- package/tests/suite/bin/cldn +0 -0
- package/tests/suite/bin/clin +0 -0
- package/tests/suite/bin/clvn +0 -0
- package/tests/suite/bin/cmpa +0 -0
- package/tests/suite/bin/cmpax +0 -0
- package/tests/suite/bin/cmpay +0 -0
- package/tests/suite/bin/cmpb +0 -0
- package/tests/suite/bin/cmpix +0 -0
- package/tests/suite/bin/cmpiy +0 -0
- package/tests/suite/bin/cmpz +0 -0
- package/tests/suite/bin/cmpzx +0 -0
- package/tests/suite/bin/cntdef +0 -0
- package/tests/suite/bin/cnto2 +0 -0
- package/tests/suite/bin/cpuport +0 -0
- package/tests/suite/bin/cputiming +0 -0
- package/tests/suite/bin/cpxa +0 -0
- package/tests/suite/bin/cpxb +0 -0
- package/tests/suite/bin/cpxz +0 -0
- package/tests/suite/bin/cpya +0 -0
- package/tests/suite/bin/cpyb +0 -0
- package/tests/suite/bin/cpyz +0 -0
- package/tests/suite/bin/dcma +0 -0
- package/tests/suite/bin/dcmax +0 -0
- package/tests/suite/bin/dcmay +0 -0
- package/tests/suite/bin/dcmix +0 -0
- package/tests/suite/bin/dcmiy +0 -0
- package/tests/suite/bin/dcmz +0 -0
- package/tests/suite/bin/dcmzx +0 -0
- package/tests/suite/bin/deca +0 -0
- package/tests/suite/bin/decax +0 -0
- package/tests/suite/bin/decz +0 -0
- package/tests/suite/bin/deczx +0 -0
- package/tests/suite/bin/dexn +0 -0
- package/tests/suite/bin/deyn +0 -0
- package/tests/suite/bin/eora +0 -0
- package/tests/suite/bin/eorax +0 -0
- package/tests/suite/bin/eoray +0 -0
- package/tests/suite/bin/eorb +0 -0
- package/tests/suite/bin/eorix +0 -0
- package/tests/suite/bin/eoriy +0 -0
- package/tests/suite/bin/eorz +0 -0
- package/tests/suite/bin/eorzx +0 -0
- package/tests/suite/bin/finish +0 -0
- package/tests/suite/bin/flipos +0 -0
- package/tests/suite/bin/icr01 +0 -0
- package/tests/suite/bin/imr +0 -0
- package/tests/suite/bin/inca +0 -0
- package/tests/suite/bin/incax +0 -0
- package/tests/suite/bin/incz +0 -0
- package/tests/suite/bin/inczx +0 -0
- package/tests/suite/bin/insa +0 -0
- package/tests/suite/bin/insax +0 -0
- package/tests/suite/bin/insay +0 -0
- package/tests/suite/bin/insix +0 -0
- package/tests/suite/bin/insiy +0 -0
- package/tests/suite/bin/insz +0 -0
- package/tests/suite/bin/inszx +0 -0
- package/tests/suite/bin/inxn +0 -0
- package/tests/suite/bin/inyn +0 -0
- package/tests/suite/bin/irq +0 -0
- package/tests/suite/bin/jmpi +0 -0
- package/tests/suite/bin/jmpw +0 -0
- package/tests/suite/bin/jsrw +0 -0
- package/tests/suite/bin/lasay +0 -0
- package/tests/suite/bin/laxa +0 -0
- package/tests/suite/bin/laxay +0 -0
- package/tests/suite/bin/laxix +0 -0
- package/tests/suite/bin/laxiy +0 -0
- package/tests/suite/bin/laxz +0 -0
- package/tests/suite/bin/laxzy +0 -0
- package/tests/suite/bin/ldaa +0 -0
- package/tests/suite/bin/ldaax +0 -0
- package/tests/suite/bin/ldaay +0 -0
- package/tests/suite/bin/ldab +0 -0
- package/tests/suite/bin/ldaix +0 -0
- package/tests/suite/bin/ldaiy +0 -0
- package/tests/suite/bin/ldaz +0 -0
- package/tests/suite/bin/ldazx +0 -0
- package/tests/suite/bin/ldxa +0 -0
- package/tests/suite/bin/ldxay +0 -0
- package/tests/suite/bin/ldxb +0 -0
- package/tests/suite/bin/ldxz +0 -0
- package/tests/suite/bin/ldxzy +0 -0
- package/tests/suite/bin/ldya +0 -0
- package/tests/suite/bin/ldyax +0 -0
- package/tests/suite/bin/ldyb +0 -0
- package/tests/suite/bin/ldyz +0 -0
- package/tests/suite/bin/ldyzx +0 -0
- package/tests/suite/bin/loadth +0 -0
- package/tests/suite/bin/lsea +0 -0
- package/tests/suite/bin/lseax +0 -0
- package/tests/suite/bin/lseay +0 -0
- package/tests/suite/bin/lseix +0 -0
- package/tests/suite/bin/lseiy +0 -0
- package/tests/suite/bin/lsez +0 -0
- package/tests/suite/bin/lsezx +0 -0
- package/tests/suite/bin/lsra +0 -0
- package/tests/suite/bin/lsrax +0 -0
- package/tests/suite/bin/lsrn +0 -0
- package/tests/suite/bin/lsrz +0 -0
- package/tests/suite/bin/lsrzx +0 -0
- package/tests/suite/bin/lxab +0 -0
- package/tests/suite/bin/mmu +0 -0
- package/tests/suite/bin/mmufetch +0 -0
- package/tests/suite/bin/nmi +0 -0
- package/tests/suite/bin/nopa +0 -0
- package/tests/suite/bin/nopax +0 -0
- package/tests/suite/bin/nopb +0 -0
- package/tests/suite/bin/nopn +0 -0
- package/tests/suite/bin/nopz +0 -0
- package/tests/suite/bin/nopzx +0 -0
- package/tests/suite/bin/oneshot +0 -0
- package/tests/suite/bin/oraa +0 -0
- package/tests/suite/bin/oraax +0 -0
- package/tests/suite/bin/oraay +0 -0
- package/tests/suite/bin/orab +0 -0
- package/tests/suite/bin/oraix +0 -0
- package/tests/suite/bin/oraiy +0 -0
- package/tests/suite/bin/oraz +0 -0
- package/tests/suite/bin/orazx +0 -0
- package/tests/suite/bin/phan +0 -0
- package/tests/suite/bin/phpn +0 -0
- package/tests/suite/bin/plan +0 -0
- package/tests/suite/bin/plpn +0 -0
- package/tests/suite/bin/rlaa +0 -0
- package/tests/suite/bin/rlaax +0 -0
- package/tests/suite/bin/rlaay +0 -0
- package/tests/suite/bin/rlaix +0 -0
- package/tests/suite/bin/rlaiy +0 -0
- package/tests/suite/bin/rlaz +0 -0
- package/tests/suite/bin/rlazx +0 -0
- package/tests/suite/bin/rola +0 -0
- package/tests/suite/bin/rolax +0 -0
- package/tests/suite/bin/roln +0 -0
- package/tests/suite/bin/rolz +0 -0
- package/tests/suite/bin/rolzx +0 -0
- package/tests/suite/bin/rora +0 -0
- package/tests/suite/bin/rorax +0 -0
- package/tests/suite/bin/rorn +0 -0
- package/tests/suite/bin/rorz +0 -0
- package/tests/suite/bin/rorzx +0 -0
- package/tests/suite/bin/rraa +0 -0
- package/tests/suite/bin/rraax +0 -0
- package/tests/suite/bin/rraay +0 -0
- package/tests/suite/bin/rraix +0 -0
- package/tests/suite/bin/rraiy +0 -0
- package/tests/suite/bin/rraz +0 -0
- package/tests/suite/bin/rrazx +0 -0
- package/tests/suite/bin/rtin +0 -0
- package/tests/suite/bin/rtsn +0 -0
- package/tests/suite/bin/sbca +0 -0
- package/tests/suite/bin/sbcax +0 -0
- package/tests/suite/bin/sbcay +0 -0
- package/tests/suite/bin/sbcb +0 -0
- package/tests/suite/bin/sbcb(eb) +0 -0
- package/tests/suite/bin/sbcix +0 -0
- package/tests/suite/bin/sbciy +0 -0
- package/tests/suite/bin/sbcz +0 -0
- package/tests/suite/bin/sbczx +0 -0
- package/tests/suite/bin/sbxb +0 -0
- package/tests/suite/bin/secn +0 -0
- package/tests/suite/bin/sedn +0 -0
- package/tests/suite/bin/sein +0 -0
- package/tests/suite/bin/shaay +0 -0
- package/tests/suite/bin/shaiy +0 -0
- package/tests/suite/bin/shsay +0 -0
- package/tests/suite/bin/shxay +0 -0
- package/tests/suite/bin/shyax +0 -0
- package/tests/suite/bin/staa +0 -0
- package/tests/suite/bin/staax +0 -0
- package/tests/suite/bin/staay +0 -0
- package/tests/suite/bin/staix +0 -0
- package/tests/suite/bin/staiy +0 -0
- package/tests/suite/bin/staz +0 -0
- package/tests/suite/bin/stazx +0 -0
- package/tests/suite/bin/stxa +0 -0
- package/tests/suite/bin/stxz +0 -0
- package/tests/suite/bin/stxzy +0 -0
- package/tests/suite/bin/stya +0 -0
- package/tests/suite/bin/styz +0 -0
- package/tests/suite/bin/styzx +0 -0
- package/tests/suite/bin/taxn +0 -0
- package/tests/suite/bin/tayn +0 -0
- package/tests/suite/bin/trap1 +0 -0
- package/tests/suite/bin/trap10 +0 -0
- package/tests/suite/bin/trap11 +0 -0
- package/tests/suite/bin/trap12 +0 -0
- package/tests/suite/bin/trap13 +0 -0
- package/tests/suite/bin/trap14 +0 -0
- package/tests/suite/bin/trap15 +0 -0
- package/tests/suite/bin/trap16 +0 -0
- package/tests/suite/bin/trap17 +0 -0
- package/tests/suite/bin/trap2 +0 -0
- package/tests/suite/bin/trap3 +0 -0
- package/tests/suite/bin/trap4 +0 -0
- package/tests/suite/bin/trap5 +0 -0
- package/tests/suite/bin/trap6 +0 -0
- package/tests/suite/bin/trap7 +0 -0
- package/tests/suite/bin/trap8 +0 -0
- package/tests/suite/bin/trap9 +0 -0
- package/tests/suite/bin/tsxn +0 -0
- package/tests/suite/bin/txan +0 -0
- package/tests/suite/bin/txsn +0 -0
- package/tests/suite/bin/tyan +0 -0
- package/tests/suite/cbm-hackers-post.html +178 -0
- package/tests/suite/cbm-hackers-post.md +78 -0
- package/tests/test-machine.js +288 -0
- package/tests/test-suite.js +147 -0
- package/tests/test.css +7 -0
- package/tests/unit/gzip/test-1 +0 -0
- package/tests/unit/gzip/test-1.gz +0 -0
- package/tests/unit/gzip/test-2 +0 -0
- package/tests/unit/gzip/test-2.gz +0 -0
- package/tests/unit/gzip/test-3 +0 -0
- package/tests/unit/gzip/test-3.gz +0 -0
- package/tests/unit/gzip/test-4 +0 -0
- package/tests/unit/gzip/test-4.gz +0 -0
- package/tests/unit/test-adc.js +307 -0
- package/tests/unit/test-bcd.js +30 -0
- package/tests/unit/test-cmos.js +266 -0
- package/tests/unit/test-disc-drive.js +85 -0
- package/tests/unit/test-disc-hfe.js +347 -0
- package/tests/unit/test-disc.js +232 -0
- package/tests/unit/test-fifo.js +35 -0
- package/tests/unit/test-gamepad-source.js +67 -0
- package/tests/unit/test-gzip.js +22 -0
- package/tests/unit/test-intel-fdc.js +93 -0
- package/tests/unit/test-keyboard.js +410 -0
- package/tests/unit/test-mouse-joystick-source.js +128 -0
- package/tests/unit/test-scheduler.js +190 -0
- package/tests/unit/test-serial.js +154 -0
- package/tests/unit/test-teletext-adaptor.js +359 -0
- package/tests/unit/test-tokenise.js +65 -0
- package/tests/unit/test-url-params.js +398 -0
- package/tests/unit/test-utils.js +276 -0
- package/tests/unit/test-video.js +498 -0
- package/tests/unit/test-zip.js +56 -0
- package/tests/unit/zip/test-mixed.zip +0 -0
- package/tests/unit/zip/test-rom.zip +0 -0
- package/tests/unit/zip/test-ssd.zip +0 -0
- package/tools/fir-generator.js +80 -0
- package/tools/vite-plugin-fir-shader.js +131 -0
- 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
|
+
});
|