badline 0.17.0 → 0.19.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 +193 -0
- data/CLAUDE.md +19 -7
- data/CONTRIBUTING.md +11 -1
- data/README.md +178 -26
- data/doc/library.md +5 -1
- data/doc/media.md +3 -2
- data/doc/pinned-behaviour.md +169 -1
- data/doc/snapshot-format.md +302 -35
- data/doc/snapshots.md +97 -26
- data/doc/vice.md +344 -0
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/renderer.rb +4 -8
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +5 -0
- data/lib/badline/c64/bus/fittings.rb +6 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +7 -0
- data/lib/badline/cbm2/keyed_state.rb +4 -0
- data/lib/badline/cbm2/p500.rb +9 -3
- data/lib/badline/cbm2.rb +10 -3
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +29 -4
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/cpu/interrupts.rb +9 -0
- data/lib/badline/chips/cpu/keyed_state.rb +15 -9
- data/lib/badline/chips/cpu/microcode.rb +10 -0
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/timer.rb +14 -3
- data/lib/badline/chips/rriot.rb +8 -3
- data/lib/badline/chips/sid/decimator.rb +1 -1
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/keyed_parts.rb +51 -18
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/sid.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +12 -2
- data/lib/badline/chips/vic_i/light_pen.rb +89 -0
- data/lib/badline/chips/vic_i.rb +91 -23
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model1541/sector_reader.rb +20 -3
- data/lib/badline/drives/model1541.rb +1 -0
- data/lib/badline/drives/model1551.rb +1 -0
- data/lib/badline/drives/model1571.rb +1 -0
- data/lib/badline/drives/model1581.rb +1 -0
- data/lib/badline/drives/model2031.rb +7 -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 +195 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +195 -0
- data/lib/badline/drives/model8050/board.rb +79 -0
- data/lib/badline/drives/model8050/dos_board.rb +134 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +7 -0
- data/lib/badline/drives/model8050/idle.rb +148 -31
- data/lib/badline/drives/model8050/keyed_state.rb +5 -2
- data/lib/badline/drives/model8050.rb +32 -130
- data/lib/badline/drives/model9060/idle.rb +169 -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 +197 -0
- data/lib/badline/frontend/app.rb +8 -0
- data/lib/badline/frontend/boot.rb +3 -2
- data/lib/badline/frontend/controls.rb +62 -6
- data/lib/badline/frontend/keys.rb +36 -0
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +42 -12
- data/lib/badline/frontend/menu/menu_pages.rb +97 -2
- data/lib/badline/frontend/menu/menu_ports.rb +38 -1
- data/lib/badline/frontend/menu/pause_menu.rb +5 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -1
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +31 -2
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/input/joystick.rb +15 -2
- 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 +118 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +194 -0
- data/lib/badline/machine/ieee_drives.rb +94 -117
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine.rb +49 -15
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/true_drive.rb +52 -45
- data/lib/badline/media/vic20_media.rb +4 -2
- data/lib/badline/media.rb +16 -6
- data/lib/badline/memory.rb +2 -4
- data/lib/badline/options/family.rb +98 -8
- data/lib/badline/options/help.rb +44 -12
- data/lib/badline/options/table.rb +44 -12
- data/lib/badline/options/validation.rb +44 -8
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus.rb +16 -2
- data/lib/badline/pet/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +92 -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 +221 -0
- data/lib/badline/pet.rb +23 -3
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +5 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +176 -0
- data/lib/badline/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/container.rb +8 -4
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +35 -10
- data/lib/badline/snapshot/image.rb +19 -7
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/pet.rb +14 -7
- data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
- data/lib/badline/snapshot/vice/pet_dongle.rb +72 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
- data/lib/badline/snapshot/vice/pet_mem.rb +55 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
- data/lib/badline/snapshot/vice/vic20.rb +70 -0
- data/lib/badline/snapshot/vice/vic20_cpu.rb +61 -0
- data/lib/badline/snapshot/vice/vic20_mem.rb +85 -0
- data/lib/badline/snapshot/vice/vic20_restore.rb +124 -0
- data/lib/badline/snapshot/vice/vic20_vici.rb +200 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/snapshot.rb +8 -4
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +4 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20/port_wiring.rb +11 -0
- data/lib/badline/vic20.rb +30 -12
- data/lib/badline.rb +1 -0
- metadata +85 -1
|
@@ -31,6 +31,10 @@ module Badline
|
|
|
31
31
|
# last cleared (Model8050::Idle).
|
|
32
32
|
attr_accessor :volatile
|
|
33
33
|
|
|
34
|
+
# The bytes of the 6530's RAM read or written since it was last
|
|
35
|
+
# cleared, a bit for each pair of them (Model8050::Idle).
|
|
36
|
+
attr_accessor :ram_touched
|
|
37
|
+
|
|
34
38
|
def initialize(common:, via:, rriot:)
|
|
35
39
|
addressable_at(0, length: 2**16)
|
|
36
40
|
@common = common
|
|
@@ -38,6 +42,7 @@ module Badline
|
|
|
38
42
|
@rriot = rriot
|
|
39
43
|
@data = 0
|
|
40
44
|
@volatile = false
|
|
45
|
+
@ram_touched = 0
|
|
41
46
|
@common_changes = 0
|
|
42
47
|
end
|
|
43
48
|
|
|
@@ -78,6 +83,7 @@ module Badline
|
|
|
78
83
|
end
|
|
79
84
|
return peek_via(addr & 0x0f) if addr.anybits?(0x40)
|
|
80
85
|
|
|
86
|
+
@ram_touched |= 1 << ((addr & 0x3f) >> 1)
|
|
81
87
|
@rriot.read_ram(addr)
|
|
82
88
|
end
|
|
83
89
|
|
|
@@ -93,6 +99,7 @@ module Badline
|
|
|
93
99
|
elsif addr.anybits?(0x40)
|
|
94
100
|
@via.poke(0x40 | (addr & 0x0f), value)
|
|
95
101
|
else
|
|
102
|
+
@ram_touched |= 1 << ((addr & 0x3f) >> 1)
|
|
96
103
|
@rriot.write_ram(addr, value)
|
|
97
104
|
end
|
|
98
105
|
end
|
|
@@ -5,10 +5,11 @@ module Badline
|
|
|
5
5
|
class Model8050
|
|
6
6
|
# Sleeping through the two processors' idle loops, exactly.
|
|
7
7
|
#
|
|
8
|
-
# With nothing to do, the IP goes round DOS
|
|
9
|
-
#
|
|
10
|
-
# finding the job queue empty, until
|
|
11
|
-
# 6530's timer the FDC, every 5 ms
|
|
8
|
+
# With nothing to do, the IP goes round its DOS's idle loop from
|
|
9
|
+
# @ip_loop, and the FDC round its job loop from @fdc_loop (IP_LOOP and
|
|
10
|
+
# FDC_LOOP on the 8050), each pass finding the job queue empty, until
|
|
11
|
+
# ATN interrupts the IP or the 6530's timer the FDC, every 5 ms on the
|
|
12
|
+
# 8050. A pass puts back what it found,
|
|
12
13
|
# so each processor comes back to its loop's start as it left it, and
|
|
13
14
|
# only the chips' counters and the cycle counts move.
|
|
14
15
|
#
|
|
@@ -20,10 +21,10 @@ module Badline
|
|
|
20
21
|
# volatile), makes the pass repeatable. Once both processors have a
|
|
21
22
|
# repeatable pass, with the motors off, no head stepping, no disk
|
|
22
23
|
# going in or out and no interrupt pulled, the unit sleeps:
|
|
23
|
-
# host_cycle! only counts
|
|
24
|
-
#
|
|
25
|
-
# line of the bus, or something outside
|
|
26
|
-
# (settle!).
|
|
24
|
+
# host_cycle! only counts host cycles, until the one in which a chip's
|
|
25
|
+
# counter is due to pull IRQ (quiet_cycles), until the controller
|
|
26
|
+
# moves a line of the bus (host_written!), or until something outside
|
|
27
|
+
# reads or changes the unit (settle!).
|
|
27
28
|
#
|
|
28
29
|
# Waking runs the counters on in bulk, counts off each processor's
|
|
29
30
|
# whole passes, and runs the rest of a pass on that processor alone.
|
|
@@ -31,8 +32,21 @@ module Badline
|
|
|
31
32
|
# order the two run in doesn't matter. Then the unit runs cycle by
|
|
32
33
|
# cycle again. Woken by a counter, it keeps the passes it found, and
|
|
33
34
|
# sleeps again as soon as both repeat: the IP's, which the timer's
|
|
34
|
-
# interrupt doesn't reach, at once, and the FDC's
|
|
35
|
-
#
|
|
35
|
+
# interrupt doesn't reach, at once, and the FDC's once it is back at
|
|
36
|
+
# its loop's start where its last pass proved to repeat but for the
|
|
37
|
+
# bytes the interrupt changed (fdc_pass_found). Woken from outside, it
|
|
38
|
+
# finds both afresh.
|
|
39
|
+
#
|
|
40
|
+
# Woken by a counter on the FDC's side, as the 6530's timer wakes the
|
|
41
|
+
# controller every 5 ms, the unit wakes only the FDC's side, as the
|
|
42
|
+
# board does: the FDC with its VIA, its 6530 and the drives run cycle
|
|
43
|
+
# by cycle (fdc_step) through the interrupt, and the IP owes its
|
|
44
|
+
# cycles. The two sides meet only in the common RAM, and the IP's
|
|
45
|
+
# pass reads none of the FDC's chips, so while no write changes the
|
|
46
|
+
# common RAM, the IP's pass runs on as it did. The IP catches up on its
|
|
47
|
+
# passes as the unit falls asleep again, as soon as a write changes
|
|
48
|
+
# the common RAM, before a counter on its side is due to pull its IRQ,
|
|
49
|
+
# and when something outside reads or changes the unit.
|
|
36
50
|
module Idle
|
|
37
51
|
IP_LOOP = 0xc5a1
|
|
38
52
|
FDC_LOOP = 0xfc17
|
|
@@ -47,20 +61,37 @@ module Badline
|
|
|
47
61
|
|
|
48
62
|
def asleep? = @asleep
|
|
49
63
|
|
|
64
|
+
# Whether the unit, or the IP's side of it, owes cycles.
|
|
65
|
+
def dozing? = @asleep || @fdc_alone
|
|
66
|
+
|
|
50
67
|
# Brings the unit up to the present, cycle for cycle, and drops the
|
|
51
68
|
# passes it found, since whoever called may change it.
|
|
52
69
|
def settle!
|
|
53
|
-
|
|
70
|
+
if @asleep
|
|
71
|
+
wake!
|
|
72
|
+
@asleep = false
|
|
73
|
+
end
|
|
74
|
+
join_ip if @fdc_alone
|
|
54
75
|
forget_passes
|
|
55
76
|
end
|
|
56
77
|
|
|
57
78
|
private
|
|
58
79
|
|
|
59
|
-
|
|
80
|
+
# The unit's loops start at +ip_loop+ and +fdc_loop+.
|
|
81
|
+
def init_idle(ip_loop, fdc_loop)
|
|
82
|
+
@ip_loop = ip_loop
|
|
83
|
+
@fdc_loop = fdc_loop
|
|
60
84
|
@idle_skip = true
|
|
61
85
|
@asleep = false
|
|
62
86
|
@owed = 0
|
|
63
87
|
@budget = 0
|
|
88
|
+
@fdc_alone = false
|
|
89
|
+
@ip_owed = 0
|
|
90
|
+
@ip_budget = 0
|
|
91
|
+
@alone_changes = 0
|
|
92
|
+
@planned = false
|
|
93
|
+
@slept = 0
|
|
94
|
+
@wake_at = 0
|
|
64
95
|
forget_passes
|
|
65
96
|
end
|
|
66
97
|
|
|
@@ -69,13 +100,53 @@ module Badline
|
|
|
69
100
|
@fdc_mark = nil
|
|
70
101
|
@ip_pass = nil
|
|
71
102
|
@fdc_pass = nil
|
|
103
|
+
@fdc_proof = nil
|
|
72
104
|
end
|
|
73
105
|
|
|
74
106
|
# A cycle owed while asleep, which wakes the unit once the budget
|
|
75
|
-
# is spent
|
|
107
|
+
# is spent: the FDC's side alone if the IP's counters leave it
|
|
108
|
+
# room.
|
|
76
109
|
def owe_cycle
|
|
77
110
|
@owed += 1
|
|
78
|
-
|
|
111
|
+
return if @owed < @budget
|
|
112
|
+
|
|
113
|
+
wake!
|
|
114
|
+
wake_fdc unless @asleep
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# The FDC's side runs on alone while the IP owes its cycles.
|
|
118
|
+
def wake_fdc
|
|
119
|
+
budget = [@data_riot.quiet_cycles, @control_riot.quiet_cycles].min
|
|
120
|
+
return if budget < 2 || @ip_pass.nil? || @fdc_alone
|
|
121
|
+
|
|
122
|
+
@fdc_alone = true
|
|
123
|
+
@ip_owed = 0
|
|
124
|
+
@ip_budget = budget
|
|
125
|
+
@alone_changes = common_changes
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# A cycle of the FDC's side alone, the IP owing it. A write that
|
|
129
|
+
# changed the common RAM, or the IP's counters coming due, has the
|
|
130
|
+
# IP catch up and the unit run whole again.
|
|
131
|
+
def fdc_alone_cycle
|
|
132
|
+
fdc_step
|
|
133
|
+
@ip_owed += 1
|
|
134
|
+
return join_ip if common_changes != @alone_changes || @ip_owed >= @ip_budget
|
|
135
|
+
|
|
136
|
+
fdc = @fdc_cpu
|
|
137
|
+
@fdc_pass = nil if fdc.irq
|
|
138
|
+
fdc_pass_found if fdc.program_counter == @fdc_loop && fdc.boundary?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Runs the IP's side over the cycles it owes, its counters in bulk
|
|
142
|
+
# and its passes, and the unit whole from then on.
|
|
143
|
+
def join_ip
|
|
144
|
+
owed = @ip_owed
|
|
145
|
+
@fdc_alone = false
|
|
146
|
+
@ip_owed = 0
|
|
147
|
+
@data_riot.fast_forward(owed)
|
|
148
|
+
@control_riot.fast_forward(owed)
|
|
149
|
+
skip(@ip_cpu, @ip_pass, @ip_mark, owed)
|
|
79
150
|
end
|
|
80
151
|
|
|
81
152
|
# Looks for the loops' starts after a cycle run awake. An interrupt
|
|
@@ -84,9 +155,9 @@ module Badline
|
|
|
84
155
|
ip = @ip_cpu
|
|
85
156
|
@ip_pass = nil if ip.irq
|
|
86
157
|
@fdc_pass = nil if @fdc_cpu.irq
|
|
87
|
-
ip_pass_found if ip.program_counter ==
|
|
158
|
+
ip_pass_found if ip.program_counter == @ip_loop && ip.boundary?
|
|
88
159
|
fdc = @fdc_cpu
|
|
89
|
-
fdc_pass_found if fdc.program_counter ==
|
|
160
|
+
fdc_pass_found if fdc.program_counter == @fdc_loop && fdc.boundary?
|
|
90
161
|
end
|
|
91
162
|
|
|
92
163
|
def ip_pass_found
|
|
@@ -97,12 +168,44 @@ module Badline
|
|
|
97
168
|
fall_asleep if @ip_pass && @fdc_pass
|
|
98
169
|
end
|
|
99
170
|
|
|
171
|
+
# The FDC is at its loop's start. A pass that repeated is kept as a
|
|
172
|
+
# proof with the bytes of the 6530's RAM it touched: from a state
|
|
173
|
+
# that differs from the proof's only in other bytes, as the timer's
|
|
174
|
+
# interrupt leaves it, the pass runs the same way and repeats too.
|
|
100
175
|
def fdc_pass_found
|
|
101
|
-
|
|
102
|
-
|
|
176
|
+
registers = fdc_state
|
|
177
|
+
ram = @rriot.ram.dup
|
|
178
|
+
state = [registers, ram]
|
|
179
|
+
bus = @fdc_bus
|
|
180
|
+
touched = bus.ram_touched
|
|
181
|
+
pass = repeated(@fdc_mark, state, @fdc_cpu, bus.volatile)
|
|
182
|
+
if pass
|
|
183
|
+
@fdc_proof = [registers, ram, touched, pass]
|
|
184
|
+
elsif proven?(registers, ram)
|
|
185
|
+
pass = @fdc_proof[3]
|
|
186
|
+
end
|
|
187
|
+
@fdc_pass = pass
|
|
103
188
|
@fdc_mark = [state, @fdc_cpu.cycles, @fdc_cpu.instructions]
|
|
104
|
-
|
|
105
|
-
|
|
189
|
+
bus.volatile = false
|
|
190
|
+
bus.ram_touched = 0
|
|
191
|
+
fall_asleep if @ip_pass && pass
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Whether the FDC stands where the proof's pass started, but for
|
|
195
|
+
# bytes of the 6530's RAM the pass didn't touch.
|
|
196
|
+
def proven?(registers, ram)
|
|
197
|
+
proof = @fdc_proof
|
|
198
|
+
return false if proof.nil? || proof[0] != registers
|
|
199
|
+
|
|
200
|
+
start = proof[1]
|
|
201
|
+
touched = proof[2]
|
|
202
|
+
index = 0
|
|
203
|
+
while index < 64
|
|
204
|
+
return false if touched.anybits?(1 << (index >> 1)) && start[index] != ram[index]
|
|
205
|
+
|
|
206
|
+
index += 1
|
|
207
|
+
end
|
|
208
|
+
true
|
|
106
209
|
end
|
|
107
210
|
|
|
108
211
|
# The pass since +mark+ as [cycles, instructions] if it left the
|
|
@@ -114,6 +217,7 @@ module Badline
|
|
|
114
217
|
end
|
|
115
218
|
|
|
116
219
|
def fall_asleep
|
|
220
|
+
join_ip if @fdc_alone
|
|
117
221
|
return unless @mechanism.quiet? && !@ip_cpu.irq && !@fdc_cpu.irq
|
|
118
222
|
|
|
119
223
|
budget = quiet_cycles
|
|
@@ -132,18 +236,30 @@ module Badline
|
|
|
132
236
|
# Runs the cycles owed: the counters in bulk, then each processor's
|
|
133
237
|
# whole passes and the rest of a pass.
|
|
134
238
|
def wake!
|
|
239
|
+
owe_slept
|
|
135
240
|
owed = @owed
|
|
136
241
|
@asleep = false
|
|
137
242
|
@owed = 0
|
|
138
|
-
|
|
243
|
+
quiet = [owed, @budget].min
|
|
244
|
+
skip_passes(quiet) if quiet.positive?
|
|
245
|
+
(owed - quiet).times { @asleep ? owe_cycle : run_cycle }
|
|
246
|
+
end
|
|
139
247
|
|
|
140
|
-
|
|
141
|
-
@
|
|
142
|
-
@
|
|
143
|
-
@
|
|
144
|
-
|
|
145
|
-
skip(@
|
|
146
|
-
@
|
|
248
|
+
def skip_passes(cycles)
|
|
249
|
+
@data_riot.fast_forward(cycles)
|
|
250
|
+
@control_riot.fast_forward(cycles)
|
|
251
|
+
@rriot.fast_forward(cycles)
|
|
252
|
+
@via.fast_forward(cycles)
|
|
253
|
+
skip(@ip_cpu, @ip_pass, @ip_mark, cycles)
|
|
254
|
+
skip(@fdc_cpu, @fdc_pass, @fdc_mark, cycles)
|
|
255
|
+
@cycles += cycles
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# The budget is spent: the unit wakes, its FDC's side alone if it
|
|
259
|
+
# can.
|
|
260
|
+
def doze
|
|
261
|
+
wake!
|
|
262
|
+
wake_fdc unless @asleep
|
|
147
263
|
end
|
|
148
264
|
|
|
149
265
|
# Counts off the processor's whole passes, moving its mark on with
|
|
@@ -163,10 +279,11 @@ module Badline
|
|
|
163
279
|
[*@ip_cpu.idle_state, *@data_riot.idle_state, *@control_riot.idle_state, @ip_bus.data, common_changes]
|
|
164
280
|
end
|
|
165
281
|
|
|
166
|
-
# What the FDC sees: its CPU, the VIA and the
|
|
167
|
-
# counters, its bus's last
|
|
282
|
+
# What the FDC sees but the 6530's RAM: its CPU, the VIA and the
|
|
283
|
+
# 6530's ports and timer but for their counters, its bus's last
|
|
284
|
+
# byte, the drives and the common RAM.
|
|
168
285
|
def fdc_state
|
|
169
|
-
[*@fdc_cpu.idle_state, *@via.idle_state, *@rriot.
|
|
286
|
+
[*@fdc_cpu.idle_state, *@via.idle_state, *@rriot.port_state, @fdc_bus.data, *@mechanism.idle_state,
|
|
170
287
|
common_changes]
|
|
171
288
|
end
|
|
172
289
|
|
|
@@ -16,7 +16,7 @@ module Badline
|
|
|
16
16
|
def save_fields(out)
|
|
17
17
|
image = @image
|
|
18
18
|
out.text("path", image.path.to_s).bool("read_only", image.read_only?)
|
|
19
|
-
out.text("image.format",
|
|
19
|
+
out.text("image.format", image_format)
|
|
20
20
|
out.bytes("image", image.contents)
|
|
21
21
|
@tracks.each do |number, cells|
|
|
22
22
|
out.ints("track#{number}", cells) unless cells == build(number)
|
|
@@ -37,6 +37,9 @@ module Badline
|
|
|
37
37
|
|
|
38
38
|
include KeyedState
|
|
39
39
|
|
|
40
|
+
# The kind of image the disk lays out, as image.format names it.
|
|
41
|
+
def image_format = @image.is_a?(Storage::D82Image) ? "d82" : "d80"
|
|
42
|
+
|
|
40
43
|
# The disk a BL-D8-DISK module holds. A detached reader gets the
|
|
41
44
|
# image write-protected, and it and a host file that is gone leave
|
|
42
45
|
# it no path to write back to.
|
|
@@ -141,7 +144,7 @@ module Badline
|
|
|
141
144
|
@rriot.load_fields(modules.required("#{name}-RRIOT"))
|
|
142
145
|
disks = [0, 1].map do |drive|
|
|
143
146
|
disk = modules.optional(disk_module(name, drive))
|
|
144
|
-
disk.nil? ? nil :
|
|
147
|
+
disk.nil? ? nil : disk_from_fields(disk, modules.detached?)
|
|
145
148
|
end
|
|
146
149
|
@mechanism.load_fields(modules.required("#{name}-MECH"), disks)
|
|
147
150
|
@via.restored!
|
|
@@ -10,6 +10,8 @@ require "badline/drives/model8050/parts"
|
|
|
10
10
|
require "badline/drives/model8050/ieee_port"
|
|
11
11
|
require "badline/drives/model8050/disk"
|
|
12
12
|
require "badline/drives/model8050/mechanism"
|
|
13
|
+
require "badline/drives/model8050/dos_board"
|
|
14
|
+
require "badline/drives/model8050/board"
|
|
13
15
|
require "badline/drives/model8050/idle"
|
|
14
16
|
require "badline/drives/model8050/keyed_state"
|
|
15
17
|
|
|
@@ -18,25 +20,21 @@ module Badline
|
|
|
18
20
|
# The 8050 dual disk drive of the PET and CBM-II, two drives on one
|
|
19
21
|
# IEEE-488 unit (schematic 8050001; the 8050/8250 service manual).
|
|
20
22
|
#
|
|
21
|
-
# Two 6502s share the board, clocked at 1 MHz from one 12 MHz
|
|
22
|
-
# on opposite phases. The interface processor (IP) runs DOS 2.7
|
|
23
|
-
# 16 KB of ROM at $C000, with two 6532s (IPBus): their RAM is its
|
|
24
|
-
# page and stack, and their ports face the IEEE-488 bus
|
|
25
|
-
# floppy controller (FDC) runs the 1 KB program in its
|
|
26
|
-
# a VIA and the 6530 facing the drives (FDCBus,
|
|
27
|
-
# meet in 4 KB of common RAM, which each reaches in
|
|
28
|
-
# the cycle: the IP leaves jobs there, and the FDC runs
|
|
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.
|
|
29
32
|
#
|
|
30
33
|
# Reset starts both. The FDC waits on the common RAM for the IP, which
|
|
31
34
|
# tests it, copies the FDC's start-up code into it ($C370) and waits for
|
|
32
35
|
# the FDC to answer, before both settle into their idle loops.
|
|
33
|
-
#
|
|
34
|
-
# The drive clocks against the host through a phase accumulator, as
|
|
35
|
-
# Drives::Engine::Core does: each host cycle adds 1 MHz of phase, and
|
|
36
|
-
# every whole host period in it runs a cycle of both processors. While
|
|
37
|
-
# both processors idle, it sleeps instead (Idle), and the readers of its
|
|
38
|
-
# parts catch it up first.
|
|
39
36
|
class Model8050
|
|
37
|
+
include Board
|
|
40
38
|
include Idle
|
|
41
39
|
|
|
42
40
|
CLOCK_HZ = 1_000_000
|
|
@@ -45,37 +43,10 @@ module Badline
|
|
|
45
43
|
# run the IP's RAM test and the FDC's start-up and reads its jumpers.
|
|
46
44
|
BOOT_CYCLES = 1_500_000
|
|
47
45
|
|
|
48
|
-
attr_reader :device, :ieee_bus
|
|
49
|
-
|
|
50
|
-
def ip_cpu = settled(@ip_cpu)
|
|
51
|
-
|
|
52
|
-
def fdc_cpu = settled(@fdc_cpu)
|
|
53
|
-
|
|
54
|
-
def ip_bus = settled(@ip_bus)
|
|
55
|
-
|
|
56
|
-
def fdc_bus = settled(@fdc_bus)
|
|
57
|
-
|
|
58
|
-
def common = settled(@common)
|
|
59
|
-
|
|
60
|
-
def data_riot = settled(@data_riot)
|
|
61
|
-
|
|
62
|
-
def control_riot = settled(@control_riot)
|
|
63
|
-
|
|
64
|
-
def via = settled(@via)
|
|
65
|
-
|
|
66
|
-
def rriot = settled(@rriot)
|
|
67
|
-
|
|
68
|
-
def mechanism = settled(@mechanism)
|
|
69
|
-
|
|
70
|
-
def cycles
|
|
71
|
-
settle!
|
|
72
|
-
@cycles
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
attr_writer :host_clock_hz
|
|
76
|
-
|
|
77
46
|
def model_name = "8050"
|
|
78
47
|
|
|
48
|
+
def boot_cycles = BOOT_CYCLES
|
|
49
|
+
|
|
79
50
|
# The sides each drive's disk has.
|
|
80
51
|
def sides = 1
|
|
81
52
|
|
|
@@ -90,40 +61,13 @@ module Badline
|
|
|
90
61
|
build_ip(dos_rom || ROM.load("8050/dos-2.7.rom", 0xc000))
|
|
91
62
|
build_fdc(fdc_rom || ROM.read("8050/fdc-901869-01.rom"))
|
|
92
63
|
@ip_cpu = IPCPU.new(@ip_bus)
|
|
64
|
+
@ip_cpu.power_on!
|
|
93
65
|
@fdc_cpu = FDCCPU.new(@fdc_bus)
|
|
94
|
-
|
|
66
|
+
@fdc_cpu.power_on!
|
|
67
|
+
init_idle(Idle::IP_LOOP, Idle::FDC_LOOP)
|
|
95
68
|
@ieee_bus.attach(self)
|
|
96
69
|
end
|
|
97
70
|
|
|
98
|
-
# Plugs the unit into an IEEE-488 bus, leaving the one it was on.
|
|
99
|
-
def connect(ieee_bus)
|
|
100
|
-
settle!
|
|
101
|
-
@ieee_bus.detach(self)
|
|
102
|
-
@ieee_bus = ieee_bus
|
|
103
|
-
@ieee_port.bus = ieee_bus
|
|
104
|
-
ieee_bus.attach(self)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# The lines the unit pulls on the bus while ATN is low or not
|
|
108
|
-
# (IEEEPort).
|
|
109
|
-
def ieee_low_lines(atn) = @ieee_port.ieee_low_lines(atn)
|
|
110
|
-
|
|
111
|
-
# The controller pushed its lines into the bus.
|
|
112
|
-
def host_written! = nil
|
|
113
|
-
|
|
114
|
-
# Which LEDs are lit: bit 0 drive 1's, bit 1 drive 0's, bit 2 the
|
|
115
|
-
# error LED. They hold still while the unit sleeps, so reading them
|
|
116
|
-
# leaves it asleep.
|
|
117
|
-
def leds = @ieee_port.leds
|
|
118
|
-
|
|
119
|
-
def led_on? = leds.anybits?(0x03)
|
|
120
|
-
|
|
121
|
-
# Puts a Disk in +drive+, 0 or 1. Nil takes the disk out.
|
|
122
|
-
def insert(disk, drive: 0)
|
|
123
|
-
settle!
|
|
124
|
-
@mechanism.insert(disk, drive)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
71
|
# Puts in the .d80 or .d82 image at +path+, write-protected with
|
|
128
72
|
# `read_only`.
|
|
129
73
|
def insert_image(path, drive: 0, read_only: false)
|
|
@@ -131,65 +75,8 @@ module Badline
|
|
|
131
75
|
insert(Disk.new(image_class.new(path, read_only:)), drive:)
|
|
132
76
|
end
|
|
133
77
|
|
|
134
|
-
# The Disk in +drive+, or nil.
|
|
135
|
-
def disk(drive = 0) = @mechanism.disk(drive)
|
|
136
|
-
|
|
137
|
-
# Stores what the heads wrote in the disks' images, as the motors
|
|
138
|
-
# stopping do.
|
|
139
|
-
def flush
|
|
140
|
-
settle!
|
|
141
|
-
@mechanism.flush
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def power_led_on? = true
|
|
145
|
-
|
|
146
|
-
# Runs the drive cycles that fall in one host cycle, then takes the
|
|
147
|
-
# controller's lines for the next. Asleep, the unit owes the cycles
|
|
148
|
-
# instead, and wakes once the controller has moved a line.
|
|
149
|
-
def host_cycle!
|
|
150
|
-
phase = @phase + CLOCK_HZ
|
|
151
|
-
while phase >= @host_clock_hz
|
|
152
|
-
@asleep ? owe_cycle : run_cycle
|
|
153
|
-
phase -= @host_clock_hz
|
|
154
|
-
end
|
|
155
|
-
@phase = phase
|
|
156
|
-
settle! if @asleep && @ieee_port.lines_moved?
|
|
157
|
-
@ieee_port.latch_host
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def cycle!
|
|
161
|
-
settle!
|
|
162
|
-
run_cycle
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# The bus's IFC line resets both processors and every chip. RAM keeps
|
|
166
|
-
# its contents.
|
|
167
|
-
def reset!
|
|
168
|
-
settle!
|
|
169
|
-
@data_riot.reset!
|
|
170
|
-
@control_riot.reset!
|
|
171
|
-
@via.reset!
|
|
172
|
-
@rriot.reset!
|
|
173
|
-
@ip_cpu.reset!
|
|
174
|
-
@fdc_cpu.reset!
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def inspect
|
|
178
|
-
"#<#{self.class.name} cycles=#{@cycles} ip=(#{@ip_cpu.inspect}) fdc=(#{@fdc_cpu.inspect})>"
|
|
179
|
-
end
|
|
180
|
-
|
|
181
78
|
private
|
|
182
79
|
|
|
183
|
-
def settled(part)
|
|
184
|
-
settle!
|
|
185
|
-
part
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def run_cycle
|
|
189
|
-
step
|
|
190
|
-
watch_loops if @idle_skip
|
|
191
|
-
end
|
|
192
|
-
|
|
193
80
|
def build_ip(rom)
|
|
194
81
|
@common = Memory.new([], length: 0x1000)
|
|
195
82
|
@ieee_bus = IEEEBus.new
|
|
@@ -210,6 +97,9 @@ module Badline
|
|
|
210
97
|
@mechanism.rriot = @rriot
|
|
211
98
|
end
|
|
212
99
|
|
|
100
|
+
# The disk a BL-D8-DISK module holds (KeyedState).
|
|
101
|
+
def disk_from_fields(input, detached) = Disk.from_fields(input, detached:)
|
|
102
|
+
|
|
213
103
|
# One cycle of the board: the drives, then the chips, then the IP and
|
|
214
104
|
# the FDC, each in its own half of the cycle. ATN reaches UE1's PA7 as
|
|
215
105
|
# the port latched it. UE1 pulls the IP's IRQ, and the VIA and the
|
|
@@ -230,6 +120,18 @@ module Badline
|
|
|
230
120
|
@fdc_cpu.cycle!
|
|
231
121
|
@cycles += 1
|
|
232
122
|
end
|
|
123
|
+
|
|
124
|
+
# One cycle of the FDC's side alone, as step runs it (Idle).
|
|
125
|
+
def fdc_step
|
|
126
|
+
so = @mechanism.take_so
|
|
127
|
+
@mechanism.cycle!
|
|
128
|
+
@via.cycle!
|
|
129
|
+
@rriot.cycle!
|
|
130
|
+
@fdc_cpu.irq = @via.irq? || @rriot.irq?
|
|
131
|
+
@fdc_cpu.so! if so
|
|
132
|
+
@fdc_cpu.cycle!
|
|
133
|
+
@cycles += 1
|
|
134
|
+
end
|
|
233
135
|
end
|
|
234
136
|
|
|
235
137
|
# The 8250: the 8050's board driving double-sided mechanisms, 154 tracks
|