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
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Frontend
|
|
5
5
|
# SDL scancodes follow key positions on a US layout. They map onto the C64
|
|
6
|
-
# keys, the C128's and the
|
|
7
|
-
# joystick directions.
|
|
6
|
+
# keys, the C128's, the 264's, the PET's and the CBM-II's own keys and,
|
|
7
|
+
# in joystick mode, the joystick directions.
|
|
8
8
|
module Keys
|
|
9
9
|
RETURN = 40
|
|
10
10
|
ESCAPE = 41
|
|
@@ -118,6 +118,41 @@ module Badline
|
|
|
118
118
|
# The cursor keys, CRSR ⇔ and CRSR ⇕, each with SHIFT for left or up.
|
|
119
119
|
PET_CURSORS = { RIGHT => :cursor_h, LEFT => :cursor_h, DOWN => :cursor_v, UP => :cursor_v }.freeze
|
|
120
120
|
|
|
121
|
+
# The CBM-II's keys for the characters a host key types: those it
|
|
122
|
+
# types without SHIFT, then those it types with it, as the KERNAL's
|
|
123
|
+
# key tables place them. ← is on _ and `, and SHIFT turns it into \.
|
|
124
|
+
CBM2_CHARACTERS = {
|
|
125
|
+
"1" => :"1", "2" => :"2", "3" => :"3", "4" => :"4", "5" => :"5", "6" => :"6", "7" => :"7", "8" => :"8",
|
|
126
|
+
"9" => :"9", "0" => :"0", "-" => :-, "=" => :"=", "[" => :"[", "]" => :"]", ";" => :";", "'" => :"'",
|
|
127
|
+
"," => :",", "." => :".", "/" => :/, "_" => :left_arrow, "`" => :left_arrow
|
|
128
|
+
}.freeze
|
|
129
|
+
|
|
130
|
+
CBM2_SHIFTED = {
|
|
131
|
+
"!" => :"1", "@" => :"2", "#" => :"3", "$" => :"4", "%" => :"5", "^" => :"6", "&" => :"7", "*" => :"8",
|
|
132
|
+
"(" => :"9", ")" => :"0", "+" => :"=", ":" => :";", "\"" => :"'", "<" => :",", ">" => :".", "?" => :/,
|
|
133
|
+
"\\" => :left_arrow
|
|
134
|
+
}.freeze
|
|
135
|
+
|
|
136
|
+
# The CBM-II's keys that don't type a character, which keep the
|
|
137
|
+
# host's SHIFT: RETURN, INST/DEL on Backspace, CLR/HOME on Home, ESC,
|
|
138
|
+
# TAB, RUN/STOP on F12, CTRL on Control, the four cursor keys on the
|
|
139
|
+
# arrows, F1-F8 on F1-F8, and the keypad on the host's.
|
|
140
|
+
CBM2_KEYS = {
|
|
141
|
+
40 => :return, 42 => :delete, 44 => :space, 74 => :clr_home, 41 => :escape, 43 => :tab, 69 => :run_stop,
|
|
142
|
+
224 => :control, 228 => :control, 79 => :cursor_right, 80 => :cursor_left, 81 => :cursor_down,
|
|
143
|
+
82 => :cursor_up, 58 => :f1, 59 => :f2, 60 => :f3, 61 => :f4, 62 => :f5, 63 => :f6, 64 => :f7, 65 => :f8,
|
|
144
|
+
84 => :kp_divide, 85 => :kp_multiply, 86 => :kp_minus, 87 => :kp_plus, 88 => :kp_enter, 89 => :kp1,
|
|
145
|
+
90 => :kp2, 91 => :kp3, 92 => :kp4, 93 => :kp5, 94 => :kp6, 95 => :kp7, 96 => :kp8, 97 => :kp9,
|
|
146
|
+
98 => :kp0, 99 => :kp_period
|
|
147
|
+
}.freeze
|
|
148
|
+
|
|
149
|
+
# The CBM-II's keys that Alt and a host key press: C= on C, CE on E,
|
|
150
|
+
# NORM/GRAPH on G, OFF/RVS on R, π on P, 00 on 0, and F9 and F10 on
|
|
151
|
+
# F9 and F10, which open the pause menu and mute without Alt.
|
|
152
|
+
CBM2_ALT_KEYS = {
|
|
153
|
+
6 => :cbm, 8 => :ce, 10 => :norm_graph, 21 => :off_rvs, 19 => :pi, 39 => :kp00, 66 => :f9, 67 => :f10
|
|
154
|
+
}.freeze
|
|
155
|
+
|
|
121
156
|
ARROWS = { 44 => :fire, 228 => :fire, 79 => :right, 80 => :left, 81 => :down, 82 => :up }.freeze
|
|
122
157
|
WASD = { 225 => :fire, 7 => :right, 4 => :left, 22 => :down, 26 => :up }.freeze
|
|
123
158
|
|
|
@@ -177,6 +212,30 @@ module Badline
|
|
|
177
212
|
shifted.nil? ? %i[none host] : [shifted, :on]
|
|
178
213
|
end
|
|
179
214
|
|
|
215
|
+
# The CBM-II key for the scancode, with the host's SHIFT down if
|
|
216
|
+
# +shift+, and how the CBM-II's SHIFT goes with it, as pet_key gives
|
|
217
|
+
# them.
|
|
218
|
+
def self.cbm2_key(scancode, shift)
|
|
219
|
+
return [LETTERS[scancode - 4], :host] if scancode.between?(4, 29)
|
|
220
|
+
|
|
221
|
+
key = CBM2_KEYS[scancode]
|
|
222
|
+
return [key, :host] unless key.nil?
|
|
223
|
+
|
|
224
|
+
characters = US_CHARACTERS[scancode]
|
|
225
|
+
return %i[none host] if characters.nil?
|
|
226
|
+
|
|
227
|
+
character = characters[shift ? 1 : 0]
|
|
228
|
+
unshifted = CBM2_CHARACTERS[character]
|
|
229
|
+
return [unshifted, :off] unless unshifted.nil?
|
|
230
|
+
|
|
231
|
+
shifted = CBM2_SHIFTED[character]
|
|
232
|
+
shifted.nil? ? %i[none host] : [shifted, :on]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# The CBM-II key Alt and the scancode press, which keeps the host's
|
|
236
|
+
# SHIFT, or :none.
|
|
237
|
+
def self.cbm2_alt_key(scancode) = [CBM2_ALT_KEYS[scancode] || :none, :host]
|
|
238
|
+
|
|
180
239
|
# The character the key types on a US layout: a letter, a capital
|
|
181
240
|
# with `shift`, or a digit, and "" for any other key.
|
|
182
241
|
def self.character(scancode, shift)
|
|
@@ -35,7 +35,7 @@ module Badline
|
|
|
35
35
|
# either cartridge's slots.
|
|
36
36
|
def cartridge?(computer)
|
|
37
37
|
return !computer.cartridge_slots.empty? if computer.family == :plus4
|
|
38
|
-
return false if computer.family
|
|
38
|
+
return false if %i[pet cbm2].include?(computer.family)
|
|
39
39
|
|
|
40
40
|
!computer.address_bus.cartridge.nil?
|
|
41
41
|
end
|
|
@@ -50,6 +50,8 @@ module Badline
|
|
|
50
50
|
Media.insert_disk(@computer, disk, read_only: !@writable)
|
|
51
51
|
list(path)
|
|
52
52
|
else
|
|
53
|
+
raise Machine::Unsupported, "the CBM-II has no datasette" if @computer.family == :cbm2
|
|
54
|
+
|
|
53
55
|
@computer.datasette.insert(Storage::TAP.new(path))
|
|
54
56
|
end
|
|
55
57
|
end
|
|
@@ -100,17 +102,18 @@ module Badline
|
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
# The extensions of the cartridges the machine takes: .crt, on the
|
|
103
|
-
# Plus/4 raw .bin ROM images too, and none on the PET
|
|
105
|
+
# Plus/4 raw .bin ROM images too, and none on the PET, or on the
|
|
106
|
+
# CBM-II, whose cartridge goes in from the command line.
|
|
104
107
|
def cartridge_extensions
|
|
105
|
-
extensions = Media::Extensions.of(@computer.family
|
|
108
|
+
extensions = Media::Extensions.of(%i[pet cbm2].include?(@computer.family) ? %i[none] : %i[cartridge])
|
|
106
109
|
@computer.family == :plus4 ? extensions + [Media::Plus4Media::ROM_IMAGE] : extensions
|
|
107
110
|
end
|
|
108
111
|
|
|
109
|
-
# The extensions of what goes in device 8: on the PET the
|
|
110
|
-
# 8250's disk images besides the others, and no raw
|
|
111
|
-
# a serial true drive reads.
|
|
112
|
+
# The extensions of what goes in device 8: on the PET and the CBM-II
|
|
113
|
+
# the 8050's and 8250's disk images besides the others, and no raw
|
|
114
|
+
# GCR, which only a serial true drive reads.
|
|
112
115
|
def disk_extensions
|
|
113
|
-
return Media::Extensions.of(%i[disk ieee_disk archive disk_list]) if @computer.family
|
|
116
|
+
return Media::Extensions.of(%i[disk ieee_disk archive disk_list]) if %i[pet cbm2].include?(@computer.family)
|
|
114
117
|
|
|
115
118
|
Media::Extensions::DEVICE8
|
|
116
119
|
end
|
|
@@ -125,14 +128,14 @@ module Badline
|
|
|
125
128
|
|
|
126
129
|
# What a save of the machine is named after: the cartridge, the disk,
|
|
127
130
|
# the tape, or BASIC, without the bracketed parts of a file's name. A
|
|
128
|
-
# VIC-20's are named after its disk, or else the machine,
|
|
129
|
-
#
|
|
131
|
+
# VIC-20's are named after its disk, or else the machine, a Plus/4's
|
|
132
|
+
# or a PET's after its disk or tape, and a CBM-II's after its disk.
|
|
130
133
|
def game_name
|
|
131
134
|
vic20 = @computer.family == :vic20
|
|
132
|
-
name = vic20 || %i[plus4 pet].include?(@computer.family) ? "" : cartridge_name
|
|
135
|
+
name = vic20 || %i[plus4 pet cbm2].include?(@computer.family) ? "" : cartridge_name
|
|
133
136
|
disk = disk_path
|
|
134
137
|
name = File.basename(disk, File.extname(disk)) if name.empty? && !disk.empty?
|
|
135
|
-
name = tape_name if name.empty? && !vic20
|
|
138
|
+
name = tape_name if name.empty? && !vic20 && @computer.family != :cbm2
|
|
136
139
|
name = name.sub(/\s*[(\[].*\z/, "").strip
|
|
137
140
|
return name unless name.empty?
|
|
138
141
|
|
|
@@ -154,7 +157,7 @@ module Badline
|
|
|
154
157
|
case @computer.family
|
|
155
158
|
when :plus4 then @computer.cartridge_slots.map { |slot| slot_name(slot) }.uniq.join(", ")
|
|
156
159
|
when :vic20 then @computer.cartridge_name
|
|
157
|
-
when :pet then ""
|
|
160
|
+
when :pet, :cbm2 then ""
|
|
158
161
|
else
|
|
159
162
|
cartridge = @computer.address_bus.cartridge
|
|
160
163
|
cartridge.nil? ? "" : cartridge.name
|
|
@@ -88,6 +88,14 @@ module Badline
|
|
|
88
88
|
# Whether the machine is a PET, whose ports page shows its keyboard.
|
|
89
89
|
def pet? = @computer.family == :pet
|
|
90
90
|
|
|
91
|
+
# Whether the machine is a CBM-II, whose ports page shows its keyboard
|
|
92
|
+
# too.
|
|
93
|
+
def cbm2? = @computer.family == :cbm2
|
|
94
|
+
|
|
95
|
+
# Whether the machine is the CBM-II's B series, whose ports page is
|
|
96
|
+
# its keyboard's alone.
|
|
97
|
+
def b_series? = cbm2? && !Controls.p500?(@computer)
|
|
98
|
+
|
|
91
99
|
private
|
|
92
100
|
|
|
93
101
|
def vic20? = @computer.family == :vic20
|
|
@@ -112,7 +120,10 @@ module Badline
|
|
|
112
120
|
info("DEVICE", drive.nil? ? "FAST LOADING" : "#{drive.model_name}, TRUE DRIVE")
|
|
113
121
|
end
|
|
114
122
|
|
|
123
|
+
# The CBM-II has no datasette.
|
|
115
124
|
def draw_datasette
|
|
125
|
+
return info("TAPE", "") if cbm2?
|
|
126
|
+
|
|
116
127
|
datasette = @computer.datasette
|
|
117
128
|
tape = datasette.tape
|
|
118
129
|
info("TAPE", tape.nil? ? "" : File.basename(tape.path))
|
|
@@ -126,6 +137,7 @@ module Badline
|
|
|
126
137
|
|
|
127
138
|
def draw_expansion
|
|
128
139
|
return draw_pet_memory if pet?
|
|
140
|
+
return info("RAM", "#{@computer.model.ram_kb}K") if cbm2?
|
|
129
141
|
|
|
130
142
|
info("CARTRIDGE", @media.cartridge_name)
|
|
131
143
|
return info("RAM EXPANSION", RAM_NAMES.fetch(@computer.ram_configuration)) if vic20?
|
|
@@ -186,12 +198,14 @@ module Badline
|
|
|
186
198
|
end
|
|
187
199
|
end
|
|
188
200
|
|
|
189
|
-
# The C128 names its model and the mode it runs in first, the Plus/4
|
|
190
|
-
# and the
|
|
191
|
-
# it has one
|
|
201
|
+
# The C128 names its model and the mode it runs in first, the Plus/4,
|
|
202
|
+
# the PET and the CBM-II their model and RAM, the Plus/4 its RAM
|
|
203
|
+
# expansion if it has one, and the CBM-II its KERNAL, the B series'
|
|
204
|
+
# 901244 or the P500's 901234.
|
|
192
205
|
def draw_power
|
|
193
206
|
info("MACHINE", "#{@computer.model.name.upcase}, #{@computer.mode.to_s.upcase} MODE") if c128?
|
|
194
|
-
info("MACHINE", @computer.model_label) if pet?
|
|
207
|
+
info("MACHINE", @computer.model_label.upcase) if pet? || cbm2?
|
|
208
|
+
info("KERNAL", "#{Controls.p500?(@computer) ? 901_234 : 901_244}-#{@computer.kernal.upcase}") if cbm2?
|
|
195
209
|
if plus4?
|
|
196
210
|
info("MACHINE", @computer.model_label)
|
|
197
211
|
expansion = @computer.ram_expansion_label
|
|
@@ -6,8 +6,9 @@ module Badline
|
|
|
6
6
|
# control port has plugged in, a joystick or a pot device, and whether
|
|
7
7
|
# the keys type or drive the joysticks. The VIC-20 has one port, a
|
|
8
8
|
# joystick's, and the Plus/4 two joysticks and no pot devices. The PET
|
|
9
|
-
#
|
|
10
|
-
# instead.
|
|
9
|
+
# and the CBM-II's B series have no ports, and the page, KEYBOARD
|
|
10
|
+
# there, says where their keys are instead. The P500 has two joysticks,
|
|
11
|
+
# and the page says where its keys are while they type.
|
|
11
12
|
module MenuPorts
|
|
12
13
|
PORT_DEVICES = %i[joystick mouse paddles].freeze
|
|
13
14
|
PORT_NAMES = %w[JOY MOUSE PADDLES].freeze
|
|
@@ -21,15 +22,21 @@ module Badline
|
|
|
21
22
|
PET_GRAPHICS_KEYS = ["ESC: RUN/STOP", "SHIFT+LETTER: GRAPHIC", "ALT+SYMBOL: GRAPHIC"].freeze
|
|
22
23
|
PET_BUSINESS_KEYS = ["ESC: ESC", "TAB: TAB", "PAGE UP: REPEAT", "KEYPAD: KEYPAD", "ALT+KEY: SHIFT+KEY"].freeze
|
|
23
24
|
|
|
25
|
+
# Where the CBM-II's keys without a character of their own are.
|
|
26
|
+
CBM2_KEYS = ["ESC: ESC", "F12: RUN/STOP", "HOME: CLR/HOME", "BACKSPACE: INST/DEL", "F1-F8: F1-F8",
|
|
27
|
+
"ALT+F9, ALT+F10: F9, F10", "KEYPAD: KEYPAD", "ALT+0: KEYPAD 00", "ALT+C: C= ALT+E: CE",
|
|
28
|
+
"ALT+G: NORM/GRAPH", "ALT+R: OFF/RVS ALT+P: PI", "_: LEFT ARROW"].freeze
|
|
29
|
+
|
|
24
30
|
private
|
|
25
31
|
|
|
26
32
|
def draw_ports
|
|
27
33
|
return draw_vic20_port if @computer.family == :vic20
|
|
28
34
|
return draw_plus4_ports if @computer.family == :plus4
|
|
29
35
|
return draw_pet_keys if @computer.family == :pet
|
|
36
|
+
return draw_p500_ports if Controls.p500?(@computer)
|
|
37
|
+
return draw_cbm2_keys if @computer.family == :cbm2
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
toggle("PORT 2", :port2, [PORT_NAMES, PORT_ACTIONS[1], PORT_DEVICES.index(port_device(2))])
|
|
39
|
+
draw_pot_ports
|
|
33
40
|
return unless draw_keys(@computer.family == :c128 ? "C128" : "C64")
|
|
34
41
|
|
|
35
42
|
row("SWAP JOYSTICKS", :swap)
|
|
@@ -70,6 +77,35 @@ module Badline
|
|
|
70
77
|
end
|
|
71
78
|
end
|
|
72
79
|
|
|
80
|
+
# What each control port has plugged in, a joystick, a mouse or
|
|
81
|
+
# paddles.
|
|
82
|
+
def draw_pot_ports
|
|
83
|
+
toggle("PORT 1", :port1, [PORT_NAMES, PORT_ACTIONS[0], PORT_DEVICES.index(port_device(1))])
|
|
84
|
+
toggle("PORT 2", :port2, [PORT_NAMES, PORT_ACTIONS[1], PORT_DEVICES.index(port_device(2))])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The P500's two ports, its joysticks one for each set of keys, and
|
|
88
|
+
# while the keys type, where its keys are.
|
|
89
|
+
def draw_p500_ports
|
|
90
|
+
draw_pot_ports
|
|
91
|
+
return draw_cbm2_keys unless draw_keys("P500")
|
|
92
|
+
|
|
93
|
+
row("SWAP JOYSTICKS", :swap)
|
|
94
|
+
skip
|
|
95
|
+
arrows = @controls.arrows_port
|
|
96
|
+
info("ARROWS, SPACE", "JOYSTICK #{arrows}")
|
|
97
|
+
info("WASD, LEFT SHIFT", "JOYSTICK #{3 - arrows}")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The CBM-II's keyboard, which types as the PET's does, and the keys
|
|
101
|
+
# that stand for the rest of its keys.
|
|
102
|
+
def draw_cbm2_keys
|
|
103
|
+
CBM2_KEYS.each do |line|
|
|
104
|
+
@painter.text(@left, @top, line, MenuTheme::TEXT)
|
|
105
|
+
@top += MenuPages::LINE
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
73
109
|
# The toggle between the keyboard named and the joysticks, and
|
|
74
110
|
# whether the keys drive the joysticks.
|
|
75
111
|
def draw_keys(keyboard)
|
|
@@ -238,7 +238,7 @@ module Badline
|
|
|
238
238
|
def draw_sections
|
|
239
239
|
top = @top + 32
|
|
240
240
|
SECTIONS.each_with_index do |name, index|
|
|
241
|
-
name = "KEYBOARD" if PAGES[index] == :ports && @pages.pet?
|
|
241
|
+
name = "KEYBOARD" if PAGES[index] == :ports && (@pages.pet? || @pages.b_series?)
|
|
242
242
|
@buttons.plain(@left + 6, top, name.ljust(14), PAGES[index], on: index == @section)
|
|
243
243
|
top += Buttons::HEIGHT + 2
|
|
244
244
|
end
|
|
@@ -12,16 +12,22 @@ module Badline
|
|
|
12
12
|
# With either Alt down, a key that types a character presses the PET
|
|
13
13
|
# key its unshifted character is on with the PET's SHIFT, so the
|
|
14
14
|
# graphics on the graphics keyboard's symbol keys can be typed.
|
|
15
|
+
#
|
|
16
|
+
# The CBM-II's keyboard goes the same way (Keys.cbm2_key), and there
|
|
17
|
+
# Alt with a key presses one of the keys the host has none for
|
|
18
|
+
# (Keys.cbm2_alt_key).
|
|
15
19
|
class PETKeyboard
|
|
16
20
|
LSHIFT = 225
|
|
17
21
|
RSHIFT = 229
|
|
18
22
|
LALT = 226
|
|
19
23
|
RALT = 230
|
|
20
24
|
|
|
21
|
-
# +layout+ is the PET's keyboard, :graphics or :business
|
|
25
|
+
# +layout+ is the PET's keyboard, :graphics or :business, or :cbm2
|
|
26
|
+
# for the CBM-II's.
|
|
22
27
|
def initialize(keyboard, layout)
|
|
23
28
|
@keyboard = keyboard
|
|
24
29
|
@layout = layout
|
|
30
|
+
@shift = layout == :cbm2 ? :shift : :lshift
|
|
25
31
|
@held = {}
|
|
26
32
|
@left_shift = false
|
|
27
33
|
@right_shift = false
|
|
@@ -43,6 +49,11 @@ module Badline
|
|
|
43
49
|
end
|
|
44
50
|
end
|
|
45
51
|
|
|
52
|
+
# Whether a key the scancode pressed is still down.
|
|
53
|
+
def holds?(scancode) = @held.key?(scancode)
|
|
54
|
+
|
|
55
|
+
def alt? = @left_alt || @right_alt
|
|
56
|
+
|
|
46
57
|
# Lets go of every key, SHIFT among them.
|
|
47
58
|
def release_all
|
|
48
59
|
@held.each_value { |key| @keyboard.release(key) }
|
|
@@ -59,7 +70,7 @@ module Badline
|
|
|
59
70
|
|
|
60
71
|
def key_down(scancode)
|
|
61
72
|
key_up(scancode) if @held.key?(scancode)
|
|
62
|
-
key, shift = alt? ? shifted_key(scancode) :
|
|
73
|
+
key, shift = alt? ? shifted_key(scancode) : layout_key(scancode, shifted?)
|
|
63
74
|
return if key == :none
|
|
64
75
|
|
|
65
76
|
unless shift == :host
|
|
@@ -99,10 +110,17 @@ module Badline
|
|
|
99
110
|
end
|
|
100
111
|
end
|
|
101
112
|
|
|
113
|
+
def layout_key(scancode, shift)
|
|
114
|
+
@layout == :cbm2 ? Keys.cbm2_key(scancode, shift) : Keys.pet_key(scancode, shift, @layout)
|
|
115
|
+
end
|
|
116
|
+
|
|
102
117
|
# The PET key for the scancode with Alt down: for a letter or a key
|
|
103
118
|
# that types a character, the key its unshifted character is on, with
|
|
104
|
-
# SHIFT, and for any other key the one it presses without Alt.
|
|
119
|
+
# SHIFT, and for any other key the one it presses without Alt. The
|
|
120
|
+
# CBM-II's is the one Keys.cbm2_alt_key names.
|
|
105
121
|
def shifted_key(scancode)
|
|
122
|
+
return Keys.cbm2_alt_key(scancode) if @layout == :cbm2
|
|
123
|
+
|
|
106
124
|
key, shift = Keys.pet_key(scancode, false, @layout)
|
|
107
125
|
return [key, shift] if key == :none
|
|
108
126
|
return [key, shift] unless scancode.between?(4, 29) || Keys::US_CHARACTERS.key?(scancode)
|
|
@@ -112,12 +130,10 @@ module Badline
|
|
|
112
130
|
|
|
113
131
|
def shifted? = @left_shift || @right_shift
|
|
114
132
|
|
|
115
|
-
def alt? = @left_alt || @right_alt
|
|
116
|
-
|
|
117
133
|
def sync_shift
|
|
118
134
|
shift = @forced == -1 ? shifted? : @forced_shift
|
|
119
|
-
@keyboard.release(
|
|
120
|
-
@keyboard.press(
|
|
135
|
+
@keyboard.release(@shift)
|
|
136
|
+
@keyboard.press(@shift) if shift
|
|
121
137
|
end
|
|
122
138
|
end
|
|
123
139
|
end
|
|
@@ -25,7 +25,8 @@ module Badline
|
|
|
25
25
|
LOCKS = %w[caps_lock forty_eighty].freeze
|
|
26
26
|
|
|
27
27
|
# What's wrong with the events for the family's machine, the first
|
|
28
|
-
# key or menu page that names nothing, or an empty string.
|
|
28
|
+
# key or menu page that names nothing, or an empty string. The
|
|
29
|
+
# CBM-II's P500 is family :p500 here, for its joysticks.
|
|
29
30
|
def self.error(events, family)
|
|
30
31
|
event = events.find { |candidate| candidate.action == "key" && !key?(candidate.argument, family) }
|
|
31
32
|
return "no such key: #{event.argument}" unless event.nil?
|
|
@@ -38,23 +39,26 @@ module Badline
|
|
|
38
39
|
# sound. An empty one is its first.
|
|
39
40
|
def self.page?(name) = name.empty? || PauseMenu::PAGES.any? { |page| page.to_s == name }
|
|
40
41
|
|
|
41
|
-
# Whether the family's machine has a key of the name
|
|
42
|
-
# no RESTORE,
|
|
42
|
+
# Whether the family's machine has a key of the name, the CBM-II's
|
|
43
|
+
# P500's as family :p500. The Plus/4 has no RESTORE, the P500 none
|
|
44
|
+
# either, and the PET and the CBM-II's B series neither that nor
|
|
45
|
+
# joysticks.
|
|
43
46
|
def self.key?(name, family)
|
|
44
|
-
pet = family
|
|
45
|
-
(name == "restore" && !pet && family
|
|
47
|
+
pet = %i[pet cbm2].include?(family)
|
|
48
|
+
(name == "restore" && !pet && !%i[plus4 p500].include?(family)) || (family == :c128 && LOCKS.include?(name)) ||
|
|
46
49
|
!keyboard_key(name, family).nil? || (!pet && !joystick_direction(name).nil?)
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
# The family's Keyboard key for a name such as space, return or a,
|
|
50
53
|
# on the C128 esc or keypad1, on the Plus/4 help or crsr_up, or on
|
|
51
|
-
# the PET rvs or kp1, of either of its keyboards
|
|
52
|
-
#
|
|
54
|
+
# the PET rvs or kp1, of either of its keyboards, or on the CBM-II
|
|
55
|
+
# escape, norm_graph or kp00. The shifted cursor keys, such as
|
|
56
|
+
# cursor_up, are the C64's and the C128's.
|
|
53
57
|
def self.keyboard_key(name, family)
|
|
54
58
|
return nil if name == "none"
|
|
55
59
|
|
|
56
60
|
key = keyboard_matrix(family).flatten.find { |candidate| candidate.to_s == name }
|
|
57
|
-
return key unless key.nil? && !%i[plus4 pet].include?(family)
|
|
61
|
+
return key unless key.nil? && !%i[plus4 pet cbm2 p500].include?(family)
|
|
58
62
|
|
|
59
63
|
Input::Keyboard::COMBINATIONS.keys.find { |candidate| candidate.to_s == name }
|
|
60
64
|
end
|
|
@@ -64,6 +68,7 @@ module Badline
|
|
|
64
68
|
when :c128 then C128::KEYBOARD_MATRIX
|
|
65
69
|
when :plus4 then Plus4::KeyboardPort::MATRIX
|
|
66
70
|
when :pet then PET::KeyboardPorts::GRAPHICS_MATRIX + PET::KeyboardPorts::BUSINESS_MATRIX
|
|
71
|
+
when :cbm2, :p500 then CBM2::KeyboardPorts::MATRIX
|
|
67
72
|
else Input::Keyboard::C64_MATRIX
|
|
68
73
|
end
|
|
69
74
|
end
|
|
@@ -228,9 +233,13 @@ module Badline
|
|
|
228
233
|
end
|
|
229
234
|
|
|
230
235
|
# Whether there's one in to eject. A VIC-20 has no cartridge to take
|
|
231
|
-
# out yet,
|
|
236
|
+
# out yet, a PET no expansion port, and a CBM-II no datasette, and
|
|
237
|
+
# its cartridge stays in.
|
|
232
238
|
def ejectable?(computer, what)
|
|
233
|
-
|
|
239
|
+
family = computer.family
|
|
240
|
+
return false if family == :cbm2 && what != "disk"
|
|
241
|
+
|
|
242
|
+
(what != "cartridge" || !%i[vic20 pet].include?(family)) && inserted?(computer, what)
|
|
234
243
|
end
|
|
235
244
|
|
|
236
245
|
def inserted?(computer, what)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module KernalTrap
|
|
5
|
+
class DOS
|
|
6
|
+
# N, R and C: the commands that format a disk, rename a file and copy
|
|
7
|
+
# files. Each takes its names after a colon. A name can't hold a
|
|
8
|
+
# wildcard, and R and C take their new name before an "=". The DOS
|
|
9
|
+
# here has one drive, so a file on drive 1 is never found, and C
|
|
10
|
+
# without a colon, which copies a whole disk between the drives of a
|
|
11
|
+
# dual drive, and D, which duplicates one, are invalid commands, as on
|
|
12
|
+
# a single drive.
|
|
13
|
+
module DiskCommands
|
|
14
|
+
WILDCARD = /[*?]/
|
|
15
|
+
OTHER_DRIVE = /\A[1-9]\d*:/
|
|
16
|
+
DRIVE_ZERO = /\A0+:/
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# N with an ID after a comma formats the disk, and without one only
|
|
21
|
+
# clears it. The ID is the two bytes after the comma, which takes the
|
|
22
|
+
# carriage return PRINT# ends the command with when the ID is one
|
|
23
|
+
# character. A comma with nothing after it gives no ID.
|
|
24
|
+
def new_disk(text)
|
|
25
|
+
colon = text.index(":")
|
|
26
|
+
return report(NO_FILE_GIVEN) unless colon
|
|
27
|
+
|
|
28
|
+
command = text.chomp("\r")
|
|
29
|
+
name, id = command[(colon + 1)..].split(",", 2)
|
|
30
|
+
return report(SYNTAX_ERROR) if name.to_s.match?(/[*?=]/)
|
|
31
|
+
return report(WRITE_PROTECT_ON) unless @storage.respond_to?(:format)
|
|
32
|
+
|
|
33
|
+
id = id.to_s.empty? ? nil : text.bytes[(command.index(",") + 1), 2]
|
|
34
|
+
writing { @storage.format(name.to_s, id && (id + [Storage::DiskImage::NAME_PADDING]).first(2)) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# R takes the new name, then the file's.
|
|
38
|
+
def rename_file(text)
|
|
39
|
+
new_name, old_names = file_names(text, NO_FILE_GIVEN)
|
|
40
|
+
return unless new_name
|
|
41
|
+
return report(SYNTAX_ERROR) unless old_names.one?
|
|
42
|
+
return report(WRITE_PROTECT_ON) unless @storage.respond_to?(:rename)
|
|
43
|
+
return report(FILE_NOT_FOUND) if old_names.first.match?(OTHER_DRIVE)
|
|
44
|
+
|
|
45
|
+
writing { @storage.rename(Storage.strip_drive_prefix(old_names.first), new_name) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# C takes the new name, then the files it joins, separated by
|
|
49
|
+
# commas. A first file named with drive 0 that is the new file
|
|
50
|
+
# itself has the others added to its end.
|
|
51
|
+
def copy_file(text)
|
|
52
|
+
new_name, old_names = file_names(text, INVALID_COMMAND)
|
|
53
|
+
return unless new_name
|
|
54
|
+
return report(WRITE_PROTECT_ON) unless @storage.respond_to?(:copy)
|
|
55
|
+
return report(FILE_NOT_FOUND) if old_names.any? { |name| name.match?(OTHER_DRIVE) }
|
|
56
|
+
|
|
57
|
+
names = old_names.map { |name| Storage.strip_drive_prefix(name) }
|
|
58
|
+
in_place = old_names.first.match?(DRIVE_ZERO) && names.first == new_name
|
|
59
|
+
writing { in_place ? @storage.concat(new_name, names.drop(1)) : @storage.copy(new_name, names) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The new name and the old ones of "new=old,...", or nil once it
|
|
63
|
+
# has reported what's wrong.
|
|
64
|
+
def file_names(text, missing_colon)
|
|
65
|
+
colon = text.index(":")
|
|
66
|
+
return report(missing_colon) unless colon
|
|
67
|
+
|
|
68
|
+
new_name, rest = text[(colon + 1)..].split("=", 2)
|
|
69
|
+
new_name = new_name.to_s
|
|
70
|
+
return report(SYNTAX_ERROR) if rest.nil? || new_name.include?(",")
|
|
71
|
+
|
|
72
|
+
old_names = rest.empty? ? [""] : rest.split(",", -1)
|
|
73
|
+
return report(SYNTAX_ERROR) if [new_name, *old_names].any? { |name| name.match?(WILDCARD) }
|
|
74
|
+
|
|
75
|
+
[new_name, old_names]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -15,8 +15,11 @@ module Badline
|
|
|
15
15
|
28 => "WRITE ERROR",
|
|
16
16
|
29 => "DISK ID MISMATCH",
|
|
17
17
|
30 => "SYNTAX ERROR",
|
|
18
|
+
31 => "SYNTAX ERROR",
|
|
19
|
+
34 => "SYNTAX ERROR",
|
|
18
20
|
62 => "FILE NOT FOUND",
|
|
19
21
|
63 => "FILE EXISTS",
|
|
22
|
+
64 => "FILE TYPE MISMATCH",
|
|
20
23
|
65 => "NO BLOCK",
|
|
21
24
|
66 => "ILLEGAL TRACK OR SECTOR",
|
|
22
25
|
70 => "NO CHANNEL",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "badline/kernal_trap/dos/block_commands"
|
|
4
4
|
require "badline/kernal_trap/dos/channel"
|
|
5
5
|
require "badline/kernal_trap/dos/channels"
|
|
6
|
+
require "badline/kernal_trap/dos/disk_commands"
|
|
6
7
|
require "badline/kernal_trap/dos/memory"
|
|
7
8
|
require "badline/kernal_trap/dos/parameters"
|
|
8
9
|
require "badline/kernal_trap/dos/status"
|
|
@@ -15,10 +16,12 @@ module Badline
|
|
|
15
16
|
# command channel and its status message. `U1` reads a raw block into a
|
|
16
17
|
# buffer channel, which is how block-access loaders bypass the KERNAL's
|
|
17
18
|
# LOAD. A disk image whose host file takes writes takes files, blocks
|
|
18
|
-
# and BAM changes too
|
|
19
|
-
#
|
|
19
|
+
# and BAM changes too, and N, R and C format it, rename and copy files.
|
|
20
|
+
# Other disks are write-protected, though storage that can write files
|
|
21
|
+
# still takes a SAVE handed over whole.
|
|
20
22
|
class DOS
|
|
21
23
|
include BlockCommands
|
|
24
|
+
include DiskCommands
|
|
22
25
|
include Writes
|
|
23
26
|
|
|
24
27
|
COMMAND_CHANNEL = 15
|
|
@@ -28,6 +31,8 @@ module Badline
|
|
|
28
31
|
FILES_SCRATCHED = 1
|
|
29
32
|
WRITE_PROTECT_ON = 26
|
|
30
33
|
SYNTAX_ERROR = 30
|
|
34
|
+
INVALID_COMMAND = 31
|
|
35
|
+
NO_FILE_GIVEN = 34
|
|
31
36
|
FILE_NOT_FOUND = 62
|
|
32
37
|
FILE_EXISTS = 63
|
|
33
38
|
NO_BLOCK = 65
|
|
@@ -60,11 +65,15 @@ module Badline
|
|
|
60
65
|
/\AV/i => :initialized,
|
|
61
66
|
/\AB-A\s*:?\s*(.*)/i => :block_allocate,
|
|
62
67
|
/\AB-F\s*:?\s*(.*)/i => :block_free,
|
|
63
|
-
/\AS[^:]*:(.*)/im => :scratch
|
|
68
|
+
/\AS[^:]*:(.*)/im => :scratch,
|
|
69
|
+
/\AN(.*)/im => :new_disk,
|
|
70
|
+
/\AR(.*)/im => :rename_file,
|
|
71
|
+
/\AC(.*)/im => :copy_file,
|
|
72
|
+
/\AD/i => :invalid
|
|
64
73
|
}.freeze
|
|
65
74
|
|
|
66
75
|
# Commands that take their argument as text rather than numbers.
|
|
67
|
-
TEXT_COMMANDS = %i[scratch].freeze
|
|
76
|
+
TEXT_COMMANDS = %i[scratch new_disk rename_file copy_file].freeze
|
|
68
77
|
|
|
69
78
|
# A drive powers on reporting its DOS version, as a reset does. It
|
|
70
79
|
# can take the RAM of a drive it replaces, and initializes its disk.
|
|
@@ -197,7 +206,7 @@ module Badline
|
|
|
197
206
|
return report(SYNTAX_ERROR) unless action
|
|
198
207
|
|
|
199
208
|
action = USER_TABLE[(command.getbyte(1) - 1) & 0x0f] if action == :user
|
|
200
|
-
argument = pattern.match(command)[1]
|
|
209
|
+
argument = pattern.match(action == :new_disk ? text.lstrip : command)[1]
|
|
201
210
|
run_command(action, TEXT_COMMANDS.include?(action) ? argument : Parameters.parse(argument))
|
|
202
211
|
end
|
|
203
212
|
|
|
@@ -218,6 +227,10 @@ module Badline
|
|
|
218
227
|
when :block_allocate then block_allocate(arguments)
|
|
219
228
|
when :block_free then block_free(arguments)
|
|
220
229
|
when :scratch then scratch(arguments)
|
|
230
|
+
when :new_disk then new_disk(arguments)
|
|
231
|
+
when :rename_file then rename_file(arguments)
|
|
232
|
+
when :copy_file then copy_file(arguments)
|
|
233
|
+
when :invalid then report(INVALID_COMMAND)
|
|
221
234
|
when :cold_reset then reset(cold: true)
|
|
222
235
|
when :warm_reset then reset(cold: false)
|
|
223
236
|
when :memory_write then memory_write(arguments)
|
|
@@ -54,7 +54,9 @@ module Badline
|
|
|
54
54
|
address_device if @listening
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
# The device TALK or LISTEN addresses: the one in A or in FA, as the
|
|
58
|
+
# layout says.
|
|
59
|
+
def addressed? = (@layout.device_in_a ? @cpu.a : @bus.peek(zero_page.device)) == @device
|
|
58
60
|
|
|
59
61
|
# TALK and LISTEN end with the command sent under ATN, which stays
|
|
60
62
|
# low, and A holding $FF.
|
|
@@ -116,7 +118,7 @@ module Badline
|
|
|
116
118
|
def acptr
|
|
117
119
|
return unless @talking
|
|
118
120
|
|
|
119
|
-
@layout.
|
|
121
|
+
@layout.byte_received(@bus)
|
|
120
122
|
byte, status = received
|
|
121
123
|
time_serial_byte(Routine::ACPTR_TIMEOUT)
|
|
122
124
|
@cpu.a = byte
|