badline 0.16.0 → 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 +76 -0
- data/CLAUDE.md +1 -1
- data/README.md +130 -17
- data/doc/media.md +14 -2
- data/doc/pinned-behaviour.md +61 -0
- data/doc/snapshot-format.md +379 -70
- data/doc/snapshots.md +185 -10
- 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/ieee_bus.rb +50 -4
- data/lib/badline/drives/model2031/ieee_port.rb +133 -0
- data/lib/badline/drives/model2031/keyed_state.rb +66 -0
- data/lib/badline/drives/model2031/parts.rb +25 -0
- data/lib/badline/drives/model2031.rb +92 -0
- 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 +29 -6
- 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 +7 -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 +26 -8
- data/lib/badline/media/cbm2_media.rb +29 -0
- data/lib/badline/media/pet_media.rb +5 -5
- data/lib/badline/media/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +50 -8
- data/lib/badline/media.rb +21 -8
- data/lib/badline/options/family.rb +15 -3
- data/lib/badline/options/help.rb +24 -6
- data/lib/badline/options/table.rb +25 -5
- data/lib/badline/options/validation.rb +47 -7
- data/lib/badline/options.rb +48 -15
- data/lib/badline/pet/bus/expansion.rb +99 -5
- data/lib/badline/pet/bus.rb +30 -12
- data/lib/badline/pet/cassettes.rb +22 -1
- data/lib/badline/pet/characters.rb +8 -2
- data/lib/badline/pet/crtc_video.rb +20 -7
- data/lib/badline/pet/ieee_drive.rb +17 -0
- data/lib/badline/pet/ieee_ports.rb +19 -1
- data/lib/badline/pet/ieee_traps.rb +10 -5
- data/lib/badline/pet/keyed_modules.rb +4 -1
- data/lib/badline/pet/keyed_parts.rb +10 -6
- data/lib/badline/pet/model.rb +56 -6
- data/lib/badline/pet/rom_sets.rb +30 -6
- data/lib/badline/pet.rb +45 -16
- 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 +144 -1
- data/lib/badline/roms/cbm2/basic128-02b.rom +0 -0
- data/lib/badline/roms/cbm2/basic128.rom +0 -0
- data/lib/badline/roms/cbm2/basic256.rom +0 -0
- data/lib/badline/roms/cbm2/characters-600.rom +0 -0
- data/lib/badline/roms/cbm2/characters-700.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-03b.rom +0 -0
- data/lib/badline/roms/cbm2/kernal-04a.rom +0 -0
- data/lib/badline/roms/cbm2/p500-basic.rom +0 -0
- data/lib/badline/roms/cbm2/p500-kernal.rom +0 -0
- data/lib/badline/roms/dos2031.rom +0 -0
- data/lib/badline/roms/pet/characters-din.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz-8296.rom +0 -0
- data/lib/badline/snapshot/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +9 -1
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +39 -3
- data/lib/badline/snapshot/image.rb +79 -15
- data/lib/badline/snapshot/pet_setup.rb +30 -13
- 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 +121 -0
- data/lib/badline/snapshot/vice/pet_cpu.rb +70 -0
- data/lib/badline/snapshot/vice/pet_crtc.rb +241 -0
- 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_mem.rb +93 -0
- data/lib/badline/snapshot/vice/pet_pias.rb +56 -0
- data/lib/badline/snapshot/vice/pet_restore.rb +214 -0
- data/lib/badline/snapshot/vice/pet_via.rb +165 -0
- 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 +4 -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 +87 -2
|
@@ -20,7 +20,17 @@ module Badline
|
|
|
20
20
|
|
|
21
21
|
# The PETs and CBMs, as PET::Model names them, the 8032 first, the
|
|
22
22
|
# default.
|
|
23
|
-
PET_MODELS = %w[8032 8096 4032 3032 3032b 2001].freeze
|
|
23
|
+
PET_MODELS = %w[8032 8096 8296 4032 3032 3032b 2001].freeze
|
|
24
|
+
|
|
25
|
+
# The CBM-II's B series, as CBM2::Model names them, the 610 first, the
|
|
26
|
+
# default, and the P500, as CBM2::P500::Model names them.
|
|
27
|
+
CBM2_MODELS = %w[610 620 710 720 510 510ntsc].freeze
|
|
28
|
+
|
|
29
|
+
# The B series' KERNALs --kernal names, 901244-04a first, the default.
|
|
30
|
+
CBM2_KERNALS = %w[04a 03b].freeze
|
|
31
|
+
|
|
32
|
+
# The P500's one KERNAL, 901234-02.
|
|
33
|
+
P500_KERNALS = %w[02].freeze
|
|
24
34
|
|
|
25
35
|
# The true drives --drive-type names for the Plus/4.
|
|
26
36
|
PLUS4_DRIVE_TYPES = %w[1541 1551].freeze
|
|
@@ -47,7 +57,8 @@ module Badline
|
|
|
47
57
|
section: "Window options", needs: :window),
|
|
48
58
|
Option.new("--no-sound", "Don't play the SID", section: "Window options", needs: :window, build: :native),
|
|
49
59
|
Option.new("--true-drive", "Put a true 1541 on device 8 instead of the KERNAL traps, a 1571 on the C128, " \
|
|
50
|
-
"a 1551 on the Plus/4"
|
|
60
|
+
"a 1551 on the Plus/4, a 2031 on the PET or the CBM-II, or for a .d80 or .d82 " \
|
|
61
|
+
"an 8050 or 8250",
|
|
51
62
|
section: "Window options", needs: :window),
|
|
52
63
|
Option.new("--drive-type MODEL", "The Plus/4's true drive: 1541 or 1551 (default: 1551); implies --true-drive",
|
|
53
64
|
section: "Window options", needs: :window),
|
|
@@ -55,7 +66,7 @@ module Badline
|
|
|
55
66
|
section: "Window options", needs: :window),
|
|
56
67
|
Option.new("--model NAME", "C64 to run: #{MODELS.join(', ')} (default: #{MODELS.first}); VIC-20: pal; " \
|
|
57
68
|
"C128: #{C128_MODELS.join(', ')}; Plus/4: #{PLUS4_MODELS.join(', ')}; " \
|
|
58
|
-
"PET: #{PET_MODELS.join(', ')}",
|
|
69
|
+
"PET: #{PET_MODELS.join(', ')}; CBM-II: #{CBM2_MODELS.join(', ')}",
|
|
59
70
|
section: "Window options", needs: :window),
|
|
60
71
|
Option.new("--ntsc", "Run an NTSC C64, as --model ntsc does", section: "Window options", needs: :window),
|
|
61
72
|
Option.new("--c64", "Start the C128 in C64 mode, holding C= at power-on",
|
|
@@ -63,13 +74,22 @@ module Badline
|
|
|
63
74
|
Option.new("--ram SIZE", "VIC-20 RAM expansion: #{RAM_NAMES.join(', ')} (default: unexpanded); " \
|
|
64
75
|
"Plus/4 RAM: 16k, 32k or 64k; PET RAM: 4k, 8k, 16k or 32k (default: the model's own)",
|
|
65
76
|
section: "Window options", needs: :window),
|
|
66
|
-
Option.new("--60hz", "Run a CRTC PET's 60 Hz editor instead of its 50 Hz one",
|
|
77
|
+
Option.new("--60hz", "Run a CRTC PET's 60 Hz editor instead of its 50 Hz one, or a CBM-II on 60 Hz mains",
|
|
78
|
+
section: "Window options", needs: :window),
|
|
79
|
+
Option.new("--kernal VERSION", "Fit a CBM-II with KERNAL #{CBM2_KERNALS.join(' or ')} " \
|
|
80
|
+
"(default: #{CBM2_KERNALS.first}); the P500 takes #{P500_KERNALS.first}",
|
|
67
81
|
section: "Window options", needs: :window),
|
|
68
82
|
Option.new("--basic VERSION", "Fit a PET with BASIC 1, 2 or 4, as its board takes (default: its own)",
|
|
69
83
|
section: "Window options", needs: :window),
|
|
70
84
|
Option.new("--editor NAME", "Fit a PET with an editor ROM its board and BASIC take: " \
|
|
71
85
|
"#{PET::ROMSets::EDITORS.keys.join(', ')}",
|
|
72
86
|
section: "Window options", needs: :window),
|
|
87
|
+
Option.new("--characters NAME", "Fit an 8296 with a character ROM: ascii or din (default: ascii)",
|
|
88
|
+
section: "Window options", needs: :window),
|
|
89
|
+
Option.new("--jumpers LIST", "Fit an 8296's board with jumpers, comma-separated: ram9 and rama for RAM at " \
|
|
90
|
+
"$9000 and $A000, userport for the user port's PA0-PA2 to switch in " \
|
|
91
|
+
"the RAM under the ROMs",
|
|
92
|
+
section: "Window options", needs: :window),
|
|
73
93
|
Option.new("--c1lo FILE", "Put a .bin ROM image in the Plus/4's cartridge 1, low slot ($8000)",
|
|
74
94
|
section: "Window options", needs: :window),
|
|
75
95
|
Option.new("--c1hi FILE", "Put a .bin ROM image in cartridge 1's high slot ($C000)",
|
|
@@ -112,7 +132,7 @@ module Badline
|
|
|
112
132
|
section: "Testing options", needs: :window),
|
|
113
133
|
Option.new("--save-snapshot FILE", "Save the machine as a .vsf snapshot after the last frame",
|
|
114
134
|
section: "Testing options", needs: :window),
|
|
115
|
-
Option.new("--vice", "Add VICE's modules to --save-snapshot's C64
|
|
135
|
+
Option.new("--vice", "Add VICE's modules to --save-snapshot's C64, PET or CBM-II snapshot",
|
|
116
136
|
section: "Testing options", needs: :window),
|
|
117
137
|
Option.new("--at FRAME:EVENT", "Run EVENT once FRAME frames have run (see above)",
|
|
118
138
|
section: "Testing options", needs: :window),
|
|
@@ -57,6 +57,12 @@ module Badline
|
|
|
57
57
|
value
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def kernal_for(value)
|
|
61
|
+
raise Error, "invalid argument: --kernal #{value}" unless (CBM2_KERNALS + P500_KERNALS).include?(value)
|
|
62
|
+
|
|
63
|
+
value
|
|
64
|
+
end
|
|
65
|
+
|
|
60
66
|
def reu_size(value)
|
|
61
67
|
raise Error, "invalid argument: --reu #{value}" unless REU_SIZES.include?(value)
|
|
62
68
|
|
|
@@ -102,6 +108,7 @@ module Badline
|
|
|
102
108
|
validate_vic20 if @family == :vic20
|
|
103
109
|
@family == :plus4 ? validate_plus4 : refuse_plus4_options
|
|
104
110
|
@family == :pet ? validate_pet : refuse_pet_options
|
|
111
|
+
@family == :cbm2 ? validate_cbm2 : refuse_cbm2_options
|
|
105
112
|
if @family == :c128
|
|
106
113
|
validate_c128
|
|
107
114
|
elsif @c64_mode
|
|
@@ -170,24 +177,40 @@ module Badline
|
|
|
170
177
|
raise Error, "--sid-card-address needs --sid-card" unless @sid_card_address.nil? || @sid_card
|
|
171
178
|
end
|
|
172
179
|
|
|
173
|
-
# Only the PET has
|
|
174
|
-
#
|
|
180
|
+
# Only the PET has BASIC and editor ROMs to fit, and only it and the
|
|
181
|
+
# CBM-II a frame rate to pick.
|
|
175
182
|
def refuse_pet_options
|
|
176
|
-
raise Error, "--60hz needs pet" unless @refresh_hz.nil?
|
|
183
|
+
raise Error, "--60hz needs pet or cbm2" unless @refresh_hz.nil? || @family == :cbm2
|
|
177
184
|
raise Error, "--basic needs pet" unless @basic.nil?
|
|
178
185
|
raise Error, "--editor needs pet" unless @editor.nil?
|
|
186
|
+
raise Error, "--characters needs pet" unless @characters.nil?
|
|
187
|
+
raise Error, "--jumpers needs pet" unless @jumpers.empty?
|
|
179
188
|
end
|
|
180
189
|
|
|
181
|
-
# The PET runs in the window, without the C64's SID and REU
|
|
182
|
-
#
|
|
190
|
+
# The PET runs in the window, without the C64's SID and REU, at the
|
|
191
|
+
# frame rate its editor sets.
|
|
183
192
|
def validate_pet
|
|
184
193
|
raise Error, "--ntsc needs the C64; a CRTC PET's 60 Hz editor is --60hz" if @models.include?("ntsc")
|
|
185
194
|
raise Error, "--sid needs the C64" unless @sid_model.nil?
|
|
186
195
|
raise Error, "--reu needs the C64" unless @reu.nil?
|
|
187
|
-
raise Error, "the PET has no true drive yet" if @true_drive
|
|
188
196
|
raise Error, "the PET needs the window" if headless?
|
|
189
197
|
|
|
190
|
-
|
|
198
|
+
return unless PET_MODELS.include?(model)
|
|
199
|
+
|
|
200
|
+
validate_pet_roms
|
|
201
|
+
validate_pet_board
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# --characters names a character ROM the board takes, --jumpers the
|
|
205
|
+
# 8296 board's jumpers, and the 8296 has all of its RAM soldered on.
|
|
206
|
+
def validate_pet_board
|
|
207
|
+
unless @characters.nil? || PET::ROMSets.characters(model).include?(@characters)
|
|
208
|
+
raise Error, "the #{model} takes no character ROM #{@characters}"
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
unknown = @jumpers.find { |jumper| !PET::ROMSets.jumpers(model).include?(jumper) }
|
|
212
|
+
raise Error, "the #{model} has no jumper #{unknown}" unless unknown.nil?
|
|
213
|
+
raise Error, "the 8296 has 32k of RAM" if model == "8296" && ![nil, 32].include?(@ram_kb)
|
|
191
214
|
end
|
|
192
215
|
|
|
193
216
|
# --basic and --editor name ROMs the model's board takes
|
|
@@ -216,6 +239,23 @@ module Badline
|
|
|
216
239
|
@refresh_hz = editor_hz
|
|
217
240
|
end
|
|
218
241
|
|
|
242
|
+
# Only the CBM-II has a KERNAL to pick.
|
|
243
|
+
def refuse_cbm2_options
|
|
244
|
+
raise Error, "--kernal needs cbm2" unless @kernal.nil?
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# The CBM-II runs in the window, without the C64's SID and REU, and
|
|
248
|
+
# its model takes the KERNAL named.
|
|
249
|
+
def validate_cbm2
|
|
250
|
+
raise Error, "--ntsc needs the C64; a CBM-II on 60 Hz mains is --60hz" if @models.include?("ntsc")
|
|
251
|
+
raise Error, "--sid needs the C64" unless @sid_model.nil?
|
|
252
|
+
raise Error, "--reu needs the C64" unless @reu.nil?
|
|
253
|
+
raise Error, "the CBM-II needs the window" if headless?
|
|
254
|
+
|
|
255
|
+
kernals = p500? ? P500_KERNALS : CBM2_KERNALS
|
|
256
|
+
raise Error, "the #{model} takes KERNAL #{kernals.join(' or ')}" unless kernals.include?(kernal)
|
|
257
|
+
end
|
|
258
|
+
|
|
219
259
|
# `sid` takes directories as well as tunes.
|
|
220
260
|
def validate_tune(path)
|
|
221
261
|
return if !File.exist?(path) || File.extname(path).casecmp?(".sid")
|
data/lib/badline/options.rb
CHANGED
|
@@ -13,10 +13,11 @@ module Badline
|
|
|
13
13
|
# Either opens the window for any media, or with --headless or
|
|
14
14
|
# --audio-out plays or renders a .sid tune without one. `sid`, given
|
|
15
15
|
# first, plays .sid tunes and directories of them in the terminal.
|
|
16
|
-
# `c64`, `vic20`, `c128`, `plus4` or `
|
|
17
|
-
# machine family, and without one the family is the C64, the C128
|
|
18
|
-
# program that loads where C128 BASIC starts,
|
|
19
|
-
# or 8250's disk or a PET tape
|
|
16
|
+
# `c64`, `vic20`, `c128`, `plus4`, `pet` or `cbm2`, given first, names
|
|
17
|
+
# the machine family, and without one the family is the C64, the C128
|
|
18
|
+
# for a program that loads where C128 BASIC starts, the PET for an
|
|
19
|
+
# 8050's or 8250's disk or a PET tape, or the CBM-II for a cartridge
|
|
20
|
+
# signed as its own.
|
|
20
21
|
# The native build adds --no-sound and --version, and badline-ruby adds
|
|
21
22
|
# --disable-jit.
|
|
22
23
|
#
|
|
@@ -77,12 +78,16 @@ module Badline
|
|
|
77
78
|
:audio_out, :seconds, :songlengths, :filter_chunk, :timeline
|
|
78
79
|
|
|
79
80
|
# The machine family to build (Badline::Machine), :c64, :vic20, :c128,
|
|
80
|
-
# :plus4 or :
|
|
81
|
+
# :plus4, :pet or :cbm2.
|
|
81
82
|
attr_reader :family
|
|
82
83
|
|
|
83
|
-
# The ROMs --basic and --
|
|
84
|
-
# PET::ROMSets::BASICS
|
|
85
|
-
|
|
84
|
+
# The ROMs --basic, --editor and --characters fit a PET with, keys of
|
|
85
|
+
# PET::ROMSets::BASICS, PET::ROMSets::EDITORS and
|
|
86
|
+
# PET::ROMSets::CHARACTERS, or nil for its own.
|
|
87
|
+
attr_reader :basic, :editor, :characters
|
|
88
|
+
|
|
89
|
+
# The 8296's board jumpers --jumpers names (PET::Bus::Expansion::JUMPERS).
|
|
90
|
+
attr_reader :jumpers
|
|
86
91
|
|
|
87
92
|
# The VIC-20's RAM expansion --ram names, a key of
|
|
88
93
|
# Vic20::Bus::RAM_CONFIGURATIONS, or nil without --ram.
|
|
@@ -132,9 +137,8 @@ module Badline
|
|
|
132
137
|
@ram = nil
|
|
133
138
|
@ram_kb = nil
|
|
134
139
|
@ram_name = nil
|
|
135
|
-
|
|
136
|
-
@
|
|
137
|
-
@editor = nil
|
|
140
|
+
initialize_pet
|
|
141
|
+
@kernal = nil
|
|
138
142
|
@cartridges = {}
|
|
139
143
|
@autostart = true
|
|
140
144
|
@writable = false
|
|
@@ -244,9 +248,20 @@ module Badline
|
|
|
244
248
|
def c64_mode? = @c64_mode
|
|
245
249
|
|
|
246
250
|
# The frame rate of a CRTC PET's editor: 60 with --60hz or a 60 Hz
|
|
247
|
-
# --editor, else 50.
|
|
251
|
+
# --editor, else 50. A B series CBM-II's mains, which its editor
|
|
252
|
+
# follows.
|
|
248
253
|
def refresh_hz = @refresh_hz || 50
|
|
249
254
|
|
|
255
|
+
# The CBM-II's mains, 60 with --60hz, or nil for the model's own.
|
|
256
|
+
def mains_hz = @refresh_hz
|
|
257
|
+
|
|
258
|
+
# The CBM-II's KERNAL --kernal names, one of CBM2_KERNALS or, on the
|
|
259
|
+
# P500, P500_KERNALS, the first of them without it.
|
|
260
|
+
def kernal = @kernal || (p500? ? P500_KERNALS : CBM2_KERNALS).first
|
|
261
|
+
|
|
262
|
+
# Whether the CBM-II to run is a P500, a 510 of either region.
|
|
263
|
+
def p500? = @family == :cbm2 && model.start_with?("510")
|
|
264
|
+
|
|
250
265
|
def help? = @help
|
|
251
266
|
|
|
252
267
|
def version? = @version
|
|
@@ -262,7 +277,7 @@ module Badline
|
|
|
262
277
|
# A .sid on its own plays in the SID player, as `sid` would, unless an
|
|
263
278
|
# option of the emulator's window asks for the machine.
|
|
264
279
|
def play_lone_tune
|
|
265
|
-
return if @sid_command || %i[vic20 plus4 pet].include?(@family) || headless? || !@window_only.empty?
|
|
280
|
+
return if @sid_command || %i[vic20 plus4 pet cbm2].include?(@family) || headless? || !@window_only.empty?
|
|
266
281
|
return if @media_path.nil?
|
|
267
282
|
|
|
268
283
|
@sid_command = File.extname(@media_path).casecmp?(".sid")
|
|
@@ -322,9 +337,9 @@ module Badline
|
|
|
322
337
|
when "--model" then @models << value
|
|
323
338
|
when "--reu" then @reu = reu_size(value)
|
|
324
339
|
when "--ram" then @ram_name = value
|
|
325
|
-
when "--basic" then
|
|
326
|
-
when "--editor" then @editor = value
|
|
340
|
+
when "--basic", "--editor", "--characters", "--jumpers" then pet_option(name, value)
|
|
327
341
|
when "--drive-type" then @drive_type = drive_type_for(value)
|
|
342
|
+
when "--kernal" then @kernal = kernal_for(value)
|
|
328
343
|
when "--c1lo", "--c1hi", "--c2lo", "--c2hi" then @cartridges[CARTRIDGE_SLOTS.fetch(name)] = value
|
|
329
344
|
when "--frames" then @frames = number(flag, value)
|
|
330
345
|
when "--screenshot" then @screenshot = value
|
|
@@ -338,6 +353,24 @@ module Badline
|
|
|
338
353
|
end
|
|
339
354
|
end
|
|
340
355
|
|
|
356
|
+
# The PET's frame rate, ROMs and board jumpers, its own until named.
|
|
357
|
+
def initialize_pet
|
|
358
|
+
@refresh_hz = nil
|
|
359
|
+
@basic = nil
|
|
360
|
+
@editor = nil
|
|
361
|
+
@characters = nil
|
|
362
|
+
@jumpers = []
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def pet_option(name, value)
|
|
366
|
+
case name
|
|
367
|
+
when "--basic" then @basic = value
|
|
368
|
+
when "--editor" then @editor = value
|
|
369
|
+
when "--characters" then @characters = value
|
|
370
|
+
else @jumpers = value.split(",")
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
341
374
|
def switch(name)
|
|
342
375
|
case name
|
|
343
376
|
when "--no-autostart" then @autostart = false
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
class PET
|
|
5
5
|
class Bus
|
|
6
|
-
# The 8096
|
|
7
|
-
# register at $FFF0 lays over $8000-$FFFF, two at
|
|
6
|
+
# The 64K board of the 8096 and the 8296: four 16K banks, which a
|
|
7
|
+
# write-only control register at $FFF0 lays over $8000-$FFFF, two at
|
|
8
|
+
# a time:
|
|
8
9
|
#
|
|
9
|
-
# bit 7 the board on; when clear,
|
|
10
|
+
# bit 7 the board on; when clear, the rest ignored
|
|
10
11
|
# bit 6 I/O shows through at $E800-$EFFF
|
|
11
12
|
# bit 5 the screen shows through at $8000-$8FFF
|
|
12
13
|
# bit 3 bank 3 at $C000-$FFFF, else bank 1
|
|
@@ -16,6 +17,26 @@ module Badline
|
|
|
16
17
|
#
|
|
17
18
|
# A write to $FFF0 lands where $FFF0 was before it, too. The RES line
|
|
18
19
|
# clears the register, so the 8096 starts as an 8032.
|
|
20
|
+
#
|
|
21
|
+
# The 8296 decodes its memory in a PLA, after the logic equations
|
|
22
|
+
# André Fachat read out of it (8296desc.txt on zimmers.net), with 32K
|
|
23
|
+
# of RAM from $8000 (Bus#screen) besides the board:
|
|
24
|
+
#
|
|
25
|
+
# - With bit 7 clear, every write from $8000 up but to $E8xx goes to
|
|
26
|
+
# that RAM, under the ROMs too. Reads find the screen at $8000,
|
|
27
|
+
# the empty sockets at $9000 and $A000, BASIC, the editor at
|
|
28
|
+
# $E000-$E7FF and again at $E900-$EFFF, and the KERNAL, unless the
|
|
29
|
+
# lines /RAMSEL9, /RAMSELA and /RAMON put RAM there (#ram_lines).
|
|
30
|
+
# RAMSEL9 puts it at $9000, RAMSELA at $A000, RAMON at
|
|
31
|
+
# $8000-$DFFF, RAMON with either of the others at $E000-$EFFF but
|
|
32
|
+
# $E8xx, and RAMON with RAMSELA at $F000 as well.
|
|
33
|
+
# - With bit 7 set, I/O showing through brings back the editor at
|
|
34
|
+
# $E900-$EFFF, which an 8096 leaves empty.
|
|
35
|
+
#
|
|
36
|
+
# Jumpers tie the lines: JU2 RAMSEL9 low and JU1 RAMSELA low
|
|
37
|
+
# (:ram9, :rama), or JU4, JU3 and JU5 RAMSEL9, RAMSELA and RAMON to
|
|
38
|
+
# the user port's PA1, PA0 and PA2 (:userport), each active low.
|
|
39
|
+
# With none fitted, as on xpet's 8296, the lines stay high.
|
|
19
40
|
class Expansion
|
|
20
41
|
# A 16K bank of the board, seen from +start+.
|
|
21
42
|
class Window
|
|
@@ -50,9 +71,16 @@ module Badline
|
|
|
50
71
|
end
|
|
51
72
|
end
|
|
52
73
|
|
|
74
|
+
# The user port lines that RAMSELA, RAMSEL9 and RAMON follow.
|
|
75
|
+
USER_PORT_LINES = 0x07
|
|
76
|
+
|
|
53
77
|
attr_reader :cells, :control
|
|
54
78
|
|
|
55
|
-
|
|
79
|
+
# The user port's PA0-PA2 as the board last saw them.
|
|
80
|
+
attr_reader :lines
|
|
81
|
+
|
|
82
|
+
# +jumpers+ are the 8296's (ROMSets::JUMPERS) when +board8296+ is.
|
|
83
|
+
def initialize(bus, board8296: false, jumpers: [])
|
|
56
84
|
@bus = bus
|
|
57
85
|
@cells = Array.new(0x10000, 0)
|
|
58
86
|
@banks = [0x8000, 0xc000, 0x8000, 0xc000].each_with_index.map do |start, bank|
|
|
@@ -60,13 +88,32 @@ module Badline
|
|
|
60
88
|
end
|
|
61
89
|
@port = ControlPort.new(self, @banks[1])
|
|
62
90
|
@control = 0
|
|
91
|
+
@board8296 = board8296
|
|
92
|
+
@ram9 = jumpers.include?("ram9")
|
|
93
|
+
@rama = jumpers.include?("rama")
|
|
94
|
+
@user_port = jumpers.include?("userport")
|
|
95
|
+
@lines = USER_PORT_LINES
|
|
63
96
|
end
|
|
64
97
|
|
|
98
|
+
def board8296? = @board8296
|
|
99
|
+
|
|
100
|
+
# Whether the board follows the user port's lines.
|
|
101
|
+
def user_port? = @user_port
|
|
102
|
+
|
|
65
103
|
def control=(value)
|
|
66
104
|
@control = value
|
|
67
105
|
@bus.map_pages
|
|
68
106
|
end
|
|
69
107
|
|
|
108
|
+
# Follows the user port's lines, +port_a+ as the VIA drives them.
|
|
109
|
+
def lines=(port_a)
|
|
110
|
+
lines = port_a & USER_PORT_LINES
|
|
111
|
+
return if lines == @lines
|
|
112
|
+
|
|
113
|
+
@lines = lines
|
|
114
|
+
@bus.map_pages
|
|
115
|
+
end
|
|
116
|
+
|
|
70
117
|
def reset!
|
|
71
118
|
self.control = 0
|
|
72
119
|
end
|
|
@@ -75,16 +122,33 @@ module Badline
|
|
|
75
122
|
@cells.fill(0)
|
|
76
123
|
end
|
|
77
124
|
|
|
125
|
+
# Whether the CPU reads the KERNAL at $F000-$FFFF.
|
|
126
|
+
def kernal_mapped?
|
|
127
|
+
return false if @control.anybits?(0x80)
|
|
128
|
+
|
|
129
|
+
!(@board8296 && ram_on? && ram_a?)
|
|
130
|
+
end
|
|
131
|
+
|
|
78
132
|
# Lays the banks the register picks over the pages, and the
|
|
79
133
|
# control register over page $FF's writes.
|
|
80
134
|
def map(reads, writes)
|
|
81
|
-
|
|
135
|
+
if @control.anybits?(0x80)
|
|
136
|
+
map_banks(reads, writes)
|
|
137
|
+
elsif @board8296
|
|
138
|
+
map_ram8296(reads, writes)
|
|
139
|
+
end
|
|
82
140
|
@port.under = writes[0xff]
|
|
83
141
|
writes[0xff] = @port
|
|
84
142
|
end
|
|
85
143
|
|
|
86
144
|
private
|
|
87
145
|
|
|
146
|
+
def ram_on? = @user_port && @lines.nobits?(0x04)
|
|
147
|
+
|
|
148
|
+
def ram9? = @ram9 || (@user_port && @lines.nobits?(0x02))
|
|
149
|
+
|
|
150
|
+
def ram_a? = @rama || (@user_port && @lines.nobits?(0x01))
|
|
151
|
+
|
|
88
152
|
def map_banks(reads, writes)
|
|
89
153
|
low = @banks[@control.anybits?(0x04) ? 2 : 0]
|
|
90
154
|
high = @banks[@control.anybits?(0x08) ? 3 : 1]
|
|
@@ -99,6 +163,36 @@ module Badline
|
|
|
99
163
|
reads.fill(high, page, count)
|
|
100
164
|
writes.fill(high, page, count)
|
|
101
165
|
end
|
|
166
|
+
map_editor_through(reads, writes) if @board8296
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# I/O showing through on the 8296 leaves $E900-$EFFF to the editor,
|
|
170
|
+
# and its writes to nothing.
|
|
171
|
+
def map_editor_through(reads, writes)
|
|
172
|
+
return unless @control.anybits?(0x40)
|
|
173
|
+
|
|
174
|
+
reads.fill(@bus.editor_rom, 0xe9, 0x07)
|
|
175
|
+
writes.fill(@bus.open_bus, 0xe9, 0x07)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def map_ram8296(reads, writes)
|
|
179
|
+
ram = @bus.screen
|
|
180
|
+
writes.fill(ram, 0x90, 0x58)
|
|
181
|
+
writes.fill(ram, 0xe9, 0x17)
|
|
182
|
+
reads.fill(@bus.editor_rom, 0xe9, 0x07)
|
|
183
|
+
ram_on = ram_on?
|
|
184
|
+
ram9 = ram9?
|
|
185
|
+
ram_a = ram_a?
|
|
186
|
+
reads.fill(ram, 0x90, 0x10) if ram9 || ram_on
|
|
187
|
+
reads.fill(ram, 0xa0, 0x10) if ram_a || ram_on
|
|
188
|
+
return unless ram_on
|
|
189
|
+
|
|
190
|
+
reads.fill(ram, 0xb0, 0x30)
|
|
191
|
+
return unless ram9 || ram_a
|
|
192
|
+
|
|
193
|
+
reads.fill(ram, 0xe0, 0x08)
|
|
194
|
+
reads.fill(ram, 0xe9, 0x07)
|
|
195
|
+
reads.fill(ram, 0xf0, 0x10) if ram_a
|
|
102
196
|
end
|
|
103
197
|
end
|
|
104
198
|
end
|
data/lib/badline/pet/bus.rb
CHANGED
|
@@ -19,7 +19,8 @@ module Badline
|
|
|
19
19
|
# empty spot finds it, mostly the high byte of the address just
|
|
20
20
|
# fetched, as PET/unconnected shows on a real 3016. Writes to ROM and
|
|
21
21
|
# to empty spots go nowhere. An 8096 banks its 64K board over
|
|
22
|
-
# $8000-$FFFF (Expansion)
|
|
22
|
+
# $8000-$FFFF (Expansion), and an 8296 its board and the RAM under its
|
|
23
|
+
# ROMs.
|
|
23
24
|
class Bus
|
|
24
25
|
include Addressable
|
|
25
26
|
|
|
@@ -33,7 +34,8 @@ module Badline
|
|
|
33
34
|
def poke(_addr, _value); end
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
# Screen RAM, repeated over the pages it answers in.
|
|
37
|
+
# Screen RAM, repeated over the pages it answers in. On the 8296 it is
|
|
38
|
+
# all 32K of RAM from $8000, which the board lays under the ROMs.
|
|
37
39
|
class ScreenRAM
|
|
38
40
|
attr_reader :cells
|
|
39
41
|
|
|
@@ -107,29 +109,31 @@ module Badline
|
|
|
107
109
|
end
|
|
108
110
|
end
|
|
109
111
|
|
|
110
|
-
attr_reader :ram, :screen, :expansion
|
|
112
|
+
attr_reader :ram, :screen, :expansion, :editor_rom, :open_bus
|
|
111
113
|
|
|
112
114
|
# The last byte the CPU read or wrote.
|
|
113
115
|
attr_reader :data
|
|
114
116
|
|
|
115
117
|
# +model+ is a Model::Profile, +ram_kb+ the RAM below $8000 and
|
|
116
118
|
# +editor+ the editor ROM's file. The CRTC answers only on a board
|
|
117
|
-
# the model fits one on.
|
|
118
|
-
def initialize(model:, ram_kb:, editor:, pia1:, pia2:, via:, crtc:)
|
|
119
|
+
# the model fits one on. +jumpers+ are the 8296's (ROMSets::JUMPERS).
|
|
120
|
+
def initialize(model:, ram_kb:, editor:, pia1:, pia2:, via:, crtc:, jumpers: [])
|
|
119
121
|
addressable_at(0, length: 2**16)
|
|
120
122
|
@ram = Memory.new([], length: ram_kb * 1024, start: 0)
|
|
121
123
|
@screen = ScreenRAM.new(model.screen_bytes)
|
|
122
124
|
@screen_pages = model.screen_pages
|
|
123
125
|
basic = ROM.read(model.basic)
|
|
124
126
|
@basic_rom = ROM.new(basic, length: basic.length, start: 0xe000 - basic.length)
|
|
125
|
-
@editor_rom = ROM.load(editor, 0xe000)
|
|
127
|
+
@editor_rom = model.cbm8296? ? editor_socket(editor) : ROM.load(editor, 0xe000)
|
|
126
128
|
@kernal_rom = ROM.load(model.kernal, 0xf000)
|
|
127
129
|
@io = IO.new(self, pia1:, pia2:, via:, crtc:, crtc_fitted: model.crtc?)
|
|
128
130
|
@open_bus = OpenBus.new(self)
|
|
129
131
|
@data = 0
|
|
130
132
|
@read_pages = Array.new(256, @open_bus)
|
|
131
133
|
@write_pages = Array.new(256, @open_bus)
|
|
132
|
-
@expansion = model.expansion ? Expansion.new(self) : nil
|
|
134
|
+
@expansion = model.expansion ? Expansion.new(self, board8296: model.cbm8296?, jumpers:) : nil
|
|
135
|
+
@via = via
|
|
136
|
+
@user_port = !@expansion.nil? && @expansion.user_port?
|
|
133
137
|
@debug_register = nil
|
|
134
138
|
@io_write_handler = nil
|
|
135
139
|
map_pages
|
|
@@ -143,11 +147,16 @@ module Badline
|
|
|
143
147
|
|
|
144
148
|
def io_written
|
|
145
149
|
@io_write_handler&.call
|
|
150
|
+
@expansion.lines = @via.port_a_output if @user_port
|
|
146
151
|
end
|
|
147
152
|
|
|
148
|
-
# The RES line reaches the
|
|
153
|
+
# The RES line reaches the board's control register, and the user
|
|
154
|
+
# port's lines float high with the VIA's.
|
|
149
155
|
def reset!
|
|
150
|
-
@expansion
|
|
156
|
+
return if @expansion.nil?
|
|
157
|
+
|
|
158
|
+
@expansion.reset!
|
|
159
|
+
@expansion.lines = @via.port_a_output if @user_port
|
|
151
160
|
end
|
|
152
161
|
|
|
153
162
|
def power_on!
|
|
@@ -166,8 +175,9 @@ module Badline
|
|
|
166
175
|
end
|
|
167
176
|
|
|
168
177
|
# Whether the CPU reads the KERNAL ROM at $F000-$FFFF, which an 8096's
|
|
169
|
-
# board covers while its control register turns it on
|
|
170
|
-
|
|
178
|
+
# board covers while its control register turns it on, and an 8296's
|
|
179
|
+
# RAM too.
|
|
180
|
+
def kernal_mapped? = @expansion.nil? || @expansion.kernal_mapped?
|
|
171
181
|
|
|
172
182
|
# Calls the block with each byte written to $8BFF (DebugRegister).
|
|
173
183
|
def install_debug_register(&)
|
|
@@ -175,7 +185,7 @@ module Badline
|
|
|
175
185
|
map_pages
|
|
176
186
|
end
|
|
177
187
|
|
|
178
|
-
# Lays the pages out again, for the
|
|
188
|
+
# Lays the pages out again, for the board's control register.
|
|
179
189
|
def map_pages
|
|
180
190
|
map(@open_bus, 0x00, 0x100)
|
|
181
191
|
map(@ram, 0x00, @ram.length >> 8)
|
|
@@ -190,6 +200,14 @@ module Badline
|
|
|
190
200
|
|
|
191
201
|
private
|
|
192
202
|
|
|
203
|
+
# The 8296's editor socket, which answers at $E000-$E7FF and
|
|
204
|
+
# $E900-$EFFF: a 4K ROM's halves there, or a 2K one twice over.
|
|
205
|
+
def editor_socket(file)
|
|
206
|
+
bytes = ROM.read(file)
|
|
207
|
+
bytes += bytes if bytes.length == 0x800
|
|
208
|
+
ROM.new(bytes, length: 0x1000, start: 0xe000)
|
|
209
|
+
end
|
|
210
|
+
|
|
193
211
|
def map_debug_register
|
|
194
212
|
@debug_register.under = @write_pages[0x8b]
|
|
195
213
|
@write_pages[0x8b] = @debug_register
|
|
@@ -11,7 +11,8 @@ module Badline
|
|
|
11
11
|
# Both record from VIA PB3. The KERNAL reads each tape as the falling
|
|
12
12
|
# edges its read line makes, as the C64 and VIC-20 do. The machine
|
|
13
13
|
# passes the motor and write lines on after each write to the I/O
|
|
14
|
-
# chips (#lines_written)
|
|
14
|
+
# chips (#lines_written), and clocks the decks only while a tape runs
|
|
15
|
+
# (#running?).
|
|
15
16
|
class Cassettes
|
|
16
17
|
attr_reader :datasette1, :datasette2
|
|
17
18
|
|
|
@@ -22,6 +23,18 @@ module Badline
|
|
|
22
23
|
@datasette2 = Drives::Datasette.new
|
|
23
24
|
@datasette1.on_flag { pulse1 }
|
|
24
25
|
@datasette2.on_flag { pulse2 }
|
|
26
|
+
@running = false
|
|
27
|
+
@running_handler = nil
|
|
28
|
+
@datasette1.on_running_change { update_running }
|
|
29
|
+
@datasette2.on_running_change { update_running }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Whether either deck's tape runs.
|
|
33
|
+
def running? = @running
|
|
34
|
+
|
|
35
|
+
# Whether either deck's tape runs, each time that changes.
|
|
36
|
+
def on_running_change(&handler)
|
|
37
|
+
@running_handler = handler
|
|
25
38
|
end
|
|
26
39
|
|
|
27
40
|
def cycle!
|
|
@@ -48,6 +61,14 @@ module Badline
|
|
|
48
61
|
|
|
49
62
|
private
|
|
50
63
|
|
|
64
|
+
def update_running
|
|
65
|
+
running = @datasette1.running? || @datasette2.running?
|
|
66
|
+
return if running == @running
|
|
67
|
+
|
|
68
|
+
@running = running
|
|
69
|
+
@running_handler&.call
|
|
70
|
+
end
|
|
71
|
+
|
|
51
72
|
# A falling edge on a read line, and the line back up.
|
|
52
73
|
def pulse1
|
|
53
74
|
@pia1.ca1 = false
|
|
@@ -6,21 +6,27 @@ module Badline
|
|
|
6
6
|
# (upper case and graphics) from $000 and text (lower and upper case)
|
|
7
7
|
# from $400, 128 characters of 8 rows each. Bit 7 of a screen code
|
|
8
8
|
# inverts the character's rows, as the hardware does, so #rows holds
|
|
9
|
-
# 256 codes a set, each row at (set << 11) | (code << 3) | row.
|
|
9
|
+
# 256 codes a set, each row at (set << 11) | (code << 3) | row. A 4K
|
|
10
|
+
# ROM, the 8296's, holds two such halves, the second at HALF.
|
|
10
11
|
class Characters
|
|
11
12
|
TEXT = 0x800
|
|
12
13
|
|
|
14
|
+
HALF = 0x1000
|
|
15
|
+
|
|
13
16
|
attr_reader :rows
|
|
14
17
|
|
|
15
18
|
def initialize(file)
|
|
16
19
|
rom = ROM.read(file)
|
|
17
|
-
@rows = Array.new(
|
|
20
|
+
@rows = Array.new(rom.length << 1) do |index|
|
|
18
21
|
set = index >> 11
|
|
19
22
|
code = (index >> 3) & 0xff
|
|
20
23
|
bits = rom[(set << 10) | ((code & 0x7f) << 3) | (index & 0x07)]
|
|
21
24
|
code >= 0x80 ? bits ^ 0xff : bits
|
|
22
25
|
end
|
|
23
26
|
end
|
|
27
|
+
|
|
28
|
+
# Whether the ROM holds two halves.
|
|
29
|
+
def halves? = @rows.length > HALF
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
end
|