badline 0.16.1 → 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 +213 -0
- data/CLAUDE.md +11 -7
- data/CONTRIBUTING.md +7 -1
- data/README.md +265 -26
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +189 -0
- data/doc/snapshot-format.md +616 -74
- data/doc/snapshots.md +192 -24
- 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/acia.rb +10 -0
- data/lib/badline/cbm2/bus.rb +263 -0
- data/lib/badline/cbm2/cpu.rb +58 -0
- data/lib/badline/cbm2/crtc_video.rb +133 -0
- data/lib/badline/cbm2/ieee_drive.rb +18 -0
- data/lib/badline/cbm2/ieee_layout.rb +69 -0
- data/lib/badline/cbm2/ieee_ports.rb +104 -0
- data/lib/badline/cbm2/ieee_traps.rb +81 -0
- data/lib/badline/cbm2/keyboard_ports.rb +60 -0
- data/lib/badline/cbm2/keyed_modules.rb +67 -0
- data/lib/badline/cbm2/keyed_state.rb +95 -0
- data/lib/badline/cbm2/model.rb +44 -0
- data/lib/badline/cbm2/p500/control_ports.rb +80 -0
- data/lib/badline/cbm2/p500/ieee_layout.rb +41 -0
- data/lib/badline/cbm2/p500/keyed_state.rb +85 -0
- data/lib/badline/cbm2/p500/model.rb +39 -0
- data/lib/badline/cbm2/p500/pages.rb +57 -0
- data/lib/badline/cbm2/p500.rb +264 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +270 -0
- data/lib/badline/{plus4 → chips}/acia/frames.rb +1 -1
- data/lib/badline/chips/acia/keyed_state.rb +47 -0
- data/lib/badline/chips/acia.rb +307 -0
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/crtc.rb +3 -2
- 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/fast_forward.rb +72 -0
- data/lib/badline/chips/riot/keyed_parts.rb +69 -0
- data/lib/badline/chips/riot/timer.rb +91 -0
- data/lib/badline/chips/riot.rb +162 -0
- data/lib/badline/chips/rriot/keyed_state.rb +30 -0
- data/lib/badline/chips/rriot.rb +134 -0
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/tpi/keyed_state.rb +38 -0
- data/lib/badline/chips/tpi.rb +233 -0
- data/lib/badline/chips/vic/bank.rb +60 -5
- data/lib/badline/chips/vic/sprite.rb +4 -3
- 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/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -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/disk.rb +163 -0
- data/lib/badline/drives/model8050/dos_board.rb +91 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +181 -0
- data/lib/badline/drives/model8050/ieee_port.rb +147 -0
- data/lib/badline/drives/model8050/ip_bus.rb +93 -0
- data/lib/badline/drives/model8050/keyed_state.rb +163 -0
- data/lib/badline/drives/model8050/mechanism.rb +230 -0
- data/lib/badline/drives/model8050/parts.rb +37 -0
- data/lib/badline/drives/model8050.rb +131 -0
- 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 +18 -3
- data/lib/badline/frontend/boot.rb +4 -3
- data/lib/badline/frontend/controls.rb +107 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +97 -2
- 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 +47 -14
- data/lib/badline/frontend/menu/menu_pages.rb +113 -4
- data/lib/badline/frontend/menu/menu_ports.rb +77 -4
- data/lib/badline/frontend/menu/pause_menu.rb +6 -2
- data/lib/badline/frontend/pet_keyboard.rb +45 -7
- 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 +49 -11
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/kernal_trap/dos/disk_commands.rb +80 -0
- data/lib/badline/kernal_trap/dos/status.rb +3 -0
- data/lib/badline/kernal_trap/dos.rb +18 -5
- data/lib/badline/kernal_trap/ieee.rb +4 -2
- data/lib/badline/kernal_trap/ieee_layout.rb +95 -43
- 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 +171 -0
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +62 -14
- data/lib/badline/media/cbm2_media.rb +29 -0
- 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/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +51 -25
- data/lib/badline/media.rb +23 -8
- data/lib/badline/options/family.rb +108 -6
- data/lib/badline/options/help.rb +58 -12
- data/lib/badline/options/table.rb +60 -14
- data/lib/badline/options/validation.rb +71 -11
- data/lib/badline/options.rb +46 -13
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/cassettes.rb +22 -1
- data/lib/badline/pet/ieee_drive.rb +5 -64
- data/lib/badline/pet/ieee_ports.rb +7 -0
- 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 +33 -6
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +2 -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/8050/dos-2.7.rom +0 -0
- data/lib/badline/roms/8050/fdc-901869-01.rom +0 -0
- data/lib/badline/roms/8050/fdc-901885-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 +277 -0
- data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
- data/lib/badline/roms/cbm2/basic128.rom +0 -0
- data/lib/badline/roms/cbm2/basic256.rom +0 -0
- data/lib/badline/roms/cbm2/characters-600.rom +0 -0
- data/lib/badline/roms/cbm2/characters-700.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
- data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
- data/lib/badline/roms/cbm2/p500-kernal.rom +0 -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/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +9 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +59 -3
- data/lib/badline/snapshot/image.rb +65 -9
- 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/cbm2.rb +93 -0
- data/lib/badline/snapshot/vice/cbm2_acia.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_crtc.rb +127 -0
- data/lib/badline/snapshot/vice/cbm2_mains.rb +49 -0
- data/lib/badline/snapshot/vice/cbm2_mem.rb +75 -0
- data/lib/badline/snapshot/vice/cbm2_restore.rb +204 -0
- data/lib/badline/snapshot/vice/cbm2_tpi.rb +58 -0
- data/lib/badline/snapshot/vice/cias.rb +13 -9
- data/lib/badline/snapshot/vice/p500.rb +96 -0
- data/lib/badline/snapshot/vice/p500_restore.rb +130 -0
- data/lib/badline/snapshot/vice/p500_vicii.rb +166 -0
- data/lib/badline/snapshot/vice/pet.rb +20 -10
- data/lib/badline/snapshot/vice/pet_cpu.rb +42 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +25 -17
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +336 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +100 -7
- data/lib/badline/snapshot/vice/pet_via.rb +6 -5
- data/lib/badline/snapshot/vice/sid_registers.rb +2 -1
- data/lib/badline/snapshot/vice/vicii_import.rb +10 -6
- data/lib/badline/snapshot/vice.rb +3 -0
- data/lib/badline/snapshot.rb +15 -3
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/disk_image/bam.rb +18 -3
- data/lib/badline/storage/disk_image/formatting.rb +69 -0
- data/lib/badline/storage/disk_image/writing.rb +37 -2
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +5 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -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 +2 -0
- metadata +148 -2
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model8050
|
|
6
|
+
# The unit's two drives as the floppy controller sees them through
|
|
7
|
+
# the VIA and the 6530 (schematic 8050001, sheets 2 and 3).
|
|
8
|
+
#
|
|
9
|
+
# VIA port B runs the drives: PB5 drive 0's motor and PB4 drive 1's,
|
|
10
|
+
# each on while low, PB2-3 drive 0's stepper phases and PB0-1 drive
|
|
11
|
+
# 1's, and PB7 reads SYNC, low while ten one bits sit in the shift
|
|
12
|
+
# register. The 6530's port B picks the drive on PB0, the zone on
|
|
13
|
+
# PB1-2 and the side on PB4, low for the second, and reads write
|
|
14
|
+
# protect on PB3, high while the disk can't be written, and the sides
|
|
15
|
+
# on PB6, low for two, which jumper E3 grounds on the 8250. Its port
|
|
16
|
+
# A holds the byte to write.
|
|
17
|
+
#
|
|
18
|
+
# Each phase change moves the head a track: the controller steps
|
|
19
|
+
# through the Gray code 00, 10, 11, 01 outward and back the other way
|
|
20
|
+
# inward ($FF9C), and bumps the head against track 1 with 92 steps
|
|
21
|
+
# out.
|
|
22
|
+
#
|
|
23
|
+
# A turning disk passes one byte cell under the head every ten bit
|
|
24
|
+
# cells, at 6 MHz divided by 16 less the track's zone, and the byte
|
|
25
|
+
# cells come round 300 times a minute (Disk). Each data cell ends in
|
|
26
|
+
# byte ready, which latches the GCR ROM's byte into VIA port A through
|
|
27
|
+
# CA1 and pulses the controller's SO pin; a sync cell holds SYNC low
|
|
28
|
+
# for the cell after it instead. While the VIA's CB2 is low the drive
|
|
29
|
+
# writes: each cell takes the 6530's port A, or a sync while CA2 is
|
|
30
|
+
# high, and still ends in byte ready.
|
|
31
|
+
#
|
|
32
|
+
# A disk going in or out passes the write-protect sensor, which the
|
|
33
|
+
# controller watches for a disk change: it reads high for a while.
|
|
34
|
+
class Mechanism
|
|
35
|
+
WRITE_PROTECT = 0x08
|
|
36
|
+
ONE_SIDE = 0x40
|
|
37
|
+
|
|
38
|
+
# The cycles the write-protect sensor reads high as a disk goes in
|
|
39
|
+
# or out, ten of the controller's 5 ms interrupts.
|
|
40
|
+
CHANGE_CYCLES = 50_000
|
|
41
|
+
|
|
42
|
+
attr_reader :sides, :data_in, :cylinders, :so_pending
|
|
43
|
+
attr_accessor :via, :rriot
|
|
44
|
+
|
|
45
|
+
def initialize(sides:)
|
|
46
|
+
@sides = sides
|
|
47
|
+
@via = nil
|
|
48
|
+
@rriot = nil
|
|
49
|
+
@disks = [nil, nil]
|
|
50
|
+
@cylinders = [0, 0]
|
|
51
|
+
@phases = [0, 0]
|
|
52
|
+
@positions = [0, 0]
|
|
53
|
+
@clocks = [0, 0]
|
|
54
|
+
@changing = [0, 0]
|
|
55
|
+
@motors = [false, false]
|
|
56
|
+
@so_pending = false
|
|
57
|
+
@data_in = 0xff
|
|
58
|
+
@syncs = [false, false]
|
|
59
|
+
@ca1_low = false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The Disk in +drive+, 0 or 1, or nil.
|
|
63
|
+
def disk(drive = 0) = @disks[drive]
|
|
64
|
+
|
|
65
|
+
def insert(disk, drive = 0)
|
|
66
|
+
@disks[drive]&.flush
|
|
67
|
+
@disks[drive] = disk
|
|
68
|
+
@changing[drive] = CHANGE_CYCLES
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def flush
|
|
72
|
+
@disks.each { |disk| disk&.flush }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def motor_on?(drive = 0) = @motors[drive]
|
|
76
|
+
|
|
77
|
+
# The track under +drive+'s head on the side the 6530 picks.
|
|
78
|
+
def track(drive = 0)
|
|
79
|
+
side = drive == selected_drive ? selected_side : 0
|
|
80
|
+
@cylinders[drive] + 1 + (side * Storage::D80Image::SIDE_TRACKS)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def cycle!
|
|
84
|
+
if @ca1_low
|
|
85
|
+
@ca1_low = false
|
|
86
|
+
@via.ca1 = true
|
|
87
|
+
end
|
|
88
|
+
drives = @via.port_b_output
|
|
89
|
+
turn(0, drives.nobits?(0x20), (drives >> 2) & 0x03)
|
|
90
|
+
turn(1, drives.nobits?(0x10), drives & 0x03)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Whether a cycle would change nothing: both motors off, no disk
|
|
94
|
+
# going in or out, and no byte ready under way.
|
|
95
|
+
def quiet?
|
|
96
|
+
!@motors[0] && !@motors[1] && @changing[0].zero? && @changing[1].zero? && !@ca1_low && !@so_pending
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# What the drives hold, for comparing them at two points.
|
|
100
|
+
def idle_state = [*@cylinders, *@phases, *@motors, *@syncs, @data_in]
|
|
101
|
+
|
|
102
|
+
# Whether byte ready pulses the controller's SO pin this cycle,
|
|
103
|
+
# which takes the pulse.
|
|
104
|
+
def take_so
|
|
105
|
+
so = @so_pending
|
|
106
|
+
@so_pending = false
|
|
107
|
+
so
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Whether ten one bits sit in the shift register, off the selected
|
|
111
|
+
# drive's head.
|
|
112
|
+
def sync? = @syncs[selected_drive]
|
|
113
|
+
|
|
114
|
+
# The 6530's port B inputs.
|
|
115
|
+
def status_lines
|
|
116
|
+
lines = 0xff
|
|
117
|
+
lines &= ~WRITE_PROTECT unless write_protected?(selected_drive)
|
|
118
|
+
@sides == 2 ? lines & ~ONE_SIDE : lines
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def selected_drive = @rriot.port_b_output & 0x01
|
|
124
|
+
|
|
125
|
+
def selected_side = @sides == 2 && @rriot.port_b_output.nobits?(0x10) ? 1 : 0
|
|
126
|
+
|
|
127
|
+
def write_protected?(drive)
|
|
128
|
+
return true if @changing[drive].positive?
|
|
129
|
+
|
|
130
|
+
disk = @disks[drive]
|
|
131
|
+
disk ? disk.write_protected? : false
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def turn(drive, motor, phase)
|
|
135
|
+
@changing[drive] -= 1 if @changing[drive].positive?
|
|
136
|
+
step(drive, phase) if phase != @phases[drive]
|
|
137
|
+
motor_off(drive) if @motors[drive] && !motor
|
|
138
|
+
@motors[drive] = motor
|
|
139
|
+
return @syncs[drive] = false unless motor && @disks[drive]
|
|
140
|
+
|
|
141
|
+
clock = @clocks[drive] + 6
|
|
142
|
+
byte = 10 * (16 - Disk.zone_of(track(drive)))
|
|
143
|
+
if clock >= byte
|
|
144
|
+
clock -= byte
|
|
145
|
+
pass_cell(drive)
|
|
146
|
+
end
|
|
147
|
+
@clocks[drive] = clock
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Gray code index of a phase pair: 00, 10, 11, 01.
|
|
151
|
+
def phase_index(phase) = [0, 3, 1, 2][phase]
|
|
152
|
+
|
|
153
|
+
def step(drive, phase)
|
|
154
|
+
change = (phase_index(phase) - phase_index(@phases[drive])) & 0x03
|
|
155
|
+
@phases[drive] = phase
|
|
156
|
+
cylinder = @cylinders[drive]
|
|
157
|
+
if change == 1
|
|
158
|
+
cylinder -= 1
|
|
159
|
+
elsif change == 3
|
|
160
|
+
cylinder += 1
|
|
161
|
+
end
|
|
162
|
+
@cylinders[drive] = cylinder.clamp(0, Storage::D80Image::SIDE_TRACKS - 1)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def motor_off(drive)
|
|
166
|
+
@disks[drive]&.flush
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# A cell has passed under +drive+'s head.
|
|
170
|
+
def pass_cell(drive)
|
|
171
|
+
disk = @disks[drive]
|
|
172
|
+
number = track(drive)
|
|
173
|
+
cells = disk.track(number)
|
|
174
|
+
return unless cells
|
|
175
|
+
|
|
176
|
+
position = @positions[drive] % cells.length
|
|
177
|
+
@positions[drive] = position + 1
|
|
178
|
+
return unless drive == selected_drive
|
|
179
|
+
|
|
180
|
+
writing? ? write_cell(drive, disk, number, cells, position) : read_cell(drive, cells, position)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def writing? = (@via.pcr & 0xe0) == 0xc0
|
|
184
|
+
|
|
185
|
+
def read_cell(drive, cells, position)
|
|
186
|
+
cell = cells[position]
|
|
187
|
+
@syncs[drive] = cell == Disk::SYNC
|
|
188
|
+
byte_ready(cell) unless cell == Disk::SYNC
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def write_cell(drive, disk, number, cells, position)
|
|
192
|
+
cells[position] = @via.pcr.allbits?(0x0e) ? Disk::SYNC : @rriot.port_a_output
|
|
193
|
+
disk.written(number)
|
|
194
|
+
@syncs[drive] = false
|
|
195
|
+
byte_ready(@data_in)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def byte_ready(byte)
|
|
199
|
+
@data_in = byte
|
|
200
|
+
@via.ca1 = false
|
|
201
|
+
@ca1_low = true
|
|
202
|
+
@so_pending = true
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# VIA port A reads the decoded byte, and PB7 SYNC, low while the
|
|
207
|
+
# shift register holds a sync.
|
|
208
|
+
class ReadLines
|
|
209
|
+
def initialize(mechanism)
|
|
210
|
+
@mechanism = mechanism
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def read_a(_lines) = @mechanism.data_in
|
|
214
|
+
|
|
215
|
+
def read_b(_lines) = @mechanism.sync? ? 0x7f : 0xff
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# The 6530's port B inputs from the drives.
|
|
219
|
+
class StatusLines
|
|
220
|
+
def initialize(mechanism)
|
|
221
|
+
@mechanism = mechanism
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def read_a(_lines) = 0xff
|
|
225
|
+
|
|
226
|
+
def read_b(_lines) = @mechanism.status_lines
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model8050
|
|
6
|
+
# Each chip a class of its own, so each keeps one type of peripheral.
|
|
7
|
+
|
|
8
|
+
# UC1, on the IEEE-488 data lines.
|
|
9
|
+
class DataRIOT < Chips::RIOT
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# UE1, on the control lines, the jumpers and the LEDs.
|
|
13
|
+
class ControlRIOT < Chips::RIOT
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# UM3, the controller's VIA, at $0040.
|
|
17
|
+
class ControllerVIA < Chips::VIA
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# UK3, the controller's 6530, its ROM the controller's program.
|
|
21
|
+
class ControllerRRIOT < Chips::RRIOT
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The interface processor's 6502, UN1, on the IPBus.
|
|
25
|
+
class IPCPU
|
|
26
|
+
include Badline::Chips::CPU::Core
|
|
27
|
+
include Engine::IdleCPU
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The floppy controller's 6502, UH3, on the FDCBus.
|
|
31
|
+
class FDCCPU
|
|
32
|
+
include Badline::Chips::CPU::Core
|
|
33
|
+
include Engine::IdleCPU
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/chips/riot"
|
|
4
|
+
require "badline/chips/rriot"
|
|
5
|
+
require "badline/drives/ieee_bus"
|
|
6
|
+
require "badline/drives/engine"
|
|
7
|
+
require "badline/drives/model8050/ip_bus"
|
|
8
|
+
require "badline/drives/model8050/fdc_bus"
|
|
9
|
+
require "badline/drives/model8050/parts"
|
|
10
|
+
require "badline/drives/model8050/ieee_port"
|
|
11
|
+
require "badline/drives/model8050/disk"
|
|
12
|
+
require "badline/drives/model8050/mechanism"
|
|
13
|
+
require "badline/drives/model8050/dos_board"
|
|
14
|
+
require "badline/drives/model8050/board"
|
|
15
|
+
require "badline/drives/model8050/idle"
|
|
16
|
+
require "badline/drives/model8050/keyed_state"
|
|
17
|
+
|
|
18
|
+
module Badline
|
|
19
|
+
module Drives
|
|
20
|
+
# The 8050 dual disk drive of the PET and CBM-II, two drives on one
|
|
21
|
+
# IEEE-488 unit (schematic 8050001; the 8050/8250 service manual).
|
|
22
|
+
#
|
|
23
|
+
# Two 6502s share the board (Board), clocked at 1 MHz from one 12 MHz
|
|
24
|
+
# crystal on opposite phases. The interface processor (IP) runs DOS 2.7
|
|
25
|
+
# from 16 KB of ROM at $C000, with two 6532s (IPBus): their RAM is its
|
|
26
|
+
# zero page and stack, and their ports face the IEEE-488 bus
|
|
27
|
+
# (IEEEPort). The floppy controller (FDC) runs the 1 KB program in its
|
|
28
|
+
# 6530's ROM, with a VIA and the 6530 facing the drives (FDCBus,
|
|
29
|
+
# Mechanism). The two meet in 4 KB of common RAM, which each reaches in
|
|
30
|
+
# its own half of the cycle: the IP leaves jobs there, and the FDC runs
|
|
31
|
+
# them.
|
|
32
|
+
#
|
|
33
|
+
# Reset starts both. The FDC waits on the common RAM for the IP, which
|
|
34
|
+
# tests it, copies the FDC's start-up code into it ($C370) and waits for
|
|
35
|
+
# the FDC to answer, before both settle into their idle loops.
|
|
36
|
+
class Model8050
|
|
37
|
+
include Board
|
|
38
|
+
include Idle
|
|
39
|
+
|
|
40
|
+
CLOCK_HZ = 1_000_000
|
|
41
|
+
|
|
42
|
+
# The host cycles by which a unit switched on with the machine has
|
|
43
|
+
# run the IP's RAM test and the FDC's start-up and reads its jumpers.
|
|
44
|
+
BOOT_CYCLES = 1_500_000
|
|
45
|
+
|
|
46
|
+
def model_name = "8050"
|
|
47
|
+
|
|
48
|
+
def boot_cycles = BOOT_CYCLES
|
|
49
|
+
|
|
50
|
+
# The sides each drive's disk has.
|
|
51
|
+
def sides = 1
|
|
52
|
+
|
|
53
|
+
# +dos_rom+ covers $C000-$FFFF and +fdc_rom+ is the 6530's mask, each
|
|
54
|
+
# defaulting to the image in the ROM path: DOS 2.7 and the mask for
|
|
55
|
+
# MPI mechanisms. +device+ is the number the jumpers set, 8 to 15.
|
|
56
|
+
def initialize(dos_rom: nil, fdc_rom: nil, host_clock_hz: CLOCK_HZ, device: 8)
|
|
57
|
+
@device = device
|
|
58
|
+
@host_clock_hz = host_clock_hz
|
|
59
|
+
@phase = 0
|
|
60
|
+
@cycles = 0
|
|
61
|
+
build_ip(dos_rom || ROM.load("8050/dos-2.7.rom", 0xc000))
|
|
62
|
+
build_fdc(fdc_rom || ROM.read("8050/fdc-901869-01.rom"))
|
|
63
|
+
@ip_cpu = IPCPU.new(@ip_bus)
|
|
64
|
+
@fdc_cpu = FDCCPU.new(@fdc_bus)
|
|
65
|
+
init_idle(Idle::IP_LOOP, Idle::FDC_LOOP)
|
|
66
|
+
@ieee_bus.attach(self)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Puts in the .d80 or .d82 image at +path+, write-protected with
|
|
70
|
+
# `read_only`.
|
|
71
|
+
def insert_image(path, drive: 0, read_only: false)
|
|
72
|
+
image_class = File.extname(path).casecmp?(".d82") ? Storage::D82Image : Storage::D80Image
|
|
73
|
+
insert(Disk.new(image_class.new(path, read_only:)), drive:)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def build_ip(rom)
|
|
79
|
+
@common = Memory.new([], length: 0x1000)
|
|
80
|
+
@ieee_bus = IEEEBus.new
|
|
81
|
+
@ieee_port = IEEEPort.new(device: @device, bus: @ieee_bus)
|
|
82
|
+
@data_riot = DataRIOT.new(peripheral: DataLines.new(@ieee_port))
|
|
83
|
+
@control_riot = ControlRIOT.new(peripheral: ControlLines.new(@ieee_port))
|
|
84
|
+
@ieee_port.data_riot = @data_riot
|
|
85
|
+
@ieee_port.control_riot = @control_riot
|
|
86
|
+
@ip_bus = IPBus.new(rom:, common: @common, data_riot: @data_riot, control_riot: @control_riot)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def build_fdc(rom)
|
|
90
|
+
@mechanism = Mechanism.new(sides:)
|
|
91
|
+
@via = ControllerVIA.new(start: 0x0040, peripheral: ReadLines.new(@mechanism))
|
|
92
|
+
@rriot = ControllerRRIOT.new(rom:, peripheral: StatusLines.new(@mechanism))
|
|
93
|
+
@fdc_bus = FDCBus.new(common: @common, via: @via, rriot: @rriot)
|
|
94
|
+
@mechanism.via = @via
|
|
95
|
+
@mechanism.rriot = @rriot
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# The disk a BL-D8-DISK module holds (KeyedState).
|
|
99
|
+
def disk_from_fields(input, detached) = Disk.from_fields(input, detached:)
|
|
100
|
+
|
|
101
|
+
# One cycle of the board: the drives, then the chips, then the IP and
|
|
102
|
+
# the FDC, each in its own half of the cycle. ATN reaches UE1's PA7 as
|
|
103
|
+
# the port latched it. UE1 pulls the IP's IRQ, and the VIA and the
|
|
104
|
+
# 6530 the FDC's. Byte ready sets the FDC's V flag through SO for the
|
|
105
|
+
# next cycle's instruction step, as on the 1541.
|
|
106
|
+
def step
|
|
107
|
+
so = @mechanism.take_so
|
|
108
|
+
@mechanism.cycle!
|
|
109
|
+
@control_riot.pa7 = @ieee_port.atn_low?
|
|
110
|
+
@data_riot.cycle!
|
|
111
|
+
@control_riot.cycle!
|
|
112
|
+
@via.cycle!
|
|
113
|
+
@rriot.cycle!
|
|
114
|
+
@ip_cpu.irq = @control_riot.irq?
|
|
115
|
+
@ip_cpu.cycle!
|
|
116
|
+
@fdc_cpu.irq = @via.irq? || @rriot.irq?
|
|
117
|
+
@fdc_cpu.so! if so
|
|
118
|
+
@fdc_cpu.cycle!
|
|
119
|
+
@cycles += 1
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# The 8250: the 8050's board driving double-sided mechanisms, 154 tracks
|
|
124
|
+
# a disk, with jumper E3 grounding the 6530's PB6 to say so.
|
|
125
|
+
class Model8250 < Model8050
|
|
126
|
+
def model_name = "8250"
|
|
127
|
+
|
|
128
|
+
def sides = 2
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model9060
|
|
6
|
+
# Sleeping through the two processors' idle loops, exactly, as
|
|
7
|
+
# Model8050::Idle does.
|
|
8
|
+
#
|
|
9
|
+
# With nothing to do, the IP goes round DOS 3.0's idle loop from
|
|
10
|
+
# IP_LOOP, and the SASI host round its job loop from SASI_LOOP, each
|
|
11
|
+
# pass finding the job queue empty, until ATN interrupts the IP. The
|
|
12
|
+
# host has no interrupt of its own. A pass puts back what it found, so
|
|
13
|
+
# each processor comes back to its loop's start as it left it.
|
|
14
|
+
#
|
|
15
|
+
# Each time a processor comes to its loop's start, the unit compares
|
|
16
|
+
# what that processor sees with what it saw last time round: the CPU,
|
|
17
|
+
# its chips but for their counters, its RAM and the common RAM. The
|
|
18
|
+
# same, with no read of a counter or an interrupt flag between, makes
|
|
19
|
+
# the pass repeatable. Once both are, with the hard disk's controller
|
|
20
|
+
# idle and no interrupt pulled, the unit sleeps until a counter is due
|
|
21
|
+
# to pull IRQ, the IEEE-488 controller moves a line, or something
|
|
22
|
+
# outside reads or changes the unit (settle!).
|
|
23
|
+
module Idle
|
|
24
|
+
IP_LOOP = 0xc131
|
|
25
|
+
SASI_LOOP = 0xf8f1
|
|
26
|
+
|
|
27
|
+
# Whether the unit may sleep through its idle loops.
|
|
28
|
+
attr_reader :idle_skip
|
|
29
|
+
|
|
30
|
+
def idle_skip=(on)
|
|
31
|
+
settle!
|
|
32
|
+
@idle_skip = on
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def asleep? = @asleep
|
|
36
|
+
|
|
37
|
+
# Brings the unit up to the present, cycle for cycle, and drops the
|
|
38
|
+
# passes it found, since whoever called may change it.
|
|
39
|
+
def settle!
|
|
40
|
+
wake! if @asleep
|
|
41
|
+
forget_passes
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def init_idle
|
|
47
|
+
@idle_skip = true
|
|
48
|
+
@asleep = false
|
|
49
|
+
@owed = 0
|
|
50
|
+
@budget = 0
|
|
51
|
+
forget_passes
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def forget_passes
|
|
55
|
+
@ip_mark = nil
|
|
56
|
+
@sasi_mark = nil
|
|
57
|
+
@ip_pass = nil
|
|
58
|
+
@sasi_pass = nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def owe_cycle
|
|
62
|
+
@owed += 1
|
|
63
|
+
wake! if @owed >= @budget
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def watch_loops
|
|
67
|
+
ip = @ip_cpu
|
|
68
|
+
@ip_pass = nil if ip.irq
|
|
69
|
+
@sasi_pass = nil if @sasi_cpu.irq
|
|
70
|
+
ip_pass_found if ip.program_counter == IP_LOOP && ip.boundary?
|
|
71
|
+
host = @sasi_cpu
|
|
72
|
+
sasi_pass_found if host.program_counter == SASI_LOOP && host.boundary?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def ip_pass_found
|
|
76
|
+
state = ip_state
|
|
77
|
+
@ip_pass = repeated(@ip_mark, state, @ip_cpu, @ip_bus.volatile)
|
|
78
|
+
@ip_mark = [state, @ip_cpu.cycles, @ip_cpu.instructions]
|
|
79
|
+
@ip_bus.volatile = false
|
|
80
|
+
fall_asleep if @ip_pass && @sasi_pass
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def sasi_pass_found
|
|
84
|
+
state = sasi_state
|
|
85
|
+
@sasi_pass = repeated(@sasi_mark, state, @sasi_cpu, @sasi_bus.volatile)
|
|
86
|
+
@sasi_mark = [state, @sasi_cpu.cycles, @sasi_cpu.instructions]
|
|
87
|
+
@sasi_bus.volatile = false
|
|
88
|
+
fall_asleep if @ip_pass && @sasi_pass
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def repeated(mark, state, cpu, volatile)
|
|
92
|
+
return nil if mark.nil? || volatile || mark[0] != state
|
|
93
|
+
|
|
94
|
+
[cpu.cycles - mark[1], cpu.instructions - mark[2]]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def fall_asleep
|
|
98
|
+
target = @target
|
|
99
|
+
return unless target.quiet? && !target.busy? && !@ip_cpu.irq && !@sasi_cpu.irq
|
|
100
|
+
|
|
101
|
+
budget = quiet_cycles
|
|
102
|
+
return if budget < 2
|
|
103
|
+
|
|
104
|
+
@asleep = true
|
|
105
|
+
@owed = 0
|
|
106
|
+
@budget = budget
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def quiet_cycles
|
|
110
|
+
[@data_riot.quiet_cycles, @control_riot.quiet_cycles, @via.quiet_cycles].min
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def wake!
|
|
114
|
+
owed = @owed
|
|
115
|
+
@asleep = false
|
|
116
|
+
@owed = 0
|
|
117
|
+
return if owed.zero?
|
|
118
|
+
|
|
119
|
+
@data_riot.fast_forward(owed)
|
|
120
|
+
@control_riot.fast_forward(owed)
|
|
121
|
+
@via.fast_forward(owed)
|
|
122
|
+
@target.fast_forward(owed)
|
|
123
|
+
skip(@ip_cpu, @ip_pass, @ip_mark, owed)
|
|
124
|
+
skip(@sasi_cpu, @sasi_pass, @sasi_mark, owed)
|
|
125
|
+
@cycles += owed
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def skip(cpu, pass, mark, owed)
|
|
129
|
+
passes = owed / pass[0]
|
|
130
|
+
cpu.fast_forward(passes * pass[0], passes * pass[1])
|
|
131
|
+
mark[1] += passes * pass[0]
|
|
132
|
+
mark[2] += passes * pass[1]
|
|
133
|
+
(owed - (passes * pass[0])).times { cpu.cycle! }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def ip_state
|
|
137
|
+
[*@ip_cpu.idle_state, *@data_riot.idle_state, *@control_riot.idle_state, @ip_bus.data, common_changes]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def sasi_state
|
|
141
|
+
[*@sasi_cpu.idle_state, *@via.idle_state, *@lines.state, *@sasi_bus.ram.read(0, 0x80), @sasi_bus.data,
|
|
142
|
+
common_changes]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def common_changes = @ip_bus.common_changes + @sasi_bus.common_changes
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model9060
|
|
6
|
+
class SASITarget
|
|
7
|
+
# The controller's fields in a keyed snapshot module (BL-D8-SASI):
|
|
8
|
+
# the bus phase, REQ, the cycles left to the next, the command, the
|
|
9
|
+
# bytes of the phase and the place in them, the write under way,
|
|
10
|
+
# the sense bytes, the fast mode, its clock, the heads' cylinder, the
|
|
11
|
+
# geometry and the block buffer, then the image's path and how it
|
|
12
|
+
# was opened. The image itself stays in its file, which each write
|
|
13
|
+
# reached as it ended.
|
|
14
|
+
module KeyedState
|
|
15
|
+
def save_fields(out)
|
|
16
|
+
out.int("phase", @phase).bool("request", @request).int("delay", @delay)
|
|
17
|
+
out.ints("command", @command).ints("buffer", @buffer).int("index", @index)
|
|
18
|
+
out.bool("writing", @writing).ints("sense", @sense).ints("last_sense", @last_sense)
|
|
19
|
+
out.bool("fast", @fast).int("cycle", @cycle).int("cylinder", @cylinder)
|
|
20
|
+
out.int("heads", @heads).int("sectors", @sectors).ints("block_buffer", @block_buffer)
|
|
21
|
+
disk = @disk
|
|
22
|
+
out.text("path", disk.nil? ? "" : disk.path.to_s).bool("read_only", disk.nil? || disk.read_only?)
|
|
23
|
+
out.int("disk_heads", disk.nil? ? 0 : disk.heads)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# A detached reader, or a host file that is gone, leaves the drive
|
|
27
|
+
# empty.
|
|
28
|
+
def load_fields(input, detached: false)
|
|
29
|
+
load_bus_fields(input)
|
|
30
|
+
path = input.text("path", "")
|
|
31
|
+
read_only = input.bool("read_only", false)
|
|
32
|
+
heads = input.int("disk_heads", 4)
|
|
33
|
+
@disk = detached || !File.file?(path) ? nil : Storage::D90Image.new(path, read_only:, heads:)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def load_bus_fields(input)
|
|
39
|
+
@phase = input.int("phase", @phase)
|
|
40
|
+
@request = input.bool("request", @request)
|
|
41
|
+
@delay = input.int("delay", @delay)
|
|
42
|
+
@command = input.ints("command", @command).dup
|
|
43
|
+
@buffer = input.ints("buffer", @buffer).dup
|
|
44
|
+
@index = input.int("index", @index)
|
|
45
|
+
@writing = input.bool("writing", @writing)
|
|
46
|
+
@sense = input.ints("sense", @sense).dup
|
|
47
|
+
@last_sense = input.ints("last_sense", @last_sense).dup
|
|
48
|
+
@fast = input.bool("fast", @fast)
|
|
49
|
+
@cycle = input.int("cycle", @cycle)
|
|
50
|
+
@cylinder = input.int("cylinder", @cylinder)
|
|
51
|
+
@heads = input.int("heads", @heads)
|
|
52
|
+
@sectors = input.int("sectors", @sectors)
|
|
53
|
+
@block_buffer = input.ints("block_buffer", @block_buffer).dup
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
include KeyedState
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The unit's own fields in a keyed snapshot module (BL-D8): its model,
|
|
61
|
+
# its clock phase and cycle count, each bus's last byte, the
|
|
62
|
+
# controller's lines as the port latched them, under ieee.*, and the
|
|
63
|
+
# SASI lines' ACK and RST levels. A save settles the unit first, so it
|
|
64
|
+
# is awake; a load finds its passes afresh.
|
|
65
|
+
module KeyedState
|
|
66
|
+
def save_fields(out)
|
|
67
|
+
settle!
|
|
68
|
+
out.text("model", model_name).int("phase", @phase).int("cycles", @cycles)
|
|
69
|
+
out.int("ip_data", @ip_bus.data).int("sasi_data", @sasi_bus.data)
|
|
70
|
+
out.int("ieee.host", @ieee_port.host)
|
|
71
|
+
ack_high, reset_high = @lines.state
|
|
72
|
+
out.bool("sasi.ack_high", ack_high).bool("sasi.reset_high", reset_high)
|
|
73
|
+
out
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def load_fields(input)
|
|
77
|
+
settle!
|
|
78
|
+
input.choice("model", [model_name], model_name)
|
|
79
|
+
@phase = input.int("phase", @phase)
|
|
80
|
+
@cycles = input.int("cycles", @cycles)
|
|
81
|
+
@ip_bus.data = input.byte("ip_data", @ip_bus.data)
|
|
82
|
+
@sasi_bus.data = input.byte("sasi_data", @sasi_bus.data)
|
|
83
|
+
@ieee_port.host = input.int("ieee.host", @ieee_port.host)
|
|
84
|
+
@lines.state = [input.bool("sasi.ack_high", true), input.bool("sasi.reset_high", false)]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The unit as modules of its own, named from `name` (BL-D8 for
|
|
88
|
+
# device 8): its own fields, the common RAM, UC1 and UE1 with their
|
|
89
|
+
# RAM, each processor, the host's RAM and VIA, and the hard disk's
|
|
90
|
+
# controller.
|
|
91
|
+
def save_modules(sections, name)
|
|
92
|
+
sections << Snapshot::Keyed.section(name) { |out| save_fields(out) }
|
|
93
|
+
sections << Snapshot::Keyed.section("#{name}-COMMON") { |out| @common.save_fields(out, "ram") }
|
|
94
|
+
sections << Snapshot::Keyed.section("#{name}-RIOT1") { |out| @data_riot.save_fields(out) }
|
|
95
|
+
sections << Snapshot::Keyed.section("#{name}-RIOT2") { |out| @control_riot.save_fields(out) }
|
|
96
|
+
sections << Snapshot::Keyed.section("#{name}-IPCPU") { |out| @ip_cpu.save_fields(out) }
|
|
97
|
+
sections << Snapshot::Keyed.section("#{name}-SASICPU") { |out| @sasi_cpu.save_fields(out) }
|
|
98
|
+
sections << Snapshot::Keyed.section("#{name}-SASIRAM") { |out| @sasi_bus.ram.save_fields(out, "ram") }
|
|
99
|
+
sections << Snapshot::Keyed.section("#{name}-VIA") { |out| @via.save_fields(out) }
|
|
100
|
+
sections << Snapshot::Keyed.section("#{name}-SASI") { |out| @target.save_fields(out) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The unit from its modules, `input` being its own.
|
|
104
|
+
def load_modules(modules, input, name)
|
|
105
|
+
load_fields(input)
|
|
106
|
+
@common.load_fields(modules.required("#{name}-COMMON"), "ram")
|
|
107
|
+
@data_riot.load_fields(modules.required("#{name}-RIOT1"))
|
|
108
|
+
@control_riot.load_fields(modules.required("#{name}-RIOT2"))
|
|
109
|
+
@ip_cpu.load_fields(modules.required("#{name}-IPCPU"))
|
|
110
|
+
@sasi_cpu.load_fields(modules.required("#{name}-SASICPU"))
|
|
111
|
+
@sasi_bus.ram.load_fields(modules.required("#{name}-SASIRAM"), "ram")
|
|
112
|
+
@via.load_fields(modules.required("#{name}-VIA"))
|
|
113
|
+
@target.load_fields(modules.required("#{name}-SASI"), detached: modules.detached?)
|
|
114
|
+
@via.restored!
|
|
115
|
+
@ip_cpu.restored!
|
|
116
|
+
@sasi_cpu.restored!
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
include KeyedState
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|