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,1411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import * as utils from "./utils.js";
|
|
3
|
+
|
|
4
|
+
const hexword = utils.hexword;
|
|
5
|
+
const hexbyte = utils.hexbyte;
|
|
6
|
+
const signExtend = utils.signExtend;
|
|
7
|
+
|
|
8
|
+
function rotate(left, logical) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
if (!left) {
|
|
11
|
+
if (!logical) lines.push("const newTopBit = cpu.p.c ? 0x80 : 0x00;");
|
|
12
|
+
lines.push("cpu.p.c = !!(REG & 0x01);");
|
|
13
|
+
if (logical) {
|
|
14
|
+
lines.push("REG >>>= 1;");
|
|
15
|
+
} else {
|
|
16
|
+
lines.push("REG = (REG >>> 1) | newTopBit;");
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
if (!logical) lines.push("const newBotBit = cpu.p.c ? 0x01 : 0x00;");
|
|
20
|
+
lines.push("cpu.p.c = !!(REG & 0x80);");
|
|
21
|
+
if (logical) {
|
|
22
|
+
lines.push("REG = (REG << 1) & 0xff;");
|
|
23
|
+
} else {
|
|
24
|
+
lines.push("REG = ((REG << 1) & 0xff) | newBotBit;");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
lines.push("cpu.p.setzn(REG);");
|
|
28
|
+
return lines;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function pull(reg) {
|
|
32
|
+
if (reg === "p") {
|
|
33
|
+
return ["cpu.p.setFromByte(cpu.pull());"];
|
|
34
|
+
}
|
|
35
|
+
return [`cpu.${reg} = cpu.p.setzn(cpu.pull());`];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function push(reg) {
|
|
39
|
+
if (reg === "p") return "cpu.push(cpu.p.asByte());";
|
|
40
|
+
return `cpu.push(cpu.${reg});`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class InstructionGen {
|
|
44
|
+
constructor(is65c12) {
|
|
45
|
+
this.is65c12 = is65c12;
|
|
46
|
+
this.ops = {};
|
|
47
|
+
this.cycle = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
appendOrPrepend(combiner, cycle, op, exact, addr) {
|
|
51
|
+
if (op === undefined) {
|
|
52
|
+
op = cycle;
|
|
53
|
+
cycle = this.cycle;
|
|
54
|
+
}
|
|
55
|
+
exact = exact || false;
|
|
56
|
+
if (typeof op === "string") op = [op];
|
|
57
|
+
if (this.ops[cycle]) {
|
|
58
|
+
this.ops[cycle].op = combiner(this.ops[cycle].op, op);
|
|
59
|
+
if (exact) this.ops[cycle].exact = true;
|
|
60
|
+
if (!this.ops[cycle].addr) this.ops[cycle].addr = addr;
|
|
61
|
+
} else this.ops[cycle] = { op: op, exact: exact, addr: addr };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
append(cycle, op, exact, addr) {
|
|
65
|
+
this.appendOrPrepend((lhs, rhs) => lhs.concat(rhs), cycle, op, exact, addr);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
prepend(cycle, op, exact, addr) {
|
|
69
|
+
this.appendOrPrepend((lhs, rhs) => rhs.concat(lhs), cycle, op, exact, addr);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
tick(cycles) {
|
|
73
|
+
this.cycle += cycles || 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
readOp(addr, reg, spurious) {
|
|
77
|
+
this.cycle++;
|
|
78
|
+
let op;
|
|
79
|
+
if (reg) op = `${reg} = cpu.readmem(${addr});`;
|
|
80
|
+
else op = `cpu.readmem(${addr});`;
|
|
81
|
+
if (spurious) op += " // spurious";
|
|
82
|
+
this.append(this.cycle, op, true, addr);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
writeOp(addr, reg, spurious) {
|
|
86
|
+
this.cycle++;
|
|
87
|
+
let op = `cpu.writemem(${addr}, ${reg});`;
|
|
88
|
+
if (spurious) op += " // spurious";
|
|
89
|
+
this.append(this.cycle, op, true, addr);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
zpReadOp(addr, reg) {
|
|
93
|
+
this.cycle++;
|
|
94
|
+
this.append(this.cycle, `${reg} = cpu.readmemZpStack(${addr});`, false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
zpWriteOp(addr, reg) {
|
|
98
|
+
this.cycle++;
|
|
99
|
+
this.append(this.cycle, `cpu.writememZpStack(${addr}, ${reg});`, true);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render(startCycle) {
|
|
103
|
+
if (this.cycle < 2) this.cycle = 2;
|
|
104
|
+
this.prepend(this.cycle - 1, "cpu.checkInt();", true);
|
|
105
|
+
return this.renderInternal(startCycle);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
spuriousOp(addr, reg) {
|
|
109
|
+
if (this.is65c12) {
|
|
110
|
+
this.readOp(addr, "", true);
|
|
111
|
+
} else {
|
|
112
|
+
this.writeOp(addr, reg, true);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
renderInternal(startCycle) {
|
|
117
|
+
startCycle = startCycle || 0;
|
|
118
|
+
let toSkip = 0;
|
|
119
|
+
let out = [];
|
|
120
|
+
for (let i = startCycle; i < this.cycle; ++i) {
|
|
121
|
+
if (!this.ops[i]) {
|
|
122
|
+
toSkip++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (toSkip && this.ops[i].exact) {
|
|
126
|
+
if (this.ops[i].addr) {
|
|
127
|
+
out.push(`cpu.polltimeAddr(${toSkip}, ${this.ops[i].addr});`);
|
|
128
|
+
} else {
|
|
129
|
+
out.push(`cpu.polltime(${toSkip});`);
|
|
130
|
+
}
|
|
131
|
+
toSkip = 0;
|
|
132
|
+
}
|
|
133
|
+
out = out.concat(this.ops[i].op);
|
|
134
|
+
toSkip++;
|
|
135
|
+
}
|
|
136
|
+
if (toSkip) {
|
|
137
|
+
if (this.ops[this.cycle] && this.ops[this.cycle].addr) {
|
|
138
|
+
out.push(`cpu.polltimeAddr(${toSkip}, ${this.ops[this.cycle].addr});`);
|
|
139
|
+
} else {
|
|
140
|
+
out.push(`cpu.polltime(${toSkip});`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (this.ops[this.cycle]) out = out.concat(this.ops[this.cycle].op);
|
|
144
|
+
return out.filter((l) => l);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
split(condition) {
|
|
148
|
+
return new SplitInstruction(this, condition, this.is65c12);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class SplitInstruction {
|
|
153
|
+
constructor(preamble, condition, is65c12) {
|
|
154
|
+
this.preamble = preamble;
|
|
155
|
+
this.condition = condition;
|
|
156
|
+
this.ifTrue = new InstructionGen(is65c12);
|
|
157
|
+
this.ifTrue.tick(preamble.cycle);
|
|
158
|
+
this.ifFalse = new InstructionGen(is65c12);
|
|
159
|
+
this.ifFalse.tick(preamble.cycle);
|
|
160
|
+
|
|
161
|
+
["append", "prepend", "readOp", "writeOp", "spuriousOp"].forEach((op) => {
|
|
162
|
+
this[op] = (...args) => {
|
|
163
|
+
this.ifTrue[op](...args);
|
|
164
|
+
this.ifFalse[op](...args);
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
indent(lines) {
|
|
170
|
+
return lines.map((line) => ` ${line}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
render() {
|
|
174
|
+
return this.preamble
|
|
175
|
+
.renderInternal()
|
|
176
|
+
.concat(`if (${this.condition}) {`)
|
|
177
|
+
.concat(this.indent(this.ifTrue.render(this.preamble.cycle)))
|
|
178
|
+
.concat("} else {")
|
|
179
|
+
.concat(this.indent(this.ifFalse.render(this.preamble.cycle)))
|
|
180
|
+
.concat("}");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getOp(op, arg) {
|
|
185
|
+
switch (op) {
|
|
186
|
+
case "NOP":
|
|
187
|
+
return { op: "", read: arg !== undefined };
|
|
188
|
+
case "BRK":
|
|
189
|
+
return { op: "cpu.brk(false);" };
|
|
190
|
+
case "CLC":
|
|
191
|
+
return { op: "cpu.p.c = false;" };
|
|
192
|
+
case "SEC":
|
|
193
|
+
return { op: "cpu.p.c = true;" };
|
|
194
|
+
case "CLD":
|
|
195
|
+
return { op: "cpu.p.d = false;" };
|
|
196
|
+
case "SED":
|
|
197
|
+
return { op: "cpu.p.d = true;" };
|
|
198
|
+
case "CLI":
|
|
199
|
+
return { op: "cpu.p.i = false;" };
|
|
200
|
+
case "SEI":
|
|
201
|
+
return { op: "cpu.p.i = true;" };
|
|
202
|
+
case "CLV":
|
|
203
|
+
return { op: "cpu.p.v = false;" };
|
|
204
|
+
case "LDA":
|
|
205
|
+
return { op: ["cpu.a = cpu.p.setzn(REG);"], read: true };
|
|
206
|
+
case "LDX":
|
|
207
|
+
return { op: ["cpu.x = cpu.p.setzn(REG);"], read: true };
|
|
208
|
+
case "LDY":
|
|
209
|
+
return { op: ["cpu.y = cpu.p.setzn(REG);"], read: true };
|
|
210
|
+
case "STA":
|
|
211
|
+
return { op: "REG = cpu.a;", write: true };
|
|
212
|
+
case "STX":
|
|
213
|
+
return { op: "REG = cpu.x;", write: true };
|
|
214
|
+
case "STY":
|
|
215
|
+
return { op: "REG = cpu.y;", write: true };
|
|
216
|
+
case "INC":
|
|
217
|
+
return {
|
|
218
|
+
op: ["REG = cpu.p.setzn(REG + 1);"],
|
|
219
|
+
read: true,
|
|
220
|
+
write: true,
|
|
221
|
+
};
|
|
222
|
+
case "DEC":
|
|
223
|
+
return {
|
|
224
|
+
op: ["REG = cpu.p.setzn(REG - 1);"],
|
|
225
|
+
read: true,
|
|
226
|
+
write: true,
|
|
227
|
+
};
|
|
228
|
+
case "INX":
|
|
229
|
+
return { op: ["cpu.x = cpu.p.setzn(cpu.x + 1);"] };
|
|
230
|
+
case "INY":
|
|
231
|
+
return { op: ["cpu.y = cpu.p.setzn(cpu.y + 1);"] };
|
|
232
|
+
case "DEX":
|
|
233
|
+
return { op: ["cpu.x = cpu.p.setzn(cpu.x - 1);"] };
|
|
234
|
+
case "DEY":
|
|
235
|
+
return { op: ["cpu.y = cpu.p.setzn(cpu.y - 1);"] };
|
|
236
|
+
case "ADC":
|
|
237
|
+
return { op: "cpu.adc(REG);", read: true };
|
|
238
|
+
case "SBC":
|
|
239
|
+
return { op: "cpu.sbc(REG);", read: true };
|
|
240
|
+
case "BIT":
|
|
241
|
+
if (arg === "imm") {
|
|
242
|
+
// According to: http://forum.6502.org/viewtopic.php?f=2&t=2241&p=27243#p27239
|
|
243
|
+
// the v and n flags are unaffected by BIT #xx
|
|
244
|
+
return { op: "cpu.p.z = !(cpu.a & REG);", read: true };
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
op: ["cpu.p.z = !(cpu.a & REG);", "cpu.p.v = !!(REG & 0x40);", "cpu.p.n = !!(REG & 0x80);"],
|
|
248
|
+
read: true,
|
|
249
|
+
};
|
|
250
|
+
case "ROL":
|
|
251
|
+
return { op: rotate(true, false), read: true, write: true, rotate: true };
|
|
252
|
+
case "ROR":
|
|
253
|
+
return {
|
|
254
|
+
op: rotate(false, false),
|
|
255
|
+
read: true,
|
|
256
|
+
write: true,
|
|
257
|
+
rotate: true,
|
|
258
|
+
};
|
|
259
|
+
case "ASL":
|
|
260
|
+
return { op: rotate(true, true), read: true, write: true, rotate: true };
|
|
261
|
+
case "LSR":
|
|
262
|
+
return { op: rotate(false, true), read: true, write: true, rotate: true };
|
|
263
|
+
case "EOR":
|
|
264
|
+
return { op: ["cpu.a = cpu.p.setzn(cpu.a ^ REG);"], read: true };
|
|
265
|
+
case "AND":
|
|
266
|
+
return { op: ["cpu.a = cpu.p.setzn(cpu.a & REG);"], read: true };
|
|
267
|
+
case "ORA":
|
|
268
|
+
return { op: ["cpu.a = cpu.p.setzn(cpu.a | REG);"], read: true };
|
|
269
|
+
case "CMP":
|
|
270
|
+
return {
|
|
271
|
+
op: ["cpu.p.setzn(cpu.a - REG);", "cpu.p.c = cpu.a >= REG;"],
|
|
272
|
+
read: true,
|
|
273
|
+
};
|
|
274
|
+
case "CPX":
|
|
275
|
+
return {
|
|
276
|
+
op: ["cpu.p.setzn(cpu.x - REG);", "cpu.p.c = cpu.x >= REG;"],
|
|
277
|
+
read: true,
|
|
278
|
+
};
|
|
279
|
+
case "CPY":
|
|
280
|
+
return {
|
|
281
|
+
op: ["cpu.p.setzn(cpu.y - REG);", "cpu.p.c = cpu.y >= REG;"],
|
|
282
|
+
read: true,
|
|
283
|
+
};
|
|
284
|
+
case "TXA":
|
|
285
|
+
return { op: ["cpu.a = cpu.p.setzn(cpu.x);"] };
|
|
286
|
+
case "TAX":
|
|
287
|
+
return { op: ["cpu.x = cpu.p.setzn(cpu.a);"] };
|
|
288
|
+
case "TXS":
|
|
289
|
+
return { op: "cpu.s = cpu.x;" };
|
|
290
|
+
case "TSX":
|
|
291
|
+
return { op: ["cpu.x = cpu.p.setzn(cpu.s);"] };
|
|
292
|
+
case "TYA":
|
|
293
|
+
return { op: ["cpu.a = cpu.p.setzn(cpu.y);"] };
|
|
294
|
+
case "TAY":
|
|
295
|
+
return { op: ["cpu.y = cpu.p.setzn(cpu.a);"] };
|
|
296
|
+
case "BEQ":
|
|
297
|
+
return { op: "cpu.branch(cpu.p.z);" };
|
|
298
|
+
case "BNE":
|
|
299
|
+
return { op: "cpu.branch(!cpu.p.z);" };
|
|
300
|
+
case "BCS":
|
|
301
|
+
return { op: "cpu.branch(cpu.p.c);" };
|
|
302
|
+
case "BCC":
|
|
303
|
+
return { op: "cpu.branch(!cpu.p.c);" };
|
|
304
|
+
case "BMI":
|
|
305
|
+
return { op: "cpu.branch(cpu.p.n);" };
|
|
306
|
+
case "BPL":
|
|
307
|
+
return { op: "cpu.branch(!cpu.p.n);" };
|
|
308
|
+
case "BVS":
|
|
309
|
+
return { op: "cpu.branch(cpu.p.v);" };
|
|
310
|
+
case "BVC":
|
|
311
|
+
return { op: "cpu.branch(!cpu.p.v);" };
|
|
312
|
+
case "PLA":
|
|
313
|
+
return { op: pull("a"), extra: 3 };
|
|
314
|
+
case "PLP":
|
|
315
|
+
return { op: pull("p"), extra: 3 };
|
|
316
|
+
case "PLX":
|
|
317
|
+
return { op: pull("x"), extra: 3 };
|
|
318
|
+
case "PLY":
|
|
319
|
+
return { op: pull("y"), extra: 3 };
|
|
320
|
+
case "PHA":
|
|
321
|
+
return { op: push("a"), extra: 2 };
|
|
322
|
+
case "PHP":
|
|
323
|
+
return { op: push("p"), extra: 2 };
|
|
324
|
+
case "PHX":
|
|
325
|
+
return { op: push("x"), extra: 2 };
|
|
326
|
+
case "PHY":
|
|
327
|
+
return { op: push("y"), extra: 2 };
|
|
328
|
+
case "RTS":
|
|
329
|
+
return {
|
|
330
|
+
op: ["let temp = cpu.pull();", "temp |= cpu.pull() << 8;", "cpu.pc = (temp + 1) & 0xffff;"],
|
|
331
|
+
extra: 5,
|
|
332
|
+
};
|
|
333
|
+
case "RTI":
|
|
334
|
+
return {
|
|
335
|
+
preop: [
|
|
336
|
+
"let temp = cpu.pull();",
|
|
337
|
+
"cpu.p.c = !!(temp & 0x01);",
|
|
338
|
+
"cpu.p.z = !!(temp & 0x02);",
|
|
339
|
+
"cpu.p.i = !!(temp & 0x04);",
|
|
340
|
+
"cpu.p.d = !!(temp & 0x08);",
|
|
341
|
+
"cpu.p.v = !!(temp & 0x40);",
|
|
342
|
+
"cpu.p.n = !!(temp & 0x80);",
|
|
343
|
+
"temp = cpu.pull();",
|
|
344
|
+
"cpu.pc = temp | (cpu.pull() << 8);",
|
|
345
|
+
],
|
|
346
|
+
extra: 5,
|
|
347
|
+
};
|
|
348
|
+
case "JSR":
|
|
349
|
+
return {
|
|
350
|
+
op: [
|
|
351
|
+
"const pushAddr = cpu.pc - 1;",
|
|
352
|
+
"cpu.push(pushAddr >>> 8);",
|
|
353
|
+
"cpu.push(pushAddr & 0xff);",
|
|
354
|
+
"cpu.pc = addr;",
|
|
355
|
+
],
|
|
356
|
+
extra: 3,
|
|
357
|
+
};
|
|
358
|
+
case "JMP":
|
|
359
|
+
return { op: "cpu.pc = addr;" };
|
|
360
|
+
|
|
361
|
+
// 65c12 opcodes
|
|
362
|
+
case "TSB":
|
|
363
|
+
return {
|
|
364
|
+
op: ["cpu.p.z = !(REG & cpu.a);", "REG |= cpu.a;"],
|
|
365
|
+
read: true,
|
|
366
|
+
write: true,
|
|
367
|
+
};
|
|
368
|
+
case "TRB":
|
|
369
|
+
return {
|
|
370
|
+
op: ["cpu.p.z = !(REG & cpu.a);", "REG &= ~cpu.a;"],
|
|
371
|
+
read: true,
|
|
372
|
+
write: true,
|
|
373
|
+
};
|
|
374
|
+
case "BRA":
|
|
375
|
+
return { op: "cpu.branch(true);" };
|
|
376
|
+
case "STZ":
|
|
377
|
+
return { op: "REG = 0;", write: true };
|
|
378
|
+
|
|
379
|
+
// Undocumented opcodes.
|
|
380
|
+
// first 3 used by Zalaga, http://stardot.org.uk/forums/viewtopic.php?f=2&t=3584&p=30514
|
|
381
|
+
|
|
382
|
+
case "SAX": // stores (A AND X)
|
|
383
|
+
return { op: "REG = cpu.a & cpu.x;", write: true };
|
|
384
|
+
case "ASR": // aka ALR equivalent to AND #&AA:LSR A
|
|
385
|
+
return {
|
|
386
|
+
op: ["REG &= cpu.a;"].concat(rotate(false, true)).concat(["cpu.a = REG;"]),
|
|
387
|
+
};
|
|
388
|
+
case "SLO": // equivalent to ASL zp:ORA zp
|
|
389
|
+
return {
|
|
390
|
+
op: rotate(true, true).concat(["cpu.a |= REG;", "cpu.p.setzn(cpu.a);"]),
|
|
391
|
+
read: true,
|
|
392
|
+
write: true,
|
|
393
|
+
};
|
|
394
|
+
case "SHX":
|
|
395
|
+
return { op: "REG = (cpu.x & ((addr >>> 8)+1)) & 0xff;", write: true, zpQuirk: true };
|
|
396
|
+
case "SHY":
|
|
397
|
+
return { op: "REG = (cpu.y & ((addr >>> 8)+1)) & 0xff;", write: true, zpQuirk: true };
|
|
398
|
+
case "LAX": // NB uses the c64 value for the magic in the OR here. I don't know what would happen on a beeb.
|
|
399
|
+
return {
|
|
400
|
+
op: ["const magic = 0xff;", "cpu.a = cpu.x = cpu.p.setzn((cpu.a|magic) & REG);"],
|
|
401
|
+
read: true,
|
|
402
|
+
};
|
|
403
|
+
case "LXA": // NB uses the c64 value for the magic in the OR here. I don't know what would happen on a beeb.
|
|
404
|
+
return {
|
|
405
|
+
op: ["const magic = 0xee;", "cpu.a = cpu.x = cpu.p.setzn((cpu.a|magic) & REG);"],
|
|
406
|
+
read: true,
|
|
407
|
+
};
|
|
408
|
+
case "SRE":
|
|
409
|
+
return {
|
|
410
|
+
op: rotate(false, true).concat(["cpu.a = cpu.p.setzn(cpu.a ^ REG);"]),
|
|
411
|
+
read: true,
|
|
412
|
+
write: true,
|
|
413
|
+
};
|
|
414
|
+
case "RLA":
|
|
415
|
+
return {
|
|
416
|
+
op: rotate(true, false).concat(["cpu.a = cpu.p.setzn(cpu.a & REG);"]),
|
|
417
|
+
read: true,
|
|
418
|
+
write: true,
|
|
419
|
+
};
|
|
420
|
+
case "ANC":
|
|
421
|
+
return {
|
|
422
|
+
op: ["cpu.a = cpu.p.setzn(cpu.a & REG); cpu.p.c = cpu.p.n;"],
|
|
423
|
+
read: true,
|
|
424
|
+
};
|
|
425
|
+
case "ANE":
|
|
426
|
+
return {
|
|
427
|
+
op: ["cpu.a = cpu.p.setzn((cpu.a | 0xee) & REG & cpu.x);"],
|
|
428
|
+
read: true,
|
|
429
|
+
};
|
|
430
|
+
case "ARR":
|
|
431
|
+
return { op: "cpu.arr(REG);", read: true };
|
|
432
|
+
case "DCP":
|
|
433
|
+
return {
|
|
434
|
+
op: ["REG = cpu.p.setzn(REG - 1);", "cpu.p.setzn(cpu.a - REG);", "cpu.p.c = cpu.a >= REG;"],
|
|
435
|
+
read: true,
|
|
436
|
+
write: true,
|
|
437
|
+
};
|
|
438
|
+
case "LAS":
|
|
439
|
+
return {
|
|
440
|
+
op: ["cpu.a = cpu.x = cpu.s = cpu.p.setzn(cpu.s & REG);"],
|
|
441
|
+
read: true,
|
|
442
|
+
};
|
|
443
|
+
case "RRA":
|
|
444
|
+
return {
|
|
445
|
+
op: rotate(false, false).concat(["cpu.adc(REG);"]),
|
|
446
|
+
read: true,
|
|
447
|
+
write: true,
|
|
448
|
+
};
|
|
449
|
+
case "SBX":
|
|
450
|
+
return {
|
|
451
|
+
op: ["const temp = cpu.a & cpu.x;", "cpu.p.c = temp >= REG;", "cpu.x = cpu.p.setzn(temp - REG);"],
|
|
452
|
+
read: true,
|
|
453
|
+
};
|
|
454
|
+
case "SHA":
|
|
455
|
+
return {
|
|
456
|
+
op: ["REG = cpu.a & cpu.x & ((addr >>> 8) + 1) & 0xff;"],
|
|
457
|
+
write: true,
|
|
458
|
+
zpQuirk: true,
|
|
459
|
+
};
|
|
460
|
+
case "SHS":
|
|
461
|
+
return {
|
|
462
|
+
op: ["cpu.s = cpu.a & cpu.x;", "REG = cpu.a & cpu.x & ((addr >>> 8) + 1) & 0xff;"],
|
|
463
|
+
write: true,
|
|
464
|
+
zpQuirk: true,
|
|
465
|
+
};
|
|
466
|
+
case "ISB":
|
|
467
|
+
return {
|
|
468
|
+
op: ["REG = (REG + 1) & 0xff;", "cpu.sbc(REG);"],
|
|
469
|
+
read: true,
|
|
470
|
+
write: true,
|
|
471
|
+
};
|
|
472
|
+
case "RMB0":
|
|
473
|
+
case "RMB1":
|
|
474
|
+
case "RMB2":
|
|
475
|
+
case "RMB3":
|
|
476
|
+
case "RMB4":
|
|
477
|
+
case "RMB5":
|
|
478
|
+
case "RMB6":
|
|
479
|
+
case "RMB7":
|
|
480
|
+
return {
|
|
481
|
+
op: [`REG = REG & (${~(1 << (op[3] - "0"))});`],
|
|
482
|
+
read: true,
|
|
483
|
+
write: true,
|
|
484
|
+
};
|
|
485
|
+
case "SMB0":
|
|
486
|
+
case "SMB1":
|
|
487
|
+
case "SMB2":
|
|
488
|
+
case "SMB3":
|
|
489
|
+
case "SMB4":
|
|
490
|
+
case "SMB5":
|
|
491
|
+
case "SMB6":
|
|
492
|
+
case "SMB7":
|
|
493
|
+
return {
|
|
494
|
+
op: [`REG = REG | ${1 << (op[3] - "0")};`],
|
|
495
|
+
read: true,
|
|
496
|
+
write: true,
|
|
497
|
+
};
|
|
498
|
+
case "BBR0":
|
|
499
|
+
case "BBR1":
|
|
500
|
+
case "BBR2":
|
|
501
|
+
case "BBR3":
|
|
502
|
+
case "BBR4":
|
|
503
|
+
case "BBR5":
|
|
504
|
+
case "BBR6":
|
|
505
|
+
case "BBR7":
|
|
506
|
+
return { op: `cpu.branch(!(REG & ${1 << (op[3] - "0")}));`, read: true };
|
|
507
|
+
case "BBS0":
|
|
508
|
+
case "BBS1":
|
|
509
|
+
case "BBS2":
|
|
510
|
+
case "BBS3":
|
|
511
|
+
case "BBS4":
|
|
512
|
+
case "BBS5":
|
|
513
|
+
case "BBS6":
|
|
514
|
+
case "BBS7":
|
|
515
|
+
return { op: `cpu.branch(REG & ${1 << (op[3] - "0")});`, read: true };
|
|
516
|
+
}
|
|
517
|
+
throw new Error(`Unrecognised operation '${op}'`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const opcodes6502 = {
|
|
521
|
+
0x00: "BRK",
|
|
522
|
+
0x01: "ORA (,x)",
|
|
523
|
+
0x03: "SLO (,x)",
|
|
524
|
+
0x04: "NOP zp",
|
|
525
|
+
0x05: "ORA zp",
|
|
526
|
+
0x06: "ASL zp",
|
|
527
|
+
0x07: "SLO zp",
|
|
528
|
+
0x08: "PHP",
|
|
529
|
+
0x09: "ORA imm",
|
|
530
|
+
0x0a: "ASL A",
|
|
531
|
+
0x0b: "ANC imm",
|
|
532
|
+
0x0c: "NOP abs",
|
|
533
|
+
0x0d: "ORA abs",
|
|
534
|
+
0x0e: "ASL abs",
|
|
535
|
+
0x0f: "SLO abs",
|
|
536
|
+
0x10: "BPL branch",
|
|
537
|
+
0x11: "ORA (),y",
|
|
538
|
+
0x13: "SLO (),y",
|
|
539
|
+
0x14: "NOP zp,x",
|
|
540
|
+
0x15: "ORA zp,x",
|
|
541
|
+
0x16: "ASL zp,x",
|
|
542
|
+
0x17: "SLO zp,x",
|
|
543
|
+
0x18: "CLC",
|
|
544
|
+
0x19: "ORA abs,y",
|
|
545
|
+
0x1a: "NOP",
|
|
546
|
+
0x1b: "SLO abs,y",
|
|
547
|
+
0x1c: "NOP abs,x",
|
|
548
|
+
0x1d: "ORA abs,x",
|
|
549
|
+
0x1e: "ASL abs,x",
|
|
550
|
+
0x1f: "SLO abs,x",
|
|
551
|
+
0x20: "JSR abs",
|
|
552
|
+
0x21: "AND (,x)",
|
|
553
|
+
0x23: "RLA (,x)",
|
|
554
|
+
0x24: "BIT zp",
|
|
555
|
+
0x25: "AND zp",
|
|
556
|
+
0x26: "ROL zp",
|
|
557
|
+
0x27: "RLA zp",
|
|
558
|
+
0x28: "PLP",
|
|
559
|
+
0x29: "AND imm",
|
|
560
|
+
0x2a: "ROL A",
|
|
561
|
+
0x2b: "ANC imm",
|
|
562
|
+
0x2c: "BIT abs",
|
|
563
|
+
0x2d: "AND abs",
|
|
564
|
+
0x2e: "ROL abs",
|
|
565
|
+
0x2f: "RLA abs",
|
|
566
|
+
0x30: "BMI branch",
|
|
567
|
+
0x31: "AND (),y",
|
|
568
|
+
0x33: "RLA (),y",
|
|
569
|
+
0x34: "NOP zp,x",
|
|
570
|
+
0x35: "AND zp,x",
|
|
571
|
+
0x36: "ROL zp,x",
|
|
572
|
+
0x37: "RLA zp,x",
|
|
573
|
+
0x38: "SEC",
|
|
574
|
+
0x39: "AND abs,y",
|
|
575
|
+
0x3a: "NOP",
|
|
576
|
+
0x3b: "RLA abs,y",
|
|
577
|
+
0x3c: "NOP abs,x",
|
|
578
|
+
0x3d: "AND abs,x",
|
|
579
|
+
0x3e: "ROL abs,x",
|
|
580
|
+
0x3f: "RLA abs,x",
|
|
581
|
+
0x40: "RTI",
|
|
582
|
+
0x41: "EOR (,x)",
|
|
583
|
+
0x43: "SRE (,x)",
|
|
584
|
+
0x44: "NOP zp",
|
|
585
|
+
0x45: "EOR zp",
|
|
586
|
+
0x46: "LSR zp",
|
|
587
|
+
0x47: "SRE zp",
|
|
588
|
+
0x48: "PHA",
|
|
589
|
+
0x49: "EOR imm",
|
|
590
|
+
0x4a: "LSR A",
|
|
591
|
+
0x4b: "ASR imm",
|
|
592
|
+
0x4c: "JMP abs",
|
|
593
|
+
0x4d: "EOR abs",
|
|
594
|
+
0x4e: "LSR abs",
|
|
595
|
+
0x4f: "SRE abs",
|
|
596
|
+
0x50: "BVC branch",
|
|
597
|
+
0x51: "EOR (),y",
|
|
598
|
+
0x53: "SRE (),y",
|
|
599
|
+
0x54: "NOP zp,x",
|
|
600
|
+
0x55: "EOR zp,x",
|
|
601
|
+
0x56: "LSR zp,x",
|
|
602
|
+
0x57: "SRE zp,x",
|
|
603
|
+
0x58: "CLI",
|
|
604
|
+
0x59: "EOR abs,y",
|
|
605
|
+
0x5a: "NOP",
|
|
606
|
+
0x5b: "SRE abs,y",
|
|
607
|
+
0x5c: "NOP abs,x",
|
|
608
|
+
0x5d: "EOR abs,x",
|
|
609
|
+
0x5e: "LSR abs,x",
|
|
610
|
+
0x5f: "SRE abs,x",
|
|
611
|
+
0x60: "RTS",
|
|
612
|
+
0x61: "ADC (,x)",
|
|
613
|
+
0x63: "RRA (,x)",
|
|
614
|
+
0x64: "NOP zp",
|
|
615
|
+
0x65: "ADC zp",
|
|
616
|
+
0x66: "ROR zp",
|
|
617
|
+
0x67: "RRA zp",
|
|
618
|
+
0x68: "PLA",
|
|
619
|
+
0x69: "ADC imm",
|
|
620
|
+
0x6a: "ROR A",
|
|
621
|
+
0x6b: "ARR imm",
|
|
622
|
+
0x6c: "JMP (abs)",
|
|
623
|
+
0x6d: "ADC abs",
|
|
624
|
+
0x6e: "ROR abs",
|
|
625
|
+
0x6f: "RRA abs",
|
|
626
|
+
0x70: "BVS branch",
|
|
627
|
+
0x71: "ADC (),y",
|
|
628
|
+
0x73: "RRA (),y",
|
|
629
|
+
0x74: "NOP zp,x",
|
|
630
|
+
0x75: "ADC zp,x",
|
|
631
|
+
0x76: "ROR zp,x",
|
|
632
|
+
0x77: "RRA zp,x",
|
|
633
|
+
0x78: "SEI",
|
|
634
|
+
0x79: "ADC abs,y",
|
|
635
|
+
0x7a: "NOP",
|
|
636
|
+
0x7b: "RRA abs,y",
|
|
637
|
+
0x7c: "NOP abs,x",
|
|
638
|
+
0x7d: "ADC abs,x",
|
|
639
|
+
0x7e: "ROR abs,x",
|
|
640
|
+
0x7f: "RRA abs,x",
|
|
641
|
+
0x80: "NOP imm",
|
|
642
|
+
0x81: "STA (,x)",
|
|
643
|
+
0x82: "NOP imm",
|
|
644
|
+
0x83: "SAX (,x)",
|
|
645
|
+
0x84: "STY zp",
|
|
646
|
+
0x85: "STA zp",
|
|
647
|
+
0x86: "STX zp",
|
|
648
|
+
0x87: "SAX zp",
|
|
649
|
+
0x88: "DEY",
|
|
650
|
+
0x89: "NOP imm",
|
|
651
|
+
0x8a: "TXA",
|
|
652
|
+
0x8b: "ANE imm",
|
|
653
|
+
0x8c: "STY abs",
|
|
654
|
+
0x8d: "STA abs",
|
|
655
|
+
0x8e: "STX abs",
|
|
656
|
+
0x8f: "SAX abs",
|
|
657
|
+
0x90: "BCC branch",
|
|
658
|
+
0x91: "STA (),y",
|
|
659
|
+
0x93: "SHA (),y",
|
|
660
|
+
0x94: "STY zp,x",
|
|
661
|
+
0x95: "STA zp,x",
|
|
662
|
+
0x96: "STX zp,y",
|
|
663
|
+
0x97: "SAX zp,y",
|
|
664
|
+
0x98: "TYA",
|
|
665
|
+
0x99: "STA abs,y",
|
|
666
|
+
0x9a: "TXS",
|
|
667
|
+
0x9b: "SHS abs,y",
|
|
668
|
+
0x9c: "SHY abs,x",
|
|
669
|
+
0x9d: "STA abs,x",
|
|
670
|
+
0x9e: "SHX abs,y",
|
|
671
|
+
0x9f: "SHA abs,y",
|
|
672
|
+
0xa0: "LDY imm",
|
|
673
|
+
0xa1: "LDA (,x)",
|
|
674
|
+
0xa2: "LDX imm",
|
|
675
|
+
0xa3: "LAX (,x)",
|
|
676
|
+
0xa4: "LDY zp",
|
|
677
|
+
0xa5: "LDA zp",
|
|
678
|
+
0xa6: "LDX zp",
|
|
679
|
+
0xa7: "LAX zp",
|
|
680
|
+
0xa8: "TAY",
|
|
681
|
+
0xa9: "LDA imm",
|
|
682
|
+
0xaa: "TAX",
|
|
683
|
+
0xab: "LXA imm",
|
|
684
|
+
0xac: "LDY abs",
|
|
685
|
+
0xad: "LDA abs",
|
|
686
|
+
0xae: "LDX abs",
|
|
687
|
+
0xaf: "LAX abs",
|
|
688
|
+
0xb0: "BCS branch",
|
|
689
|
+
0xb1: "LDA (),y",
|
|
690
|
+
0xb3: "LAX (),y",
|
|
691
|
+
0xb4: "LDY zp,x",
|
|
692
|
+
0xb5: "LDA zp,x",
|
|
693
|
+
0xb6: "LDX zp,y",
|
|
694
|
+
0xb7: "LAX zp,y",
|
|
695
|
+
0xb8: "CLV",
|
|
696
|
+
0xb9: "LDA abs,y",
|
|
697
|
+
0xba: "TSX",
|
|
698
|
+
0xbb: "LAS abs,y",
|
|
699
|
+
0xbc: "LDY abs,x",
|
|
700
|
+
0xbd: "LDA abs,x",
|
|
701
|
+
0xbe: "LDX abs,y",
|
|
702
|
+
0xbf: "LAX abs,y",
|
|
703
|
+
0xc0: "CPY imm",
|
|
704
|
+
0xc1: "CMP (,x)",
|
|
705
|
+
0xc2: "NOP imm",
|
|
706
|
+
0xc3: "DCP (,x)",
|
|
707
|
+
0xc4: "CPY zp",
|
|
708
|
+
0xc5: "CMP zp",
|
|
709
|
+
0xc6: "DEC zp",
|
|
710
|
+
0xc7: "DCP zp",
|
|
711
|
+
0xc8: "INY",
|
|
712
|
+
0xc9: "CMP imm",
|
|
713
|
+
0xca: "DEX",
|
|
714
|
+
0xcb: "SBX imm",
|
|
715
|
+
0xcc: "CPY abs",
|
|
716
|
+
0xcd: "CMP abs",
|
|
717
|
+
0xce: "DEC abs",
|
|
718
|
+
0xcf: "DCP abs",
|
|
719
|
+
0xd0: "BNE branch",
|
|
720
|
+
0xd1: "CMP (),y",
|
|
721
|
+
0xd3: "DCP (),y",
|
|
722
|
+
0xd4: "NOP zp,x",
|
|
723
|
+
0xd5: "CMP zp,x",
|
|
724
|
+
0xd6: "DEC zp,x",
|
|
725
|
+
0xd7: "DCP zp,x",
|
|
726
|
+
0xd8: "CLD",
|
|
727
|
+
0xd9: "CMP abs,y",
|
|
728
|
+
0xda: "NOP",
|
|
729
|
+
0xdb: "DCP abs,y",
|
|
730
|
+
0xdc: "NOP abs,x",
|
|
731
|
+
0xdd: "CMP abs,x",
|
|
732
|
+
0xde: "DEC abs,x",
|
|
733
|
+
0xdf: "DCP abs,x",
|
|
734
|
+
0xe0: "CPX imm",
|
|
735
|
+
0xe1: "SBC (,x)",
|
|
736
|
+
0xe2: "NOP imm",
|
|
737
|
+
0xe3: "ISB (,x)",
|
|
738
|
+
0xe4: "CPX zp",
|
|
739
|
+
0xe5: "SBC zp",
|
|
740
|
+
0xe6: "INC zp",
|
|
741
|
+
0xe7: "ISB zp",
|
|
742
|
+
0xe8: "INX",
|
|
743
|
+
0xe9: "SBC imm",
|
|
744
|
+
0xea: "NOP",
|
|
745
|
+
0xeb: "SBC imm",
|
|
746
|
+
0xec: "CPX abs",
|
|
747
|
+
0xed: "SBC abs",
|
|
748
|
+
0xee: "INC abs",
|
|
749
|
+
0xef: "ISB abs",
|
|
750
|
+
0xf0: "BEQ branch",
|
|
751
|
+
0xf1: "SBC (),y",
|
|
752
|
+
0xf3: "ISB (),y",
|
|
753
|
+
0xf4: "NOP zpx",
|
|
754
|
+
0xf5: "SBC zp,x",
|
|
755
|
+
0xf6: "INC zp,x",
|
|
756
|
+
0xf7: "ISB zp,x",
|
|
757
|
+
0xf8: "SED",
|
|
758
|
+
0xf9: "SBC abs,y",
|
|
759
|
+
0xfa: "NOP",
|
|
760
|
+
0xfb: "ISB abs,y",
|
|
761
|
+
0xfc: "NOP abs,x",
|
|
762
|
+
0xfd: "SBC abs,x",
|
|
763
|
+
0xfe: "INC abs,x",
|
|
764
|
+
0xff: "ISB abs,x",
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
const opcodes65c12 = {
|
|
768
|
+
0x00: "BRK",
|
|
769
|
+
0x01: "ORA (,x)",
|
|
770
|
+
0x04: "TSB zp",
|
|
771
|
+
0x05: "ORA zp",
|
|
772
|
+
0x06: "ASL zp",
|
|
773
|
+
0x08: "PHP",
|
|
774
|
+
0x09: "ORA imm",
|
|
775
|
+
0x0a: "ASL A",
|
|
776
|
+
0x0c: "TSB abs",
|
|
777
|
+
0x0d: "ORA abs",
|
|
778
|
+
0x0e: "ASL abs",
|
|
779
|
+
0x10: "BPL branch",
|
|
780
|
+
0x11: "ORA (),y",
|
|
781
|
+
0x12: "ORA ()",
|
|
782
|
+
0x14: "TRB zp",
|
|
783
|
+
0x15: "ORA zp,x",
|
|
784
|
+
0x16: "ASL zp,x",
|
|
785
|
+
0x18: "CLC",
|
|
786
|
+
0x19: "ORA abs,y",
|
|
787
|
+
0x1a: "INC A",
|
|
788
|
+
0x1c: "TRB abs",
|
|
789
|
+
0x1d: "ORA abs,x",
|
|
790
|
+
0x1e: "ASL abs,x",
|
|
791
|
+
0x20: "JSR abs",
|
|
792
|
+
0x21: "AND (,x)",
|
|
793
|
+
0x24: "BIT zp",
|
|
794
|
+
0x25: "AND zp",
|
|
795
|
+
0x26: "ROL zp",
|
|
796
|
+
0x28: "PLP",
|
|
797
|
+
0x29: "AND imm",
|
|
798
|
+
0x2a: "ROL A",
|
|
799
|
+
0x2c: "BIT abs",
|
|
800
|
+
0x2d: "AND abs",
|
|
801
|
+
0x2e: "ROL abs",
|
|
802
|
+
0x30: "BMI branch",
|
|
803
|
+
0x31: "AND (),y",
|
|
804
|
+
0x32: "AND ()",
|
|
805
|
+
0x34: "BIT zp,x",
|
|
806
|
+
0x35: "AND zp,x",
|
|
807
|
+
0x36: "ROL zp,x",
|
|
808
|
+
0x38: "SEC",
|
|
809
|
+
0x39: "AND abs,y",
|
|
810
|
+
0x3a: "DEC A",
|
|
811
|
+
0x3c: "BIT abs,x",
|
|
812
|
+
0x3d: "AND abs,x",
|
|
813
|
+
0x3e: "ROL abs,x",
|
|
814
|
+
0x40: "RTI",
|
|
815
|
+
0x41: "EOR (,x)",
|
|
816
|
+
0x45: "EOR zp",
|
|
817
|
+
0x46: "LSR zp",
|
|
818
|
+
0x48: "PHA",
|
|
819
|
+
0x49: "EOR imm",
|
|
820
|
+
0x4a: "LSR A",
|
|
821
|
+
0x4c: "JMP abs",
|
|
822
|
+
0x4d: "EOR abs",
|
|
823
|
+
0x4e: "LSR abs",
|
|
824
|
+
0x50: "BVC branch",
|
|
825
|
+
0x51: "EOR (),y",
|
|
826
|
+
0x52: "EOR ()",
|
|
827
|
+
0x55: "EOR zp,x",
|
|
828
|
+
0x56: "LSR zp,x",
|
|
829
|
+
0x58: "CLI",
|
|
830
|
+
0x59: "EOR abs,y",
|
|
831
|
+
0x5a: "PHY",
|
|
832
|
+
0x5d: "EOR abs,x",
|
|
833
|
+
0x5e: "LSR abs,x",
|
|
834
|
+
0x60: "RTS",
|
|
835
|
+
0x61: "ADC (,x)",
|
|
836
|
+
0x64: "STZ zp",
|
|
837
|
+
0x65: "ADC zp",
|
|
838
|
+
0x66: "ROR zp",
|
|
839
|
+
0x68: "PLA",
|
|
840
|
+
0x69: "ADC imm",
|
|
841
|
+
0x6a: "ROR A",
|
|
842
|
+
0x6c: "JMP (abs)",
|
|
843
|
+
0x6d: "ADC abs",
|
|
844
|
+
0x6e: "ROR abs",
|
|
845
|
+
0x70: "BVS branch",
|
|
846
|
+
0x71: "ADC (),y",
|
|
847
|
+
0x72: "ADC ()",
|
|
848
|
+
0x74: "STZ zp,x",
|
|
849
|
+
0x75: "ADC zp,x",
|
|
850
|
+
0x76: "ROR zp,x",
|
|
851
|
+
0x78: "SEI",
|
|
852
|
+
0x79: "ADC abs,y",
|
|
853
|
+
0x7a: "PLY",
|
|
854
|
+
0x7c: "JMP (abs,x)",
|
|
855
|
+
0x7d: "ADC abs,x",
|
|
856
|
+
0x7e: "ROR abs,x",
|
|
857
|
+
0x80: "BRA branch",
|
|
858
|
+
0x81: "STA (,x)",
|
|
859
|
+
0x84: "STY zp",
|
|
860
|
+
0x85: "STA zp",
|
|
861
|
+
0x86: "STX zp",
|
|
862
|
+
0x88: "DEY",
|
|
863
|
+
0x89: "BIT imm",
|
|
864
|
+
0x8a: "TXA",
|
|
865
|
+
0x8c: "STY abs",
|
|
866
|
+
0x8d: "STA abs",
|
|
867
|
+
0x8e: "STX abs",
|
|
868
|
+
0x90: "BCC branch",
|
|
869
|
+
0x91: "STA (),y",
|
|
870
|
+
0x92: "STA ()",
|
|
871
|
+
0x94: "STY zp,x",
|
|
872
|
+
0x95: "STA zp,x",
|
|
873
|
+
0x96: "STX zp,y",
|
|
874
|
+
0x98: "TYA",
|
|
875
|
+
0x99: "STA abs,y",
|
|
876
|
+
0x9a: "TXS",
|
|
877
|
+
0x9c: "STZ abs",
|
|
878
|
+
0x9d: "STA abs,x",
|
|
879
|
+
0x9e: "STZ abs,x",
|
|
880
|
+
0xa0: "LDY imm",
|
|
881
|
+
0xa1: "LDA (,x)",
|
|
882
|
+
0xa2: "LDX imm",
|
|
883
|
+
0xa4: "LDY zp",
|
|
884
|
+
0xa5: "LDA zp",
|
|
885
|
+
0xa6: "LDX zp",
|
|
886
|
+
0xa8: "TAY",
|
|
887
|
+
0xa9: "LDA imm",
|
|
888
|
+
0xaa: "TAX",
|
|
889
|
+
0xac: "LDY abs",
|
|
890
|
+
0xad: "LDA abs",
|
|
891
|
+
0xae: "LDX abs",
|
|
892
|
+
0xb0: "BCS branch",
|
|
893
|
+
0xb1: "LDA (),y",
|
|
894
|
+
0xb2: "LDA ()",
|
|
895
|
+
0xb4: "LDY zp,x",
|
|
896
|
+
0xb5: "LDA zp,x",
|
|
897
|
+
0xb6: "LDX zp,y",
|
|
898
|
+
0xb8: "CLV",
|
|
899
|
+
0xb9: "LDA abs,y",
|
|
900
|
+
0xba: "TSX",
|
|
901
|
+
0xbc: "LDY abs,x",
|
|
902
|
+
0xbd: "LDA abs,x",
|
|
903
|
+
0xbe: "LDX abs,y",
|
|
904
|
+
0xc0: "CPY imm",
|
|
905
|
+
0xc1: "CMP (,x)",
|
|
906
|
+
0xc4: "CPY zp",
|
|
907
|
+
0xc5: "CMP zp",
|
|
908
|
+
0xc6: "DEC zp",
|
|
909
|
+
0xc8: "INY",
|
|
910
|
+
0xc9: "CMP imm",
|
|
911
|
+
0xca: "DEX",
|
|
912
|
+
//0xCB: "WAI", // was "WAI" but testing by @tom-seddon indicate this isn't a 65c12 thing
|
|
913
|
+
0xcc: "CPY abs",
|
|
914
|
+
0xcd: "CMP abs",
|
|
915
|
+
0xce: "DEC abs",
|
|
916
|
+
0xd0: "BNE branch",
|
|
917
|
+
0xd1: "CMP (),y",
|
|
918
|
+
0xd2: "CMP ()",
|
|
919
|
+
0xd5: "CMP zp,x",
|
|
920
|
+
0xd6: "DEC zp,x",
|
|
921
|
+
0xd8: "CLD",
|
|
922
|
+
0xd9: "CMP abs,y",
|
|
923
|
+
0xda: "PHX",
|
|
924
|
+
0xdd: "CMP abs,x",
|
|
925
|
+
0xde: "DEC abs,x",
|
|
926
|
+
0xe0: "CPX imm",
|
|
927
|
+
0xe1: "SBC (,x)",
|
|
928
|
+
0xe4: "CPX zp",
|
|
929
|
+
0xe5: "SBC zp",
|
|
930
|
+
0xe6: "INC zp",
|
|
931
|
+
0xe8: "INX",
|
|
932
|
+
0xe9: "SBC imm",
|
|
933
|
+
0xea: "NOP",
|
|
934
|
+
0xec: "CPX abs",
|
|
935
|
+
0xed: "SBC abs",
|
|
936
|
+
0xee: "INC abs",
|
|
937
|
+
0xf0: "BEQ branch",
|
|
938
|
+
0xf1: "SBC (),y",
|
|
939
|
+
0xf2: "SBC ()",
|
|
940
|
+
0xf5: "SBC zp,x",
|
|
941
|
+
0xf6: "INC zp,x",
|
|
942
|
+
0xf8: "SED",
|
|
943
|
+
0xf9: "SBC abs,y",
|
|
944
|
+
0xfa: "PLX",
|
|
945
|
+
0xfd: "SBC abs,x",
|
|
946
|
+
0xfe: "INC abs,x",
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
const opcodes65c02 = {
|
|
950
|
+
...opcodes65c12,
|
|
951
|
+
0x07: "RMB0 zp",
|
|
952
|
+
0x17: "RMB1 zp",
|
|
953
|
+
0x27: "RMB2 zp",
|
|
954
|
+
0x37: "RMB3 zp",
|
|
955
|
+
0x47: "RMB4 zp",
|
|
956
|
+
0x57: "RMB5 zp",
|
|
957
|
+
0x67: "RMB6 zp",
|
|
958
|
+
0x77: "RMB7 zp",
|
|
959
|
+
0x87: "SMB0 zp",
|
|
960
|
+
0x97: "SMB1 zp",
|
|
961
|
+
0xa7: "SMB2 zp",
|
|
962
|
+
0xb7: "SMB3 zp",
|
|
963
|
+
0xc7: "SMB4 zp",
|
|
964
|
+
0xd7: "SMB5 zp",
|
|
965
|
+
0xe7: "SMB6 zp",
|
|
966
|
+
0xf7: "SMB7 zp",
|
|
967
|
+
0x0f: "BBR0 zp,branch",
|
|
968
|
+
0x1f: "BBR1 zp,branch",
|
|
969
|
+
0x2f: "BBR2 zp,branch",
|
|
970
|
+
0x3f: "BBR3 zp,branch",
|
|
971
|
+
0x4f: "BBR4 zp,branch",
|
|
972
|
+
0x5f: "BBR5 zp,branch",
|
|
973
|
+
0x6f: "BBR6 zp,branch",
|
|
974
|
+
0x7f: "BBR7 zp,branch",
|
|
975
|
+
0x8f: "BBS0 zp,branch",
|
|
976
|
+
0x9f: "BBS1 zp,branch",
|
|
977
|
+
0xaf: "BBS2 zp,branch",
|
|
978
|
+
0xbf: "BBS3 zp,branch",
|
|
979
|
+
0xcf: "BBS4 zp,branch",
|
|
980
|
+
0xdf: "BBS5 zp,branch",
|
|
981
|
+
0xef: "BBS6 zp,branch",
|
|
982
|
+
0xff: "BBS7 zp,branch",
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
class Disassemble6502 {
|
|
986
|
+
constructor(cpu, opcodes) {
|
|
987
|
+
this.cpu = cpu;
|
|
988
|
+
this.opcodes = opcodes;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
disassemble(addr, plain) {
|
|
992
|
+
let formatAddr = (addr) => `<span class="instr_mem_ref" data-ref="${addr}">${hexword(addr)}</span>`;
|
|
993
|
+
let formatJumpAddr = (addr) => `<span class="instr_instr_ref" data-ref="${addr}">${hexword(addr)}</span>`;
|
|
994
|
+
if (plain) {
|
|
995
|
+
formatAddr = hexword;
|
|
996
|
+
formatJumpAddr = hexword;
|
|
997
|
+
}
|
|
998
|
+
const opcode = this.opcodes[this.cpu.peekmem(addr)];
|
|
999
|
+
if (!opcode) {
|
|
1000
|
+
return ["???", addr + 1];
|
|
1001
|
+
}
|
|
1002
|
+
const split = opcode.split(" ");
|
|
1003
|
+
if (!split[1]) {
|
|
1004
|
+
return [opcode, addr + 1];
|
|
1005
|
+
}
|
|
1006
|
+
let param = split[1] || "";
|
|
1007
|
+
let suffix = "";
|
|
1008
|
+
let suffix2 = "";
|
|
1009
|
+
const index = param.match(/(.*),([xy])$/);
|
|
1010
|
+
if (index) {
|
|
1011
|
+
param = index[1];
|
|
1012
|
+
suffix = `,${index[2].toUpperCase()}`;
|
|
1013
|
+
suffix2 = ` + ${index[2].toUpperCase()}`;
|
|
1014
|
+
}
|
|
1015
|
+
switch (param) {
|
|
1016
|
+
case "imm":
|
|
1017
|
+
return [`${split[0]} #$${hexbyte(this.cpu.peekmem(addr + 1))}${suffix}`, addr + 2];
|
|
1018
|
+
case "abs": {
|
|
1019
|
+
const formatter = split[0] === "JMP" || split[0] === "JSR" ? formatJumpAddr : formatAddr;
|
|
1020
|
+
const destAddr = this.cpu.peekmem(addr + 1) | (this.cpu.peekmem(addr + 2) << 8);
|
|
1021
|
+
return [`${split[0]} $${formatter(destAddr)}${suffix}`, addr + 3, destAddr];
|
|
1022
|
+
}
|
|
1023
|
+
case "branch": {
|
|
1024
|
+
const destAddr = addr + signExtend(this.cpu.peekmem(addr + 1)) + 2;
|
|
1025
|
+
return [`${split[0]} $${formatJumpAddr(destAddr)}${suffix}`, addr + 2, destAddr];
|
|
1026
|
+
}
|
|
1027
|
+
case "zp":
|
|
1028
|
+
return [`${split[0]} $${hexbyte(this.cpu.peekmem(addr + 1))}${suffix}`, addr + 2];
|
|
1029
|
+
case "(,x)":
|
|
1030
|
+
return [`${split[0]} ($${hexbyte(this.cpu.peekmem(addr + 1))}, X)${suffix}`, addr + 2];
|
|
1031
|
+
case "()": {
|
|
1032
|
+
const zp = this.cpu.peekmem(addr + 1);
|
|
1033
|
+
const destAddr = this.cpu.peekmem(zp) | (this.cpu.peekmem(zp + 1) << 8);
|
|
1034
|
+
return [`${split[0]} ($${hexbyte(zp)})${suffix} ; $${utils.hexword(destAddr)}${suffix2}`, addr + 2];
|
|
1035
|
+
}
|
|
1036
|
+
case "(abs)": {
|
|
1037
|
+
const destAddr = this.cpu.peekmem(addr + 1) | (this.cpu.peekmem(addr + 2) << 8);
|
|
1038
|
+
const indDest = this.cpu.peekmem(destAddr) | (this.cpu.peekmem(destAddr + 1) << 8);
|
|
1039
|
+
return [
|
|
1040
|
+
`${split[0]} ($${formatJumpAddr(destAddr)})${suffix} ; $${utils.hexword(indDest)}${suffix2}`,
|
|
1041
|
+
addr + 3,
|
|
1042
|
+
indDest,
|
|
1043
|
+
];
|
|
1044
|
+
}
|
|
1045
|
+
case "(abs,x)": {
|
|
1046
|
+
const destAddr = this.cpu.peekmem(addr + 1) | (this.cpu.peekmem(addr + 2) << 8);
|
|
1047
|
+
const indDest = this.cpu.peekmem(destAddr) | (this.cpu.peekmem(destAddr + 1) << 8);
|
|
1048
|
+
return [`${split[0]} ($${formatJumpAddr(destAddr)},x)${suffix}`, addr + 3, indDest];
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return [opcode, addr + 1];
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function makeCpuFunctions(cpu, opcodes, is65c12) {
|
|
1056
|
+
function getInstruction(opcodeString, needsReg) {
|
|
1057
|
+
const split = opcodeString.split(" ");
|
|
1058
|
+
const opcode = split[0];
|
|
1059
|
+
const arg = split[1];
|
|
1060
|
+
const op = getOp(opcode, arg);
|
|
1061
|
+
if (!op) return null;
|
|
1062
|
+
|
|
1063
|
+
let ig = new InstructionGen(is65c12);
|
|
1064
|
+
if (needsReg) ig.append("let REG = 0|0;");
|
|
1065
|
+
|
|
1066
|
+
switch (arg) {
|
|
1067
|
+
case undefined:
|
|
1068
|
+
// Many of these ops need a little special casing.
|
|
1069
|
+
if (op.read || op.write) throw new Error(`Unsupported ${opcodeString}`);
|
|
1070
|
+
ig.append(op.preop);
|
|
1071
|
+
ig.tick(Math.max(2, 1 + (op.extra || 0)));
|
|
1072
|
+
ig.append(op.op);
|
|
1073
|
+
return ig.render();
|
|
1074
|
+
|
|
1075
|
+
case "branch":
|
|
1076
|
+
return [op.op]; // special cased here, would be nice to pull out of cpu
|
|
1077
|
+
|
|
1078
|
+
case "zp,branch":
|
|
1079
|
+
ig.tick(2);
|
|
1080
|
+
ig.append("const addr = cpu.getb() | 0;");
|
|
1081
|
+
if (op.read) {
|
|
1082
|
+
ig.zpReadOp("addr", "REG");
|
|
1083
|
+
if (op.write) {
|
|
1084
|
+
ig.tick(1); // Spurious write
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
ig.append(op.op);
|
|
1088
|
+
if (op.write) ig.zpWriteOp("addr", "REG");
|
|
1089
|
+
return ig.render();
|
|
1090
|
+
|
|
1091
|
+
case "zp":
|
|
1092
|
+
case "zpx": // Seems to be enough to keep tests happy, but needs investigation.
|
|
1093
|
+
case "zp,x":
|
|
1094
|
+
case "zp,y":
|
|
1095
|
+
if (arg === "zp") {
|
|
1096
|
+
ig.tick(2);
|
|
1097
|
+
ig.append("const addr = cpu.getb() | 0;");
|
|
1098
|
+
} else {
|
|
1099
|
+
ig.tick(3);
|
|
1100
|
+
ig.append(`const addr = (cpu.getb() + cpu.${arg[3]}) & 0xff;`);
|
|
1101
|
+
}
|
|
1102
|
+
if (op.read) {
|
|
1103
|
+
ig.zpReadOp("addr", "REG");
|
|
1104
|
+
if (op.write) {
|
|
1105
|
+
ig.tick(1); // Spurious write
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
ig.append(op.op);
|
|
1109
|
+
if (op.write) ig.zpWriteOp("addr", "REG");
|
|
1110
|
+
return ig.render();
|
|
1111
|
+
|
|
1112
|
+
case "abs":
|
|
1113
|
+
ig.tick(3 + (op.extra || 0));
|
|
1114
|
+
ig.append("const addr = cpu.getw() | 0;");
|
|
1115
|
+
if (op.read) {
|
|
1116
|
+
ig.readOp("addr", "REG");
|
|
1117
|
+
if (op.write) ig.spuriousOp("addr", "REG");
|
|
1118
|
+
}
|
|
1119
|
+
ig.append(op.op);
|
|
1120
|
+
if (op.write) ig.writeOp("addr", "REG");
|
|
1121
|
+
|
|
1122
|
+
return ig.render();
|
|
1123
|
+
|
|
1124
|
+
case "abs,x":
|
|
1125
|
+
case "abs,y":
|
|
1126
|
+
ig.append("const addr = cpu.getw() | 0;");
|
|
1127
|
+
ig.append(`let addrWithCarry = (addr + cpu.${arg[4]}) & 0xffff;`);
|
|
1128
|
+
ig.append("const addrNonCarry = (addr & 0xff00) | (addrWithCarry & 0xff);");
|
|
1129
|
+
ig.tick(3);
|
|
1130
|
+
ig = ig.split("addrWithCarry !== addrNonCarry");
|
|
1131
|
+
if (op.read && !op.write) {
|
|
1132
|
+
if (is65c12) {
|
|
1133
|
+
// the 65c12 reads the instruction byte again while it's carrying.
|
|
1134
|
+
ig.ifTrue.tick(1);
|
|
1135
|
+
} else {
|
|
1136
|
+
// the 6502 reads the uncarried address
|
|
1137
|
+
ig.ifTrue.readOp("addrNonCarry");
|
|
1138
|
+
}
|
|
1139
|
+
ig.readOp("addrWithCarry", "REG");
|
|
1140
|
+
} else if (op.read) {
|
|
1141
|
+
if (is65c12) {
|
|
1142
|
+
// RMWs on 65c12 burn a cycle reading the instruction byte again while carrying.
|
|
1143
|
+
ig.ifTrue.tick(1);
|
|
1144
|
+
// For anything but rotates, there's a bug: during carrying the CPU reads again.
|
|
1145
|
+
// beebjit reads the carried address, but I can't see how that could be the case,
|
|
1146
|
+
// so I read the non-carry here.
|
|
1147
|
+
if (!op.rotate) ig.ifFalse.readOp("addrNonCarry", "REG");
|
|
1148
|
+
ig.readOp("addrWithCarry", "REG");
|
|
1149
|
+
ig.writeOp("addrWithCarry", "REG");
|
|
1150
|
+
} else {
|
|
1151
|
+
// For RMW we always have a spurious read and then a spurious read or write
|
|
1152
|
+
ig.readOp("addrNonCarry");
|
|
1153
|
+
ig.readOp("addrWithCarry", "REG");
|
|
1154
|
+
ig.spuriousOp("addrWithCarry", "REG");
|
|
1155
|
+
}
|
|
1156
|
+
} else if (op.write) {
|
|
1157
|
+
if (is65c12) {
|
|
1158
|
+
// Discovered on Stardot: https://stardot.org.uk/forums/viewtopic.php?f=55&t=25298&sid=86e65177447d407aa6510f1f98efca87&start=30
|
|
1159
|
+
// With page crossing, the CPU re-reads the third byte of the instruction.
|
|
1160
|
+
ig.ifTrue.tick(1);
|
|
1161
|
+
// Without page crossing, the CPU still has the bug of reading the non-carried address(!)
|
|
1162
|
+
ig.ifFalse.readOp("addrNonCarry");
|
|
1163
|
+
} else {
|
|
1164
|
+
// Pure stores still exhibit a read at the non-carried address.
|
|
1165
|
+
ig.readOp("addrNonCarry");
|
|
1166
|
+
if (op.zpQuirk) {
|
|
1167
|
+
// with this quirk on undocumented instructions, a page crossing writes to 00XX
|
|
1168
|
+
ig.append("if (addrWithCarry !== addrNonCarry) addrWithCarry &= 0xff;");
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
ig.append(op.op);
|
|
1173
|
+
if (op.write) ig.writeOp("addrWithCarry", "REG");
|
|
1174
|
+
return ig.render();
|
|
1175
|
+
|
|
1176
|
+
case "imm":
|
|
1177
|
+
if (op.write) {
|
|
1178
|
+
throw new Error("This isn't possible");
|
|
1179
|
+
}
|
|
1180
|
+
if (op.read) {
|
|
1181
|
+
// NOP imm
|
|
1182
|
+
}
|
|
1183
|
+
ig.tick(2);
|
|
1184
|
+
ig.append("REG = cpu.getb() | 0;");
|
|
1185
|
+
ig.append(op.op);
|
|
1186
|
+
return ig.render();
|
|
1187
|
+
|
|
1188
|
+
case "A":
|
|
1189
|
+
ig.tick(2);
|
|
1190
|
+
ig.append("REG = cpu.a;");
|
|
1191
|
+
ig.append(op.op);
|
|
1192
|
+
ig.append("cpu.a = REG;");
|
|
1193
|
+
return ig.render();
|
|
1194
|
+
|
|
1195
|
+
case "(,x)":
|
|
1196
|
+
ig.tick(3); // two, plus one for the seemingly spurious extra read of zp
|
|
1197
|
+
ig.append("const zpAddr = (cpu.getb() + cpu.x) & 0xff;");
|
|
1198
|
+
ig.append("let lo, hi;");
|
|
1199
|
+
ig.zpReadOp("zpAddr", "lo");
|
|
1200
|
+
ig.zpReadOp("(zpAddr + 1) & 0xff", "hi");
|
|
1201
|
+
ig.append("const addr = lo | (hi << 8);");
|
|
1202
|
+
if (op.read) {
|
|
1203
|
+
ig.readOp("addr", "REG");
|
|
1204
|
+
if (op.write) ig.spuriousOp("addr", "REG");
|
|
1205
|
+
}
|
|
1206
|
+
ig.append(op.op);
|
|
1207
|
+
if (op.write) ig.writeOp("addr", "REG");
|
|
1208
|
+
return ig.render();
|
|
1209
|
+
|
|
1210
|
+
case "(),y":
|
|
1211
|
+
ig.tick(2);
|
|
1212
|
+
ig.append("const zpAddr = cpu.getb() | 0;");
|
|
1213
|
+
ig.append("let lo, hi;");
|
|
1214
|
+
ig.zpReadOp("zpAddr", "lo");
|
|
1215
|
+
ig.zpReadOp("(zpAddr + 1) & 0xff", "hi");
|
|
1216
|
+
ig.append("const addr = lo | (hi << 8);");
|
|
1217
|
+
ig.append("let addrWithCarry = (addr + cpu.y) & 0xffff;");
|
|
1218
|
+
ig.append("const addrNonCarry = (addr & 0xff00) | (addrWithCarry & 0xff);");
|
|
1219
|
+
if (op.read && !op.write) {
|
|
1220
|
+
// For non-RMW, we only pay the cost of the spurious read if the address carried on 6502
|
|
1221
|
+
ig = ig.split("addrWithCarry !== addrNonCarry");
|
|
1222
|
+
if (!is65c12) {
|
|
1223
|
+
ig.ifTrue.readOp("addrNonCarry");
|
|
1224
|
+
} else {
|
|
1225
|
+
ig.ifTrue.tick(1);
|
|
1226
|
+
}
|
|
1227
|
+
ig.readOp("addrWithCarry", "REG");
|
|
1228
|
+
} else if (op.read) {
|
|
1229
|
+
// For RMW we always have a spurious read and then a spurious read or write
|
|
1230
|
+
ig.readOp("addrNonCarry");
|
|
1231
|
+
ig.readOp("addrWithCarry", "REG");
|
|
1232
|
+
ig.spuriousOp("addrWithCarry", "REG");
|
|
1233
|
+
} else if (op.write) {
|
|
1234
|
+
// Pure stores still exhibit a read at the non-carried address.
|
|
1235
|
+
ig.readOp("addrNonCarry");
|
|
1236
|
+
if (op.zpQuirk) {
|
|
1237
|
+
// with this quirk on undocumented instructions, a page crossing writes to 00XX
|
|
1238
|
+
ig.append("if (addrWithCarry !== addrNonCarry) addrWithCarry &= 0xff;");
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
ig.append(op.op);
|
|
1242
|
+
if (op.write) ig.writeOp("addrWithCarry", "REG");
|
|
1243
|
+
return ig.render();
|
|
1244
|
+
|
|
1245
|
+
case "(abs)":
|
|
1246
|
+
ig.tick(is65c12 ? 4 : 3);
|
|
1247
|
+
ig.append("const absAddr = cpu.getw() | 0;");
|
|
1248
|
+
if (is65c12) {
|
|
1249
|
+
ig.append("const nextAddr = (absAddr + 1) & 0xffff;");
|
|
1250
|
+
} else {
|
|
1251
|
+
ig.append("const nextAddr = ((absAddr + 1) & 0xff) | (absAddr & 0xff00);");
|
|
1252
|
+
}
|
|
1253
|
+
ig.append("let lo, hi;");
|
|
1254
|
+
ig.readOp("absAddr", "lo");
|
|
1255
|
+
ig.readOp("nextAddr", "hi");
|
|
1256
|
+
ig.append("const addr = lo | (hi << 8);");
|
|
1257
|
+
ig.append(op.op);
|
|
1258
|
+
return ig.render();
|
|
1259
|
+
|
|
1260
|
+
case "(abs,x)":
|
|
1261
|
+
ig.tick(4);
|
|
1262
|
+
ig.append("const absAddr = (cpu.getw() + cpu.x) | 0;");
|
|
1263
|
+
ig.append("let lo, hi;");
|
|
1264
|
+
ig.readOp("absAddr", "lo");
|
|
1265
|
+
ig.readOp("(absAddr + 1) & 0xffff", "hi");
|
|
1266
|
+
ig.append("const addr = lo | (hi << 8);");
|
|
1267
|
+
ig.append(op.op);
|
|
1268
|
+
return ig.render();
|
|
1269
|
+
|
|
1270
|
+
case "()":
|
|
1271
|
+
// Timing here is guessed at, but appears to be correct.
|
|
1272
|
+
ig.tick(2);
|
|
1273
|
+
ig.append("const zpAddr = cpu.getb() | 0;");
|
|
1274
|
+
ig.append("let lo, hi;");
|
|
1275
|
+
ig.zpReadOp("zpAddr", "lo");
|
|
1276
|
+
ig.zpReadOp("(zpAddr + 1) & 0xff", "hi");
|
|
1277
|
+
ig.append("const addr = lo | (hi << 8);");
|
|
1278
|
+
if (op.read) ig.readOp("addr", "REG");
|
|
1279
|
+
ig.append(op.op);
|
|
1280
|
+
if (op.write) ig.writeOp("addr", "REG");
|
|
1281
|
+
return ig.render();
|
|
1282
|
+
|
|
1283
|
+
default:
|
|
1284
|
+
throw new Error(`Unknown arg type ${arg}`);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function getIndentedSource(indent, opcodeNum, needsReg) {
|
|
1289
|
+
const opcode = opcodes[opcodeNum];
|
|
1290
|
+
return (
|
|
1291
|
+
indent +
|
|
1292
|
+
[
|
|
1293
|
+
'"use strict";',
|
|
1294
|
+
`// ${utils.hexbyte(opcodeNum)} - ${opcode}
|
|
1295
|
+
`,
|
|
1296
|
+
].concat(
|
|
1297
|
+
opcode
|
|
1298
|
+
? getInstruction(opcode, !!needsReg)
|
|
1299
|
+
: [`this.invalidOpcode(cpu, 0x${utils.hexbyte(opcodeNum)});`],
|
|
1300
|
+
).join(`
|
|
1301
|
+
${indent}`)
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// Empty to hold prototypical stuff.
|
|
1306
|
+
function Runner() {}
|
|
1307
|
+
|
|
1308
|
+
function generate6502JumpTable() {
|
|
1309
|
+
const funcs = [];
|
|
1310
|
+
for (let opcodeNum = 0; opcodeNum < 256; ++opcodeNum) {
|
|
1311
|
+
const opcodeFunc = new Function("cpu", getIndentedSource(" ", opcodeNum, true));
|
|
1312
|
+
let funcName = `exec_${utils.hexbyte(opcodeNum)}_`;
|
|
1313
|
+
if (opcodes[opcodeNum]) {
|
|
1314
|
+
const instrName = opcodes[opcodeNum]
|
|
1315
|
+
.replace("()", "ind")
|
|
1316
|
+
.replace("(,x)", "ind_x")
|
|
1317
|
+
.replace("(abs)", "ind_abs")
|
|
1318
|
+
.replace(/[^A-Za-z0-9]/g, "_")
|
|
1319
|
+
.toLowerCase();
|
|
1320
|
+
funcName += instrName;
|
|
1321
|
+
} else funcName += "undef";
|
|
1322
|
+
Object.defineProperty(opcodeFunc, "name", { writable: true, value: funcName });
|
|
1323
|
+
funcs[opcodeNum] = opcodeFunc;
|
|
1324
|
+
}
|
|
1325
|
+
return function exec(opcode) {
|
|
1326
|
+
return funcs[opcode].call(this, this.cpu);
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function invalidOpcode(cpu, opcode) {
|
|
1331
|
+
if (is65c12) {
|
|
1332
|
+
// All undefined opcodes are NOPs on 65c12 (of varying lengths)
|
|
1333
|
+
// http://6502.org/tutorials/65c02opcodes.html has a list.
|
|
1334
|
+
// The default case is to treat them as one-cycle NOPs. Anything more than this is picked up below.
|
|
1335
|
+
switch (opcode) {
|
|
1336
|
+
case 0x02:
|
|
1337
|
+
case 0x22:
|
|
1338
|
+
case 0x42:
|
|
1339
|
+
case 0x62:
|
|
1340
|
+
case 0x82:
|
|
1341
|
+
case 0xc2:
|
|
1342
|
+
case 0xe2:
|
|
1343
|
+
// two bytes, two cycles
|
|
1344
|
+
cpu.getb();
|
|
1345
|
+
cpu.polltime(2);
|
|
1346
|
+
break;
|
|
1347
|
+
|
|
1348
|
+
case 0x44:
|
|
1349
|
+
// two bytes, three cycles
|
|
1350
|
+
cpu.getb();
|
|
1351
|
+
cpu.polltime(3);
|
|
1352
|
+
break;
|
|
1353
|
+
|
|
1354
|
+
case 0x54:
|
|
1355
|
+
case 0xd4:
|
|
1356
|
+
case 0xf4:
|
|
1357
|
+
// two bytes, four cycles
|
|
1358
|
+
cpu.getb();
|
|
1359
|
+
cpu.polltime(4);
|
|
1360
|
+
break;
|
|
1361
|
+
|
|
1362
|
+
case 0x5c:
|
|
1363
|
+
// three bytes, eight cycles
|
|
1364
|
+
cpu.getw();
|
|
1365
|
+
cpu.polltime(8);
|
|
1366
|
+
break;
|
|
1367
|
+
|
|
1368
|
+
case 0xdc:
|
|
1369
|
+
case 0xfc:
|
|
1370
|
+
// three bytes, four cycles (plus a memory access)
|
|
1371
|
+
cpu.polltimeAddr(4, cpu.getw());
|
|
1372
|
+
break;
|
|
1373
|
+
|
|
1374
|
+
default:
|
|
1375
|
+
// one byte one cycle
|
|
1376
|
+
cpu.polltime(1);
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
// Anything else is a HLT. Just hang forever...
|
|
1382
|
+
cpu.pc--; // Account for the fact we've already incremented pc.
|
|
1383
|
+
cpu.polltime(1); // Take up some time though. Else we'll spin forever
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
Runner.prototype.invalidOpcode = invalidOpcode;
|
|
1387
|
+
Runner.prototype.cpu = cpu;
|
|
1388
|
+
// We used to use jump tables for Firefox, and binary search for everything else.
|
|
1389
|
+
// Chrome used to have a problem with 256 entries in a switch, but that seems to have been fixed.
|
|
1390
|
+
// We now use a jump table for everything.
|
|
1391
|
+
Runner.prototype.run = generate6502JumpTable();
|
|
1392
|
+
|
|
1393
|
+
return {
|
|
1394
|
+
disassembler: new Disassemble6502(cpu, opcodes),
|
|
1395
|
+
runInstruction: new Runner(),
|
|
1396
|
+
opcodes: opcodes,
|
|
1397
|
+
getInstruction: getInstruction,
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
export function Cpu6502(cpu) {
|
|
1402
|
+
return makeCpuFunctions(cpu, opcodes6502, false);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
export function Cpu65c12(cpu) {
|
|
1406
|
+
return makeCpuFunctions(cpu, opcodes65c12, true);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
export function Cpu65c02(cpu) {
|
|
1410
|
+
return makeCpuFunctions(cpu, opcodes65c02, true);
|
|
1411
|
+
}
|