badline 0.14.0 → 0.15.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/CLAUDE.md +15 -5
  4. data/README.md +68 -8
  5. data/doc/pinned-behaviour.md +145 -0
  6. data/doc/snapshot-format.md +188 -38
  7. data/doc/snapshots.md +18 -1
  8. data/lib/badline/audio/bare_player.rb +4 -8
  9. data/lib/badline/audio/machine_player.rb +2 -12
  10. data/lib/badline/c128/banks.rb +1 -3
  11. data/lib/badline/c128/bus.rb +19 -1
  12. data/lib/badline/c128/mmu_pages.rb +7 -0
  13. data/lib/badline/c64/bus/extra_sids.rb +80 -0
  14. data/lib/badline/c64/bus.rb +1 -0
  15. data/lib/badline/c64/cartridge/action_replay.rb +6 -2
  16. data/lib/badline/c64/cartridge/comal80.rb +1 -0
  17. data/lib/badline/c64/cartridge/dinamic.rb +3 -1
  18. data/lib/badline/c64/cartridge/final_cartridge3.rb +1 -0
  19. data/lib/badline/c64/cartridge/fun_play.rb +2 -1
  20. data/lib/badline/c64/cartridge/game_system.rb +2 -1
  21. data/lib/badline/c64/cartridge/keyed_state.rb +155 -34
  22. data/lib/badline/c64/cartridge/magic_desk.rb +2 -1
  23. data/lib/badline/c64/cartridge/ocean.rb +1 -0
  24. data/lib/badline/c64/cartridge/pagefox.rb +1 -0
  25. data/lib/badline/c64/cartridge/rgcd.rb +2 -1
  26. data/lib/badline/c64/cartridge/super_games.rb +3 -2
  27. data/lib/badline/c64/cartridge/zaxxon.rb +1 -0
  28. data/lib/badline/c64/cartridge.rb +3 -3
  29. data/lib/badline/c64/keyed_modules.rb +6 -2
  30. data/lib/badline/chips/ted/beam.rb +13 -14
  31. data/lib/badline/chips/ted/fetches.rb +199 -0
  32. data/lib/badline/chips/ted/keyed_state.rb +156 -0
  33. data/lib/badline/chips/ted/modes.rb +134 -0
  34. data/lib/badline/chips/ted/palette.rb +43 -0
  35. data/lib/badline/chips/ted/picture.rb +182 -0
  36. data/lib/badline/chips/ted/sound/output.rb +86 -0
  37. data/lib/badline/chips/ted/sound.rb +284 -0
  38. data/lib/badline/chips/ted.rb +53 -6
  39. data/lib/badline/chips/tia.rb +12 -0
  40. data/lib/badline/drives/datasette/keyed_state.rb +7 -2
  41. data/lib/badline/drives/datasette.rb +22 -5
  42. data/lib/badline/frontend/boot.rb +3 -1
  43. data/lib/badline/frontend/controls.rb +7 -3
  44. data/lib/badline/frontend/keys.rb +14 -2
  45. data/lib/badline/frontend/media_slots.rb +11 -1
  46. data/lib/badline/frontend/menu/menu_dialogs.rb +2 -1
  47. data/lib/badline/frontend/menu/menu_media.rb +13 -3
  48. data/lib/badline/frontend/menu/menu_pages.rb +8 -8
  49. data/lib/badline/frontend/menu/menu_ports.rb +13 -1
  50. data/lib/badline/frontend/screen.rb +33 -12
  51. data/lib/badline/frontend/screens.rb +2 -1
  52. data/lib/badline/frontend/sound.rb +4 -4
  53. data/lib/badline/frontend/timeline.rb +19 -6
  54. data/lib/badline/machine/settling.rb +1 -1
  55. data/lib/badline/machine.rb +26 -6
  56. data/lib/badline/media/plus4_media.rb +68 -0
  57. data/lib/badline/media/program.rb +20 -6
  58. data/lib/badline/media.rb +33 -12
  59. data/lib/badline/options/family.rb +1 -0
  60. data/lib/badline/options/help.rb +30 -6
  61. data/lib/badline/options/table.rb +15 -2
  62. data/lib/badline/options/validation.rb +26 -4
  63. data/lib/badline/options.rb +37 -9
  64. data/lib/badline/plus4/bus.rb +77 -14
  65. data/lib/badline/plus4/cassette_sense.rb +21 -0
  66. data/lib/badline/plus4/keyboard_port.rb +35 -10
  67. data/lib/badline/plus4/keyed_modules.rb +99 -0
  68. data/lib/badline/plus4/keyed_parts.rb +143 -0
  69. data/lib/badline/plus4/keyed_state.rb +41 -0
  70. data/lib/badline/plus4.rb +92 -16
  71. data/lib/badline/snapshot/container.rb +2 -0
  72. data/lib/badline/snapshot/field_doc/markdown.rb +7 -6
  73. data/lib/badline/snapshot/field_doc.rb +46 -7
  74. data/lib/badline/snapshot/image.rb +12 -3
  75. data/lib/badline/snapshot/plus4_setup.rb +39 -0
  76. data/lib/badline/snapshot.rb +9 -4
  77. data/lib/badline/storage/g64_image.rb +1 -1
  78. data/lib/badline/storage/tap.rb +21 -12
  79. data/lib/badline/support/checkpoint.rb +3 -2
  80. data/lib/badline/support/plus4_checkpoint.rb +42 -0
  81. data/lib/badline/version.rb +1 -1
  82. metadata +15 -1
@@ -13,19 +13,20 @@ machine built afresh has: the default below. `word`, `byte` and
13
13
  `int & 0x..` are ints cut to the latch's width. A text that lists
14
14
  its values takes one of them. An optional int is absent when the
15
15
  latch holds nothing. A true drive's modules are named from its
16
- device number, `BL-D8` to `BL-D11`. Keys with `<N>` stand for each
17
- numbered unit. A default of "written only" is a key the load
18
- doesn't read, or reads only when the snapshot has it. Where the
19
- machines differ, the default is given for each, and a key only
20
- some of them have names them.
16
+ device number, `BL-D8` to `BL-D11`, and the SIDs past the first
17
+ are `BL-SID2` onwards, in the order they were fitted. Keys with
18
+ `<N>` stand for each numbered unit. A default of "written only" is
19
+ a key the load doesn't read, or reads only when the snapshot has
20
+ it. Where the machines differ, the default is given for each, and a
21
+ key only some of them have names them.
21
22
 
22
23
  ## BL-MACHINE
23
24
 
24
- Written by the C64, VIC-20, C128.
25
+ Written by the C64, VIC-20, C128, Plus/4.
25
26
 
26
27
  | Key | Type | Default |
27
28
  | --- | --- | --- |
28
- | `family` | text: c64 vic20 c128 | "c64" |
29
+ | `family` | text: c64 vic20 c128 plus4 | "c64" |
29
30
  | `region` | text: pal ntsc ntscold drean | C64: "pal" |
30
31
  | `vic_model` | text: mos6569 mos8565 mos8566 mos8564 | C64: "mos6569" |
31
32
  | `cia_model` | text: mos6526 mos6526a | C64: "mos6526" |
@@ -36,7 +37,7 @@ Written by the C64, VIC-20, C128.
36
37
  | `datasette` | bool | C64: true |
37
38
  | `board` | text: c64 pet64 gs max | C64: "c64" |
38
39
  | `cycles` | int | 0 |
39
- | `nmi_asserted` | bool | false |
40
+ | `nmi_asserted` | bool | C64: false; VIC-20: false; C128: false |
40
41
  | `cartridge_nmi` | bool | C64: false; C128: false |
41
42
  | `restore_pulse` | bool | C64: false; C128: false |
42
43
  | `freezing` | bool | C64: false |
@@ -48,7 +49,7 @@ Written by the C64, VIC-20, C128.
48
49
  | `ram` | text: unexpanded 3k 8k 16k 24k 32k all | VIC-20: "unexpanded" |
49
50
  | `bus.data` | byte | VIC-20: 253; C128: 255 |
50
51
  | `bus.video_data` | byte | VIC-20: 0 |
51
- | `model` | text: c128 c128ntsc c128dcr c128dcrntsc | C128: "c128" |
52
+ | `model` | text: c128 c128ntsc c128dcr c128dcrntsc | C128: "c128"; Plus/4: "plus4" |
52
53
  | `mode` | text: c64 c128 | C128: "c64" |
53
54
  | `bus.address` | word | C128: 65533 |
54
55
  | `clock_bits` | int | C128: 0 |
@@ -60,14 +61,20 @@ Written by the C64, VIC-20, C128.
60
61
  | `host.typist.code` | int, optional | C128: absent |
61
62
  | `host.typist.wait` | int | C128: 0 |
62
63
  | `host.typist.typing` | bool | C128: false |
64
+ | `ram_kb` | int | Plus/4: 64 or 16 |
65
+ | `tcbm` | bool | Plus/4: false |
63
66
 
64
67
  ## BL-RAM
65
68
 
66
- Written by the C64, VIC-20, C128.
69
+ Written by the C64, VIC-20, C128, Plus/4.
67
70
 
68
71
  | Key | Type | Default |
69
72
  | --- | --- | --- |
70
- | `ram` | bytes | C64: 65536 values; VIC-20: 49152 values; C128: 131072 values |
73
+ | `ram` | bytes | C64: 65536 values; VIC-20: 49152 values; C128: 131072 values; Plus/4: 65536 × 0 or 16384 × 0 |
74
+ | `bus.low_bank` | int & 0x3 | Plus/4: 0 |
75
+ | `bus.high_bank` | int & 0x3 | Plus/4: 0 |
76
+ | `bus.rom_selected` | bool | Plus/4: true |
77
+ | `bus.data` | byte | Plus/4: 255 |
71
78
 
72
79
  ## BL-COLORRAM
73
80
 
@@ -80,14 +87,14 @@ Written by the C64, VIC-20, C128.
80
87
 
81
88
  ## BL-CPU
82
89
 
83
- Written by the C64, C128, VIC-20.
90
+ Written by the C64, C128, Plus/4, VIC-20.
84
91
 
85
92
  | Key | Type | Default |
86
93
  | --- | --- | --- |
87
- | `port.ddr` | byte | C64: 0; C128: 0 |
88
- | `port.data` | byte | C64: 0; C128: 0 |
94
+ | `port.ddr` | byte | C64: 0; C128: 0; Plus/4: 0 |
95
+ | `port.data` | byte | C64: 0; C128: 0; Plus/4: 0 |
89
96
  | `port.floating` | byte | C64: 0; C128: 0 |
90
- | `pc` | word | C64: 64738; VIC-20: 64802; C128: 65341 |
97
+ | `pc` | word | C64: 64738; VIC-20: 64802; C128: 65341; Plus/4: 65526 |
91
98
  | `sp` | byte | 255 |
92
99
  | `a` | byte | 0 |
93
100
  | `x` | byte | 0 |
@@ -410,16 +417,18 @@ Written by the C64, C128.
410
417
 
411
418
  ## BL-TAPE
412
419
 
413
- Written by the C64, VIC-20, C128.
420
+ Written by the C64, VIC-20, C128, Plus/4.
414
421
 
415
422
  | Key | Type | Default |
416
423
  | --- | --- | --- |
417
424
  | `playing` | bool | false |
418
425
  | `motor` | bool | false |
419
426
  | `countdown` | int | 0 |
427
+ | `rise_at` | int | 0 |
428
+ | `low_recorded` | bool | false |
420
429
  | `tape.bytes` | bytes | [] |
421
- | `tape.path` | text | C64: "" |
422
- | `tape.position` | int | C64: 0 |
430
+ | `tape.path` | text | C64: ""; Plus/4: "" |
431
+ | `tape.position` | int | C64: 0; Plus/4: 0 |
423
432
 
424
433
  ## BL-RAMEXP
425
434
 
@@ -476,14 +485,14 @@ Written by the C64.
476
485
 
477
486
  ## BL-CART
478
487
 
479
- Written by the C64, VIC-20.
488
+ Written by the C64, VIC-20, Plus/4.
480
489
 
481
490
  | Key | Type | Default |
482
491
  | --- | --- | --- |
483
492
  | `geo_ram_kb` | int | C64: 0 |
484
493
  | `crt` | bytes | C64: [] |
485
- | `exrom` | int & 0x1 | C64: 1 |
486
- | `game` | int & 0x1 | C64: 0 |
494
+ | `exrom` | int & 0x1 | C64: 1 or 0 |
495
+ | `game` | int & 0x1 | C64: 0 or 1 |
487
496
  | `nmi` | bool | C64: false |
488
497
  | `button` | bool | C64: false |
489
498
  | `bank` | int | C64: 0 |
@@ -511,9 +520,17 @@ Written by the C64, VIC-20.
511
520
  | `flash_high.erasing_sectors` | ints | C64: [] |
512
521
  | `flash_high.windows` | ints | C64: [] |
513
522
  | `flash_high.status_windows` | ints | C64: [] |
523
+ | `high_bank` | int | C64: 0 |
524
+ | `active` | bool | C64: true |
525
+ | `ram` | bytes | C64: 8192 × 0 |
526
+ | `ram<N>.data` | bytes | C64: 8192 × 0 |
514
527
  | `roms` | int | VIC-20: 0 |
515
528
  | `rom0.bytes` | bytes | VIC-20: [] |
516
529
  | `rom0.start` | word | VIC-20: 0 |
530
+ | `c1lo` | bytes | Plus/4: [] |
531
+ | `c2lo` | bytes | Plus/4: [] |
532
+ | `c1hi` | bytes | Plus/4: [] |
533
+ | `c2hi` | bytes | Plus/4: [] |
517
534
 
518
535
  ## BL-TRAPDOS
519
536
 
@@ -547,29 +564,29 @@ Written by the C64.
547
564
 
548
565
  ## BL-D8
549
566
 
550
- Written by the C64, C128.
567
+ Written by the C64, C128, Plus/4.
551
568
 
552
569
  | Key | Type | Default |
553
570
  | --- | --- | --- |
554
- | `model` | text: 1541 1571 1581 | C64: "1541" or "1581"; C128: "1541" or "1571" |
571
+ | `model` | text: 1541 1571 1581 | C64: "1541" or "1581"; C128: "1541" or "1571"; Plus/4: "1541" |
555
572
  | `phase` | int | 0 |
556
573
  | `cycles` | int | 0 |
557
574
  | `so_pending` | bool | false |
558
- | `serial.host` | int | 255 |
575
+ | `serial.host` | int | C64: 255; C128: 255; Plus/4: 0 |
559
576
  | `atn` | bool | C64: true |
560
577
 
561
578
  ## BL-D8-RAM
562
579
 
563
- Written by the C64, C128.
580
+ Written by the C64, C128, Plus/4.
564
581
 
565
582
  | Key | Type | Default |
566
583
  | --- | --- | --- |
567
- | `data` | byte | C64: 234 or 175; C128: 234 |
568
- | `ram` | bytes | C64: 2048 × 0 or 8192 × 0; C128: 2048 × 0 |
584
+ | `data` | byte | C64: 234 or 175; C128: 234; Plus/4: 234 |
585
+ | `ram` | bytes | C64: 2048 × 0 or 8192 × 0; C128: 2048 × 0; Plus/4: 2048 × 0 |
569
586
 
570
587
  ## BL-D8-VIA1
571
588
 
572
- Written by the C64, C128.
589
+ Written by the C64, C128, Plus/4.
573
590
 
574
591
  | Key | Type | Default |
575
592
  | --- | --- | --- |
@@ -603,7 +620,7 @@ Written by the C64, C128.
603
620
  | `ifr.flags` | int & 0x7f | 0 |
604
621
  | `ifr.enable` | int & 0x7f | 0 |
605
622
  | `ca.control` | int & 0xf | 0 |
606
- | `ca.c1_high` | bool | true |
623
+ | `ca.c1_high` | bool | C64: true; C128: true; Plus/4: false |
607
624
  | `ca.c2_high` | bool | true |
608
625
  | `ca.handshake_low` | bool | false |
609
626
  | `ca.pulse` | int | 0 |
@@ -617,7 +634,7 @@ Written by the C64, C128.
617
634
 
618
635
  ## BL-D8-VIA2
619
636
 
620
- Written by the C64, C128.
637
+ Written by the C64, C128, Plus/4.
621
638
 
622
639
  | Key | Type | Default |
623
640
  | --- | --- | --- |
@@ -665,11 +682,11 @@ Written by the C64, C128.
665
682
 
666
683
  ## BL-D8-CPU
667
684
 
668
- Written by the C64, C128.
685
+ Written by the C64, C128, Plus/4.
669
686
 
670
687
  | Key | Type | Default |
671
688
  | --- | --- | --- |
672
- | `pc` | word | C64: 60064 or 44836; C128: 60064 |
689
+ | `pc` | word | C64: 60064 or 44836; C128: 60064; Plus/4: 60064 |
673
690
  | `sp` | byte | 255 |
674
691
  | `a` | byte | 0 |
675
692
  | `x` | byte | 0 |
@@ -691,7 +708,7 @@ Written by the C64, C128.
691
708
 
692
709
  ## BL-D8-DISK
693
710
 
694
- Written by the C64, C128.
711
+ Written by the C64, C128, Plus/4.
695
712
 
696
713
  | Key | Type | Default |
697
714
  | --- | --- | --- |
@@ -706,13 +723,13 @@ Written by the C64, C128.
706
723
 
707
724
  ## BL-D8-MECH
708
725
 
709
- Written by the C64, C128.
726
+ Written by the C64, C128, Plus/4.
710
727
 
711
728
  | Key | Type | Default |
712
729
  | --- | --- | --- |
713
- | `half_track` | int | C64: 35; C128: 37 |
714
- | `slip` | int & 0x3 | C64: 0; C128: 2 |
715
- | `side` | int | C64: 0 or 1; C128: 85 |
730
+ | `half_track` | int | C64: 35; C128: 37; Plus/4: 35 |
731
+ | `slip` | int & 0x3 | C64: 0; C128: 2; Plus/4: 0 |
732
+ | `side` | int | C64: 0 or 1; C128: 85; Plus/4: 0 |
716
733
  | `zone` | int & 0x3 | 3 |
717
734
  | `time` | int | 0 |
718
735
  | `read_clock_in` | int | 2097152 |
@@ -721,7 +738,7 @@ Written by the C64, C128.
721
738
  | `ones` | int | 0 |
722
739
  | `bits` | int | 0 |
723
740
  | `write_shift` | int | 0 |
724
- | `motor` | bool | C64: true or false; C128: true |
741
+ | `motor` | bool | C64: true or false; C128: true; Plus/4: true |
725
742
  | `led` | bool | true |
726
743
  | `sync` | bool | false |
727
744
  | `byte_ready` | bool | false |
@@ -843,6 +860,49 @@ Written by the C64, C128.
843
860
  | `registers` | ints | C128: [0, 0, 0, 0] |
844
861
  | `result` | int | C128: 0 |
845
862
 
863
+ ## BL-SID2
864
+
865
+ Written by the C64.
866
+
867
+ | Key | Type | Default |
868
+ | --- | --- | --- |
869
+ | `address` | word | 54304 |
870
+ | `model` | text: mos6581 mos8580 | "mos6581" |
871
+ | `registers` | bytes | 32 × 0 |
872
+ | `bus_value` | byte | 0 |
873
+ | `bus_ttl` | int | 0 |
874
+ | `voice<N>.wave.accumulator` | int & 0xffffff | 5592405 |
875
+ | `voice<N>.wave.shift_register` | int & 0x7fffff | 8388606 |
876
+ | `voice<N>.wave.shift_register_reset` | int | 0 |
877
+ | `voice<N>.wave.shift_pipeline` | int | 0 |
878
+ | `voice<N>.wave.delayed_triangle` | int & 0xfff | 0 |
879
+ | `voice<N>.wave.delayed_sawtooth` | int & 0xfff | 0 |
880
+ | `voice<N>.wave.msb_rising` | bool | false |
881
+ | `voice<N>.wave.floating` | int & 0xfff | 0 |
882
+ | `voice<N>.wave.floating_ttl` | int | 0 |
883
+ | `voice<N>.wave.pulse` | int & 0xfff | 4095 |
884
+ | `voice<N>.env.state` | text: attack decay_sustain release | "release" |
885
+ | `voice<N>.env.next_state` | text: attack decay_sustain release | "release" |
886
+ | `voice<N>.env.counter` | byte | 0 |
887
+ | `voice<N>.env.env<N>` | byte | 0 |
888
+ | `voice<N>.env.rate_counter` | int & 0x7fff | 0 |
889
+ | `voice<N>.env.rate_period` | int | 8 |
890
+ | `voice<N>.env.reset_rate_counter` | bool | false |
891
+ | `voice<N>.env.exponential_counter` | int | 0 |
892
+ | `voice<N>.env.exponential_period` | int | 1 |
893
+ | `voice<N>.env.state_pipeline` | int | 0 |
894
+ | `voice<N>.env.envelope_pipeline` | int | 0 |
895
+ | `voice<N>.env.exponential_pipeline` | int | 0 |
896
+ | `voice<N>.env.hold_zero` | bool | true |
897
+ | `filter.highpass` | int | 0 |
898
+ | `filter.bandpass` | int | 0 |
899
+ | `filter.lowpass` | int | 0 |
900
+ | `filter.unfiltered` | int | 0 |
901
+ | `filter.input` | int | 0 |
902
+ | `ext.lowpass` | int | 0 |
903
+ | `ext.highpass` | int | 0 |
904
+ | `ext.output` | int | 0 |
905
+
846
906
  ## BL-VICI
847
907
 
848
908
  Written by the VIC-20.
@@ -1045,3 +1105,93 @@ Written by the C128.
1045
1105
  | `int` | bool | false |
1046
1106
  | `nmi` | bool | false |
1047
1107
  | `nmi_pending` | bool | false |
1108
+
1109
+ ## BL-TED
1110
+
1111
+ Written by the Plus/4.
1112
+
1113
+ | Key | Type | Default |
1114
+ | --- | --- | --- |
1115
+ | `registers` | bytes | 32 × 0 |
1116
+ | `interrupt_flags` | byte | 0 |
1117
+ | `key_latch` | byte | 255 |
1118
+ | `blink` | int & 0xf | 0 |
1119
+ | `blink_on` | bool | false |
1120
+ | `timer<N>.count` | word | 0 |
1121
+ | `timer<N>.running` | bool | false |
1122
+ | `timer1.reload` | word | 0 |
1123
+ | `column` | int | 0 |
1124
+ | `line` | int & 0x1ff | 0 |
1125
+ | `ba` | bool | false |
1126
+ | `cpu_slots` | int | 1 |
1127
+ | `fetch.patterns` | bytes | 40 × 0 |
1128
+ | `fetch.pointers` | bytes | 40 × 0 |
1129
+ | `fetch.attributes` | bytes | 40 × 0 |
1130
+ | `fetch.next_attributes` | bytes | 40 × 0 |
1131
+ | `fetch.char_position` | int & 0x3ff | 0 |
1132
+ | `fetch.line_position` | int & 0x3ff | 0 |
1133
+ | `fetch.row_line` | int & 0x7 | 0 |
1134
+ | `fetch.row_active` | bool | false |
1135
+ | `fetch.dma` | text: none attributes pointers | "none" |
1136
+ | `fetch.patterns_on` | bool | false |
1137
+ | `scanline` | int | 0 |
1138
+ | `window_open` | bool | false |
1139
+ | `line.layout` | int | 0 |
1140
+ | `line.painted` | int | 0 |
1141
+ | `display` | bytes | 119808 × 0 |
1142
+
1143
+ ## BL-TEDSOUND
1144
+
1145
+ Written by the Plus/4.
1146
+
1147
+ | Key | Type | Default |
1148
+ | --- | --- | --- |
1149
+ | `voice<N>.phase` | int & 0x1 | 1 |
1150
+ | `voice<N>.toggle_in` | int, optional | absent |
1151
+ | `noise` | byte | 255 |
1152
+ | `ran_ago` | int | 0 |
1153
+
1154
+ ## BL-KEYPORT
1155
+
1156
+ Written by the Plus/4.
1157
+
1158
+ | Key | Type | Default |
1159
+ | --- | --- | --- |
1160
+ | `output` | byte | 255 |
1161
+
1162
+ ## BL-USERPORT
1163
+
1164
+ Written by the Plus/4.
1165
+
1166
+ | Key | Type | Default |
1167
+ | --- | --- | --- |
1168
+ | `output` | byte | 255 |
1169
+
1170
+ ## BL-ACIA
1171
+
1172
+ Written by the Plus/4.
1173
+
1174
+ | Key | Type | Default |
1175
+ | --- | --- | --- |
1176
+ | `command` | byte | 0 |
1177
+ | `control` | byte | 0 |
1178
+
1179
+ ## BL-TIA1
1180
+
1181
+ Written by the Plus/4.
1182
+
1183
+ | Key | Type | Default |
1184
+ | --- | --- | --- |
1185
+ | `ports` | ints | [0, 0, 0] |
1186
+ | `directions` | ints | [0, 0, 0] |
1187
+ | `inputs` | ints | [0, 0, 0] |
1188
+
1189
+ ## BL-TIA2
1190
+
1191
+ Written by the Plus/4.
1192
+
1193
+ | Key | Type | Default |
1194
+ | --- | --- | --- |
1195
+ | `ports` | ints | [0, 0, 0] |
1196
+ | `directions` | ints | [0, 0, 0] |
1197
+ | `inputs` | ints | [0, 0, 0] |
data/doc/snapshots.md CHANGED
@@ -87,7 +87,9 @@ blocks given to `on_init` that hadn't run yet, which a restore reports.
87
87
  Every model saves this way, PAL, NTSC, old NTSC and Drean alike, and so
88
88
  does a machine with an REU, with its RAM and its registers. A snapshot
89
89
  restores into a machine with the same chip models, region, RAM
90
- expansion, REU, KERNAL, datasette and board.
90
+ expansion, REU, KERNAL, datasette and board. The second and third SIDs
91
+ the SID player fits for a stereo tune are saved too, each with its
92
+ address and model, and a restore fits them.
91
93
 
92
94
  Text typed with `type_text` that the machine hasn't taken yet, such as
93
95
  autostart's `RUN`, carries over, and so do the keys a C128 is typing on
@@ -123,6 +125,21 @@ do, and holds only badline's modules, so x128 doesn't open it, and
123
125
  badline doesn't open x128's. Restoring a C128 snapshot while another machine runs swaps the
124
126
  machine in the window, as for the VIC-20.
125
127
 
128
+ ## The Plus/4
129
+
130
+ The Plus/4, the C16 and the C116 save and restore the same way. A
131
+ snapshot of one holds its RAM with the ROM bank latch and whether ROM or
132
+ RAM is selected, the cartridges' ROMs in each slot, the CPU with its
133
+ port, the TED with its timers, its counters and its picture, the TED's
134
+ sound, the keyboard's 6529, the Plus/4's user port 6529 and ACIA, a TCBM
135
+ interface's TIAs, the tape with its place on it, the disk or directory
136
+ device 8 serves through the traps and a true 1541 or 1581. It restores
137
+ into a machine of the same model with the same RAM, and the BASIC,
138
+ KERNAL and function ROMs come with the model. Its file names its machine
139
+ `PLUS4`, as xplus4's do, and holds only badline's modules, so xplus4
140
+ doesn't open it, and badline doesn't open xplus4's. `--vice` and
141
+ `computer.export_snapshot(path)` refuse a Plus/4.
142
+
126
143
  ## VICE
127
144
 
128
145
  Snapshots use VICE's `.vsf` format, and badline's own modules are named
@@ -93,10 +93,7 @@ module Badline
93
93
  sections << Snapshot::Keyed.section("BL-CIA1") { |out| bus.cia1.save_fields(out) }
94
94
  sections << Snapshot::Keyed.section("BL-CIA2") { |out| bus.cia2.save_fields(out) }
95
95
  sections << Snapshot::Keyed.section("BL-SID") { |out| bus.sid.save_fields(out) }
96
- @extras.each_with_index do |sid, index|
97
- sections << Snapshot::Keyed.section("BL-SID#{index + 2}") { |out| sid.save_fields(out) }
98
- end
99
- sections
96
+ bus.save_extra_sid_modules(sections)
100
97
  end
101
98
 
102
99
  # Takes a rig that hasn't started to where #save_modules found one, in
@@ -152,10 +149,9 @@ module Badline
152
149
  bus.vic.load_fields(modules.required("BL-VIC"))
153
150
  bus.cia1.load_fields(modules.required("BL-CIA1"))
154
151
  bus.cia2.load_fields(modules.required("BL-CIA2"))
155
- @stereo.sids.each_with_index do |sid, index|
156
- sid.load_fields(modules.required(index.zero? ? "BL-SID" : "BL-SID#{index + 1}"))
157
- sid.restored!
158
- end
152
+ bus.sid.load_fields(modules.required("BL-SID"))
153
+ bus.load_extra_sid_modules(modules)
154
+ @stereo.sids.each(&:restored!)
159
155
  bus.vic.restored!
160
156
  bus.cia1.restored!
161
157
  bus.cia2.restored!
@@ -40,23 +40,13 @@ module Badline
40
40
  def clock_hz = @computer.region.clock_hz
41
41
 
42
42
  # The whole machine as its keyed snapshot modules, as it stands between
43
- # frames, and the extra SIDs as BL-SID2 and BL-SID3, once it plays.
44
- def save_modules
45
- sections = @computer.save_modules
46
- @extras.each_with_index do |sid, index|
47
- sections << Snapshot::Keyed.section("BL-SID#{index + 2}") { |out| sid.save_fields(out) }
48
- end
49
- sections
50
- end
43
+ # frames, with the extra SIDs as BL-SID2 and BL-SID3, once it plays.
44
+ def save_modules = @computer.save_modules
51
45
 
52
46
  # Takes a machine that hasn't started to where #save_modules found one,
53
47
  # in place of #start.
54
48
  def load_modules(modules)
55
49
  @computer.load_modules(modules)
56
- @extras.each_with_index do |sid, index|
57
- sid.load_fields(modules.required("BL-SID#{index + 2}"))
58
- sid.restored!
59
- end
60
50
  @stereo.synthesize!
61
51
  @started = true
62
52
  end
@@ -21,9 +21,7 @@ module Badline
21
21
  # The CR value the KERNAL's table holds for the bank.
22
22
  def configuration(bank) = @kernal_rom.peek(CONFIGURATIONS + (bank & 0x0f))
23
23
 
24
- def write(bank, addr, bytes)
25
- bytes.each_with_index { |byte, i| @ram.poke(physical(bank, (addr + i) & 0xffff), byte) }
26
- end
24
+ def poke(bank, addr, value) = @ram.poke(physical(bank, addr), value)
27
25
 
28
26
  private
29
27
 
@@ -228,7 +228,25 @@ module Badline
228
228
  system_rom_peek(addr)
229
229
  end
230
230
 
231
- def write_bank(bank, addr, bytes) = @banks.write(bank, addr, bytes)
231
+ # Stores +bytes+ from +addr+ on in the C128 KERNAL's bank number
232
+ # +bank+, as its INDSTA does, with CR set to the bank's CR value for
233
+ # each byte and put back after it: into the chips where that value
234
+ # maps I/O, the MMU's at $D500 among them, into nothing at $FF00-$FF04,
235
+ # where the configuration registers take the byte, and otherwise into
236
+ # the RAM the bank reaches (Banks).
237
+ def write_bank(bank, addr, bytes)
238
+ cr = @mmu.cr
239
+ io = @banks.configuration(bank).nobits?(0x01)
240
+ bytes.each_with_index do |byte, i|
241
+ address = (addr + i) & 0xffff
242
+ if io && (address & 0xf000) == 0xd000
243
+ bank_io_poke(address, byte)
244
+ elsif !address.between?(0xff00, 0xff04)
245
+ @banks.poke(bank, address, byte)
246
+ end
247
+ end
248
+ @mmu.poke_configuration(0xff00, cr) unless @mmu.cr == cr
249
+ end
232
250
 
233
251
  def install_debug_register(&)
234
252
  @debug_page = DebugPage.new(@open_bus, &)
@@ -66,6 +66,13 @@ module Badline
66
66
  @bank_io_reads[addr >> 8].peek(addr)
67
67
  end
68
68
 
69
+ # Writes the chip at +addr+, in $D000-$DFFF, as C128 mode's I/O maps it.
70
+ def bank_io_poke(addr, value)
71
+ lay_out_io(@bank_io_reads, @bank_io_writes)
72
+ @bank_io_writes[0xd5] = @mmu
73
+ @bank_io_writes[addr >> 8].poke(addr, value)
74
+ end
75
+
69
76
  # The bank CR picks, with common RAM in bank 0, then the pages page 0
70
77
  # and page 1 move to.
71
78
  def map_ram_pages
@@ -7,7 +7,16 @@ module Badline
7
7
  # them, each over its 32 bytes of the I/O area (SIDSlots). The SID
8
8
  # player's machine has nothing else in I/O 1 and 2, so the rest of a
9
9
  # page there reads the open bus.
10
+ #
11
+ # A snapshot holds each in its own keyed module, BL-SID2 onwards in the
12
+ # order they were fitted, with the address it sits at and its model
13
+ # ahead of the SID's own fields.
10
14
  module ExtraSIDs
15
+ SID_MODEL_NAMES = %w[mos6581 mos8580].freeze
16
+
17
+ # The SIDs fitted past the first, in the order they were fitted.
18
+ attr_reader :extra_sids
19
+
11
20
  # Fits a SID at the address it was built at, where it takes its 32
12
21
  # bytes over from whatever maps there.
13
22
  def add_sid(sid)
@@ -18,14 +27,85 @@ module Badline
18
27
  @sid_slots << slots
19
28
  end
20
29
  slots.place(sid)
30
+ @extra_sids << sid
31
+ update_overlays!
32
+ end
33
+
34
+ # Takes the extra SIDs out, giving their addresses back to what maps
35
+ # there.
36
+ def remove_extra_sids
37
+ @sid_slots.clear
38
+ @extra_sids.clear
21
39
  update_overlays!
22
40
  end
23
41
 
42
+ def save_extra_sid_modules(sections)
43
+ @extra_sids.each_with_index do |sid, index|
44
+ sections << Snapshot::Keyed.section("BL-SID#{index + 2}") do |out|
45
+ out.int("address", sid.start).text("model", sid.model.to_s)
46
+ sid.save_fields(out)
47
+ end
48
+ end
49
+ sections
50
+ end
51
+
52
+ # Fits the extra SIDs the snapshot's modules hold and loads them. A
53
+ # SID already fitted at the same address stays, on the model the
54
+ # snapshot names, and one the snapshot hasn't comes out. A module
55
+ # whose address no SID fits at is left out, and reported.
56
+ def load_extra_sid_modules(modules)
57
+ addresses = []
58
+ inputs = extra_sid_inputs(modules, addresses)
59
+ models = inputs.map { |input| sid_model_named(input.choice("model", SID_MODEL_NAMES, "mos6581")) }
60
+ refit_sids(addresses, models)
61
+ @extra_sids.each_with_index do |sid, index|
62
+ model = models[index]
63
+ sid.model = model unless sid.model == model
64
+ sid.load_fields(inputs[index])
65
+ end
66
+ end
67
+
24
68
  private
25
69
 
26
70
  def map_extra_sids
27
71
  @sid_slots.each { |slots| @read_pages[slots.page] = @write_pages[slots.page] = slots }
28
72
  end
73
+
74
+ def extra_sid_inputs(modules, taken)
75
+ inputs = []
76
+ number = 2
77
+ input = modules.optional("BL-SID2")
78
+ while input
79
+ address = input.word("address", 0xd420)
80
+ if sid_fits?(address) && !taken.include?(address)
81
+ inputs << input
82
+ taken << address
83
+ else
84
+ input.adjust(format("no SID fits at $%04x, so it's left out", address))
85
+ end
86
+ number += 1
87
+ input = modules.optional("BL-SID#{number}")
88
+ end
89
+ inputs
90
+ end
91
+
92
+ # A SID's 32 bytes past the first SID's own, in $D420-$D7FF, or in
93
+ # I/O 1 or 2.
94
+ def sid_fits?(address)
95
+ address.nobits?(0x1f) && ((0xd420..0xd7e0).cover?(address) || (0xde00..0xdfe0).cover?(address))
96
+ end
97
+
98
+ def sid_model_named(name) = name == "mos8580" ? :mos8580 : :mos6581
99
+
100
+ def refit_sids(addresses, models)
101
+ fitted = @extra_sids.dup
102
+ return if fitted.map(&:start) == addresses
103
+
104
+ remove_extra_sids
105
+ addresses.each_with_index do |address, index|
106
+ add_sid(fitted.find { |sid| sid.start == address } || Chips::SID.new(model: models[index], at: address))
107
+ end
108
+ end
29
109
  end
30
110
  end
31
111
  end
@@ -91,6 +91,7 @@ module Badline
91
91
  @cia1.on_port_b4_change { |high| @vic.lightpen_level(high) }
92
92
  @sid = Chips::SID.new(model: sid_model, pots: @control_ports)
93
93
  @sid_slots = []
94
+ @extra_sids = []
94
95
 
95
96
  @datasette = Drives::Datasette.new
96
97
  @datasette.on_flag { @cia1.flag! }
@@ -22,6 +22,10 @@ module Badline
22
22
  include Freezer
23
23
 
24
24
  CONTROL_MODES = %i[rom8k rom16k off ultimax].freeze
25
+
26
+ # Ultimax mode with the RAM at ROML and I/O 2 and the first bank at
27
+ # ROMH, as a freeze leaves the register.
28
+ FREEZE_CONTROL = 0x23
25
29
  IO2_PAGE = 0x1f00
26
30
 
27
31
  def connect(ram:, open_bus:)
@@ -59,8 +63,7 @@ module Badline
59
63
 
60
64
  def freeze!
61
65
  @active = true
62
- @rom = bank(@rom_banks, 0)
63
- select(:ultimax, @isolated, io2_ram: true)
66
+ control(FREEZE_CONTROL)
64
67
  end
65
68
 
66
69
  private
@@ -72,6 +75,7 @@ module Badline
72
75
  end
73
76
 
74
77
  def control(value)
78
+ @control = value
75
79
  self.nmi = false if value.anybits?(0x40)
76
80
  @rom = bank(@rom_banks, (value >> 3) & 0x03)
77
81
  configure(value)
@@ -37,6 +37,7 @@ module Badline
37
37
  def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
38
38
 
39
39
  def select(number, mode)
40
+ @bank = number
40
41
  @roml = bank(@roml_banks, number)
41
42
  @romh = bank(@romh_banks, number)
42
43
  self.mode = mode