badline 0.15.0 → 0.16.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 +102 -0
- data/CLAUDE.md +15 -7
- data/README.md +142 -16
- data/doc/media.md +19 -0
- data/doc/pinned-behaviour.md +67 -0
- data/doc/snapshot-format.md +274 -31
- data/doc/snapshots.md +14 -0
- data/exe/badline-ruby +1 -1
- data/lib/badline/audio/machine_player.rb +2 -14
- data/lib/badline/c64/common.rb +1 -1
- data/lib/badline/c64/true_drives.rb +13 -1
- data/lib/badline/c64.rb +6 -0
- data/lib/badline/chips/crtc/keyed_state.rb +46 -0
- data/lib/badline/chips/crtc.rb +163 -0
- data/lib/badline/chips/pia/control_lines.rb +138 -0
- data/lib/badline/chips/pia/keyed_parts.rb +56 -0
- data/lib/badline/chips/pia.rb +138 -0
- data/lib/badline/chips/tia.rb +16 -0
- data/lib/badline/core.rb +3 -0
- data/lib/badline/drives/datasette/keyed_state.rb +8 -3
- data/lib/badline/drives/engine/core.rb +6 -14
- data/lib/badline/drives/engine/idle.rb +42 -4
- data/lib/badline/drives/engine/idle_cpu.rb +18 -0
- data/lib/badline/drives/engine/serial_connection.rb +25 -0
- data/lib/badline/drives/engine/sleep.rb +1 -0
- data/lib/badline/drives/engine.rb +1 -0
- data/lib/badline/drives/ieee_bus.rb +28 -0
- data/lib/badline/drives/model1541/byte_ready.rb +19 -3
- data/lib/badline/drives/model1541/mechanism.rb +10 -1
- data/lib/badline/drives/model1541.rb +1 -0
- data/lib/badline/drives/model1551/bus.rb +138 -0
- data/lib/badline/drives/model1551/cable.rb +107 -0
- data/lib/badline/drives/model1551/cpu.rb +14 -0
- data/lib/badline/drives/model1551/keyed_state.rb +65 -0
- data/lib/badline/drives/model1551/port.rb +74 -0
- data/lib/badline/drives/model1551/tia.rb +71 -0
- data/lib/badline/drives/model1551.rb +192 -0
- data/lib/badline/drives/model1571.rb +1 -0
- data/lib/badline/drives/model1581.rb +1 -0
- data/lib/badline/drives.rb +1 -0
- data/lib/badline/frontend/app.rb +1 -1
- data/lib/badline/frontend/boot.rb +20 -1
- data/lib/badline/frontend/controls.rb +23 -3
- data/lib/badline/frontend/gamepads.rb +10 -3
- data/lib/badline/frontend/keys.rb +88 -0
- data/lib/badline/frontend/media_slots.rb +1 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +15 -6
- data/lib/badline/frontend/menu/menu_media.rb +96 -19
- data/lib/badline/frontend/menu/menu_pages.rb +25 -7
- data/lib/badline/frontend/menu/menu_ports.rb +22 -1
- data/lib/badline/frontend/menu/pause_menu.rb +1 -0
- data/lib/badline/frontend/pet_keyboard.rb +124 -0
- data/lib/badline/frontend/player/player_controller.rb +11 -4
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/timeline.rb +16 -9
- data/lib/badline/frontend.rb +1 -0
- data/lib/badline/kernal_trap/dos/writes.rb +3 -1
- data/lib/badline/kernal_trap/dos.rb +6 -1
- data/lib/badline/kernal_trap/frames.rb +117 -0
- data/lib/badline/kernal_trap/ieee.rb +148 -0
- data/lib/badline/kernal_trap/ieee_layout.rb +113 -0
- data/lib/badline/kernal_trap/keyed_state.rb +4 -4
- data/lib/badline/kernal_trap/routine.rb +3 -53
- data/lib/badline/kernal_trap/serial.rb +4 -102
- data/lib/badline/kernal_trap/steps.rb +64 -0
- data/lib/badline/kernal_trap.rb +4 -0
- data/lib/badline/machine/keyboard_buffer.rb +8 -0
- data/lib/badline/machine/settling.rb +1 -1
- data/lib/badline/machine.rb +37 -11
- data/lib/badline/media/extensions.rb +5 -2
- data/lib/badline/media/pet_media.rb +40 -0
- data/lib/badline/media/program.rb +14 -2
- data/lib/badline/media/true_drive.rb +7 -5
- data/lib/badline/media/vic20_media.rb +2 -2
- data/lib/badline/media.rb +36 -16
- data/lib/badline/options/family.rb +17 -6
- data/lib/badline/options/help.rb +23 -5
- data/lib/badline/options/option.rb +2 -1
- data/lib/badline/options/table.rb +28 -4
- data/lib/badline/options/validation.rb +117 -14
- data/lib/badline/options.rb +65 -21
- data/lib/badline/pet/bus/expansion.rb +106 -0
- data/lib/badline/pet/bus.rb +204 -0
- data/lib/badline/pet/canvas.rb +97 -0
- data/lib/badline/pet/cassettes.rb +63 -0
- data/lib/badline/pet/characters.rb +26 -0
- data/lib/badline/pet/cpu.rb +10 -0
- data/lib/badline/pet/crtc_video.rb +142 -0
- data/lib/badline/pet/discrete_video.rb +83 -0
- data/lib/badline/pet/ieee_ports.rb +66 -0
- data/lib/badline/pet/ieee_traps.rb +75 -0
- data/lib/badline/pet/keyboard_ports.rb +74 -0
- data/lib/badline/pet/keyed_modules.rb +85 -0
- data/lib/badline/pet/keyed_parts.rb +123 -0
- data/lib/badline/pet/keyed_state.rb +42 -0
- data/lib/badline/pet/model.rb +113 -0
- data/lib/badline/pet/rom_sets.rb +65 -0
- data/lib/badline/pet/sound.rb +128 -0
- data/lib/badline/pet/via.rb +10 -0
- data/lib/badline/pet/via_ports.rb +19 -0
- data/lib/badline/pet.rb +226 -0
- data/lib/badline/plus4/acia/frames.rb +55 -0
- data/lib/badline/plus4/acia.rb +276 -16
- data/lib/badline/plus4/bus.rb +181 -45
- data/lib/badline/plus4/keyed_modules.rb +22 -5
- data/lib/badline/plus4/keyed_parts.rb +33 -4
- data/lib/badline/plus4/keyed_state.rb +10 -5
- data/lib/badline/plus4/ram_expansion.rb +145 -0
- data/lib/badline/plus4/sid_card.rb +95 -0
- data/lib/badline/plus4/sound_mix.rb +51 -0
- data/lib/badline/plus4/tcbm_drive.rb +134 -0
- data/lib/badline/plus4/user_port.rb +11 -2
- data/lib/badline/plus4.rb +104 -22
- data/lib/badline/roms/README +146 -0
- data/lib/badline/roms/dos1551.rom +0 -0
- data/lib/badline/roms/pet/basic1.rom +0 -0
- data/lib/badline/roms/pet/basic2.rom +0 -0
- data/lib/badline/roms/pet/basic4.rom +0 -0
- data/lib/badline/roms/pet/characters1.rom +0 -0
- data/lib/badline/roms/pet/characters2.rom +0 -0
- data/lib/badline/roms/pet/editor1.rom +0 -0
- data/lib/badline/roms/pet/editor2-b.rom +0 -0
- data/lib/badline/roms/pet/editor2-n.rom +0 -0
- data/lib/badline/roms/pet/editor4-40-n-50hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-40-n-60hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz-0283.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-60hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-b.rom +0 -0
- data/lib/badline/roms/pet/editor4-n.rom +0 -0
- data/lib/badline/roms/pet/kernal1.rom +0 -0
- data/lib/badline/roms/pet/kernal2.rom +0 -0
- data/lib/badline/roms/pet/kernal4.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 +21 -6
- data/lib/badline/snapshot/image.rb +14 -7
- data/lib/badline/snapshot/pet_setup.rb +71 -0
- data/lib/badline/snapshot/plus4_setup.rb +52 -10
- data/lib/badline/snapshot/storage_setup.rb +7 -1
- data/lib/badline/snapshot.rb +9 -5
- data/lib/badline/storage/d80_image.rb +48 -0
- data/lib/badline/storage/d82_image.rb +21 -0
- data/lib/badline/storage/g64_image.rb +1 -1
- data/lib/badline/storage/tap.rb +16 -3
- data/lib/badline/storage.rb +2 -0
- data/lib/badline/support/pet_checkpoint.rb +38 -0
- data/lib/badline/timing.rb +5 -4
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +7 -2
- data/lib/badline/vic20/bus/keyed_state.rb +5 -4
- data/lib/badline/vic20/bus.rb +5 -0
- data/lib/badline/vic20.rb +1 -1
- metadata +69 -1
data/lib/badline/plus4/bus.rb
CHANGED
|
@@ -10,9 +10,14 @@ module Badline
|
|
|
10
10
|
# $C000-$FCFF - the high ROM bank, or RAM; $FC00-$FCFF is always the
|
|
11
11
|
# KERNAL's while ROM is selected
|
|
12
12
|
# $FD00-$FD0F - the 6551 ACIA (Plus/4 only)
|
|
13
|
-
# $FD10-$FD1F - the user port's 6529 (Plus/4 only)
|
|
13
|
+
# $FD10-$FD1F - the user port's 6529 (Plus/4 only), or with a RAM
|
|
14
|
+
# expansion, the 6529 at $FD10 and the expansion's
|
|
15
|
+
# registers at $FD15 and $FD16
|
|
14
16
|
# $FD30-$FD3F - the keyboard's 6529 (KeyboardPort)
|
|
17
|
+
# $FD40-$FD5F - a SID card's SID, where one is fitted there
|
|
15
18
|
# $FDD0-$FDDF - the ROM bank latch
|
|
19
|
+
# $FE80-$FE9F - a SID card's SID, where one is fitted there
|
|
20
|
+
# $FEC0-$FEFF - a 1551's TCBM interface, where one is plugged in
|
|
16
21
|
# $FF00-$FF1F - the TED
|
|
17
22
|
# $FF3E/$FF3F - a write selects ROM or RAM at $8000-$FFFF
|
|
18
23
|
# $FF40-$FFFF - the high ROM bank, or RAM
|
|
@@ -27,7 +32,8 @@ module Badline
|
|
|
27
32
|
#
|
|
28
33
|
# RAM smaller than 64K repeats through the 64K, as the C16's 16K does
|
|
29
34
|
# without A14 and A15. Where nothing answers, a read finds the last byte
|
|
30
|
-
# on the bus.
|
|
35
|
+
# on the bus. A RAM expansion (RAMExpansion) picks the RAM each page
|
|
36
|
+
# reads and writes, the CPU's and the TED's.
|
|
31
37
|
#
|
|
32
38
|
# The TED fetches through the same banks: the video matrix and the
|
|
33
39
|
# attributes from ROM or RAM at $8000-$FFFF as the CPU sees it, with no
|
|
@@ -40,17 +46,18 @@ module Badline
|
|
|
40
46
|
|
|
41
47
|
RAM_POWER_ON = 0x00
|
|
42
48
|
|
|
43
|
-
# RAM through a size it repeats at
|
|
49
|
+
# RAM through a size it repeats at, from +offset+ on in +ram+.
|
|
44
50
|
class MirroredRAM
|
|
45
|
-
def initialize(ram, mask)
|
|
51
|
+
def initialize(ram, mask, offset = 0)
|
|
46
52
|
@ram = ram
|
|
47
53
|
@mask = mask
|
|
54
|
+
@offset = offset
|
|
48
55
|
end
|
|
49
56
|
|
|
50
|
-
def peek(addr) = @ram.peek(addr & @mask)
|
|
57
|
+
def peek(addr) = @ram.peek((addr & @mask) + @offset)
|
|
51
58
|
|
|
52
59
|
def poke(addr, value)
|
|
53
|
-
@ram.poke(addr & @mask, value)
|
|
60
|
+
@ram.poke((addr & @mask) + @offset, value)
|
|
54
61
|
end
|
|
55
62
|
end
|
|
56
63
|
|
|
@@ -80,14 +87,20 @@ module Badline
|
|
|
80
87
|
end
|
|
81
88
|
end
|
|
82
89
|
|
|
83
|
-
# Page $FD: the ACIA, the user port, the keyboard port
|
|
84
|
-
# bank latch, by the address's bits 4-7. +acia
|
|
85
|
-
# nil where the machine has none, and
|
|
86
|
-
# user port's place on a machine
|
|
90
|
+
# Page $FD: the ACIA, the user port, the keyboard port, a SID card and
|
|
91
|
+
# the ROM bank latch, by the address's bits 4-7. +acia+, +user_port+
|
|
92
|
+
# and +sid_card+ are nil where the machine has none, and
|
|
93
|
+
# +cassette_sense+ answers in the user port's place on a machine
|
|
94
|
+
# without one (CassetteSense). The SID card decodes its 32 bytes
|
|
95
|
+
# alone, with no mirrors, as xplus4's does. A RAM expansion
|
|
96
|
+
# (RAMExpansion) leaves the user port only $FD10 of $FD10-$FD1F, and
|
|
97
|
+
# has its register at $FD16.
|
|
87
98
|
class IOPage
|
|
88
|
-
def initialize(keyboard_port:, acia: nil, user_port: nil, cassette_sense: nil)
|
|
99
|
+
def initialize(keyboard_port:, acia: nil, user_port: nil, cassette_sense: nil, sid_card: nil)
|
|
89
100
|
@bus = nil
|
|
101
|
+
@ram_expansion = nil
|
|
90
102
|
@acia = acia
|
|
103
|
+
@sid_card = sid_card
|
|
91
104
|
@user_port = user_port
|
|
92
105
|
@cassette_sense = cassette_sense
|
|
93
106
|
@keyboard_port = keyboard_port
|
|
@@ -97,13 +110,15 @@ module Badline
|
|
|
97
110
|
# a write to $FDD0-$FDDF sets.
|
|
98
111
|
def connect(bus)
|
|
99
112
|
@bus = bus
|
|
113
|
+
@ram_expansion = bus.ram_expansion
|
|
100
114
|
end
|
|
101
115
|
|
|
102
116
|
def peek(addr)
|
|
103
117
|
case addr & 0xf0
|
|
104
118
|
when 0x00 then @acia ? @acia.peek(addr) : @bus.data
|
|
105
|
-
when 0x10 then user_port_peek(addr)
|
|
119
|
+
when 0x10 then @ram_expansion ? expansion_peek(addr) : user_port_peek(addr)
|
|
106
120
|
when 0x30 then @keyboard_port.peek(addr)
|
|
121
|
+
when 0x40, 0x50 then @sid_card ? @sid_card.peek(addr) : @bus.data
|
|
107
122
|
else @bus.data
|
|
108
123
|
end
|
|
109
124
|
end
|
|
@@ -111,14 +126,31 @@ module Badline
|
|
|
111
126
|
def poke(addr, value)
|
|
112
127
|
case addr & 0xf0
|
|
113
128
|
when 0x00 then @acia&.poke(addr, value)
|
|
114
|
-
when 0x10 then @user_port&.poke(addr, value)
|
|
129
|
+
when 0x10 then @ram_expansion ? expansion_poke(addr, value) : @user_port&.poke(addr, value)
|
|
115
130
|
when 0x30 then @keyboard_port.poke(addr, value)
|
|
131
|
+
when 0x40, 0x50 then @sid_card&.poke(addr, value)
|
|
116
132
|
when 0xd0 then @bus.select_banks(addr)
|
|
117
133
|
end
|
|
118
134
|
end
|
|
119
135
|
|
|
120
136
|
private
|
|
121
137
|
|
|
138
|
+
def expansion_peek(addr)
|
|
139
|
+
case addr & 0x0f
|
|
140
|
+
when 0x00 then user_port_peek(addr)
|
|
141
|
+
when RAMExpansion::CONTROL then @ram_expansion.control
|
|
142
|
+
else @bus.data
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def expansion_poke(addr, value)
|
|
147
|
+
if addr.nobits?(0x0f)
|
|
148
|
+
@user_port&.poke(addr, value)
|
|
149
|
+
else
|
|
150
|
+
@bus.ram_expansion_written(addr, value)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
122
154
|
def user_port_peek(addr)
|
|
123
155
|
return @user_port.peek(addr) if @user_port
|
|
124
156
|
return @cassette_sense.read(@bus.data) if @cassette_sense
|
|
@@ -148,26 +180,50 @@ module Badline
|
|
|
148
180
|
end
|
|
149
181
|
end
|
|
150
182
|
|
|
151
|
-
# Page $FE
|
|
152
|
-
# that talks to drive 9 at $FEC0-$FEDF and drive 8's at $FEE0-$FEFF
|
|
153
|
-
#
|
|
183
|
+
# Page $FE, where a 1551's TCBM interface puts its 6523 TIA: the one
|
|
184
|
+
# that talks to drive 9 at $FEC0-$FEDF and drive 8's at $FEE0-$FEFF,
|
|
185
|
+
# and where a SID card can put its SID, at $FE80-$FE9F, decoding its
|
|
186
|
+
# 32 bytes alone as xplus4's does. The rest of the page, and a place
|
|
187
|
+
# with nothing in it, is open.
|
|
154
188
|
class TCBMPage
|
|
155
|
-
|
|
189
|
+
# The TIA at $FEE0-$FEFF and the one at $FEC0-$FEDF, or nil.
|
|
190
|
+
attr_accessor :drive8, :drive9
|
|
191
|
+
|
|
192
|
+
# The SIDCard at $FE80-$FE9F, or nil.
|
|
193
|
+
attr_accessor :sid_card
|
|
194
|
+
|
|
195
|
+
def initialize(bus)
|
|
156
196
|
@bus = bus
|
|
157
|
-
@drive8 =
|
|
158
|
-
@drive9 =
|
|
197
|
+
@drive8 = nil
|
|
198
|
+
@drive9 = nil
|
|
199
|
+
@sid_card = nil
|
|
159
200
|
end
|
|
160
201
|
|
|
161
202
|
def peek(addr)
|
|
162
|
-
return
|
|
203
|
+
return low_peek(addr) if addr < 0xfec0
|
|
163
204
|
|
|
164
|
-
addr < 0xfee0 ? @drive9
|
|
205
|
+
tia = addr < 0xfee0 ? @drive9 : @drive8
|
|
206
|
+
tia ? tia.peek(addr) : @bus.data
|
|
165
207
|
end
|
|
166
208
|
|
|
167
209
|
def poke(addr, value)
|
|
168
|
-
|
|
210
|
+
if addr < 0xfec0
|
|
211
|
+
sid_card = @sid_card
|
|
212
|
+
sid_card.poke(addr, value) if sid_card && sid_card?(addr)
|
|
213
|
+
return
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
tia = addr < 0xfee0 ? @drive9 : @drive8
|
|
217
|
+
tia&.poke(addr, value)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
private
|
|
221
|
+
|
|
222
|
+
def sid_card?(addr) = (addr & 0xe0) == 0x80
|
|
169
223
|
|
|
170
|
-
|
|
224
|
+
def low_peek(addr)
|
|
225
|
+
sid_card = @sid_card
|
|
226
|
+
sid_card && sid_card?(addr) ? sid_card.peek(addr) : @bus.data
|
|
171
227
|
end
|
|
172
228
|
end
|
|
173
229
|
|
|
@@ -186,7 +242,7 @@ module Badline
|
|
|
186
242
|
end
|
|
187
243
|
|
|
188
244
|
def poke(addr, value)
|
|
189
|
-
return @bus.
|
|
245
|
+
return @bus.top_ram.poke(addr, value) if addr >= 0xff40
|
|
190
246
|
|
|
191
247
|
case addr
|
|
192
248
|
when 0xff3e then @bus.rom_selected = true
|
|
@@ -201,17 +257,29 @@ module Badline
|
|
|
201
257
|
# What the top page reads above $FF3F: the high ROM bank or RAM.
|
|
202
258
|
attr_reader :top_memory
|
|
203
259
|
|
|
260
|
+
# The RAM the CPU sees at $FF00-$FFFF, which a write above $FF3F
|
|
261
|
+
# reaches.
|
|
262
|
+
attr_reader :top_ram
|
|
263
|
+
|
|
264
|
+
# The RAMExpansion fitted, or nil.
|
|
265
|
+
attr_reader :ram_expansion
|
|
266
|
+
|
|
204
267
|
# The CPU sees ROM at $8000-$FFFF.
|
|
205
268
|
attr_reader :rom_selected
|
|
206
269
|
|
|
207
270
|
# +io+ is the IOPage, +model+ a Model profile, whose KERNAL it maps
|
|
208
271
|
# and whose function ROMs it maps where the model has them, and
|
|
209
|
-
# +ram_kb+ the RAM fitted.
|
|
210
|
-
|
|
272
|
+
# +ram_kb+ the RAM fitted. +ram_expansion+ names the kind of
|
|
273
|
+
# RAMExpansion to fit over 64K of it.
|
|
274
|
+
def initialize(ted:, port:, io:, model: Model::PLUS4, ram_kb: model.ram_kb, ram_expansion: nil)
|
|
211
275
|
addressable_at(0, length: 2**16)
|
|
212
276
|
size = ram_kb * 1024
|
|
213
277
|
@ram = Memory.new(Array.new(size, RAM_POWER_ON), length: size, start: 0)
|
|
214
278
|
@ram_view = MirroredRAM.new(@ram, size - 1)
|
|
279
|
+
@ram_expansion = ram_expansion ? RAMExpansion.new(ram_expansion, @ram, @ram_view) : nil
|
|
280
|
+
@cpu_ram = Array.new(256, @ram_view)
|
|
281
|
+
@ted_ram = Array.new(256, @ram_view)
|
|
282
|
+
@top_ram = @ram_view
|
|
215
283
|
@ted = ted
|
|
216
284
|
@open_bus = OpenBus.new(self)
|
|
217
285
|
@low_roms = [ROM.load("plus4/basic.rom", 0x8000), nil, nil, nil]
|
|
@@ -220,11 +288,12 @@ module Badline
|
|
|
220
288
|
@low_roms[1] = ROM.load("plus4/function-low.rom", 0x8000)
|
|
221
289
|
@high_roms[1] = ROM.load("plus4/function-high.rom", 0xc000)
|
|
222
290
|
end
|
|
291
|
+
@port = port
|
|
223
292
|
@zero_page = ZeroPage.new(port, @ram_view)
|
|
224
293
|
@io_page = io
|
|
225
294
|
io.connect(self)
|
|
226
295
|
@top_page = TopPage.new(self, ted)
|
|
227
|
-
@tcbm_page =
|
|
296
|
+
@tcbm_page = TCBMPage.new(self)
|
|
228
297
|
@debug_register = nil
|
|
229
298
|
@data = 0
|
|
230
299
|
@read_pages = Array.new(256, @open_bus)
|
|
@@ -234,19 +303,29 @@ module Badline
|
|
|
234
303
|
@low_bank = @high_bank = 0
|
|
235
304
|
@rom_selected = true
|
|
236
305
|
@top_memory = @ram_view
|
|
237
|
-
|
|
238
|
-
map_reads
|
|
306
|
+
map_ram
|
|
239
307
|
end
|
|
240
308
|
|
|
241
309
|
def power_on!
|
|
242
310
|
@ram.clear!(Array.new(@ram.length, RAM_POWER_ON))
|
|
311
|
+
@ram_expansion&.power_on!
|
|
243
312
|
reset!
|
|
244
313
|
end
|
|
245
314
|
|
|
246
|
-
# The RESET line selects ROM and the first bank of each
|
|
315
|
+
# The RESET line selects ROM and the first bank of each, and sets a
|
|
316
|
+
# RAM expansion's register.
|
|
247
317
|
def reset!
|
|
248
318
|
@low_bank = @high_bank = 0
|
|
249
|
-
|
|
319
|
+
@ram_expansion&.reset!
|
|
320
|
+
@rom_selected = true
|
|
321
|
+
@ted.rom_selected = true
|
|
322
|
+
map_ram
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# A write to a RAM expansion's registers in $FD11-$FD1F.
|
|
326
|
+
def ram_expansion_written(addr, value)
|
|
327
|
+
@ram_expansion.poke(addr, value)
|
|
328
|
+
map_ram
|
|
250
329
|
end
|
|
251
330
|
|
|
252
331
|
def rom_selected=(selected)
|
|
@@ -274,9 +353,23 @@ module Badline
|
|
|
274
353
|
|
|
275
354
|
# Plugs in a 1551's TCBM interface, the TIAs at $FEC0-$FEFF.
|
|
276
355
|
def plug_tcbm(drive8:, drive9:)
|
|
277
|
-
@tcbm_page =
|
|
278
|
-
|
|
279
|
-
|
|
356
|
+
@tcbm_page.drive8 = drive8
|
|
357
|
+
@tcbm_page.drive9 = drive9
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# Puts a SIDCard at $FE80-$FE9F.
|
|
361
|
+
def plug_sid_card(sid_card)
|
|
362
|
+
@tcbm_page.sid_card = sid_card
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Puts +tia+ where the TCBM interface for +device+, 8 or 9, answers,
|
|
366
|
+
# or takes the one there out with nil.
|
|
367
|
+
def plug_tia(device, tia)
|
|
368
|
+
if device == 9
|
|
369
|
+
@tcbm_page.drive9 = tia
|
|
370
|
+
else
|
|
371
|
+
@tcbm_page.drive8 = tia
|
|
372
|
+
end
|
|
280
373
|
end
|
|
281
374
|
|
|
282
375
|
# Puts a cartridge's ROM chips in their slots, as a PLUS4 CARTRIDGE
|
|
@@ -334,12 +427,35 @@ module Badline
|
|
|
334
427
|
|
|
335
428
|
# A TED fetch of character or bitmap data, from ROM when +rom+.
|
|
336
429
|
def ted_pattern_fetch(addr, rom)
|
|
337
|
-
@data = rom ? @ted_rom_pages[addr >> 8].peek(addr) : @
|
|
430
|
+
@data = rom ? @ted_rom_pages[addr >> 8].peek(addr) : @ted_ram[addr >> 8].peek(addr)
|
|
338
431
|
end
|
|
339
432
|
|
|
340
433
|
private
|
|
341
434
|
|
|
435
|
+
# Lays out the RAM a RAM expansion banks in, then every page.
|
|
436
|
+
def map_ram
|
|
437
|
+
expansion = @ram_expansion
|
|
438
|
+
if expansion
|
|
439
|
+
expansion.map(@cpu_ram, @ted_ram)
|
|
440
|
+
@zero_page = ZeroPage.new(@port, @cpu_ram[0])
|
|
441
|
+
@top_ram = @cpu_ram[0xff]
|
|
442
|
+
end
|
|
443
|
+
map_writes
|
|
444
|
+
map_reads
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Copies the RAM the CPU sees into +pages+, up to the I/O pages.
|
|
448
|
+
def copy_ram(pages)
|
|
449
|
+
cpu_ram = @cpu_ram
|
|
450
|
+
page = 1
|
|
451
|
+
while page < 0xfd
|
|
452
|
+
pages[page] = cpu_ram[page]
|
|
453
|
+
page += 1
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
342
457
|
def map_writes
|
|
458
|
+
copy_ram(@write_pages) if @ram_expansion
|
|
343
459
|
@write_pages[0x00] = @zero_page
|
|
344
460
|
@write_pages[0xfd] = @debug_register || @io_page
|
|
345
461
|
@write_pages[0xfe] = @tcbm_page
|
|
@@ -347,10 +463,14 @@ module Badline
|
|
|
347
463
|
end
|
|
348
464
|
|
|
349
465
|
def map_reads
|
|
350
|
-
@
|
|
466
|
+
if @ram_expansion
|
|
467
|
+
copy_ram(@read_pages)
|
|
468
|
+
else
|
|
469
|
+
@read_pages.fill(@ram_view, 0x00, 0xfd)
|
|
470
|
+
end
|
|
351
471
|
@read_pages[0x00] = @zero_page
|
|
352
|
-
map_roms(@read_pages) if @rom_selected
|
|
353
|
-
@top_memory = @rom_selected ? @high_roms[@high_bank] || @open_bus : @
|
|
472
|
+
map_roms(@read_pages, @high_bank) if @rom_selected
|
|
473
|
+
@top_memory = @rom_selected ? @high_roms[@high_bank] || @open_bus : @top_ram
|
|
354
474
|
@read_pages[0xfd] = @io_page
|
|
355
475
|
@read_pages[0xfe] = @tcbm_page
|
|
356
476
|
@read_pages[0xff] = @top_page
|
|
@@ -358,16 +478,32 @@ module Badline
|
|
|
358
478
|
end
|
|
359
479
|
|
|
360
480
|
def map_ted_reads
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
481
|
+
expansion = @ram_expansion
|
|
482
|
+
if expansion
|
|
483
|
+
copy_ted_ram
|
|
484
|
+
else
|
|
485
|
+
@ted_pages.fill(@ram_view, 0x80, 0x80)
|
|
486
|
+
end
|
|
487
|
+
high_bank = expansion&.kernal_for_ted? ? 0 : @high_bank
|
|
488
|
+
high_rom = @high_roms[high_bank] || @open_bus
|
|
489
|
+
map_roms(@ted_pages, high_bank) if @rom_selected
|
|
490
|
+
map_roms(@ted_rom_pages, high_bank)
|
|
491
|
+
@ted_pages.fill(high_rom, 0xfd, 3) if @rom_selected
|
|
492
|
+
@ted_rom_pages.fill(high_rom, 0xfd, 3)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def copy_ted_ram
|
|
496
|
+
ted_ram = @ted_ram
|
|
497
|
+
page = 0
|
|
498
|
+
while page < 0x100
|
|
499
|
+
@ted_pages[page] = ted_ram[page]
|
|
500
|
+
page += 1
|
|
501
|
+
end
|
|
366
502
|
end
|
|
367
503
|
|
|
368
|
-
def map_roms(pages)
|
|
504
|
+
def map_roms(pages, high_bank)
|
|
369
505
|
pages.fill(@low_roms[@low_bank] || @open_bus, 0x80, 0x40)
|
|
370
|
-
pages.fill(@high_roms[
|
|
506
|
+
pages.fill(@high_roms[high_bank] || @open_bus, 0xc0, 0x3c)
|
|
371
507
|
pages[0xfc] = @high_roms[0]
|
|
372
508
|
end
|
|
373
509
|
end
|
|
@@ -4,11 +4,12 @@ module Badline
|
|
|
4
4
|
class Plus4
|
|
5
5
|
# The whole machine as keyed snapshot modules (Snapshot::Keyed): BL-MACHINE
|
|
6
6
|
# with the setup, the cartridges' ROMs by slot, the CPU with its port,
|
|
7
|
-
# the TED, its sound, the
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
7
|
+
# the TED, its sound, the SID card's SID where one is fitted, the
|
|
8
|
+
# keyboard's 6529, the user port's 6529 and the ACIA where the model
|
|
9
|
+
# has them, the TCBM interface's TIAs where one is plugged in, the RAM
|
|
10
|
+
# with the ROM bank latch and the ROM/RAM switch under bus.*, a RAM
|
|
11
|
+
# expansion's registers and banks, the datasette, device 8 through the
|
|
12
|
+
# traps and the true drives.
|
|
12
13
|
#
|
|
13
14
|
# A load maps the banks before the chips load, and works out what each
|
|
14
15
|
# unit derives from its fields once every unit is loaded.
|
|
@@ -22,6 +23,8 @@ module Badline
|
|
|
22
23
|
bus.ram.save_fields(out, "ram")
|
|
23
24
|
bus.save_fields(out, "bus.")
|
|
24
25
|
end
|
|
26
|
+
expansion = bus.ram_expansion
|
|
27
|
+
sections << Snapshot::Keyed.section("BL-RAMEXP") { |out| expansion.save_fields(out) } if expansion
|
|
25
28
|
sections << Snapshot::Keyed.section("BL-TAPE") { |out| @datasette.save_fields(out) }
|
|
26
29
|
sections << Snapshot::Keyed.section("BL-TRAPDOS") { |out| save_trap_dos_fields(out) } if mounted?
|
|
27
30
|
save_drive_modules(sections)
|
|
@@ -38,6 +41,7 @@ module Badline
|
|
|
38
41
|
bus.ram.load_fields(memory, "ram")
|
|
39
42
|
bus.load_fields(memory, "bus.")
|
|
40
43
|
bus.load_slot_fields(modules.optional("BL-CART") || Snapshot::Keyed::Reader.new("BL-CART"))
|
|
44
|
+
load_ram_expansion(modules)
|
|
41
45
|
bus.restored!
|
|
42
46
|
load_chip_modules(modules)
|
|
43
47
|
@datasette.load_fields(modules.required("BL-TAPE"))
|
|
@@ -50,6 +54,13 @@ module Badline
|
|
|
50
54
|
|
|
51
55
|
private
|
|
52
56
|
|
|
57
|
+
def load_ram_expansion(modules)
|
|
58
|
+
expansion = @bus.ram_expansion
|
|
59
|
+
return if expansion.nil?
|
|
60
|
+
|
|
61
|
+
expansion.load_fields(modules.required("BL-RAMEXP"))
|
|
62
|
+
end
|
|
63
|
+
|
|
53
64
|
def save_chip_modules(sections)
|
|
54
65
|
sections << Snapshot::Keyed.section("BL-CPU") do |out|
|
|
55
66
|
@cpu.save_fields(out)
|
|
@@ -57,6 +68,8 @@ module Badline
|
|
|
57
68
|
end
|
|
58
69
|
sections << Snapshot::Keyed.section("BL-TED") { |out| @ted.save_fields(out) }
|
|
59
70
|
sections << Snapshot::Keyed.section("BL-TEDSOUND") { |out| @sound.save_fields(out) }
|
|
71
|
+
sid_card = @sid_card
|
|
72
|
+
sections << Snapshot::Keyed.section("BL-SID") { |out| sid_card.save_fields(out) } if sid_card
|
|
60
73
|
sections << Snapshot::Keyed.section("BL-KEYPORT") { |out| @keyboard_port.save_fields(out) }
|
|
61
74
|
user_port = @user_port
|
|
62
75
|
sections << Snapshot::Keyed.section("BL-USERPORT") { |out| user_port.save_fields(out) } if user_port
|
|
@@ -72,6 +85,7 @@ module Badline
|
|
|
72
85
|
@port.load_fields(cpu, "port.")
|
|
73
86
|
@ted.load_fields(modules.required("BL-TED"))
|
|
74
87
|
@sound.load_fields(modules.required("BL-TEDSOUND"))
|
|
88
|
+
@sid_card&.load_fields(modules.required("BL-SID"))
|
|
75
89
|
@keyboard_port.load_fields(modules.required("BL-KEYPORT"))
|
|
76
90
|
@user_port&.load_fields(modules.required("BL-USERPORT"))
|
|
77
91
|
@acia&.load_fields(modules.required("BL-ACIA"))
|
|
@@ -83,7 +97,10 @@ module Badline
|
|
|
83
97
|
@cpu.restored!
|
|
84
98
|
@ted.restored!
|
|
85
99
|
@sound.restored!(@ted)
|
|
100
|
+
@sid_card&.restored!
|
|
86
101
|
@port.restored!
|
|
102
|
+
@acia&.restored!
|
|
103
|
+
schedule
|
|
87
104
|
end
|
|
88
105
|
|
|
89
106
|
def check_snapshot_setup(setup)
|
|
@@ -53,14 +53,43 @@ module Badline
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
class ACIA
|
|
56
|
-
# The
|
|
57
|
-
# (BL-ACIA).
|
|
56
|
+
# The registers, the status, the shift register and when the
|
|
57
|
+
# transmitter's character ends, in a keyed snapshot module (BL-ACIA).
|
|
58
|
+
# What the device on the user port has still to send stays with the
|
|
59
|
+
# host, and the receiver starts on it again once loaded (#restored!).
|
|
60
|
+
# A snapshot from before the transmitter's timing was saved starts
|
|
61
|
+
# its character clock on load.
|
|
58
62
|
module KeyedState
|
|
59
|
-
def save_fields(out)
|
|
63
|
+
def save_fields(out)
|
|
64
|
+
out.int("command", @command).int("control", @control).int("errors", @errors)
|
|
65
|
+
out.bool("receiver_full", @receiver_full).bool("transmitter_empty", @transmitter_empty).bool("irq", @irq)
|
|
66
|
+
out.int("receive_data", @receive_data).int("transmit_data", @transmit_data).int("shifting", @shifting)
|
|
67
|
+
out.int("transmit_at", @transmit_at).int("transmit_remainder", @transmit_remainder)
|
|
68
|
+
end
|
|
60
69
|
|
|
61
70
|
def load_fields(input)
|
|
62
71
|
@command = input.byte("command", @command)
|
|
63
72
|
@control = input.byte("control", @control)
|
|
73
|
+
@errors = input.masked("errors", 0, PARITY_ERROR | FRAMING_ERROR | OVERRUN)
|
|
74
|
+
@receiver_full = input.bool("receiver_full", false)
|
|
75
|
+
@transmitter_empty = input.bool("transmitter_empty", true)
|
|
76
|
+
@irq = input.bool("irq", false)
|
|
77
|
+
@receive_data = input.byte("receive_data", 0)
|
|
78
|
+
@transmit_data = input.byte("transmit_data", 0)
|
|
79
|
+
@shifting = input.int("shifting", IDLE)
|
|
80
|
+
@transmit_remainder = input.int("transmit_remainder", 0)
|
|
81
|
+
transmit_at = input.optional_int("transmit_at")
|
|
82
|
+
@transmit_at = transmit_at || NEVER
|
|
83
|
+
@clock_lost = transmit_at.nil?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def restored!
|
|
87
|
+
@receive_at = NEVER
|
|
88
|
+
@lines = modem_lines
|
|
89
|
+
start_transmitting if @clock_lost && transmitter_on?
|
|
90
|
+
@clock_lost = false
|
|
91
|
+
start_receiving
|
|
92
|
+
changed
|
|
64
93
|
end
|
|
65
94
|
end
|
|
66
95
|
|
|
@@ -126,7 +155,7 @@ module Badline
|
|
|
126
155
|
|
|
127
156
|
def restored!
|
|
128
157
|
@ted.rom_selected = @rom_selected
|
|
129
|
-
|
|
158
|
+
map_ram
|
|
130
159
|
end
|
|
131
160
|
|
|
132
161
|
private
|
|
@@ -4,9 +4,9 @@ module Badline
|
|
|
4
4
|
class Plus4
|
|
5
5
|
# The machine's own fields in a keyed snapshot module (Snapshot::Keyed,
|
|
6
6
|
# BL-MACHINE): the family, its setup by name (Snapshot::Plus4Setup), the
|
|
7
|
-
# cycle count, and the host's typing still to feed
|
|
8
|
-
# still to run
|
|
9
|
-
# written for reports and never read.
|
|
7
|
+
# cycle count, and the host's typing still to feed, on_init handlers
|
|
8
|
+
# still to run and the model plug_true_drive plugs in under host.*. The
|
|
9
|
+
# badline version that wrote it is written for reports and never read.
|
|
10
10
|
module KeyedState
|
|
11
11
|
# How many more on_init handlers the machine a snapshot was taken of had
|
|
12
12
|
# yet to run than this one has. Nil until the machine is restored.
|
|
@@ -16,8 +16,11 @@ module Badline
|
|
|
16
16
|
# instructions or jammed.
|
|
17
17
|
def settled_for_snapshot? = @cpu.boundary? || @cpu.jammed?
|
|
18
18
|
|
|
19
|
-
# The sound's voices, run up to this cycle.
|
|
20
|
-
def settle_chips_for_snapshot
|
|
19
|
+
# The sound's voices and the SID card's, run up to this cycle.
|
|
20
|
+
def settle_chips_for_snapshot
|
|
21
|
+
@sound.catch_up!
|
|
22
|
+
@sid_card&.catch_up!
|
|
23
|
+
end
|
|
21
24
|
|
|
22
25
|
def save_machine_fields(out)
|
|
23
26
|
out.text("family", "plus4").text("written_by", VERSION)
|
|
@@ -26,6 +29,7 @@ module Badline
|
|
|
26
29
|
pending = @pending_keys
|
|
27
30
|
out.ints("host.pending_keys", pending) if pending
|
|
28
31
|
out.int("host.init_pending", booting? ? @init_handlers.length : 0)
|
|
32
|
+
out.text("host.true_drive_model", @true_drive_model)
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def load_machine_fields(input)
|
|
@@ -33,6 +37,7 @@ module Badline
|
|
|
33
37
|
pending = input.ints("host.pending_keys", [])
|
|
34
38
|
@pending_keys = pending.empty? ? nil : pending.dup
|
|
35
39
|
@init_handlers_lost = [input.int("host.init_pending", 0) - @init_handlers.length, 0].max
|
|
40
|
+
@true_drive_model = input.choice("host.true_drive_model", TCBMDrive::TRUE_DRIVE_MODELS, @true_drive_model)
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
|