badline 0.11.1 → 0.12.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 +18 -0
- data/CLAUDE.md +8 -7
- data/CONTRIBUTING.md +7 -6
- data/README.md +12 -5
- data/doc/media.md +28 -1
- data/doc/pinned-behaviour.md +31 -1
- data/exe/badline-ruby +1 -1
- data/lib/badline/c128/bus.rb +4 -17
- data/lib/badline/c128/drives.rb +35 -22
- data/lib/badline/c128/fast_serial.rb +1 -0
- data/lib/badline/c128/io_pages.rb +35 -0
- data/lib/badline/c128/mmu.rb +9 -7
- data/lib/badline/c128/modes.rb +17 -4
- data/lib/badline/c128/saved_state.rb +22 -0
- data/lib/badline/c128/z80.rb +49 -0
- data/lib/badline/c128/z80_pages.rb +118 -0
- data/lib/badline/c128/z80_turns.rb +106 -0
- data/lib/badline/c128.rb +16 -19
- data/lib/badline/cartridge/action_replay.rb +2 -2
- data/lib/badline/cartridge/comal80.rb +1 -7
- data/lib/badline/cartridge/dinamic.rb +0 -8
- data/lib/badline/cartridge/final_cartridge3.rb +6 -6
- data/lib/badline/cartridge/fun_play.rb +0 -8
- data/lib/badline/cartridge/game_system.rb +0 -8
- data/lib/badline/cartridge/kcs_power.rb +10 -7
- data/lib/badline/cartridge/mach5.rb +2 -4
- data/lib/badline/cartridge/magic_desk.rb +0 -8
- data/lib/badline/cartridge/ocean.rb +0 -8
- data/lib/badline/cartridge/pagefox.rb +2 -2
- data/lib/badline/cartridge/retro_replay.rb +7 -6
- data/lib/badline/cartridge/rex_utility.rb +2 -6
- data/lib/badline/cartridge/rgcd.rb +2 -2
- data/lib/badline/cartridge/simons_basic.rb +0 -5
- data/lib/badline/cartridge/super_games.rb +4 -2
- data/lib/badline/cartridge/westermann.rb +2 -6
- data/lib/badline/cartridge.rb +37 -9
- data/lib/badline/cia/fast_forward.rb +21 -3
- data/lib/badline/cia.rb +8 -5
- data/lib/badline/computer/attachments.rb +4 -56
- data/lib/badline/computer/true_drives.rb +80 -0
- data/lib/badline/computer.rb +4 -0
- data/lib/badline/core.rb +2 -0
- data/lib/badline/drive/core.rb +8 -33
- data/lib/badline/drive/fast_serial.rb +73 -0
- data/lib/badline/drive/idle.rb +1 -11
- data/lib/badline/drive/orbit.rb +2 -5
- data/lib/badline/drive/sleep.rb +0 -2
- data/lib/badline/drive/vias.rb +84 -0
- data/lib/badline/drive.rb +2 -0
- data/lib/badline/drive1541.rb +1 -0
- data/lib/badline/drive1571/wd1770.rb +94 -8
- data/lib/badline/drive1571.rb +8 -58
- data/lib/badline/drive1581/bus.rb +137 -0
- data/lib/badline/drive1581/cpu.rb +11 -0
- data/lib/badline/drive1581/disk.rb +135 -0
- data/lib/badline/drive1581/disk_state.rb +87 -0
- data/lib/badline/drive1581/mechanism.rb +117 -0
- data/lib/badline/drive1581/serial_port.rb +52 -0
- data/lib/badline/drive1581/slot.rb +78 -0
- data/lib/badline/drive1581/track.rb +100 -0
- data/lib/badline/drive1581/track_bytes.rb +75 -0
- data/lib/badline/drive1581/wd1772/registers.rb +89 -0
- data/lib/badline/drive1581/wd1772/sector_writes.rb +73 -0
- data/lib/badline/drive1581/wd1772/sectors.rb +150 -0
- data/lib/badline/drive1581/wd1772/state.rb +66 -0
- data/lib/badline/drive1581/wd1772/stepping.rb +109 -0
- data/lib/badline/drive1581/wd1772/tracks.rb +125 -0
- data/lib/badline/drive1581/wd1772/turn.rb +73 -0
- data/lib/badline/drive1581/wd1772.rb +216 -0
- data/lib/badline/drive1581.rb +233 -0
- data/lib/badline/frontend/app.rb +1 -3
- data/lib/badline/frontend/audio_device.rb +67 -0
- data/lib/badline/frontend/audio_sink.rb +16 -50
- data/lib/badline/frontend/boot.rb +18 -10
- data/lib/badline/frontend/buttons.rb +7 -7
- data/lib/badline/frontend/keys.rb +33 -8
- data/lib/badline/frontend/media_slots.rb +42 -0
- data/lib/badline/frontend/{confirmation.rb → menu/confirmation.rb} +4 -4
- data/lib/badline/frontend/{file_browser.rb → menu/file_browser.rb} +18 -34
- data/lib/badline/frontend/{menu_dialogs.rb → menu/menu_dialogs.rb} +3 -7
- data/lib/badline/frontend/{menu_media.rb → menu/menu_media.rb} +26 -51
- data/lib/badline/frontend/{menu_pages.rb → menu/menu_pages.rb} +5 -8
- data/lib/badline/frontend/{name_field.rb → menu/name_field.rb} +11 -24
- data/lib/badline/frontend/{pause_menu.rb → menu/pause_menu.rb} +13 -28
- data/lib/badline/frontend/menu/theme.rb +18 -0
- data/lib/badline/frontend/{filter_view.rb → player/filter_view.rb} +4 -4
- data/lib/badline/frontend/{info_view.rb → player/info_view.rb} +3 -3
- data/lib/badline/frontend/{player_window.rb → player/player_controller.rb} +17 -25
- data/lib/badline/frontend/{player_footer.rb → player/player_footer.rb} +8 -8
- data/lib/badline/frontend/{player_header.rb → player/player_header.rb} +5 -5
- data/lib/badline/frontend/{player_screen.rb → player/player_screen.rb} +4 -5
- data/lib/badline/frontend/{scope.rb → player/scope.rb} +1 -1
- data/lib/badline/frontend/{sid_view.rb → player/sid_view.rb} +16 -20
- data/lib/badline/frontend/player/theme.rb +18 -0
- data/lib/badline/frontend/{visualizer_view.rb → player/visualizer_view.rb} +8 -8
- data/lib/badline/frontend/sound.rb +19 -38
- data/lib/badline/frontend/timeline.rb +3 -14
- data/lib/badline/frontend.rb +24 -20
- data/lib/badline/iec_bus.rb +14 -2
- data/lib/badline/machine.rb +5 -4
- data/lib/badline/media/boot_disk.rb +50 -0
- data/lib/badline/media/not_disk.rb +8 -0
- data/lib/badline/media/true_drive.rb +35 -15
- data/lib/badline/media.rb +9 -3
- data/lib/badline/options/event.rb +2 -2
- data/lib/badline/options/family.rb +3 -9
- data/lib/badline/options/help.rb +2 -1
- data/lib/badline/roms/README +9 -0
- data/lib/badline/roms/dos1581.rom +0 -0
- data/lib/badline/sdl.rb +37 -20
- data/lib/badline/snapshot/state.rb +5 -4
- data/lib/badline/storage/p00.rb +8 -0
- data/lib/badline/time_of_day.rb +3 -3
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +21 -7
- data/lib/badline/vic20/saved_state.rb +2 -0
- data/lib/badline/vic20.rb +3 -0
- data/lib/badline/z80/core.rb +2 -0
- data/lib/badline/z80/saved_state.rb +69 -0
- metadata +54 -21
- /data/lib/badline/frontend/{menu_ports.rb → menu/menu_ports.rb} +0 -0
- /data/lib/badline/frontend/{player_commands.rb → player/player_commands.rb} +0 -0
- /data/lib/badline/frontend/{player_state.rb → player/player_state.rb} +0 -0
- /data/lib/badline/frontend/{sid_history.rb → player/sid_history.rb} +0 -0
data/lib/badline/cartridge.rb
CHANGED
|
@@ -63,11 +63,7 @@ module Badline
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def from_crt(crt, **)
|
|
66
|
-
|
|
67
|
-
raise UnsupportedTypeError,
|
|
68
|
-
"Unsupported cartridge hardware type #{crt.hardware_type}"
|
|
69
|
-
end
|
|
70
|
-
mapper.new(crt, **)
|
|
66
|
+
mapper_for(crt).new(crt, **)
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
# A cartridge built afresh from what save_setup wrote, before
|
|
@@ -76,9 +72,21 @@ module Badline
|
|
|
76
72
|
return GeoRAM.new(size: input.int) if input.int == GEO_RAM_SETUP
|
|
77
73
|
|
|
78
74
|
crt = Storage::CRTFile.new(bytes: input.string)
|
|
79
|
-
|
|
75
|
+
mapper_for(crt).from_jumpers(crt, input)
|
|
76
|
+
end
|
|
80
77
|
|
|
81
|
-
|
|
78
|
+
# A cartridge from its image and what save_jumpers wrote: whether
|
|
79
|
+
# there are jumpers, then their settings on a mapper that has them.
|
|
80
|
+
def from_jumpers(crt, input)
|
|
81
|
+
input.boolean?
|
|
82
|
+
new(crt)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def mapper_for(crt)
|
|
86
|
+
HARDWARE_TYPES.fetch(crt.hardware_type) do
|
|
87
|
+
raise UnsupportedTypeError,
|
|
88
|
+
"Unsupported cartridge hardware type #{crt.hardware_type}"
|
|
89
|
+
end
|
|
82
90
|
end
|
|
83
91
|
end
|
|
84
92
|
|
|
@@ -95,6 +103,7 @@ module Badline
|
|
|
95
103
|
@exrom = crt.exrom
|
|
96
104
|
@game = crt.game
|
|
97
105
|
@roml = @romh = nil
|
|
106
|
+
@banks = nil
|
|
98
107
|
@on_change = nil
|
|
99
108
|
@on_nmi_change = nil
|
|
100
109
|
@nmi = false
|
|
@@ -193,9 +202,22 @@ module Badline
|
|
|
193
202
|
out.boolean(false)
|
|
194
203
|
end
|
|
195
204
|
|
|
196
|
-
|
|
205
|
+
# The mapper's own state: where its windows point, for a mapper that
|
|
206
|
+
# moves them, and whatever a mapper adds after calling super.
|
|
207
|
+
def save_mapper(out)
|
|
208
|
+
save_windows(out, windows) if windows
|
|
209
|
+
end
|
|
197
210
|
|
|
198
|
-
def load_mapper(
|
|
211
|
+
def load_mapper(input)
|
|
212
|
+
load_windows(input, windows) if windows
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# The banks and views ROML and ROMH can point at, which a snapshot
|
|
216
|
+
# names them by: the mapper's banks, or nothing for a mapper whose
|
|
217
|
+
# windows stay where install_chips put them.
|
|
218
|
+
def windows
|
|
219
|
+
[*@banks, EMPTY_BANK] if @banks
|
|
220
|
+
end
|
|
199
221
|
|
|
200
222
|
# Where the ROML and ROMH windows point, as their places in `windows`,
|
|
201
223
|
# the banks and views the mapper picks them from, or -1 for none.
|
|
@@ -232,6 +254,12 @@ module Badline
|
|
|
232
254
|
@exrom, @game = MODES.fetch(mode)
|
|
233
255
|
end
|
|
234
256
|
|
|
257
|
+
# Sets the lines and tells the machine to map the cartridge again.
|
|
258
|
+
def select_mode(mode)
|
|
259
|
+
self.mode = mode
|
|
260
|
+
changed!
|
|
261
|
+
end
|
|
262
|
+
|
|
235
263
|
def open_bus(addr)
|
|
236
264
|
@open_bus ? @open_bus.peek(addr) : 0xff
|
|
237
265
|
end
|
|
@@ -4,6 +4,9 @@ module Badline
|
|
|
4
4
|
class CIA
|
|
5
5
|
# Running a CIA a stretch of cycles at once, for Drive::Idle.
|
|
6
6
|
module FastForward
|
|
7
|
+
# quiet_cycles for a CIA whose counters will never set a flag.
|
|
8
|
+
QUIET = 1 << 40
|
|
9
|
+
|
|
7
10
|
# Whether fast_forward can run the CIA: timer B stopped with nothing
|
|
8
11
|
# in its pipeline, no interrupt or acknowledge on its way, CNT where it
|
|
9
12
|
# was, the TOD clock stopped, and timer A either stopped, or free
|
|
@@ -16,11 +19,24 @@ module Badline
|
|
|
16
19
|
@ta.idle? ? @serial.idle : timer_a_free_running?
|
|
17
20
|
end
|
|
18
21
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
+
# How many cycles the CIA can run with nothing but its counters
|
|
23
|
+
# moving: as quiet? allows, but with timer B free running too, up to
|
|
24
|
+
# the cycle before it underflows. The 1581's DOS leaves timer B
|
|
25
|
+
# interrupting every 10 ms.
|
|
26
|
+
def quiet_cycles
|
|
27
|
+
return 0 unless @icr.quiet && @tod.quiet? && cnt_steady? && timer_a_quiet?
|
|
28
|
+
return QUIET if @tb.idle?
|
|
29
|
+
return 0 unless @tb.free_running? && @control_b.value.nobits?(0x60)
|
|
30
|
+
|
|
31
|
+
@tb.counter - 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Runs +cycles+ cycles at once while quiet? or for quiet_cycles, as
|
|
35
|
+
# cycle! would: timer A's underflows flag in the ICR, and reach the
|
|
36
|
+
# serial port's underflow line.
|
|
22
37
|
def fast_forward(cycles)
|
|
23
38
|
@tod.fast_forward(cycles)
|
|
39
|
+
@tb.fast_forward(cycles) unless @tb.idle?
|
|
24
40
|
return if @ta.idle?
|
|
25
41
|
|
|
26
42
|
@icr.flag(:timer_a) if @ta.fast_forward(cycles).positive?
|
|
@@ -29,6 +45,8 @@ module Badline
|
|
|
29
45
|
|
|
30
46
|
private
|
|
31
47
|
|
|
48
|
+
def timer_a_quiet? = @ta.idle? ? @serial.idle : timer_a_free_running?
|
|
49
|
+
|
|
32
50
|
# Timer A free running with its interrupt masked, and the serial port
|
|
33
51
|
# an idle input it only clocks.
|
|
34
52
|
def timer_a_free_running?
|
data/lib/badline/cia.rb
CHANGED
|
@@ -61,14 +61,16 @@ module Badline
|
|
|
61
61
|
@tb.latch = value
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
# +tod_hz+ is the rate of the pulses on the TOD pin, the region's mains
|
|
65
|
+
# unless a machine feeds it otherwise, and 0 for a pin held still.
|
|
66
|
+
def initialize(start: 0, peripheral: nil, model: :mos6526, region: Region::PAL, tod_hz: nil)
|
|
65
67
|
raise ArgumentError, "unknown CIA model #{model}" unless MODELS.include?(model)
|
|
66
68
|
|
|
67
69
|
addressable_at(start, length: 2**8)
|
|
68
70
|
|
|
69
71
|
@model = model
|
|
70
72
|
@clock_hz = region.clock_hz
|
|
71
|
-
@mains_hz = region.mains_hz
|
|
73
|
+
@mains_hz = tod_hz || region.mains_hz
|
|
72
74
|
|
|
73
75
|
@peripheral = peripheral
|
|
74
76
|
@port_b4_handler = nil
|
|
@@ -139,9 +141,10 @@ module Badline
|
|
|
139
141
|
|
|
140
142
|
# Port A as driven by the data/direction registers alone, without
|
|
141
143
|
# peripheral pulldown. Cheap path for the VIC bank lookup.
|
|
142
|
-
def port_a_lines
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
def port_a_lines = driven_lines(@data_port_a, @data_dir_a)
|
|
145
|
+
|
|
146
|
+
# Port B as driven by the data/direction registers alone.
|
|
147
|
+
def port_b_lines = driven_lines(@data_port_b, @data_dir_b)
|
|
145
148
|
|
|
146
149
|
def read_port_b
|
|
147
150
|
lines = driven_lines(@data_port_b, @data_dir_b)
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
class Computer
|
|
5
5
|
# What plugs into the machine besides its chips, the cartridge, the
|
|
6
|
-
# disk device 8 serves through the traps,
|
|
7
|
-
# their state for a snapshot.
|
|
6
|
+
# disk device 8 serves through the traps, its true drives (TrueDrives)
|
|
7
|
+
# and an REU, and their state for a snapshot.
|
|
8
8
|
module Attachments
|
|
9
|
+
include TrueDrives
|
|
10
|
+
|
|
9
11
|
# Puts the cartridge in the expansion port and wires its clock and NMI
|
|
10
12
|
# line to the machine, leaving the machine's state as it is. A
|
|
11
13
|
# restored snapshot's cartridge comes back through here.
|
|
@@ -44,42 +46,8 @@ module Badline
|
|
|
44
46
|
# it.
|
|
45
47
|
attr_reader :iec_bus
|
|
46
48
|
|
|
47
|
-
# Plugs in a Drive1541, which then runs alongside the C64 on its own
|
|
48
|
-
# clock and talks to it over the serial bus. The serial traps stop
|
|
49
|
-
# answering the drive's device number, so the KERNAL's TALK, LISTEN and
|
|
50
|
-
# byte transfers reach the drive. The LOAD and SAVE traps stay, and
|
|
51
|
-
# still serve a mounted image.
|
|
52
|
-
def attach_drive1541(drive)
|
|
53
|
-
@iec_bus.detach(@drive1541) if @drive1541
|
|
54
|
-
drive.host_clock_hz = region.clock_hz
|
|
55
|
-
@drive1541 = drive
|
|
56
|
-
drive.connect(iec_bus)
|
|
57
|
-
@serial_trap&.device = serial_trap_device
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# The true drive on the serial bus: the Drive1541, or nil.
|
|
61
|
-
def true_drive = @drive1541
|
|
62
|
-
|
|
63
|
-
# Plugs a Drive1541 in as device 8, and returns it.
|
|
64
|
-
def plug_true_drive = Drive1541.new.tap { |drive| attach_drive1541(drive) }
|
|
65
|
-
|
|
66
|
-
# Unplugs the Drive1541, leaving the serial bus with nothing on it.
|
|
67
|
-
def detach_drive1541
|
|
68
|
-
return unless @drive1541
|
|
69
|
-
|
|
70
|
-
@iec_bus.detach(@drive1541)
|
|
71
|
-
@drive1541 = nil
|
|
72
|
-
@serial_trap&.device = serial_trap_device
|
|
73
|
-
end
|
|
74
|
-
|
|
75
49
|
private
|
|
76
50
|
|
|
77
|
-
# The device number the serial traps answer: device 8, unless a true
|
|
78
|
-
# drive is on the bus as device 8.
|
|
79
|
-
def serial_trap_device
|
|
80
|
-
@drive1541&.device == KernalTrap::Routine::DEVICE ? nil : KernalTrap::Routine::DEVICE
|
|
81
|
-
end
|
|
82
|
-
|
|
83
51
|
# CIA 2's port A drives the serial bus, which reads CLK and DATA back
|
|
84
52
|
# into it.
|
|
85
53
|
def plug_serial_bus
|
|
@@ -132,26 +100,6 @@ module Badline
|
|
|
132
100
|
@save_trap.load_state(input)
|
|
133
101
|
end
|
|
134
102
|
|
|
135
|
-
# A true 1541 on the serial bus. The leading flag is the bus itself,
|
|
136
|
-
# which every machine now has, kept so older snapshots still load.
|
|
137
|
-
def save_serial_bus(out)
|
|
138
|
-
out.boolean(true).boolean(!@drive1541.nil?)
|
|
139
|
-
return unless @drive1541
|
|
140
|
-
|
|
141
|
-
out.int(@drive1541.device)
|
|
142
|
-
@drive1541.save_state(out)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def load_serial_bus(input)
|
|
146
|
-
input.boolean?
|
|
147
|
-
return detach_drive1541 unless input.boolean?
|
|
148
|
-
|
|
149
|
-
device = input.int
|
|
150
|
-
detach_drive1541 if @drive1541 && @drive1541.device != device
|
|
151
|
-
attach_drive1541(Drive1541.new(device:)) unless @drive1541
|
|
152
|
-
@drive1541.load_state(input)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
103
|
# The REU, which the setup says the machine has, and whether it has
|
|
156
104
|
# asked for the bus. Its IRQ line reaches the CPU as it was.
|
|
157
105
|
def save_reu(out)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class Computer
|
|
5
|
+
# The true drives on the C64's serial bus: a Drive1541, and a Drive1581
|
|
6
|
+
# (Drive1581::Slot), each in a field of its own, and their state for a
|
|
7
|
+
# snapshot.
|
|
8
|
+
module TrueDrives
|
|
9
|
+
include Drive1581::Slot
|
|
10
|
+
|
|
11
|
+
# Plugs in a Drive1541, which then runs alongside the C64 on its own
|
|
12
|
+
# clock and talks to it over the serial bus. The serial traps stop
|
|
13
|
+
# answering the drive's device number, so the KERNAL's TALK, LISTEN and
|
|
14
|
+
# byte transfers reach the drive. The LOAD and SAVE traps stay, and
|
|
15
|
+
# still serve a mounted image.
|
|
16
|
+
def attach_drive1541(drive)
|
|
17
|
+
@iec_bus.detach(@drive1541) if @drive1541
|
|
18
|
+
drive.host_clock_hz = region.clock_hz
|
|
19
|
+
@drive1541 = drive
|
|
20
|
+
drive.connect(iec_bus)
|
|
21
|
+
@serial_trap&.device = serial_trap_device
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The true drive on the serial bus: the Drive1581 ahead of the
|
|
25
|
+
# Drive1541, or nil.
|
|
26
|
+
def true_drive = @drive1581 || @drive1541
|
|
27
|
+
|
|
28
|
+
# Plugs a Drive1541 in as device 8, in place of a 1581 there, and
|
|
29
|
+
# returns it.
|
|
30
|
+
def plug_true_drive
|
|
31
|
+
detach_drive1581 if drive1581_on_device8?
|
|
32
|
+
Drive1541.new.tap { |drive| attach_drive1541(drive) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Unplugs the Drive1541, leaving the serial bus with nothing on it.
|
|
36
|
+
def detach_drive1541
|
|
37
|
+
return unless @drive1541
|
|
38
|
+
|
|
39
|
+
@iec_bus.detach(@drive1541)
|
|
40
|
+
@drive1541 = nil
|
|
41
|
+
@serial_trap&.device = serial_trap_device
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# The device number the serial traps answer: device 8, unless a true
|
|
47
|
+
# drive is on the bus as device 8.
|
|
48
|
+
def serial_trap_device
|
|
49
|
+
device = KernalTrap::Routine::DEVICE
|
|
50
|
+
@drive1541&.device == device || drive1581_on_device8? ? nil : device
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# A true 1541 on the serial bus, then a 1581. The leading flag is the
|
|
54
|
+
# bus itself, which every machine now has.
|
|
55
|
+
def save_serial_bus(out)
|
|
56
|
+
out.boolean(true).boolean(!@drive1541.nil?)
|
|
57
|
+
if @drive1541
|
|
58
|
+
out.int(@drive1541.device)
|
|
59
|
+
@drive1541.save_state(out)
|
|
60
|
+
end
|
|
61
|
+
save_drive1581(out)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def load_serial_bus(input)
|
|
65
|
+
input.boolean?
|
|
66
|
+
load_drive1541(input)
|
|
67
|
+
load_drive1581(input)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_drive1541(input)
|
|
71
|
+
return detach_drive1541 unless input.boolean?
|
|
72
|
+
|
|
73
|
+
device = input.int
|
|
74
|
+
detach_drive1541 if @drive1541 && @drive1541.device != device
|
|
75
|
+
attach_drive1541(Drive1541.new(device:)) unless @drive1541
|
|
76
|
+
@drive1541.load_state(input)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/badline/computer.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/computer/true_drives"
|
|
3
4
|
require "badline/computer/attachments"
|
|
4
5
|
require "badline/computer/kernal_traps"
|
|
5
6
|
require "badline/computer/saved_state"
|
|
@@ -77,6 +78,7 @@ module Badline
|
|
|
77
78
|
@serial_trap = nil
|
|
78
79
|
@save_trap = nil
|
|
79
80
|
@drive1541 = nil
|
|
81
|
+
@drive1581 = nil
|
|
80
82
|
plug_serial_bus
|
|
81
83
|
@reu = reu ? plug_reu(reu) : nil
|
|
82
84
|
end
|
|
@@ -101,6 +103,7 @@ module Badline
|
|
|
101
103
|
watch_freeze if @freezing
|
|
102
104
|
clock_cpu
|
|
103
105
|
@drive1541&.host_cycle!
|
|
106
|
+
@drive1581&.host_cycle!
|
|
104
107
|
|
|
105
108
|
@cycles += 1
|
|
106
109
|
end
|
|
@@ -174,6 +177,7 @@ module Badline
|
|
|
174
177
|
@dma = false
|
|
175
178
|
@drive&.reset!
|
|
176
179
|
@drive1541&.reset!
|
|
180
|
+
@drive1581&.reset!
|
|
177
181
|
@freezing = false
|
|
178
182
|
@nmi_asserted = false
|
|
179
183
|
cpu.reset!
|
data/lib/badline/core.rb
CHANGED
|
@@ -38,6 +38,8 @@ require "badline/vic"
|
|
|
38
38
|
require "badline/keyboard_buffer"
|
|
39
39
|
require "badline/iec_bus"
|
|
40
40
|
require "badline/drive1541"
|
|
41
|
+
require "badline/drive1581"
|
|
42
|
+
require "badline/drive1581/slot"
|
|
41
43
|
require "badline/snapshot/state"
|
|
42
44
|
require "badline/snapshot/setup"
|
|
43
45
|
require "badline/computer"
|
data/lib/badline/drive/core.rb
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Drive
|
|
5
5
|
# What every Commodore disk drive here shares: a 6502 running the DOS
|
|
6
|
-
# ROM with
|
|
7
|
-
#
|
|
6
|
+
# ROM, with chips on its bus facing the serial bus and the disk
|
|
7
|
+
# mechanism, on a clock of its own.
|
|
8
8
|
#
|
|
9
9
|
# The drive runs on its own crystal, so it clocks against the host's
|
|
10
10
|
# clock through a fractional accumulator: each host cycle adds the
|
|
@@ -14,8 +14,7 @@ module Badline
|
|
|
14
14
|
# every 67. The host sets its clock on attaching the drive, and until
|
|
15
15
|
# then a 1 MHz drive runs one cycle per host cycle.
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
# insert.
|
|
17
|
+
# The Mechanism reads a Disk put in with insert.
|
|
19
18
|
#
|
|
20
19
|
# While the DOS idles, host_cycle! skips the drive's cycles and catches
|
|
21
20
|
# up on them later, exactly (see Idle). The readers of the drive's parts
|
|
@@ -24,7 +23,10 @@ module Badline
|
|
|
24
23
|
# sleeps through them too (see Orbit).
|
|
25
24
|
#
|
|
26
25
|
# A model includes Core and builds its parts, sets @clock_hz and
|
|
27
|
-
# @idle_loop, and runs one drive cycle in step.
|
|
26
|
+
# @idle_loop, and runs one drive cycle in step. It hears ATN on
|
|
27
|
+
# connecting (hear_atn), drives the serial bus (serial_output), and
|
|
28
|
+
# answers Idle's and Orbit's questions about its chips, as VIAs does
|
|
29
|
+
# for the 1541 and the 1571.
|
|
28
30
|
module Core
|
|
29
31
|
include Sleep
|
|
30
32
|
include Idle
|
|
@@ -42,16 +44,6 @@ module Badline
|
|
|
42
44
|
@bus
|
|
43
45
|
end
|
|
44
46
|
|
|
45
|
-
def via1
|
|
46
|
-
settle!
|
|
47
|
-
@via1
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def via2
|
|
51
|
-
settle!
|
|
52
|
-
@via2
|
|
53
|
-
end
|
|
54
|
-
|
|
55
47
|
def mechanism
|
|
56
48
|
settle!
|
|
57
49
|
@mechanism
|
|
@@ -77,7 +69,7 @@ module Badline
|
|
|
77
69
|
@serial_bus = serial_bus
|
|
78
70
|
@serial_port.bus = serial_bus
|
|
79
71
|
serial_bus.attach(self)
|
|
80
|
-
|
|
72
|
+
hear_atn
|
|
81
73
|
end
|
|
82
74
|
|
|
83
75
|
# Puts a Disk in the drive (Disk.from_d64 makes one from an image).
|
|
@@ -93,10 +85,6 @@ module Badline
|
|
|
93
85
|
# is the same at the end of every pass the drive sleeps through.
|
|
94
86
|
def led_on? = @mechanism.led_on?
|
|
95
87
|
|
|
96
|
-
# VIA 1's port B as it drives the serial bus. It holds still while the
|
|
97
|
-
# drive sleeps, so reading it leaves the drive asleep.
|
|
98
|
-
def serial_output = @via1.port_b_output
|
|
99
|
-
|
|
100
88
|
# Stores what the head wrote since the motor last stopped in the
|
|
101
89
|
# disk's image, as the motor stopping does.
|
|
102
90
|
def flush
|
|
@@ -104,15 +92,6 @@ module Badline
|
|
|
104
92
|
@mechanism.flush
|
|
105
93
|
end
|
|
106
94
|
|
|
107
|
-
# The serial bus's RESET line reaches the CPU and both VIAs. RAM keeps
|
|
108
|
-
# its contents.
|
|
109
|
-
def reset!
|
|
110
|
-
settle!
|
|
111
|
-
@via1.reset!
|
|
112
|
-
@via2.reset!
|
|
113
|
-
@cpu.reset!
|
|
114
|
-
end
|
|
115
|
-
|
|
116
95
|
# Runs the drive cycles that fall in one host cycle: none, one or two.
|
|
117
96
|
# The host has run this cycle already, and the drive sees what it did
|
|
118
97
|
# from the next one on (see SerialPort). Asleep, the drive owes the
|
|
@@ -158,10 +137,6 @@ module Badline
|
|
|
158
137
|
|
|
159
138
|
private
|
|
160
139
|
|
|
161
|
-
# Runs the model's chips beyond the VIAs +cycles+ quiet cycles at once
|
|
162
|
-
# (see quiet_cycles). The 1541 has none.
|
|
163
|
-
def fast_forward_chips(_cycles) = nil
|
|
164
|
-
|
|
165
140
|
# A drive cycle from host_cycle!, which may find the idle loop.
|
|
166
141
|
def run_cycle
|
|
167
142
|
step
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drive
|
|
5
|
+
# Fast serial on a drive with a CIA, the 1571's 6526 or the 1581's
|
|
6
|
+
# 8520: through buffers a port line turns round, the CIA's CNT drives
|
|
7
|
+
# SRQ and its SP drives DATA, or it hears them. The drive pushes its
|
|
8
|
+
# pins into the bus as they move, and hears the host's as the host
|
|
9
|
+
# pushes them, pin by pin, so each byte goes through both shift
|
|
10
|
+
# registers bit by bit. The CIA takes no part in a pass of the idle
|
|
11
|
+
# loop, and any access to it, or the host moving its fast serial pins,
|
|
12
|
+
# wakes the drive.
|
|
13
|
+
#
|
|
14
|
+
# A model includes it beside Core, and calls turn_fast_serial when its
|
|
15
|
+
# direction line moves and drive_fast_serial each cycle the buffers
|
|
16
|
+
# face out. Its serial_output carries @fast_output above its port.
|
|
17
|
+
module FastSerial
|
|
18
|
+
# Whether the buffers face out, the drive's CIA driving DATA and SRQ.
|
|
19
|
+
attr_reader :fast_serial_out
|
|
20
|
+
|
|
21
|
+
# The host moved its fast serial pins: the CIA hears SRQ on CNT and
|
|
22
|
+
# DATA on SP while the buffers face in.
|
|
23
|
+
def fast_lines_moved
|
|
24
|
+
settle!
|
|
25
|
+
hear_fast_serial unless @fast_serial_out
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def init_fast_serial
|
|
31
|
+
@fast_serial_out = false
|
|
32
|
+
@fast_output = 0
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The buffers turned round: outwards the CIA's pins drive the lines,
|
|
36
|
+
# and inwards they let go and hear them.
|
|
37
|
+
def turn_fast_serial(out)
|
|
38
|
+
@fast_serial_out = out
|
|
39
|
+
return unless @serial_bus
|
|
40
|
+
|
|
41
|
+
push_fast_output(out ? fast_pins : 0)
|
|
42
|
+
hear_fast_serial unless out
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def drive_fast_serial
|
|
46
|
+
low = fast_pins
|
|
47
|
+
push_fast_output(low) if low != @fast_output
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The lines the CIA's CNT and SP pull while its serial port drives
|
|
51
|
+
# them.
|
|
52
|
+
def fast_pins
|
|
53
|
+
serial = @cia.serial
|
|
54
|
+
return 0 unless serial.output?
|
|
55
|
+
|
|
56
|
+
low = serial.cnt ? 0 : IECBus::DRIVE_FAST_SRQ
|
|
57
|
+
serial.sp_out ? low : low | IECBus::DRIVE_FAST_DATA
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def push_fast_output(low)
|
|
61
|
+
@fast_output = low
|
|
62
|
+
@serial_bus&.drives_fast_moved!
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def hear_fast_serial
|
|
66
|
+
low = @serial_bus.low_lines
|
|
67
|
+
serial = @cia.serial
|
|
68
|
+
serial.cnt_in = low.nobits?(IECBus::SRQ)
|
|
69
|
+
serial.sp_in = low.nobits?(IECBus::DATA)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/badline/drive/idle.rb
CHANGED
|
@@ -81,7 +81,7 @@ module Badline
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def start_recording
|
|
84
|
-
return if
|
|
84
|
+
return if busy_for_pass? || @cpu.trapped?
|
|
85
85
|
|
|
86
86
|
@recording = true
|
|
87
87
|
@record_state = idle_state
|
|
@@ -116,20 +116,10 @@ module Badline
|
|
|
116
116
|
anchor
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
def quiet_cycles
|
|
120
|
-
[@via1.quiet_cycles, @via2.quiet_cycles].min
|
|
121
|
-
end
|
|
122
|
-
|
|
123
119
|
def ram_as_found?
|
|
124
120
|
ram = @bus.ram
|
|
125
121
|
@bus.touched.all? { |addr, value| ram.peek(addr) == value }
|
|
126
122
|
end
|
|
127
|
-
|
|
128
|
-
def idle_state
|
|
129
|
-
mechanism = @mechanism
|
|
130
|
-
[*@cpu.idle_state, *@via1.idle_state, *@via2.idle_state, @bus.data, mechanism.so_pending,
|
|
131
|
-
*mechanism.idle_state, mechanism.read_a(0xff), mechanism.read_b(0xff)]
|
|
132
|
-
end
|
|
133
123
|
end
|
|
134
124
|
end
|
|
135
125
|
end
|
data/lib/badline/drive/orbit.rb
CHANGED
|
@@ -63,9 +63,7 @@ module Badline
|
|
|
63
63
|
cycles = orbits * @orbit_cycles
|
|
64
64
|
touched = @bus.touched_timers
|
|
65
65
|
@cpu.fast_forward(cycles, orbits * @orbit_instructions)
|
|
66
|
-
|
|
67
|
-
@via2.skip_orbits(cycles, touched >> 2)
|
|
68
|
-
fast_forward_chips(cycles)
|
|
66
|
+
skip_chip_orbits(cycles, touched)
|
|
69
67
|
@cycles += cycles
|
|
70
68
|
cycles
|
|
71
69
|
end
|
|
@@ -74,8 +72,7 @@ module Badline
|
|
|
74
72
|
# the unarmed timers it hasn't touched since it was last tainted.
|
|
75
73
|
def anchor_state
|
|
76
74
|
touched = @bus.touched_timers
|
|
77
|
-
[*idle_state,
|
|
78
|
-
@bus.ram.snapshot]
|
|
75
|
+
[*idle_state, *counter_state(touched), @bus.ram.snapshot]
|
|
79
76
|
end
|
|
80
77
|
end
|
|
81
78
|
end
|
data/lib/badline/drive/sleep.rb
CHANGED
|
@@ -102,8 +102,6 @@ module Badline
|
|
|
102
102
|
def skip_passes(passes)
|
|
103
103
|
cycles = passes * @pass_cycles
|
|
104
104
|
@cpu.fast_forward(cycles, passes * @pass_instructions)
|
|
105
|
-
@via1.fast_forward(cycles)
|
|
106
|
-
@via2.fast_forward(cycles)
|
|
107
105
|
fast_forward_chips(cycles)
|
|
108
106
|
@cycles += cycles
|
|
109
107
|
@budget -= cycles
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drive
|
|
5
|
+
# The two VIAs the 1541 and the 1571 share: VIA 1 facing the serial
|
|
6
|
+
# bus, its CA1 on ATN, and VIA 2 running the GCR Mechanism. A model
|
|
7
|
+
# includes it beside Core, and it answers Core's, Idle's and Orbit's
|
|
8
|
+
# questions about the chips: what they hold, how long their counters
|
|
9
|
+
# leave them quiet, and how to run them a stretch at once. A model with
|
|
10
|
+
# more chips on its bus runs them in fast_forward_more.
|
|
11
|
+
module VIAs
|
|
12
|
+
def via1
|
|
13
|
+
settle!
|
|
14
|
+
@via1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def via2
|
|
18
|
+
settle!
|
|
19
|
+
@via2
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Puts in a Disk from the .d64, .g64, .d71 or .g71 image at +path+
|
|
23
|
+
# (Drive1541::Disk.open), write-protected with `read_only`.
|
|
24
|
+
def insert_image(path, read_only: false)
|
|
25
|
+
insert(Drive1541::Disk.open(path, read_only:))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# VIA 1's port B as it drives the serial bus. It holds still while the
|
|
29
|
+
# drive sleeps, so reading it leaves the drive asleep.
|
|
30
|
+
def serial_output = @via1.port_b_output
|
|
31
|
+
|
|
32
|
+
# The serial bus's RESET line reaches the CPU and both VIAs. RAM keeps
|
|
33
|
+
# its contents.
|
|
34
|
+
def reset!
|
|
35
|
+
settle!
|
|
36
|
+
@via1.reset!
|
|
37
|
+
@via2.reset!
|
|
38
|
+
@cpu.reset!
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# ATN reaches VIA 1's CA1 as the serial port latched it.
|
|
44
|
+
def hear_atn
|
|
45
|
+
@via1.ca1 = @serial_port.atn_low?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Whether the drive is doing anything a pass of the idle loop can't
|
|
49
|
+
# repeat: the motor on, or a VIA pulling IRQ.
|
|
50
|
+
def busy_for_pass? = @mechanism.motor_on? || @via1.irq? || @via2.irq?
|
|
51
|
+
|
|
52
|
+
def quiet_cycles
|
|
53
|
+
[@via1.quiet_cycles, @via2.quiet_cycles].min
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def idle_state
|
|
57
|
+
mechanism = @mechanism
|
|
58
|
+
[*@cpu.idle_state, *@via1.idle_state, *@via2.idle_state, @bus.data, mechanism.so_pending,
|
|
59
|
+
*mechanism.idle_state, mechanism.read_a(0xff), mechanism.read_b(0xff)]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The counters an orbit has to bring back (VIA#counter_state).
|
|
63
|
+
def counter_state(touched)
|
|
64
|
+
[*@via1.counter_state(touched & 0x03), *@via2.counter_state(touched >> 2)]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def fast_forward_chips(cycles)
|
|
68
|
+
@via1.fast_forward(cycles)
|
|
69
|
+
@via2.fast_forward(cycles)
|
|
70
|
+
fast_forward_more(cycles)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def skip_chip_orbits(cycles, touched)
|
|
74
|
+
@via1.skip_orbits(cycles, touched & 0x03)
|
|
75
|
+
@via2.skip_orbits(cycles, touched >> 2)
|
|
76
|
+
fast_forward_more(cycles)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Runs the model's chips beyond the VIAs +cycles+ quiet cycles at once
|
|
80
|
+
# (see quiet_cycles). The 1541 has none.
|
|
81
|
+
def fast_forward_more(_cycles) = nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/badline/drive.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "badline/drive/sleep"
|
|
|
4
4
|
require "badline/drive/idle"
|
|
5
5
|
require "badline/drive/orbit"
|
|
6
6
|
require "badline/drive/core"
|
|
7
|
+
require "badline/drive/vias"
|
|
8
|
+
require "badline/drive/fast_serial"
|
|
7
9
|
require "badline/drive/idle_cpu"
|
|
8
10
|
require "badline/drive/watch"
|
|
9
11
|
require "badline/drive/serial_lines"
|