badline 0.17.0 → 0.18.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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +153 -0
  3. data/CLAUDE.md +11 -7
  4. data/CONTRIBUTING.md +7 -1
  5. data/README.md +169 -20
  6. data/doc/pinned-behaviour.md +128 -0
  7. data/doc/snapshot-format.md +253 -18
  8. data/doc/snapshots.md +57 -23
  9. data/exe/badline-ruby +5 -0
  10. data/lib/badline/audio/terminal.rb +34 -2
  11. data/lib/badline/c128/keyed_state.rb +2 -0
  12. data/lib/badline/c128.rb +1 -0
  13. data/lib/badline/c64/keyed_state.rb +2 -0
  14. data/lib/badline/c64.rb +1 -0
  15. data/lib/badline/cbm2/keyed_state.rb +4 -0
  16. data/lib/badline/cbm2/p500.rb +5 -3
  17. data/lib/badline/cbm2.rb +6 -3
  18. data/lib/badline/chips/cia/timer.rb +2 -2
  19. data/lib/badline/chips/cpu/instruction_set.rb +1 -1
  20. data/lib/badline/chips/m6809/arithmetic.rb +140 -0
  21. data/lib/badline/chips/m6809/bus_steps.rb +223 -0
  22. data/lib/badline/chips/m6809/core.rb +149 -0
  23. data/lib/badline/chips/m6809/indexed.rb +82 -0
  24. data/lib/badline/chips/m6809/interrupts.rb +159 -0
  25. data/lib/badline/chips/m6809/keyed_state.rb +147 -0
  26. data/lib/badline/chips/m6809/opcodes.rb +159 -0
  27. data/lib/badline/chips/m6809/operations.rb +258 -0
  28. data/lib/badline/chips/m6809/plans.rb +143 -0
  29. data/lib/badline/chips/m6809/registers.rb +82 -0
  30. data/lib/badline/chips/m6809/stack.rb +140 -0
  31. data/lib/badline/chips/m6809.rb +13 -0
  32. data/lib/badline/chips/riot/timer.rb +14 -3
  33. data/lib/badline/chips/rriot.rb +4 -2
  34. data/lib/badline/chips/sid/filter.rb +4 -4
  35. data/lib/badline/chips/sid/waveform.rb +6 -6
  36. data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
  37. data/lib/badline/chips/vic_i.rb +84 -21
  38. data/lib/badline/data_path.rb +3 -3
  39. data/lib/badline/drives/ieee_models.rb +54 -0
  40. data/lib/badline/drives/model1001.rb +37 -0
  41. data/lib/badline/drives/model2031.rb +6 -0
  42. data/lib/badline/drives/model4040/disk.rb +62 -0
  43. data/lib/badline/drives/model4040/ip_bus.rb +46 -0
  44. data/lib/badline/drives/model4040/keyed_state.rb +57 -0
  45. data/lib/badline/drives/model4040/mechanism.rb +192 -0
  46. data/lib/badline/drives/model4040/parts.rb +20 -0
  47. data/lib/badline/drives/model4040.rb +179 -0
  48. data/lib/badline/drives/model8050/board.rb +77 -0
  49. data/lib/badline/drives/model8050/dos_board.rb +91 -0
  50. data/lib/badline/drives/model8050/idle.rb +11 -7
  51. data/lib/badline/drives/model8050/keyed_state.rb +5 -2
  52. data/lib/badline/drives/model8050.rb +18 -130
  53. data/lib/badline/drives/model9060/idle.rb +149 -0
  54. data/lib/badline/drives/model9060/keyed_state.rb +123 -0
  55. data/lib/badline/drives/model9060/parts.rb +98 -0
  56. data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
  57. data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
  58. data/lib/badline/drives/model9060/sasi_target.rb +161 -0
  59. data/lib/badline/drives/model9060.rb +195 -0
  60. data/lib/badline/frontend/app.rb +8 -0
  61. data/lib/badline/frontend/boot.rb +3 -2
  62. data/lib/badline/frontend/controls.rb +62 -6
  63. data/lib/badline/frontend/keys.rb +36 -0
  64. data/lib/badline/frontend/kim1_panel.rb +208 -0
  65. data/lib/badline/frontend/media_slots.rb +8 -1
  66. data/lib/badline/frontend/menu/drive_types.rb +56 -0
  67. data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
  68. data/lib/badline/frontend/menu/menu_media.rb +42 -12
  69. data/lib/badline/frontend/menu/menu_pages.rb +97 -2
  70. data/lib/badline/frontend/menu/menu_ports.rb +38 -1
  71. data/lib/badline/frontend/menu/pause_menu.rb +5 -1
  72. data/lib/badline/frontend/pet_keyboard.rb +23 -1
  73. data/lib/badline/frontend/screen.rb +1 -1
  74. data/lib/badline/frontend/screens.rb +40 -2
  75. data/lib/badline/frontend/teletype_pane.rb +152 -0
  76. data/lib/badline/frontend/timeline.rb +31 -2
  77. data/lib/badline/frontend.rb +3 -0
  78. data/lib/badline/kim1/bus.rb +117 -0
  79. data/lib/badline/kim1/cpu.rb +25 -0
  80. data/lib/badline/kim1/host_terminal.rb +74 -0
  81. data/lib/badline/kim1/key_entry.rb +74 -0
  82. data/lib/badline/kim1/keyed_modules.rb +53 -0
  83. data/lib/badline/kim1/keyed_state.rb +133 -0
  84. data/lib/badline/kim1/panel.rb +176 -0
  85. data/lib/badline/kim1/rriots.rb +28 -0
  86. data/lib/badline/kim1/sound.rb +29 -0
  87. data/lib/badline/kim1/tape.rb +116 -0
  88. data/lib/badline/kim1/teletype.rb +193 -0
  89. data/lib/badline/kim1.rb +190 -0
  90. data/lib/badline/machine/ieee_drives.rb +94 -117
  91. data/lib/badline/machine/keyboard_buffer.rb +20 -3
  92. data/lib/badline/machine/line_sound.rb +122 -0
  93. data/lib/badline/machine.rb +46 -15
  94. data/lib/badline/media/extensions.rb +8 -4
  95. data/lib/badline/media/kim1_media.rb +150 -0
  96. data/lib/badline/media/pet_media.rb +3 -1
  97. data/lib/badline/media/true_drive.rb +42 -45
  98. data/lib/badline/media.rb +12 -6
  99. data/lib/badline/options/family.rb +98 -8
  100. data/lib/badline/options/help.rb +44 -12
  101. data/lib/badline/options/table.rb +43 -11
  102. data/lib/badline/options/validation.rb +44 -8
  103. data/lib/badline/options.rb +27 -8
  104. data/lib/badline/pet/bus.rb +16 -2
  105. data/lib/badline/pet/keyboard_ports.rb +9 -2
  106. data/lib/badline/pet/keyed_state.rb +4 -0
  107. data/lib/badline/pet/model.rb +11 -2
  108. data/lib/badline/pet/rom_sets.rb +1 -0
  109. data/lib/badline/pet/sound.rb +5 -105
  110. data/lib/badline/pet/superpet/acia.rb +12 -0
  111. data/lib/badline/pet/superpet/board.rb +221 -0
  112. data/lib/badline/pet/superpet/dongle.rb +81 -0
  113. data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
  114. data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
  115. data/lib/badline/pet/superpet/m6809.rb +32 -0
  116. data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
  117. data/lib/badline/pet/superpet.rb +216 -0
  118. data/lib/badline/pet.rb +19 -3
  119. data/lib/badline/plus4/keyed_state.rb +2 -0
  120. data/lib/badline/plus4.rb +1 -0
  121. data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
  122. data/lib/badline/roms/4040/dos-1.2.rom +0 -0
  123. data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
  124. data/lib/badline/roms/4040/dos-2.1.rom +0 -0
  125. data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
  126. data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
  127. data/lib/badline/roms/9060/dos-3.0.rom +0 -0
  128. data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
  129. data/lib/badline/roms/README +166 -0
  130. data/lib/badline/roms/kim1/6530-002.rom +0 -0
  131. data/lib/badline/roms/kim1/6530-003.rom +0 -0
  132. data/lib/badline/roms/pet/characters-superpet.rom +0 -0
  133. data/lib/badline/roms/pet/superpet-a000.rom +0 -0
  134. data/lib/badline/roms/pet/superpet-c000.rom +0 -0
  135. data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
  136. data/lib/badline/roms/pet/superpet-e000.rom +0 -0
  137. data/lib/badline/snapshot/container.rb +2 -0
  138. data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
  139. data/lib/badline/snapshot/field_doc.rb +27 -6
  140. data/lib/badline/snapshot/image.rb +4 -1
  141. data/lib/badline/snapshot/kim1_setup.rb +28 -0
  142. data/lib/badline/snapshot/pet_setup.rb +27 -11
  143. data/lib/badline/snapshot/storage_setup.rb +4 -1
  144. data/lib/badline/snapshot/vic20_setup.rb +11 -7
  145. data/lib/badline/snapshot/vice/pet.rb +13 -7
  146. data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
  147. data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
  148. data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
  149. data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
  150. data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
  151. data/lib/badline/snapshot.rb +4 -0
  152. data/lib/badline/status.rb +30 -30
  153. data/lib/badline/storage/d67_image.rb +55 -0
  154. data/lib/badline/storage/d90_image.rb +180 -0
  155. data/lib/badline/storage/disk_image/writing.rb +2 -2
  156. data/lib/badline/storage/kim_tape.rb +254 -0
  157. data/lib/badline/storage/papertape.rb +90 -0
  158. data/lib/badline/storage.rb +4 -0
  159. data/lib/badline/support/kim1_checkpoint.rb +41 -0
  160. data/lib/badline/support/superpet_checkpoint.rb +35 -0
  161. data/lib/badline/version.rb +1 -1
  162. data/lib/badline/vic20/keyed_modules.rb +6 -3
  163. data/lib/badline/vic20/keyed_state.rb +6 -4
  164. data/lib/badline/vic20.rb +26 -12
  165. data/lib/badline.rb +1 -0
  166. metadata +77 -1
@@ -7,8 +7,7 @@ module Badline
7
7
  # The names Badline::Model builds, in the order the help lists them.
8
8
  MODELS = %w[c64 c64c ntsc newntsc oldntsc drean sx64 pet64 c64gs ultimax].freeze
9
9
 
10
- # The VIC-20's models, as xvic names its video standards. Only PAL is
11
- # emulated.
10
+ # The VIC-20's models, as xvic names its video standards.
12
11
  VIC20_MODELS = %w[pal ntsc].freeze
13
12
 
14
13
  # The C128's models, as C128::Model names them.
@@ -20,12 +19,22 @@ module Badline
20
19
 
21
20
  # The PETs and CBMs, as PET::Model names them, the 8032 first, the
22
21
  # default.
23
- PET_MODELS = %w[8032 8096 8296 4032 3032 3032b 2001].freeze
22
+ PET_MODELS = %w[8032 8096 8296 4032 3032 3032b 2001 superpet].freeze
24
23
 
25
24
  # The CBM-II's B series, as CBM2::Model names them, the 610 first, the
26
25
  # default, and the P500, as CBM2::P500::Model names them.
27
26
  CBM2_MODELS = %w[610 620 710 720 510 510ntsc].freeze
28
27
 
28
+ # The KIM-1, which has the one model.
29
+ KIM1_MODELS = %w[kim1].freeze
30
+
31
+ # The KIM-1 teletype's rates, as KIM1::Teletype takes them.
32
+ KIM1_BAUDS = %w[110 300 1200 2400 4800 9600].freeze
33
+
34
+ # The KIM-1's RAM as --ram names it, and the names
35
+ # KIM1::Bus::RAM_CONFIGURATIONS gives it.
36
+ KIM1_RAM = { "1k" => :"1k", "5k" => :"5k", "expanded" => :expanded }.freeze
37
+
29
38
  # The B series' KERNALs --kernal names, 901244-04a first, the default.
30
39
  CBM2_KERNALS = %w[04a 03b].freeze
31
40
 
@@ -49,6 +58,22 @@ module Badline
49
58
  Option.new("--disable-jit", "Run without enabling YJIT", section: "Options", build: :ruby),
50
59
  Option.new("-h, --help", "Show this help", section: "Options"),
51
60
  Option.new("--version", "Show the version and what built it", section: "Options", build: :native),
61
+ Option.new("--tty", "Fit the KIM-1's TTY/KB jumper, so its monitor talks to a teletype: a pane in the " \
62
+ "window, or the terminal with --headless",
63
+ section: "Options"),
64
+ Option.new("--baud N", "The KIM-1 teletype's baud: #{KIM1_BAUDS.join(', ')} (default: 9600)",
65
+ section: "Options"),
66
+ Option.new("--tty-load", "Load the KIM-1's .ptp or .pap through its monitor's L command on the teletype, " \
67
+ "in real time, instead of straight into RAM; implies --tty",
68
+ section: "Options"),
69
+ Option.new("--load-address ADDR", "Where the KIM-1 loads a .bin, in hex (default: 0200)",
70
+ section: "Options"),
71
+ Option.new("--start-address ADDR", "Where the KIM-1 starts its program, in hex (default: where it loads)",
72
+ section: "Options"),
73
+ Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded); " \
74
+ "Plus/4 RAM: 16k, 32k or 64k; PET RAM: 4k, 8k, 16k or 32k (default: the model's own); " \
75
+ "KIM-1 RAM: 1k, 5k or expanded (default: 1k)",
76
+ section: "Options"),
52
77
  Option.new("--no-autostart", "Boot to READY. instead of running the program",
53
78
  section: "Window options", needs: :window),
54
79
  Option.new("--writable", "Let the machine write to disk images, changing their files",
@@ -60,19 +85,20 @@ module Badline
60
85
  "a 1551 on the Plus/4, a 2031 on the PET or the CBM-II, or for a .d80 or .d82 " \
61
86
  "an 8050 or 8250",
62
87
  section: "Window options", needs: :window),
63
- Option.new("--drive-type MODEL", "The Plus/4's true drive: 1541 or 1551 (default: 1551); implies --true-drive",
88
+ Option.new("--drive-type MODEL", "The Plus/4's true drive: 1541 or 1551 (default: 1551); the PET's and the " \
89
+ "CBM-II's: #{Drives::IEEEModels::NAMES.join(', ')} (default: 2031); " \
90
+ "implies --true-drive",
64
91
  section: "Window options", needs: :window),
65
92
  Option.new("--reu SIZE", "Plug in an REU of SIZE K: 128, 256, 512 (a 1750) or up to 16384",
66
93
  section: "Window options", needs: :window),
67
- Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal; " \
68
- "C128: #{C128_MODELS.join(', ')}; Plus/4: #{PLUS4_MODELS.join(', ')}; " \
94
+ Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); " \
95
+ "VIC-20: #{VIC20_MODELS.join(', ')}; C128: #{C128_MODELS.join(', ')}; " \
96
+ "Plus/4: #{PLUS4_MODELS.join(', ')}; " \
69
97
  "PET: #{PET_MODELS.join(', ')}; CBM-II: #{CBM2_MODELS.join(', ')}",
70
98
  section: "Window options", needs: :window),
71
- Option.new("--ntsc", "Run an NTSC C64, as --model ntsc does", section: "Window options", needs: :window),
72
- Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
99
+ Option.new("--ntsc", "Run an NTSC C64 or VIC-20, as --model ntsc does",
73
100
  section: "Window options", needs: :window),
74
- Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded); " \
75
- "Plus/4 RAM: 16k, 32k or 64k; PET RAM: 4k, 8k, 16k or 32k (default: the model's own)",
101
+ Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
76
102
  section: "Window options", needs: :window),
77
103
  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
104
  section: "Window options", needs: :window),
@@ -90,6 +116,12 @@ module Badline
90
116
  "$9000 and $A000, userport for the user port's PA0-PA2 to switch in " \
91
117
  "the RAM under the ROMs",
92
118
  section: "Window options", needs: :window),
119
+ Option.new("--cpu CPU", "Set the SuperPET's CPU switch: 6502, 6809 or prog, for the system latch to pick " \
120
+ "(default: 6502)",
121
+ section: "Window options", needs: :window),
122
+ Option.new("--bank-ram MODE", "Set the SuperPET's bank RAM switch: rw, read for write-protected, or prog, " \
123
+ "for the system latch to pick (default: rw)",
124
+ section: "Window options", needs: :window),
93
125
  Option.new("--c1lo FILE", "Put a .bin ROM image in the Plus/4's cartridge 1, low slot ($8000)",
94
126
  section: "Window options", needs: :window),
95
127
  Option.new("--c1hi FILE", "Put a .bin ROM image in cartridge 1's high slot ($C000)",
@@ -108,7 +140,7 @@ module Badline
108
140
  section: "Window options", needs: :window),
109
141
  Option.new("--verbose", "Print the display, sound and gamepad setup and the frame timing",
110
142
  section: "Window options", needs: :window),
111
- Option.new("--headless", "Play a .sid tune in the terminal instead of the window",
143
+ Option.new("--headless", "Play a .sid tune, or run the KIM-1's teletype, in the terminal instead of the window",
112
144
  section: "Options without the window"),
113
145
  Option.new("--audio-out FILE", "Render a .sid tune to a .wav or .aiff file",
114
146
  section: "Options without the window"),
@@ -52,7 +52,9 @@ module Badline
52
52
  end
53
53
 
54
54
  def drive_type_for(value)
55
- raise Error, "invalid argument: --drive-type #{value}" unless PLUS4_DRIVE_TYPES.include?(value)
55
+ unless (PLUS4_DRIVE_TYPES + Drives::IEEEModels::NAMES).include?(value)
56
+ raise Error, "invalid argument: --drive-type #{value}"
57
+ end
56
58
 
57
59
  value
58
60
  end
@@ -63,6 +65,12 @@ module Badline
63
65
  value
64
66
  end
65
67
 
68
+ def baud_for(value)
69
+ raise Error, "invalid argument: --baud #{value}" unless KIM1_BAUDS.include?(value)
70
+
71
+ value.to_i
72
+ end
73
+
66
74
  def reu_size(value)
67
75
  raise Error, "invalid argument: --reu #{value}" unless REU_SIZES.include?(value)
68
76
 
@@ -83,7 +91,7 @@ module Badline
83
91
 
84
92
  def validate
85
93
  validate_family
86
- raise Error, "--drive-type needs plus4" unless @drive_type.nil? || @family == :plus4
94
+ validate_drive_type
87
95
 
88
96
  validate_mode
89
97
  validate_models
@@ -91,10 +99,20 @@ module Badline
91
99
  validate_paths
92
100
  end
93
101
 
102
+ def validate_drive_type
103
+ return if @drive_type.nil?
104
+
105
+ types = @family == :plus4 ? PLUS4_DRIVE_TYPES : Drives::IEEEModels::NAMES
106
+ return if %i[plus4 pet cbm2].include?(@family) && types.include?(@drive_type)
107
+
108
+ raise Error, "--drive-type needs #{PLUS4_DRIVE_TYPES.include?(@drive_type) ? 'plus4' : 'pet or cbm2'}"
109
+ end
110
+
94
111
  def validate_mode
95
112
  if headless?
96
113
  raise Error, "#{@window_only.first} needs the window" unless @window_only.empty?
97
114
  raise Error, "#{@any_window_only.first} needs the window" unless player_window? || @any_window_only.empty?
115
+ return if @family == :kim1
98
116
  raise Error, "no tune given" if tune_paths.empty? && !player_window?
99
117
 
100
118
  tune_paths.each { |path| validate_tune(path) }
@@ -105,6 +123,7 @@ module Badline
105
123
 
106
124
  def validate_family
107
125
  ram_option
126
+ @family == :kim1 ? validate_kim1 : refuse_kim1_options
108
127
  validate_vic20 if @family == :vic20
109
128
  @family == :plus4 ? validate_plus4 : refuse_plus4_options
110
129
  @family == :pet ? validate_pet : refuse_pet_options
@@ -125,7 +144,8 @@ module Badline
125
144
  when :vic20 then @ram = ram_configuration(@ram_name)
126
145
  when :plus4 then @ram_kb = plus4_ram(@ram_name)
127
146
  when :pet then @ram_kb = pet_ram(@ram_name)
128
- else raise Error, "--ram needs vic20, plus4 or pet"
147
+ when :kim1 then @ram = kim1_ram(@ram_name)
148
+ else raise Error, "--ram needs vic20, plus4, pet or kim1"
129
149
  end
130
150
  end
131
151
 
@@ -137,11 +157,8 @@ module Badline
137
157
  raise Error, "the C128 needs the window" if headless?
138
158
  end
139
159
 
140
- # The VIC-20 is a PAL one, in the window, without the C64's SID and
141
- # REU.
160
+ # The VIC-20 runs in the window, without the C64's SID and REU.
142
161
  def validate_vic20
143
- raise Error, "the NTSC VIC-20 isn't emulated yet" if @models.include?("ntsc")
144
-
145
162
  raise Error, "--sid needs the C64" unless @sid_model.nil?
146
163
  raise Error, "--reu needs the C64" unless @reu.nil?
147
164
  raise Error, "the VIC-20 needs the window" if headless?
@@ -185,6 +202,8 @@ module Badline
185
202
  raise Error, "--editor needs pet" unless @editor.nil?
186
203
  raise Error, "--characters needs pet" unless @characters.nil?
187
204
  raise Error, "--jumpers needs pet" unless @jumpers.empty?
205
+ raise Error, "--cpu needs pet --model superpet" unless @cpu.nil?
206
+ raise Error, "--bank-ram needs pet --model superpet" unless @bank_ram.nil?
188
207
  end
189
208
 
190
209
  # The PET runs in the window, without the C64's SID and REU, at the
@@ -202,7 +221,8 @@ module Badline
202
221
  end
203
222
 
204
223
  # --characters names a character ROM the board takes, --jumpers the
205
- # 8296 board's jumpers, and the 8296 has all of its RAM soldered on.
224
+ # 8296 board's jumpers, and the 8296 has all of its RAM soldered on,
225
+ # as the SuperPET has besides its bank RAM.
206
226
  def validate_pet_board
207
227
  unless @characters.nil? || PET::ROMSets.characters(model).include?(@characters)
208
228
  raise Error, "the #{model} takes no character ROM #{@characters}"
@@ -211,6 +231,22 @@ module Badline
211
231
  unknown = @jumpers.find { |jumper| !PET::ROMSets.jumpers(model).include?(jumper) }
212
232
  raise Error, "the #{model} has no jumper #{unknown}" unless unknown.nil?
213
233
  raise Error, "the 8296 has 32k of RAM" if model == "8296" && ![nil, 32].include?(@ram_kb)
234
+ raise Error, "--ram doesn't fit the superpet" if model == "superpet" && !@ram_kb.nil?
235
+
236
+ validate_cpu_switch
237
+ end
238
+
239
+ # --cpu and --bank-ram set the SuperPET's switches, which no other
240
+ # PET has.
241
+ def validate_cpu_switch
242
+ validate_switch("--cpu", @cpu, PET::SuperPET::Board::CPU_SWITCH)
243
+ validate_switch("--bank-ram", @bank_ram, PET::SuperPET::Board::RAM_SWITCH)
244
+ end
245
+
246
+ def validate_switch(flag, value, positions)
247
+ return if value.nil?
248
+ raise Error, "#{flag} needs pet --model superpet" unless model == "superpet"
249
+ raise Error, "invalid argument: #{flag} #{value}" unless positions.include?(value)
214
250
  end
215
251
 
216
252
  # --basic and --editor name ROMs the model's board takes
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "badline/pet/rom_sets"
4
+ require "badline/drives/ieee_models"
4
5
  require "badline/options/option"
5
6
  require "badline/options/event"
6
7
  require "badline/options/table"
@@ -17,7 +18,7 @@ module Badline
17
18
  # the machine family, and without one the family is the C64, the C128
18
19
  # for a program that loads where C128 BASIC starts, the PET for an
19
20
  # 8050's or 8250's disk or a PET tape, or the CBM-II for a cartridge
20
- # signed as its own.
21
+ # signed as its own. `kim1` runs a MOS KIM-1.
21
22
  # The native build adds --no-sound and --version, and badline-ruby adds
22
23
  # --disable-jit.
23
24
  #
@@ -41,7 +42,7 @@ module Badline
41
42
  SID_MODELS = { "6581" => :mos6581, "8580" => :mos8580 }.freeze
42
43
 
43
44
  # What `sid` leaves out besides the window's options.
44
- NOT_FOR_SID = %w[--audio-out].freeze
45
+ NOT_FOR_SID = %w[--audio-out --tty --baud --tty-load --load-address --start-address --ram].freeze
45
46
 
46
47
  REU_SIZES = %w[128 256 512 1024 2048 4096 8192 16384].freeze
47
48
 
@@ -89,8 +90,17 @@ module Badline
89
90
  # The 8296's board jumpers --jumpers names (PET::Bus::Expansion::JUMPERS).
90
91
  attr_reader :jumpers
91
92
 
93
+ # The SuperPET's CPU switch, as --cpu sets it: "6502", "6809" or
94
+ # "prog".
95
+ def cpu = @cpu || "6502"
96
+
97
+ # The SuperPET's bank RAM switch, as --bank-ram sets it: "rw", "read"
98
+ # or "prog".
99
+ def bank_ram = @bank_ram || "rw"
100
+
92
101
  # The VIC-20's RAM expansion --ram names, a key of
93
- # Vic20::Bus::RAM_CONFIGURATIONS, or nil without --ram.
102
+ # Vic20::Bus::RAM_CONFIGURATIONS, or the KIM-1's, one of
103
+ # KIM1::Bus::RAM_CONFIGURATIONS, or nil without --ram.
94
104
  attr_reader :ram
95
105
 
96
106
  # The Plus/4's or the PET's RAM in K --ram names, one of PLUS4_RAM's or
@@ -138,6 +148,7 @@ module Badline
138
148
  @ram_kb = nil
139
149
  @ram_name = nil
140
150
  initialize_pet
151
+ initialize_kim1
141
152
  @kernal = nil
142
153
  @cartridges = {}
143
154
  @autostart = true
@@ -230,8 +241,9 @@ module Badline
230
241
 
231
242
  def true_drive? = @true_drive || !@drive_type.nil?
232
243
 
233
- # The Plus/4's true drive --drive-type names, "1551" without it.
234
- def drive_type = @drive_type || "1551"
244
+ # The true drive --drive-type names: without it, "1551" on the Plus/4
245
+ # and the 2031 on the PET and the CBM-II.
246
+ def drive_type = @drive_type || (@family == :plus4 ? "1551" : Drives::IEEEModels::DEFAULT)
235
247
 
236
248
  # Whether --save-snapshot adds VICE's modules, so x64sc opens it.
237
249
  def vice? = @vice
@@ -277,7 +289,7 @@ module Badline
277
289
  # A .sid on its own plays in the SID player, as `sid` would, unless an
278
290
  # option of the emulator's window asks for the machine.
279
291
  def play_lone_tune
280
- return if @sid_command || %i[vic20 plus4 pet cbm2].include?(@family) || headless? || !@window_only.empty?
292
+ return if @sid_command || %i[vic20 plus4 pet cbm2 kim1].include?(@family) || headless? || !@window_only.empty?
281
293
  return if @media_path.nil?
282
294
 
283
295
  @sid_command = File.extname(@media_path).casecmp?(".sid")
@@ -337,7 +349,7 @@ module Badline
337
349
  when "--model" then @models << value
338
350
  when "--reu" then @reu = reu_size(value)
339
351
  when "--ram" then @ram_name = value
340
- when "--basic", "--editor", "--characters", "--jumpers" then pet_option(name, value)
352
+ when "--basic", "--editor", "--characters", "--jumpers", "--cpu", "--bank-ram" then pet_option(name, value)
341
353
  when "--drive-type" then @drive_type = drive_type_for(value)
342
354
  when "--kernal" then @kernal = kernal_for(value)
343
355
  when "--c1lo", "--c1hi", "--c2lo", "--c2hi" then @cartridges[CARTRIDGE_SLOTS.fetch(name)] = value
@@ -349,12 +361,16 @@ module Badline
349
361
  when "--seconds" then @seconds = decimal(flag, value)
350
362
  when "--songlengths" then @songlengths = value
351
363
  when "--rate" then @rate = number(flag, value)
364
+ when "--baud", "--load-address", "--start-address" then kim1_option(name, flag, value)
352
365
  else @filter_chunk = number(flag, value)
353
366
  end
354
367
  end
355
368
 
356
- # The PET's frame rate, ROMs and board jumpers, its own until named.
369
+ # The PET's frame rate, ROMs, board jumpers and the SuperPET's CPU and
370
+ # bank RAM switches, its own until named.
357
371
  def initialize_pet
372
+ @cpu = nil
373
+ @bank_ram = nil
358
374
  @refresh_hz = nil
359
375
  @basic = nil
360
376
  @editor = nil
@@ -367,6 +383,8 @@ module Badline
367
383
  when "--basic" then @basic = value
368
384
  when "--editor" then @editor = value
369
385
  when "--characters" then @characters = value
386
+ when "--cpu" then @cpu = value
387
+ when "--bank-ram" then @bank_ram = value
370
388
  else @jumpers = value.split(",")
371
389
  end
372
390
  end
@@ -391,6 +409,7 @@ module Badline
391
409
  when "--headless" then @headless = true
392
410
  when "--quiet" then @quiet = true
393
411
  when "--all-subtunes" then @all_subtunes = true
412
+ when "--tty", "--tty-load" then kim1_switch(name)
394
413
  else @tui = false
395
414
  end
396
415
  end
@@ -8,11 +8,12 @@ module Badline
8
8
  #
9
9
  # $0000-$7FFF - RAM, 4K, 8K, 16K or 32K of it from $0000, then nothing
10
10
  # $8000-$8FFF - screen RAM, repeated (Model), then nothing
11
- # $9000-$AFFF - two empty ROM sockets
11
+ # $9000-$AFFF - two empty ROM sockets, or the SuperPET's bank RAM at
12
+ # $9000
12
13
  # $B000-$DFFF - BASIC 4, or BASIC 1 or 2 from $C000
13
14
  # $E000-$E7FF - the screen editor ROM
14
15
  # $E800-$E8FF - I/O (IO)
15
- # $E900-$EFFF - nothing
16
+ # $E900-$EFFF - nothing, but the SuperPET's I/O page at $EF00
16
17
  # $F000-$FFFF - the KERNAL ROM
17
18
  #
18
19
  # The data lines hold the last byte they carried, so a read from an
@@ -136,6 +137,7 @@ module Badline
136
137
  @user_port = !@expansion.nil? && @expansion.user_port?
137
138
  @debug_register = nil
138
139
  @io_write_handler = nil
140
+ @board = nil
139
141
  map_pages
140
142
  end
141
143
 
@@ -174,6 +176,11 @@ module Badline
174
176
  @write_pages[addr >> 8].poke(addr, value)
175
177
  end
176
178
 
179
+ # #peek and #poke as the SuperPET's 6809 calls them.
180
+ def read(addr) = peek(addr)
181
+
182
+ def write(addr, value) = poke(addr, value)
183
+
177
184
  # Whether the CPU reads the KERNAL ROM at $F000-$FFFF, which an 8096's
178
185
  # board covers while its control register turns it on, and an 8296's
179
186
  # RAM too.
@@ -185,6 +192,12 @@ module Badline
185
192
  map_pages
186
193
  end
187
194
 
195
+ # Lays the SuperPET's board (SuperPET::Board) over its pages.
196
+ def fit_board(board)
197
+ @board = board
198
+ map_pages
199
+ end
200
+
188
201
  # Lays the pages out again, for the board's control register.
189
202
  def map_pages
190
203
  map(@open_bus, 0x00, 0x100)
@@ -195,6 +208,7 @@ module Badline
195
208
  map(@io, 0xe8, 0x01)
196
209
  @read_pages.fill(@kernal_rom, 0xf0, 0x10)
197
210
  @expansion&.map(@read_pages, @write_pages)
211
+ @board&.map(@read_pages, @write_pages)
198
212
  map_debug_register if @debug_register
199
213
  end
200
214
 
@@ -42,8 +42,12 @@ module Badline
42
42
 
43
43
  attr_reader :keyboard
44
44
 
45
+ # Whether the SuperPET's board pulls the diagnostic sense low.
46
+ attr_accessor :diagnostic_low
47
+
45
48
  def initialize(keyboard)
46
49
  @keyboard = keyboard
50
+ @diagnostic_low = false
47
51
  @pia = nil
48
52
  @ieee = nil
49
53
  @cassettes = nil
@@ -60,8 +64,11 @@ module Badline
60
64
 
61
65
  # Port A's other lines: PA4 and PA5 read the cassette sense switches,
62
66
  # PA6 the IEEE bus's EOI, and the diagnostic sense on PA7 floats high
63
- # with nothing on it.
64
- def read_a(_lines) = @ieee.eoi_input & @cassettes.sense_input
67
+ # with nothing on it but the SuperPET's board, which can pull it low.
68
+ def read_a(_lines)
69
+ lines = @ieee.eoi_input & @cassettes.sense_input
70
+ @diagnostic_low ? lines & 0x7f : lines
71
+ end
65
72
 
66
73
  def read_b(lines)
67
74
  row = @pia.port_a_output & 0x0f
@@ -25,7 +25,9 @@ module Badline
25
25
  out.int("cycles", @cycles).bool("vertical_drive", @vertical_drive)
26
26
  pending = @pending_keys
27
27
  out.ints("host.pending_keys", pending) if pending
28
+ save_init_keys(out)
28
29
  out.int("host.init_pending", @cycles <= @init_threshold ? @init_handlers.length : 0)
30
+ save_ieee_drive_fields(out)
29
31
  end
30
32
 
31
33
  def load_machine_fields(input)
@@ -33,7 +35,9 @@ module Badline
33
35
  @vertical_drive = input.bool("vertical_drive", @vertical_drive)
34
36
  pending = input.ints("host.pending_keys", [])
35
37
  @pending_keys = pending.empty? ? nil : pending.dup
38
+ load_init_keys(input)
36
39
  @init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
40
+ load_ieee_drive_fields(input)
37
41
  end
38
42
  end
39
43
 
@@ -17,7 +17,8 @@ module Badline
17
17
  # 1K twice over and the 80-column ones with 2K. The 8296 has 128K of
18
18
  # dynamic RAM, the 32K below $8000, the 32K above it, of which the
19
19
  # screen is the start and the rest lies under the ROMs, and the 64K
20
- # board (Bus::Expansion).
20
+ # board (Bus::Expansion). The SuperPET is an 8032 with the 901640-01
21
+ # character ROM, twice the 8032's size, and its own board (SuperPET).
21
22
  module Model
22
23
  Profile = Data.define(:name, :ram_kb, :basic, :editor_50hz, :editor_60hz, :kernal, :characters, :keyboard,
23
24
  :video, :expansion) do
@@ -28,6 +29,9 @@ module Badline
28
29
  # The 8296, whose board decodes its memory in a PLA.
29
30
  def cbm8296? = name == "8296"
30
31
 
32
+ # The SuperPET, an 8032 with the Waterloo board (SuperPET).
33
+ def superpet? = name == "superpet"
34
+
31
35
  # The screen RAM's bytes, and on the 8296 all 32K of RAM from $8000.
32
36
  def screen_bytes
33
37
  return 0x8000 if cbm8296?
@@ -86,7 +90,12 @@ module Badline
86
90
  kernal: "pet/kernal4.rom", characters: "pet/characters2.rom", keyboard: :business,
87
91
  video: :crtc80, expansion: true)
88
92
 
89
- ALL = [PET2001, PET3032, PET3032B, PET4032, CBM8032, CBM8096, CBM8296].freeze
93
+ SUPERPET = Profile.new(name: "superpet", ram_kb: 32, basic: "pet/basic4.rom",
94
+ editor_50hz: "pet/editor4-80-b-50hz.rom", editor_60hz: "pet/editor4-80-b-60hz.rom",
95
+ kernal: "pet/kernal4.rom", characters: "pet/characters-superpet.rom",
96
+ keyboard: :business, video: :crtc80, expansion: false)
97
+
98
+ ALL = [PET2001, PET3032, PET3032B, PET4032, CBM8032, CBM8096, CBM8296, SUPERPET].freeze
90
99
 
91
100
  DEFAULT = "8032"
92
101
 
@@ -39,6 +39,7 @@ module Badline
39
39
  "4032" => { "4" => %w[4-40-n-50hz 4-40-n-60hz] },
40
40
  "8032" => { "4" => %w[4-80-b-50hz 4-80-b-50hz-0283 4-80-b-60hz] },
41
41
  "8096" => { "4" => %w[4-80-b-50hz 4-80-b-50hz-0283 4-80-b-60hz] },
42
+ "superpet" => { "4" => %w[4-80-b-50hz 4-80-b-50hz-0283 4-80-b-60hz] },
42
43
  "8296" => { "4" => %w[4-80-b-50hz-8296 4-80-b-50hz 4-80-b-50hz-0283 4-80-b-60hz] }
43
44
  }.freeze
44
45
 
@@ -8,121 +8,21 @@ module Badline
8
8
  # added. CB2 carries the shift register's bits in its shift-out modes,
9
9
  # mode 4's free-running square wave under timer 2 for the bell and most
10
10
  # PET music, and the PCR's CB2 output otherwise, held high or low by
11
- # hand or floating high as an input.
12
- #
13
- # The machine calls #cycle! once a cycle while #recording?, from #record
14
- # on, so a sound nobody records costs only that check. Each sample is
15
- # the share of its cycles CB2 spent high, through the output stage: a
16
- # one-pole low-pass at 5 kHz and a one-pole high-pass at 100 Hz, so a
17
- # line left high or low settles to silence.
11
+ # hand or floating high as an input. Machine::LineSound turns it into
12
+ # samples.
18
13
  class Sound
19
- LOW_PASS_HZ = 5_000
20
- HIGH_PASS_HZ = 100
21
-
22
- # A swing from CB2 low to CB2 high, as a signed 16-bit sample.
23
- SCALE = 24_000.0
14
+ include Machine::LineSound
24
15
 
25
16
  def initialize(via)
26
17
  @via = via
27
- @recording = false
28
- @high = true
29
- @run = 0
30
- @rate = 0
31
- @clock_hz = 0
32
- @remainder = 0
33
- @window = 0
34
- @left = 0
35
- @sum = 0
36
- @low = 0.0
37
- @drift = 0.0
38
- @low_pass = 0.0
39
- @high_pass = 0.0
40
- @samples = []
18
+ init_line_sound
41
19
  end
42
20
 
43
21
  def model = :cb2
44
22
 
45
- attr_reader :recording
46
- alias recording? recording
47
-
48
- # Starts collecting the output, averaged down to `rate` samples a
49
- # second at `clock_hz` cycles a second, for #drain_samples. The
50
- # output stage takes CB2's level now as settled.
51
- def record(rate:, clock_hz: 1_000_000)
52
- @rate = rate
53
- @clock_hz = clock_hz
54
- @remainder = 0
55
- @window = next_window
56
- @left = @window
57
- @sum = 0
58
- @high = @via.cb2_output
59
- @run = 0
60
- @low = @drift = @high ? 1.0 : 0.0
61
- @low_pass = 1.0 - Math.exp(-2.0 * Math::PI * LOW_PASS_HZ / rate)
62
- @high_pass = 1.0 - Math.exp(-2.0 * Math::PI * HIGH_PASS_HZ / rate)
63
- @samples = []
64
- @recording = true
65
- end
66
-
67
- # Takes CB2's level for this cycle, while recording.
68
- def cycle!
69
- high = @via.cb2_output
70
- if high == @high
71
- @run += 1
72
- else
73
- hold(@high, @run)
74
- @high = high
75
- @run = 1
76
- end
77
- end
78
-
79
- # The samples closed since the last drain.
80
- def drain_samples
81
- return [] unless @recording
82
-
83
- hold(@high, @run)
84
- @run = 0
85
- samples = @samples
86
- @samples = []
87
- samples
88
- end
89
-
90
23
  private
91
24
 
92
- # Adds `cycles` cycles with CB2 high or low, closing every sample
93
- # they finish.
94
- def hold(high, cycles)
95
- left = @left
96
- while cycles >= left
97
- @sum += left if high
98
- cycles -= left
99
- close
100
- left = @left
101
- end
102
- @sum += cycles if high
103
- @left = left - cycles
104
- end
105
-
106
- def close
107
- @low += @low_pass * (@sum.fdiv(@window) - @low)
108
- @drift += @high_pass * (@low - @drift)
109
- sample = ((@low - @drift) * SCALE).round
110
- sample = 32_767 if sample > 32_767
111
- sample = -32_768 if sample < -32_768
112
- @samples << sample
113
- @sum = 0
114
- @window = next_window
115
- @left = @window
116
- end
117
-
118
- # Each window is a whole number of cycles, and they add up to the
119
- # clock's cycles over a second.
120
- def next_window
121
- @remainder += @clock_hz
122
- cycles = @remainder / @rate
123
- @remainder -= cycles * @rate
124
- cycles
125
- end
25
+ def line_high? = @via.cb2_output
126
26
  end
127
27
  end
128
28
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class PET
5
+ class SuperPET < PET
6
+ # The board's 6551 at $EFF0, the RS-232 port's, on a 1.8432 MHz
7
+ # crystal, whose IRQ joins the PET's.
8
+ class ACIA < Chips::ACIA
9
+ end
10
+ end
11
+ end
12
+ end