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
@@ -5,9 +5,11 @@ module Badline
5
5
  # How a PET was built: its model's name, one of PET::Model::ALL, its
6
6
  # RAM below $8000 in K and its editor's frame rate, the arguments
7
7
  # PET.new takes, the ROMs it runs by file: BASIC, the editor, the
8
- # KERNAL and the characters, and the 8296 board's jumpers, by name and
9
- # comma-separated.
10
- PETSetup = Data.define(:model, :ram_kb, :refresh_hz, :basic, :editor, :kernal, :characters, :jumpers)
8
+ # KERNAL and the characters, the 8296 board's jumpers, by name and
9
+ # comma-separated, and the SuperPET's CPU and RAM switches, as
10
+ # PET::SuperPET::Board names their positions, empty on the others.
11
+ PETSetup = Data.define(:model, :ram_kb, :refresh_hz, :basic, :editor, :kernal, :characters, :jumpers,
12
+ :cpu_switch, :ram_switch)
11
13
 
12
14
  class PETSetup
13
15
  MODELS = PET::Model::ALL.map(&:name).freeze
@@ -20,20 +22,24 @@ module Badline
20
22
  refresh_hz = machine.refresh_hz
21
23
  new(model: model.name, ram_kb: machine.ram.length >> 10, refresh_hz:, basic: model.basic,
22
24
  editor: model.editor(refresh_hz), kernal: model.kernal, characters: model.characters,
23
- jumpers: machine.jumpers.join(","))
25
+ jumpers: machine.jumpers.join(","), cpu_switch: machine.superpet? ? machine.board.cpu_switch : "",
26
+ ram_switch: machine.superpet? ? machine.board.ram_switch : "")
24
27
  end
25
28
 
26
- # The model by name, the RAM, the frame rate, the ROMs and the
27
- # jumpers where a board has any, in a keyed snapshot module.
29
+ # The model by name, the RAM, the frame rate, the ROMs, the jumpers
30
+ # where a board has any and the switches where it has them, in a
31
+ # keyed snapshot module.
28
32
  def write_fields(out)
29
33
  out.text("model", model).int("ram_kb", ram_kb).int("refresh_hz", refresh_hz)
30
34
  out.text("rom.basic", basic).text("rom.editor", editor).text("rom.kernal", kernal)
31
35
  out.text("rom.characters", characters)
32
36
  out.text("jumpers", jumpers) unless jumpers.empty?
37
+ out.text("cpu_switch", cpu_switch).text("ram_switch", ram_switch) unless cpu_switch.empty?
33
38
  end
34
39
 
35
- # A ROM the snapshot doesn't name is the model's own, and a board
36
- # without jumpers named has none fitted.
40
+ # A ROM the snapshot doesn't name is the model's own, a board without
41
+ # jumpers named has none fitted, and a SuperPET without its switches
42
+ # named has them at 6502 and R/W.
37
43
  def self.read_fields(input)
38
44
  profile = PET::Model.named(input.choice("model", MODELS, PET::Model::DEFAULT))
39
45
  ram_kb = input.int("ram_kb", profile.ram_kb)
@@ -47,18 +53,27 @@ module Badline
47
53
  end
48
54
 
49
55
  refresh_hz = 60 unless profile.crtc?
56
+ superpet = profile.superpet?
50
57
  new(model: profile.name, ram_kb:, refresh_hz:, basic: input.text("rom.basic", profile.basic),
51
58
  editor: input.text("rom.editor", profile.editor(refresh_hz)),
52
59
  kernal: input.text("rom.kernal", profile.kernal),
53
- characters: input.text("rom.characters", profile.characters), jumpers: input.text("jumpers", ""))
60
+ characters: input.text("rom.characters", profile.characters), jumpers: input.text("jumpers", ""),
61
+ cpu_switch: superpet ? input.choice("cpu_switch", PET::SuperPET::Board::CPU_SWITCH, "6502") : "",
62
+ ram_switch: superpet ? input.choice("ram_switch", PET::SuperPET::Board::RAM_SWITCH, "rw") : "")
54
63
  end
55
64
 
56
65
  # A machine built this way, at power-on, with the BASIC, editor and
57
66
  # character ROMs PET::ROMSets names for its ROMs.
58
67
  def build
59
68
  version = PET::ROMSets::BASICS.find { |_, roms| roms.first == basic }&.first
60
- PET.new(model:, ram_kb:, refresh_hz:, basic: version, editor: PET::ROMSets::EDITORS.key(editor),
61
- characters: fitted_characters, jumpers: jumpers.split(","))
69
+ editor_name = PET::ROMSets::EDITORS.key(editor)
70
+ unless cpu_switch.empty?
71
+ return PET::SuperPET.new(refresh_hz:, basic: version, editor: editor_name, cpu: cpu_switch,
72
+ bank_ram: ram_switch)
73
+ end
74
+
75
+ PET.new(model:, ram_kb:, refresh_hz:, basic: version, editor: editor_name, characters: fitted_characters,
76
+ jumpers: jumpers.split(","))
62
77
  rescue ArgumentError => e
63
78
  raise FormatError, "the snapshot is of a #{self}, which badline doesn't build: #{e.message}"
64
79
  end
@@ -68,6 +83,7 @@ module Badline
68
83
  def to_s
69
84
  text = "#{model} with #{ram_kb}K at #{refresh_hz} Hz"
70
85
  text = "#{text} jumpered #{jumpers}" unless jumpers.empty?
86
+ text = "#{text} with its CPU switch at #{cpu_switch} and RAM switch at #{ram_switch}" unless cpu_switch.empty?
71
87
  roms = [basic, editor, kernal, characters]
72
88
  profile = PET::Model.named(model)
73
89
  stock = [profile.basic, profile.editor(refresh_hz), profile.kernal, profile.characters]
@@ -13,6 +13,7 @@ module Badline
13
13
  T64_ARCHIVE = 4
14
14
  D80 = 5
15
15
  D82 = 6
16
+ D67 = 7
16
17
 
17
18
  module_function
18
19
 
@@ -22,6 +23,7 @@ module Badline
22
23
  return D81 if image.is_a?(Storage::D81Image)
23
24
  return D82 if image.is_a?(Storage::D82Image)
24
25
  return D80 if image.is_a?(Storage::D80Image)
26
+ return D67 if image.is_a?(Storage::D67Image)
25
27
 
26
28
  raise ArgumentError, "#{image.class} can't go in a snapshot"
27
29
  end
@@ -41,7 +43,7 @@ module Badline
41
43
  end
42
44
  end
43
45
 
44
- KIND_NAMES = %w[directory d64 d71 d81 t64 d80 d82].freeze
46
+ KIND_NAMES = %w[directory d64 d71 d81 t64 d80 d82 d67].freeze
45
47
 
46
48
  # Opens the storage write_fields wrote: the same directory by its
47
49
  # path, an archive with the bytes it held, or a disk image with the
@@ -61,6 +63,7 @@ module Badline
61
63
  when D71 then Storage::D71Image.new(path, read_only:, bytes:)
62
64
  when D80 then Storage::D80Image.new(path, read_only:, bytes:)
63
65
  when D82 then Storage::D82Image.new(path, read_only:, bytes:)
66
+ when D67 then Storage::D67Image.new(path, read_only:, bytes:)
64
67
  else Storage::D81Image.new(path, read_only:, bytes:)
65
68
  end
66
69
  end
@@ -3,26 +3,30 @@
3
3
  module Badline
4
4
  module Snapshot
5
5
  # How a VIC-20 was built: its RAM expansion, a key of
6
- # Vic20::Bus::RAM_CONFIGURATIONS, the argument Vic20.new takes.
7
- Vic20Setup = Data.define(:ram)
6
+ # Vic20::Bus::RAM_CONFIGURATIONS, and its model, one of Vic20::MODELS,
7
+ # the arguments Vic20.new takes.
8
+ Vic20Setup = Data.define(:ram, :model)
8
9
 
9
10
  class Vic20Setup
10
11
  RAM_CONFIGURATIONS = Vic20::Bus::RAM_CONFIGURATIONS.keys.freeze
11
12
 
12
- # The RAM expansion by name, in a keyed snapshot module.
13
+ # The RAM expansion and the model by name, in a keyed snapshot module.
14
+ # A snapshot without a model is of a PAL machine, the only one badline
15
+ # built before the NTSC one.
13
16
  def write_fields(out)
14
- out.text("ram", ram.to_s)
17
+ out.text("ram", ram.to_s).text("model", model)
15
18
  end
16
19
 
17
20
  def self.read_fields(input)
18
21
  text = input.choice("ram", RAM_CONFIGURATIONS.map(&:to_s), "unexpanded")
19
- new(ram: RAM_CONFIGURATIONS.find { |name| name.to_s == text } || :unexpanded)
22
+ new(ram: RAM_CONFIGURATIONS.find { |name| name.to_s == text } || :unexpanded,
23
+ model: input.choice("model", Vic20::MODELS, "pal"))
20
24
  end
21
25
 
22
26
  # A machine built this way, at power-on.
23
- def build = Vic20.new(ram:)
27
+ def build = Vic20.new(ram:, model:)
24
28
 
25
- def to_s = ram.to_s
29
+ def to_s = model == "pal" ? ram.to_s : "#{ram} #{model}"
26
30
  end
27
31
  end
28
32
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "badline/snapshot/vice/cbm2_acia"
3
4
  require "badline/snapshot/vice/pet_cpu"
4
5
  require "badline/snapshot/vice/pet_mem"
5
6
  require "badline/snapshot/vice/pet_pias"
@@ -22,8 +23,8 @@ module Badline
22
23
  # The models badline builds by the board PETMEM names (PETMem): the
23
24
  # discrete board is a 2001's where xpet's settings say so, and a
24
25
  # 3032 otherwise.
25
- MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::CBM8096 => "8096",
26
- PETMem::CBM8296 => "8296" }.freeze
26
+ MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::SUPERPET => "superpet",
27
+ PETMem::CBM8096 => "8096", PETMem::CBM8296 => "8296" }.freeze
27
28
  # The modules xpet resets the machine without, for the parts badline
28
29
  # doesn't model in VICE's terms, each as xpet leaves them with
29
30
  # nothing attached: no file system drive open, both cassette ports
@@ -50,7 +51,8 @@ module Badline
50
51
  PIAs.export("PIA1", machine.pia1, reader(readers, "BL-PIA1")),
51
52
  PIAs.export("PIA2", machine.pia2, reader(readers, "BL-PIA2")), VIA.export(reader(readers, "BL-VIA")),
52
53
  *Drive2031.export(machine, readers), *Drive8050.export(machine, readers)] +
53
- PERIPHERALS.map { |name, major, minor, bytes| FieldWriter.new.bytes(bytes).section(name, major, minor) }
54
+ PERIPHERALS.map { |name, major, minor, bytes| FieldWriter.new.bytes(bytes).section(name, major, minor) } +
55
+ (machine.superpet? ? [CBM2::ACIA.export(reader(readers, "BL-ACIA"), machine.cycles)] : [])
54
56
  end
55
57
 
56
58
  def reader(readers, name) = readers[name] || Keyed::Reader.missing(name)
@@ -61,7 +63,9 @@ module Badline
61
63
  # xpet doesn't save the keyboard, the ROMs or an 8296's jumpers, so
62
64
  # a discrete board is a 3032 with the graphics keyboard unless it is
63
65
  # a 2001, each model has its own ROMs, and an 8296 none of its
64
- # jumpers, as xpet's own has.
66
+ # jumpers, as xpet's own has. Nor does it save the SuperPET's
67
+ # switches: its CPU switch is at the CPU that has the bus, and its
68
+ # RAM switch at R/W, which is the only position xpet has.
65
69
  def setup(container)
66
70
  memory = memory(container)
67
71
  ram_kb = PETMem.ram_kb(memory)
@@ -75,8 +79,11 @@ module Badline
75
79
  crtc = nil unless crtc.nil? || CRTC.reads?(crtc)
76
80
  refresh_hz = refresh_hz(crtc, profile)
77
81
  profile = Badline::PET::Model.upgraded(profile, nil, editor(crtc, profile, refresh_hz))
82
+ superpet = profile.superpet?
83
+ cpu_switch = PETMem.m6809?(memory) ? "6809" : "6502"
78
84
  PETSetup.new(model: name, ram_kb:, refresh_hz:, basic: profile.basic, editor: profile.editor(refresh_hz),
79
- kernal: profile.kernal, characters: profile.characters, jumpers: "")
85
+ kernal: profile.kernal, characters: profile.characters, jumpers: "",
86
+ cpu_switch: superpet ? cpu_switch : "", ram_switch: superpet ? "rw" : "")
80
87
  end
81
88
 
82
89
  def memory(container)
@@ -94,8 +101,7 @@ module Badline
94
101
  name = MODELS[board]
95
102
  return name unless name.nil?
96
103
 
97
- what = board == PETMem::SUPERPET ? "a SuperPET" : "board #{board}"
98
- raise FormatError, "#{memory}: #{what}, which badline doesn't build"
104
+ raise FormatError, "#{memory}: board #{board}, which badline doesn't build"
99
105
  end
100
106
 
101
107
  # The editor's frame rate, from the cycles the CRTC's registers set
@@ -25,7 +25,11 @@ module Badline
25
25
 
26
26
  def reads?(section) = section.major == MAJOR && section.minor == MINOR
27
27
 
28
- def export(machine) = write(machine, [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?].count(true))
28
+ def export(machine)
29
+ sources = [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?]
30
+ sources << machine.acia_irq? if machine.superpet?
31
+ write(machine, sources.count(true))
32
+ end
29
33
 
30
34
  # The module for `machine`, whose CPU's IRQ line `irqs` sources
31
35
  # pull.
@@ -52,16 +56,46 @@ module Badline
52
56
  end
53
57
 
54
58
  # The SuperPET's 6809, CPU6809 1.1, which xpet saves on every model
55
- # and won't load a snapshot without: the clock and the registers as
56
- # an xpet with no SuperPET board leaves them. badline doesn't read
57
- # it.
59
+ # and won't load a snapshot without: the clock, X, Y, U, S and PC,
60
+ # DP, CC, A and B, then five bytes. xpet 3.10's layout was inferred
61
+ # from its snapshots of a 6809 running a loop that loads each
62
+ # register with a value of its own; the five bytes are 0 in every
63
+ # snapshot xpet wrote.
64
+ #
65
+ # Written: the clock and the registers, the SuperPET's 6809's
66
+ # between instructions while it has the bus, and otherwise the
67
+ # registers of a 6809 that hasn't run, as xpet leaves them. Read:
68
+ # the registers, with the 6809 on an instruction boundary, as
69
+ # xpet's snapshot is taken on one.
58
70
  module CPU6809
59
71
  NAME = "CPU6809"
72
+ MAJOR = 1
73
+ MINOR = 1
74
+ # The CC an xpet without a SuperPET board saves.
75
+ IDLE_CC = 0x04
60
76
 
61
77
  module_function
62
78
 
79
+ def reads?(section) = section.major == MAJOR && section.minor == MINOR
80
+
63
81
  def export(machine)
64
- FieldWriter.new.qword(machine.cycles).zeros(11).byte(0x04).zeros(7).section(NAME, 1, 1)
82
+ fields = FieldWriter.new.qword(machine.cycles)
83
+ unless machine.superpet? && machine.m6809_running?
84
+ return fields.zeros(11).byte(IDLE_CC).zeros(7).section(NAME, MAJOR, MINOR)
85
+ end
86
+
87
+ cpu = machine.m6809
88
+ [cpu.x, cpu.y, cpu.u, cpu.s, cpu.pc].each { |register| fields.word(register) }
89
+ fields.byte(cpu.dp).byte(cpu.cc).byte(cpu.a).byte(cpu.b).zeros(5).section(NAME, MAJOR, MINOR)
90
+ end
91
+
92
+ # The registers by their BL-M6809 keys, in a keyed module's writer,
93
+ # with the 6809's cycle count the machine's `cycles`.
94
+ def import(section, out, cycles)
95
+ fields = FieldReader.new(section).skip(8)
96
+ %w[x y u s pc].each { |name| out.int(name, fields.word) }
97
+ %w[dp cc a b].each { |name| out.int(name, fields.byte) }
98
+ out.int("cycles", cycles).bool("nmi.armed", true)
65
99
  end
66
100
  end
67
101
  end
@@ -6,7 +6,7 @@ module Badline
6
6
  module PET
7
7
  # CRTC 1.2, which xpet saves on every model, the discrete boards'
8
8
  # video too, as a 6545 with the registers fixed: the board's screen
9
- # settings, the registers, the one selected, the beam's character,
9
+ # settings, the character set VIA CA2 picks, the registers, the one selected, the beam's character,
10
10
  # row and line in the row, whether the frame is in its vertical
11
11
  # adjust, the row's start in screen RAM, the lines of vertical sync
12
12
  # left, whether the rows show, the picture's size, the lines in the
@@ -36,6 +36,13 @@ module Badline
36
36
  SETTINGS = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x1f].freeze
37
37
  # The 8296's, whose screen RAM spans 4K.
38
38
  SETTINGS_8296 = [0xff, 0x0f, 0x00, 0x20, 0x00, 0x20, 0xff, 0x0f, 0xff, 0x1f].freeze
39
+ # The SuperPET's, whose character ROM spans 4K, inferred from
40
+ # xpet 3.10's SuperPET snapshots.
41
+ SETTINGS_SUPERPET = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x3f].freeze
42
+ # The word after the settings while VIA CA2 is high, picking the
43
+ # character ROM's second set, as xpet 3.10 writes it and paints
44
+ # the picture from on every PET board.
45
+ SECOND_SET = 0x1000
39
46
  # The 2001's and 3000 series' video as xpet's CRTC runs it: 64
40
47
  # cycles a line, 32 rows of 8 lines and 4 more, the first 25 rows
41
48
  # shown.
@@ -85,7 +92,7 @@ module Badline
85
92
  crtc_beam(run_on(fresh_crtc(machine), crtc_input, -CRTC_LEAD % frame_cycles(registers)),
86
93
  registers)
87
94
  end
88
- fields = FieldWriter.new.bytes(model.cbm8296? ? SETTINGS_8296 : SETTINGS).word(attributes(model))
95
+ fields = FieldWriter.new.bytes(settings(model)).word(machine.via.ca2_output ? SECOND_SET : 0)
89
96
  fields.byte(0).byte(columns(model))
90
97
  fields.byte(model.basic1? ? 1 : 0).bytes(registers).byte(discrete ? 0 : crtc_input.int("selected", 0))
91
98
  beam_fields(fields, beam, registers, model)
@@ -93,6 +100,12 @@ module Badline
93
100
  fields.section(NAME, MAJOR, MINOR)
94
101
  end
95
102
 
103
+ def settings(model)
104
+ return SETTINGS_8296 if model.cbm8296?
105
+
106
+ model.superpet? ? SETTINGS_SUPERPET : SETTINGS
107
+ end
108
+
96
109
  def crtc_registers(input)
97
110
  registers = input.bytes("registers", "".b).bytes
98
111
  Array.new(REGISTERS) { |index| registers[index] || 0 }