badline 0.16.1 → 0.17.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/CLAUDE.md +1 -1
  4. data/README.md +102 -12
  5. data/doc/media.md +5 -2
  6. data/doc/pinned-behaviour.md +61 -0
  7. data/doc/snapshot-format.md +376 -69
  8. data/doc/snapshots.md +148 -14
  9. data/lib/badline/cbm2/acia.rb +10 -0
  10. data/lib/badline/cbm2/bus.rb +263 -0
  11. data/lib/badline/cbm2/cpu.rb +58 -0
  12. data/lib/badline/cbm2/crtc_video.rb +133 -0
  13. data/lib/badline/cbm2/ieee_drive.rb +18 -0
  14. data/lib/badline/cbm2/ieee_layout.rb +69 -0
  15. data/lib/badline/cbm2/ieee_ports.rb +104 -0
  16. data/lib/badline/cbm2/ieee_traps.rb +81 -0
  17. data/lib/badline/cbm2/keyboard_ports.rb +60 -0
  18. data/lib/badline/cbm2/keyed_modules.rb +67 -0
  19. data/lib/badline/cbm2/keyed_state.rb +91 -0
  20. data/lib/badline/cbm2/model.rb +44 -0
  21. data/lib/badline/cbm2/p500/control_ports.rb +80 -0
  22. data/lib/badline/cbm2/p500/ieee_layout.rb +41 -0
  23. data/lib/badline/cbm2/p500/keyed_state.rb +85 -0
  24. data/lib/badline/cbm2/p500/model.rb +39 -0
  25. data/lib/badline/cbm2/p500/pages.rb +57 -0
  26. data/lib/badline/cbm2/p500.rb +262 -0
  27. data/lib/badline/cbm2/sid_page.rb +19 -0
  28. data/lib/badline/cbm2.rb +267 -0
  29. data/lib/badline/{plus4 → chips}/acia/frames.rb +1 -1
  30. data/lib/badline/chips/acia/keyed_state.rb +47 -0
  31. data/lib/badline/chips/acia.rb +307 -0
  32. data/lib/badline/chips/crtc.rb +3 -2
  33. data/lib/badline/chips/riot/fast_forward.rb +72 -0
  34. data/lib/badline/chips/riot/keyed_parts.rb +69 -0
  35. data/lib/badline/chips/riot/timer.rb +80 -0
  36. data/lib/badline/chips/riot.rb +162 -0
  37. data/lib/badline/chips/rriot/keyed_state.rb +30 -0
  38. data/lib/badline/chips/rriot.rb +132 -0
  39. data/lib/badline/chips/tpi/keyed_state.rb +38 -0
  40. data/lib/badline/chips/tpi.rb +233 -0
  41. data/lib/badline/chips/vic/bank.rb +60 -5
  42. data/lib/badline/chips/vic/sprite.rb +4 -3
  43. data/lib/badline/drives/datasette/keyed_state.rb +9 -1
  44. data/lib/badline/drives/datasette.rb +27 -3
  45. data/lib/badline/drives/model8050/disk.rb +163 -0
  46. data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
  47. data/lib/badline/drives/model8050/idle.rb +177 -0
  48. data/lib/badline/drives/model8050/ieee_port.rb +147 -0
  49. data/lib/badline/drives/model8050/ip_bus.rb +93 -0
  50. data/lib/badline/drives/model8050/keyed_state.rb +160 -0
  51. data/lib/badline/drives/model8050/mechanism.rb +230 -0
  52. data/lib/badline/drives/model8050/parts.rb +37 -0
  53. data/lib/badline/drives/model8050.rb +243 -0
  54. data/lib/badline/frontend/app.rb +10 -3
  55. data/lib/badline/frontend/boot.rb +1 -1
  56. data/lib/badline/frontend/controls.rb +51 -15
  57. data/lib/badline/frontend/gamepads.rb +3 -3
  58. data/lib/badline/frontend/keys.rb +61 -2
  59. data/lib/badline/frontend/media_slots.rb +1 -1
  60. data/lib/badline/frontend/menu/menu_media.rb +14 -11
  61. data/lib/badline/frontend/menu/menu_pages.rb +18 -4
  62. data/lib/badline/frontend/menu/menu_ports.rb +40 -4
  63. data/lib/badline/frontend/menu/pause_menu.rb +1 -1
  64. data/lib/badline/frontend/pet_keyboard.rb +23 -7
  65. data/lib/badline/frontend/timeline.rb +19 -10
  66. data/lib/badline/kernal_trap/dos/disk_commands.rb +80 -0
  67. data/lib/badline/kernal_trap/dos/status.rb +3 -0
  68. data/lib/badline/kernal_trap/dos.rb +18 -5
  69. data/lib/badline/kernal_trap/ieee.rb +4 -2
  70. data/lib/badline/kernal_trap/ieee_layout.rb +95 -43
  71. data/lib/badline/machine/ieee_drives.rb +194 -0
  72. data/lib/badline/machine/settling.rb +4 -0
  73. data/lib/badline/machine.rb +23 -6
  74. data/lib/badline/media/cbm2_media.rb +29 -0
  75. data/lib/badline/media/program.rb +6 -3
  76. data/lib/badline/media/true_drive.rb +38 -9
  77. data/lib/badline/media.rb +15 -6
  78. data/lib/badline/options/family.rb +15 -3
  79. data/lib/badline/options/help.rb +16 -2
  80. data/lib/badline/options/table.rb +18 -4
  81. data/lib/badline/options/validation.rb +27 -3
  82. data/lib/badline/options.rb +21 -7
  83. data/lib/badline/pet/cassettes.rb +22 -1
  84. data/lib/badline/pet/ieee_drive.rb +5 -64
  85. data/lib/badline/pet/ieee_ports.rb +7 -0
  86. data/lib/badline/pet.rb +15 -4
  87. data/lib/badline/plus4/acia.rb +4 -298
  88. data/lib/badline/plus4/keyed_parts.rb +0 -44
  89. data/lib/badline/plus4.rb +1 -0
  90. data/lib/badline/roms/8050/dos-2.7.rom +0 -0
  91. data/lib/badline/roms/8050/fdc-901869-01.rom +0 -0
  92. data/lib/badline/roms/8050/fdc-901885-04.rom +0 -0
  93. data/lib/badline/roms/README +111 -0
  94. data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
  95. data/lib/badline/roms/cbm2/basic128.rom +0 -0
  96. data/lib/badline/roms/cbm2/basic256.rom +0 -0
  97. data/lib/badline/roms/cbm2/characters-600.rom +0 -0
  98. data/lib/badline/roms/cbm2/characters-700.rom +0 -0
  99. data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
  100. data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
  101. data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
  102. data/lib/badline/roms/cbm2/p500-kernal.rom +0 -0
  103. data/lib/badline/snapshot/cbm2_setup.rb +53 -0
  104. data/lib/badline/snapshot/container.rb +7 -2
  105. data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
  106. data/lib/badline/snapshot/field_doc.rb +38 -3
  107. data/lib/badline/snapshot/image.rb +62 -9
  108. data/lib/badline/snapshot/vice/cbm2.rb +93 -0
  109. data/lib/badline/snapshot/vice/cbm2_acia.rb +75 -0
  110. data/lib/badline/snapshot/vice/cbm2_crtc.rb +127 -0
  111. data/lib/badline/snapshot/vice/cbm2_mains.rb +49 -0
  112. data/lib/badline/snapshot/vice/cbm2_mem.rb +75 -0
  113. data/lib/badline/snapshot/vice/cbm2_restore.rb +204 -0
  114. data/lib/badline/snapshot/vice/cbm2_tpi.rb +58 -0
  115. data/lib/badline/snapshot/vice/cias.rb +13 -9
  116. data/lib/badline/snapshot/vice/p500.rb +96 -0
  117. data/lib/badline/snapshot/vice/p500_restore.rb +130 -0
  118. data/lib/badline/snapshot/vice/p500_vicii.rb +166 -0
  119. data/lib/badline/snapshot/vice/pet.rb +7 -3
  120. data/lib/badline/snapshot/vice/pet_cpu.rb +5 -2
  121. data/lib/badline/snapshot/vice/pet_crtc.rb +10 -15
  122. data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
  123. data/lib/badline/snapshot/vice/pet_drive8050.rb +216 -0
  124. data/lib/badline/snapshot/vice/pet_restore.rb +70 -6
  125. data/lib/badline/snapshot/vice/pet_via.rb +6 -5
  126. data/lib/badline/snapshot/vice/sid_registers.rb +2 -1
  127. data/lib/badline/snapshot/vice/vicii_import.rb +10 -6
  128. data/lib/badline/snapshot/vice.rb +3 -0
  129. data/lib/badline/snapshot.rb +11 -3
  130. data/lib/badline/storage/crt_file.rb +7 -1
  131. data/lib/badline/storage/d64_image.rb +7 -0
  132. data/lib/badline/storage/d80_image.rb +17 -1
  133. data/lib/badline/storage/d81_image.rb +14 -0
  134. data/lib/badline/storage/disk_image/bam.rb +18 -3
  135. data/lib/badline/storage/disk_image/formatting.rb +69 -0
  136. data/lib/badline/storage/disk_image/writing.rb +35 -0
  137. data/lib/badline/storage/disk_image.rb +2 -0
  138. data/lib/badline/storage.rb +1 -0
  139. data/lib/badline/support/cbm2_checkpoint.rb +48 -0
  140. data/lib/badline/support/p500_checkpoint.rb +35 -0
  141. data/lib/badline/version.rb +1 -1
  142. data/lib/badline.rb +1 -0
  143. metadata +72 -2
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "badline/storage/crt_file"
3
4
  require "badline/storage/p00"
4
5
  require "badline/storage/tap"
5
6
 
@@ -13,7 +14,8 @@ module Badline
13
14
 
14
15
  private
15
16
 
16
- # Takes `sid`, `c64`, `vic20`, `c128`, `plus4` or `pet` when given first.
17
+ # Takes `sid`, `c64`, `vic20`, `c128`, `plus4`, `pet` or `cbm2` when
18
+ # given first.
17
19
  def subcommand(args)
18
20
  case args.first
19
21
  when "sid" then @sid_command = true
@@ -22,6 +24,7 @@ module Badline
22
24
  when "c128" then @family = :c128
23
25
  when "plus4" then @family = :plus4
24
26
  when "pet" then @family = :pet
27
+ when "cbm2" then @family = :cbm2
25
28
  else return
26
29
  end
27
30
  @family_named = !@sid_command
@@ -34,23 +37,32 @@ module Badline
34
37
  when :c128 then C128_MODELS
35
38
  when :plus4 then PLUS4_MODELS
36
39
  when :pet then PET_MODELS
40
+ when :cbm2 then CBM2_MODELS
37
41
  else MODELS
38
42
  end
39
43
  end
40
44
 
41
45
  # Without a family named, a program that loads at C128_BASIC_START is
42
- # the C128's, and an 8050's or 8250's disk or a PET tape the PET's. A
43
- # program that loads where PET BASIC starts could be a VIC-20's too.
46
+ # the C128's, an 8050's or 8250's disk or a PET tape the PET's, and a
47
+ # cartridge signed as a CBM-II's the CBM-II's. A program that loads
48
+ # where PET BASIC starts could be a VIC-20's too.
44
49
  def pick_family
45
50
  return if @family_named || @sid_command || @media_path.nil? || !File.file?(@media_path)
46
51
 
47
52
  if pet_media?
48
53
  @family = :pet
54
+ elsif cbm2_cartridge?
55
+ @family = :cbm2
49
56
  elsif Media::Extensions::PROGRAMS.include?(File.extname(@media_path).downcase)
50
57
  @family = :c128 if Storage::P00.load_address(@media_path) == C128_BASIC_START
51
58
  end
52
59
  end
53
60
 
61
+ # Whether the media is a cartridge signed as a CBM-II's.
62
+ def cbm2_cartridge?
63
+ Media::Extensions.kind(@media_path) == :cartridge && Storage::CRTFile.cbm2?(@media_path)
64
+ end
65
+
54
66
  # Whether the media is an 8050's or 8250's disk or a PET tape.
55
67
  def pet_media?
56
68
  kind = Media::Extensions.kind(@media_path)
@@ -86,6 +86,7 @@ module Badline
86
86
  #{@program} c128 [options] [media]
87
87
  #{@program} plus4 [options] [media]
88
88
  #{@program} pet [options] [media]
89
+ #{@program} cbm2 [options] [media]
89
90
  #{@program} --headless [options] tune.sid
90
91
  #{@program} [options] tune.sid --audio-out FILE
91
92
  #{@program} sid [options] FILE|DIR...
@@ -96,6 +97,7 @@ module Badline
96
97
  c128 Run a C128 in C128 mode, the one --model names
97
98
  plus4 Run a Plus/4 or a C16, the one --model names
98
99
  pet Run a PET or a CBM, the one --model names
100
+ cbm2 Run a CBM-II, a B series or a P500, the one --model names
99
101
  sid Play .sid tunes and directories of them
100
102
 
101
103
  #{media_kinds}
@@ -135,6 +137,18 @@ module Badline
135
137
  it with the DIN character ROM, and --jumpers ram9,rama puts RAM at
136
138
  $9000 and $A000, as xpet's -petram9 and -petramA do.
137
139
 
140
+ The CBM-II takes programs, disks, .d80 and .d82 among them, and
141
+ cartridges, and a cartridge made for it starts one without `cbm2`.
142
+ --kernal 03b fits the 1983 KERNAL in place of 04a, and --60hz runs
143
+ it on 60 Hz mains. Its --true-drive makes device 8 an emulated 2031
144
+ on its IEEE-488 bus, which reads .d64 and .g64 images, and a .d80
145
+ or a .d82 then goes in an emulated 8050 or 8250. Its keys type what
146
+ the host's keys type, F12 is RUN/STOP, Alt with F9 or F10 presses
147
+ its F9 or F10, and the pause menu's KEYBOARD page says where the
148
+ rest are. --model 510 or 510ntsc runs the P500, on the VIC-II with
149
+ KERNAL 02, whose keys are the B series' and whose two joysticks Tab
150
+ switches to.
151
+
138
152
  --headless plays a .sid tune on the host's audio device without the
139
153
  window, and --audio-out renders it to 16-bit PCM instead. The
140
154
  container follows the file's extension, .wav or .aiff. Played on a
@@ -156,8 +170,8 @@ module Badline
156
170
  in --at 3500:key=space or --at 250,500:screenshot=shot%05d.bmp.
157
171
  key=NAME presses a key (space, return, a, f1, run_stop, restore, and
158
172
  on the C128 esc, help, keypad1 and the rest, on the Plus/4
159
- esc, help and f1 to f3, and on the PET rvs, escape and kp1) or a
160
- joystick's
173
+ esc, help and f1 to f3, on the PET rvs, escape and kp1, and on
174
+ the CBM-II escape, norm_graph and kp00) or a joystick's
161
175
  direction or fire (joy1-up, joy2-fire) for #{Event::HOLD} frames, and
162
176
  pushes the C128's caps_lock or forty_eighty, which lock. type=TEXT
163
177
  types it, with \\n for RETURN. insert=FILE swaps in a disk, a list's
@@ -22,6 +22,16 @@ module Badline
22
22
  # default.
23
23
  PET_MODELS = %w[8032 8096 8296 4032 3032 3032b 2001].freeze
24
24
 
25
+ # The CBM-II's B series, as CBM2::Model names them, the 610 first, the
26
+ # default, and the P500, as CBM2::P500::Model names them.
27
+ CBM2_MODELS = %w[610 620 710 720 510 510ntsc].freeze
28
+
29
+ # The B series' KERNALs --kernal names, 901244-04a first, the default.
30
+ CBM2_KERNALS = %w[04a 03b].freeze
31
+
32
+ # The P500's one KERNAL, 901234-02.
33
+ P500_KERNALS = %w[02].freeze
34
+
25
35
  # The true drives --drive-type names for the Plus/4.
26
36
  PLUS4_DRIVE_TYPES = %w[1541 1551].freeze
27
37
 
@@ -47,7 +57,8 @@ module Badline
47
57
  section: "Window options", needs: :window),
48
58
  Option.new("--no-sound", "Don't play the SID", section: "Window options", needs: :window, build: :native),
49
59
  Option.new("--true-drive", "Put a true 1541 on device 8 instead of the KERNAL traps, a 1571 on the C128, " \
50
- "a 1551 on the Plus/4, a 2031 on the PET",
60
+ "a 1551 on the Plus/4, a 2031 on the PET or the CBM-II, or for a .d80 or .d82 " \
61
+ "an 8050 or 8250",
51
62
  section: "Window options", needs: :window),
52
63
  Option.new("--drive-type MODEL", "The Plus/4's true drive: 1541 or 1551 (default: 1551); implies --true-drive",
53
64
  section: "Window options", needs: :window),
@@ -55,7 +66,7 @@ module Badline
55
66
  section: "Window options", needs: :window),
56
67
  Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal; " \
57
68
  "C128: #{C128_MODELS.join(', ')}; Plus/4: #{PLUS4_MODELS.join(', ')}; " \
58
- "PET: #{PET_MODELS.join(', ')}",
69
+ "PET: #{PET_MODELS.join(', ')}; CBM-II: #{CBM2_MODELS.join(', ')}",
59
70
  section: "Window options", needs: :window),
60
71
  Option.new("--ntsc", "Run an NTSC C64, as --model ntsc does", section: "Window options", needs: :window),
61
72
  Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
@@ -63,7 +74,10 @@ module Badline
63
74
  Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded); " \
64
75
  "Plus/4 RAM: 16k, 32k or 64k; PET RAM: 4k, 8k, 16k or 32k (default: the model's own)",
65
76
  section: "Window options", needs: :window),
66
- Option.new("--60hz", "Run a CRTC PET's 60 Hz editor instead of its 50 Hz one",
77
+ Option.new("--60hz", "Run a CRTC PET's 60 Hz editor instead of its 50 Hz one, or a CBM-II on 60 Hz mains",
78
+ section: "Window options", needs: :window),
79
+ Option.new("--kernal VERSION", "Fit a CBM-II with KERNAL #{CBM2_KERNALS.join(' or ')} " \
80
+ "(default: #{CBM2_KERNALS.first}); the P500 takes #{P500_KERNALS.first}",
67
81
  section: "Window options", needs: :window),
68
82
  Option.new("--basic VERSION", "Fit a PET with BASIC 1, 2 or 4, as its board takes (default: its own)",
69
83
  section: "Window options", needs: :window),
@@ -118,7 +132,7 @@ module Badline
118
132
  section: "Testing options", needs: :window),
119
133
  Option.new("--save-snapshot FILE", "Save the machine as a .vsf snapshot after the last frame",
120
134
  section: "Testing options", needs: :window),
121
- Option.new("--vice", "Add VICE's modules to --save-snapshot's C64 or PET snapshot, for x64sc or xpet",
135
+ Option.new("--vice", "Add VICE's modules to --save-snapshot's C64, PET or CBM-II snapshot",
122
136
  section: "Testing options", needs: :window),
123
137
  Option.new("--at FRAME:EVENT", "Run EVENT once FRAME frames have run (see above)",
124
138
  section: "Testing options", needs: :window),
@@ -57,6 +57,12 @@ module Badline
57
57
  value
58
58
  end
59
59
 
60
+ def kernal_for(value)
61
+ raise Error, "invalid argument: --kernal #{value}" unless (CBM2_KERNALS + P500_KERNALS).include?(value)
62
+
63
+ value
64
+ end
65
+
60
66
  def reu_size(value)
61
67
  raise Error, "invalid argument: --reu #{value}" unless REU_SIZES.include?(value)
62
68
 
@@ -102,6 +108,7 @@ module Badline
102
108
  validate_vic20 if @family == :vic20
103
109
  @family == :plus4 ? validate_plus4 : refuse_plus4_options
104
110
  @family == :pet ? validate_pet : refuse_pet_options
111
+ @family == :cbm2 ? validate_cbm2 : refuse_cbm2_options
105
112
  if @family == :c128
106
113
  validate_c128
107
114
  elsif @c64_mode
@@ -170,10 +177,10 @@ module Badline
170
177
  raise Error, "--sid-card-address needs --sid-card" unless @sid_card_address.nil? || @sid_card
171
178
  end
172
179
 
173
- # Only the PET has a frame rate to pick and BASIC and editor ROMs to
174
- # fit.
180
+ # Only the PET has BASIC and editor ROMs to fit, and only it and the
181
+ # CBM-II a frame rate to pick.
175
182
  def refuse_pet_options
176
- raise Error, "--60hz needs pet" unless @refresh_hz.nil?
183
+ raise Error, "--60hz needs pet or cbm2" unless @refresh_hz.nil? || @family == :cbm2
177
184
  raise Error, "--basic needs pet" unless @basic.nil?
178
185
  raise Error, "--editor needs pet" unless @editor.nil?
179
186
  raise Error, "--characters needs pet" unless @characters.nil?
@@ -232,6 +239,23 @@ module Badline
232
239
  @refresh_hz = editor_hz
233
240
  end
234
241
 
242
+ # Only the CBM-II has a KERNAL to pick.
243
+ def refuse_cbm2_options
244
+ raise Error, "--kernal needs cbm2" unless @kernal.nil?
245
+ end
246
+
247
+ # The CBM-II runs in the window, without the C64's SID and REU, and
248
+ # its model takes the KERNAL named.
249
+ def validate_cbm2
250
+ raise Error, "--ntsc needs the C64; a CBM-II on 60 Hz mains is --60hz" if @models.include?("ntsc")
251
+ raise Error, "--sid needs the C64" unless @sid_model.nil?
252
+ raise Error, "--reu needs the C64" unless @reu.nil?
253
+ raise Error, "the CBM-II needs the window" if headless?
254
+
255
+ kernals = p500? ? P500_KERNALS : CBM2_KERNALS
256
+ raise Error, "the #{model} takes KERNAL #{kernals.join(' or ')}" unless kernals.include?(kernal)
257
+ end
258
+
235
259
  # `sid` takes directories as well as tunes.
236
260
  def validate_tune(path)
237
261
  return if !File.exist?(path) || File.extname(path).casecmp?(".sid")
@@ -13,10 +13,11 @@ module Badline
13
13
  # Either opens the window for any media, or with --headless or
14
14
  # --audio-out plays or renders a .sid tune without one. `sid`, given
15
15
  # first, plays .sid tunes and directories of them in the terminal.
16
- # `c64`, `vic20`, `c128`, `plus4` or `pet`, given first, names the
17
- # machine family, and without one the family is the C64, the C128 for a
18
- # program that loads where C128 BASIC starts, or the PET for an 8050's
19
- # or 8250's disk or a PET tape.
16
+ # `c64`, `vic20`, `c128`, `plus4`, `pet` or `cbm2`, given first, names
17
+ # the machine family, and without one the family is the C64, the C128
18
+ # for a program that loads where C128 BASIC starts, the PET for an
19
+ # 8050's or 8250's disk or a PET tape, or the CBM-II for a cartridge
20
+ # signed as its own.
20
21
  # The native build adds --no-sound and --version, and badline-ruby adds
21
22
  # --disable-jit.
22
23
  #
@@ -77,7 +78,7 @@ module Badline
77
78
  :audio_out, :seconds, :songlengths, :filter_chunk, :timeline
78
79
 
79
80
  # The machine family to build (Badline::Machine), :c64, :vic20, :c128,
80
- # :plus4 or :pet.
81
+ # :plus4, :pet or :cbm2.
81
82
  attr_reader :family
82
83
 
83
84
  # The ROMs --basic, --editor and --characters fit a PET with, keys of
@@ -137,6 +138,7 @@ module Badline
137
138
  @ram_kb = nil
138
139
  @ram_name = nil
139
140
  initialize_pet
141
+ @kernal = nil
140
142
  @cartridges = {}
141
143
  @autostart = true
142
144
  @writable = false
@@ -246,9 +248,20 @@ module Badline
246
248
  def c64_mode? = @c64_mode
247
249
 
248
250
  # The frame rate of a CRTC PET's editor: 60 with --60hz or a 60 Hz
249
- # --editor, else 50.
251
+ # --editor, else 50. A B series CBM-II's mains, which its editor
252
+ # follows.
250
253
  def refresh_hz = @refresh_hz || 50
251
254
 
255
+ # The CBM-II's mains, 60 with --60hz, or nil for the model's own.
256
+ def mains_hz = @refresh_hz
257
+
258
+ # The CBM-II's KERNAL --kernal names, one of CBM2_KERNALS or, on the
259
+ # P500, P500_KERNALS, the first of them without it.
260
+ def kernal = @kernal || (p500? ? P500_KERNALS : CBM2_KERNALS).first
261
+
262
+ # Whether the CBM-II to run is a P500, a 510 of either region.
263
+ def p500? = @family == :cbm2 && model.start_with?("510")
264
+
252
265
  def help? = @help
253
266
 
254
267
  def version? = @version
@@ -264,7 +277,7 @@ module Badline
264
277
  # A .sid on its own plays in the SID player, as `sid` would, unless an
265
278
  # option of the emulator's window asks for the machine.
266
279
  def play_lone_tune
267
- return if @sid_command || %i[vic20 plus4 pet].include?(@family) || headless? || !@window_only.empty?
280
+ return if @sid_command || %i[vic20 plus4 pet cbm2].include?(@family) || headless? || !@window_only.empty?
268
281
  return if @media_path.nil?
269
282
 
270
283
  @sid_command = File.extname(@media_path).casecmp?(".sid")
@@ -326,6 +339,7 @@ module Badline
326
339
  when "--ram" then @ram_name = value
327
340
  when "--basic", "--editor", "--characters", "--jumpers" then pet_option(name, value)
328
341
  when "--drive-type" then @drive_type = drive_type_for(value)
342
+ when "--kernal" then @kernal = kernal_for(value)
329
343
  when "--c1lo", "--c1hi", "--c2lo", "--c2hi" then @cartridges[CARTRIDGE_SLOTS.fetch(name)] = value
330
344
  when "--frames" then @frames = number(flag, value)
331
345
  when "--screenshot" then @screenshot = value
@@ -11,7 +11,8 @@ module Badline
11
11
  # Both record from VIA PB3. The KERNAL reads each tape as the falling
12
12
  # edges its read line makes, as the C64 and VIC-20 do. The machine
13
13
  # passes the motor and write lines on after each write to the I/O
14
- # chips (#lines_written).
14
+ # chips (#lines_written), and clocks the decks only while a tape runs
15
+ # (#running?).
15
16
  class Cassettes
16
17
  attr_reader :datasette1, :datasette2
17
18
 
@@ -22,6 +23,18 @@ module Badline
22
23
  @datasette2 = Drives::Datasette.new
23
24
  @datasette1.on_flag { pulse1 }
24
25
  @datasette2.on_flag { pulse2 }
26
+ @running = false
27
+ @running_handler = nil
28
+ @datasette1.on_running_change { update_running }
29
+ @datasette2.on_running_change { update_running }
30
+ end
31
+
32
+ # Whether either deck's tape runs.
33
+ def running? = @running
34
+
35
+ # Whether either deck's tape runs, each time that changes.
36
+ def on_running_change(&handler)
37
+ @running_handler = handler
25
38
  end
26
39
 
27
40
  def cycle!
@@ -48,6 +61,14 @@ module Badline
48
61
 
49
62
  private
50
63
 
64
+ def update_running
65
+ running = @datasette1.running? || @datasette2.running?
66
+ return if running == @running
67
+
68
+ @running = running
69
+ @running_handler&.call
70
+ end
71
+
51
72
  # A falling edge on a read line, and the line back up.
52
73
  def pulse1
53
74
  @pia1.ca1 = false
@@ -1,76 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "badline/drives/model2031"
3
+ require "badline/machine/ieee_drives"
4
4
 
5
5
  module Badline
6
6
  class PET
7
- # The PET's true drive: a Drives::Model2031 on the IEEE-488 bus
8
- # (IEEEPorts), clocked after the CPU each cycle and reset with it, as
9
- # the PET's RES line reaches IFC. The traps on the KERNAL's primitives
10
- # (IEEETraps) leave device 8 to the drive while it is device 8.
7
+ # The PET's true drive, a 2031, 8050 or 8250 on its IEEE-488 bus
8
+ # (IEEEPorts), as Machine::IEEEDrives has it, at the PET's 1 MHz.
11
9
  module IEEEDrive
12
- # The 2031 on the bus, or nil.
13
- attr_reader :drive2031
14
-
15
- # Plugs in a Drives::Model2031, in place of the one there.
16
- def attach_drive2031(drive)
17
- detach_drive2031
18
- drive.host_clock_hz = 1_000_000
19
- drive.connect(@ieee.bus)
20
- @drive2031 = drive
21
- @ieee.lines_written
22
- @ieee_trap&.device = trap_device
23
- @init_threshold = [@init_threshold, Badline::Drives::Model2031::BOOT_CYCLES].max if @cycles < @init_threshold
24
- end
25
-
26
- # Unplugs the 2031, leaving it on a bus of its own.
27
- def detach_drive2031
28
- drive = @drive2031
29
- return unless drive
30
-
31
- drive.connect(Badline::Drives::IEEEBus.new)
32
- @drive2031 = nil
33
- @ieee_trap&.device = trap_device
34
- end
35
-
36
- # The true drive on the bus, or nil.
37
- def true_drive = @drive2031
38
-
39
- # Plugs in a 2031 as device 8, in place of the drive there, and
40
- # returns it.
41
- def plug_true_drive
42
- Badline::Drives::Model2031.new.tap { |drive| attach_drive2031(drive) }
43
- end
10
+ include Machine::IEEEDrives
44
11
 
45
12
  private
46
13
 
47
- # The 2031 as keyed snapshot modules named from its device number:
48
- # BL-D8, BL-D8-CPU and so on for device 8.
49
- def save_drive_modules(sections)
50
- drive = @drive2031
51
- drive&.save_modules(sections, "BL-D#{drive.device}")
52
- sections
53
- end
54
-
55
- # The 2031 a snapshot's modules hold, built afresh unless one is on
56
- # the bus as the same device, or none when they hold no drive. The
57
- # bus then carries the PET's lines as its chips left them.
58
- def load_drive_modules(modules)
59
- device = 8
60
- input = modules.optional("BL-D8")
61
- while input.nil? && device < 11
62
- device += 1
63
- input = modules.optional("BL-D#{device}")
64
- end
65
- if input
66
- detach_drive2031 if @drive2031 && @drive2031.device != device
67
- attach_drive2031(Badline::Drives::Model2031.new(device:)) unless @drive2031
68
- @drive2031.load_modules(modules, input, "BL-D#{device}")
69
- else
70
- detach_drive2031
71
- end
72
- @ieee.lines_written
73
- end
14
+ def ieee_host_clock_hz = 1_000_000
74
15
  end
75
16
  end
76
17
  end
@@ -57,6 +57,13 @@ module Badline
57
57
  bus.host_lines = controller_lines unless bus.drives.empty?
58
58
  end
59
59
 
60
+ # The drives on the bus see the lines the PET pulls now.
61
+ def push_lines = lines_written
62
+
63
+ # Nothing to push once a cycle: the PET pushes its lines as its chips
64
+ # are written.
65
+ def cycle_lines = nil
66
+
60
67
  private
61
68
 
62
69
  def low_lines = @bus.low_lines(controller_lines)
data/lib/badline/pet.rb CHANGED
@@ -106,6 +106,7 @@ module Badline
106
106
  @via = VIA.new(start: 0xe800, peripheral: VIAPorts.new(self))
107
107
  @sound = Sound.new(@via)
108
108
  @cassettes = Cassettes.new(@pia1, @via)
109
+ watch_tapes
109
110
  keyboard_ports.connect(@pia1, @ieee, @cassettes)
110
111
  characters = Characters.new(@model.characters)
111
112
  @crtc_output = CRTCVideo.new(characters, @via, registers, columns80: @model.video == :crtc80,
@@ -130,7 +131,7 @@ module Badline
130
131
  @dos = nil
131
132
  @ieee_trap = nil
132
133
  @capture_output = nil
133
- @drive2031 = nil
134
+ init_ieee_drives
134
135
  @init_threshold = boot_cycles(ram_kb)
135
136
  end
136
137
 
@@ -177,10 +178,10 @@ module Badline
177
178
  @pia2.cycle!
178
179
  @via.cycle!
179
180
  @sound.cycle! if @sound.recording?
180
- @cassettes.cycle!
181
+ @cassettes.cycle! if @tapes_running
181
182
  @cpu.irq = @pia1.irq? || @pia2.irq? || @via.irq?
182
183
  @cpu.cycle!
183
- @drive2031&.host_cycle!
184
+ clock_ieee_drives
184
185
 
185
186
  @cycles += 1
186
187
  end
@@ -197,7 +198,7 @@ module Badline
197
198
  @via.reset!
198
199
  @crtc.reset!
199
200
  @bus.reset!
200
- @drive2031&.reset!
201
+ reset_ieee_drives
201
202
  @ieee.lines_written
202
203
  @cpu.reset!
203
204
  end
@@ -234,6 +235,16 @@ module Badline
234
235
  # on a 3032 and 1,980,000 on an 8032.
235
236
  def boot_cycles(ram_kb) = (@model.basic == "pet/basic4.rom" ? 700_000 : 150_000) + (46_000 * ram_kb)
236
237
 
238
+ # The decks are clocked only while a tape runs.
239
+ def watch_tapes
240
+ @tapes_running = false
241
+ @cassettes.on_running_change { tapes_changed }
242
+ end
243
+
244
+ def tapes_changed
245
+ @tapes_running = @cassettes.running?
246
+ end
247
+
237
248
  def drive_retrace(high)
238
249
  return if high == @vertical_drive
239
250