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
package/src/ddnoise.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import * as utils from "./utils.js";
|
|
3
|
+
import _ from "underscore";
|
|
4
|
+
|
|
5
|
+
const IDLE = 0;
|
|
6
|
+
const SPIN_UP = 1;
|
|
7
|
+
const SPINNING = 2;
|
|
8
|
+
const VOLUME = 0.25;
|
|
9
|
+
|
|
10
|
+
export class DdNoise {
|
|
11
|
+
constructor(context) {
|
|
12
|
+
this.context = context;
|
|
13
|
+
this.sounds = {};
|
|
14
|
+
this.state = IDLE;
|
|
15
|
+
this.motor = null;
|
|
16
|
+
this.gain = context.createGain();
|
|
17
|
+
this.gain.gain.value = VOLUME;
|
|
18
|
+
this.gain.connect(context.destination);
|
|
19
|
+
// workaround for older safaris that GC sounds when they're playing...
|
|
20
|
+
this.playing = [];
|
|
21
|
+
}
|
|
22
|
+
async initialise() {
|
|
23
|
+
const sounds = await loadSounds(this.context, {
|
|
24
|
+
motorOn: "sounds/disc525/motoron.wav",
|
|
25
|
+
motorOff: "sounds/disc525/motoroff.wav",
|
|
26
|
+
motor: "sounds/disc525/motor.wav",
|
|
27
|
+
step: "sounds/disc525/step.wav",
|
|
28
|
+
seek: "sounds/disc525/seek.wav",
|
|
29
|
+
seek2: "sounds/disc525/seek2.wav",
|
|
30
|
+
seek3: "sounds/disc525/seek3.wav",
|
|
31
|
+
});
|
|
32
|
+
this.sounds = sounds;
|
|
33
|
+
}
|
|
34
|
+
oneShot(sound) {
|
|
35
|
+
const duration = sound.duration;
|
|
36
|
+
const context = this.context;
|
|
37
|
+
if (context.state !== "running") return duration;
|
|
38
|
+
const source = context.createBufferSource();
|
|
39
|
+
source.buffer = sound;
|
|
40
|
+
source.connect(this.gain);
|
|
41
|
+
source.start();
|
|
42
|
+
return duration;
|
|
43
|
+
}
|
|
44
|
+
play(sound, loop) {
|
|
45
|
+
if (this.context.state !== "running") return Promise.reject();
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const source = this.context.createBufferSource();
|
|
48
|
+
source.loop = !!loop;
|
|
49
|
+
source.buffer = sound;
|
|
50
|
+
source.connect(this.gain);
|
|
51
|
+
source.onended = () => {
|
|
52
|
+
this.playing = _.without(this.playing, source);
|
|
53
|
+
if (!source.loop) resolve();
|
|
54
|
+
};
|
|
55
|
+
source.start();
|
|
56
|
+
this.playing.push(source);
|
|
57
|
+
if (source.loop) {
|
|
58
|
+
resolve(source);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
spinUp() {
|
|
63
|
+
if (this.state === SPINNING || this.state === SPIN_UP) return;
|
|
64
|
+
this.state = SPIN_UP;
|
|
65
|
+
this.play(this.sounds.motorOn).then(
|
|
66
|
+
() => {
|
|
67
|
+
// Handle race: we may have had spinDown() called on us before the
|
|
68
|
+
// spinUp() initial sound finished playing.
|
|
69
|
+
if (this.state === IDLE) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.play(this.sounds.motor, true).then((source) => {
|
|
73
|
+
this.motor = source;
|
|
74
|
+
this.state = SPINNING;
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
() => {},
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
spinDown() {
|
|
81
|
+
if (this.state === IDLE) return;
|
|
82
|
+
this.state = IDLE;
|
|
83
|
+
if (this.motor) {
|
|
84
|
+
this.motor.stop();
|
|
85
|
+
this.motor = null;
|
|
86
|
+
this.oneShot(this.sounds.motorOff);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
seek(diff) {
|
|
90
|
+
if (diff < 0) diff = -diff;
|
|
91
|
+
if (diff === 0) return 0;
|
|
92
|
+
else if (diff <= 2) return this.oneShot(this.sounds.step);
|
|
93
|
+
else if (diff <= 20) return this.oneShot(this.sounds.seek);
|
|
94
|
+
else if (diff <= 40) return this.oneShot(this.sounds.seek2);
|
|
95
|
+
else return this.oneShot(this.sounds.seek3);
|
|
96
|
+
}
|
|
97
|
+
mute() {
|
|
98
|
+
this.gain.gain.value = 0;
|
|
99
|
+
}
|
|
100
|
+
unmute() {
|
|
101
|
+
this.gain.gain.value = VOLUME;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function loadSounds(context, sounds) {
|
|
106
|
+
const loaded = await Promise.all(
|
|
107
|
+
_.map(sounds, async (sound) => {
|
|
108
|
+
// Safari doesn't support the Promise stuff directly, so we create
|
|
109
|
+
// our own Promise here.
|
|
110
|
+
const data = await utils.loadData(sound);
|
|
111
|
+
return await new Promise((resolve) => {
|
|
112
|
+
context.decodeAudioData(data.buffer, (decodedData) => {
|
|
113
|
+
resolve(decodedData);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
const keys = _.keys(sounds);
|
|
119
|
+
const result = {};
|
|
120
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
121
|
+
result[keys[i]] = loaded[i];
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export class FakeDdNoise {
|
|
127
|
+
constructor() {}
|
|
128
|
+
seek() {
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
initialise() {
|
|
132
|
+
return Promise.resolve();
|
|
133
|
+
}
|
|
134
|
+
spinUp() {}
|
|
135
|
+
spinDown() {}
|
|
136
|
+
mute() {}
|
|
137
|
+
unmute() {}
|
|
138
|
+
}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// Translated from beebjit by Chris Evans.
|
|
2
|
+
// https://github.com/scarybeasts/beebjit
|
|
3
|
+
// eslint-disable-next-line no-unused-vars
|
|
4
|
+
import { Scheduler } from "./scheduler.js";
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
import { Disc } from "./disc.js";
|
|
7
|
+
import { IbmDiscFormat } from "./disc.js";
|
|
8
|
+
|
|
9
|
+
class StepEvent extends Event {
|
|
10
|
+
constructor(stepAmount) {
|
|
11
|
+
super("step");
|
|
12
|
+
this.stepAmount = stepAmount;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Abstract base class defining the interface for disc drives.
|
|
18
|
+
* All disc drive implementations must extend this class.
|
|
19
|
+
*/
|
|
20
|
+
export class BaseDiscDrive extends EventTarget {
|
|
21
|
+
/** @returns {Disc|undefined} */
|
|
22
|
+
get disc() {
|
|
23
|
+
throw new Error("Not implemented: disc getter");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @returns {number} */
|
|
27
|
+
get track() {
|
|
28
|
+
throw new Error("Not implemented: track getter");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @returns {number} */
|
|
32
|
+
get headPosition() {
|
|
33
|
+
throw new Error("Not implemented: headPosition getter");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @returns {boolean} */
|
|
37
|
+
get indexPulse() {
|
|
38
|
+
throw new Error("Not implemented: indexPulse getter");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @returns {boolean} */
|
|
42
|
+
get spinning() {
|
|
43
|
+
throw new Error("Not implemented: spinning getter");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @returns {boolean} */
|
|
47
|
+
get writeProtect() {
|
|
48
|
+
throw new Error("Not implemented: writeProtect getter");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @returns {number} */
|
|
52
|
+
get trackLength() {
|
|
53
|
+
throw new Error("Not implemented: trackLength getter");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @returns {number} */
|
|
57
|
+
get positionFraction() {
|
|
58
|
+
throw new Error("Not implemented: positionFraction getter");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @returns {number} */
|
|
62
|
+
get positionTime() {
|
|
63
|
+
throw new Error("Not implemented: positionTime getter");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {Disc|undefined} _disc
|
|
68
|
+
*/
|
|
69
|
+
setDisc(_disc) {
|
|
70
|
+
throw new Error("Not implemented: setDisc");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {function(number, number): void} _callback
|
|
75
|
+
*/
|
|
76
|
+
setPulsesCallback(_callback) {
|
|
77
|
+
throw new Error("Not implemented: setPulsesCallback");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
startSpinning() {
|
|
81
|
+
throw new Error("Not implemented: startSpinning");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
stopSpinning() {
|
|
85
|
+
throw new Error("Not implemented: stopSpinning");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {boolean} _isSideUpper
|
|
90
|
+
*/
|
|
91
|
+
selectSide(_isSideUpper) {
|
|
92
|
+
throw new Error("Not implemented: selectSide");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {number} _delta
|
|
97
|
+
*/
|
|
98
|
+
seekOneTrack(_delta) {
|
|
99
|
+
throw new Error("Not implemented: seekOneTrack");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {number} _newTrack
|
|
104
|
+
*/
|
|
105
|
+
notifySeek(_newTrack) {
|
|
106
|
+
throw new Error("Not implemented: notifySeek");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {number} _delta
|
|
111
|
+
*/
|
|
112
|
+
notifySeekAmount(_delta) {
|
|
113
|
+
throw new Error("Not implemented: notifySeekAmount");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {boolean} _isDoubleDensity
|
|
118
|
+
*/
|
|
119
|
+
set32usMode(_isDoubleDensity) {
|
|
120
|
+
throw new Error("Not implemented: set32usMode");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {number} _pulses
|
|
125
|
+
*/
|
|
126
|
+
writePulses(_pulses) {
|
|
127
|
+
throw new Error("Not implemented: writePulses");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @returns {number} */
|
|
131
|
+
getQuasiRandomPulses() {
|
|
132
|
+
throw new Error("Not implemented: getQuasiRandomPulses");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class DiscDrive extends BaseDiscDrive {
|
|
137
|
+
static get TicksPerRevolution() {
|
|
138
|
+
// 300 rpm
|
|
139
|
+
return 400000;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// scarybeast's Chinon drive holds the index pulse low for about 4ms. */
|
|
143
|
+
static get DiscIndexMs() {
|
|
144
|
+
return 4;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a new DiscDrive.
|
|
149
|
+
*
|
|
150
|
+
* @param {Number} id which drive id this is (0 or 1)
|
|
151
|
+
* @param {Scheduler} scheduler scheduler to register callbacks etc
|
|
152
|
+
*/
|
|
153
|
+
constructor(id, scheduler) {
|
|
154
|
+
super();
|
|
155
|
+
this._scheduler = scheduler;
|
|
156
|
+
/** @type {Disc|undefined} */
|
|
157
|
+
this._disc = undefined;
|
|
158
|
+
this._is40Track = false;
|
|
159
|
+
// Physically always 80 tracks even if we're in 40 track mode. 40 track mode essentially double steps.
|
|
160
|
+
this._track = 0;
|
|
161
|
+
this._isSideUpper = false;
|
|
162
|
+
// In units where 3125 is a normal track length.
|
|
163
|
+
this._headPosition = 0;
|
|
164
|
+
// Extra precision for head position, needed for MFM.
|
|
165
|
+
this._pulsePosition = 0;
|
|
166
|
+
this._in32usMode = false;
|
|
167
|
+
/** @type {function(number, number): void} */
|
|
168
|
+
this._pulsesCallback = null;
|
|
169
|
+
|
|
170
|
+
this._timer = this._scheduler.newTask(this._onTimer.bind(this));
|
|
171
|
+
this._spinning = false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @returns {Disc|undefined}
|
|
176
|
+
*/
|
|
177
|
+
get disc() {
|
|
178
|
+
return this._disc;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getQuasiRandomPulses() {
|
|
182
|
+
const ticks = this._scheduler.epoch;
|
|
183
|
+
const fmData = (ticks ^ (ticks >>> 8) ^ (ticks >>> 16) ^ (ticks >>> 24)) & 0xff;
|
|
184
|
+
return IbmDiscFormat.fmTo2usPulses(0xff, fmData);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
get trackLength() {
|
|
188
|
+
const disc = this.disc;
|
|
189
|
+
if (!disc) return IbmDiscFormat.bytesPerTrack;
|
|
190
|
+
return disc.getTrack(this._isSideUpper, this._track).length;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
_onTimer() {
|
|
194
|
+
let pulses = this.disc ? this.disc.readPulses(this._isSideUpper, this._track, this._headPosition) : 0;
|
|
195
|
+
let numPulses = 32;
|
|
196
|
+
if (this._pulsePosition === 16 || this._in32usMode) {
|
|
197
|
+
numPulses = 16;
|
|
198
|
+
if (this._pulsePosition === 0) pulses >>>= 16;
|
|
199
|
+
pulses &= 0xffff;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// If there's an empty patch on the disc surface, the disc drive's head amplifier will typically desperately
|
|
203
|
+
// seek for a signal in the noise, resulting in "weak bits". @scarybeasts verified this with an oscilloscope on
|
|
204
|
+
// his Chinon F-051MD drive, which has a Motorola MC3470AP head amplifier. We need to return an inconsistent yet
|
|
205
|
+
// deterministic set of weak bits.
|
|
206
|
+
if (pulses === 0) pulses = this.getQuasiRandomPulses();
|
|
207
|
+
|
|
208
|
+
if (this._pulsesCallback) {
|
|
209
|
+
this._pulsesCallback(pulses, numPulses);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const thisTicks = this.positionTime;
|
|
213
|
+
|
|
214
|
+
// Advance head position.
|
|
215
|
+
if (numPulses === 16) {
|
|
216
|
+
if (this._pulsePosition === 0) {
|
|
217
|
+
this._pulsePosition = 16;
|
|
218
|
+
} else {
|
|
219
|
+
this._pulsePosition = 0;
|
|
220
|
+
this._headPosition++;
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
this._headPosition++;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const nextTicks = this.positionTime;
|
|
227
|
+
|
|
228
|
+
if (this._headPosition === this.trackLength) {
|
|
229
|
+
this._headPosition = 0;
|
|
230
|
+
this._checkTrackNeedsWrite();
|
|
231
|
+
}
|
|
232
|
+
if (this._spinning) this._timer.reschedule(nextTicks - thisTicks);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
get headPosition() {
|
|
236
|
+
return this._headPosition;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
get track() {
|
|
240
|
+
return this._track;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
get positionFraction() {
|
|
244
|
+
return (this._headPosition + this._pulsePosition / 32) / this.trackLength;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
set positionFraction(fraction) {
|
|
248
|
+
this._headPosition = (this.trackLength * fraction) | 0;
|
|
249
|
+
this._pulsePosition = 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
get positionTime() {
|
|
253
|
+
return (this.positionFraction * DiscDrive.TicksPerRevolution) | 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @param {function(number, number): void} callback
|
|
258
|
+
*/
|
|
259
|
+
setPulsesCallback(callback) {
|
|
260
|
+
this._pulsesCallback = callback;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
get spinning() {
|
|
264
|
+
// beebjit uses the timer's scheduledness here, but our schedule system deschedules timers
|
|
265
|
+
// during callbacks, which makes this briefly "false" and disturbs things.
|
|
266
|
+
return this._spinning;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
startSpinning() {
|
|
270
|
+
if (!this._spinning) {
|
|
271
|
+
this.dispatchEvent(new Event("startSpinning"));
|
|
272
|
+
this._timer.schedule(1);
|
|
273
|
+
}
|
|
274
|
+
this._spinning = true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
stopSpinning() {
|
|
278
|
+
if (this._spinning) {
|
|
279
|
+
this.dispatchEvent(new Event("stopSpinning"));
|
|
280
|
+
}
|
|
281
|
+
this._timer.cancel();
|
|
282
|
+
this._spinning = false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
selectSide(isSideUpper) {
|
|
286
|
+
const fraction = this.positionFraction;
|
|
287
|
+
this._checkTrackNeedsWrite();
|
|
288
|
+
this._isSideUpper = isSideUpper;
|
|
289
|
+
this.positionFraction = fraction;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @param {Disc} disc
|
|
294
|
+
*/
|
|
295
|
+
setDisc(disc) {
|
|
296
|
+
this._disc = disc;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
get indexPulse() {
|
|
300
|
+
// With no disc loaded the drive asserts the index all the time.
|
|
301
|
+
if (!this.disc) return true;
|
|
302
|
+
// The 8271 datasheet says that the index pulse must be held for over 0.5us. Most drives are in the millisecond range.
|
|
303
|
+
return this._headPosition < (this.trackLength * DiscDrive.DiscIndexMs) / 200;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
writePulses(pulses) {
|
|
307
|
+
if (!this.disc) return;
|
|
308
|
+
// All drives seen have a write-protect failsafe on the drive itself.
|
|
309
|
+
if (this.disc.writeProtected) return;
|
|
310
|
+
if (this._in32usMode) {
|
|
311
|
+
if (pulses & 0xffff0000) throw new Error(`Unable to write 32us pulses for ${pulses}`);
|
|
312
|
+
const existingPulses = this.disc.readPulses(this._isSideUpper, this.track, this.headPosition);
|
|
313
|
+
if (this._pulsePosition === 0) pulses = (existingPulses & 0x0000ffff) | (pulses << 16);
|
|
314
|
+
else pulses = (existingPulses & 0xffff0000) | pulses;
|
|
315
|
+
}
|
|
316
|
+
this.disc.writePulses(this._isSideUpper, this.track, this.headPosition, pulses);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
get writeProtect() {
|
|
320
|
+
return this.disc ? this.disc.writeProtected : false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
set32usMode(isDoubleDensity) {
|
|
324
|
+
this._in32usMode = isDoubleDensity;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Seek a relative track.
|
|
329
|
+
*
|
|
330
|
+
* @param {Number} delta track step delta, either 1 or -1
|
|
331
|
+
*/
|
|
332
|
+
seekOneTrack(delta) {
|
|
333
|
+
if (this._is40Track) delta *= 2;
|
|
334
|
+
this._selectTrack(this._track + delta);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Notify that an overall seek is happening to a particular track. Purely informational.
|
|
339
|
+
*/
|
|
340
|
+
notifySeek(newTrack) {
|
|
341
|
+
this.notifySeekAmount(newTrack - this._track);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Notify that an overall seek is happening by some delta smount. Purely informational.
|
|
346
|
+
*/
|
|
347
|
+
notifySeekAmount(delta) {
|
|
348
|
+
this.dispatchEvent(new StepEvent(delta));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @param {Number} track
|
|
353
|
+
*/
|
|
354
|
+
_selectTrack(track) {
|
|
355
|
+
this._checkTrackNeedsWrite();
|
|
356
|
+
if (track < 0) {
|
|
357
|
+
track = 0;
|
|
358
|
+
console.log("Clang! disc head stopped at track 0");
|
|
359
|
+
} else if (track >= IbmDiscFormat.tracksPerDisc) {
|
|
360
|
+
track = IbmDiscFormat.tracksPerDisc - 1;
|
|
361
|
+
console.log("Clang! disc head stopper at track max");
|
|
362
|
+
}
|
|
363
|
+
const fraction = this.positionFraction;
|
|
364
|
+
this._track = track;
|
|
365
|
+
this.positionFraction = fraction;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
_checkTrackNeedsWrite() {
|
|
369
|
+
if (this.disc) this.disc.flushWrites();
|
|
370
|
+
}
|
|
371
|
+
}
|