badline 0.16.1 → 0.17.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 +60 -0
- data/CLAUDE.md +1 -1
- data/README.md +102 -12
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +61 -0
- data/doc/snapshot-format.md +376 -69
- data/doc/snapshots.md +148 -14
- 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 +91 -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 +262 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +267 -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/crtc.rb +3 -2
- 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 +80 -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 +132 -0
- 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/drives/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -3
- data/lib/badline/drives/model8050/disk.rb +163 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +177 -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 +160 -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 +243 -0
- data/lib/badline/frontend/app.rb +10 -3
- data/lib/badline/frontend/boot.rb +1 -1
- data/lib/badline/frontend/controls.rb +51 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +61 -2
- data/lib/badline/frontend/media_slots.rb +1 -1
- data/lib/badline/frontend/menu/menu_media.rb +14 -11
- data/lib/badline/frontend/menu/menu_pages.rb +18 -4
- data/lib/badline/frontend/menu/menu_ports.rb +40 -4
- data/lib/badline/frontend/menu/pause_menu.rb +1 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -7
- data/lib/badline/frontend/timeline.rb +19 -10
- 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/machine/ieee_drives.rb +194 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +23 -6
- data/lib/badline/media/cbm2_media.rb +29 -0
- data/lib/badline/media/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +38 -9
- data/lib/badline/media.rb +15 -6
- data/lib/badline/options/family.rb +15 -3
- data/lib/badline/options/help.rb +16 -2
- data/lib/badline/options/table.rb +18 -4
- data/lib/badline/options/validation.rb +27 -3
- data/lib/badline/options.rb +21 -7
- 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.rb +15 -4
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4.rb +1 -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/README +111 -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/snapshot/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +7 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +38 -3
- data/lib/badline/snapshot/image.rb +62 -9
- 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 +7 -3
- data/lib/badline/snapshot/vice/pet_cpu.rb +5 -2
- data/lib/badline/snapshot/vice/pet_crtc.rb +10 -15
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +216 -0
- data/lib/badline/snapshot/vice/pet_restore.rb +70 -6
- 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 +11 -3
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -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 +35 -0
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage.rb +1 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline.rb +1 -0
- metadata +72 -2
|
@@ -2,70 +2,122 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module KernalTrap
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
# The +keep+ of an IEEELineStep that only reads its register.
|
|
6
|
+
IEEE_LINE_READ = -1
|
|
7
|
+
|
|
8
|
+
# One step a KERNAL takes on the IEEE-488 bus's lines: a write of +set+
|
|
9
|
+
# to the register at +address+, or with a +keep+ mask the register read
|
|
10
|
+
# first, its +keep+ bits kept and +set+ ORed in, or with keep
|
|
11
|
+
# IEEE_LINE_READ only a read. Returns the value written, or read.
|
|
12
|
+
IEEELineStep = Data.define(:address, :keep, :set) do
|
|
13
|
+
def act_on(bus)
|
|
14
|
+
return bus.peek(address) if keep == IEEE_LINE_READ
|
|
15
|
+
|
|
16
|
+
value = keep.zero? ? set : (bus.peek(address) & keep) | set
|
|
17
|
+
bus.poke(address, value)
|
|
18
|
+
value
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The steps a KERNAL takes on the bus's lines at each moment the traps
|
|
23
|
+
# stand in for, each run in order (#run_steps), the last one's value
|
|
24
|
+
# returned: +timer+ starts the timer that times out each byte's
|
|
25
|
+
# handshake, +attention+ readies the bus and pulls ATN low for TALK
|
|
26
|
+
# and LISTEN, +byte_sent+ ends a byte sent, +release_attention+ ends
|
|
27
|
+
# SECOND, TKSA, UNTALK and UNLISTEN by letting ATN go, +listen_to_talker+
|
|
28
|
+
# ends TKSA with the computer listening, +byte_received+ ends ACPTR,
|
|
29
|
+
# and +release+ ends UNTALK and UNLISTEN.
|
|
30
|
+
IEEELines = Data.define(:timer, :attention, :byte_sent, :release_attention, :listen_to_talker, :byte_received,
|
|
31
|
+
:release) do
|
|
32
|
+
def run_steps(steps, bus)
|
|
33
|
+
value = 0
|
|
34
|
+
steps.each { |step| value = step.act_on(bus) }
|
|
35
|
+
value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Where a KERNAL keeps the eight IEEE-488 bus primitives, which are
|
|
40
|
+
# internal routines, not jump table entries, the ZeroPage of its file
|
|
41
|
+
# variables, and the steps it takes on the bus's lines (IEEELines).
|
|
42
|
+
# Each ROM set has its own.
|
|
16
43
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
44
|
+
# On a PET, TALK and LISTEN take no device number in A: they send $40
|
|
45
|
+
# or $20 ORed with FA. UNTALK and UNLISTEN send $5F and $3F through the
|
|
46
|
+
# same code, and SECOND, TKSA, UNTALK and UNLISTEN end by letting go of
|
|
47
|
+
# ATN themselves. LOAD and SAVE run in the ROM, through the primitives.
|
|
48
|
+
# +overwrites_device+ says UNTALK and UNLISTEN store their command in
|
|
49
|
+
# FA before they send it. +device_in_a+ says TALK and LISTEN take the
|
|
50
|
+
# device in A, as the CBM-II's do, where a PET's ignore A and read FA.
|
|
23
51
|
IEEELayout = Data.define(:talk, :listen, :second, :tksa, :ciout, :untalk, :unlisten, :acptr, :overwrites_device,
|
|
24
|
-
:zero_page) do
|
|
25
|
-
# The KERNAL is mapped unless
|
|
52
|
+
:device_in_a, :zero_page, :lines) do
|
|
53
|
+
# The KERNAL is mapped unless something lies over it.
|
|
26
54
|
def kernal_mapped?(bus) = bus.kernal_mapped?
|
|
27
55
|
|
|
28
|
-
# Starts
|
|
56
|
+
# Starts the timer that times out the handshake of each byte.
|
|
29
57
|
def time_serial_byte(bus, _timer_high)
|
|
30
|
-
|
|
58
|
+
lines.run_steps(lines.timer, bus)
|
|
31
59
|
end
|
|
32
60
|
|
|
33
|
-
# TALK and LISTEN:
|
|
61
|
+
# TALK and LISTEN: the handshake lines let go, then ATN pulled low.
|
|
34
62
|
def attention(bus)
|
|
35
|
-
|
|
36
|
-
bus.poke(0xe821, 0x3c)
|
|
37
|
-
bus.poke(0xe840, bus.peek(0xe840) & 0xfb)
|
|
63
|
+
lines.run_steps(lines.attention, bus)
|
|
38
64
|
end
|
|
39
65
|
|
|
40
66
|
# The end of a byte sent: DAV and the data lines let go.
|
|
41
67
|
def byte_sent(bus)
|
|
42
|
-
|
|
43
|
-
bus.poke(0xe822, 0xff)
|
|
68
|
+
lines.run_steps(lines.byte_sent, bus)
|
|
44
69
|
end
|
|
45
70
|
|
|
46
71
|
# The end of SECOND, TKSA, UNTALK and UNLISTEN: ATN let go. Returns
|
|
47
72
|
# the value written, which the ROM leaves in A.
|
|
48
73
|
def release_attention(bus)
|
|
49
|
-
|
|
50
|
-
bus.poke(0xe840, value)
|
|
51
|
-
value
|
|
74
|
+
lines.run_steps(lines.release_attention, bus)
|
|
52
75
|
end
|
|
53
76
|
|
|
54
|
-
# TKSA
|
|
77
|
+
# TKSA ends listening: NRFD and NDAC pulled low.
|
|
55
78
|
def listen_to_talker(bus)
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
lines.run_steps(lines.listen_to_talker, bus)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# ACPTR ends listening as well.
|
|
83
|
+
def byte_received(bus)
|
|
84
|
+
lines.run_steps(lines.byte_received, bus)
|
|
58
85
|
end
|
|
59
86
|
|
|
60
87
|
# The end of UNTALK and UNLISTEN, which address the bus as TALK and
|
|
61
|
-
# LISTEN do, send their command and let ATN go.
|
|
88
|
+
# LISTEN do, send their command and let ATN go. Returns the value
|
|
89
|
+
# the ROM leaves in A.
|
|
62
90
|
def release_serial_lines(bus)
|
|
63
|
-
|
|
64
|
-
byte_sent(bus)
|
|
65
|
-
release_attention(bus)
|
|
91
|
+
lines.run_steps(lines.release, bus)
|
|
66
92
|
end
|
|
67
93
|
end
|
|
68
94
|
|
|
95
|
+
# A PET drives the bus's control lines from three chips: ATN from VIA
|
|
96
|
+
# PB2 and NRFD from PB1 at $E840, NDAC from PIA 2's CA2 and DAV from its
|
|
97
|
+
# CB2, through the control registers at $E821 and $E823, which the ROM
|
|
98
|
+
# writes $34 to pull a line low and $3C to let it go, and the data lines
|
|
99
|
+
# from PIA 2's port B at $E822. Each handshake times out on VIA timer 1,
|
|
100
|
+
# which the ROM starts with a write of $FF to $E845. The steps leave the
|
|
101
|
+
# lines as each primitive leaves them.
|
|
102
|
+
PET_ATTENTION = [
|
|
103
|
+
IEEELineStep.new(address: 0xe840, keep: 0xff, set: 0x02),
|
|
104
|
+
IEEELineStep.new(address: 0xe821, keep: 0, set: 0x3c),
|
|
105
|
+
IEEELineStep.new(address: 0xe840, keep: 0xfb, set: 0)
|
|
106
|
+
].freeze
|
|
107
|
+
PET_BYTE_SENT = [
|
|
108
|
+
IEEELineStep.new(address: 0xe823, keep: 0, set: 0x3c),
|
|
109
|
+
IEEELineStep.new(address: 0xe822, keep: 0, set: 0xff)
|
|
110
|
+
].freeze
|
|
111
|
+
PET_RELEASE_ATTENTION = [IEEELineStep.new(address: 0xe840, keep: 0xff, set: 0x04)].freeze
|
|
112
|
+
PET_LISTEN = [IEEELineStep.new(address: 0xe840, keep: 0xfd, set: 0),
|
|
113
|
+
IEEELineStep.new(address: 0xe821, keep: 0, set: 0x34)].freeze
|
|
114
|
+
PET_LINES = IEEELines.new(
|
|
115
|
+
timer: [IEEELineStep.new(address: 0xe845, keep: 0, set: 0xff)].freeze,
|
|
116
|
+
attention: PET_ATTENTION, byte_sent: PET_BYTE_SENT, release_attention: PET_RELEASE_ATTENTION,
|
|
117
|
+
listen_to_talker: PET_LISTEN, byte_received: PET_LISTEN,
|
|
118
|
+
release: (PET_ATTENTION + PET_BYTE_SENT + PET_RELEASE_ATTENTION).freeze
|
|
119
|
+
)
|
|
120
|
+
|
|
69
121
|
# The file variables of the BASIC 2 and BASIC 4 KERNALs, which keep
|
|
70
122
|
# them at the same addresses. SAVE copies STAL at $FB to SAL at $C7 and
|
|
71
123
|
# walks SAL up to EAL at $C9. LOAD keeps no MEMUSS: it stores each byte
|
|
@@ -90,24 +142,24 @@ module Badline
|
|
|
90
142
|
# LISTEN without a secondary address.
|
|
91
143
|
PET_BASIC1_LAYOUT = IEEELayout.new(
|
|
92
144
|
talk: 0xf0b6, listen: 0xf0ba, second: 0xf12c, tksa: 0xf15b,
|
|
93
|
-
ciout: 0xf167, untalk: 0xf17a, unlisten: 0xf17e, acptr: 0xf187, overwrites_device: true,
|
|
94
|
-
zero_page: PET_BASIC1_ZERO_PAGE
|
|
145
|
+
ciout: 0xf167, untalk: 0xf17a, unlisten: 0xf17e, acptr: 0xf187, overwrites_device: true, device_in_a: false,
|
|
146
|
+
zero_page: PET_BASIC1_ZERO_PAGE, lines: PET_LINES
|
|
95
147
|
)
|
|
96
148
|
|
|
97
149
|
# BASIC 2, 901465-03. Its LOAD sends the secondary address after TALK
|
|
98
150
|
# with SECOND, not TKSA.
|
|
99
151
|
PET_BASIC2_LAYOUT = IEEELayout.new(
|
|
100
152
|
talk: 0xf0b6, listen: 0xf0ba, second: 0xf128, tksa: 0xf164,
|
|
101
|
-
ciout: 0xf16f, untalk: 0xf17f, unlisten: 0xf183, acptr: 0xf18c, overwrites_device: false,
|
|
102
|
-
zero_page: PET_ZERO_PAGE
|
|
153
|
+
ciout: 0xf16f, untalk: 0xf17f, unlisten: 0xf183, acptr: 0xf18c, overwrites_device: false, device_in_a: false,
|
|
154
|
+
zero_page: PET_ZERO_PAGE, lines: PET_LINES
|
|
103
155
|
)
|
|
104
156
|
|
|
105
157
|
# BASIC 4, 901465-22. LISTEN at $F0D5 is the operand of the BIT
|
|
106
158
|
# instruction that skips it on the way from TALK.
|
|
107
159
|
PET_BASIC4_LAYOUT = IEEELayout.new(
|
|
108
160
|
talk: 0xf0d2, listen: 0xf0d5, second: 0xf143, tksa: 0xf193,
|
|
109
|
-
ciout: 0xf19e, untalk: 0xf1ae, unlisten: 0xf1b9, acptr: 0xf1c0, overwrites_device: false,
|
|
110
|
-
zero_page: PET_ZERO_PAGE
|
|
161
|
+
ciout: 0xf19e, untalk: 0xf1ae, unlisten: 0xf1b9, acptr: 0xf1c0, overwrites_device: false, device_in_a: false,
|
|
162
|
+
zero_page: PET_ZERO_PAGE, lines: PET_LINES
|
|
111
163
|
)
|
|
112
164
|
end
|
|
113
165
|
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/drives/model2031"
|
|
4
|
+
require "badline/drives/model8050"
|
|
5
|
+
|
|
6
|
+
module Badline
|
|
7
|
+
module Machine
|
|
8
|
+
# A machine's true drive on its IEEE-488 bus: a Drives::Model2031,
|
|
9
|
+
# Model8050 or Model8250, one at a time, clocked after the CPU each
|
|
10
|
+
# cycle at the machine's clock (ieee_host_clock_hz) and reset with it,
|
|
11
|
+
# as the machine's RES line reaches IFC. The traps on the KERNAL's
|
|
12
|
+
# primitives leave device 8 to the drive while it is device 8
|
|
13
|
+
# (trap_device). The PET (PET::IEEEDrive) and the CBM-II
|
|
14
|
+
# (CBM2::IEEEDrive) include it.
|
|
15
|
+
#
|
|
16
|
+
# The machine's @ieee is its side of the bus: #bus, the
|
|
17
|
+
# Drives::IEEEBus, #push_lines, which shows the drives the lines the
|
|
18
|
+
# machine pulls now, and #cycle_lines, which does so once a cycle if
|
|
19
|
+
# the machine doesn't push them as its chips are written.
|
|
20
|
+
module IEEEDrives
|
|
21
|
+
# The 2031 on the bus, or nil.
|
|
22
|
+
attr_reader :drive2031
|
|
23
|
+
|
|
24
|
+
# The 8050 or 8250 on the bus, or nil.
|
|
25
|
+
attr_reader :drive8050
|
|
26
|
+
|
|
27
|
+
# Plugs in a Drives::Model2031, in place of the drive there.
|
|
28
|
+
def attach_drive2031(drive)
|
|
29
|
+
detach_true_drives
|
|
30
|
+
connect_drive(drive)
|
|
31
|
+
@drive2031 = drive
|
|
32
|
+
@ieee_trap&.device = trap_device
|
|
33
|
+
@init_threshold = [@init_threshold, Badline::Drives::Model2031::BOOT_CYCLES].max if @cycles < @init_threshold
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Unplugs the 2031, leaving it on a bus of its own.
|
|
37
|
+
def detach_drive2031
|
|
38
|
+
drive = @drive2031
|
|
39
|
+
return unless drive
|
|
40
|
+
|
|
41
|
+
drive.connect(Badline::Drives::IEEEBus.new)
|
|
42
|
+
@drive2031 = nil
|
|
43
|
+
@ieee_trap&.device = trap_device
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Plugs in a Drives::Model8050 or Model8250, in place of the drive
|
|
47
|
+
# there.
|
|
48
|
+
def attach_drive8050(drive)
|
|
49
|
+
detach_true_drives
|
|
50
|
+
connect_drive(drive)
|
|
51
|
+
@drive8050 = drive
|
|
52
|
+
@ieee_trap&.device = trap_device
|
|
53
|
+
@init_threshold = [@init_threshold, Badline::Drives::Model8050::BOOT_CYCLES].max if @cycles < @init_threshold
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Unplugs the 8050 or 8250, leaving it on a bus of its own.
|
|
57
|
+
def detach_drive8050
|
|
58
|
+
drive = @drive8050
|
|
59
|
+
return unless drive
|
|
60
|
+
|
|
61
|
+
drive.connect(Badline::Drives::IEEEBus.new)
|
|
62
|
+
@drive8050 = nil
|
|
63
|
+
@ieee_trap&.device = trap_device
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Plugs in an 8050, or an 8250 for +model+ "8250", as device 8, in
|
|
67
|
+
# place of the drive there, and returns it.
|
|
68
|
+
def plug_drive8050(model = "8050")
|
|
69
|
+
drive = model == "8250" ? Badline::Drives::Model8250.new : Badline::Drives::Model8050.new
|
|
70
|
+
drive.tap { |unit| attach_drive8050(unit) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The true drive on the bus, or nil.
|
|
74
|
+
def true_drive = @drive8050 || @drive2031
|
|
75
|
+
|
|
76
|
+
# Plugs in a 2031 as device 8, in place of the drive there, and
|
|
77
|
+
# returns it.
|
|
78
|
+
def plug_true_drive
|
|
79
|
+
Badline::Drives::Model2031.new.tap { |drive| attach_drive2031(drive) }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Settles for a snapshot, then runs on until a 2031's CPU, or an
|
|
83
|
+
# 8050's interface processor with its controller at the start of its
|
|
84
|
+
# idle loop, is between instructions with the machine's, as VICE
|
|
85
|
+
# saves them, or for so many cycles, then on to the machine's next
|
|
86
|
+
# instruction. It stands in for Machine::Settling's, which the
|
|
87
|
+
# machines include first.
|
|
88
|
+
def settle_for_vice!
|
|
89
|
+
ran = settle_for_snapshot!
|
|
90
|
+
return ran if @drive2031.nil? && @drive8050.nil?
|
|
91
|
+
|
|
92
|
+
until ran >= Machine::Settling::LIMIT || (drive_settled_for_vice? && settled_for_snapshot?)
|
|
93
|
+
cycle!
|
|
94
|
+
ran += 1
|
|
95
|
+
end
|
|
96
|
+
ran + settle_for_snapshot!
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Whether the true drive is where VICE saves it (#settle_for_vice!).
|
|
100
|
+
def drive_settled_for_vice?
|
|
101
|
+
return @drive2031.cpu.boundary? if @drive2031
|
|
102
|
+
|
|
103
|
+
fdc = @drive8050.fdc_cpu
|
|
104
|
+
@drive8050.ip_cpu.boundary? && fdc.boundary? && fdc.program_counter == Badline::Drives::Model8050::Idle::FDC_LOOP
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def init_ieee_drives
|
|
110
|
+
@drive2031 = nil
|
|
111
|
+
@drive8050 = nil
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def detach_true_drives
|
|
115
|
+
detach_drive2031
|
|
116
|
+
detach_drive8050
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def connect_drive(drive)
|
|
120
|
+
drive.host_clock_hz = ieee_host_clock_hz
|
|
121
|
+
drive.connect(@ieee.bus)
|
|
122
|
+
@ieee.push_lines
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The true drive's cycles for one of the machine's, after the lines
|
|
126
|
+
# it pulls.
|
|
127
|
+
def clock_ieee_drives
|
|
128
|
+
drive2031 = @drive2031
|
|
129
|
+
drive8050 = @drive8050
|
|
130
|
+
return unless drive2031 || drive8050
|
|
131
|
+
|
|
132
|
+
@ieee.cycle_lines
|
|
133
|
+
drive2031&.host_cycle!
|
|
134
|
+
drive8050&.host_cycle!
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# The RES line reaches the true drive as IFC.
|
|
138
|
+
def reset_ieee_drives
|
|
139
|
+
@drive2031&.reset!
|
|
140
|
+
@drive8050&.reset!
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# The true drive as keyed snapshot modules named from its device
|
|
144
|
+
# number: BL-D8, BL-D8-CPU and so on for device 8.
|
|
145
|
+
def save_drive_modules(sections)
|
|
146
|
+
drive2031 = @drive2031
|
|
147
|
+
drive2031&.save_modules(sections, "BL-D#{drive2031.device}")
|
|
148
|
+
drive8050 = @drive8050
|
|
149
|
+
drive8050&.save_modules(sections, "BL-D#{drive8050.device}")
|
|
150
|
+
sections
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# The true drive a snapshot's modules hold, a 2031, an 8050 or an
|
|
154
|
+
# 8250 by its model, built afresh unless the same model is on the bus
|
|
155
|
+
# as the same device, or none when they hold no drive. The bus then
|
|
156
|
+
# carries the machine's lines as its chips left them.
|
|
157
|
+
def load_drive_modules(modules)
|
|
158
|
+
device = 8
|
|
159
|
+
input = modules.optional("BL-D8")
|
|
160
|
+
while input.nil? && device < 15
|
|
161
|
+
device += 1
|
|
162
|
+
input = modules.optional("BL-D#{device}")
|
|
163
|
+
end
|
|
164
|
+
model = input&.text("model", "2031")
|
|
165
|
+
if %w[8050 8250].include?(model)
|
|
166
|
+
load_drive8050_modules(modules, input, model, device)
|
|
167
|
+
elsif input
|
|
168
|
+
load_drive2031_modules(modules, input, device)
|
|
169
|
+
else
|
|
170
|
+
detach_true_drives
|
|
171
|
+
end
|
|
172
|
+
@ieee.push_lines
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def load_drive2031_modules(modules, input, device)
|
|
176
|
+
detach_drive8050
|
|
177
|
+
detach_drive2031 if @drive2031 && @drive2031.device != device
|
|
178
|
+
attach_drive2031(Badline::Drives::Model2031.new(device:)) unless @drive2031
|
|
179
|
+
@drive2031.load_modules(modules, input, "BL-D#{device}")
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def load_drive8050_modules(modules, input, model, device)
|
|
183
|
+
detach_drive2031
|
|
184
|
+
drive = @drive8050
|
|
185
|
+
detach_drive8050 if drive && (drive.device != device || drive.model_name != model)
|
|
186
|
+
unless @drive8050
|
|
187
|
+
unit = model == "8250" ? Badline::Drives::Model8250.new(device:) : Badline::Drives::Model8050.new(device:)
|
|
188
|
+
attach_drive8050(unit)
|
|
189
|
+
end
|
|
190
|
+
@drive8050.load_modules(modules, input, "BL-D#{device}")
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -24,6 +24,10 @@ module Badline
|
|
|
24
24
|
settle_chips_for_snapshot
|
|
25
25
|
ran
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
# Settles as #settle_for_snapshot! does, for a snapshot VICE opens,
|
|
29
|
+
# which a machine with a drive VICE runs settles further.
|
|
30
|
+
def settle_for_vice! = settle_for_snapshot!
|
|
27
31
|
end
|
|
28
32
|
end
|
|
29
33
|
end
|
data/lib/badline/machine.rb
CHANGED
|
@@ -69,20 +69,24 @@ module Badline
|
|
|
69
69
|
# Media the machine named doesn't take.
|
|
70
70
|
class Unsupported < ArgumentError; end
|
|
71
71
|
|
|
72
|
-
# A new machine of `family`, :c64, :vic20, :c128, :plus4 or :
|
|
73
|
-
# model of it `model` names. A C64's model is one of Model::ALL, with
|
|
74
|
-
# `sid_model` when not nil and an REU of `reu` K when not nil.
|
|
75
|
-
# VIC-20's is "pal", with the RAM expansion `ram` names
|
|
72
|
+
# A new machine of `family`, :c64, :vic20, :c128, :plus4, :pet or :cbm2, the
|
|
73
|
+
# model of it `model` names. A C64's model is one of Model::ALL, with
|
|
74
|
+
# its SID `sid_model` when not nil and an REU of `reu` K when not nil.
|
|
75
|
+
# A VIC-20's is "pal", with the RAM expansion `ram` names
|
|
76
76
|
# (Vic20::Bus::RAM_CONFIGURATIONS), unexpanded when nil. A C128's is
|
|
77
77
|
# one of C128::Model::ALL, with its SID `sid_model` when not nil, built
|
|
78
78
|
# for C64 mode. A Plus/4's is one of Plus4::Model::ALL or c116, with
|
|
79
79
|
# the model's own RAM. A PET's is one of PET::Model::ALL, with its own
|
|
80
|
-
# RAM and ROMs, its CRTC's editor the 50 Hz one.
|
|
80
|
+
# RAM and ROMs, its CRTC's editor the 50 Hz one. A CBM-II's is one of
|
|
81
|
+
# CBM2::Model::ALL, with VICE's KERNAL, 901244-04a, at 50 Hz, or a 510,
|
|
82
|
+
# one of CBM2::P500::Model::ALL, with its region's mains.
|
|
81
83
|
def self.build(family, model:, sid_model: nil, reu: nil, ram: nil)
|
|
82
84
|
return vic20(model, ram) if family == :vic20
|
|
83
85
|
return c128(model, sid_model, :c64) if family == :c128
|
|
84
86
|
return Plus4.new(model:) if family == :plus4
|
|
85
87
|
return PET.new(model:) if family == :pet
|
|
88
|
+
return CBM2::P500.new(model:) if family == :cbm2 && model.start_with?("510")
|
|
89
|
+
return CBM2.new(model:) if family == :cbm2
|
|
86
90
|
raise ArgumentError, "no machine family named #{family}" unless family == :c64
|
|
87
91
|
|
|
88
92
|
profile = Model.named(model)
|
|
@@ -98,12 +102,14 @@ module Badline
|
|
|
98
102
|
# only C64 mode runs: a .sid tune or a program that loads at the C64's
|
|
99
103
|
# BASIC start. A VIC-20 without RAM named gets the expansion the media
|
|
100
104
|
# needs (Media::Vic20Media.ram_for) and yields a line saying so. Raises
|
|
101
|
-
# Unsupported for media a VIC-20, a Plus/4 or a
|
|
105
|
+
# Unsupported for media a VIC-20, a Plus/4, a PET or a CBM-II doesn't
|
|
106
|
+
# take.
|
|
102
107
|
def self.for_options(options, media, &)
|
|
103
108
|
family = options.family
|
|
104
109
|
return vic20_for(media, options.model, options.ram, &) if family == :vic20
|
|
105
110
|
return plus4_for(media, options) if family == :plus4
|
|
106
111
|
return pet_for(media, options) if family == :pet
|
|
112
|
+
return cbm2_for(media, options) if family == :cbm2
|
|
107
113
|
|
|
108
114
|
sid_model = options.sid_model || Media.sid_model(media, otherwise: nil)
|
|
109
115
|
return build(:c64, model: options.model, sid_model:, reu: options.reu) unless family == :c128
|
|
@@ -164,6 +170,17 @@ module Badline
|
|
|
164
170
|
end
|
|
165
171
|
private_class_method :pet_for
|
|
166
172
|
|
|
173
|
+
# A CBM-II with the KERNAL --kernal names, on the mains --60hz asks
|
|
174
|
+
# for: a B series' 50 Hz without it, and a P500's its region's.
|
|
175
|
+
def self.cbm2_for(media, options)
|
|
176
|
+
unsupported = !media.nil? && Media::CBM2Media::UNSUPPORTED.include?(File.extname(media).downcase)
|
|
177
|
+
raise Unsupported, "doesn't go in a CBM-II" if unsupported
|
|
178
|
+
return CBM2::P500.new(model: options.model, mains_hz: options.mains_hz) if options.p500?
|
|
179
|
+
|
|
180
|
+
CBM2.new(model: options.model, kernal: options.kernal, mains_hz: options.mains_hz || 50)
|
|
181
|
+
end
|
|
182
|
+
private_class_method :cbm2_for
|
|
183
|
+
|
|
167
184
|
def self.c64_media?(media)
|
|
168
185
|
return false if media.nil? || !File.file?(media)
|
|
169
186
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Media
|
|
5
|
+
# What a CBM-II does with media of its own (Media.attach). Disks of
|
|
6
|
+
# every kind, .d80 and .d82 among them, directories and .t64 archives
|
|
7
|
+
# mount on device 8 through the traps on the KERNAL's IEEE-488
|
|
8
|
+
# primitives (CBM2::IEEETraps), programs load into BASIC's text segment
|
|
9
|
+
# (Program), and a CBM2 .crt's ROMs go in at their load addresses in
|
|
10
|
+
# segment 15. There is no datasette, the SID plays no .sid tunes, and
|
|
11
|
+
# the true drives are serial ones.
|
|
12
|
+
module CBM2Media
|
|
13
|
+
# What a CBM-II doesn't take.
|
|
14
|
+
UNSUPPORTED = Extensions.of(%i[tune tape gcr])
|
|
15
|
+
|
|
16
|
+
# What loads and runs the first program on the disk in device 8.
|
|
17
|
+
AUTOSTART = %(load"*",8\rrun\r)
|
|
18
|
+
|
|
19
|
+
# Where BASIC's text starts in segment 1, and the pointers to its end
|
|
20
|
+
# and to the KERNAL's end address (EAL) in segment 15.
|
|
21
|
+
BASIC_START = 0x0003
|
|
22
|
+
TEXT_END = 0x2f
|
|
23
|
+
EAL = 0x96
|
|
24
|
+
|
|
25
|
+
# The ROM chips of a CBM2 .crt.
|
|
26
|
+
def self.cartridge_chips(path) = Storage::CRTFile.new(path, machine: :cbm2).chips
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Media
|
|
5
|
-
# A program loading into a C64, a C128, a 264 series machine
|
|
6
|
-
# once it has booted (Media.attach). The VIC-20 has its own
|
|
5
|
+
# A program loading into a C64, a C128, a 264 series machine, a PET or
|
|
6
|
+
# a CBM-II once it has booted (Media.attach). The VIC-20 has its own
|
|
7
7
|
# (Vic20Basic).
|
|
8
8
|
module Program
|
|
9
9
|
# Where BASIC 3.5 starts on the 264 series.
|
|
@@ -35,10 +35,13 @@ module Badline
|
|
|
35
35
|
# KERNAL's end address (EAL). BASIC 7.0 starts at $1C01 and keeps
|
|
36
36
|
# the end of its text at $1210, and the 264 KERNAL keeps EAL at $9D.
|
|
37
37
|
# PET BASIC 2 and 4 keep the end of their text at $2A and EAL at
|
|
38
|
-
# $C9, and BASIC 1 at $7C and $E5.
|
|
38
|
+
# $C9, and BASIC 1 at $7C and $E5. The CBM-II's BASIC starts at
|
|
39
|
+
# $0003 of segment 1 and keeps the end of its text at $2F, and its
|
|
40
|
+
# KERNAL EAL at $96, in segment 15.
|
|
39
41
|
def basic(computer)
|
|
40
42
|
return [PLUS4_BASIC_START, 0x2d, KernalTrap::PLUS4_ZERO_PAGE.end_address] if computer.family == :plus4
|
|
41
43
|
return pet_basic(computer) if computer.family == :pet
|
|
44
|
+
return [CBM2Media::BASIC_START, CBM2Media::TEXT_END, CBM2Media::EAL] if computer.family == :cbm2
|
|
42
45
|
return [0x1c01, 0x1210, 0xae] if computer.family == :c128 && computer.mode == :c128
|
|
43
46
|
|
|
44
47
|
[BASIC_START, 0x2d, 0xae]
|
|
@@ -4,19 +4,23 @@ module Badline
|
|
|
4
4
|
module Media
|
|
5
5
|
# Device 8 as a true drive instead of the KERNAL traps: a 1541, or on
|
|
6
6
|
# the C128 its 1571, or on the Plus/4 a 1551 if it asks for one, or a
|
|
7
|
-
# 1581, or on the PET a 2031 on the IEEE-488 bus
|
|
7
|
+
# 1581, or on the PET or the CBM-II a 2031 on the IEEE-488 bus, or for
|
|
8
|
+
# a .d80 or .d82 an 8050 or 8250. Once a true drive is plugged in as device 8, disk media go into
|
|
8
9
|
# it rather than into the traps, so a LOAD runs the DOS and every byte
|
|
9
10
|
# crosses the serial bus or TCBM. A .g64 holds the
|
|
10
11
|
# disk's raw GCR, which only a true drive reads, so it plugs one in
|
|
11
12
|
# when there's none yet. The 1541 reads only .d64 and .g64 images, the
|
|
12
13
|
# 1571 .d71 and .g71 as well, and the 1581 only .d81, so a .d81 swaps
|
|
13
14
|
# the drive on device 8 for a 1581 (Drives::Model1581::Slot), and another disk
|
|
14
|
-
# image swaps a 1581 back for the machine's own drive. The PET
|
|
15
|
-
# 1581, and
|
|
16
|
-
# disk media, a .t64
|
|
15
|
+
# image swaps a 1581 back for the machine's own drive. The PET and the
|
|
16
|
+
# CBM-II have no 1581, and their 2031 reads .d64 and .g64 images only.
|
|
17
|
+
# The traps' other disk media, a .t64 or a host directory, raise Error.
|
|
17
18
|
module TrueDrive
|
|
18
19
|
class Error < ArgumentError; end
|
|
19
20
|
|
|
21
|
+
# The families whose drives sit on the IEEE-488 bus.
|
|
22
|
+
IEEE_FAMILIES = %i[pet cbm2].freeze
|
|
23
|
+
|
|
20
24
|
class << self
|
|
21
25
|
# Plugs the machine's true drive in as device 8, unless one is there
|
|
22
26
|
# already, and returns it.
|
|
@@ -45,9 +49,10 @@ module Badline
|
|
|
45
49
|
# mounted through the traps, so LOAD and SAVE reach the drive too.
|
|
46
50
|
# `read_only` puts it in write-protected.
|
|
47
51
|
def insert(computer, path, read_only: false)
|
|
48
|
-
|
|
49
|
-
return
|
|
52
|
+
swapped = insert_swapping(computer, path, read_only)
|
|
53
|
+
return swapped if swapped
|
|
50
54
|
|
|
55
|
+
pet = IEEE_FAMILIES.include?(computer.family)
|
|
51
56
|
drive = own_drive(computer)
|
|
52
57
|
name = drive&.model_name || default_model(computer)
|
|
53
58
|
types = name == "1571" ? %w[.d64 .g64 .d71 .g71] : %w[.d64 .g64]
|
|
@@ -69,15 +74,39 @@ module Badline
|
|
|
69
74
|
def default_model(computer)
|
|
70
75
|
case computer.family
|
|
71
76
|
when :c128 then "1571"
|
|
72
|
-
when :pet then "2031"
|
|
77
|
+
when :pet, :cbm2 then "2031"
|
|
73
78
|
else "1541"
|
|
74
79
|
end
|
|
75
80
|
end
|
|
76
81
|
|
|
77
|
-
# The drive on device 8 unless it's a 1581
|
|
82
|
+
# The drive on device 8 unless it's a 1581, or an 8050 or 8250,
|
|
83
|
+
# which a .d64 swaps back out.
|
|
78
84
|
def own_drive(computer)
|
|
79
85
|
drive = drive(computer)
|
|
80
|
-
drive unless drive&.model_name
|
|
86
|
+
drive unless %w[1581 8050 8250].include?(drive&.model_name)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# A .d81 in a 1581, and on the PET or the CBM-II a .d80 or .d82 in
|
|
90
|
+
# an 8050 or 8250, each swapped in for the drive there, or nil for
|
|
91
|
+
# the rest.
|
|
92
|
+
def insert_swapping(computer, path, read_only)
|
|
93
|
+
extension = File.extname(path).downcase
|
|
94
|
+
if IEEE_FAMILIES.include?(computer.family)
|
|
95
|
+
insert_d80(computer, path, read_only) if %w[.d80 .d82].include?(extension)
|
|
96
|
+
elsif extension == ".d81"
|
|
97
|
+
insert_d81(computer, path, read_only)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# An 8050 for a .d80 and an 8250 for a .d82, swapped in for the
|
|
102
|
+
# drive on device 8 unless it's that model already.
|
|
103
|
+
def insert_d80(computer, path, read_only)
|
|
104
|
+
model = File.extname(path).casecmp?(".d82") ? "8250" : "8050"
|
|
105
|
+
drive = drive(computer)
|
|
106
|
+
drive = computer.plug_drive8050(model) unless drive&.model_name == model
|
|
107
|
+
drive.insert_image(path, read_only:)
|
|
108
|
+
computer.unmount
|
|
109
|
+
"Inserted #{path} in the #{model} as device 8"
|
|
81
110
|
end
|
|
82
111
|
|
|
83
112
|
def insert_d81(computer, path, read_only)
|
data/lib/badline/media.rb
CHANGED
|
@@ -12,6 +12,7 @@ require "badline/media/vic20_cartridge"
|
|
|
12
12
|
require "badline/media/vic20_media"
|
|
13
13
|
require "badline/media/plus4_media"
|
|
14
14
|
require "badline/media/pet_media"
|
|
15
|
+
require "badline/media/cbm2_media"
|
|
15
16
|
require "badline/media/tune"
|
|
16
17
|
require "badline/media/program"
|
|
17
18
|
|
|
@@ -53,8 +54,9 @@ module Badline
|
|
|
53
54
|
# .crt cartridges and raw .bin ROM images in its two cartridges'
|
|
54
55
|
# slots (Plus4Media), and no .sid tunes either. The PET mounts .d80
|
|
55
56
|
# and .d82 disks too, which no other machine takes, and has no
|
|
56
|
-
# cartridges, tunes or true drives (PETMedia)
|
|
57
|
-
# takes
|
|
57
|
+
# cartridges, tunes or true drives (PETMedia), and so does the CBM-II,
|
|
58
|
+
# which takes its own .crt cartridges and no tapes (CBM2Media). The
|
|
59
|
+
# C128 in C64 mode takes a C64's.
|
|
58
60
|
def attach(computer, path, autostart: true, subtune: nil, **)
|
|
59
61
|
path = DiskList.disk(path)
|
|
60
62
|
refuse_unsupported(computer, path)
|
|
@@ -126,17 +128,19 @@ module Badline
|
|
|
126
128
|
private
|
|
127
129
|
|
|
128
130
|
# The VIC-20 and the 264 series have no SID to play a .sid tune on,
|
|
129
|
-
# and only the PET
|
|
131
|
+
# and only the PET and the CBM-II have the IEEE-488 drive a .d80 or
|
|
132
|
+
# .d82 goes in.
|
|
130
133
|
def refuse_unsupported(computer, path)
|
|
131
134
|
family = computer.family
|
|
132
|
-
if Extensions.kind(path) == :ieee_disk && family
|
|
133
|
-
raise ArgumentError, "#{path} needs a PET's IEEE-488 drive"
|
|
135
|
+
if Extensions.kind(path) == :ieee_disk && !%i[pet cbm2].include?(family)
|
|
136
|
+
raise ArgumentError, "#{path} needs a PET's or a CBM-II's IEEE-488 drive"
|
|
134
137
|
end
|
|
135
138
|
|
|
136
139
|
refused, name = case family
|
|
137
140
|
when :vic20 then [Vic20Media::UNSUPPORTED, "a VIC-20"]
|
|
138
141
|
when :plus4 then [Vic20Media::UNSUPPORTED, "a Plus/4"]
|
|
139
142
|
when :pet then [PETMedia::UNSUPPORTED, "a PET"]
|
|
143
|
+
when :cbm2 then [CBM2Media::UNSUPPORTED, "a CBM-II"]
|
|
140
144
|
else [[], ""]
|
|
141
145
|
end
|
|
142
146
|
raise ArgumentError, "#{path} doesn't go in #{name}" if refused.include?(File.extname(path).downcase)
|
|
@@ -182,7 +186,11 @@ module Badline
|
|
|
182
186
|
# VIC-20 goes by that program, which the block gives
|
|
183
187
|
# (Vic20Media.disk_autostart).
|
|
184
188
|
def disk_autostart(computer)
|
|
185
|
-
computer.family
|
|
189
|
+
case computer.family
|
|
190
|
+
when :vic20 then Vic20Media.disk_autostart(yield)
|
|
191
|
+
when :cbm2 then CBM2Media::AUTOSTART
|
|
192
|
+
else AUTOSTART
|
|
193
|
+
end
|
|
186
194
|
end
|
|
187
195
|
|
|
188
196
|
# A VIC-20 or 264 series cartridge is its ROM chips. On the 264, the
|
|
@@ -194,6 +202,7 @@ module Badline
|
|
|
194
202
|
crt = Storage::CRTFile.new(path, machine: :vic20)
|
|
195
203
|
computer.attach_cartridge(crt.chips, crt.name.empty? ? File.basename(path, File.extname(path)) : crt.name)
|
|
196
204
|
when :plus4 then computer.attach_cartridge(Plus4Media.cartridge_chips(path, slot: options.fetch(:slot, nil)))
|
|
205
|
+
when :cbm2 then computer.attach_cartridge(CBM2Media.cartridge_chips(path))
|
|
197
206
|
else
|
|
198
207
|
computer.attach_cartridge(C64::Cartridge.from_file(path, **options))
|
|
199
208
|
end
|