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,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// PAL Composite Video Filter - Approach D: Baseband Chroma Blending
|
|
4
|
+
//
|
|
5
|
+
// Simulates PAL composite video artifacts by encoding the framebuffer to a
|
|
6
|
+
// composite signal and decoding it back to RGB, mimicking the behavior of
|
|
7
|
+
// connecting a BBC Micro to a PAL television via composite cable.
|
|
8
|
+
//
|
|
9
|
+
// REFERENCES:
|
|
10
|
+
// - John Watkinson's "Engineer's Guide to Decoding & Encoding" (Section 3.4)
|
|
11
|
+
// - https://www.jim-easterbrook.me.uk/pal/ - Jim Easterbrook's PAL decoder research
|
|
12
|
+
// - docs/pal-simulation-design.md - Full implementation details and alternatives tried
|
|
13
|
+
// - docs/pal-comb-filter-research.md - Research on authentic PAL TV implementations
|
|
14
|
+
|
|
15
|
+
import VERT_SHADER from "./shaders/pal-composite.vert.glsl?raw";
|
|
16
|
+
import FRAG_SHADER from "./shaders/pal-composite.frag.glsl?raw";
|
|
17
|
+
|
|
18
|
+
export class PALCompositeFilter {
|
|
19
|
+
static requiresGl() {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static getDisplayConfig() {
|
|
24
|
+
return {
|
|
25
|
+
name: "PAL TV",
|
|
26
|
+
image: "images/tv.png",
|
|
27
|
+
imageAlt: "A SolaVox television",
|
|
28
|
+
imageWidth: 1000,
|
|
29
|
+
imageHeight: 719,
|
|
30
|
+
canvasLeft: 50,
|
|
31
|
+
canvasTop: 70,
|
|
32
|
+
visibleWidth: 800,
|
|
33
|
+
visibleHeight: 600,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor(gl) {
|
|
38
|
+
this.gl = gl;
|
|
39
|
+
this.program = null;
|
|
40
|
+
this.locations = {};
|
|
41
|
+
|
|
42
|
+
this._init();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_init() {
|
|
46
|
+
const gl = this.gl;
|
|
47
|
+
|
|
48
|
+
// Compile shaders
|
|
49
|
+
const vertShader = this._compileShader(gl.VERTEX_SHADER, VERT_SHADER);
|
|
50
|
+
const fragShader = this._compileShader(gl.FRAGMENT_SHADER, FRAG_SHADER);
|
|
51
|
+
|
|
52
|
+
// Link program
|
|
53
|
+
this.program = gl.createProgram();
|
|
54
|
+
gl.attachShader(this.program, vertShader);
|
|
55
|
+
gl.attachShader(this.program, fragShader);
|
|
56
|
+
gl.linkProgram(this.program);
|
|
57
|
+
|
|
58
|
+
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
|
|
59
|
+
const info = gl.getProgramInfoLog(this.program);
|
|
60
|
+
throw new Error("Failed to link PAL shader program: " + info);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Get uniform locations
|
|
64
|
+
this.locations.uFramebuffer = gl.getUniformLocation(this.program, "uFramebuffer");
|
|
65
|
+
this.locations.uResolution = gl.getUniformLocation(this.program, "uResolution");
|
|
66
|
+
this.locations.uTexelSize = gl.getUniformLocation(this.program, "uTexelSize");
|
|
67
|
+
this.locations.uFrameCount = gl.getUniformLocation(this.program, "uFrameCount");
|
|
68
|
+
|
|
69
|
+
console.log("PAL composite filter initialized");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_compileShader(type, source) {
|
|
73
|
+
const gl = this.gl;
|
|
74
|
+
const shader = gl.createShader(type);
|
|
75
|
+
gl.shaderSource(shader, source);
|
|
76
|
+
gl.compileShader(shader);
|
|
77
|
+
|
|
78
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
79
|
+
const info = gl.getShaderInfoLog(shader);
|
|
80
|
+
const typeName = type === gl.VERTEX_SHADER ? "vertex" : "fragment";
|
|
81
|
+
throw new Error(`Failed to compile ${typeName} shader: ${info}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return shader;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setUniforms(params) {
|
|
88
|
+
const gl = this.gl;
|
|
89
|
+
gl.uniform1i(this.locations.uFramebuffer, 0); // Texture unit 0
|
|
90
|
+
gl.uniform2f(this.locations.uResolution, params.width, params.height);
|
|
91
|
+
gl.uniform2f(this.locations.uTexelSize, 1.0 / params.width, 1.0 / params.height);
|
|
92
|
+
gl.uniform1f(this.locations.uFrameCount, params.frameCount % 8); // 8-field temporal phase sequence
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import VERT_SHADER from "./shaders/passthrough.vert.glsl?raw";
|
|
4
|
+
import FRAG_SHADER from "./shaders/passthrough.frag.glsl?raw";
|
|
5
|
+
|
|
6
|
+
export class PassthroughFilter {
|
|
7
|
+
static requiresGl() {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static getDisplayConfig() {
|
|
12
|
+
return {
|
|
13
|
+
name: "RGB Monitor",
|
|
14
|
+
image: "images/cub-monitor.png",
|
|
15
|
+
imageAlt: "A fake CUB computer monitor",
|
|
16
|
+
imageWidth: 896,
|
|
17
|
+
imageHeight: 648,
|
|
18
|
+
canvasLeft: 0,
|
|
19
|
+
canvasTop: 8,
|
|
20
|
+
visibleWidth: 896,
|
|
21
|
+
visibleHeight: 600,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
constructor(gl) {
|
|
26
|
+
this.gl = gl;
|
|
27
|
+
this.program = null;
|
|
28
|
+
this.locations = {};
|
|
29
|
+
|
|
30
|
+
this._init();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_init() {
|
|
34
|
+
const gl = this.gl;
|
|
35
|
+
|
|
36
|
+
const vertexShader = this._compileShader(gl.VERTEX_SHADER, VERT_SHADER);
|
|
37
|
+
const fragmentShader = this._compileShader(gl.FRAGMENT_SHADER, FRAG_SHADER);
|
|
38
|
+
|
|
39
|
+
this.program = gl.createProgram();
|
|
40
|
+
gl.attachShader(this.program, vertexShader);
|
|
41
|
+
gl.attachShader(this.program, fragmentShader);
|
|
42
|
+
gl.linkProgram(this.program);
|
|
43
|
+
|
|
44
|
+
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
|
|
45
|
+
throw new Error("Failed to link passthrough shader program: " + gl.getProgramInfoLog(this.program));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.locations.tex = gl.getUniformLocation(this.program, "tex");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_compileShader(type, source) {
|
|
52
|
+
const gl = this.gl;
|
|
53
|
+
const shader = gl.createShader(type);
|
|
54
|
+
gl.shaderSource(shader, source);
|
|
55
|
+
gl.compileShader(shader);
|
|
56
|
+
|
|
57
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
58
|
+
const error = gl.getShaderInfoLog(shader);
|
|
59
|
+
gl.deleteShader(shader);
|
|
60
|
+
throw new Error("Shader compilation failed: " + error);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return shader;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setUniforms(_params) {
|
|
67
|
+
const gl = this.gl;
|
|
68
|
+
gl.uniform1i(this.locations.tex, 0);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
precision highp float;
|
|
2
|
+
|
|
3
|
+
varying vec2 vTexCoord;
|
|
4
|
+
|
|
5
|
+
uniform sampler2D uFramebuffer;
|
|
6
|
+
uniform vec2 uResolution;
|
|
7
|
+
uniform vec2 uTexelSize;
|
|
8
|
+
uniform float uFrameCount;
|
|
9
|
+
|
|
10
|
+
const float PI = 3.14159265359;
|
|
11
|
+
|
|
12
|
+
// IMPLEMENTATION (Baseband Blending Method):
|
|
13
|
+
// 1. Encode RGB to PAL composite: Y + U*sin(ωt) + V*cos(ωt)*v_switch
|
|
14
|
+
// 2. Demodulate current line (with correct phase) → U_curr, V_curr
|
|
15
|
+
// 3. Demodulate previous line (2H for interlaced, same field) → U_prev, V_prev
|
|
16
|
+
// 4. Blend at baseband: U_final = mix(U_curr, U_prev), V_final = mix(V_curr, V_prev)
|
|
17
|
+
// 5. Remodulate blended chroma back to composite frequency
|
|
18
|
+
// 6. Extract luma via complementary subtraction: Y = composite - remodulated_chroma
|
|
19
|
+
// 7. Combine luma and chroma, convert back to RGB
|
|
20
|
+
//
|
|
21
|
+
// NOTE: Uses 2H delay (line-2) not 1H (line-1) because jsbeeb simulates interlacing by
|
|
22
|
+
// rendering only odd or even lines per frame. A real PAL TV's 1H delay line would contain
|
|
23
|
+
// the previous scanline from the SAME field, which is 2 texture lines apart. Proper
|
|
24
|
+
// support for non-interlaced modes needs to be added.
|
|
25
|
+
|
|
26
|
+
// Chroma demodulation gain: compensates for sin²(x) = 0.5 - 0.5·cos(2x) amplitude loss
|
|
27
|
+
const float FIR_GAIN = 2.0;
|
|
28
|
+
|
|
29
|
+
// Chroma vertical blending weight (0.0 = no blend, 0.5 = equal blend)
|
|
30
|
+
const float CHROMA_BLEND_WEIGHT = 0.5;
|
|
31
|
+
|
|
32
|
+
// PAL standard base parameters
|
|
33
|
+
const float PAL_TOTAL_LINES = 625.0; // Total scanlines per frame
|
|
34
|
+
const float PAL_FRAME_RATE = 25.0; // Frames per second
|
|
35
|
+
const float PAL_SUBCARRIER_MHZ = 4.43361875; // PAL color subcarrier frequency (exact)
|
|
36
|
+
|
|
37
|
+
// Derived PAL parameters
|
|
38
|
+
const float PAL_LINES_PER_FIELD = PAL_TOTAL_LINES / 2.0;
|
|
39
|
+
const float PAL_CYCLES_PER_LINE = PAL_SUBCARRIER_MHZ * 1e6 / (PAL_TOTAL_LINES * PAL_FRAME_RATE);
|
|
40
|
+
const float PAL_LINE_PHASE_OFFSET = fract(PAL_CYCLES_PER_LINE);
|
|
41
|
+
const float PAL_FIELD_PHASE_OFFSET = PAL_LINE_PHASE_OFFSET * PAL_LINES_PER_FIELD;
|
|
42
|
+
|
|
43
|
+
// jsbeeb texture parameters
|
|
44
|
+
const float TEXTURE_WIDTH = 1024.0; // Framebuffer width (896 visible + 128 blanking)
|
|
45
|
+
|
|
46
|
+
// RGB → YUV conversion with proper PAL signal levels baked in
|
|
47
|
+
// Derived from ITU-R BT.470-6: white at 0.7V, peak at 0.931V
|
|
48
|
+
// Matrix ensures RGB(1,1,1) → YUV(0.7,0,0) and worst case (yellow) peaks at 0.931V
|
|
49
|
+
vec3 rgb_to_yuv(vec3 rgb) {
|
|
50
|
+
return vec3(
|
|
51
|
+
0.2093 * rgb.r + 0.4109 * rgb.g + 0.0798 * rgb.b,
|
|
52
|
+
-0.102228 * rgb.r - 0.200704 * rgb.g + 0.302939 * rgb.b,
|
|
53
|
+
0.427311 * rgb.r - 0.357823 * rgb.g - 0.069488 * rgb.b
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// YUV → RGB inverse matrix
|
|
58
|
+
vec3 yuv_to_rgb(vec3 yuv) {
|
|
59
|
+
return vec3(
|
|
60
|
+
1.42857143 * yuv.x - 0.0000193387 * yuv.y + 1.64048673 * yuv.z,
|
|
61
|
+
1.42857711 * yuv.x - 0.567986687 * yuv.y - 0.83560997 * yuv.z,
|
|
62
|
+
1.42854218 * yuv.x + 2.92468392 * yuv.y - 0.0000217418 * yuv.z
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Demodulate composite signal at given position
|
|
67
|
+
vec2 demodulate_uv(vec2 xy, float pixel_x, float offset_pixels, float v_switch, float cycles_per_pixel, float phase_offset) {
|
|
68
|
+
float t = ((pixel_x + offset_pixels) * cycles_per_pixel + phase_offset) * 2.0 * PI;
|
|
69
|
+
|
|
70
|
+
vec2 sample_uv = xy + vec2(offset_pixels * uTexelSize.x, 0.0);
|
|
71
|
+
vec3 rgb = texture2D(uFramebuffer, sample_uv).rgb;
|
|
72
|
+
vec3 yuv = rgb_to_yuv(rgb);
|
|
73
|
+
|
|
74
|
+
// Encode to composite: Y + U*sin(ωt) + V*cos(ωt)*v_switch
|
|
75
|
+
float composite = yuv.x + yuv.y * sin(t) + yuv.z * cos(t) * v_switch;
|
|
76
|
+
|
|
77
|
+
// Demodulate: multiply by carrier to shift chroma to baseband
|
|
78
|
+
return vec2(composite * sin(t), composite * cos(t) * v_switch);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void main() {
|
|
82
|
+
// Use gl_FragCoord for pixel coordinates - it's hardware-provided and avoids interpolation artifacts
|
|
83
|
+
vec2 pixelCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
|
|
84
|
+
|
|
85
|
+
// BEGIN_FIR_COEFFICIENTS
|
|
86
|
+
// This section is replaced by the Vite build to include FIR filter coefficients.
|
|
87
|
+
// Change Cutoff (in comment below) or FIRTAPS value to configure.
|
|
88
|
+
// Cutoff: 1.108 MHz (quarter subcarrier)
|
|
89
|
+
const int FIRTAPS = 21;
|
|
90
|
+
float FIR[FIRTAPS];
|
|
91
|
+
// END_FIR_COEFFICIENTS
|
|
92
|
+
|
|
93
|
+
float line = floor(pixelCoord.y);
|
|
94
|
+
|
|
95
|
+
// PAL phase alternates each scanline (V component inverts)
|
|
96
|
+
float v_switch = mod(line, 2.0) < 1.0 ? 1.0 : -1.0;
|
|
97
|
+
|
|
98
|
+
// Map PAL subcarrier across texture width
|
|
99
|
+
float cycles_per_pixel = PAL_CYCLES_PER_LINE / TEXTURE_WIDTH;
|
|
100
|
+
|
|
101
|
+
// PAL temporal phase (8-field sequence creates animated dot crawl)
|
|
102
|
+
float line_phase_offset = line * PAL_LINE_PHASE_OFFSET;
|
|
103
|
+
float frame_phase_offset = uFrameCount * PAL_FIELD_PHASE_OFFSET;
|
|
104
|
+
float phase_offset = line_phase_offset + frame_phase_offset;
|
|
105
|
+
|
|
106
|
+
// Step 1: Demodulate current line with FIR filter
|
|
107
|
+
vec2 filtered_uv_curr = vec2(0.0);
|
|
108
|
+
for (int i = 0; i < FIRTAPS; i++) {
|
|
109
|
+
float offset = float(i - (FIRTAPS - 1) / 2);
|
|
110
|
+
vec2 uv = demodulate_uv(vTexCoord, pixelCoord.x, offset, v_switch, cycles_per_pixel, phase_offset);
|
|
111
|
+
filtered_uv_curr += FIR_GAIN * uv * FIR[i];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Step 2: Demodulate previous line (2H for interlaced, same field) with FIR filter
|
|
115
|
+
// In interlaced mode, only odd OR even lines are rendered per frame.
|
|
116
|
+
// Using 2H (line-2) ensures we sample from the same field (both fresh data).
|
|
117
|
+
// This represents the TV's 1H delay within a single field.
|
|
118
|
+
vec2 prev_uv = vTexCoord - vec2(0.0, 2.0 * uTexelSize.y);
|
|
119
|
+
float prev_line = line - 2.0;
|
|
120
|
+
float prev_v_switch = v_switch * -1.0;
|
|
121
|
+
float prev_phase_offset = prev_line * PAL_LINE_PHASE_OFFSET + frame_phase_offset;
|
|
122
|
+
|
|
123
|
+
vec2 filtered_uv_prev = vec2(0.0);
|
|
124
|
+
for (int i = 0; i < FIRTAPS; i++) {
|
|
125
|
+
float offset = float(i - (FIRTAPS - 1) / 2);
|
|
126
|
+
vec2 uv = demodulate_uv(prev_uv, pixelCoord.x, offset, prev_v_switch, cycles_per_pixel, prev_phase_offset);
|
|
127
|
+
filtered_uv_prev += FIR_GAIN * uv * FIR[i];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Step 3: Blend chroma at baseband
|
|
131
|
+
vec2 filtered_uv = mix(filtered_uv_curr, filtered_uv_prev, CHROMA_BLEND_WEIGHT);
|
|
132
|
+
|
|
133
|
+
// Step 4: Get luma via complementary subtraction
|
|
134
|
+
float t_curr = (pixelCoord.x * cycles_per_pixel + phase_offset) * 2.0 * PI;
|
|
135
|
+
vec3 rgb_curr = texture2D(uFramebuffer, vTexCoord).rgb;
|
|
136
|
+
vec3 yuv_curr = rgb_to_yuv(rgb_curr);
|
|
137
|
+
float composite_curr = yuv_curr.x + yuv_curr.y * sin(t_curr) + yuv_curr.z * cos(t_curr) * v_switch;
|
|
138
|
+
|
|
139
|
+
// Remodulate blended chroma back to composite frequency
|
|
140
|
+
float remodulated_chroma = filtered_uv.x * sin(t_curr) + filtered_uv.y * cos(t_curr) * v_switch;
|
|
141
|
+
|
|
142
|
+
// Complementary subtraction: luma = composite - chroma
|
|
143
|
+
float y_out = composite_curr - remodulated_chroma;
|
|
144
|
+
|
|
145
|
+
vec3 rgb_out = yuv_to_rgb(vec3(y_out, filtered_uv.x, filtered_uv.y));
|
|
146
|
+
gl_FragColor = vec4(clamp(rgb_out, 0.0, 1.0), 1.0);
|
|
147
|
+
}
|