jsbeeb 1.22.4 → 1.24.0

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.
Files changed (112) hide show
  1. package/README.md +2 -2
  2. package/package.json +16 -11
  3. package/src/6502.js +39 -42
  4. package/src/6502.opcodes.js +198 -123
  5. package/src/6847.js +4 -5
  6. package/src/6847_fontdata.js +0 -2
  7. package/src/acia.js +2 -3
  8. package/src/app/app.js +0 -1
  9. package/src/app/args.js +0 -2
  10. package/src/app/electron.js +4 -6
  11. package/src/app-bench.js +1 -3
  12. package/src/archive.js +243 -0
  13. package/src/basic-loader.js +0 -7
  14. package/src/basic-tokenise.js +19 -6
  15. package/src/bbc-palette.js +0 -2
  16. package/src/bbcdiscs.js +0 -2
  17. package/src/bem-snapshot.js +1 -3
  18. package/src/binary.js +175 -0
  19. package/src/cmos.js +0 -2
  20. package/src/ddnoise.js +0 -1
  21. package/src/debounce.js +7 -0
  22. package/src/disc-surface.js +0 -2
  23. package/src/disc.js +10 -9
  24. package/src/fake6502.js +7 -23
  25. package/src/fdc.js +9 -51
  26. package/src/filestore.js +10 -17
  27. package/src/hex.js +38 -0
  28. package/src/index.js +11 -0
  29. package/src/intel-fdc.js +18 -20
  30. package/src/jsbeeb.css +33 -0
  31. package/src/{utils_atom.js → keymap-atom.js} +2 -18
  32. package/src/{utils.js → keymap.js} +47 -610
  33. package/src/loader.js +63 -0
  34. package/src/machine-session.js +37 -177
  35. package/src/machine-spec.js +59 -0
  36. package/src/main.js +69 -184
  37. package/src/media-resolver.js +68 -0
  38. package/src/mmc.js +3 -2
  39. package/src/models.js +40 -4
  40. package/src/music5000.js +0 -2
  41. package/src/ppia.js +3 -4
  42. package/src/printer.js +7 -14
  43. package/src/relaynoise.js +0 -1
  44. package/src/rewind.js +0 -2
  45. package/src/sample-player.js +2 -3
  46. package/src/serial.js +0 -2
  47. package/src/snapshot-helpers.js +0 -2
  48. package/src/snapshot.js +0 -2
  49. package/src/soundchip.js +1 -1
  50. package/src/state-utils.js +0 -2
  51. package/src/sth.js +2 -4
  52. package/src/tapes.js +10 -4
  53. package/src/teletext.js +2 -3
  54. package/src/teletext_adaptor.js +2 -4
  55. package/src/teletext_data.js +0 -2
  56. package/src/test-machine.js +307 -0
  57. package/src/touchscreen.js +2 -4
  58. package/src/tube.js +5 -6
  59. package/src/typist.js +103 -0
  60. package/src/uef-snapshot.js +0 -2
  61. package/src/url-params.js +4 -5
  62. package/src/vdu-capture.js +113 -0
  63. package/src/via.js +3 -4
  64. package/src/video-filters/pal-composite.js +0 -2
  65. package/src/video-filters/passthrough-filter.js +0 -2
  66. package/src/video-filters/pixel-grid.js +0 -2
  67. package/src/video-filters/shader-program.js +0 -2
  68. package/src/video-filters/xbr-filter.js +0 -2
  69. package/src/video.js +12 -11
  70. package/src/wd-fdc.js +20 -20
  71. package/src/web/analogue-inputs.js +6 -8
  72. package/src/web/analytics.js +13 -0
  73. package/src/web/audio-handler.js +2 -2
  74. package/src/{audio-utils.js → web/audio-utils.js} +0 -2
  75. package/src/web/autoboot.js +10 -13
  76. package/src/{canvas.js → web/canvas.js} +4 -5
  77. package/src/web/config.js +42 -72
  78. package/src/web/console-surface.js +25 -0
  79. package/src/web/debug.js +8 -49
  80. package/src/web/disc-visualiser.js +0 -2
  81. package/src/web/display.js +21 -4
  82. package/src/{dom-utils.js → web/dom-utils.js} +1 -11
  83. package/src/web/drives.js +1 -1
  84. package/src/web/emulation-loop.js +54 -33
  85. package/src/web/front-panel.js +13 -12
  86. package/src/{gamepads.js → web/gamepads.js} +2 -6
  87. package/src/web/google-drive-picker.js +14 -9
  88. package/src/{google-drive.js → web/google-drive.js} +9 -10
  89. package/src/web/hfe-picker.js +5 -4
  90. package/src/web/icons.js +42 -0
  91. package/src/web/keyboard-setup.js +29 -40
  92. package/src/{keyboard.js → web/keyboard.js} +18 -119
  93. package/src/web/layout.js +4 -0
  94. package/src/web/local-disc.js +44 -0
  95. package/src/web/machine.js +27 -20
  96. package/src/web/media-loader.js +38 -147
  97. package/src/{microphone-input.js → web/microphone-input.js} +1 -1
  98. package/src/web/modals.js +6 -9
  99. package/src/web/page-actions.js +88 -0
  100. package/src/web/quick-settings.js +16 -15
  101. package/src/web/reporting.js +0 -7
  102. package/src/{rewind-thumbnail.js → web/rewind-thumbnail.js} +0 -2
  103. package/src/web/rewind-ui.js +29 -29
  104. package/src/web/run-controls.js +39 -0
  105. package/src/web/settings.js +64 -135
  106. package/src/web/snapshot-ui.js +10 -16
  107. package/src/{speech-output.js → web/speech-output.js} +0 -2
  108. package/src/web/sth-picker.js +9 -7
  109. package/src/web/url-state.js +20 -6
  110. package/src/econet/L3FS.dat +0 -0
  111. package/src/econet/scsi.dat +0 -0
  112. package/tests/test-machine.js +0 -609
@@ -1,9 +1,5 @@
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;
1
+ import { signExtend } from "./binary.js";
2
+ import { hexbyte, hexword } from "./hex.js";
7
3
 
8
4
  function rotate(left, logical) {
9
5
  const lines = [];
@@ -525,254 +521,269 @@ function getOp(op, arg) {
525
521
  throw new Error(`Unrecognised operation '${op}'`);
526
522
  }
527
523
 
528
- const opcodes6502 = {
524
+ // Undocumented opcodes carry a leading "*", as 6502 listings conventionally show them.
525
+ function splitUndocumented(markedOpcodes) {
526
+ const opcodes = {};
527
+ const undocumented = new Set();
528
+ for (const [op, text] of Object.entries(markedOpcodes)) {
529
+ if (text.startsWith("*")) {
530
+ undocumented.add(Number(op));
531
+ opcodes[op] = text.slice(1);
532
+ } else {
533
+ opcodes[op] = text;
534
+ }
535
+ }
536
+ return { opcodes, undocumented };
537
+ }
538
+
539
+ const { opcodes: opcodes6502, undocumented: undocumented6502 } = splitUndocumented({
529
540
  0x00: "BRK",
530
541
  0x01: "ORA (,x)",
531
- 0x03: "SLO (,x)",
532
- 0x04: "NOP zp",
542
+ 0x03: "*SLO (,x)",
543
+ 0x04: "*NOP zp",
533
544
  0x05: "ORA zp",
534
545
  0x06: "ASL zp",
535
- 0x07: "SLO zp",
546
+ 0x07: "*SLO zp",
536
547
  0x08: "PHP",
537
548
  0x09: "ORA imm",
538
549
  0x0a: "ASL A",
539
- 0x0b: "ANC imm",
540
- 0x0c: "NOP abs",
550
+ 0x0b: "*ANC imm",
551
+ 0x0c: "*NOP abs",
541
552
  0x0d: "ORA abs",
542
553
  0x0e: "ASL abs",
543
- 0x0f: "SLO abs",
554
+ 0x0f: "*SLO abs",
544
555
  0x10: "BPL branch",
545
556
  0x11: "ORA (),y",
546
- 0x13: "SLO (),y",
547
- 0x14: "NOP zp,x",
557
+ 0x13: "*SLO (),y",
558
+ 0x14: "*NOP zp,x",
548
559
  0x15: "ORA zp,x",
549
560
  0x16: "ASL zp,x",
550
- 0x17: "SLO zp,x",
561
+ 0x17: "*SLO zp,x",
551
562
  0x18: "CLC",
552
563
  0x19: "ORA abs,y",
553
- 0x1a: "NOP",
554
- 0x1b: "SLO abs,y",
555
- 0x1c: "NOP abs,x",
564
+ 0x1a: "*NOP",
565
+ 0x1b: "*SLO abs,y",
566
+ 0x1c: "*NOP abs,x",
556
567
  0x1d: "ORA abs,x",
557
568
  0x1e: "ASL abs,x",
558
- 0x1f: "SLO abs,x",
569
+ 0x1f: "*SLO abs,x",
559
570
  0x20: "JSR abs",
560
571
  0x21: "AND (,x)",
561
- 0x23: "RLA (,x)",
572
+ 0x23: "*RLA (,x)",
562
573
  0x24: "BIT zp",
563
574
  0x25: "AND zp",
564
575
  0x26: "ROL zp",
565
- 0x27: "RLA zp",
576
+ 0x27: "*RLA zp",
566
577
  0x28: "PLP",
567
578
  0x29: "AND imm",
568
579
  0x2a: "ROL A",
569
- 0x2b: "ANC imm",
580
+ 0x2b: "*ANC imm",
570
581
  0x2c: "BIT abs",
571
582
  0x2d: "AND abs",
572
583
  0x2e: "ROL abs",
573
- 0x2f: "RLA abs",
584
+ 0x2f: "*RLA abs",
574
585
  0x30: "BMI branch",
575
586
  0x31: "AND (),y",
576
- 0x33: "RLA (),y",
577
- 0x34: "NOP zp,x",
587
+ 0x33: "*RLA (),y",
588
+ 0x34: "*NOP zp,x",
578
589
  0x35: "AND zp,x",
579
590
  0x36: "ROL zp,x",
580
- 0x37: "RLA zp,x",
591
+ 0x37: "*RLA zp,x",
581
592
  0x38: "SEC",
582
593
  0x39: "AND abs,y",
583
- 0x3a: "NOP",
584
- 0x3b: "RLA abs,y",
585
- 0x3c: "NOP abs,x",
594
+ 0x3a: "*NOP",
595
+ 0x3b: "*RLA abs,y",
596
+ 0x3c: "*NOP abs,x",
586
597
  0x3d: "AND abs,x",
587
598
  0x3e: "ROL abs,x",
588
- 0x3f: "RLA abs,x",
599
+ 0x3f: "*RLA abs,x",
589
600
  0x40: "RTI",
590
601
  0x41: "EOR (,x)",
591
- 0x43: "SRE (,x)",
592
- 0x44: "NOP zp",
602
+ 0x43: "*SRE (,x)",
603
+ 0x44: "*NOP zp",
593
604
  0x45: "EOR zp",
594
605
  0x46: "LSR zp",
595
- 0x47: "SRE zp",
606
+ 0x47: "*SRE zp",
596
607
  0x48: "PHA",
597
608
  0x49: "EOR imm",
598
609
  0x4a: "LSR A",
599
- 0x4b: "ASR imm",
610
+ 0x4b: "*ASR imm",
600
611
  0x4c: "JMP abs",
601
612
  0x4d: "EOR abs",
602
613
  0x4e: "LSR abs",
603
- 0x4f: "SRE abs",
614
+ 0x4f: "*SRE abs",
604
615
  0x50: "BVC branch",
605
616
  0x51: "EOR (),y",
606
- 0x53: "SRE (),y",
607
- 0x54: "NOP zp,x",
617
+ 0x53: "*SRE (),y",
618
+ 0x54: "*NOP zp,x",
608
619
  0x55: "EOR zp,x",
609
620
  0x56: "LSR zp,x",
610
- 0x57: "SRE zp,x",
621
+ 0x57: "*SRE zp,x",
611
622
  0x58: "CLI",
612
623
  0x59: "EOR abs,y",
613
- 0x5a: "NOP",
614
- 0x5b: "SRE abs,y",
615
- 0x5c: "NOP abs,x",
624
+ 0x5a: "*NOP",
625
+ 0x5b: "*SRE abs,y",
626
+ 0x5c: "*NOP abs,x",
616
627
  0x5d: "EOR abs,x",
617
628
  0x5e: "LSR abs,x",
618
- 0x5f: "SRE abs,x",
629
+ 0x5f: "*SRE abs,x",
619
630
  0x60: "RTS",
620
631
  0x61: "ADC (,x)",
621
- 0x63: "RRA (,x)",
622
- 0x64: "NOP zp",
632
+ 0x63: "*RRA (,x)",
633
+ 0x64: "*NOP zp",
623
634
  0x65: "ADC zp",
624
635
  0x66: "ROR zp",
625
- 0x67: "RRA zp",
636
+ 0x67: "*RRA zp",
626
637
  0x68: "PLA",
627
638
  0x69: "ADC imm",
628
639
  0x6a: "ROR A",
629
- 0x6b: "ARR imm",
640
+ 0x6b: "*ARR imm",
630
641
  0x6c: "JMP (abs)",
631
642
  0x6d: "ADC abs",
632
643
  0x6e: "ROR abs",
633
- 0x6f: "RRA abs",
644
+ 0x6f: "*RRA abs",
634
645
  0x70: "BVS branch",
635
646
  0x71: "ADC (),y",
636
- 0x73: "RRA (),y",
637
- 0x74: "NOP zp,x",
647
+ 0x73: "*RRA (),y",
648
+ 0x74: "*NOP zp,x",
638
649
  0x75: "ADC zp,x",
639
650
  0x76: "ROR zp,x",
640
- 0x77: "RRA zp,x",
651
+ 0x77: "*RRA zp,x",
641
652
  0x78: "SEI",
642
653
  0x79: "ADC abs,y",
643
- 0x7a: "NOP",
644
- 0x7b: "RRA abs,y",
645
- 0x7c: "NOP abs,x",
654
+ 0x7a: "*NOP",
655
+ 0x7b: "*RRA abs,y",
656
+ 0x7c: "*NOP abs,x",
646
657
  0x7d: "ADC abs,x",
647
658
  0x7e: "ROR abs,x",
648
- 0x7f: "RRA abs,x",
649
- 0x80: "NOP imm",
659
+ 0x7f: "*RRA abs,x",
660
+ 0x80: "*NOP imm",
650
661
  0x81: "STA (,x)",
651
- 0x82: "NOP imm",
652
- 0x83: "SAX (,x)",
662
+ 0x82: "*NOP imm",
663
+ 0x83: "*SAX (,x)",
653
664
  0x84: "STY zp",
654
665
  0x85: "STA zp",
655
666
  0x86: "STX zp",
656
- 0x87: "SAX zp",
667
+ 0x87: "*SAX zp",
657
668
  0x88: "DEY",
658
- 0x89: "NOP imm",
669
+ 0x89: "*NOP imm",
659
670
  0x8a: "TXA",
660
- 0x8b: "ANE imm",
671
+ 0x8b: "*ANE imm",
661
672
  0x8c: "STY abs",
662
673
  0x8d: "STA abs",
663
674
  0x8e: "STX abs",
664
- 0x8f: "SAX abs",
675
+ 0x8f: "*SAX abs",
665
676
  0x90: "BCC branch",
666
677
  0x91: "STA (),y",
667
- 0x93: "SHA (),y",
678
+ 0x93: "*SHA (),y",
668
679
  0x94: "STY zp,x",
669
680
  0x95: "STA zp,x",
670
681
  0x96: "STX zp,y",
671
- 0x97: "SAX zp,y",
682
+ 0x97: "*SAX zp,y",
672
683
  0x98: "TYA",
673
684
  0x99: "STA abs,y",
674
685
  0x9a: "TXS",
675
- 0x9b: "SHS abs,y",
676
- 0x9c: "SHY abs,x",
686
+ 0x9b: "*SHS abs,y",
687
+ 0x9c: "*SHY abs,x",
677
688
  0x9d: "STA abs,x",
678
- 0x9e: "SHX abs,y",
679
- 0x9f: "SHA abs,y",
689
+ 0x9e: "*SHX abs,y",
690
+ 0x9f: "*SHA abs,y",
680
691
  0xa0: "LDY imm",
681
692
  0xa1: "LDA (,x)",
682
693
  0xa2: "LDX imm",
683
- 0xa3: "LAX (,x)",
694
+ 0xa3: "*LAX (,x)",
684
695
  0xa4: "LDY zp",
685
696
  0xa5: "LDA zp",
686
697
  0xa6: "LDX zp",
687
- 0xa7: "LAX zp",
698
+ 0xa7: "*LAX zp",
688
699
  0xa8: "TAY",
689
700
  0xa9: "LDA imm",
690
701
  0xaa: "TAX",
691
- 0xab: "LXA imm",
702
+ 0xab: "*LXA imm",
692
703
  0xac: "LDY abs",
693
704
  0xad: "LDA abs",
694
705
  0xae: "LDX abs",
695
- 0xaf: "LAX abs",
706
+ 0xaf: "*LAX abs",
696
707
  0xb0: "BCS branch",
697
708
  0xb1: "LDA (),y",
698
- 0xb3: "LAX (),y",
709
+ 0xb3: "*LAX (),y",
699
710
  0xb4: "LDY zp,x",
700
711
  0xb5: "LDA zp,x",
701
712
  0xb6: "LDX zp,y",
702
- 0xb7: "LAX zp,y",
713
+ 0xb7: "*LAX zp,y",
703
714
  0xb8: "CLV",
704
715
  0xb9: "LDA abs,y",
705
716
  0xba: "TSX",
706
- 0xbb: "LAS abs,y",
717
+ 0xbb: "*LAS abs,y",
707
718
  0xbc: "LDY abs,x",
708
719
  0xbd: "LDA abs,x",
709
720
  0xbe: "LDX abs,y",
710
- 0xbf: "LAX abs,y",
721
+ 0xbf: "*LAX abs,y",
711
722
  0xc0: "CPY imm",
712
723
  0xc1: "CMP (,x)",
713
- 0xc2: "NOP imm",
714
- 0xc3: "DCP (,x)",
724
+ 0xc2: "*NOP imm",
725
+ 0xc3: "*DCP (,x)",
715
726
  0xc4: "CPY zp",
716
727
  0xc5: "CMP zp",
717
728
  0xc6: "DEC zp",
718
- 0xc7: "DCP zp",
729
+ 0xc7: "*DCP zp",
719
730
  0xc8: "INY",
720
731
  0xc9: "CMP imm",
721
732
  0xca: "DEX",
722
- 0xcb: "SBX imm",
733
+ 0xcb: "*SBX imm",
723
734
  0xcc: "CPY abs",
724
735
  0xcd: "CMP abs",
725
736
  0xce: "DEC abs",
726
- 0xcf: "DCP abs",
737
+ 0xcf: "*DCP abs",
727
738
  0xd0: "BNE branch",
728
739
  0xd1: "CMP (),y",
729
- 0xd3: "DCP (),y",
730
- 0xd4: "NOP zp,x",
740
+ 0xd3: "*DCP (),y",
741
+ 0xd4: "*NOP zp,x",
731
742
  0xd5: "CMP zp,x",
732
743
  0xd6: "DEC zp,x",
733
- 0xd7: "DCP zp,x",
744
+ 0xd7: "*DCP zp,x",
734
745
  0xd8: "CLD",
735
746
  0xd9: "CMP abs,y",
736
- 0xda: "NOP",
737
- 0xdb: "DCP abs,y",
738
- 0xdc: "NOP abs,x",
747
+ 0xda: "*NOP",
748
+ 0xdb: "*DCP abs,y",
749
+ 0xdc: "*NOP abs,x",
739
750
  0xdd: "CMP abs,x",
740
751
  0xde: "DEC abs,x",
741
- 0xdf: "DCP abs,x",
752
+ 0xdf: "*DCP abs,x",
742
753
  0xe0: "CPX imm",
743
754
  0xe1: "SBC (,x)",
744
- 0xe2: "NOP imm",
745
- 0xe3: "ISB (,x)",
755
+ 0xe2: "*NOP imm",
756
+ 0xe3: "*ISB (,x)",
746
757
  0xe4: "CPX zp",
747
758
  0xe5: "SBC zp",
748
759
  0xe6: "INC zp",
749
- 0xe7: "ISB zp",
760
+ 0xe7: "*ISB zp",
750
761
  0xe8: "INX",
751
762
  0xe9: "SBC imm",
752
763
  0xea: "NOP",
753
- 0xeb: "SBC imm",
764
+ 0xeb: "*SBC imm",
754
765
  0xec: "CPX abs",
755
766
  0xed: "SBC abs",
756
767
  0xee: "INC abs",
757
- 0xef: "ISB abs",
768
+ 0xef: "*ISB abs",
758
769
  0xf0: "BEQ branch",
759
770
  0xf1: "SBC (),y",
760
- 0xf3: "ISB (),y",
761
- 0xf4: "NOP zpx",
771
+ 0xf3: "*ISB (),y",
772
+ 0xf4: "*NOP zp,x",
762
773
  0xf5: "SBC zp,x",
763
774
  0xf6: "INC zp,x",
764
- 0xf7: "ISB zp,x",
775
+ 0xf7: "*ISB zp,x",
765
776
  0xf8: "SED",
766
777
  0xf9: "SBC abs,y",
767
- 0xfa: "NOP",
768
- 0xfb: "ISB abs,y",
769
- 0xfc: "NOP abs,x",
778
+ 0xfa: "*NOP",
779
+ 0xfb: "*ISB abs,y",
780
+ 0xfc: "*NOP abs,x",
770
781
  0xfd: "SBC abs,x",
771
782
  0xfe: "INC abs,x",
772
- 0xff: "ISB abs,x",
773
- };
783
+ 0xff: "*ISB abs,x",
784
+ });
774
785
 
775
- const opcodes65c12 = {
786
+ const { opcodes: opcodes65c12, undocumented: undocumented65c12 } = splitUndocumented({
776
787
  0x00: "BRK",
777
788
  0x01: "ORA (,x)",
778
789
  0x04: "TSB zp",
@@ -952,9 +963,9 @@ const opcodes65c12 = {
952
963
  0xfa: "PLX",
953
964
  0xfd: "SBC abs,x",
954
965
  0xfe: "INC abs,x",
955
- };
966
+ });
956
967
 
957
- const opcodes65c02 = {
968
+ const { opcodes: opcodes65c02, undocumented: undocumented65c02 } = splitUndocumented({
958
969
  ...opcodes65c12,
959
970
  0x07: "RMB0 zp",
960
971
  0x17: "RMB1 zp",
@@ -988,12 +999,20 @@ const opcodes65c02 = {
988
999
  0xdf: "BBS5 zp,branch",
989
1000
  0xef: "BBS6 zp,branch",
990
1001
  0xff: "BBS7 zp,branch",
991
- };
1002
+ });
1003
+
1004
+ const PrevInstructionWindow = 64;
992
1005
 
993
1006
  class Disassemble6502 {
994
- constructor(cpu, opcodes) {
1007
+ constructor(cpu, opcodes, undocumented) {
995
1008
  this.cpu = cpu;
996
1009
  this.opcodes = opcodes;
1010
+ this.undocumented = undocumented;
1011
+ }
1012
+
1013
+ isDocumented(addr) {
1014
+ const op = this.cpu.peekmem(addr);
1015
+ return op in this.opcodes && !this.undocumented.has(op);
997
1016
  }
998
1017
 
999
1018
  disassemble(addr, plain) {
@@ -1003,13 +1022,15 @@ class Disassemble6502 {
1003
1022
  formatAddr = hexword;
1004
1023
  formatJumpAddr = hexword;
1005
1024
  }
1006
- const opcode = this.opcodes[this.cpu.peekmem(addr)];
1025
+ const opcodeByte = this.cpu.peekmem(addr);
1026
+ const opcode = this.opcodes[opcodeByte];
1007
1027
  if (!opcode) {
1008
1028
  return ["???", addr + 1];
1009
1029
  }
1010
- const split = opcode.split(" ");
1030
+ const [mnemonic, ...rest] = opcode.split(" ");
1031
+ const split = [(this.undocumented.has(opcodeByte) ? "*" : "") + mnemonic, ...rest];
1011
1032
  if (!split[1]) {
1012
- return [opcode, addr + 1];
1033
+ return [split[0], addr + 1];
1013
1034
  }
1014
1035
  let param = split[1] || "";
1015
1036
  let suffix = "";
@@ -1034,18 +1055,26 @@ class Disassemble6502 {
1034
1055
  }
1035
1056
  case "zp":
1036
1057
  return [`${split[0]} $${hexbyte(this.cpu.peekmem(addr + 1))}${suffix}`, addr + 2];
1058
+ case "zp,branch": {
1059
+ const destAddr = addr + signExtend(this.cpu.peekmem(addr + 2)) + 3;
1060
+ return [
1061
+ `${split[0]} $${hexbyte(this.cpu.peekmem(addr + 1))}, $${formatJumpAddr(destAddr)}`,
1062
+ addr + 3,
1063
+ destAddr,
1064
+ ];
1065
+ }
1037
1066
  case "(,x)":
1038
1067
  return [`${split[0]} ($${hexbyte(this.cpu.peekmem(addr + 1))}, X)${suffix}`, addr + 2];
1039
1068
  case "()": {
1040
1069
  const zp = this.cpu.peekmem(addr + 1);
1041
1070
  const destAddr = this.cpu.peekmem(zp) | (this.cpu.peekmem(zp + 1) << 8);
1042
- return [`${split[0]} ($${hexbyte(zp)})${suffix} ; $${utils.hexword(destAddr)}${suffix2}`, addr + 2];
1071
+ return [`${split[0]} ($${hexbyte(zp)})${suffix} ; $${hexword(destAddr)}${suffix2}`, addr + 2];
1043
1072
  }
1044
1073
  case "(abs)": {
1045
1074
  const destAddr = this.cpu.peekmem(addr + 1) | (this.cpu.peekmem(addr + 2) << 8);
1046
1075
  const indDest = this.cpu.peekmem(destAddr) | (this.cpu.peekmem(destAddr + 1) << 8);
1047
1076
  return [
1048
- `${split[0]} ($${formatJumpAddr(destAddr)})${suffix} ; $${utils.hexword(indDest)}${suffix2}`,
1077
+ `${split[0]} ($${formatJumpAddr(destAddr)})${suffix} ; $${hexword(indDest)}${suffix2}`,
1049
1078
  addr + 3,
1050
1079
  indDest,
1051
1080
  ];
@@ -1056,11 +1085,60 @@ class Disassemble6502 {
1056
1085
  return [`${split[0]} ($${formatJumpAddr(destAddr)},x)${suffix}`, addr + 3, indDest];
1057
1086
  }
1058
1087
  }
1059
- return [opcode, addr + 1];
1088
+ return [split.join(" "), addr + 1];
1089
+ }
1090
+
1091
+ nextInstruction(address) {
1092
+ return this.disassemble(address)[1] & 0xffff;
1093
+ }
1094
+
1095
+ /**
1096
+ * Guesses the address of the instruction before the given one by scoring
1097
+ * each run of instructions in the preceding window that lands exactly on
1098
+ * it: a point per "common" instruction (loads, stores, branches, compares,
1099
+ * arithmetic and carry-set/clear that avoid unusual indexing modes) and a
1100
+ * large boost for a run that passes through pc. Runs through undocumented
1101
+ * opcodes are discarded. The highest-scoring run's last instruction wins;
1102
+ * any run beats none, since a wrong parse resynchronises within a few
1103
+ * instructions and so nearly always shares the true stream's last one.
1104
+ * Good test cases:
1105
+ * Repton 2 @ 2cbb
1106
+ * MOS @ cfc8, and the unrolled STA abs,X screen clear @ cc63
1107
+ * also, just starting from the back of ROM and going up...
1108
+ */
1109
+ prevInstruction(address, pc) {
1110
+ const commonInstructions =
1111
+ /(RTS|B..|JMP|JSR|LD[AXY]|ST[AXY]|TA[XY]|T[XY]A|AD[DC]|SUB|SBC|CLC|SEC|CMP|EOR|ORA|AND|INC|DEC).*/;
1112
+ const uncommonInstructions = /.*,\s*([XY]|X\))$/;
1113
+
1114
+ address &= 0xffff;
1115
+ let bestAddr = address - 1;
1116
+ let bestScore = -1;
1117
+ for (let startingPoint = address - PrevInstructionWindow; startingPoint !== address; startingPoint++) {
1118
+ let score = 0;
1119
+ let addr = startingPoint & 0xffff;
1120
+ while (addr < address) {
1121
+ if (!this.isDocumented(addr)) break;
1122
+ const result = this.disassemble(addr);
1123
+ if (addr === pc) score += 10;
1124
+ if (result[0].match(commonInstructions) && !result[0].match(uncommonInstructions)) {
1125
+ score++;
1126
+ }
1127
+ if (result[1] === address) {
1128
+ if (score > bestScore) {
1129
+ bestScore = score;
1130
+ bestAddr = addr;
1131
+ }
1132
+ break;
1133
+ }
1134
+ addr = result[1];
1135
+ }
1136
+ }
1137
+ return bestAddr & 0xffff;
1060
1138
  }
1061
1139
  }
1062
1140
 
1063
- function makeCpuFunctions(cpu, opcodes, is65c12, cycleAccurate = true) {
1141
+ function makeCpuFunctions(cpu, opcodes, undocumented, is65c12, cycleAccurate = true) {
1064
1142
  function getInstruction(opcodeString, needsReg) {
1065
1143
  const split = opcodeString.split(" ");
1066
1144
  const opcode = split[0];
@@ -1097,7 +1175,6 @@ function makeCpuFunctions(cpu, opcodes, is65c12, cycleAccurate = true) {
1097
1175
  return ig.render();
1098
1176
 
1099
1177
  case "zp":
1100
- case "zpx": // Seems to be enough to keep tests happy, but needs investigation.
1101
1178
  case "zp,x":
1102
1179
  case "zp,y":
1103
1180
  if (arg === "zp") {
@@ -1309,12 +1386,10 @@ function makeCpuFunctions(cpu, opcodes, is65c12, cycleAccurate = true) {
1309
1386
  indent +
1310
1387
  [
1311
1388
  '"use strict";',
1312
- `// ${utils.hexbyte(opcodeNum)} - ${opcode}
1389
+ `// ${hexbyte(opcodeNum)} - ${opcode}
1313
1390
  `,
1314
1391
  ].concat(
1315
- opcode
1316
- ? getInstruction(opcode, !!needsReg)
1317
- : [`this.invalidOpcode(cpu, 0x${utils.hexbyte(opcodeNum)});`],
1392
+ opcode ? getInstruction(opcode, !!needsReg) : [`this.invalidOpcode(cpu, 0x${hexbyte(opcodeNum)});`],
1318
1393
  ).join(`
1319
1394
  ${indent}`)
1320
1395
  );
@@ -1327,7 +1402,7 @@ ${indent}`)
1327
1402
  const funcs = [];
1328
1403
  for (let opcodeNum = 0; opcodeNum < 256; ++opcodeNum) {
1329
1404
  const opcodeFunc = new Function("cpu", getIndentedSource(" ", opcodeNum, true));
1330
- let funcName = `exec_${utils.hexbyte(opcodeNum)}_`;
1405
+ let funcName = `exec_${hexbyte(opcodeNum)}_`;
1331
1406
  if (opcodes[opcodeNum]) {
1332
1407
  const instrName = opcodes[opcodeNum]
1333
1408
  .replace("()", "ind")
@@ -1409,7 +1484,7 @@ ${indent}`)
1409
1484
  Runner.prototype.run = generate6502JumpTable();
1410
1485
 
1411
1486
  return {
1412
- disassembler: new Disassemble6502(cpu, opcodes),
1487
+ disassembler: new Disassemble6502(cpu, opcodes, undocumented),
1413
1488
  runInstruction: new Runner(),
1414
1489
  opcodes: opcodes,
1415
1490
  getInstruction: getInstruction,
@@ -1417,13 +1492,13 @@ ${indent}`)
1417
1492
  }
1418
1493
 
1419
1494
  export function Cpu6502(cpu, { cycleAccurate = true } = {}) {
1420
- return makeCpuFunctions(cpu, opcodes6502, false, cycleAccurate);
1495
+ return makeCpuFunctions(cpu, opcodes6502, undocumented6502, false, cycleAccurate);
1421
1496
  }
1422
1497
 
1423
1498
  export function Cpu65c12(cpu, { cycleAccurate = true } = {}) {
1424
- return makeCpuFunctions(cpu, opcodes65c12, true, cycleAccurate);
1499
+ return makeCpuFunctions(cpu, opcodes65c12, undocumented65c12, true, cycleAccurate);
1425
1500
  }
1426
1501
 
1427
1502
  export function Cpu65c02(cpu, { cycleAccurate = true } = {}) {
1428
- return makeCpuFunctions(cpu, opcodes65c02, true, cycleAccurate);
1503
+ return makeCpuFunctions(cpu, opcodes65c02, undocumented65c02, true, cycleAccurate);
1429
1504
  }
package/src/6847.js CHANGED
@@ -1,7 +1,6 @@
1
- "use strict";
2
- import * as utils from "./utils.js";
3
1
  import * as fontData from "./6847_fontdata.js";
4
2
  import { encodeLineGrid } from "./video-filters/pixel-grid.js";
3
+ import { makeFast32 } from "./binary.js";
5
4
 
6
5
  const VDISPENABLE = 1 << 0,
7
6
  HDISPENABLE = 1 << 1,
@@ -114,7 +113,7 @@ const MODE_AG = 0x10; // graphics mode
114
113
 
115
114
  // The MC6847 datasheet specifies 3.579545 MHz, but 3.638004 is used here as an
116
115
  // empirical correction to align VSync/interrupt timing with the CPU clock.
117
- // TODO: revisit once full integration is testable.
116
+ // TODO(#1067) revisit once full integration is testable.
118
117
  const VdgClockMhz = 3.638004;
119
118
 
120
119
  export class Video6847 {
@@ -127,7 +126,7 @@ export class Video6847 {
127
126
 
128
127
  // 8 colours (alpha on MSB)
129
128
  //
130
- this.collook = utils.makeFast32(
129
+ this.collook = makeFast32(
131
130
  new Uint32Array([
132
131
  0xff000000, // #00000000, // black
133
132
  0xff03b91e, // #00ff00, // green
@@ -496,7 +495,7 @@ export class Video6847 {
496
495
  // Render data depending on display enable state.
497
496
  if (this.bitmapX >= 0 && this.bitmapX < 1024 && this.bitmapY < 625) {
498
497
  {
499
- // TODO: Add in the INTEXT modifiers to mode (if necessary)
498
+ // TODO(#1067) Add in the INTEXT modifiers to mode (if necessary)
500
499
  // blit into the fb32 buffer which is painted by VIDEO
501
500
  const textMode = (mode & MODE_AG) === 0; // 0x10 is the AG bit
502
501
  this.recordLineGrid(textMode);
@@ -1,5 +1,3 @@
1
- "use strict";
2
-
3
1
  // MC6847 internal character ROM data (PAL square pixel variant, 8x12).
4
2
  // Derived from the MAME project's MC6847 VDG emulation:
5
3
  // https://github.com/mamedev/mame/blob/master/src/devices/video/mc6847.cpp
package/src/acia.js CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  // Some info:
3
2
  // http://www.playvectrex.com/designit/lecture/UP12.HTM
4
3
  // https://books.google.com/books?id=wUecAQAAQBAJ&pg=PA431&lpg=PA431&dq=acia+tdre&source=bl&ots=mp-yF-mK-P&sig=e6aXkFRfiIOb57WZmrvdIGsCooI&hl=en&sa=X&ei=0g2fVdDyFIXT-QG8-JD4BA&ved=0CCwQ6AEwAw#v=onepage&q=acia%20tdre&f=false
@@ -52,7 +51,7 @@ export class Acia extends EventTarget {
52
51
  }
53
52
 
54
53
  reset() {
55
- // TODO: testing on a real beeb seems to suggest that reset also
54
+ // TODO(#1064) testing on a real beeb seems to suggest that reset also
56
55
  // clears CR bits (i.e. things stop working until CR is rewritten
57
56
  // with sane value). This disagrees with the datasheet.
58
57
  // CTS and DTD are based on external inputs so leave them alone.
@@ -199,7 +198,7 @@ export class Acia extends EventTarget {
199
198
  byte |= 0;
200
199
  if (this.sr & 0x01) {
201
200
  // Overrun.
202
- // TODO: this doesn't match the datasheet:
201
+ // TODO(#1064) this doesn't match the datasheet:
203
202
  // "The Overrun does not occur in the Status Register until the
204
203
  // valid character prior to Overrun has been read."
205
204
  this.sr |= 0xa0;
package/src/app/app.js CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  import { app, BrowserWindow, dialog, ipcMain, Menu, shell, nativeImage } from "electron";
3
2
  import Store from "electron-store";
4
3
  import * as fs from "fs";
package/src/app/args.js CHANGED
@@ -1,5 +1,3 @@
1
- "use strict";
2
-
3
1
  /**
4
2
  * Returns the command-line arguments the user actually passed to jsbeeb, with
5
3
  * the Electron binary path(s) and known runtime-injected flags stripped.