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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +153 -0
- data/CLAUDE.md +11 -7
- data/CONTRIBUTING.md +7 -1
- data/README.md +169 -20
- data/doc/pinned-behaviour.md +128 -0
- data/doc/snapshot-format.md +253 -18
- data/doc/snapshots.md +57 -23
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +1 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +1 -0
- data/lib/badline/cbm2/keyed_state.rb +4 -0
- data/lib/badline/cbm2/p500.rb +5 -3
- data/lib/badline/cbm2.rb +6 -3
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/timer.rb +14 -3
- data/lib/badline/chips/rriot.rb +4 -2
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/vic_i/keyed_parts.rb +7 -2
- data/lib/badline/chips/vic_i.rb +84 -21
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model2031.rb +6 -0
- data/lib/badline/drives/model4040/disk.rb +62 -0
- data/lib/badline/drives/model4040/ip_bus.rb +46 -0
- data/lib/badline/drives/model4040/keyed_state.rb +57 -0
- data/lib/badline/drives/model4040/mechanism.rb +192 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +179 -0
- data/lib/badline/drives/model8050/board.rb +77 -0
- data/lib/badline/drives/model8050/dos_board.rb +91 -0
- data/lib/badline/drives/model8050/idle.rb +11 -7
- data/lib/badline/drives/model8050/keyed_state.rb +5 -2
- data/lib/badline/drives/model8050.rb +18 -130
- data/lib/badline/drives/model9060/idle.rb +149 -0
- data/lib/badline/drives/model9060/keyed_state.rb +123 -0
- data/lib/badline/drives/model9060/parts.rb +98 -0
- data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
- data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
- data/lib/badline/drives/model9060/sasi_target.rb +161 -0
- data/lib/badline/drives/model9060.rb +195 -0
- data/lib/badline/frontend/app.rb +8 -0
- data/lib/badline/frontend/boot.rb +3 -2
- data/lib/badline/frontend/controls.rb +62 -6
- data/lib/badline/frontend/keys.rb +36 -0
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +42 -12
- data/lib/badline/frontend/menu/menu_pages.rb +97 -2
- data/lib/badline/frontend/menu/menu_ports.rb +38 -1
- data/lib/badline/frontend/menu/pause_menu.rb +5 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -1
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +31 -2
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/kim1/bus.rb +117 -0
- data/lib/badline/kim1/cpu.rb +25 -0
- data/lib/badline/kim1/host_terminal.rb +74 -0
- data/lib/badline/kim1/key_entry.rb +74 -0
- data/lib/badline/kim1/keyed_modules.rb +53 -0
- data/lib/badline/kim1/keyed_state.rb +133 -0
- data/lib/badline/kim1/panel.rb +176 -0
- data/lib/badline/kim1/rriots.rb +28 -0
- data/lib/badline/kim1/sound.rb +29 -0
- data/lib/badline/kim1/tape.rb +116 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +190 -0
- data/lib/badline/machine/ieee_drives.rb +94 -117
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine.rb +46 -15
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/true_drive.rb +42 -45
- data/lib/badline/media.rb +12 -6
- data/lib/badline/options/family.rb +98 -8
- data/lib/badline/options/help.rb +44 -12
- data/lib/badline/options/table.rb +43 -11
- data/lib/badline/options/validation.rb +44 -8
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +81 -0
- data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
- data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
- data/lib/badline/pet/superpet/m6809.rb +32 -0
- data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
- data/lib/badline/pet/superpet.rb +216 -0
- data/lib/badline/pet.rb +19 -3
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +1 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +166 -0
- data/lib/badline/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/container.rb +2 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +27 -6
- data/lib/badline/snapshot/image.rb +4 -1
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/pet.rb +13 -7
- data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
- data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
- data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
- data/lib/badline/snapshot.rb +4 -0
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/disk_image/writing.rb +2 -2
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +4 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20.rb +26 -12
- data/lib/badline.rb +1 -0
- metadata +77 -1
|
@@ -4,70 +4,96 @@ module Badline
|
|
|
4
4
|
module Snapshot
|
|
5
5
|
module Vice
|
|
6
6
|
module PET
|
|
7
|
-
# A true
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
7
|
+
# A true unit with an interface processor on device 8, as xpet 3.10,
|
|
8
|
+
# xcbm2 and xcbm5x0 save it with true drive emulation: an 8050, an
|
|
9
|
+
# 8250, an SFD-1001, a 4040, a 3040, a 2040, or a D9060 or D9090,
|
|
10
|
+
# which xpet's type 9000 stands for. Each is DRIVE8 2.0 with the
|
|
11
|
+
# unit's drives, DRIVE9 to DRIVE11 with none, DRIVECPU0 1.3, the
|
|
12
|
+
# interface processor and its RAM, RIOT1D0 and RIOT2D0 0.0, its two
|
|
13
|
+
# 6532s, and FDC0 0.0. xpet runs the floppy controller and the hard
|
|
14
|
+
# disk's SASI host at a high level, so their processors, VIAs and
|
|
15
|
+
# 6530s aren't in the snapshot: FDC0 holds the controller's state,
|
|
16
|
+
# its last track in byte 6.
|
|
14
17
|
#
|
|
15
|
-
# DRIVE8 holds a block of 139 bytes for each
|
|
16
|
-
# unit: each block has its head's half track, twice
|
|
17
|
-
# $10 and the
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
18
|
+
# DRIVE8 holds a block of 139 bytes for each of the unit's drives,
|
|
19
|
+
# then 6 for the unit: each block has its head's half track, twice
|
|
20
|
+
# the track, at $10 and the unit's type as a word at $28 (TYPES).
|
|
21
|
+
# The 1001 and the hard disks have one block, and a hard disk's head
|
|
22
|
+
# rests at HARD_DISK_TRACK. DRIVECPU0 is laid out as the 2031's
|
|
23
|
+
# (Drive2031), its RAM the IP's page 0, the 6532s' 128 bytes each,
|
|
24
|
+
# then the 4K of common RAM. A RIOT module holds port A's output and
|
|
25
|
+
# direction registers, port B's, the edge control (bit 0 the PA7
|
|
26
|
+
# interrupt, bit 1 a rising edge), the flags (bit 7 the timer's
|
|
27
|
+
# interrupt, which xpet raises IRQ for, bit 6 PA7's), the timer's
|
|
24
28
|
# counter, its divider as a word, two bytes xpet leaves 0, and
|
|
25
29
|
# whether the timer interrupts.
|
|
26
30
|
#
|
|
27
|
-
# Written, once the controller is in its idle
|
|
28
|
-
# instructions (PET#settle_for_vice!): the IP's
|
|
29
|
-
# the 6532s, the heads' tracks, and FDC0 as xpet
|
|
30
|
-
# controller with the track badline's controller last
|
|
31
|
-
# A unit whose controller is still busy is left out of
|
|
32
|
-
# modules. Read: the IP's registers and RAM and the 6532s,
|
|
33
|
-
# controller as a unit switched on has it in its idle loop,
|
|
34
|
-
# heads on track 1, from where its next job steps them. A job
|
|
35
|
-
# way in xpet's controller starts again from there. The disks
|
|
36
|
-
# the drives stay
|
|
31
|
+
# Written, once the controller, or the SASI host, is in its idle
|
|
32
|
+
# loop between instructions (PET#settle_for_vice!): the IP's
|
|
33
|
+
# registers and RAM, the 6532s, the heads' tracks, and FDC0 as xpet
|
|
34
|
+
# leaves an idle controller with the track badline's controller last
|
|
35
|
+
# stepped to. A unit whose controller is still busy is left out of
|
|
36
|
+
# xpet's modules. Read: the IP's registers and RAM and the 6532s,
|
|
37
|
+
# with the controller as a unit switched on has it in its idle loop,
|
|
38
|
+
# its heads on track 1, from where its next job steps them. A job
|
|
39
|
+
# under way in xpet's controller starts again from there. The disks
|
|
40
|
+
# in the drives stay, and a hard disk's image with them, which also
|
|
41
|
+
# says whether the unit is a D9060 or a D9090.
|
|
37
42
|
module Drive8050
|
|
38
43
|
UNIT = "DRIVE8"
|
|
39
44
|
CPU = Drive2031::CPU
|
|
40
45
|
RIOTS = %w[RIOT1D0 RIOT2D0].freeze
|
|
41
46
|
FDC = "FDC0"
|
|
42
47
|
NAMES = [UNIT, CPU, *RIOTS, FDC].freeze
|
|
43
|
-
|
|
44
|
-
#
|
|
45
|
-
|
|
48
|
+
# xpet's drive types by DRIVE8's type word, for the units written
|
|
49
|
+
# and read here.
|
|
50
|
+
TYPES = { 0x1f72 => "8050", 0x203a => "8250", 0x03e9 => "1001", 0x0fc8 => "4040", 0x0be0 => "3040",
|
|
51
|
+
0x07f8 => "2040", 0x2328 => "9060" }.freeze
|
|
52
|
+
# The type word each model is written with.
|
|
53
|
+
WORDS = TYPES.invert.merge("9090" => 0x2328).freeze
|
|
54
|
+
# The units that are hard disks, and the DOS 2.1 and DOS 1 units
|
|
55
|
+
# on the 4040's board.
|
|
56
|
+
HARD_DISKS = %w[9060 9090].freeze
|
|
57
|
+
DOS2_BOARD = %w[4040 3040 2040].freeze
|
|
58
|
+
# The drives each unit has, a block of DRIVE8 each.
|
|
59
|
+
DRIVE_COUNTS = { "8050" => 2, "8250" => 2, "1001" => 1, "4040" => 2, "3040" => 2, "2040" => 2, "9060" => 1,
|
|
60
|
+
"9090" => 1 }.freeze
|
|
61
|
+
# A drive's block in DRIVE8, and its fields.
|
|
62
|
+
BLOCK = 0x8b
|
|
46
63
|
HALF_TRACK = 0x10
|
|
47
64
|
TYPE = 0x28
|
|
48
|
-
|
|
65
|
+
# The half track xpet keeps a hard disk's head on.
|
|
66
|
+
HARD_DISK_TRACK = 0x98
|
|
67
|
+
# The unit's bytes after the drives' blocks.
|
|
68
|
+
TAIL = 6
|
|
49
69
|
# DRIVE8 as xpet saves an idle 8050, by the bytes that aren't 0,
|
|
50
|
-
# but for the type
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
70
|
+
# but for the type and the drive count at COUNT: the first drive's
|
|
71
|
+
# block, and from BLOCK on the second's with a byte past it.
|
|
72
|
+
IDLE = { 0x00 => 0x01, 0x02 => 0x01, 0x0e => 0x01, 0x0f => 0x01, 0x10 => 0x4c, 0x1a => 0x01, 0x20 => 0x55,
|
|
73
|
+
0x30 => 0x06, 0x6f => 0xcd, 0x70 => 0xab, 0x71 => 0x34, 0x72 => 0x12 }.freeze
|
|
74
|
+
IDLE_SECOND = { 0x0e => 0x01, 0x0f => 0x01, 0x10 => 0x24, 0x1a => 0x01, 0x20 => 0x55, 0x8f => 0x01 }.freeze
|
|
75
|
+
COUNT = 0x01
|
|
54
76
|
ZERO_PAGE = 0x80
|
|
55
77
|
COMMON = 0x1000
|
|
56
78
|
# FDC0 as xpet leaves an idle controller, and where the track goes.
|
|
57
79
|
FDC_IDLE = [0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00].freeze
|
|
58
80
|
FDC_TRACK = 6
|
|
59
81
|
# Where badline's controller keeps drive 0's track, in the 6530's
|
|
60
|
-
# RAM.
|
|
82
|
+
# RAM: the 8050's, and the 4040's in the low six bits.
|
|
61
83
|
TRACK_AT = 0x21
|
|
84
|
+
DOS2_TRACK_AT = 0x03
|
|
85
|
+
DOS2_TRACK = 0x3f
|
|
62
86
|
DIVIDERS = [1, 8, 64, 1024].freeze
|
|
63
87
|
IRQ_EDGE = 0x01
|
|
64
88
|
RISING = 0x02
|
|
65
89
|
TIMER_FLAG = 0x80
|
|
66
90
|
PA7_FLAG = 0x40
|
|
91
|
+
# The hard disk's controller, which holds the image's path.
|
|
92
|
+
SASI = "BL-D8-SASI"
|
|
67
93
|
|
|
68
94
|
module_function
|
|
69
95
|
|
|
70
|
-
# Whether the snapshot has a true
|
|
96
|
+
# Whether the snapshot has a true unit of these.
|
|
71
97
|
def present?(container) = !container[FDC].nil? && !container[CPU].nil?
|
|
72
98
|
|
|
73
99
|
def reads?(section)
|
|
@@ -78,48 +104,93 @@ module Badline
|
|
|
78
104
|
end
|
|
79
105
|
end
|
|
80
106
|
|
|
81
|
-
# The model DRIVE8 names, or nil.
|
|
107
|
+
# The model DRIVE8 names, or nil. Type 9000 reads as the D9060.
|
|
82
108
|
def model(container)
|
|
83
109
|
unit = container[UNIT]
|
|
84
|
-
return nil if unit.nil? || unit.data.bytesize <
|
|
110
|
+
return nil if unit.nil? || unit.data.bytesize < BLOCK
|
|
85
111
|
|
|
86
|
-
TYPES[FieldReader.new(unit).skip(TYPE).word]
|
|
112
|
+
model = TYPES[FieldReader.new(unit).skip(TYPE).word]
|
|
113
|
+
model if model && unit.data.bytesize >= size(model)
|
|
87
114
|
end
|
|
88
115
|
|
|
89
|
-
# xpet
|
|
90
|
-
|
|
91
|
-
|
|
116
|
+
# Whether xpet saves the two models as one type.
|
|
117
|
+
def same_type?(model, other) = WORDS.key?(model) && WORDS[model] == WORDS[other]
|
|
118
|
+
|
|
119
|
+
def hard_disk?(model) = HARD_DISKS.include?(model)
|
|
120
|
+
|
|
121
|
+
# The hard disk whose image the kept modules `disks` hold: a
|
|
122
|
+
# D9090 for one with six heads, and otherwise a D9060.
|
|
123
|
+
def hard_disk_model(disks)
|
|
124
|
+
own = disks.find { |section| section.name == SASI }
|
|
125
|
+
own && Keyed::Reader.parse(own).int("disk_heads", 4) == 6 ? "9090" : "9060"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# DRIVE8's size for the model.
|
|
129
|
+
def size(model) = (BLOCK * DRIVE_COUNTS.fetch(model)) + TAIL
|
|
130
|
+
|
|
131
|
+
# xpet's modules for the unit whose BL-D8 modules `readers` holds,
|
|
132
|
+
# and none without one, or with its controller away from its idle
|
|
133
|
+
# loop.
|
|
92
134
|
def export(machine, readers)
|
|
93
135
|
drive = readers["BL-D8"]
|
|
94
136
|
model = drive&.text("model", "")
|
|
95
|
-
return [] unless
|
|
137
|
+
return [] unless WORDS.key?(model) && idle?(model, readers)
|
|
96
138
|
|
|
97
|
-
[unit(model, readers
|
|
139
|
+
[unit(model, readers), *Drive2031::UNITS.drop(1).map { |name| none(name) },
|
|
98
140
|
Drive2031.cpu_section(machine, readers.fetch("BL-D8-IPCPU"), ram(readers)),
|
|
99
141
|
riot(RIOTS[0], readers.fetch("BL-D8-RIOT1")), riot(RIOTS[1], readers.fetch("BL-D8-RIOT2")),
|
|
100
|
-
fdc(readers
|
|
142
|
+
fdc(model, readers)]
|
|
101
143
|
end
|
|
102
144
|
|
|
103
145
|
# Whether both processors are between instructions, the
|
|
104
146
|
# controller at the start of its idle loop.
|
|
105
|
-
def idle?(readers)
|
|
106
|
-
|
|
107
|
-
!position?(
|
|
108
|
-
|
|
147
|
+
def idle?(model, readers)
|
|
148
|
+
controller = readers.fetch(hard_disk?(model) ? "BL-D8-SASICPU" : "BL-D8-FDCCPU")
|
|
149
|
+
!position?(controller) && !position?(readers.fetch("BL-D8-IPCPU")) &&
|
|
150
|
+
controller.int("pc", 0) == controller_loop(model)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Where the model's controller, or its SASI host, waits for jobs.
|
|
154
|
+
def controller_loop(model)
|
|
155
|
+
case model
|
|
156
|
+
when "9060", "9090" then Drives::Model9060::Idle::SASI_LOOP
|
|
157
|
+
when "2040" then Drives::Model2040::FDC_LOOP
|
|
158
|
+
when "4040", "3040" then Drives::Model4040::FDC_LOOP
|
|
159
|
+
else Drives::Model8050::Idle::FDC_LOOP
|
|
160
|
+
end
|
|
109
161
|
end
|
|
110
162
|
|
|
111
163
|
def position?(cpu) = !cpu.text("pos.seq", "").empty?
|
|
112
164
|
|
|
113
|
-
def unit(model,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
165
|
+
def unit(model, readers)
|
|
166
|
+
count = DRIVE_COUNTS.fetch(model)
|
|
167
|
+
bytes = Array.new(size(model), 0)
|
|
168
|
+
IDLE.each { |offset, value| bytes[offset] = value }
|
|
169
|
+
IDLE_SECOND.each { |offset, value| bytes[BLOCK + offset] = value } if count > 1
|
|
170
|
+
bytes[COUNT] = count
|
|
171
|
+
tracks = half_tracks(model, readers)
|
|
172
|
+
count.times do |drive|
|
|
173
|
+
at = BLOCK * drive
|
|
174
|
+
bytes[at + HALF_TRACK] = tracks[drive]
|
|
175
|
+
bytes[at + TYPE, 2] = [WORDS.fetch(model)].pack("v").bytes
|
|
119
176
|
end
|
|
120
177
|
FieldWriter.new.bytes(bytes).section(UNIT, Drive2031::DRIVE_MAJOR, Drive2031::DRIVE_MINOR)
|
|
121
178
|
end
|
|
122
179
|
|
|
180
|
+
# Each drive's head as xpet's half track: the 8050's cylinder on
|
|
181
|
+
# from 1, the 4040's half track on from track 1, and a hard
|
|
182
|
+
# disk's where xpet keeps it.
|
|
183
|
+
def half_tracks(model, readers)
|
|
184
|
+
return [HARD_DISK_TRACK] if hard_disk?(model)
|
|
185
|
+
|
|
186
|
+
mechanism = readers.fetch("BL-D8-MECH")
|
|
187
|
+
if DOS2_BOARD.include?(model)
|
|
188
|
+
mechanism.ints("half_tracks", [0, 0]).map { |half_track| half_track + 2 }
|
|
189
|
+
else
|
|
190
|
+
mechanism.ints("cylinders", [0, 0]).map { |cylinder| (cylinder + 1) * 2 }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
123
194
|
def none(name) = Drive2031.unit(Drive2031::NONE, name)
|
|
124
195
|
|
|
125
196
|
# The IP's RAM as xpet lays it out: page 0 from both 6532s, then
|
|
@@ -143,16 +214,25 @@ module Badline
|
|
|
143
214
|
fields.zeros(2).flag(input.bool("timer.irq_enabled", false)).section(name, 0, 0)
|
|
144
215
|
end
|
|
145
216
|
|
|
146
|
-
|
|
217
|
+
# FDC0 with the controller's track, and a hard disk's with none.
|
|
218
|
+
def fdc(model, readers)
|
|
147
219
|
bytes = FDC_IDLE.dup
|
|
148
|
-
|
|
220
|
+
unless hard_disk?(model)
|
|
221
|
+
ram = readers.fetch("BL-D8-RRIOT").bytes("ram", "".b)
|
|
222
|
+
bytes[FDC_TRACK] = if DOS2_BOARD.include?(model)
|
|
223
|
+
ram.getbyte(DOS2_TRACK_AT).to_i & DOS2_TRACK
|
|
224
|
+
else
|
|
225
|
+
ram.getbyte(TRACK_AT).to_i
|
|
226
|
+
end
|
|
227
|
+
end
|
|
149
228
|
FieldWriter.new.bytes(bytes).section(FDC, 0, 0)
|
|
150
229
|
end
|
|
151
230
|
|
|
152
231
|
# The unit's keyed modules, named for device 8, from the
|
|
153
232
|
# snapshot's: the IP's side from xpet's, the controller's side
|
|
154
233
|
# from `reference`, a unit's in its idle loop, and `disks`, the
|
|
155
|
-
# drives' disk modules badline keeps
|
|
234
|
+
# drives' disk modules badline keeps, or a hard disk's controller
|
|
235
|
+
# with its image.
|
|
156
236
|
def import(container, reference, disks)
|
|
157
237
|
cpu = container[CPU]
|
|
158
238
|
page = FieldReader.new(cpu).skip(Drive2031::RAM_AT).string(ZERO_PAGE * 2)
|
|
@@ -162,16 +242,48 @@ module Badline
|
|
|
162
242
|
riot_section("BL-D8-RIOT1", container[RIOTS[0]], page.byteslice(0, ZERO_PAGE)),
|
|
163
243
|
riot_section("BL-D8-RIOT2", container[RIOTS[1]], page.byteslice(ZERO_PAGE, ZERO_PAGE)),
|
|
164
244
|
Keyed.section("BL-D8-IPCPU") { |out| Drive2031.registers(cpu, out) }]
|
|
165
|
-
sections + reference
|
|
245
|
+
sections + controller_side(reference, disks)
|
|
166
246
|
end
|
|
167
247
|
|
|
168
|
-
# The controller's side, which comes from a unit in its idle loop
|
|
248
|
+
# The controller's side, which comes from a unit in its idle loop:
|
|
249
|
+
# a floppy unit's, and a hard disk's SASI host.
|
|
169
250
|
CONTROLLER = %w[BL-D8-FDCCPU BL-D8-VIA BL-D8-RRIOT BL-D8-MECH].freeze
|
|
251
|
+
HOST = %w[BL-D8-SASICPU BL-D8-SASIRAM BL-D8-VIA].freeze
|
|
252
|
+
|
|
253
|
+
def controller_side(reference, disks)
|
|
254
|
+
base = reference.find { |section| section.name == SASI }
|
|
255
|
+
return reference.select { |section| CONTROLLER.include?(section.name) } + disks if base.nil?
|
|
256
|
+
|
|
257
|
+
reference.select { |section| HOST.include?(section.name) } + [controller(base, disks)]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# The hard disk's controller as `base` has it, with the image
|
|
261
|
+
# badline's kept in `disks`.
|
|
262
|
+
def controller(base, disks)
|
|
263
|
+
kept = disks.find { |section| section.name == SASI }
|
|
264
|
+
return base if kept.nil?
|
|
170
265
|
|
|
266
|
+
target = Drives::Model9060::SASITarget.new
|
|
267
|
+
target.load_fields(Keyed::Reader.parse(base))
|
|
268
|
+
own = Keyed::Reader.parse(kept)
|
|
269
|
+
path = own.text("path", "")
|
|
270
|
+
if File.file?(path)
|
|
271
|
+
target.disk = Storage::D90Image.new(path, read_only: own.bool("read_only", false),
|
|
272
|
+
heads: own.int("disk_heads", 4))
|
|
273
|
+
end
|
|
274
|
+
Keyed.section(SASI) { |out| target.save_fields(out) }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# The unit's own fields: its model and clock, with the controller's
|
|
278
|
+
# last byte from `reference`, or a hard disk's SASI lines.
|
|
171
279
|
def unit_fields(container, reference, out)
|
|
172
280
|
own = Keyed::Reader.parse(reference.find { |section| section.name == "BL-D8" })
|
|
173
|
-
|
|
174
|
-
out.
|
|
281
|
+
model = own.text("model", "8050")
|
|
282
|
+
out.text("model", model).int("phase", 0).int("cycles", FieldReader.new(container[CPU]).qword)
|
|
283
|
+
return out.int("fdc_data", own.int("fdc_data", 0)) unless hard_disk?(model)
|
|
284
|
+
|
|
285
|
+
out.int("sasi_data", own.int("sasi_data", 0)).bool("sasi.ack_high", own.bool("sasi.ack_high", true))
|
|
286
|
+
out.bool("sasi.reset_high", own.bool("sasi.reset_high", false))
|
|
175
287
|
end
|
|
176
288
|
|
|
177
289
|
def riot_section(name, section, ram)
|
|
@@ -199,15 +311,23 @@ module Badline
|
|
|
199
311
|
end
|
|
200
312
|
|
|
201
313
|
def booted(model)
|
|
202
|
-
unit = model
|
|
203
|
-
|
|
204
|
-
unit.cycle! until
|
|
314
|
+
unit = new_unit(model)
|
|
315
|
+
unit.boot_cycles.times { unit.host_cycle! }
|
|
316
|
+
unit.cycle! until unit.settled_for_vice?
|
|
205
317
|
unit.save_modules([], "BL-D8")
|
|
206
318
|
end
|
|
207
319
|
|
|
208
|
-
def
|
|
209
|
-
|
|
210
|
-
|
|
320
|
+
def new_unit(model)
|
|
321
|
+
case model
|
|
322
|
+
when "8250" then Drives::Model8250.new
|
|
323
|
+
when "1001" then Drives::Model1001.new
|
|
324
|
+
when "4040" then Drives::Model4040.new
|
|
325
|
+
when "3040" then Drives::Model3040.new
|
|
326
|
+
when "2040" then Drives::Model2040.new
|
|
327
|
+
when "9060" then Drives::Model9060.new
|
|
328
|
+
when "9090" then Drives::Model9090.new
|
|
329
|
+
else Drives::Model8050.new
|
|
330
|
+
end
|
|
211
331
|
end
|
|
212
332
|
end
|
|
213
333
|
end
|
|
@@ -5,14 +5,24 @@ module Badline
|
|
|
5
5
|
module Vice
|
|
6
6
|
module PET
|
|
7
7
|
# PETMEM 1.3: the board, the RAM below $8000 in K and the 8096's
|
|
8
|
-
# control register at $FFF0,
|
|
9
|
-
# as the board has, or on the 8296
|
|
10
|
-
#
|
|
11
|
-
# the 2001
|
|
8
|
+
# control register at $FFF0, a byte for the SuperPET's latches, then
|
|
9
|
+
# the RAM, the screen RAM, 1K or 2K as the board has, or on the 8296
|
|
10
|
+
# its 32K from $8000, an 8096's, 8296's or SuperPET's 64K board, and
|
|
11
|
+
# 24 bytes of xpet's own settings, which name the 2001, and on the
|
|
12
|
+
# SuperPET the CPU that has the bus.
|
|
12
13
|
#
|
|
13
14
|
# The board is 0 for the discrete video, 1 for a 40-column CRTC, 2
|
|
14
15
|
# for an 80-column one, 3 for the SuperPET, 4 for the 8096 and 5 for
|
|
15
16
|
# the 8296. xpet's ROMs aren't in the snapshot.
|
|
17
|
+
#
|
|
18
|
+
# The SuperPET's latch byte holds the bank at $9000 in its high
|
|
19
|
+
# nibble, and sets bit 0 while the bank RAM is there rather than the
|
|
20
|
+
# empty socket, and bit 2 always. Its settings hold 1 at offset 2
|
|
21
|
+
# while the 6809 has the bus. Both were inferred from xpet 3.10's
|
|
22
|
+
# snapshots after writes to the bank latch and $EFFE, and in both
|
|
23
|
+
# modes. xpet has no system latch, so the bank latch's bit 7 and the
|
|
24
|
+
# system latch aren't saved, and a restore clears them. Nor is the
|
|
25
|
+
# 6702: a restore leaves it as the RES line does.
|
|
16
26
|
module PETMem
|
|
17
27
|
NAME = "PETMEM"
|
|
18
28
|
MAJOR = 1
|
|
@@ -30,6 +40,14 @@ module Badline
|
|
|
30
40
|
SETTINGS = [0x00, 0x00, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00,
|
|
31
41
|
0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00].freeze
|
|
32
42
|
SETTINGS_2001 = 0x03
|
|
43
|
+
# The SuperPET's settings byte for the CPU, and its value while the
|
|
44
|
+
# 6809 has the bus.
|
|
45
|
+
SETTINGS_CPU = 2
|
|
46
|
+
SETTINGS_M6809 = 0x01
|
|
47
|
+
# The SuperPET's latch byte: the bit always set, and the one for
|
|
48
|
+
# the bank RAM at $9000.
|
|
49
|
+
LATCHES = 0x04
|
|
50
|
+
BANK_RAM = 0x01
|
|
33
51
|
EXPANSION = 0x10000
|
|
34
52
|
|
|
35
53
|
module_function
|
|
@@ -37,6 +55,7 @@ module Badline
|
|
|
37
55
|
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
38
56
|
|
|
39
57
|
def board(model)
|
|
58
|
+
return SUPERPET if model.superpet?
|
|
40
59
|
return CBM8296 if model.cbm8296?
|
|
41
60
|
return CBM8096 if model.expansion
|
|
42
61
|
|
|
@@ -50,15 +69,40 @@ module Badline
|
|
|
50
69
|
def export(machine)
|
|
51
70
|
bus = machine.bus
|
|
52
71
|
expansion = bus.expansion
|
|
72
|
+
superpet = machine.superpet?
|
|
53
73
|
fields = FieldWriter.new.byte(board(machine.model)).byte(0).byte(machine.ram_kb)
|
|
54
|
-
fields.byte(expansion ? expansion.control : 0).byte(HEADER_END)
|
|
74
|
+
fields.byte(expansion ? expansion.control : 0).byte(superpet ? latches(machine.board) : HEADER_END)
|
|
55
75
|
fields.bytes(machine.ram.snapshot).bytes(bus.screen.cells)
|
|
56
76
|
fields.bytes(expansion.cells) if expansion
|
|
77
|
+
fields.bytes(machine.board.cells) if superpet
|
|
57
78
|
settings = SETTINGS.dup
|
|
58
79
|
settings[1] = SETTINGS_2001 if machine.model.basic1?
|
|
80
|
+
settings[SETTINGS_CPU] = SETTINGS_M6809 if superpet && machine.m6809_running?
|
|
59
81
|
fields.bytes(settings).section(NAME, MAJOR, MINOR)
|
|
60
82
|
end
|
|
61
83
|
|
|
84
|
+
def latches(board) = (board.bank << 4) | LATCHES | (board.socket.zero? ? BANK_RAM : 0)
|
|
85
|
+
|
|
86
|
+
# Whether a SuperPET's settings say the 6809 has the bus.
|
|
87
|
+
def m6809?(section)
|
|
88
|
+
data = section.data
|
|
89
|
+
offset = data.bytesize - SETTINGS.length + SETTINGS_CPU
|
|
90
|
+
offset >= 0 && data.getbyte(offset).to_i.anybits?(SETTINGS_M6809)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# A SuperPET's bank latch, socket bit and 64K board by their
|
|
94
|
+
# BL-SUPERPET keys, in a keyed module's writer.
|
|
95
|
+
def import_superpet(section, out)
|
|
96
|
+
fields = FieldReader.new(section).skip(4)
|
|
97
|
+
latches = fields.byte
|
|
98
|
+
fields.skip(fields_before_board(section))
|
|
99
|
+
out.int("bank_latch", latches >> 4).int("socket", latches.anybits?(BANK_RAM) ? 0 : 1)
|
|
100
|
+
out.int("system_latch", 0).bytes("ram", fields.string(EXPANSION))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The RAM and the screen RAM before a SuperPET's board.
|
|
104
|
+
def fields_before_board(section) = (ram_kb(section) * 1024) + 0x800
|
|
105
|
+
|
|
62
106
|
# The board's number in the header.
|
|
63
107
|
def board_of(section) = FieldReader.new(section).byte
|
|
64
108
|
|
|
@@ -8,18 +8,23 @@ module Badline
|
|
|
8
8
|
# reads become the PET's own keyed modules (Keyed), which it loads
|
|
9
9
|
# as it loads its own snapshot. Whatever they don't set stays as the
|
|
10
10
|
# machine has it at power-on, and the datasettes' tapes and the
|
|
11
|
-
# disk device 8 serves stay as they are. A snapshot with a true
|
|
12
|
-
#
|
|
13
|
-
#
|
|
11
|
+
# disk device 8 serves stay as they are. A snapshot with a true drive
|
|
12
|
+
# puts one on the bus, with the disks badline's drive of that model
|
|
13
|
+
# had in it (Drive2031, Drive8050), and one without takes badline's
|
|
14
|
+
# off.
|
|
14
15
|
module Restore
|
|
15
16
|
# MAINCPU and PETMEM hold the machine itself, so a version badline
|
|
16
17
|
# doesn't read fails the restore. The others are left out and
|
|
17
18
|
# reported.
|
|
18
19
|
READ = [MainCPU::NAME, PETMem::NAME, CRTC::NAME, "PIA1", "PIA2", VIA::NAME].freeze
|
|
20
|
+
# The SuperPET's too: its 6809 and its ACIA. A SuperPET's board
|
|
21
|
+
# comes from PETMEM.
|
|
22
|
+
SUPERPET_READ = [CPU6809::NAME, CBM2::ACIA::NAME].freeze
|
|
19
23
|
# The host's media, which a restore keeps.
|
|
20
24
|
KEPT = %w[BL-TAPE1 BL-TAPE2 BL-TRAPDOS].freeze
|
|
21
|
-
# The disks in a true drive's drives,
|
|
22
|
-
|
|
25
|
+
# The disks in a true drive's drives, and a hard disk's controller
|
|
26
|
+
# with its image, which a restore keeps.
|
|
27
|
+
DISKS = %w[BL-D8-DISK BL-D8-DISK1 BL-D8-SASI].freeze
|
|
23
28
|
|
|
24
29
|
module_function
|
|
25
30
|
|
|
@@ -53,7 +58,8 @@ module Badline
|
|
|
53
58
|
applied = []
|
|
54
59
|
ignored = []
|
|
55
60
|
sections = []
|
|
56
|
-
|
|
61
|
+
names = read_names(computer)
|
|
62
|
+
names.each do |name|
|
|
57
63
|
section = container[name]
|
|
58
64
|
next if section.nil?
|
|
59
65
|
|
|
@@ -72,17 +78,20 @@ module Badline
|
|
|
72
78
|
end
|
|
73
79
|
sections += drive(container, applied, ignored, kept_disks(container, computer, disks))
|
|
74
80
|
computer.load_modules(Keyed::Modules.new(modules(container, computer, applied) + sections))
|
|
75
|
-
Report.new(applied:, ignored: ignored + left_out(container, applied))
|
|
81
|
+
Report.new(applied:, ignored: ignored + left_out(container, names, applied))
|
|
76
82
|
end
|
|
77
83
|
|
|
84
|
+
def read_names(computer) = computer.superpet? ? READ + SUPERPET_READ : READ
|
|
85
|
+
|
|
78
86
|
# The disks badline's true drive has, which stay in the drive the
|
|
79
|
-
# snapshot puts on the bus when it is of the same model
|
|
87
|
+
# snapshot puts on the bus when it is of the same model, or of the
|
|
88
|
+
# same xpet type.
|
|
80
89
|
def kept_disks(container, computer, disks)
|
|
81
90
|
drive = computer.true_drive
|
|
82
91
|
return [] if drive.nil?
|
|
83
92
|
|
|
84
93
|
if Drive8050.present?(container)
|
|
85
|
-
Drive8050.model(container)
|
|
94
|
+
Drive8050.same_type?(Drive8050.model(container), drive.model_name) ? disks : []
|
|
86
95
|
else
|
|
87
96
|
drive.model_name == "2031" ? disks.first(1) : []
|
|
88
97
|
end
|
|
@@ -113,6 +122,7 @@ module Badline
|
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
applied.concat(Drive8050::NAMES)
|
|
125
|
+
model = Drive8050.hard_disk_model(disks) if Drive8050.hard_disk?(model)
|
|
116
126
|
Drive8050.import(container, Drive8050.reference(model), disks)
|
|
117
127
|
end
|
|
118
128
|
|
|
@@ -134,6 +144,8 @@ module Badline
|
|
|
134
144
|
when PETMem::NAME then PETMem.reads?(section)
|
|
135
145
|
when CRTC::NAME then CRTC.reads?(section)
|
|
136
146
|
when VIA::NAME then VIA.reads?(section)
|
|
147
|
+
when CPU6809::NAME then CPU6809.reads?(section)
|
|
148
|
+
when CBM2::ACIA::NAME then CBM2::ACIA.reads?(section)
|
|
137
149
|
else PIAs.reads?(section)
|
|
138
150
|
end
|
|
139
151
|
end
|
|
@@ -160,9 +172,26 @@ module Badline
|
|
|
160
172
|
if applied.include?(VIA::NAME)
|
|
161
173
|
sections << Keyed.section("BL-VIA") { |out| VIA.import(container[VIA::NAME], out) }
|
|
162
174
|
end
|
|
175
|
+
sections += superpet(container, applied) if computer.superpet?
|
|
163
176
|
sections + video
|
|
164
177
|
end
|
|
165
178
|
|
|
179
|
+
# The SuperPET's board, and its 6809 while it has the bus and its
|
|
180
|
+
# ACIA where the snapshot has them. The 6809 the 6502 has the bus
|
|
181
|
+
# from, and a module left out, stay as the machine has them at
|
|
182
|
+
# power-on.
|
|
183
|
+
def superpet(container, applied)
|
|
184
|
+
cycles = FieldReader.new(container[MainCPU::NAME]).qword
|
|
185
|
+
memory = container[PETMem::NAME]
|
|
186
|
+
sections = [Keyed.section("BL-SUPERPET") { |out| PETMem.import_superpet(memory, out) }]
|
|
187
|
+
if applied.include?(CPU6809::NAME) && PETMem.m6809?(memory)
|
|
188
|
+
sections << Keyed.section("BL-M6809") { |out| CPU6809.import(container[CPU6809::NAME], out, cycles) }
|
|
189
|
+
end
|
|
190
|
+
return sections unless applied.include?(CBM2::ACIA::NAME)
|
|
191
|
+
|
|
192
|
+
sections << Keyed.section("BL-ACIA") { |out| CBM2::ACIA.import(container[CBM2::ACIA::NAME], out, cycles) }
|
|
193
|
+
end
|
|
194
|
+
|
|
166
195
|
def machine(out, container, computer, drive)
|
|
167
196
|
out.text("family", "pet")
|
|
168
197
|
PETSetup.of(computer).write_fields(out)
|
|
@@ -203,8 +232,8 @@ module Badline
|
|
|
203
232
|
[Keyed.section("BL-CRTC") { |out| CRTC.copy(fields, out) }]
|
|
204
233
|
end
|
|
205
234
|
|
|
206
|
-
def left_out(container, applied)
|
|
207
|
-
container.sections.reject { |section|
|
|
235
|
+
def left_out(container, names, applied)
|
|
236
|
+
container.sections.reject { |section| names.include?(section.name) || applied.include?(section.name) }
|
|
208
237
|
.map { |section| "#{section}: badline doesn't read this module, left out" }
|
|
209
238
|
end
|
|
210
239
|
end
|