badline 0.15.1 → 0.16.1
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 +55 -0
- data/CLAUDE.md +8 -7
- data/README.md +128 -9
- data/doc/media.md +9 -0
- data/doc/pinned-behaviour.md +67 -0
- data/doc/snapshot-format.md +215 -18
- data/doc/snapshots.md +55 -0
- data/lib/badline/c64/common.rb +1 -1
- data/lib/badline/chips/crtc/keyed_state.rb +46 -0
- data/lib/badline/chips/crtc.rb +163 -0
- data/lib/badline/chips/pia/control_lines.rb +138 -0
- data/lib/badline/chips/pia/keyed_parts.rb +56 -0
- data/lib/badline/chips/pia.rb +138 -0
- data/lib/badline/chips/vic/register_log.rb +1 -4
- data/lib/badline/core.rb +3 -0
- data/lib/badline/drives/ieee_bus.rb +74 -0
- data/lib/badline/drives/model1551/cable.rb +5 -2
- data/lib/badline/drives/model1551/tia.rb +13 -34
- data/lib/badline/drives/model1551.rb +26 -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.rb +1 -0
- data/lib/badline/frontend/app.rb +1 -1
- data/lib/badline/frontend/controls.rb +23 -3
- data/lib/badline/frontend/gamepads.rb +10 -3
- data/lib/badline/frontend/keys.rb +88 -0
- data/lib/badline/frontend/media_slots.rb +1 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +2 -1
- data/lib/badline/frontend/menu/menu_media.rb +27 -28
- data/lib/badline/frontend/menu/menu_pages.rb +24 -2
- data/lib/badline/frontend/menu/menu_ports.rb +22 -1
- data/lib/badline/frontend/menu/pause_menu.rb +1 -0
- data/lib/badline/frontend/pet_keyboard.rb +124 -0
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/timeline.rb +16 -9
- data/lib/badline/frontend.rb +1 -0
- data/lib/badline/kernal_trap/dos/writes.rb +3 -1
- data/lib/badline/kernal_trap/dos.rb +6 -1
- data/lib/badline/kernal_trap/frames.rb +117 -0
- data/lib/badline/kernal_trap/ieee.rb +151 -0
- data/lib/badline/kernal_trap/ieee_layout.rb +113 -0
- data/lib/badline/kernal_trap/keyed_state.rb +4 -4
- data/lib/badline/kernal_trap/routine.rb +3 -53
- data/lib/badline/kernal_trap/serial.rb +4 -102
- data/lib/badline/kernal_trap/steps.rb +64 -0
- data/lib/badline/kernal_trap.rb +4 -0
- data/lib/badline/machine/keyboard_buffer.rb +8 -0
- data/lib/badline/machine/settling.rb +1 -1
- data/lib/badline/machine.rb +29 -6
- data/lib/badline/media/extensions.rb +5 -2
- data/lib/badline/media/pet_media.rb +40 -0
- data/lib/badline/media/program.rb +14 -2
- data/lib/badline/media/true_drive.rb +19 -6
- data/lib/badline/media/vic20_media.rb +2 -2
- data/lib/badline/media.rb +41 -17
- data/lib/badline/options/family.rb +17 -6
- data/lib/badline/options/help.rb +23 -3
- data/lib/badline/options/table.rb +22 -4
- data/lib/badline/options/validation.rb +84 -5
- data/lib/badline/options.rb +47 -16
- data/lib/badline/pet/bus/expansion.rb +200 -0
- data/lib/badline/pet/bus.rb +222 -0
- data/lib/badline/pet/canvas.rb +97 -0
- data/lib/badline/pet/cassettes.rb +63 -0
- data/lib/badline/pet/characters.rb +32 -0
- data/lib/badline/pet/cpu.rb +10 -0
- data/lib/badline/pet/crtc_video.rb +155 -0
- data/lib/badline/pet/discrete_video.rb +83 -0
- data/lib/badline/pet/ieee_drive.rb +76 -0
- data/lib/badline/pet/ieee_ports.rb +77 -0
- data/lib/badline/pet/ieee_traps.rb +80 -0
- data/lib/badline/pet/keyboard_ports.rb +74 -0
- data/lib/badline/pet/keyed_modules.rb +88 -0
- data/lib/badline/pet/keyed_parts.rb +127 -0
- data/lib/badline/pet/keyed_state.rb +42 -0
- data/lib/badline/pet/model.rb +163 -0
- data/lib/badline/pet/rom_sets.rb +89 -0
- data/lib/badline/pet/sound.rb +128 -0
- data/lib/badline/pet/via.rb +10 -0
- data/lib/badline/pet/via_ports.rb +19 -0
- data/lib/badline/pet.rb +244 -0
- data/lib/badline/plus4/keyed_state.rb +5 -3
- data/lib/badline/plus4/tcbm_drive.rb +14 -0
- data/lib/badline/plus4.rb +1 -1
- data/lib/badline/roms/README +169 -0
- data/lib/badline/roms/dos2031.rom +0 -0
- data/lib/badline/roms/pet/basic1.rom +0 -0
- data/lib/badline/roms/pet/basic2.rom +0 -0
- data/lib/badline/roms/pet/basic4.rom +0 -0
- data/lib/badline/roms/pet/characters-din.rom +0 -0
- data/lib/badline/roms/pet/characters1.rom +0 -0
- data/lib/badline/roms/pet/characters2.rom +0 -0
- data/lib/badline/roms/pet/editor1.rom +0 -0
- data/lib/badline/roms/pet/editor2-b.rom +0 -0
- data/lib/badline/roms/pet/editor2-n.rom +0 -0
- data/lib/badline/roms/pet/editor4-40-n-50hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-40-n-60hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz-0283.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz-8296.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-60hz.rom +0 -0
- data/lib/badline/roms/pet/editor4-b.rom +0 -0
- data/lib/badline/roms/pet/editor4-n.rom +0 -0
- data/lib/badline/roms/pet/kernal1.rom +0 -0
- data/lib/badline/roms/pet/kernal2.rom +0 -0
- data/lib/badline/roms/pet/kernal4.rom +0 -0
- data/lib/badline/snapshot/container.rb +6 -1
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +17 -3
- data/lib/badline/snapshot/image.rb +28 -10
- data/lib/badline/snapshot/pet_setup.rb +88 -0
- data/lib/badline/snapshot/storage_setup.rb +7 -1
- data/lib/badline/snapshot/vice/pet.rb +117 -0
- data/lib/badline/snapshot/vice/pet_cpu.rb +67 -0
- data/lib/badline/snapshot/vice/pet_crtc.rb +246 -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 +150 -0
- data/lib/badline/snapshot/vice/pet_via.rb +164 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/snapshot.rb +9 -5
- data/lib/badline/storage/d80_image.rb +48 -0
- data/lib/badline/storage/d82_image.rb +21 -0
- data/lib/badline/storage/tap.rb +16 -3
- data/lib/badline/storage.rb +2 -0
- data/lib/badline/support/pet_checkpoint.rb +38 -0
- data/lib/badline/timing.rb +5 -4
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +7 -2
- data/lib/badline/vic20/bus/keyed_state.rb +5 -4
- data/lib/badline/vic20/bus.rb +5 -0
- data/lib/badline/vic20.rb +1 -1
- metadata +70 -1
|
@@ -34,7 +34,7 @@ module Badline
|
|
|
34
34
|
# of a machine in memory (.of).
|
|
35
35
|
class Image
|
|
36
36
|
MACHINE = "BL-MACHINE"
|
|
37
|
-
FAMILIES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4" }.freeze
|
|
37
|
+
FAMILIES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4", "pet" => "PET" }.freeze
|
|
38
38
|
|
|
39
39
|
attr_reader :container
|
|
40
40
|
|
|
@@ -53,15 +53,18 @@ module Badline
|
|
|
53
53
|
new(Container.new(computer.save_modules, machine: machine_name(computer)))
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
# The snapshot with VICE's modules ahead of the BL-* ones for a C64
|
|
57
|
-
# so x64sc opens it too. Raises FormatError for a
|
|
58
|
-
# snapshots badline doesn't write for xplus4.
|
|
56
|
+
# The snapshot with VICE's modules ahead of the BL-* ones for a C64
|
|
57
|
+
# or a PET, so x64sc or xpet opens it too. Raises FormatError for a
|
|
58
|
+
# Plus/4, whose snapshots badline doesn't write for xplus4.
|
|
59
59
|
def self.exported(computer)
|
|
60
60
|
raise FormatError, "badline doesn't export a Plus/4's snapshot for VICE" if computer.family == :plus4
|
|
61
61
|
|
|
62
62
|
computer.settle_for_snapshot!
|
|
63
63
|
sections = computer.save_modules
|
|
64
|
-
|
|
64
|
+
case computer.family
|
|
65
|
+
when :c64 then sections = Vice.export(computer) + sections
|
|
66
|
+
when :pet then sections = Vice::PET.export(computer, sections) + sections
|
|
67
|
+
end
|
|
65
68
|
new(Container.new(sections, machine: machine_name(computer)))
|
|
66
69
|
end
|
|
67
70
|
|
|
@@ -71,6 +74,7 @@ module Badline
|
|
|
71
74
|
when :vic20 then Container::VIC20
|
|
72
75
|
when :c128 then Container::C128
|
|
73
76
|
when :plus4 then Container::PLUS4
|
|
77
|
+
when :pet then Container::PET
|
|
74
78
|
else Container::MACHINE
|
|
75
79
|
end
|
|
76
80
|
end
|
|
@@ -82,6 +86,7 @@ module Badline
|
|
|
82
86
|
when "vic20" then Vic20Setup.read_fields(machine)
|
|
83
87
|
when "c128" then C128Setup.read_fields(machine)
|
|
84
88
|
when "plus4" then Plus4Setup.read_fields(machine)
|
|
89
|
+
when "pet" then PETSetup.read_fields(machine)
|
|
85
90
|
else C64Setup.read_fields(machine)
|
|
86
91
|
end
|
|
87
92
|
end
|
|
@@ -97,15 +102,16 @@ module Badline
|
|
|
97
102
|
|
|
98
103
|
# A new machine, built as the snapshot's was and restored from it. A
|
|
99
104
|
# badline snapshot restores from its BL-* modules alone, and a VICE
|
|
100
|
-
# one through the modules badline reads
|
|
101
|
-
# modules aren't read. Yields a line for
|
|
102
|
-
# warns it without a block. `detached`
|
|
103
|
-
# files (Keyed::Modules).
|
|
105
|
+
# one through the modules badline reads, x64sc's or xpet's. xvic's,
|
|
106
|
+
# x128's and xplus4's own modules aren't read. Yields a line for
|
|
107
|
+
# each thing left out, or warns it without a block. `detached`
|
|
108
|
+
# restores it without the host's files (Keyed::Modules).
|
|
104
109
|
def load(detached: false, &)
|
|
105
110
|
refuse_old_layout
|
|
106
111
|
raise FormatError, "badline reads only its own VIC-20 snapshots, not xvic's" if vic20? && !badline?
|
|
107
112
|
raise FormatError, "badline reads only its own C128 snapshots, not x128's" if c128? && !badline?
|
|
108
113
|
raise FormatError, "badline reads only its own Plus/4 snapshots, not xplus4's" if plus4? && !badline?
|
|
114
|
+
return load_xpet(&) if pet? && !badline?
|
|
109
115
|
return load_vice(&) unless badline?
|
|
110
116
|
|
|
111
117
|
modules = Keyed::Modules.new(sections, detached:)
|
|
@@ -123,12 +129,18 @@ module Badline
|
|
|
123
129
|
# Whether the machine is of the 264 series, as the container names it.
|
|
124
130
|
def plus4? = @container.machine == Container::PLUS4
|
|
125
131
|
|
|
132
|
+
# Whether the machine is a PET, as the container names it.
|
|
133
|
+
def pet? = @container.machine == Container::PET
|
|
134
|
+
|
|
126
135
|
# Restores `computer` from the snapshot, and returns the Report. The
|
|
127
136
|
# snapshot goes into a new machine first, so one that fails, or is of
|
|
128
137
|
# a machine built another way, leaves `computer` as it was.
|
|
129
138
|
def restore(computer, &)
|
|
130
139
|
refuse_old_layout
|
|
131
|
-
report = badline?
|
|
140
|
+
report = if badline? then restore_modules(computer)
|
|
141
|
+
elsif pet? then Vice::PET::Restore.import(@container, computer)
|
|
142
|
+
else Vice::Restore.import(@container, computer)
|
|
143
|
+
end
|
|
132
144
|
tell(report, &)
|
|
133
145
|
report
|
|
134
146
|
end
|
|
@@ -141,6 +153,12 @@ module Badline
|
|
|
141
153
|
computer
|
|
142
154
|
end
|
|
143
155
|
|
|
156
|
+
def load_xpet(&)
|
|
157
|
+
computer = Vice::PET.setup(@container).build
|
|
158
|
+
tell(Vice::PET::Restore.apply(@container, computer), &)
|
|
159
|
+
computer
|
|
160
|
+
end
|
|
161
|
+
|
|
144
162
|
# The snapshot goes into a new machine first, so one that fails
|
|
145
163
|
# leaves `computer` as it was.
|
|
146
164
|
def restore_modules(computer)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
# How a PET was built: its model's name, one of PET::Model::ALL, its
|
|
6
|
+
# RAM below $8000 in K and its editor's frame rate, the arguments
|
|
7
|
+
# PET.new takes, the ROMs it runs by file: BASIC, the editor, the
|
|
8
|
+
# KERNAL and the characters, and the 8296 board's jumpers, by name and
|
|
9
|
+
# comma-separated.
|
|
10
|
+
PETSetup = Data.define(:model, :ram_kb, :refresh_hz, :basic, :editor, :kernal, :characters, :jumpers)
|
|
11
|
+
|
|
12
|
+
class PETSetup
|
|
13
|
+
MODELS = PET::Model::ALL.map(&:name).freeze
|
|
14
|
+
|
|
15
|
+
REFRESH_RATES = [50, 60].freeze
|
|
16
|
+
|
|
17
|
+
# The setup of `machine`.
|
|
18
|
+
def self.of(machine)
|
|
19
|
+
model = machine.model
|
|
20
|
+
refresh_hz = machine.refresh_hz
|
|
21
|
+
new(model: model.name, ram_kb: machine.ram.length >> 10, refresh_hz:, basic: model.basic,
|
|
22
|
+
editor: model.editor(refresh_hz), kernal: model.kernal, characters: model.characters,
|
|
23
|
+
jumpers: machine.jumpers.join(","))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The model by name, the RAM, the frame rate, the ROMs and the
|
|
27
|
+
# jumpers where a board has any, in a keyed snapshot module.
|
|
28
|
+
def write_fields(out)
|
|
29
|
+
out.text("model", model).int("ram_kb", ram_kb).int("refresh_hz", refresh_hz)
|
|
30
|
+
out.text("rom.basic", basic).text("rom.editor", editor).text("rom.kernal", kernal)
|
|
31
|
+
out.text("rom.characters", characters)
|
|
32
|
+
out.text("jumpers", jumpers) unless jumpers.empty?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# A ROM the snapshot doesn't name is the model's own, and a board
|
|
36
|
+
# without jumpers named has none fitted.
|
|
37
|
+
def self.read_fields(input)
|
|
38
|
+
profile = PET::Model.named(input.choice("model", MODELS, PET::Model::DEFAULT))
|
|
39
|
+
ram_kb = input.int("ram_kb", profile.ram_kb)
|
|
40
|
+
unless PET::Model.ram_fits?(profile, ram_kb)
|
|
41
|
+
raise FormatError, "the snapshot is of a PET with #{ram_kb}K of RAM, which badline doesn't build"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
refresh_hz = input.int("refresh_hz", profile.crtc? ? 50 : 60)
|
|
45
|
+
unless REFRESH_RATES.include?(refresh_hz)
|
|
46
|
+
raise FormatError, "the snapshot is of a PET at #{refresh_hz} Hz, which badline doesn't build"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
refresh_hz = 60 unless profile.crtc?
|
|
50
|
+
new(model: profile.name, ram_kb:, refresh_hz:, basic: input.text("rom.basic", profile.basic),
|
|
51
|
+
editor: input.text("rom.editor", profile.editor(refresh_hz)),
|
|
52
|
+
kernal: input.text("rom.kernal", profile.kernal),
|
|
53
|
+
characters: input.text("rom.characters", profile.characters), jumpers: input.text("jumpers", ""))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# A machine built this way, at power-on, with the BASIC, editor and
|
|
57
|
+
# character ROMs PET::ROMSets names for its ROMs.
|
|
58
|
+
def build
|
|
59
|
+
version = PET::ROMSets::BASICS.find { |_, roms| roms.first == basic }&.first
|
|
60
|
+
PET.new(model:, ram_kb:, refresh_hz:, basic: version, editor: PET::ROMSets::EDITORS.key(editor),
|
|
61
|
+
characters: fitted_characters, jumpers: jumpers.split(","))
|
|
62
|
+
rescue ArgumentError => e
|
|
63
|
+
raise FormatError, "the snapshot is of a #{self}, which badline doesn't build: #{e.message}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# The ROMs are named where they aren't the model's own, and the
|
|
67
|
+
# jumpers where any are fitted.
|
|
68
|
+
def to_s
|
|
69
|
+
text = "#{model} with #{ram_kb}K at #{refresh_hz} Hz"
|
|
70
|
+
text = "#{text} jumpered #{jumpers}" unless jumpers.empty?
|
|
71
|
+
roms = [basic, editor, kernal, characters]
|
|
72
|
+
profile = PET::Model.named(model)
|
|
73
|
+
stock = [profile.basic, profile.editor(refresh_hz), profile.kernal, profile.characters]
|
|
74
|
+
roms == stock ? text : "#{text} running #{roms.join(', ')}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# The name of a character ROM other than the model's own, nil for its
|
|
80
|
+
# own.
|
|
81
|
+
def fitted_characters
|
|
82
|
+
return nil if characters == PET::Model.named(model).characters
|
|
83
|
+
|
|
84
|
+
PET::ROMSets::CHARACTERS.key(characters) || characters
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -11,6 +11,8 @@ module Badline
|
|
|
11
11
|
D71 = 2
|
|
12
12
|
D81 = 3
|
|
13
13
|
T64_ARCHIVE = 4
|
|
14
|
+
D80 = 5
|
|
15
|
+
D82 = 6
|
|
14
16
|
|
|
15
17
|
module_function
|
|
16
18
|
|
|
@@ -18,6 +20,8 @@ module Badline
|
|
|
18
20
|
return D71 if image.is_a?(Storage::D71Image)
|
|
19
21
|
return D64 if image.is_a?(Storage::D64Image)
|
|
20
22
|
return D81 if image.is_a?(Storage::D81Image)
|
|
23
|
+
return D82 if image.is_a?(Storage::D82Image)
|
|
24
|
+
return D80 if image.is_a?(Storage::D80Image)
|
|
21
25
|
|
|
22
26
|
raise ArgumentError, "#{image.class} can't go in a snapshot"
|
|
23
27
|
end
|
|
@@ -37,7 +41,7 @@ module Badline
|
|
|
37
41
|
end
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
KIND_NAMES = %w[directory d64 d71 d81 t64].freeze
|
|
44
|
+
KIND_NAMES = %w[directory d64 d71 d81 t64 d80 d82].freeze
|
|
41
45
|
|
|
42
46
|
# Opens the storage write_fields wrote: the same directory by its
|
|
43
47
|
# path, an archive with the bytes it held, or a disk image with the
|
|
@@ -55,6 +59,8 @@ module Badline
|
|
|
55
59
|
case kind
|
|
56
60
|
when D64 then Storage::D64Image.new(path, read_only:, bytes:)
|
|
57
61
|
when D71 then Storage::D71Image.new(path, read_only:, bytes:)
|
|
62
|
+
when D80 then Storage::D80Image.new(path, read_only:, bytes:)
|
|
63
|
+
when D82 then Storage::D82Image.new(path, read_only:, bytes:)
|
|
58
64
|
else Storage::D81Image.new(path, read_only:, bytes:)
|
|
59
65
|
end
|
|
60
66
|
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/snapshot/vice/pet_cpu"
|
|
4
|
+
require "badline/snapshot/vice/pet_mem"
|
|
5
|
+
require "badline/snapshot/vice/pet_pias"
|
|
6
|
+
require "badline/snapshot/vice/pet_via"
|
|
7
|
+
require "badline/snapshot/vice/pet_crtc"
|
|
8
|
+
require "badline/snapshot/vice/pet_restore"
|
|
9
|
+
|
|
10
|
+
module Badline
|
|
11
|
+
module Snapshot
|
|
12
|
+
module Vice
|
|
13
|
+
# xpet's modules for the PET: the CPU, the RAM, the CRTC, both PIAs
|
|
14
|
+
# and the VIA, written from badline's machine and read into it. Each
|
|
15
|
+
# module's file says what maps and what is approximated. They go
|
|
16
|
+
# through the PET's own keyed fields (Keyed), which a save writes and a
|
|
17
|
+
# load reads, so they read no chip's insides.
|
|
18
|
+
module PET
|
|
19
|
+
# The models badline builds by the board PETMEM names (PETMem): the
|
|
20
|
+
# discrete board is a 2001's where xpet's settings say so, and a
|
|
21
|
+
# 3032 otherwise.
|
|
22
|
+
MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::CBM8096 => "8096",
|
|
23
|
+
PETMem::CBM8296 => "8296" }.freeze
|
|
24
|
+
# The modules xpet resets the machine without, for the parts badline
|
|
25
|
+
# doesn't model in VICE's terms, each as xpet leaves them with
|
|
26
|
+
# nothing attached: no file system drive open, both cassette ports
|
|
27
|
+
# on with a datasette in #1, and nothing in the user port.
|
|
28
|
+
PERIPHERALS = [
|
|
29
|
+
["FSDRIVE", 0, 0, Array.new(258, 0)], ["TAPEPORT", 1, 0, [0x01, 0x01, 0x01]], ["USERPORT", 1, 0, [0x01, 0x00]]
|
|
30
|
+
].freeze
|
|
31
|
+
# The cycles a frame below which a CRTC editor runs at 60 Hz.
|
|
32
|
+
SIXTY_HZ_FRAME = 18_333
|
|
33
|
+
# The 8032's 50 Hz editors by the cycles a line they set up: xpet's
|
|
34
|
+
# own, 901474-04 dated 0283, sets 64.
|
|
35
|
+
EDITOR_0283_LINE = 64
|
|
36
|
+
|
|
37
|
+
module_function
|
|
38
|
+
|
|
39
|
+
# The modules xpet 3.10 needs, in its order, describing `machine`,
|
|
40
|
+
# which Machine::Settling has brought between instructions, from
|
|
41
|
+
# its keyed modules, `sections`.
|
|
42
|
+
def export(machine, sections)
|
|
43
|
+
readers = {}
|
|
44
|
+
sections.each { |section| readers[section.name] = Keyed::Reader.parse(section) }
|
|
45
|
+
[MainCPU.export(machine), CPU6809.export(machine), PETMem.export(machine),
|
|
46
|
+
CRTC.export(machine, reader(readers, "BL-CRTC"), reader(readers, "BL-VIDEO")),
|
|
47
|
+
PIAs.export("PIA1", machine.pia1, reader(readers, "BL-PIA1")),
|
|
48
|
+
PIAs.export("PIA2", machine.pia2, reader(readers, "BL-PIA2")), VIA.export(reader(readers, "BL-VIA"))] +
|
|
49
|
+
PERIPHERALS.map { |name, major, minor, bytes| FieldWriter.new.bytes(bytes).section(name, major, minor) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def reader(readers, name) = readers[name] || Keyed::Reader.missing(name)
|
|
53
|
+
|
|
54
|
+
# How the machine xpet saved was built: the model from PETMEM's
|
|
55
|
+
# board, the RAM, and on a CRTC board the editor's frame rate, and
|
|
56
|
+
# for an 8032's 50 Hz editor which one, from the CRTC's registers.
|
|
57
|
+
# xpet doesn't save the keyboard, the ROMs or an 8296's jumpers, so
|
|
58
|
+
# a discrete board is a 3032 with the graphics keyboard unless it is
|
|
59
|
+
# a 2001, each model has its own ROMs, and an 8296 none of its
|
|
60
|
+
# jumpers, as xpet's own has.
|
|
61
|
+
def setup(container)
|
|
62
|
+
memory = memory(container)
|
|
63
|
+
ram_kb = PETMem.ram_kb(memory)
|
|
64
|
+
name = model_name(memory)
|
|
65
|
+
profile = Badline::PET::Model.named(name)
|
|
66
|
+
unless Badline::PET::Model.ram_fits?(profile, ram_kb)
|
|
67
|
+
raise FormatError, "#{memory}: a #{name} with #{ram_kb}K of RAM, which badline doesn't build"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
crtc = container[CRTC::NAME]
|
|
71
|
+
crtc = nil unless crtc.nil? || CRTC.reads?(crtc)
|
|
72
|
+
refresh_hz = refresh_hz(crtc, profile)
|
|
73
|
+
profile = Badline::PET::Model.upgraded(profile, nil, editor(crtc, profile, refresh_hz))
|
|
74
|
+
PETSetup.new(model: name, ram_kb:, refresh_hz:, basic: profile.basic, editor: profile.editor(refresh_hz),
|
|
75
|
+
kernal: profile.kernal, characters: profile.characters, jumpers: "")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def memory(container)
|
|
79
|
+
section = container[PETMem::NAME]
|
|
80
|
+
raise FormatError, "the snapshot has no #{PETMem::NAME} module" if section.nil?
|
|
81
|
+
raise FormatError, "#{section}: badline doesn't read this version" unless PETMem.reads?(section)
|
|
82
|
+
|
|
83
|
+
section
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def model_name(memory)
|
|
87
|
+
board = PETMem.board_of(memory)
|
|
88
|
+
return PETMem.pet2001?(memory) ? "2001" : "3032" if board == PETMem::DISCRETE
|
|
89
|
+
|
|
90
|
+
name = MODELS[board]
|
|
91
|
+
return name unless name.nil?
|
|
92
|
+
|
|
93
|
+
what = board == PETMem::SUPERPET ? "a SuperPET" : "board #{board}"
|
|
94
|
+
raise FormatError, "#{memory}: #{what}, which badline doesn't build"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The editor's frame rate, from the cycles the CRTC's registers set
|
|
98
|
+
# up a frame.
|
|
99
|
+
def refresh_hz(crtc, profile)
|
|
100
|
+
return 60 unless profile.crtc?
|
|
101
|
+
return 50 if crtc.nil?
|
|
102
|
+
|
|
103
|
+
registers = CRTC.registers(crtc)
|
|
104
|
+
(registers[0] + 1) * CRTC.frame_lines(registers) > SIXTY_HZ_FRAME ? 50 : 60
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The editor by ROMSets' name where the model's own doesn't set up
|
|
108
|
+
# the CRTC's frame, or nil.
|
|
109
|
+
def editor(crtc, profile, refresh_hz)
|
|
110
|
+
return nil if crtc.nil? || refresh_hz != 50 || profile.video != :crtc80
|
|
111
|
+
|
|
112
|
+
CRTC.registers(crtc)[0] + 1 == EDITOR_0283_LINE ? "4-80-b-50hz-0283" : nil
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module PET
|
|
7
|
+
# The 6502 as xpet 3.10 saves it, MAINCPU 1.3: the clock, the
|
|
8
|
+
# registers, the last opcode and two words after it, then the IRQ
|
|
9
|
+
# and NMI bookkeeping: five clocks, the IRQ and NMI source counts and
|
|
10
|
+
# the pending interrupts. xpet's MAINCPU has no BA line to save.
|
|
11
|
+
#
|
|
12
|
+
# Written: the clock and the registers, which between instructions
|
|
13
|
+
# are the whole CPU, with the IRQ sources that pull the line and a
|
|
14
|
+
# pending IRQ. VICE's interrupt clocks are written as long past.
|
|
15
|
+
#
|
|
16
|
+
# Read: the clock and the registers. The CPU starts on an
|
|
17
|
+
# instruction boundary, as xpet's snapshot is taken on one.
|
|
18
|
+
module MainCPU
|
|
19
|
+
NAME = "MAINCPU"
|
|
20
|
+
MAJOR = 1
|
|
21
|
+
MINOR = 3
|
|
22
|
+
IK_IRQ = 0x02
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
27
|
+
|
|
28
|
+
def export(machine)
|
|
29
|
+
cpu = machine.cpu
|
|
30
|
+
fields = FieldWriter.new.qword(machine.cycles)
|
|
31
|
+
[cpu.a, cpu.x, cpu.y, cpu.stack_pointer].each { |register| fields.byte(register) }
|
|
32
|
+
fields.word(cpu.program_counter).byte(cpu.p)
|
|
33
|
+
fields.dword(0).dword(0).dword(0)
|
|
34
|
+
5.times { fields.qword(0) }
|
|
35
|
+
irqs = [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?].count(true)
|
|
36
|
+
fields.dword(irqs).dword(0).dword(irqs.positive? ? IK_IRQ : 0)
|
|
37
|
+
fields.section(NAME, MAJOR, MINOR)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The clock, the registers by their BL-CPU keys, in a keyed
|
|
41
|
+
# module's writer.
|
|
42
|
+
def import(section, out)
|
|
43
|
+
fields = FieldReader.new(section)
|
|
44
|
+
clock = fields.qword
|
|
45
|
+
out.int("a", fields.byte).int("x", fields.byte).int("y", fields.byte).int("sp", fields.byte)
|
|
46
|
+
out.int("pc", fields.word).int("p", fields.byte).int("cycles", clock)
|
|
47
|
+
clock
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The SuperPET's 6809, CPU6809 1.1, which xpet saves on every model
|
|
52
|
+
# and won't load a snapshot without: the clock and the registers as
|
|
53
|
+
# an xpet with no SuperPET board leaves them. badline doesn't read
|
|
54
|
+
# it.
|
|
55
|
+
module CPU6809
|
|
56
|
+
NAME = "CPU6809"
|
|
57
|
+
|
|
58
|
+
module_function
|
|
59
|
+
|
|
60
|
+
def export(machine)
|
|
61
|
+
FieldWriter.new.qword(machine.cycles).zeros(11).byte(0x04).zeros(7).section(NAME, 1, 1)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module PET
|
|
7
|
+
# CRTC 1.2, which xpet saves on every model, the discrete boards'
|
|
8
|
+
# video too, as a 6545 with the registers fixed: the board's screen
|
|
9
|
+
# settings, the registers, the one selected, the beam's character,
|
|
10
|
+
# row and line in the row, whether the frame is in its vertical
|
|
11
|
+
# adjust, the row's start in screen RAM, the lines of vertical sync
|
|
12
|
+
# left, whether the rows show, the picture's size, the lines in the
|
|
13
|
+
# frame and the line in it, then xpet's picture.
|
|
14
|
+
#
|
|
15
|
+
# Written: the registers and the beam as badline's CRTC, or its
|
|
16
|
+
# discrete video, has them. The picture is left blank, as xpet
|
|
17
|
+
# paints it again.
|
|
18
|
+
#
|
|
19
|
+
# Read: the registers and the beam, with what follows from them: the
|
|
20
|
+
# memory address, the characters since horizontal sync and whether
|
|
21
|
+
# the character shows. The picture starts again with the next
|
|
22
|
+
# frame.
|
|
23
|
+
module CRTC
|
|
24
|
+
NAME = "CRTC"
|
|
25
|
+
MAJOR = 1
|
|
26
|
+
MINOR = 2
|
|
27
|
+
# The registers xpet saves, two more than badline's CRTC has.
|
|
28
|
+
REGISTERS = 20
|
|
29
|
+
# Where the selected register is in the module, and the beam after
|
|
30
|
+
# it.
|
|
31
|
+
SELECTED = 0x23
|
|
32
|
+
# The board's screen settings xpet saves ahead of the registers:
|
|
33
|
+
# the screen RAM's masks and the bits of the frame's start address
|
|
34
|
+
# it latches (BOARD_ATTRIBUTES), then the bytes a character reads
|
|
35
|
+
# and whether the board is a 2001's.
|
|
36
|
+
SETTINGS = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x1f].freeze
|
|
37
|
+
# The 8296's, whose screen RAM spans 4K.
|
|
38
|
+
SETTINGS_8296 = [0xff, 0x0f, 0x00, 0x20, 0x00, 0x20, 0xff, 0x0f, 0xff, 0x1f].freeze
|
|
39
|
+
# The 2001's and 3000 series' video as xpet's CRTC runs it: 64
|
|
40
|
+
# cycles a line, 32 rows of 8 lines and 4 more, the first 25 rows
|
|
41
|
+
# shown.
|
|
42
|
+
DISCRETE_REGISTERS = [0x3f, 0x28, 0x32, 0x08, 0x1f, 0x04, 0x19, 0x1d, 0x00, 0x07, 0x00, 0x00, 0x10, 0x00,
|
|
43
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00].freeze
|
|
44
|
+
DISCRETE_HEIGHT = 216
|
|
45
|
+
DISCRETE_LINE = 64
|
|
46
|
+
DISCRETE_FRAME = DISCRETE_LINE * 260
|
|
47
|
+
# How many cycles badline's beam runs ahead of xpet's, where both
|
|
48
|
+
# drop the vertical drive line on the same cycle.
|
|
49
|
+
DISCRETE_LEAD = 20
|
|
50
|
+
CRTC_LEAD = -3
|
|
51
|
+
HEIGHT = 272
|
|
52
|
+
# A byte xpet writes after the picture's size.
|
|
53
|
+
AFTER_SIZE = 0x20
|
|
54
|
+
# The memory address's bits that pick a byte of screen RAM.
|
|
55
|
+
SCREEN_BITS = 0x0fff
|
|
56
|
+
# The bits above them, which the boards decode as attributes.
|
|
57
|
+
HIGH_BITS = 0x3000
|
|
58
|
+
|
|
59
|
+
# Where the beam is, in xpet's terms: the character, the row and
|
|
60
|
+
# its line, the lines of vertical sync left, whether the rows show,
|
|
61
|
+
# the row's start address and the line in the frame.
|
|
62
|
+
Beam = Data.define(:column, :row, :raster, :vsync, :shown, :start, :frame_line)
|
|
63
|
+
|
|
64
|
+
module_function
|
|
65
|
+
|
|
66
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
67
|
+
|
|
68
|
+
def width(model) = model.video == :crtc80 ? 704 : 384
|
|
69
|
+
|
|
70
|
+
def height(model) = model.video == :discrete ? DISCRETE_HEIGHT : HEIGHT
|
|
71
|
+
|
|
72
|
+
def columns(model) = model.video == :crtc80 ? 2 : 1
|
|
73
|
+
|
|
74
|
+
# The frame's start address bits xpet latches on the board: MA12,
|
|
75
|
+
# which inverts the 80-column boards' picture.
|
|
76
|
+
def attributes(model) = model.video == :crtc80 ? 0x1000 : 0
|
|
77
|
+
|
|
78
|
+
def export(machine, crtc_input, video_input)
|
|
79
|
+
model = machine.model
|
|
80
|
+
discrete = model.video == :discrete
|
|
81
|
+
registers = discrete ? DISCRETE_REGISTERS : crtc_registers(crtc_input)
|
|
82
|
+
beam = if discrete
|
|
83
|
+
discrete_beam(video_input)
|
|
84
|
+
else
|
|
85
|
+
crtc_beam(run_on(machine, crtc_input, -CRTC_LEAD % frame_cycles(registers)), registers)
|
|
86
|
+
end
|
|
87
|
+
fields = FieldWriter.new.bytes(model.cbm8296? ? SETTINGS_8296 : SETTINGS).word(attributes(model))
|
|
88
|
+
fields.byte(0).byte(columns(model))
|
|
89
|
+
fields.byte(model.basic1? ? 1 : 0).bytes(registers).byte(discrete ? 0 : crtc_input.int("selected", 0))
|
|
90
|
+
beam_fields(fields, beam, registers, model)
|
|
91
|
+
picture(fields, model)
|
|
92
|
+
fields.section(NAME, MAJOR, MINOR)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def crtc_registers(input)
|
|
96
|
+
registers = input.bytes("registers", "".b).bytes
|
|
97
|
+
Array.new(REGISTERS) { |index| registers[index] || 0 }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def beam_fields(fields, beam, registers, model)
|
|
101
|
+
fields.byte(beam.column).byte(beam.row).byte(beam.raster).zeros(2).flag(beam.raster.zero?)
|
|
102
|
+
fields.word(beam.start & attributes(model)).word((beam.start & SCREEN_BITS) * columns(model))
|
|
103
|
+
fields.byte(beam.vsync).byte(0).flag(beam.shown)
|
|
104
|
+
fields.word(width(model)).word(height(model)).byte(AFTER_SIZE).zeros(3)
|
|
105
|
+
shown = registers[6] * (registers[9] + 1)
|
|
106
|
+
fields.byte([(height(model) - shown) / 2, 0].max).byte(0)
|
|
107
|
+
fields.word(frame_lines(registers)).word(beam.frame_line).byte(0)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Blank, as xpet paints it again: the line it draws, its size twice
|
|
111
|
+
# over, and four lines more than it shows.
|
|
112
|
+
def picture(fields, model)
|
|
113
|
+
width = width(model)
|
|
114
|
+
height = height(model)
|
|
115
|
+
fields.dword(0).dword(width).dword(height).dword(width).zeros(width * (height + 4))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def frame_lines(registers) = ((registers[4] + 1) * (registers[9] + 1)) + registers[5]
|
|
119
|
+
|
|
120
|
+
def frame_cycles(registers) = (registers[0] + 1) * frame_lines(registers)
|
|
121
|
+
|
|
122
|
+
# The BL-CRTC fields of a CRTC loaded with those in `input` and run
|
|
123
|
+
# `cycles` cycles on its own, in a machine built as `machine` was.
|
|
124
|
+
def run_on(machine, input, cycles)
|
|
125
|
+
crtc = PETSetup.of(machine).build.crtc
|
|
126
|
+
crtc.load_fields(input)
|
|
127
|
+
cycles.times { crtc.cycle! }
|
|
128
|
+
Keyed::Reader.parse(Keyed.section("BL-CRTC") { |out| crtc.save_fields(out) })
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Writes the BL-CRTC fields `input` holds to `out`.
|
|
132
|
+
def copy(input, out)
|
|
133
|
+
out.bytes("registers", input.bytes("registers", "".b)).int("selected", input.int("selected", 0))
|
|
134
|
+
%w[column since_hsync line row vsync_lines row_start address].each { |key| out.int(key, input.int(key, 0)) }
|
|
135
|
+
%w[adjusting row_display column_display].each { |key| out.bool(key, input.bool(key, false)) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Both move a row's start on at R1 of its last line, and badline's
|
|
139
|
+
# moves it again where the vertical adjust runs past R9 lines,
|
|
140
|
+
# which xpet's doesn't.
|
|
141
|
+
def crtc_beam(input, registers)
|
|
142
|
+
column = input.int("column", 0)
|
|
143
|
+
row = input.int("row", 0)
|
|
144
|
+
line = input.int("line", 0)
|
|
145
|
+
adjusting = input.bool("adjusting", false)
|
|
146
|
+
start = input.int("row_start", 0)
|
|
147
|
+
start = (start - registers[1]) & 0x3fff if adjusting && moved?(line, column, registers)
|
|
148
|
+
rows = registers[4] + 1
|
|
149
|
+
lines = registers[9] + 1
|
|
150
|
+
Beam.new(column:, row: adjusting ? rows : row, raster: adjusting ? 0 : line,
|
|
151
|
+
vsync: input.int("vsync_lines", 0), shown: input.bool("row_display", true), start:,
|
|
152
|
+
frame_line: adjusting ? (rows * lines) + line : (row * lines) + line)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Whether the row's start has moved on by `column` of `line`.
|
|
156
|
+
def moved?(line, column, registers)
|
|
157
|
+
line > registers[9] || (line == registers[9] && column > registers[1])
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The discrete video's line in the frame, in xpet's fixed rows: the
|
|
161
|
+
# rows show until line 200, vertical sync runs from row 29 for 16
|
|
162
|
+
# lines, and the 4 lines past row 31 are the vertical adjust.
|
|
163
|
+
def discrete_beam(input)
|
|
164
|
+
cycle = ((input.int("line", 0) * DISCRETE_LINE) + input.int("column", 0) - DISCRETE_LEAD) % DISCRETE_FRAME
|
|
165
|
+
line = cycle / DISCRETE_LINE
|
|
166
|
+
row = [line >> 3, 32].min
|
|
167
|
+
adjusting = row == 32
|
|
168
|
+
raster = adjusting ? 0 : line & 0x07
|
|
169
|
+
column = cycle % DISCRETE_LINE
|
|
170
|
+
sync = line - (DISCRETE_REGISTERS[7] * 8)
|
|
171
|
+
moved = !adjusting && moved?(raster, column, DISCRETE_REGISTERS)
|
|
172
|
+
Beam.new(column:, row:, raster:, vsync: sync.between?(0, 15) ? 16 - sync : 0, shown: line < 200,
|
|
173
|
+
start: 0x1000 + ((moved ? row + 1 : row) * 40), frame_line: line)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# The beam the module holds, with the row's start as the module
|
|
177
|
+
# has it: the attribute bits latched in the high word and the
|
|
178
|
+
# offset into screen RAM, in bytes, in the low.
|
|
179
|
+
def beam(section)
|
|
180
|
+
fields = FieldReader.new(section).skip(SELECTED + 1)
|
|
181
|
+
column, row, raster = fields.bytes(3)
|
|
182
|
+
fields.skip(3)
|
|
183
|
+
latched = fields.word
|
|
184
|
+
offset = fields.word
|
|
185
|
+
vsync = fields.byte
|
|
186
|
+
fields.skip(1)
|
|
187
|
+
shown = fields.flag?
|
|
188
|
+
fields.skip(12)
|
|
189
|
+
Beam.new(column:, row:, raster:, vsync:, shown:, start: (latched << 16) | offset,
|
|
190
|
+
frame_line: fields.word)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def registers(section) = FieldReader.new(section).skip(SETTINGS.length + 5).bytes(REGISTERS)
|
|
194
|
+
|
|
195
|
+
def selected(section) = FieldReader.new(section).skip(SELECTED).byte
|
|
196
|
+
|
|
197
|
+
# How many cycles into its line xpet's beam is. A beam whose line
|
|
198
|
+
# has moved on a cycle before it starts holds -1.
|
|
199
|
+
def column_of(section)
|
|
200
|
+
column = beam(section).column
|
|
201
|
+
column < 0x80 ? column : column - 0x100
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# The CRTC's fields by their BL-CRTC keys at the start of the line
|
|
205
|
+
# the module's beam is in (#column_of), in a keyed module's writer,
|
|
206
|
+
# for a board that reads `columns` bytes a character with
|
|
207
|
+
# `attributes` latched from the frame's start.
|
|
208
|
+
def import(section, out, columns, attributes)
|
|
209
|
+
registers = registers(section)
|
|
210
|
+
beam = beam(section)
|
|
211
|
+
rows = registers[4] + 1
|
|
212
|
+
adjusting = beam.row >= rows
|
|
213
|
+
line = adjusting ? beam.frame_line - (rows * (registers[9] + 1)) : beam.raster
|
|
214
|
+
start = start_address(beam.start, registers, columns, attributes)
|
|
215
|
+
if adjusting
|
|
216
|
+
start += registers[1] if line > registers[9]
|
|
217
|
+
elsif moved?(line, column_of(section), registers)
|
|
218
|
+
start -= registers[1]
|
|
219
|
+
end
|
|
220
|
+
start &= 0x3fff
|
|
221
|
+
out.bytes("registers", registers.first(Chips::CRTC::MASKS.length)).int("selected", selected(section) & 0x1f)
|
|
222
|
+
out.int("column", 0).int("line", [line, 0].max).int("row", adjusting ? rows - 1 : beam.row)
|
|
223
|
+
out.int("since_hsync", registers[0] + 1 - registers[2]).int("vsync_lines", beam.vsync)
|
|
224
|
+
out.bool("adjusting", adjusting).bool("row_display", beam.shown).bool("column_display", true)
|
|
225
|
+
out.int("row_start", start).int("address", start)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# The row's start address from the module's (#beam): the bits
|
|
229
|
+
# that pick screen RAM from its offset, the attribute bits the
|
|
230
|
+
# board latches from its high word, and those above from R12.
|
|
231
|
+
def start_address(start, registers, columns, attributes)
|
|
232
|
+
high = (registers[12] << 8) & HIGH_BITS & ~attributes
|
|
233
|
+
high | ((start >> 16) & attributes) | (((start & 0xffff) / columns) & SCREEN_BITS)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# The discrete video's fields by their BL-VIDEO keys, in a keyed
|
|
237
|
+
# module's writer.
|
|
238
|
+
def import_discrete(section, out)
|
|
239
|
+
cycle = ((beam(section).frame_line * DISCRETE_LINE) + column_of(section) + DISCRETE_LEAD) % DISCRETE_FRAME
|
|
240
|
+
out.int("column", cycle % DISCRETE_LINE).int("line", cycle / DISCRETE_LINE)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|