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,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import * as utils from "./utils.js";
|
|
3
|
+
import * as models from "./models.js";
|
|
4
|
+
import { fake6502 } from "./fake6502.js";
|
|
5
|
+
|
|
6
|
+
export async function create() {
|
|
7
|
+
const cpu = fake6502(models.basicOnly);
|
|
8
|
+
const callTokeniser = function (line) {
|
|
9
|
+
// Address of the tokenisation subroutine in the Master's BASIC ROM.
|
|
10
|
+
// With thanks to http://8bs.com/basic/basic4-8db2.htm
|
|
11
|
+
const tokeniseBASIC = 0x8db2;
|
|
12
|
+
// Address of the instruction to intercept where the tail is copied down.
|
|
13
|
+
const copyIntercept = 0x8ea1;
|
|
14
|
+
// Set the stack top to this, then execute until the CPU pops past this.
|
|
15
|
+
const stackTop = 0xf0;
|
|
16
|
+
// Address to inject the BASIC program text at.
|
|
17
|
+
const workSpace = 0x1000;
|
|
18
|
+
// Pointer to the program text.
|
|
19
|
+
const textPtrLo = 0x37;
|
|
20
|
+
const textPtrHi = 0x38;
|
|
21
|
+
|
|
22
|
+
cpu.pc = tokeniseBASIC;
|
|
23
|
+
cpu.s = stackTop;
|
|
24
|
+
let offset = workSpace;
|
|
25
|
+
// Set flags to indicate that we're at the start of a statement
|
|
26
|
+
// but have already processed the line number.
|
|
27
|
+
cpu.writemem(0x3b, 0x00);
|
|
28
|
+
cpu.writemem(0x3c, 0x00);
|
|
29
|
+
cpu.writemem(textPtrLo, offset & 0xff);
|
|
30
|
+
cpu.writemem(textPtrHi, (offset >>> 8) & 0xff);
|
|
31
|
+
// Set the paged ROM latch to page in the BASIC.
|
|
32
|
+
cpu.writemem(0xfe30, 12);
|
|
33
|
+
for (let i = 0; i < line.length; ++i) {
|
|
34
|
+
cpu.writemem(offset + i, line.charCodeAt(i));
|
|
35
|
+
}
|
|
36
|
+
cpu.writemem(offset + line.length, 0x0d);
|
|
37
|
+
let safety = 20 * 1000 * 1000;
|
|
38
|
+
let result = "";
|
|
39
|
+
while (cpu.s <= stackTop) {
|
|
40
|
+
cpu.execute(1);
|
|
41
|
+
if (--safety === 0) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
if (cpu.pc === copyIntercept) {
|
|
45
|
+
// Intercept the subroutine in the BASIC ROM which replaces a keyword
|
|
46
|
+
// with a token and copies down the tail of the line. The 6502 code
|
|
47
|
+
// uses the Y register to index the copy and fails if the untokenised
|
|
48
|
+
// tail is longer than 255 bytes. It also makes tokenisation O(n²)
|
|
49
|
+
// for a line with a lot of tokens.
|
|
50
|
+
//
|
|
51
|
+
// Instead we copy out the newly processed part and advance the pointer
|
|
52
|
+
// to the unprocessed part.
|
|
53
|
+
let to = (cpu.readmemZpStack(textPtrHi) << 8) | cpu.readmemZpStack(textPtrLo);
|
|
54
|
+
while (offset < to) {
|
|
55
|
+
result += String.fromCharCode(cpu.readmem(offset));
|
|
56
|
+
offset++;
|
|
57
|
+
}
|
|
58
|
+
result += String.fromCharCode(cpu.a);
|
|
59
|
+
offset += cpu.y;
|
|
60
|
+
cpu.writememZpStack(textPtrLo, offset & 0xff);
|
|
61
|
+
cpu.writememZpStack(textPtrHi, (offset >>> 8) & 0xff);
|
|
62
|
+
++offset;
|
|
63
|
+
// Skip over the JSR instruction.
|
|
64
|
+
cpu.pc += 3;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (let i = offset; cpu.readmem(i) !== 0x0d; ++i) {
|
|
68
|
+
result += String.fromCharCode(cpu.readmem(i));
|
|
69
|
+
}
|
|
70
|
+
if (safety === 0) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"Unable to tokenize '" + line + "' - got as far as '" + result + "' pc=" + utils.hexword(cpu.pc),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
};
|
|
77
|
+
const lineRe = /^([0-9]+)?(.*)/;
|
|
78
|
+
const tokeniseLine = function (line, lineNumIfNotSpec) {
|
|
79
|
+
const lineSplit = line.match(lineRe);
|
|
80
|
+
const lineNum = lineSplit[1] ? parseInt(lineSplit[1]) : lineNumIfNotSpec;
|
|
81
|
+
const tokens = callTokeniser(lineSplit[2]);
|
|
82
|
+
if (tokens.length > 251) {
|
|
83
|
+
throw new Error("Line " + lineNum + " tokenised length " + tokens.length + " > 251 bytes");
|
|
84
|
+
}
|
|
85
|
+
return (
|
|
86
|
+
"\r" +
|
|
87
|
+
String.fromCharCode((lineNum >>> 8) & 0xff) +
|
|
88
|
+
String.fromCharCode(lineNum & 0xff) +
|
|
89
|
+
String.fromCharCode(tokens.length + 4) +
|
|
90
|
+
tokens
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
const tokenise = function (text) {
|
|
94
|
+
let result = "";
|
|
95
|
+
text.split("\n").forEach(function (line, i) {
|
|
96
|
+
if (line) {
|
|
97
|
+
result += tokeniseLine(line, 10 + i * 10);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return result + "\r\xff";
|
|
101
|
+
};
|
|
102
|
+
await cpu.initialise();
|
|
103
|
+
return { tokenise };
|
|
104
|
+
}
|
package/src/canvas.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import webglDebug from "./lib/webgl-debug.js";
|
|
3
|
+
import { PALCompositeFilter } from "./video-filters/pal-composite.js";
|
|
4
|
+
import { PassthroughFilter } from "./video-filters/passthrough-filter.js";
|
|
5
|
+
|
|
6
|
+
const DISPLAY_MODE_FILTERS = {
|
|
7
|
+
pal: PALCompositeFilter,
|
|
8
|
+
rgb: PassthroughFilter,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function getFilterForMode(mode) {
|
|
12
|
+
return DISPLAY_MODE_FILTERS[mode] || DISPLAY_MODE_FILTERS.rgb;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class Canvas {
|
|
16
|
+
isWebGl() {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
constructor(canvas) {
|
|
21
|
+
this.ctx = canvas.getContext("2d", { alpha: false });
|
|
22
|
+
if (this.ctx === null) throw new Error("Unable to get a 2D context");
|
|
23
|
+
this.ctx.fillStyle = "black";
|
|
24
|
+
this.ctx.fillRect(0, 0, 1024, 625);
|
|
25
|
+
this.backBuffer = window.document.createElement("canvas");
|
|
26
|
+
this.backBuffer.width = 1024;
|
|
27
|
+
this.backBuffer.height = 625;
|
|
28
|
+
this.backCtx = this.backBuffer.getContext("2d", { alpha: false });
|
|
29
|
+
this.imageData = this.backCtx.createImageData(this.backBuffer.width, this.backBuffer.height);
|
|
30
|
+
this.canvasWidth = canvas.width;
|
|
31
|
+
this.canvasHeight = canvas.height;
|
|
32
|
+
|
|
33
|
+
this.fb32 = new Uint32Array(this.imageData.data.buffer);
|
|
34
|
+
}
|
|
35
|
+
paint(minx, miny, maxx, maxy, _frameCount) {
|
|
36
|
+
const width = maxx - minx;
|
|
37
|
+
const height = maxy - miny;
|
|
38
|
+
this.backCtx.putImageData(this.imageData, 0, 0, minx, miny, width, height);
|
|
39
|
+
this.ctx.drawImage(this.backBuffer, minx, miny, width, height, 0, 0, this.canvasWidth, this.canvasHeight);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const width = 1024;
|
|
44
|
+
const height = 1024;
|
|
45
|
+
export class GlCanvas {
|
|
46
|
+
isWebGl() {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
constructor(canvas, filterClass) {
|
|
51
|
+
// failIfMajorPerformanceCaveat prevents the use of CPU based WebGL
|
|
52
|
+
// rendering, which is much worse than simply using a 2D canvas for
|
|
53
|
+
// rendering.
|
|
54
|
+
const glAttrs = {
|
|
55
|
+
alpha: false,
|
|
56
|
+
antialias: false,
|
|
57
|
+
depth: false,
|
|
58
|
+
preserveDrawingBuffer: false,
|
|
59
|
+
stencil: false,
|
|
60
|
+
failIfMajorPerformanceCaveat: true,
|
|
61
|
+
};
|
|
62
|
+
const gl = canvas.getContext("webgl", glAttrs) || canvas.getContext("experimental-webgl", glAttrs);
|
|
63
|
+
this.gl = gl;
|
|
64
|
+
if (!gl) {
|
|
65
|
+
throw new Error("Unable to create a GL context");
|
|
66
|
+
}
|
|
67
|
+
const checkedGl = webglDebug.makeDebugContext(gl, function (err, funcName) {
|
|
68
|
+
throw new Error("Problem creating GL context: " + webglDebug.glEnumToString(err) + " in " + funcName);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
checkedGl.depthMask(false);
|
|
72
|
+
|
|
73
|
+
this.filter = new filterClass(checkedGl);
|
|
74
|
+
const program = this.filter.program;
|
|
75
|
+
checkedGl.useProgram(program);
|
|
76
|
+
|
|
77
|
+
this.fb8 = new Uint8Array(width * height * 4);
|
|
78
|
+
this.fb32 = new Uint32Array(this.fb8.buffer);
|
|
79
|
+
this.texture = checkedGl.createTexture();
|
|
80
|
+
checkedGl.bindTexture(checkedGl.TEXTURE_2D, this.texture);
|
|
81
|
+
checkedGl.pixelStorei(checkedGl.UNPACK_ALIGNMENT, 4);
|
|
82
|
+
checkedGl.texParameteri(checkedGl.TEXTURE_2D, checkedGl.TEXTURE_WRAP_S, checkedGl.CLAMP_TO_EDGE);
|
|
83
|
+
checkedGl.texParameteri(checkedGl.TEXTURE_2D, checkedGl.TEXTURE_WRAP_T, checkedGl.CLAMP_TO_EDGE);
|
|
84
|
+
checkedGl.texParameteri(checkedGl.TEXTURE_2D, checkedGl.TEXTURE_MAG_FILTER, checkedGl.LINEAR);
|
|
85
|
+
checkedGl.texParameteri(checkedGl.TEXTURE_2D, checkedGl.TEXTURE_MIN_FILTER, checkedGl.LINEAR);
|
|
86
|
+
checkedGl.texImage2D(
|
|
87
|
+
checkedGl.TEXTURE_2D,
|
|
88
|
+
0,
|
|
89
|
+
checkedGl.RGBA,
|
|
90
|
+
width,
|
|
91
|
+
height,
|
|
92
|
+
0,
|
|
93
|
+
checkedGl.RGBA,
|
|
94
|
+
checkedGl.UNSIGNED_BYTE,
|
|
95
|
+
this.fb8,
|
|
96
|
+
);
|
|
97
|
+
checkedGl.bindTexture(checkedGl.TEXTURE_2D, null);
|
|
98
|
+
|
|
99
|
+
const vertexPositionAttrLoc = checkedGl.getAttribLocation(program, "pos");
|
|
100
|
+
checkedGl.enableVertexAttribArray(vertexPositionAttrLoc);
|
|
101
|
+
const vertexPositionBuffer = checkedGl.createBuffer();
|
|
102
|
+
checkedGl.bindBuffer(checkedGl.ARRAY_BUFFER, vertexPositionBuffer);
|
|
103
|
+
checkedGl.bufferData(checkedGl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), checkedGl.STATIC_DRAW);
|
|
104
|
+
checkedGl.vertexAttribPointer(vertexPositionAttrLoc, 2, checkedGl.FLOAT, false, 0, 0);
|
|
105
|
+
|
|
106
|
+
const uvAttrLoc = checkedGl.getAttribLocation(program, "uvIn");
|
|
107
|
+
checkedGl.enableVertexAttribArray(uvAttrLoc);
|
|
108
|
+
this.uvBuffer = checkedGl.createBuffer();
|
|
109
|
+
checkedGl.bindBuffer(checkedGl.ARRAY_BUFFER, this.uvBuffer);
|
|
110
|
+
checkedGl.vertexAttribPointer(uvAttrLoc, 2, checkedGl.FLOAT, false, 0, 0);
|
|
111
|
+
|
|
112
|
+
checkedGl.activeTexture(gl.TEXTURE0);
|
|
113
|
+
checkedGl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
114
|
+
|
|
115
|
+
this.uvFloatArray = new Float32Array(8);
|
|
116
|
+
this.lastExtent = {};
|
|
117
|
+
|
|
118
|
+
console.log("GL Canvas set up");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
paint(minx, miny, maxx, maxy, frameCount) {
|
|
122
|
+
const gl = this.gl;
|
|
123
|
+
// We can't specify a stride for the source, so have to use the full width.
|
|
124
|
+
gl.texSubImage2D(
|
|
125
|
+
gl.TEXTURE_2D,
|
|
126
|
+
0,
|
|
127
|
+
0,
|
|
128
|
+
miny,
|
|
129
|
+
width,
|
|
130
|
+
maxy - miny,
|
|
131
|
+
gl.RGBA,
|
|
132
|
+
gl.UNSIGNED_BYTE,
|
|
133
|
+
this.fb8.subarray(miny * width * 4, maxy * width * 4),
|
|
134
|
+
);
|
|
135
|
+
const extent = { minx, miny, maxx, maxy };
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
extent.minx !== this.lastExtent.minx ||
|
|
139
|
+
extent.miny !== this.lastExtent.miny ||
|
|
140
|
+
extent.maxx !== this.lastExtent.maxx ||
|
|
141
|
+
extent.maxy !== this.lastExtent.maxy
|
|
142
|
+
) {
|
|
143
|
+
this.lastExtent = extent;
|
|
144
|
+
minx /= width;
|
|
145
|
+
maxx /= width;
|
|
146
|
+
miny /= height;
|
|
147
|
+
maxy /= height;
|
|
148
|
+
this.uvFloatArray[0] = minx;
|
|
149
|
+
this.uvFloatArray[1] = maxy;
|
|
150
|
+
this.uvFloatArray[2] = minx;
|
|
151
|
+
this.uvFloatArray[3] = miny;
|
|
152
|
+
this.uvFloatArray[4] = maxx;
|
|
153
|
+
this.uvFloatArray[5] = maxy;
|
|
154
|
+
this.uvFloatArray[6] = maxx;
|
|
155
|
+
this.uvFloatArray[7] = miny;
|
|
156
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
|
|
157
|
+
gl.bufferData(gl.ARRAY_BUFFER, this.uvFloatArray, gl.DYNAMIC_DRAW);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.filter.setUniforms({ width, height, frameCount });
|
|
161
|
+
|
|
162
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function bestCanvas(canvas, filterClass) {
|
|
167
|
+
try {
|
|
168
|
+
return new GlCanvas(canvas, filterClass);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
console.log("Unable to use OpenGL: " + e);
|
|
171
|
+
if (filterClass.requiresGl()) {
|
|
172
|
+
const config = filterClass.getDisplayConfig();
|
|
173
|
+
console.warn(`${config.name} requires WebGL. Falling back to standard 2D canvas.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return new Canvas(canvas);
|
|
177
|
+
}
|
package/src/cmos.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const defaultCmos = [
|
|
4
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xeb, 0x00,
|
|
5
|
+
0xc9, 0xff, 0xff, 0x12, 0x00, 0x17, 0xca, 0x1e, 0x05, 0x00, 0x35, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
let timeOffset = 0;
|
|
10
|
+
|
|
11
|
+
function getBbcDateTime() {
|
|
12
|
+
const result = new Date(Date.now() + timeOffset);
|
|
13
|
+
result.setMilliseconds(0);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function toBcd(value) {
|
|
18
|
+
return parseInt(value.toString(10), 16);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fromBcd(value) {
|
|
22
|
+
return parseInt(value.toString(16), 10);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class Cmos {
|
|
26
|
+
constructor(persistence, cmosOverride, econet) {
|
|
27
|
+
this.store = persistence ? persistence.load() : null;
|
|
28
|
+
this.persistence = persistence;
|
|
29
|
+
this.enabled = false;
|
|
30
|
+
this.isRead = false;
|
|
31
|
+
this.addressSelect = false;
|
|
32
|
+
this.dataSelect = false;
|
|
33
|
+
this.cmosAddr = 0;
|
|
34
|
+
|
|
35
|
+
if (!this.store) {
|
|
36
|
+
this.store = defaultCmos;
|
|
37
|
+
}
|
|
38
|
+
if (cmosOverride) {
|
|
39
|
+
this.store = cmosOverride(this.store);
|
|
40
|
+
}
|
|
41
|
+
if (econet) {
|
|
42
|
+
this.store[0xe] = econet.stationId;
|
|
43
|
+
if (this.store[0xf] === 0) {
|
|
44
|
+
// Catch an invalid FS configuration setting (possibly as a result of using a prior version that didn't set a default of 254 in CMOS)
|
|
45
|
+
this.store[0xf] = 254;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
this.save();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
save() {
|
|
52
|
+
if (this.persistence) {
|
|
53
|
+
this.persistence.save(this.store);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
read() {
|
|
58
|
+
if (!this.enabled) return 0xff;
|
|
59
|
+
// To drive the bus we need:
|
|
60
|
+
// - CMOS enabled.
|
|
61
|
+
// - Address Select low.
|
|
62
|
+
// - Data Select high.
|
|
63
|
+
// - Read high.
|
|
64
|
+
|
|
65
|
+
if (!this.addressSelect && this.dataSelect && this.isRead) {
|
|
66
|
+
// The first 10 bytes of CMOS RAM store the RTC clock
|
|
67
|
+
if (this.cmosAddr < 10) {
|
|
68
|
+
const current = getBbcDateTime();
|
|
69
|
+
switch (this.cmosAddr) {
|
|
70
|
+
case 0:
|
|
71
|
+
return toBcd(current.getSeconds());
|
|
72
|
+
case 2:
|
|
73
|
+
return toBcd(current.getMinutes());
|
|
74
|
+
case 4:
|
|
75
|
+
return toBcd(current.getHours());
|
|
76
|
+
case 6:
|
|
77
|
+
return toBcd(current.getDay() + 1);
|
|
78
|
+
case 7:
|
|
79
|
+
return toBcd(current.getDate());
|
|
80
|
+
case 8:
|
|
81
|
+
return toBcd(current.getMonth() + 1);
|
|
82
|
+
case 9:
|
|
83
|
+
return toBcd(current.getFullYear());
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
return this.store[this.cmosAddr] & 0xff;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return 0xff;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
writeControl(portBpins, portApins, IC32) {
|
|
93
|
+
this.enabled = !!(portBpins & 0x40);
|
|
94
|
+
if (!this.enabled) return;
|
|
95
|
+
const oldDataSelect = this.dataSelect;
|
|
96
|
+
const oldAddressSelect = this.addressSelect;
|
|
97
|
+
this.isRead = !!(IC32 & 2);
|
|
98
|
+
this.dataSelect = !!(IC32 & 4);
|
|
99
|
+
this.addressSelect = !!(portBpins & 0x80);
|
|
100
|
+
if (oldAddressSelect && !this.addressSelect) this.cmosAddr = portApins & 0x3f;
|
|
101
|
+
if (oldDataSelect && !this.dataSelect && !this.addressSelect && !this.isRead) {
|
|
102
|
+
if (this.cmosAddr > 0xb) {
|
|
103
|
+
this.store[this.cmosAddr] = portApins;
|
|
104
|
+
this.save();
|
|
105
|
+
} else {
|
|
106
|
+
const bbcTime = getBbcDateTime();
|
|
107
|
+
switch (this.cmosAddr) {
|
|
108
|
+
case 0:
|
|
109
|
+
bbcTime.setSeconds(fromBcd(portApins));
|
|
110
|
+
break;
|
|
111
|
+
case 2:
|
|
112
|
+
bbcTime.setMinutes(fromBcd(portApins));
|
|
113
|
+
break;
|
|
114
|
+
case 4:
|
|
115
|
+
bbcTime.setHours(fromBcd(portApins));
|
|
116
|
+
break;
|
|
117
|
+
// I tried some day offset stuff to lazily simulate the fact the day is separate
|
|
118
|
+
// from the date, but I couldn't easily get it to work during the multi-write update
|
|
119
|
+
// cycle/ I'm probably being dumb. Or else I should emulate the clock "properly" but
|
|
120
|
+
// that seems like an awful lot of work.
|
|
121
|
+
// case 6:
|
|
122
|
+
// dayOffset = (bbcTime.getDay() - fromBcd(portApins - 1)) % 7;
|
|
123
|
+
// break;
|
|
124
|
+
case 7:
|
|
125
|
+
bbcTime.setDate(fromBcd(portApins));
|
|
126
|
+
break;
|
|
127
|
+
case 8:
|
|
128
|
+
bbcTime.setMonth(fromBcd(portApins - 1));
|
|
129
|
+
break;
|
|
130
|
+
case 9: {
|
|
131
|
+
const yearBase = fromBcd(portApins) > 80 ? 1900 : 2000;
|
|
132
|
+
bbcTime.setFullYear(fromBcd(portApins) + yearBase);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const secondsNow = Math.floor(Date.now() / 1000) * 1000;
|
|
137
|
+
timeOffset = bbcTime.getTime() - secondsNow;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import $ from "jquery";
|
|
3
|
+
import { findModel } from "./models.js";
|
|
4
|
+
import { getFilterForMode } from "./canvas.js";
|
|
5
|
+
|
|
6
|
+
export class Config {
|
|
7
|
+
constructor(onChange, onClose) {
|
|
8
|
+
this.onChange = onChange;
|
|
9
|
+
this.onClose = onClose;
|
|
10
|
+
this.changed = {};
|
|
11
|
+
this.model = null;
|
|
12
|
+
this.coProcessor = null;
|
|
13
|
+
const $configuration = document.getElementById("configuration");
|
|
14
|
+
$configuration.addEventListener("show.bs.modal", () => {
|
|
15
|
+
this.changed = {};
|
|
16
|
+
this.setDropdownText(this.model.name);
|
|
17
|
+
this.set65c02(this.model.tube);
|
|
18
|
+
this.setTeletext(this.model.hasTeletextAdaptor);
|
|
19
|
+
this.setMusic5000(this.model.hasMusic5000);
|
|
20
|
+
this.setEconet(this.model.hasEconet);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
$configuration.addEventListener("hide.bs.modal", () => this.onClose(this.changed));
|
|
24
|
+
|
|
25
|
+
$(".model-menu a").on("click", (e) => {
|
|
26
|
+
this.changed.model = $(e.target).attr("data-target");
|
|
27
|
+
this.setDropdownText($(e.target).text());
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
$("#65c02").on("click", () => {
|
|
31
|
+
this.changed.coProcessor = $("#65c02").prop("checked");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
$("#hasTeletextAdaptor").on("click", () => {
|
|
35
|
+
this.changed.hasTeletextAdaptor = $("#hasTeletextAdaptor").prop("checked");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
$("#hasEconet").on("click", () => {
|
|
39
|
+
this.changed.hasEconet = $("#hasEconet").prop("checked");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
$("#hasMusic5000").on("click", () => {
|
|
43
|
+
this.changed.hasMusic5000 = $("#hasMusic5000").prop("checked");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
$(".keyboard-menu a").on("click", (e) => {
|
|
47
|
+
const keyLayout = $(e.target).attr("data-target");
|
|
48
|
+
this.changed.keyLayout = keyLayout;
|
|
49
|
+
this.setKeyLayout(keyLayout);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
$(".mic-channel-option").on("click", (e) => {
|
|
53
|
+
const channelString = $(e.target).data("channel");
|
|
54
|
+
const channel = channelString === "" ? undefined : parseInt($(e.target).data("channel"), 10);
|
|
55
|
+
this.changed.microphoneChannel = channel;
|
|
56
|
+
this.setMicrophoneChannel(channel);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
$("#mouseJoystickEnabled").on("click", () => {
|
|
60
|
+
this.changed.mouseJoystickEnabled = $("#mouseJoystickEnabled").prop("checked");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
$(".display-mode-option").on("click", (e) => {
|
|
64
|
+
const mode = $(e.target).data("mode");
|
|
65
|
+
this.changed.displayMode = mode;
|
|
66
|
+
this.setDisplayMode(mode);
|
|
67
|
+
this.onChange({ displayMode: mode });
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setMicrophoneChannel(channel) {
|
|
72
|
+
if (channel !== undefined) {
|
|
73
|
+
$(".mic-channel-text").text(`Channel ${channel}`);
|
|
74
|
+
} else {
|
|
75
|
+
$(".mic-channel-text").text("Disabled");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setMouseJoystickEnabled(enabled) {
|
|
80
|
+
$("#mouseJoystickEnabled").prop("checked", !!enabled);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
setDisplayMode(mode) {
|
|
84
|
+
const config = getFilterForMode(mode).getDisplayConfig();
|
|
85
|
+
$(".display-mode-text").text(config.name);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setModel(modelName) {
|
|
89
|
+
this.model = findModel(modelName);
|
|
90
|
+
$(".bbc-model").text(this.model.name);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setKeyLayout(keyLayout) {
|
|
94
|
+
$(".keyboard-layout").text(keyLayout[0].toUpperCase() + keyLayout.substring(1));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
set65c02(enabled) {
|
|
98
|
+
enabled = !!enabled;
|
|
99
|
+
$("#65c02").prop("checked", enabled);
|
|
100
|
+
this.model.tube = enabled ? findModel("Tube65c02") : null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setEconet(enabled) {
|
|
104
|
+
enabled = !!enabled;
|
|
105
|
+
$("#hasEconet").prop("checked", enabled);
|
|
106
|
+
this.model.hasEconet = enabled;
|
|
107
|
+
|
|
108
|
+
if (enabled && this.model.isMaster) {
|
|
109
|
+
this.addRemoveROM("master/anfs-4.25.rom", true);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setMusic5000(enabled) {
|
|
114
|
+
enabled = !!enabled;
|
|
115
|
+
$("#hasMusic5000").prop("checked", enabled);
|
|
116
|
+
this.model.hasMusic5000 = enabled;
|
|
117
|
+
this.addRemoveROM("ample.rom", enabled);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setTeletext(enabled) {
|
|
121
|
+
enabled = !!enabled;
|
|
122
|
+
$("#hasTeletextAdaptor").prop("checked", enabled);
|
|
123
|
+
this.model.hasTeletextAdaptor = enabled;
|
|
124
|
+
this.addRemoveROM("ats-3.0.rom", enabled);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setDropdownText(modelName) {
|
|
128
|
+
$("#bbc-model-dropdown .bbc-model").text(modelName);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
addRemoveROM(romName, required) {
|
|
132
|
+
if (required && !this.model.os.includes(romName)) {
|
|
133
|
+
this.model.os.push(romName);
|
|
134
|
+
} else {
|
|
135
|
+
let pos = this.model.os.indexOf(romName);
|
|
136
|
+
if (pos !== -1) {
|
|
137
|
+
this.model.os.splice(pos, 1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
mapLegacyModels(parsedQuery) {
|
|
143
|
+
if (!parsedQuery.model) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// "MasterTurbo" = Master + 6502 second processor
|
|
148
|
+
if (parsedQuery.model.toLowerCase() === "masterturbo") {
|
|
149
|
+
parsedQuery.model = "Master";
|
|
150
|
+
parsedQuery.coProcessor = true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// "BMusic5000" = BBC DFS 1.2 + Music 5000
|
|
154
|
+
if (parsedQuery.model.toLowerCase() === "bmusic5000") {
|
|
155
|
+
parsedQuery.model = "B-DFS1.2";
|
|
156
|
+
parsedQuery.hasMusic5000 = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// "BTeletext" = BBC DFS 1.2 + Teletext adaptor
|
|
160
|
+
if (parsedQuery.model.toLowerCase() === "bteletext") {
|
|
161
|
+
parsedQuery.model = "B-DFS1.2";
|
|
162
|
+
parsedQuery.hasTeletextAdaptor = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|