jsbeeb 1.1.1 → 1.3.3
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/package.json +26 -12
- package/src/6502.js +51 -41
- package/src/app/app.js +99 -8
- package/src/app/electron.js +47 -5
- package/src/app/preload.js +11 -1
- package/src/config.js +9 -2
- package/src/disc.js +2 -2
- package/src/filestore.js +1 -4
- package/src/gamepad-source.js +1 -1
- package/src/machine-session.js +393 -0
- package/src/main.js +36 -7
- package/src/music5000-worklet.js +1 -0
- package/src/music5000.js +1 -9
- package/src/sth.js +3 -1
- package/src/utils.js +14 -2
- package/src/web/audio-renderer.js +1 -1
- package/src/web/debug.js +2 -2
- package/tests/test-machine.js +82 -1
- package/.editorconfig +0 -15
- package/.git-blame-ignore-revs +0 -3
- package/.github/copilot-instructions.md +0 -94
- package/.github/workflows/claude-issue-triage.yml +0 -105
- package/.github/workflows/claude.yml +0 -63
- package/.github/workflows/release-please.yml +0 -75
- package/.github/workflows/test-and-deploy.yml +0 -86
- package/.gitmodules +0 -6
- package/.husky/pre-commit +0 -1
- package/.idea/codeStyleSettings.xml +0 -9
- package/.idea/codeStyles/Project.xml +0 -62
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/compiler.xml +0 -22
- package/.idea/copyright/profiles_settings.xml +0 -3
- package/.idea/encodings.xml +0 -6
- package/.idea/inspectionProfiles/Project_Default.xml +0 -7
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/jsLinters/jshint.xml +0 -85
- package/.idea/jsLinters/jslint.xml +0 -15
- package/.idea/jsbeeb.iml +0 -11
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -7
- package/.idea/runConfigurations/Debug.xml +0 -5
- package/.idea/scopes/scope_settings.xml +0 -5
- package/.idea/vcs.xml +0 -8
- package/.prettierignore +0 -4
- package/.prettierrc.json +0 -1
- package/.release-please-manifest.json +0 -3
- package/.vscode/launch.json +0 -14
- package/.vscode/settings.json +0 -6
- package/CHANGELOG.md +0 -32
- package/CLAUDE.md +0 -136
- package/Dockerfile +0 -22
- package/Makefile +0 -30
- package/docker/nginx-default.conf +0 -10
- package/docs/pal-comb-filter-research.md +0 -129
- package/docs/pal-simulation-design.md +0 -368
- package/eslint.config.js +0 -35
- package/index.html +0 -954
- package/jsconfig.json +0 -10
- package/public/discs/README.Irq-Timing +0 -3
- package/public/discs/README.bcdtest +0 -5
- package/public/discs/README.elite +0 -6
- package/public/discs/README.eng_test +0 -3
- package/public/discs/README.protection +0 -7
- 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 +0 -3
- 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 +0 -13
- package/run-container.sh +0 -92
- package/tests/integration/RmwX.asm +0 -47
- package/tests/integration/TestInstructionsSource +0 -27
- package/tests/integration/TestTimingsResults +0 -27
- package/tests/integration/TestTimingsSource +0 -61
- package/tests/integration/bcd.js +0 -23
- package/tests/integration/dormann.js +0 -101
- package/tests/integration/dp111timing.js +0 -42
- package/tests/integration/ensure-submodules.js +0 -25
- package/tests/integration/nops.bas +0 -119
- package/tests/integration/nops.js +0 -24
- package/tests/integration/protection.js +0 -26
- package/tests/integration/rmw.js +0 -69
- 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 +0 -126
- package/tests/integration/timings.js +0 -56
- package/tests/integration/via.js +0 -1125
- package/tests/suite/README.md +0 -7
- package/tests/suite/Test Suite 2.15.txt +0 -373
- 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 +0 -178
- package/tests/suite/cbm-hackers-post.md +0 -78
- package/tests/test-suite.js +0 -147
- package/tests/test.css +0 -7
- 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 +0 -307
- package/tests/unit/test-bcd.js +0 -30
- package/tests/unit/test-cmos.js +0 -266
- package/tests/unit/test-disc-drive.js +0 -85
- package/tests/unit/test-disc-hfe.js +0 -347
- package/tests/unit/test-disc.js +0 -232
- package/tests/unit/test-fifo.js +0 -35
- package/tests/unit/test-gamepad-source.js +0 -67
- package/tests/unit/test-gzip.js +0 -22
- package/tests/unit/test-intel-fdc.js +0 -93
- package/tests/unit/test-keyboard.js +0 -410
- package/tests/unit/test-mouse-joystick-source.js +0 -128
- package/tests/unit/test-scheduler.js +0 -190
- package/tests/unit/test-serial.js +0 -154
- package/tests/unit/test-teletext-adaptor.js +0 -359
- package/tests/unit/test-tokenise.js +0 -65
- package/tests/unit/test-url-params.js +0 -398
- package/tests/unit/test-utils.js +0 -276
- package/tests/unit/test-video.js +0 -498
- package/tests/unit/test-zip.js +0 -56
- 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 +0 -80
- package/tools/vite-plugin-fir-shader.js +0 -131
- package/vite.config.js +0 -34
package/package.json
CHANGED
|
@@ -7,13 +7,24 @@
|
|
|
7
7
|
"name": "jsbeeb",
|
|
8
8
|
"description": "Emulate a BBC Micro",
|
|
9
9
|
"repository": "git@github.com:mattgodbolt/jsbeeb.git",
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.3.3",
|
|
11
11
|
"//": "If you change the version of Node, it must also be updated at the top of the Dockerfile.",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": "22"
|
|
14
14
|
},
|
|
15
15
|
"type": "module",
|
|
16
16
|
"main": "./src/app/app.js",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./src/app/app.js",
|
|
19
|
+
"./machine-session": "./src/machine-session.js",
|
|
20
|
+
"./src/*": "./src/*",
|
|
21
|
+
"./tests/test-machine.js": "./tests/test-machine.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src/",
|
|
25
|
+
"tests/test-machine.js",
|
|
26
|
+
"public/roms/"
|
|
27
|
+
],
|
|
17
28
|
"dependencies": {
|
|
18
29
|
"@popperjs/core": "^2.11.8",
|
|
19
30
|
"argparse": "^2.0.1",
|
|
@@ -21,28 +32,31 @@
|
|
|
21
32
|
"bootswatch": "^5.3.8",
|
|
22
33
|
"event-emitter-es6": "^1.1.5",
|
|
23
34
|
"fflate": "^0.8.2",
|
|
24
|
-
"jquery": "^
|
|
35
|
+
"jquery": "^4.0.0",
|
|
25
36
|
"pako": "^2.1.0",
|
|
26
37
|
"smoothie": "^1.36.1",
|
|
38
|
+
"sharp": "^0.34.5",
|
|
27
39
|
"underscore": "^1.13.7"
|
|
28
40
|
},
|
|
29
41
|
"devDependencies": {
|
|
30
|
-
"@
|
|
31
|
-
"
|
|
42
|
+
"@eslint/js": "^10.0.1",
|
|
43
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
44
|
+
"eslint": "^10.0.0",
|
|
32
45
|
"eslint-config-prettier": "^10.1.8",
|
|
33
|
-
"eslint-plugin-prettier": "^5.5.
|
|
46
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
47
|
+
"globals": "^17.3.0",
|
|
34
48
|
"husky": "^9.1.7",
|
|
35
49
|
"lint-staged": "^16.2.7",
|
|
36
|
-
"npm-run-
|
|
50
|
+
"npm-run-all2": "^8.0.4",
|
|
37
51
|
"pixelmatch": "^7.1.0",
|
|
38
|
-
"prettier": "^3.
|
|
39
|
-
"
|
|
40
|
-
"vite": "^7.2.4",
|
|
52
|
+
"prettier": "^3.8.1",
|
|
53
|
+
"vite": "^7.3.1",
|
|
41
54
|
"vitest": "^4.0.10"
|
|
42
55
|
},
|
|
43
56
|
"optionalDependencies": {
|
|
44
|
-
"electron": "^
|
|
45
|
-
"electron-builder": "^26.
|
|
57
|
+
"electron": "^40.4.1",
|
|
58
|
+
"electron-builder": "^26.8.1",
|
|
59
|
+
"electron-store": "^11.0.2"
|
|
46
60
|
},
|
|
47
61
|
"license": "GPL-3.0-or-later",
|
|
48
62
|
"build": {
|
|
@@ -87,7 +101,7 @@
|
|
|
87
101
|
"test:cpu": "node tests/test-suite.js",
|
|
88
102
|
"coverage:unit": "vitest run tests/unit --coverage",
|
|
89
103
|
"coverage:all-tests": "vitest run --coverage",
|
|
90
|
-
"benchmark": "node app-bench.js",
|
|
104
|
+
"benchmark": "node src/app-bench.js",
|
|
91
105
|
"electron": "npm run build && ELECTRON_DISABLE_SANDBOX=1 electron .",
|
|
92
106
|
"electron:build": "electron-builder"
|
|
93
107
|
},
|
package/src/6502.js
CHANGED
|
@@ -419,6 +419,7 @@ class Tube6502 extends Base6502 {
|
|
|
419
419
|
this.romPaged = true;
|
|
420
420
|
this.pc = this.readmem(0xfffc) | (this.readmem(0xfffd) << 8);
|
|
421
421
|
this.p.i = true;
|
|
422
|
+
this.s = (this.s - 3) & 0xff; // Simulate 3 dummy pushes during reset
|
|
422
423
|
this.tube.reset(hard);
|
|
423
424
|
}
|
|
424
425
|
|
|
@@ -607,11 +608,7 @@ export class Cpu6502 extends Base6502 {
|
|
|
607
608
|
this.peripheralCycles = 0;
|
|
608
609
|
this.videoCycles = 0;
|
|
609
610
|
|
|
610
|
-
|
|
611
|
-
this.polltime = this.polltimeFast;
|
|
612
|
-
} else {
|
|
613
|
-
this.polltime = this.polltimeSlow;
|
|
614
|
-
}
|
|
611
|
+
this.polltime = this.buildPolltime();
|
|
615
612
|
|
|
616
613
|
this._debugRead = this._debugWrite = this._debugInstruction = null;
|
|
617
614
|
this.debugInstruction = new DebugHook(this, "_debugInstruction");
|
|
@@ -1185,6 +1182,7 @@ export class Cpu6502 extends Base6502 {
|
|
|
1185
1182
|
}
|
|
1186
1183
|
this.pc = this.readmem(0xfffc) | (this.readmem(0xfffd) << 8);
|
|
1187
1184
|
this.p.i = true;
|
|
1185
|
+
this.s = (this.s - 3) & 0xff; // Simulate 3 dummy pushes during reset
|
|
1188
1186
|
this._nmiEdge = false;
|
|
1189
1187
|
this._nmiLevel = false;
|
|
1190
1188
|
this.halted = false;
|
|
@@ -1211,44 +1209,56 @@ export class Cpu6502 extends Base6502 {
|
|
|
1211
1209
|
this.polltime(cycles);
|
|
1212
1210
|
}
|
|
1213
1211
|
|
|
1214
|
-
//
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
this.tube.execute(cycles);
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1212
|
+
// Builds commong code between polltimeSlow and polltimeFast
|
|
1213
|
+
buildPolltime() {
|
|
1214
|
+
const nop = (_cycles) => {};
|
|
1215
|
+
const tubeStuff = (cycles) => (this.model.tube ? this.tube.execute(cycles) : nop);
|
|
1216
|
+
const teletextStuff = this.teletextAdaptor ? (cycles) => this.teletextAdaptor.polltime(cycles) : nop;
|
|
1217
|
+
const musicStuff = this.music5000 ? (cycles) => this.music5000.polltime(cycles) : nop;
|
|
1218
|
+
const econetStuff = this.econet
|
|
1219
|
+
? (cycles) => {
|
|
1220
|
+
const donmi = this.econet.polltime(cycles);
|
|
1221
|
+
if (donmi && this.econet.econetNMIEnabled) {
|
|
1222
|
+
this.NMI(true);
|
|
1223
|
+
}
|
|
1224
|
+
this.filestore.polltime(cycles);
|
|
1225
|
+
}
|
|
1226
|
+
: nop;
|
|
1227
|
+
|
|
1228
|
+
const commonStuff = (cycles) => {
|
|
1229
|
+
this.scheduler.polltime(cycles);
|
|
1230
|
+
tubeStuff(cycles);
|
|
1231
|
+
teletextStuff(cycles);
|
|
1232
|
+
musicStuff(cycles);
|
|
1233
|
+
econetStuff(cycles);
|
|
1234
|
+
};
|
|
1228
1235
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1236
|
+
if (this.cpuMultiplier === 1 && this.videoCyclesBatch === 0) {
|
|
1237
|
+
// Simplest, common case where multiplier is 1 and batching is off.
|
|
1238
|
+
return (cycles) => {
|
|
1239
|
+
cycles |= 0;
|
|
1240
|
+
this.currentCycles += cycles;
|
|
1241
|
+
this.video.polltime(cycles);
|
|
1242
|
+
commonStuff(cycles);
|
|
1243
|
+
};
|
|
1244
|
+
} else {
|
|
1245
|
+
// Handle speedup case.
|
|
1246
|
+
return (cycles) => {
|
|
1247
|
+
cycles |= 0;
|
|
1248
|
+
this.currentCycles += cycles;
|
|
1249
|
+
this.peripheralCycles += cycles;
|
|
1250
|
+
this.videoCycles += cycles;
|
|
1251
|
+
cycles = (this.videoCycles / this.cpuMultiplier) | 0;
|
|
1252
|
+
if (cycles > this.videoCyclesBatch) {
|
|
1253
|
+
this.video.polltime(cycles);
|
|
1254
|
+
this.videoCycles -= (cycles * this.cpuMultiplier) | 0;
|
|
1255
|
+
}
|
|
1256
|
+
cycles = (this.peripheralCycles / this.cpuMultiplier) | 0;
|
|
1257
|
+
if (!cycles) return;
|
|
1258
|
+
this.peripheralCycles -= (cycles * this.cpuMultiplier) | 0;
|
|
1259
|
+
commonStuff(cycles);
|
|
1260
|
+
};
|
|
1239
1261
|
}
|
|
1240
|
-
cycles = (this.peripheralCycles / this.cpuMultiplier) | 0;
|
|
1241
|
-
if (!cycles) return;
|
|
1242
|
-
this.peripheralCycles -= (cycles * this.cpuMultiplier) | 0;
|
|
1243
|
-
this.polltimeCommon(cycles);
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
// Faster, but more limited version
|
|
1247
|
-
polltimeFast(cycles) {
|
|
1248
|
-
cycles |= 0;
|
|
1249
|
-
this.currentCycles += cycles;
|
|
1250
|
-
this.video.polltime(cycles);
|
|
1251
|
-
this.polltimeCommon(cycles);
|
|
1252
1262
|
}
|
|
1253
1263
|
|
|
1254
1264
|
execute(numCyclesToRun) {
|
package/src/app/app.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
import { app, BrowserWindow, dialog, Menu, shell, nativeImage } from "electron";
|
|
2
|
+
import { app, BrowserWindow, dialog, ipcMain, Menu, shell, nativeImage } from "electron";
|
|
3
|
+
import Store from "electron-store";
|
|
3
4
|
import * as fs from "fs";
|
|
4
5
|
import * as path from "path";
|
|
5
6
|
import { ArgumentParser } from "argparse";
|
|
6
7
|
|
|
8
|
+
const store = new Store();
|
|
9
|
+
|
|
10
|
+
ipcMain.on("set-title", (event, title) => {
|
|
11
|
+
const webContents = event.sender;
|
|
12
|
+
const win = BrowserWindow.fromWebContents(webContents);
|
|
13
|
+
if (win) win.setTitle(title);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
ipcMain.on("save-settings", (event, settings) => {
|
|
17
|
+
const current = store.get("settings", {});
|
|
18
|
+
store.set("settings", { ...current, ...settings });
|
|
19
|
+
});
|
|
20
|
+
|
|
7
21
|
const isMac = process.platform === "darwin";
|
|
8
22
|
|
|
9
23
|
function getArguments() {
|
|
@@ -62,7 +76,9 @@ async function createWindow() {
|
|
|
62
76
|
},
|
|
63
77
|
});
|
|
64
78
|
|
|
65
|
-
|
|
79
|
+
// Load saved settings, then override with command-line args
|
|
80
|
+
const savedSettings = store.get("settings", {});
|
|
81
|
+
const query = { ...savedSettings };
|
|
66
82
|
if (args.disc1 && !args.noboot) query.autoboot = true;
|
|
67
83
|
if (args.disc1) query.disc1 = getFileParam(args.disc1);
|
|
68
84
|
if (args.disc2) query.disc2 = getFileParam(args.disc2);
|
|
@@ -95,14 +111,43 @@ function makeLoader(drive) {
|
|
|
95
111
|
properties: ["openFile"],
|
|
96
112
|
});
|
|
97
113
|
if (!result.canceled) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
114
|
+
const filePath = getFileParam(result.filePaths[0]);
|
|
115
|
+
store.set(`settings.disc${drive + 1}`, filePath);
|
|
116
|
+
browserWindow.webContents.send("load", { drive, path: filePath });
|
|
102
117
|
}
|
|
103
118
|
};
|
|
104
119
|
}
|
|
105
120
|
|
|
121
|
+
function makeTapeLoader() {
|
|
122
|
+
return async (_, browserWindow) => {
|
|
123
|
+
const result = await dialog.showOpenDialog(browserWindow, {
|
|
124
|
+
title: "Load a tape image",
|
|
125
|
+
filters: [
|
|
126
|
+
{ name: "Tape images", extensions: ["uef"] },
|
|
127
|
+
{ name: "ZIPped tape images", extensions: ["zip"] },
|
|
128
|
+
],
|
|
129
|
+
properties: ["openFile"],
|
|
130
|
+
});
|
|
131
|
+
if (!result.canceled) {
|
|
132
|
+
const filePath = getFileParam(result.filePaths[0]);
|
|
133
|
+
store.set("settings.tape", filePath);
|
|
134
|
+
browserWindow.webContents.send("load-tape", { path: filePath });
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function showModal(modalId, options = {}) {
|
|
140
|
+
return (_, browserWindow) => {
|
|
141
|
+
browserWindow.webContents.send("show-modal", { modalId, ...options });
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function sendAction(actionId) {
|
|
146
|
+
return (_, browserWindow) => {
|
|
147
|
+
browserWindow.webContents.send("action", { actionId });
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
106
151
|
const template = [
|
|
107
152
|
// { role: 'appMenu' }
|
|
108
153
|
...(isMac
|
|
@@ -128,13 +173,32 @@ const template = [
|
|
|
128
173
|
label: "File",
|
|
129
174
|
submenu: [
|
|
130
175
|
{
|
|
131
|
-
label: "Load disc 0",
|
|
176
|
+
label: "Load disc 0...",
|
|
132
177
|
click: makeLoader(0),
|
|
133
178
|
},
|
|
134
179
|
{
|
|
135
|
-
label: "Load disc 1",
|
|
180
|
+
label: "Load disc 1...",
|
|
136
181
|
click: makeLoader(1),
|
|
137
182
|
},
|
|
183
|
+
{ type: "separator" },
|
|
184
|
+
{
|
|
185
|
+
label: "Browse STH Disc Archive...",
|
|
186
|
+
click: showModal("sth", { sthType: "discs" }),
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
label: "Browse Example Discs...",
|
|
190
|
+
click: showModal("discs"),
|
|
191
|
+
},
|
|
192
|
+
{ type: "separator" },
|
|
193
|
+
{
|
|
194
|
+
label: "Load Tape from File...",
|
|
195
|
+
click: makeTapeLoader(),
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
label: "Browse STH Tape Archive...",
|
|
199
|
+
click: showModal("sth", { sthType: "tapes" }),
|
|
200
|
+
},
|
|
201
|
+
{ type: "separator" },
|
|
138
202
|
isMac ? { role: "close" } : { role: "quit" },
|
|
139
203
|
],
|
|
140
204
|
},
|
|
@@ -158,9 +222,36 @@ const template = [
|
|
|
158
222
|
{ role: "togglefullscreen" },
|
|
159
223
|
],
|
|
160
224
|
},
|
|
225
|
+
{
|
|
226
|
+
label: "Machine",
|
|
227
|
+
submenu: [
|
|
228
|
+
{
|
|
229
|
+
label: "Configuration...",
|
|
230
|
+
click: showModal("configuration"),
|
|
231
|
+
},
|
|
232
|
+
{ type: "separator" },
|
|
233
|
+
{
|
|
234
|
+
label: "Soft Reset",
|
|
235
|
+
click: sendAction("soft-reset"),
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
label: "Hard Reset",
|
|
239
|
+
click: sendAction("hard-reset"),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
},
|
|
161
243
|
{
|
|
162
244
|
role: "help",
|
|
163
245
|
submenu: [
|
|
246
|
+
{
|
|
247
|
+
label: "Help...",
|
|
248
|
+
click: showModal("help"),
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
label: "About jsbeeb...",
|
|
252
|
+
click: showModal("info"),
|
|
253
|
+
},
|
|
254
|
+
{ type: "separator" },
|
|
164
255
|
{
|
|
165
256
|
label: "Learn More",
|
|
166
257
|
click: async () => {
|
package/src/app/electron.js
CHANGED
|
@@ -1,20 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
// Electron integration for jsbeeb desktop application.
|
|
4
|
-
// Handles IPC communication for loading disc images from Electron's main process.
|
|
4
|
+
// Handles IPC communication for loading disc/tape images and showing modals from Electron's main process.
|
|
5
5
|
|
|
6
6
|
export let initialise = function () {};
|
|
7
7
|
|
|
8
8
|
function init(args) {
|
|
9
|
-
const { loadDiscImage, processor } = args;
|
|
10
|
-
const
|
|
11
|
-
|
|
9
|
+
const { loadDiscImage, loadTapeImage, processor, modals, actions, config } = args;
|
|
10
|
+
const api = window.electronAPI;
|
|
11
|
+
|
|
12
|
+
api.onLoadDisc(async (message) => {
|
|
12
13
|
const { drive, path } = message;
|
|
13
14
|
const image = await loadDiscImage(path);
|
|
14
15
|
processor.fdc.loadDisc(drive, image);
|
|
15
16
|
});
|
|
17
|
+
|
|
18
|
+
api.onLoadTape(async (message) => {
|
|
19
|
+
const { path } = message;
|
|
20
|
+
const tape = await loadTapeImage(path);
|
|
21
|
+
processor.acia.setTape(tape);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
api.onShowModal((message) => {
|
|
25
|
+
const { modalId, sthType } = message;
|
|
26
|
+
if (modals && modals.show) {
|
|
27
|
+
modals.show(modalId, sthType);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
api.onAction((message) => {
|
|
32
|
+
if (actions && actions[message.actionId]) {
|
|
33
|
+
actions[message.actionId]();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Observe model name changes and update window title
|
|
38
|
+
const modelElement = document.querySelector(".bbc-model");
|
|
39
|
+
if (modelElement) {
|
|
40
|
+
const updateTitle = () => api.setTitle(`jsbeeb - ${modelElement.textContent}`);
|
|
41
|
+
updateTitle();
|
|
42
|
+
new MutationObserver(updateTitle).observe(modelElement, {
|
|
43
|
+
childList: true,
|
|
44
|
+
characterData: true,
|
|
45
|
+
subtree: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Save settings when they change
|
|
50
|
+
if (config) {
|
|
51
|
+
config.on("settings-changed", (settings) => {
|
|
52
|
+
api.saveSettings(settings);
|
|
53
|
+
});
|
|
54
|
+
config.on("media-changed", (media) => {
|
|
55
|
+
api.saveSettings(media);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
16
58
|
}
|
|
17
59
|
|
|
18
|
-
if (typeof window.
|
|
60
|
+
if (typeof window.electronAPI !== "undefined") {
|
|
19
61
|
initialise = init;
|
|
20
62
|
}
|
package/src/app/preload.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
const { contextBridge, ipcRenderer } = require("electron");
|
|
3
|
+
|
|
4
|
+
// Expose a secure API to the renderer
|
|
5
|
+
contextBridge.exposeInMainWorld("electronAPI", {
|
|
6
|
+
onLoadDisc: (callback) => ipcRenderer.on("load", (event, message) => callback(message)),
|
|
7
|
+
onLoadTape: (callback) => ipcRenderer.on("load-tape", (event, message) => callback(message)),
|
|
8
|
+
onShowModal: (callback) => ipcRenderer.on("show-modal", (event, message) => callback(message)),
|
|
9
|
+
onAction: (callback) => ipcRenderer.on("action", (event, message) => callback(message)),
|
|
10
|
+
setTitle: (title) => ipcRenderer.send("set-title", title),
|
|
11
|
+
saveSettings: (settings) => ipcRenderer.send("save-settings", settings),
|
|
12
|
+
});
|
|
3
13
|
|
|
4
14
|
window.addEventListener("DOMContentLoaded", () => {
|
|
5
15
|
for (const node of document.getElementsByClassName("not-electron")) {
|
package/src/config.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
import $ from "jquery";
|
|
3
|
+
import EventEmitter from "event-emitter-es6";
|
|
3
4
|
import { findModel } from "./models.js";
|
|
4
5
|
import { getFilterForMode } from "./canvas.js";
|
|
5
6
|
|
|
6
|
-
export class Config {
|
|
7
|
+
export class Config extends EventEmitter {
|
|
7
8
|
constructor(onChange, onClose) {
|
|
9
|
+
super();
|
|
8
10
|
this.onChange = onChange;
|
|
9
11
|
this.onClose = onClose;
|
|
10
12
|
this.changed = {};
|
|
@@ -20,7 +22,12 @@ export class Config {
|
|
|
20
22
|
this.setEconet(this.model.hasEconet);
|
|
21
23
|
});
|
|
22
24
|
|
|
23
|
-
$configuration.addEventListener("hide.bs.modal", () =>
|
|
25
|
+
$configuration.addEventListener("hide.bs.modal", () => {
|
|
26
|
+
this.onClose(this.changed);
|
|
27
|
+
if (Object.keys(this.changed).length > 0) {
|
|
28
|
+
this.emit("settings-changed", this.changed);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
24
31
|
|
|
25
32
|
$(".model-menu a").on("click", (e) => {
|
|
26
33
|
this.changed.model = $(e.target).attr("data-target");
|
package/src/disc.js
CHANGED
|
@@ -343,7 +343,7 @@ class Sector {
|
|
|
343
343
|
let sectorSize = Sector.toSectorSize(sectorEndByte - sectorStartByte - 5);
|
|
344
344
|
|
|
345
345
|
this.hasDataCrcError = true;
|
|
346
|
-
let seenIffyData
|
|
346
|
+
let seenIffyData;
|
|
347
347
|
do {
|
|
348
348
|
const { crcOk, sectorData, iffyPulses } = this._tryLoadSectorData(dataMarker, sectorSize);
|
|
349
349
|
seenIffyData = iffyPulses;
|
|
@@ -425,7 +425,7 @@ class Track {
|
|
|
425
425
|
const top32of64b = 0xffffffff00000000n;
|
|
426
426
|
const fmMarker = 0x8888888800000000n;
|
|
427
427
|
const mfmMarker = 0xaaaa448944894489n;
|
|
428
|
-
let dataByte
|
|
428
|
+
let dataByte;
|
|
429
429
|
let sector = null;
|
|
430
430
|
for (let pulseIndex = 0; pulseIndex < bitLength; ++pulseIndex) {
|
|
431
431
|
if ((pulseIndex & 31) === 0) pulses = this.pulses2Us[pulseIndex >>> 5];
|
package/src/filestore.js
CHANGED