badline 0.16.0 → 0.16.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +32 -9
  4. data/doc/media.md +9 -0
  5. data/doc/snapshot-format.md +4 -2
  6. data/doc/snapshots.md +51 -10
  7. data/lib/badline/drives/ieee_bus.rb +50 -4
  8. data/lib/badline/drives/model2031/ieee_port.rb +133 -0
  9. data/lib/badline/drives/model2031/keyed_state.rb +66 -0
  10. data/lib/badline/drives/model2031/parts.rb +25 -0
  11. data/lib/badline/drives/model2031.rb +92 -0
  12. data/lib/badline/frontend/menu/menu_pages.rb +11 -2
  13. data/lib/badline/kernal_trap/ieee.rb +3 -0
  14. data/lib/badline/machine.rb +3 -2
  15. data/lib/badline/media/pet_media.rb +5 -5
  16. data/lib/badline/media/true_drive.rb +19 -6
  17. data/lib/badline/media.rb +6 -2
  18. data/lib/badline/options/help.rb +8 -4
  19. data/lib/badline/options/table.rb +9 -3
  20. data/lib/badline/options/validation.rb +20 -4
  21. data/lib/badline/options.rb +27 -8
  22. data/lib/badline/pet/bus/expansion.rb +99 -5
  23. data/lib/badline/pet/bus.rb +30 -12
  24. data/lib/badline/pet/characters.rb +8 -2
  25. data/lib/badline/pet/crtc_video.rb +20 -7
  26. data/lib/badline/pet/ieee_drive.rb +76 -0
  27. data/lib/badline/pet/ieee_ports.rb +12 -1
  28. data/lib/badline/pet/ieee_traps.rb +10 -5
  29. data/lib/badline/pet/keyed_modules.rb +4 -1
  30. data/lib/badline/pet/keyed_parts.rb +10 -6
  31. data/lib/badline/pet/model.rb +56 -6
  32. data/lib/badline/pet/rom_sets.rb +30 -6
  33. data/lib/badline/pet.rb +33 -15
  34. data/lib/badline/roms/README +33 -1
  35. data/lib/badline/roms/dos2031.rom +0 -0
  36. data/lib/badline/roms/pet/characters-din.rom +0 -0
  37. data/lib/badline/roms/pet/editor4-80-b-50hz-8296.rom +0 -0
  38. data/lib/badline/snapshot/container.rb +4 -1
  39. data/lib/badline/snapshot/field_doc.rb +4 -3
  40. data/lib/badline/snapshot/image.rb +22 -11
  41. data/lib/badline/snapshot/pet_setup.rb +30 -13
  42. data/lib/badline/snapshot/vice/pet.rb +117 -0
  43. data/lib/badline/snapshot/vice/pet_cpu.rb +67 -0
  44. data/lib/badline/snapshot/vice/pet_crtc.rb +246 -0
  45. data/lib/badline/snapshot/vice/pet_mem.rb +93 -0
  46. data/lib/badline/snapshot/vice/pet_pias.rb +56 -0
  47. data/lib/badline/snapshot/vice/pet_restore.rb +150 -0
  48. data/lib/badline/snapshot/vice/pet_via.rb +164 -0
  49. data/lib/badline/snapshot/vice.rb +1 -0
  50. data/lib/badline/version.rb +1 -1
  51. metadata +16 -1
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Snapshot
5
+ module Vice
6
+ module PET
7
+ # PETMEM 1.3: the board, the RAM below $8000 in K and the 8096's
8
+ # control register at $FFF0, then the RAM, the screen RAM, 1K or 2K
9
+ # as the board has, or on the 8296 its 32K from $8000, an 8096's or
10
+ # 8296's 64K board, and 24 bytes of xpet's own settings, which name
11
+ # the 2001.
12
+ #
13
+ # The board is 0 for the discrete video, 1 for a 40-column CRTC, 2
14
+ # for an 80-column one, 3 for the SuperPET, 4 for the 8096 and 5 for
15
+ # the 8296. xpet's ROMs aren't in the snapshot.
16
+ module PETMem
17
+ NAME = "PETMEM"
18
+ MAJOR = 1
19
+ MINOR = 3
20
+ DISCRETE = 0
21
+ CRTC40 = 1
22
+ CRTC80 = 2
23
+ SUPERPET = 3
24
+ CBM8096 = 4
25
+ CBM8296 = 5
26
+ # The byte after the header's control register, as xpet writes it.
27
+ HEADER_END = 0x05
28
+ # xpet's settings at the end, but for the second byte, which is
29
+ # SETTINGS_2001 on a 2001.
30
+ SETTINGS = [0x00, 0x00, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00,
31
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00].freeze
32
+ SETTINGS_2001 = 0x03
33
+ EXPANSION = 0x10000
34
+
35
+ module_function
36
+
37
+ def reads?(section) = section.major == MAJOR && section.minor == MINOR
38
+
39
+ def board(model)
40
+ return CBM8296 if model.cbm8296?
41
+ return CBM8096 if model.expansion
42
+
43
+ case model.video
44
+ when :crtc40 then CRTC40
45
+ when :crtc80 then CRTC80
46
+ else DISCRETE
47
+ end
48
+ end
49
+
50
+ def export(machine)
51
+ bus = machine.bus
52
+ expansion = bus.expansion
53
+ fields = FieldWriter.new.byte(board(machine.model)).byte(0).byte(machine.ram_kb)
54
+ fields.byte(expansion ? expansion.control : 0).byte(HEADER_END)
55
+ fields.bytes(machine.ram.snapshot).bytes(bus.screen.cells)
56
+ fields.bytes(expansion.cells) if expansion
57
+ settings = SETTINGS.dup
58
+ settings[1] = SETTINGS_2001 if machine.model.basic1?
59
+ fields.bytes(settings).section(NAME, MAJOR, MINOR)
60
+ end
61
+
62
+ # The board's number in the header.
63
+ def board_of(section) = FieldReader.new(section).byte
64
+
65
+ def ram_kb(section) = FieldReader.new(section).skip(2).byte
66
+
67
+ # Whether xpet's settings at the end name a 2001.
68
+ def pet2001?(section)
69
+ data = section.data
70
+ data.bytesize >= SETTINGS.length && data.getbyte(data.bytesize - SETTINGS.length + 1).to_i.anybits?(0x01)
71
+ end
72
+
73
+ # The RAM, the screen RAM and an 8096's or 8296's board and control
74
+ # register
75
+ # by their BL-RAM keys, in a keyed module's writer, for a machine
76
+ # with `screen_bytes` of screen RAM.
77
+ def import(section, out, screen_bytes)
78
+ fields = FieldReader.new(section)
79
+ board = fields.byte
80
+ fields.skip(1)
81
+ ram_kb = fields.byte
82
+ control = fields.byte
83
+ fields.skip(1)
84
+ out.bytes("ram", fields.string(ram_kb * 1024)).bytes("screen", fields.string(screen_bytes))
85
+ return unless [CBM8096, CBM8296].include?(board)
86
+
87
+ out.int("expansion.control", control).bytes("expansion.ram", fields.string(EXPANSION))
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Snapshot
5
+ module Vice
6
+ module PET
7
+ # PIA1 and PIA2 1.0: port A's output and direction registers and
8
+ # its control register as read, with the CA1 and CA2 flags in bits 7
9
+ # and 6, the same for port B, and the levels on CA2 and CB2 in bits
10
+ # 7 and 6 of the last byte, high while a line is an input.
11
+ #
12
+ # Read: each side's flags come from its control register, and a
13
+ # handshake under way from the level its C2 output is at. The levels
14
+ # outside on CA1 and CB1 aren't saved: they stay as the machine has
15
+ # them, with PIA 1's CB1 on the vertical drive line.
16
+ module PIAs
17
+ NAMES = %w[PIA1 PIA2].freeze
18
+ MAJOR = 1
19
+ MINOR = 0
20
+ CA2_HIGH = 0x80
21
+ CB2_HIGH = 0x40
22
+ HANDSHAKE = 0x20
23
+
24
+ module_function
25
+
26
+ def reads?(section) = section.major == MAJOR && section.minor == MINOR
27
+
28
+ # The PIA's module, from its BL-PIA1 or BL-PIA2 fields in `input`.
29
+ def export(name, pia, input)
30
+ fields = FieldWriter.new.byte(input.int("ora", 0)).byte(input.int("ddra", 0)).byte(pia.cra)
31
+ fields.byte(input.int("orb", 0)).byte(input.int("ddrb", 0)).byte(pia.crb)
32
+ fields.byte((pia.ca2_output ? CA2_HIGH : 0) | (pia.cb2_output ? CB2_HIGH : 0))
33
+ fields.section(name, MAJOR, MINOR)
34
+ end
35
+
36
+ # The registers and the control lines by their BL-PIA keys, in a
37
+ # keyed module's writer.
38
+ def import(section, out)
39
+ ora, ddra, cra, orb, ddrb, crb, lines = FieldReader.new(section).bytes(7)
40
+ out.int("ora", ora).int("ddra", ddra).int("orb", orb).int("ddrb", ddrb)
41
+ control_lines(out, "ca.", cra, lines.anybits?(CA2_HIGH))
42
+ control_lines(out, "cb.", crb, lines.anybits?(CB2_HIGH))
43
+ end
44
+
45
+ def control_lines(out, prefix, register, c2_high)
46
+ control = register & 0x3f
47
+ out.int("#{prefix}control", control).bool("#{prefix}c1_flag", register.anybits?(0x80))
48
+ out.bool("#{prefix}c2_flag", register.anybits?(0x40)).int("#{prefix}pulse", 0)
49
+ out.bool("#{prefix}handshake_low", control & 0x38 == HANDSHAKE && !c2_high)
50
+ out.bool("#{prefix}c2_high", c2_high) if control.nobits?(0x20)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Snapshot
5
+ module Vice
6
+ module PET
7
+ # Restores a PET from a snapshot xpet wrote. The modules badline
8
+ # reads become the PET's own keyed modules (Keyed), which it loads
9
+ # as it loads its own snapshot. Whatever they don't set stays as the
10
+ # machine has it at power-on, and the datasettes' tapes and the
11
+ # disk device 8 serves stay as they are.
12
+ module Restore
13
+ # MAINCPU and PETMEM hold the machine itself, so a version badline
14
+ # doesn't read fails the restore. The others are left out and
15
+ # reported.
16
+ READ = [MainCPU::NAME, PETMem::NAME, CRTC::NAME, "PIA1", "PIA2", VIA::NAME].freeze
17
+ # The host's media, which a restore keeps.
18
+ KEPT = %w[BL-TAPE1 BL-TAPE2 BL-TRAPDOS].freeze
19
+
20
+ module_function
21
+
22
+ # Applies the modules badline reads to `computer`, and returns a
23
+ # Report. The snapshot goes into a new machine first, so one that
24
+ # fails leaves `computer` as it was, and one of a machine built
25
+ # another way fails too.
26
+ def import(container, computer)
27
+ unless computer.family == :pet
28
+ raise FormatError, "the snapshot is of a PET, not a #{Image::FAMILIES.fetch(computer.family.to_s)}"
29
+ end
30
+
31
+ setup = PET.setup(container)
32
+ ours = PETSetup.of(computer)
33
+ raise FormatError, "the snapshot is of a #{setup}, not a #{ours}" unless setup == ours
34
+
35
+ apply(container, setup.build)
36
+ computer.power_cycle!
37
+ report = apply(container, computer)
38
+ kept = computer.mounted? ? ["the disk mounted in device 8 stays mounted"] : []
39
+ Report.new(applied: report.applied, ignored: report.ignored + kept)
40
+ end
41
+
42
+ # Applies the modules badline reads to `computer`, a machine built
43
+ # as the snapshot's was, and returns the Report.
44
+ def apply(container, computer)
45
+ applied = []
46
+ ignored = []
47
+ sections = []
48
+ READ.each do |name|
49
+ section = container[name]
50
+ next if section.nil?
51
+
52
+ if reads?(section)
53
+ applied << name
54
+ else
55
+ raise FormatError, "#{section}: badline doesn't read this version" if required?(name)
56
+
57
+ ignored << "#{section}: badline doesn't read this version, left out"
58
+ end
59
+ end
60
+ computer.save_modules.each { |section| sections << section if KEPT.include?(section.name) }
61
+ computer.load_modules(Keyed::Modules.new(modules(container, computer, applied) + sections))
62
+ Report.new(applied:, ignored: ignored + left_out(container))
63
+ end
64
+
65
+ def required?(name) = [MainCPU::NAME, PETMem::NAME].include?(name)
66
+
67
+ def reads?(section)
68
+ case section.name
69
+ when MainCPU::NAME then MainCPU.reads?(section)
70
+ when PETMem::NAME then PETMem.reads?(section)
71
+ when CRTC::NAME then CRTC.reads?(section)
72
+ when VIA::NAME then VIA.reads?(section)
73
+ else PIAs.reads?(section)
74
+ end
75
+ end
76
+
77
+ # The PET's keyed modules from the ones `applied` names.
78
+ def modules(container, computer, applied)
79
+ model = computer.model
80
+ crtc = applied.include?(CRTC::NAME) ? container[CRTC::NAME] : nil
81
+ video = video(crtc, computer)
82
+ drive = driving?(crtc, video, model)
83
+ sections = [Keyed.section("BL-MACHINE") { |out| machine(out, container, computer, drive) },
84
+ Keyed.section("BL-RAM") do |out|
85
+ PETMem.import(container[PETMem::NAME], out, model.screen_bytes)
86
+ end]
87
+ sections << Keyed.section("BL-CPU") { |out| MainCPU.import(container[MainCPU::NAME], out) }
88
+ %w[PIA1 PIA2].each do |name|
89
+ next unless applied.include?(name)
90
+
91
+ sections << Keyed.section("BL-#{name}") do |out|
92
+ PIAs.import(container[name], out)
93
+ out.bool("cb.c1_high", drive) if name == "PIA1"
94
+ end
95
+ end
96
+ if applied.include?(VIA::NAME)
97
+ sections << Keyed.section("BL-VIA") { |out| VIA.import(container[VIA::NAME], out) }
98
+ end
99
+ sections + video
100
+ end
101
+
102
+ def machine(out, container, computer, drive)
103
+ out.text("family", "pet")
104
+ PETSetup.of(computer).write_fields(out)
105
+ out.int("cycles", FieldReader.new(container[MainCPU::NAME]).qword).bool("vertical_drive", drive)
106
+ end
107
+
108
+ # The vertical drive line, low in the CRTC's vertical sync or past
109
+ # the discrete video's rows, as the video's module, `video`, has it.
110
+ def driving?(crtc, video, model)
111
+ return true if crtc.nil?
112
+
113
+ fields = Keyed::Reader.parse(video.first)
114
+ if model.video == :discrete
115
+ fields.int("line", 0) < Badline::PET::DiscreteVideo::DISPLAYED_LINES
116
+ else
117
+ fields.int("vsync_lines", 0).zero?
118
+ end
119
+ end
120
+
121
+ # The discrete video's module, or the CRTC's, run on from xpet's
122
+ # counters to where badline's drops the vertical drive line on the
123
+ # cycle xpet's does (CRTC::CRTC_LEAD). The CRTC runs on from the
124
+ # start of the beam's line, or for a beam in the first cycles of
125
+ # its line, a frame less those cycles on, which lands on the same
126
+ # counters while the registers stood through the frame as they
127
+ # stand.
128
+ def video(crtc, computer)
129
+ return [] if crtc.nil?
130
+
131
+ model = computer.model
132
+ return [Keyed.section("BL-VIDEO") { |out| CRTC.import_discrete(crtc, out) }] if model.video == :discrete
133
+
134
+ counters = Keyed.section("BL-CRTC") do |out|
135
+ CRTC.import(crtc, out, CRTC.columns(model), CRTC.attributes(model))
136
+ end
137
+ cycles = (CRTC::CRTC_LEAD + CRTC.column_of(crtc)) % CRTC.frame_cycles(CRTC.registers(crtc))
138
+ fields = CRTC.run_on(computer, Keyed::Reader.parse(counters), cycles)
139
+ [Keyed.section("BL-CRTC") { |out| CRTC.copy(fields, out) }]
140
+ end
141
+
142
+ def left_out(container)
143
+ container.sections.reject { |section| READ.include?(section.name) }
144
+ .map { |section| "#{section}: badline doesn't read this module, left out" }
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Snapshot
5
+ module Vice
6
+ module PET
7
+ # VIA 2.2, the 6522 at $E840: port A's and port B's output and
8
+ # direction registers, timer 1's latch and counter, timer 2's low
9
+ # and high latch, the value it was last loaded with and its counter,
10
+ # which timers will set their interrupt flag (timer 1 in bit 7,
11
+ # timer 2 in bit 6 while it counts cycles), the shift register, the
12
+ # ACR, the PCR, the interrupt flags and enable mask, timer 1's level
13
+ # on PB7 in bit 7, the shift register's clock ticks into its byte,
14
+ # CA2's level in bit 7, and whether timer 2's next underflow sets
15
+ # its flag. xpet saves each counter as a read on the next cycle
16
+ # finds it, where badline's holds it before that cycle counts.
17
+ #
18
+ # Written: timer 2's high latch and the value it was loaded with,
19
+ # which badline doesn't keep, as xpet has them before a program
20
+ # loads it, or while the shift register clocks off it, as its low
21
+ # byte reloads. Free-running, the shift register's ticks are only
22
+ # whether CB1 is low.
23
+ #
24
+ # Read: the registers and both timers with their latches, as they
25
+ # count from the next cycle, and the shift register where its clock
26
+ # ticks are. A load or reload under way, a shift clock tick due off
27
+ # timer 2 and the latched ports start afresh, and CB2's level
28
+ # follows from the PCR.
29
+ module VIA
30
+ NAME = "VIA"
31
+ MAJOR = 2
32
+ MINOR = 2
33
+ T1_ARMED = 0x80
34
+ T2_TIMED = 0x40
35
+ LEVELS = 0x60
36
+ # The shift register's clock ticks a byte takes, which an idle
37
+ # register's count stands at.
38
+ TICKS = 16
39
+ SIZE = 28
40
+ # The shift register's modes that clock off timer 2.
41
+ T2_SHIFTS = [1, 4, 5].freeze
42
+
43
+ module_function
44
+
45
+ def reads?(section) = section.major == MAJOR && section.minor == MINOR
46
+
47
+ # The VIA's module, from its BL-VIA fields in `input`.
48
+ def export(input)
49
+ fields = FieldWriter.new
50
+ %w[ora ddra orb ddrb].each { |key| fields.byte(input.int(key, 0)) }
51
+ fields.word(input.int("t1.latch", 0xffff)).word(timer1(input))
52
+ t2 = timer2(input)
53
+ latch_low = input.int("t2.latch_low", 0xff)
54
+ fields.byte(latch_low).byte(0xff).word(shifting?(input) ? (t2 & 0xff00) | latch_low : 0xffff).word(t2)
55
+ fields.byte(armed(input)).byte(input.int("sr.data", 0))
56
+ fields.byte(input.int("acr", 0)).byte(input.int("pcr", 0))
57
+ fields.byte(input.int("ifr.flags", 0)).byte(input.int("ifr.enable", 0))
58
+ fields.byte(input.bool("t1.pb7", true) ? 0x80 : 0)
59
+ fields.byte(ticks(input))
60
+ fields.byte(LEVELS | (input.bool("ca.c2_output", true) ? 0x80 : 0)).zeros(2)
61
+ fields.byte(input.bool("t2.armed", false) ? 1 : 0).zeros(2).section(NAME, MAJOR, MINOR)
62
+ end
63
+
64
+ # Timer 1's counter as a read on the next cycle finds it: as it
65
+ # stands while a load holds it, the latch while it reloads, and one
66
+ # less otherwise.
67
+ def timer1(input)
68
+ counter = input.int("t1.counter", 0xffff)
69
+ return counter if input.bool("t1.hold", false)
70
+ return input.int("t1.latch", 0xffff) if input.bool("t1.reload", false)
71
+
72
+ (counter - 1) & 0xffff
73
+ end
74
+
75
+ # Timer 2's counter as a read on the next cycle finds it: as it
76
+ # stands while it counts pulses or a load holds it, and one less
77
+ # otherwise.
78
+ def timer2(input)
79
+ counter = input.int("t2.counter", 0xffff)
80
+ return counter if input.bool("t2.hold", false) || input.bool("t2.counting_pulses", false)
81
+
82
+ (counter - 1) & 0xffff
83
+ end
84
+
85
+ # The shift register's clock ticks into its byte: two a bit, the
86
+ # first one dropping CB1, TICKS when idle. Free-running, its bits
87
+ # never run out, and only whether CB1 is low counts.
88
+ def ticks(input)
89
+ bits = input.int("sr.bits", 0)
90
+ return TICKS if bits.zero?
91
+
92
+ low = input.bool("sr.clock", true) ? 0 : 1
93
+ return low if input.int("sr.mode", 0) == 4
94
+
95
+ ((8 - bits) * 2) + low
96
+ end
97
+
98
+ # The shift register from its mode, data and the ticks into its
99
+ # byte (#ticks), with the last bit put out on CB2.
100
+ def shift_register(out, mode, data, ticks)
101
+ ticks = [ticks, TICKS].min
102
+ bits = ticks == TICKS ? 0 : 8 - (ticks / 2)
103
+ bits = 8 if mode == 4 && ticks < TICKS
104
+ out.int("sr.mode", mode).int("sr.data", data).int("sr.bits", bits).int("sr.delay", 0)
105
+ out.bool("sr.clock", ticks.even?).bool("sr.cb2", ticks.zero? || data.anybits?(0x01))
106
+ end
107
+
108
+ # Whether the shift register clocks off timer 2.
109
+ def shifting?(input) = T2_SHIFTS.include?((input.int("acr", 0) >> 2) & 0x07)
110
+
111
+ # Timer 2 sets its flag, or clocks the shift register, while it
112
+ # counts cycles.
113
+ def armed(input)
114
+ pulses = input.bool("t2.count_pulses", false)
115
+ timed = !pulses && (input.bool("t2.armed", false) || shifting?(input))
116
+ (input.bool("t1.armed", false) ? T1_ARMED : 0) | (timed ? T2_TIMED : 0)
117
+ end
118
+
119
+ # The registers, the timers and the control lines by their BL-VIA
120
+ # keys, in a keyed module's writer.
121
+ def import(section, out)
122
+ bytes = FieldReader.new(section).bytes(SIZE)
123
+ acr = bytes[0x10]
124
+ pcr = bytes[0x11]
125
+ out.int("ora", bytes[0]).int("ddra", bytes[1]).int("orb", bytes[2]).int("ddrb", bytes[3])
126
+ out.int("acr", acr).int("pcr", pcr)
127
+ timers(out, bytes, acr)
128
+ shift_register(out, (acr >> 2) & 0x07, bytes[0x0f], bytes[0x15])
129
+ out.int("ifr.flags", bytes[0x12] & 0x7f).int("ifr.enable", bytes[0x13] & 0x7f)
130
+ control_lines(out, "ca.", pcr & 0x0f, bytes[0x16].anybits?(0x80))
131
+ control_lines(out, "cb.", pcr >> 4, true)
132
+ end
133
+
134
+ def timers(out, bytes, acr)
135
+ out.int("t1.latch", bytes[4] | (bytes[5] << 8)).int("t1.counter",
136
+ ((bytes[6] | (bytes[7] << 8)) + 1) & 0xffff)
137
+ out.bool("t1.hold", false)
138
+ out.bool("t1.reload", false).bool("t1.armed", bytes[0x0e].anybits?(T1_ARMED))
139
+ out.bool("t1.pb7", bytes[0x14].anybits?(0x80))
140
+ pulses = acr.anybits?(0x20)
141
+ t2 = bytes[0x0c] | (bytes[0x0d] << 8)
142
+ out.int("t2.latch_low", bytes[8]).int("t2.counter", pulses ? t2 : (t2 + 1) & 0xffff).bool("t2.hold", false)
143
+ out.bool("t2.low_reload", false).bool("t2.low_underflowed", false)
144
+ out.bool("t2.armed", bytes[0x19].anybits?(0x01))
145
+ out.bool("t2.count_pulses", pulses).bool("t2.counting_pulses", pulses)
146
+ end
147
+
148
+ # A handshake under way holds C2 low.
149
+ def control_lines(out, prefix, control, c2_high)
150
+ mode = control >> 1
151
+ handshake_low = mode == 4 && !c2_high
152
+ output = case mode
153
+ when 4 then !handshake_low
154
+ when 6 then false
155
+ else true
156
+ end
157
+ out.int("#{prefix}control", control).bool("#{prefix}handshake_low", handshake_low)
158
+ out.int("#{prefix}pulse", 0).bool("#{prefix}c2_output", output)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -13,6 +13,7 @@ require "badline/snapshot/vice/peripherals"
13
13
  require "badline/snapshot/vice/vicii"
14
14
  require "badline/snapshot/vice/vicii_import"
15
15
  require "badline/snapshot/vice/restore"
16
+ require "badline/snapshot/vice/pet"
16
17
 
17
18
  module Badline
18
19
  module Snapshot
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Badline
4
- VERSION = "0.16.0"
4
+ VERSION = "0.16.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: badline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
@@ -314,6 +314,10 @@ files:
314
314
  - lib/badline/drives/model1581/wd1772/stepping.rb
315
315
  - lib/badline/drives/model1581/wd1772/tracks.rb
316
316
  - lib/badline/drives/model1581/wd1772/turn.rb
317
+ - lib/badline/drives/model2031.rb
318
+ - lib/badline/drives/model2031/ieee_port.rb
319
+ - lib/badline/drives/model2031/keyed_state.rb
320
+ - lib/badline/drives/model2031/parts.rb
317
321
  - lib/badline/drives/serial_bus.rb
318
322
  - lib/badline/ffi.rb
319
323
  - lib/badline/frontend.rb
@@ -428,6 +432,7 @@ files:
428
432
  - lib/badline/pet/cpu.rb
429
433
  - lib/badline/pet/crtc_video.rb
430
434
  - lib/badline/pet/discrete_video.rb
435
+ - lib/badline/pet/ieee_drive.rb
431
436
  - lib/badline/pet/ieee_ports.rb
432
437
  - lib/badline/pet/ieee_traps.rb
433
438
  - lib/badline/pet/keyboard_ports.rb
@@ -469,6 +474,7 @@ files:
469
474
  - lib/badline/roms/dos1551.rom
470
475
  - lib/badline/roms/dos1571.rom
471
476
  - lib/badline/roms/dos1581.rom
477
+ - lib/badline/roms/dos2031.rom
472
478
  - lib/badline/roms/eapi/LICENSE.md
473
479
  - lib/badline/roms/eapi/README
474
480
  - lib/badline/roms/eapi/eapi-am29f040-14
@@ -478,6 +484,7 @@ files:
478
484
  - lib/badline/roms/pet/basic1.rom
479
485
  - lib/badline/roms/pet/basic2.rom
480
486
  - lib/badline/roms/pet/basic4.rom
487
+ - lib/badline/roms/pet/characters-din.rom
481
488
  - lib/badline/roms/pet/characters1.rom
482
489
  - lib/badline/roms/pet/characters2.rom
483
490
  - lib/badline/roms/pet/editor1.rom
@@ -486,6 +493,7 @@ files:
486
493
  - lib/badline/roms/pet/editor4-40-n-50hz.rom
487
494
  - lib/badline/roms/pet/editor4-40-n-60hz.rom
488
495
  - lib/badline/roms/pet/editor4-80-b-50hz-0283.rom
496
+ - lib/badline/roms/pet/editor4-80-b-50hz-8296.rom
489
497
  - lib/badline/roms/pet/editor4-80-b-50hz.rom
490
498
  - lib/badline/roms/pet/editor4-80-b-60hz.rom
491
499
  - lib/badline/roms/pet/editor4-b.rom
@@ -535,6 +543,13 @@ files:
535
543
  - lib/badline/snapshot/vice/fields.rb
536
544
  - lib/badline/snapshot/vice/main_cpu.rb
537
545
  - lib/badline/snapshot/vice/peripherals.rb
546
+ - lib/badline/snapshot/vice/pet.rb
547
+ - lib/badline/snapshot/vice/pet_cpu.rb
548
+ - lib/badline/snapshot/vice/pet_crtc.rb
549
+ - lib/badline/snapshot/vice/pet_mem.rb
550
+ - lib/badline/snapshot/vice/pet_pias.rb
551
+ - lib/badline/snapshot/vice/pet_restore.rb
552
+ - lib/badline/snapshot/vice/pet_via.rb
538
553
  - lib/badline/snapshot/vice/restore.rb
539
554
  - lib/badline/snapshot/vice/reu1764.rb
540
555
  - lib/badline/snapshot/vice/reu_fields.rb