badline 0.16.1 → 0.18.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 +213 -0
- data/CLAUDE.md +11 -7
- data/CONTRIBUTING.md +7 -1
- data/README.md +265 -26
- data/doc/media.md +5 -2
- data/doc/pinned-behaviour.md +189 -0
- data/doc/snapshot-format.md +616 -74
- data/doc/snapshots.md +192 -24
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +1 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +1 -0
- 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 +95 -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 +264 -0
- data/lib/badline/cbm2/sid_page.rb +19 -0
- data/lib/badline/cbm2.rb +270 -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/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/crtc.rb +3 -2
- data/lib/badline/chips/m6809/arithmetic.rb +140 -0
- data/lib/badline/chips/m6809/bus_steps.rb +223 -0
- data/lib/badline/chips/m6809/core.rb +149 -0
- data/lib/badline/chips/m6809/indexed.rb +82 -0
- data/lib/badline/chips/m6809/interrupts.rb +159 -0
- data/lib/badline/chips/m6809/keyed_state.rb +147 -0
- data/lib/badline/chips/m6809/opcodes.rb +159 -0
- data/lib/badline/chips/m6809/operations.rb +258 -0
- data/lib/badline/chips/m6809/plans.rb +143 -0
- data/lib/badline/chips/m6809/registers.rb +82 -0
- data/lib/badline/chips/m6809/stack.rb +140 -0
- data/lib/badline/chips/m6809.rb +13 -0
- data/lib/badline/chips/riot/fast_forward.rb +72 -0
- data/lib/badline/chips/riot/keyed_parts.rb +69 -0
- data/lib/badline/chips/riot/timer.rb +91 -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 +134 -0
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/waveform.rb +6 -6
- 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/chips/vic_i/keyed_parts.rb +7 -2
- data/lib/badline/chips/vic_i.rb +84 -21
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/datasette/keyed_state.rb +9 -1
- data/lib/badline/drives/datasette.rb +27 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model2031.rb +6 -0
- data/lib/badline/drives/model4040/disk.rb +62 -0
- data/lib/badline/drives/model4040/ip_bus.rb +46 -0
- data/lib/badline/drives/model4040/keyed_state.rb +57 -0
- data/lib/badline/drives/model4040/mechanism.rb +192 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +179 -0
- data/lib/badline/drives/model8050/board.rb +77 -0
- data/lib/badline/drives/model8050/disk.rb +163 -0
- data/lib/badline/drives/model8050/dos_board.rb +91 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +102 -0
- data/lib/badline/drives/model8050/idle.rb +181 -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 +163 -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 +131 -0
- data/lib/badline/drives/model9060/idle.rb +149 -0
- data/lib/badline/drives/model9060/keyed_state.rb +123 -0
- data/lib/badline/drives/model9060/parts.rb +98 -0
- data/lib/badline/drives/model9060/sasi_bus.rb +88 -0
- data/lib/badline/drives/model9060/sasi_commands.rb +168 -0
- data/lib/badline/drives/model9060/sasi_target.rb +161 -0
- data/lib/badline/drives/model9060.rb +195 -0
- data/lib/badline/frontend/app.rb +18 -3
- data/lib/badline/frontend/boot.rb +4 -3
- data/lib/badline/frontend/controls.rb +107 -15
- data/lib/badline/frontend/gamepads.rb +3 -3
- data/lib/badline/frontend/keys.rb +97 -2
- data/lib/badline/frontend/kim1_panel.rb +208 -0
- data/lib/badline/frontend/media_slots.rb +8 -1
- data/lib/badline/frontend/menu/drive_types.rb +56 -0
- data/lib/badline/frontend/menu/menu_dialogs.rb +61 -14
- data/lib/badline/frontend/menu/menu_media.rb +47 -14
- data/lib/badline/frontend/menu/menu_pages.rb +113 -4
- data/lib/badline/frontend/menu/menu_ports.rb +77 -4
- data/lib/badline/frontend/menu/pause_menu.rb +6 -2
- data/lib/badline/frontend/pet_keyboard.rb +45 -7
- data/lib/badline/frontend/screen.rb +1 -1
- data/lib/badline/frontend/screens.rb +40 -2
- data/lib/badline/frontend/teletype_pane.rb +152 -0
- data/lib/badline/frontend/timeline.rb +49 -11
- data/lib/badline/frontend.rb +3 -0
- 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/kim1/bus.rb +117 -0
- data/lib/badline/kim1/cpu.rb +25 -0
- data/lib/badline/kim1/host_terminal.rb +74 -0
- data/lib/badline/kim1/key_entry.rb +74 -0
- data/lib/badline/kim1/keyed_modules.rb +53 -0
- data/lib/badline/kim1/keyed_state.rb +133 -0
- data/lib/badline/kim1/panel.rb +176 -0
- data/lib/badline/kim1/rriots.rb +28 -0
- data/lib/badline/kim1/sound.rb +29 -0
- data/lib/badline/kim1/tape.rb +116 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +190 -0
- data/lib/badline/machine/ieee_drives.rb +171 -0
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine/settling.rb +4 -0
- data/lib/badline/machine.rb +62 -14
- data/lib/badline/media/cbm2_media.rb +29 -0
- data/lib/badline/media/extensions.rb +8 -4
- data/lib/badline/media/kim1_media.rb +150 -0
- data/lib/badline/media/pet_media.rb +3 -1
- data/lib/badline/media/program.rb +6 -3
- data/lib/badline/media/true_drive.rb +51 -25
- data/lib/badline/media.rb +23 -8
- data/lib/badline/options/family.rb +108 -6
- data/lib/badline/options/help.rb +58 -12
- data/lib/badline/options/table.rb +60 -14
- data/lib/badline/options/validation.rb +71 -11
- data/lib/badline/options.rb +46 -13
- data/lib/badline/pet/bus.rb +16 -2
- 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/keyboard_ports.rb +9 -2
- data/lib/badline/pet/keyed_state.rb +4 -0
- data/lib/badline/pet/model.rb +11 -2
- data/lib/badline/pet/rom_sets.rb +1 -0
- data/lib/badline/pet/sound.rb +5 -105
- data/lib/badline/pet/superpet/acia.rb +12 -0
- data/lib/badline/pet/superpet/board.rb +221 -0
- data/lib/badline/pet/superpet/dongle.rb +81 -0
- data/lib/badline/pet/superpet/keyed_modules.rb +63 -0
- data/lib/badline/pet/superpet/keyed_parts.rb +76 -0
- data/lib/badline/pet/superpet/m6809.rb +32 -0
- data/lib/badline/pet/superpet/waterloo_traps.rb +199 -0
- data/lib/badline/pet/superpet.rb +216 -0
- data/lib/badline/pet.rb +33 -6
- data/lib/badline/plus4/acia.rb +4 -298
- data/lib/badline/plus4/keyed_parts.rb +0 -44
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +2 -0
- data/lib/badline/roms/1001/fdc-251257-02a.rom +0 -0
- data/lib/badline/roms/4040/dos-1.2.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1-rev.rom +0 -0
- data/lib/badline/roms/4040/dos-2.1.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-02.rom +0 -0
- data/lib/badline/roms/4040/fdc-901466-04.rom +0 -0
- data/lib/badline/roms/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/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +277 -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/roms/kim1/6530-002.rom +0 -0
- data/lib/badline/roms/kim1/6530-003.rom +0 -0
- data/lib/badline/roms/pet/characters-superpet.rom +0 -0
- data/lib/badline/roms/pet/superpet-a000.rom +0 -0
- data/lib/badline/roms/pet/superpet-c000.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000-50hz.rom +0 -0
- data/lib/badline/roms/pet/superpet-e000.rom +0 -0
- data/lib/badline/snapshot/cbm2_setup.rb +53 -0
- data/lib/badline/snapshot/container.rb +9 -2
- data/lib/badline/snapshot/field_doc/markdown.rb +4 -2
- data/lib/badline/snapshot/field_doc.rb +59 -3
- data/lib/badline/snapshot/image.rb +65 -9
- data/lib/badline/snapshot/kim1_setup.rb +28 -0
- data/lib/badline/snapshot/pet_setup.rb +27 -11
- data/lib/badline/snapshot/storage_setup.rb +4 -1
- data/lib/badline/snapshot/vic20_setup.rb +11 -7
- data/lib/badline/snapshot/vice/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 +20 -10
- data/lib/badline/snapshot/vice/pet_cpu.rb +42 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +25 -17
- data/lib/badline/snapshot/vice/pet_drive.rb +180 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +336 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +49 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +100 -7
- 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 +15 -3
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/crt_file.rb +7 -1
- data/lib/badline/storage/d64_image.rb +7 -0
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d80_image.rb +17 -1
- data/lib/badline/storage/d81_image.rb +14 -0
- data/lib/badline/storage/d90_image.rb +180 -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 +37 -2
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +5 -0
- data/lib/badline/support/cbm2_checkpoint.rb +48 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -0
- data/lib/badline/support/p500_checkpoint.rb +35 -0
- data/lib/badline/support/superpet_checkpoint.rb +35 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +6 -3
- data/lib/badline/vic20/keyed_state.rb +6 -4
- data/lib/badline/vic20.rb +26 -12
- data/lib/badline.rb +2 -0
- metadata +148 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
# How a KIM-1 was built: its RAM, one of KIM1::Bus::RAM_CONFIGURATIONS
|
|
6
|
+
# by name, the argument KIM1.new takes. The ROMs are the board's own.
|
|
7
|
+
KIM1Setup = Data.define(:ram)
|
|
8
|
+
|
|
9
|
+
class KIM1Setup
|
|
10
|
+
RAM = KIM1::Bus::RAM_CONFIGURATIONS.map(&:to_s).freeze
|
|
11
|
+
|
|
12
|
+
# The setup of `machine`.
|
|
13
|
+
def self.of(machine) = new(ram: machine.bus.ram_configuration.to_s)
|
|
14
|
+
|
|
15
|
+
# The RAM by name, in a keyed snapshot module.
|
|
16
|
+
def write_fields(out)
|
|
17
|
+
out.text("ram", ram)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.read_fields(input) = new(ram: input.choice("ram", RAM, "1k"))
|
|
21
|
+
|
|
22
|
+
# A machine built this way, at power-on.
|
|
23
|
+
def build = KIM1.new(ram: ram.to_sym)
|
|
24
|
+
|
|
25
|
+
def to_s = "KIM-1 with #{ram == 'expanded' ? 'expanded' : ram.upcase} RAM"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -5,9 +5,11 @@ module Badline
|
|
|
5
5
|
# How a PET was built: its model's name, one of PET::Model::ALL, its
|
|
6
6
|
# RAM below $8000 in K and its editor's frame rate, the arguments
|
|
7
7
|
# PET.new takes, the ROMs it runs by file: BASIC, the editor, the
|
|
8
|
-
# KERNAL and the characters,
|
|
9
|
-
# comma-separated
|
|
10
|
-
|
|
8
|
+
# KERNAL and the characters, the 8296 board's jumpers, by name and
|
|
9
|
+
# comma-separated, and the SuperPET's CPU and RAM switches, as
|
|
10
|
+
# PET::SuperPET::Board names their positions, empty on the others.
|
|
11
|
+
PETSetup = Data.define(:model, :ram_kb, :refresh_hz, :basic, :editor, :kernal, :characters, :jumpers,
|
|
12
|
+
:cpu_switch, :ram_switch)
|
|
11
13
|
|
|
12
14
|
class PETSetup
|
|
13
15
|
MODELS = PET::Model::ALL.map(&:name).freeze
|
|
@@ -20,20 +22,24 @@ module Badline
|
|
|
20
22
|
refresh_hz = machine.refresh_hz
|
|
21
23
|
new(model: model.name, ram_kb: machine.ram.length >> 10, refresh_hz:, basic: model.basic,
|
|
22
24
|
editor: model.editor(refresh_hz), kernal: model.kernal, characters: model.characters,
|
|
23
|
-
jumpers: machine.jumpers.join(",")
|
|
25
|
+
jumpers: machine.jumpers.join(","), cpu_switch: machine.superpet? ? machine.board.cpu_switch : "",
|
|
26
|
+
ram_switch: machine.superpet? ? machine.board.ram_switch : "")
|
|
24
27
|
end
|
|
25
28
|
|
|
26
|
-
# The model by name, the RAM, the frame rate, the ROMs
|
|
27
|
-
#
|
|
29
|
+
# The model by name, the RAM, the frame rate, the ROMs, the jumpers
|
|
30
|
+
# where a board has any and the switches where it has them, in a
|
|
31
|
+
# keyed snapshot module.
|
|
28
32
|
def write_fields(out)
|
|
29
33
|
out.text("model", model).int("ram_kb", ram_kb).int("refresh_hz", refresh_hz)
|
|
30
34
|
out.text("rom.basic", basic).text("rom.editor", editor).text("rom.kernal", kernal)
|
|
31
35
|
out.text("rom.characters", characters)
|
|
32
36
|
out.text("jumpers", jumpers) unless jumpers.empty?
|
|
37
|
+
out.text("cpu_switch", cpu_switch).text("ram_switch", ram_switch) unless cpu_switch.empty?
|
|
33
38
|
end
|
|
34
39
|
|
|
35
|
-
# A ROM the snapshot doesn't name is the model's own,
|
|
36
|
-
#
|
|
40
|
+
# A ROM the snapshot doesn't name is the model's own, a board without
|
|
41
|
+
# jumpers named has none fitted, and a SuperPET without its switches
|
|
42
|
+
# named has them at 6502 and R/W.
|
|
37
43
|
def self.read_fields(input)
|
|
38
44
|
profile = PET::Model.named(input.choice("model", MODELS, PET::Model::DEFAULT))
|
|
39
45
|
ram_kb = input.int("ram_kb", profile.ram_kb)
|
|
@@ -47,18 +53,27 @@ module Badline
|
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
refresh_hz = 60 unless profile.crtc?
|
|
56
|
+
superpet = profile.superpet?
|
|
50
57
|
new(model: profile.name, ram_kb:, refresh_hz:, basic: input.text("rom.basic", profile.basic),
|
|
51
58
|
editor: input.text("rom.editor", profile.editor(refresh_hz)),
|
|
52
59
|
kernal: input.text("rom.kernal", profile.kernal),
|
|
53
|
-
characters: input.text("rom.characters", profile.characters), jumpers: input.text("jumpers", "")
|
|
60
|
+
characters: input.text("rom.characters", profile.characters), jumpers: input.text("jumpers", ""),
|
|
61
|
+
cpu_switch: superpet ? input.choice("cpu_switch", PET::SuperPET::Board::CPU_SWITCH, "6502") : "",
|
|
62
|
+
ram_switch: superpet ? input.choice("ram_switch", PET::SuperPET::Board::RAM_SWITCH, "rw") : "")
|
|
54
63
|
end
|
|
55
64
|
|
|
56
65
|
# A machine built this way, at power-on, with the BASIC, editor and
|
|
57
66
|
# character ROMs PET::ROMSets names for its ROMs.
|
|
58
67
|
def build
|
|
59
68
|
version = PET::ROMSets::BASICS.find { |_, roms| roms.first == basic }&.first
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
editor_name = PET::ROMSets::EDITORS.key(editor)
|
|
70
|
+
unless cpu_switch.empty?
|
|
71
|
+
return PET::SuperPET.new(refresh_hz:, basic: version, editor: editor_name, cpu: cpu_switch,
|
|
72
|
+
bank_ram: ram_switch)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
PET.new(model:, ram_kb:, refresh_hz:, basic: version, editor: editor_name, characters: fitted_characters,
|
|
76
|
+
jumpers: jumpers.split(","))
|
|
62
77
|
rescue ArgumentError => e
|
|
63
78
|
raise FormatError, "the snapshot is of a #{self}, which badline doesn't build: #{e.message}"
|
|
64
79
|
end
|
|
@@ -68,6 +83,7 @@ module Badline
|
|
|
68
83
|
def to_s
|
|
69
84
|
text = "#{model} with #{ram_kb}K at #{refresh_hz} Hz"
|
|
70
85
|
text = "#{text} jumpered #{jumpers}" unless jumpers.empty?
|
|
86
|
+
text = "#{text} with its CPU switch at #{cpu_switch} and RAM switch at #{ram_switch}" unless cpu_switch.empty?
|
|
71
87
|
roms = [basic, editor, kernal, characters]
|
|
72
88
|
profile = PET::Model.named(model)
|
|
73
89
|
stock = [profile.basic, profile.editor(refresh_hz), profile.kernal, profile.characters]
|
|
@@ -13,6 +13,7 @@ module Badline
|
|
|
13
13
|
T64_ARCHIVE = 4
|
|
14
14
|
D80 = 5
|
|
15
15
|
D82 = 6
|
|
16
|
+
D67 = 7
|
|
16
17
|
|
|
17
18
|
module_function
|
|
18
19
|
|
|
@@ -22,6 +23,7 @@ module Badline
|
|
|
22
23
|
return D81 if image.is_a?(Storage::D81Image)
|
|
23
24
|
return D82 if image.is_a?(Storage::D82Image)
|
|
24
25
|
return D80 if image.is_a?(Storage::D80Image)
|
|
26
|
+
return D67 if image.is_a?(Storage::D67Image)
|
|
25
27
|
|
|
26
28
|
raise ArgumentError, "#{image.class} can't go in a snapshot"
|
|
27
29
|
end
|
|
@@ -41,7 +43,7 @@ module Badline
|
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
KIND_NAMES = %w[directory d64 d71 d81 t64 d80 d82].freeze
|
|
46
|
+
KIND_NAMES = %w[directory d64 d71 d81 t64 d80 d82 d67].freeze
|
|
45
47
|
|
|
46
48
|
# Opens the storage write_fields wrote: the same directory by its
|
|
47
49
|
# path, an archive with the bytes it held, or a disk image with the
|
|
@@ -61,6 +63,7 @@ module Badline
|
|
|
61
63
|
when D71 then Storage::D71Image.new(path, read_only:, bytes:)
|
|
62
64
|
when D80 then Storage::D80Image.new(path, read_only:, bytes:)
|
|
63
65
|
when D82 then Storage::D82Image.new(path, read_only:, bytes:)
|
|
66
|
+
when D67 then Storage::D67Image.new(path, read_only:, bytes:)
|
|
64
67
|
else Storage::D81Image.new(path, read_only:, bytes:)
|
|
65
68
|
end
|
|
66
69
|
end
|
|
@@ -3,26 +3,30 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Snapshot
|
|
5
5
|
# How a VIC-20 was built: its RAM expansion, a key of
|
|
6
|
-
# Vic20::Bus::RAM_CONFIGURATIONS,
|
|
7
|
-
|
|
6
|
+
# Vic20::Bus::RAM_CONFIGURATIONS, and its model, one of Vic20::MODELS,
|
|
7
|
+
# the arguments Vic20.new takes.
|
|
8
|
+
Vic20Setup = Data.define(:ram, :model)
|
|
8
9
|
|
|
9
10
|
class Vic20Setup
|
|
10
11
|
RAM_CONFIGURATIONS = Vic20::Bus::RAM_CONFIGURATIONS.keys.freeze
|
|
11
12
|
|
|
12
|
-
# The RAM expansion by name, in a keyed snapshot module.
|
|
13
|
+
# The RAM expansion and the model by name, in a keyed snapshot module.
|
|
14
|
+
# A snapshot without a model is of a PAL machine, the only one badline
|
|
15
|
+
# built before the NTSC one.
|
|
13
16
|
def write_fields(out)
|
|
14
|
-
out.text("ram", ram.to_s)
|
|
17
|
+
out.text("ram", ram.to_s).text("model", model)
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def self.read_fields(input)
|
|
18
21
|
text = input.choice("ram", RAM_CONFIGURATIONS.map(&:to_s), "unexpanded")
|
|
19
|
-
new(ram: RAM_CONFIGURATIONS.find { |name| name.to_s == text } || :unexpanded
|
|
22
|
+
new(ram: RAM_CONFIGURATIONS.find { |name| name.to_s == text } || :unexpanded,
|
|
23
|
+
model: input.choice("model", Vic20::MODELS, "pal"))
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
# A machine built this way, at power-on.
|
|
23
|
-
def build = Vic20.new(ram:)
|
|
27
|
+
def build = Vic20.new(ram:, model:)
|
|
24
28
|
|
|
25
|
-
def to_s = ram.to_s
|
|
29
|
+
def to_s = model == "pal" ? ram.to_s : "#{ram} #{model}"
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
32
|
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/snapshot/vice/cbm2_mem"
|
|
4
|
+
require "badline/snapshot/vice/cbm2_tpi"
|
|
5
|
+
require "badline/snapshot/vice/cbm2_acia"
|
|
6
|
+
require "badline/snapshot/vice/cbm2_crtc"
|
|
7
|
+
require "badline/snapshot/vice/cbm2_mains"
|
|
8
|
+
require "badline/snapshot/vice/cbm2_restore"
|
|
9
|
+
|
|
10
|
+
module Badline
|
|
11
|
+
module Snapshot
|
|
12
|
+
module Vice
|
|
13
|
+
# xcbm2's modules for the CBM-II's B series: the CPU, the RAM, the
|
|
14
|
+
# CRTC, the CIA, both TPIs, the ACIA and the SID, written from
|
|
15
|
+
# badline's machine and read into it. Each module's file says what
|
|
16
|
+
# maps and what is approximated. The CPU's, the CIA's and the SID's
|
|
17
|
+
# are the PET's and the C64's. They go through the machine's own
|
|
18
|
+
# keyed fields (Keyed), which a save writes and a load reads, but for
|
|
19
|
+
# the CIA and the SID, which go through the chips as the C64's do.
|
|
20
|
+
module CBM2
|
|
21
|
+
# The models badline builds by the RAM board's size in K and
|
|
22
|
+
# whether the model line is a 700's.
|
|
23
|
+
MODELS = { [128, false] => "610", [256, false] => "620", [128, true] => "710", [256, true] => "720" }.freeze
|
|
24
|
+
# The modules xcbm2 resets the machine without, each as xcbm2 leaves
|
|
25
|
+
# them with nothing attached: no cartridge, no file system drive
|
|
26
|
+
# open, the cassette port on with a datasette in it, and nothing in
|
|
27
|
+
# the user port.
|
|
28
|
+
PERIPHERALS = [["CBM2CART", 0, 1, [0x00]], ["FSDRIVE", 0, 0, Array.new(258, 0)],
|
|
29
|
+
["TAPEPORT", 1, 0, [0x01, 0x01]], ["USERPORT", 1, 0, [0x01, 0x00]]].freeze
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
# The modules xcbm2 3.10 reads, in its order, describing `machine`,
|
|
34
|
+
# which Machine::Settling has brought between instructions, from
|
|
35
|
+
# its keyed modules, `sections`.
|
|
36
|
+
def export(machine, sections)
|
|
37
|
+
readers = {}
|
|
38
|
+
sections.each { |section| readers[section.name] = Keyed::Reader.parse(section) }
|
|
39
|
+
machine.sid.catch_up!
|
|
40
|
+
cart, *rest = PERIPHERALS.map do |name, major, minor, bytes|
|
|
41
|
+
FieldWriter.new.bytes(bytes).section(name, major, minor)
|
|
42
|
+
end
|
|
43
|
+
[PET::MainCPU.write(machine, machine.tpi1.irq? ? 1 : 0), CBM2Mem.export(machine), cart,
|
|
44
|
+
CRTC.export(machine, reader(readers, "BL-CRTC"), reader(readers, "BL-VIDEO")),
|
|
45
|
+
CIAs.section(machine.cia, "CIA1"),
|
|
46
|
+
*TPI::NAMES.map { |name| TPI.export(name, reader(readers, "BL-#{name}")) },
|
|
47
|
+
ACIA.export(reader(readers, "BL-ACIA"), machine.cycles),
|
|
48
|
+
SIDRegisters.export(machine), SIDExtended.export(machine), *drives(machine, readers), *rest]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The true drive's modules, as xpet's (PET::Drive2031,
|
|
52
|
+
# PET::Drive8050), or with no true drive, DRIVE8 to DRIVE11 as
|
|
53
|
+
# xcbm2 and xcbm5x0 save no drive.
|
|
54
|
+
def drives(machine, readers)
|
|
55
|
+
return no_drives unless machine.true_drive
|
|
56
|
+
|
|
57
|
+
PET::Drive2031.export(machine, readers) + PET::Drive8050.export(machine, readers)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def no_drives = PET::Drive2031::UNITS.map { |name| PET::Drive2031.unit(PET::Drive2031::NONE, name) }
|
|
61
|
+
|
|
62
|
+
def reader(readers, name) = readers[name] || Keyed::Reader.missing(name)
|
|
63
|
+
|
|
64
|
+
# How the machine xcbm2 saved was built: the model from the RAM
|
|
65
|
+
# board's size and the model line, and the mains from the model
|
|
66
|
+
# line too, 60 Hz on a 600 whose line says so and 50 Hz otherwise:
|
|
67
|
+
# xcbm2 has no 700 at 60 Hz. xcbm2 doesn't save its ROMs, so the
|
|
68
|
+
# KERNAL is 901244-04a, xcbm2's own.
|
|
69
|
+
def setup(container)
|
|
70
|
+
memory = memory(container)
|
|
71
|
+
ram_kb = CBM2Mem.ram_kb(memory)
|
|
72
|
+
line = CBM2Mem.line(memory)
|
|
73
|
+
unless CBM2Mem.mapped(memory).zero?
|
|
74
|
+
raise FormatError, "#{memory}: RAM in segment 15 beyond the B series', which badline doesn't build"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
model = MODELS[[ram_kb, line == CBM2Mem::M700]]
|
|
78
|
+
raise FormatError, "#{memory}: a CBM-II with #{ram_kb}K of RAM, which badline doesn't build" if model.nil?
|
|
79
|
+
|
|
80
|
+
CBM2Setup.new(model:, kernal: "04a", mains_hz: line == CBM2Mem::M600_60HZ ? 60 : 50, refresh: true)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def memory(container)
|
|
84
|
+
section = container[CBM2Mem::NAME]
|
|
85
|
+
raise FormatError, "the snapshot has no #{CBM2Mem::NAME} module" if section.nil?
|
|
86
|
+
raise FormatError, "#{section}: badline doesn't read this version" unless CBM2Mem.reads?(section)
|
|
87
|
+
|
|
88
|
+
section
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module CBM2
|
|
7
|
+
# Acia1 1.1, the 6551: the transmitter's and the receiver's data
|
|
8
|
+
# registers, the status, the command and control registers, the
|
|
9
|
+
# transmitter's state (0 idle, 2 a byte waiting), then four clocks:
|
|
10
|
+
# the cycles to the transmitter's next character end, a clock
|
|
11
|
+
# xcbm2 leaves 0, the cycles to its next receiver poll and another
|
|
12
|
+
# 0.
|
|
13
|
+
#
|
|
14
|
+
# Written: the registers, the status as a read finds it, and while
|
|
15
|
+
# the transmitter is on, the cycles to its character's end in both
|
|
16
|
+
# clocks. Read: the registers, the receiver full, the transmitter
|
|
17
|
+
# empty, the interrupt and the errors from the status, and the
|
|
18
|
+
# transmitter's character end from its clock, or one a character's
|
|
19
|
+
# time on when the clock has run out. Nothing is plugged
|
|
20
|
+
# in, in either, and a character being shifted out is lost.
|
|
21
|
+
module ACIA
|
|
22
|
+
NAME = "Acia1"
|
|
23
|
+
MAJOR = 1
|
|
24
|
+
MINOR = 1
|
|
25
|
+
WAITING = 2
|
|
26
|
+
ERRORS = 0x07
|
|
27
|
+
RECEIVER_FULL = 0x08
|
|
28
|
+
TRANSMITTER_EMPTY = 0x10
|
|
29
|
+
IRQ = 0x80
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
34
|
+
|
|
35
|
+
# The module from the ACIA's BL-ACIA fields in `input`, for a
|
|
36
|
+
# machine at `cycles`.
|
|
37
|
+
def export(input, cycles)
|
|
38
|
+
empty = input.bool("transmitter_empty", true)
|
|
39
|
+
status = input.int("errors", 0) | Chips::ACIA::DCD | Chips::ACIA::DSR
|
|
40
|
+
status |= RECEIVER_FULL if input.bool("receiver_full", false)
|
|
41
|
+
status |= TRANSMITTER_EMPTY if empty
|
|
42
|
+
status |= IRQ if input.bool("irq", false)
|
|
43
|
+
at = input.int("transmit_at", Chips::ACIA::NEVER)
|
|
44
|
+
due = at == Chips::ACIA::NEVER ? 0 : [at - cycles, 0].max
|
|
45
|
+
FieldWriter.new.byte(input.int("transmit_data", 0)).byte(input.int("receive_data", 0)).byte(status)
|
|
46
|
+
.byte(input.int("command", 0)).byte(input.int("control", 0)).byte(empty ? 0 : WAITING)
|
|
47
|
+
.dword(due).dword(0).dword(due).dword(0).section(NAME, MAJOR, MINOR)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The ACIA by its BL-ACIA keys, in a keyed module's writer, for a
|
|
51
|
+
# machine at `cycles`.
|
|
52
|
+
def import(section, out, cycles)
|
|
53
|
+
fields = FieldReader.new(section)
|
|
54
|
+
transmit, receive, status, command, control = fields.bytes(5)
|
|
55
|
+
fields.skip(1)
|
|
56
|
+
due = fields.dword
|
|
57
|
+
out.int("command", command).int("control", control).int("errors", status & ERRORS)
|
|
58
|
+
out.bool("receiver_full", status.anybits?(RECEIVER_FULL))
|
|
59
|
+
out.bool("transmitter_empty", status.anybits?(TRANSMITTER_EMPTY)).bool("irq", status.anybits?(IRQ))
|
|
60
|
+
out.int("receive_data", receive).int("transmit_data", transmit).int("shifting", Chips::ACIA::IDLE)
|
|
61
|
+
out.int("transmit_remainder", 0)
|
|
62
|
+
return out.int("transmit_at", Chips::ACIA::NEVER) unless transmitting?(command)
|
|
63
|
+
|
|
64
|
+
out.optional_int("transmit_at", due.positive? ? cycles + due : nil)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def transmitting?(command)
|
|
68
|
+
control = command & 0x0c
|
|
69
|
+
control != 0 && control != 0x0c
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module CBM2
|
|
7
|
+
# CRTC 1.2 as xcbm2 saves it, laid out as xpet's (PET::CRTC): the
|
|
8
|
+
# board's screen settings, the registers, the one selected, the
|
|
9
|
+
# beam, the picture's size and the frame's lines, then xcbm2's
|
|
10
|
+
# picture. xcbm2's board has the 6845's cursor and reads one byte a
|
|
11
|
+
# character from 2K of screen RAM. After the beam's line come the
|
|
12
|
+
# fields left in the cursor's blink phase, counted down from 32, and
|
|
13
|
+
# whether the cursor shows: xcbm2 blinks it 32 fields on and 32 off,
|
|
14
|
+
# where badline's 6845 blinks it 16 and 16, or 8 and 8.
|
|
15
|
+
#
|
|
16
|
+
# xcbm2 ticks the mains once a frame, as vertical sync ends, so its
|
|
17
|
+
# mains follows the CRTC (Mains).
|
|
18
|
+
#
|
|
19
|
+
# Written: the registers and the beam as badline's CRTC has them,
|
|
20
|
+
# and the cursor shown as badline's is, its phase scaled to xcbm2's.
|
|
21
|
+
# The picture is left blank, as xcbm2 paints it again. Read: the
|
|
22
|
+
# registers and the beam as xpet's are (PET::CRTC), the cursor shown
|
|
23
|
+
# as xcbm2's is, its phase scaled to badline's, and where the
|
|
24
|
+
# picture is in its frame. The picture starts again with the next
|
|
25
|
+
# frame.
|
|
26
|
+
module CRTC
|
|
27
|
+
NAME = PET::CRTC::NAME
|
|
28
|
+
SETTINGS = [0xff, 0x07, 0x00, 0x10, 0x00, 0x20, 0x00, 0xe0, 0xff, 0x3f].freeze
|
|
29
|
+
# The 6845's cursor, one byte a character, and not a 2001.
|
|
30
|
+
BOARD = [0x01, 0x01, 0x00].freeze
|
|
31
|
+
WIDTH = 704
|
|
32
|
+
# The picture's lines and the line its first row starts on, for a
|
|
33
|
+
# 600 and a 700.
|
|
34
|
+
FRAMES = { false => [266, 33], true => [366, 8] }.freeze
|
|
35
|
+
BLINK = 32
|
|
36
|
+
# Where R10 is in the module, and the blink's fields left.
|
|
37
|
+
R10_AT = PET::CRTC::SETTINGS.length + 5 + 10
|
|
38
|
+
BLINK_AT = PET::CRTC::SELECTED + 4
|
|
39
|
+
# R10's blink of 8 fields on and 8 off.
|
|
40
|
+
FAST_BLINK = 0x40
|
|
41
|
+
|
|
42
|
+
module_function
|
|
43
|
+
|
|
44
|
+
def reads?(section) = PET::CRTC.reads?(section)
|
|
45
|
+
|
|
46
|
+
def fresh_crtc(machine) = CBM2Setup.of(machine).build.crtc
|
|
47
|
+
|
|
48
|
+
# The module from the BL-CRTC and BL-VIDEO fields in `crtc_input`
|
|
49
|
+
# and `video_input`.
|
|
50
|
+
def export(machine, crtc_input, video_input)
|
|
51
|
+
registers = PET::CRTC.crtc_registers(crtc_input)
|
|
52
|
+
beam = beam(machine, crtc_input, registers)
|
|
53
|
+
height, top = FRAMES.fetch(machine.model.high_profile)
|
|
54
|
+
fields = FieldWriter.new.bytes(SETTINGS).word(0).bytes(BOARD).bytes(registers)
|
|
55
|
+
fields.byte(crtc_input.int("selected", 0)).byte(beam.column).byte(beam.row).byte(beam.raster)
|
|
56
|
+
fields.bytes(blink(video_input.int("frames", 0), registers[10])).byte(1)
|
|
57
|
+
fields.word(0).word(beam.start & PET::CRTC::SCREEN_BITS).byte(beam.vsync).byte(0).flag(beam.shown)
|
|
58
|
+
fields.word(WIDTH).word(height).byte(PET::CRTC::AFTER_SIZE).zeros(3).byte(top).byte(0)
|
|
59
|
+
fields.word(PET::CRTC.frame_lines(registers)).word(beam.frame_line).byte(1)
|
|
60
|
+
fields.dword(0).dword(WIDTH).dword(height).dword(WIDTH).zeros(WIDTH * (height + 4))
|
|
61
|
+
fields.section(NAME, PET::CRTC::MAJOR, PET::CRTC::MINOR)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# The beam where xcbm2's is with badline's CRTC where `input` has
|
|
65
|
+
# it (PET::CRTC::CRTC_LEAD). On a line's last character xcbm2's
|
|
66
|
+
# beam has moved on to the next line, a cycle before it starts.
|
|
67
|
+
def beam(machine, input, registers)
|
|
68
|
+
cycles = -PET::CRTC::CRTC_LEAD % PET::CRTC.frame_cycles(registers)
|
|
69
|
+
beam = PET::CRTC.crtc_beam(PET::CRTC.run_on(fresh_crtc(machine), input, cycles), registers)
|
|
70
|
+
return beam unless beam.column == registers[0]
|
|
71
|
+
|
|
72
|
+
PET::CRTC.crtc_beam(PET::CRTC.run_on(fresh_crtc(machine), input, cycles + 1), registers).with(column: 0xff)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The fields left in xcbm2's blink phase and whether its cursor
|
|
76
|
+
# shows, for badline's fields counted, `frames`, and R10.
|
|
77
|
+
def blink(frames, r10)
|
|
78
|
+
fast = r10 & 0x60 == FAST_BLINK
|
|
79
|
+
shown = frames.nobits?(fast ? 0x08 : 0x10)
|
|
80
|
+
phase = fast ? (frames & 0x07) << 1 : frames & 0x0f
|
|
81
|
+
[BLINK - (phase << 1), shown ? 1 : 0]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# badline's fields counted for xcbm2's blink in `section`.
|
|
85
|
+
def frames(section)
|
|
86
|
+
r10 = FieldReader.new(section).skip(R10_AT).byte
|
|
87
|
+
left, shown = FieldReader.new(section).skip(BLINK_AT).bytes(2)
|
|
88
|
+
phase = ((BLINK - [left, BLINK].min) >> 1) & 0x0f
|
|
89
|
+
return (shown.zero? ? 0x08 : 0) | (phase >> 1) if r10 & 0x60 == FAST_BLINK
|
|
90
|
+
|
|
91
|
+
(shown.zero? ? 0x10 : 0) | phase
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# The BL-CRTC module, run on from xcbm2's beam as xpet's is.
|
|
95
|
+
def import(section, machine)
|
|
96
|
+
counters = Keyed.section("BL-CRTC") { |out| PET::CRTC.import(section, out, 1, 0) }
|
|
97
|
+
frame = PET::CRTC.frame_cycles(PET::CRTC.registers(section))
|
|
98
|
+
cycles = (PET::CRTC::CRTC_LEAD + PET::CRTC.column_of(section)) % frame
|
|
99
|
+
fields = PET::CRTC.run_on(fresh_crtc(machine), Keyed::Reader.parse(counters), cycles)
|
|
100
|
+
Keyed.section("BL-CRTC") { |out| PET::CRTC.copy(fields, out) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The BL-VIDEO module for a CRTC whose BL-CRTC fields `crtc` holds:
|
|
104
|
+
# the cursor's blink, the line the frame paints next and the frame's
|
|
105
|
+
# layout. The picture stays as the machine has it.
|
|
106
|
+
def video(section, crtc)
|
|
107
|
+
registers = PET::CRTC.crtc_registers(crtc)
|
|
108
|
+
Keyed.section("BL-VIDEO") do |out|
|
|
109
|
+
out.int("frames", frames(section)).int("line", painted(crtc, registers)).bool("line_shown", false)
|
|
110
|
+
out.int("origin", Badline::CBM2::CRTCVideo::LEFT - ((registers[0] + 1 - registers[2]) << 3))
|
|
111
|
+
out.ints("layout", registers.values_at(0, 1, 2, 4, 5, 6, 9, 12, 13))
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The lines the frame has displayed so far.
|
|
116
|
+
def painted(crtc, registers)
|
|
117
|
+
lines = registers[9] + 1
|
|
118
|
+
shown = registers[6] * lines
|
|
119
|
+
return shown if crtc.bool("adjusting", false) || !crtc.bool("row_display", true)
|
|
120
|
+
|
|
121
|
+
[(crtc.int("row", 0) * lines) + crtc.int("line", 0), shown].min
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module CBM2
|
|
7
|
+
# The mains on TPI1's I0. xcbm2 saves no mains: it ticks it once a
|
|
8
|
+
# frame of its CRTC, as vertical sync ends, whatever the mains is
|
|
9
|
+
# set to. A machine read from its snapshot has its mains' next
|
|
10
|
+
# falling edge where xcbm2's tick would come, and runs it at its own
|
|
11
|
+
# 50 or 60 Hz from there. A snapshot written for xcbm2 ticks where
|
|
12
|
+
# its CRTC says.
|
|
13
|
+
module Mains
|
|
14
|
+
# The cycle of the line where vertical sync ends that the tick
|
|
15
|
+
# latches on, counted as badline's CRTC counts columns.
|
|
16
|
+
TICK_COLUMN = 0
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
# The cycles to the mains' next falling edge for a CRTC whose
|
|
21
|
+
# BL-CRTC fields `crtc` holds.
|
|
22
|
+
def next_tick(crtc)
|
|
23
|
+
registers = crtc.bytes("registers", "".b).bytes
|
|
24
|
+
regs = Array.new(Chips::CRTC::MASKS.length) { |index| registers[index] || 0 }
|
|
25
|
+
line_cycles = regs[0] + 1
|
|
26
|
+
lines = regs[9] + 1
|
|
27
|
+
rows = regs[4] + 1
|
|
28
|
+
frame_lines = (rows * lines) + regs[5]
|
|
29
|
+
line = crtc.int("line", 0)
|
|
30
|
+
line += crtc.bool("adjusting", false) ? rows * lines : crtc.int("row", 0) * lines
|
|
31
|
+
tick_line = ((regs[7] * lines) + Chips::CRTC::VSYNC_LINES) % frame_lines
|
|
32
|
+
frame = line_cycles * frame_lines
|
|
33
|
+
cycles = ((tick_line * line_cycles) + TICK_COLUMN - ((line * line_cycles) + crtc.int("column", 0))) % frame
|
|
34
|
+
cycles.zero? ? frame : cycles
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The BL-MACHINE mains fields for a mains `half_period` cycles high
|
|
38
|
+
# and as long low, whose next falling edge is `cycles` away.
|
|
39
|
+
def write(out, cycles, half_period)
|
|
40
|
+
high = cycles <= half_period
|
|
41
|
+
countdown = high ? cycles : [cycles - half_period, half_period].min
|
|
42
|
+
out.int("mains_countdown", countdown).bool("mains_high", high)
|
|
43
|
+
high
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module CBM2
|
|
7
|
+
# CBM2MEM 1.0: the RAM board's size in units of 128K, the RAM xcbm2
|
|
8
|
+
# maps into segment 15 beyond the B series' own (none on a B series
|
|
9
|
+
# as built, $40 on a P500), its model line (0 a 700, 1 a 600 at
|
|
10
|
+
# 60 Hz, 2 a 600 at 50 Hz), the execution and indirection segment
|
|
11
|
+
# registers, then segment 15's 2K of RAM, the 2K of screen RAM, and
|
|
12
|
+
# the board's segments from segment 1 up.
|
|
13
|
+
#
|
|
14
|
+
# xcbm2 keeps the segment registers in segment 15's bytes $0000 and
|
|
15
|
+
# $0001 too, and they are written there. Under the registers
|
|
16
|
+
# badline's RAM is never written, so those two bytes read as 0.
|
|
17
|
+
# xcbm2's ROMs aren't in the snapshot.
|
|
18
|
+
module CBM2Mem
|
|
19
|
+
NAME = "CBM2MEM"
|
|
20
|
+
MAJOR = 1
|
|
21
|
+
MINOR = 0
|
|
22
|
+
SEGMENT = 0x10000
|
|
23
|
+
SYSTEM_RAM = 0x800
|
|
24
|
+
SCREEN_RAM = 0x800
|
|
25
|
+
HEADER = 5
|
|
26
|
+
# The model lines.
|
|
27
|
+
M700 = 0
|
|
28
|
+
M600_60HZ = 1
|
|
29
|
+
M600_50HZ = 2
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
34
|
+
|
|
35
|
+
def model_line(machine)
|
|
36
|
+
return M700 if machine.model.high_profile
|
|
37
|
+
|
|
38
|
+
machine.mains_hz == 60 ? M600_60HZ : M600_50HZ
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def export(machine)
|
|
42
|
+
bus = machine.bus
|
|
43
|
+
system = bus.ram.snapshot
|
|
44
|
+
system[0] = bus.execution_segment
|
|
45
|
+
system[1] = bus.indirect_segment
|
|
46
|
+
fields = FieldWriter.new.byte(machine.model.ram_segments.length / 2).byte(0).byte(model_line(machine))
|
|
47
|
+
fields.byte(bus.execution_segment).byte(bus.indirect_segment)
|
|
48
|
+
fields.bytes(system).bytes(bus.screen.snapshot)
|
|
49
|
+
machine.model.ram_segments.each { |segment| fields.bytes(bus.segment_ram[segment].snapshot) }
|
|
50
|
+
fields.section(NAME, MAJOR, MINOR)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The RAM in K, the RAM mapped into segment 15 and the model line.
|
|
54
|
+
def ram_kb(section) = FieldReader.new(section).byte * 128
|
|
55
|
+
|
|
56
|
+
def mapped(section) = FieldReader.new(section).skip(1).byte
|
|
57
|
+
|
|
58
|
+
def line(section) = FieldReader.new(section).skip(2).byte
|
|
59
|
+
|
|
60
|
+
# The RAM and the segment registers by their BL-RAM keys, in a
|
|
61
|
+
# keyed module's writer, for a machine whose board fills
|
|
62
|
+
# `segments`.
|
|
63
|
+
def import(section, out, segments)
|
|
64
|
+
fields = FieldReader.new(section).skip(3)
|
|
65
|
+
out.int("execution_segment", fields.byte & 0x0f).int("indirect_segment", fields.byte & 0x0f)
|
|
66
|
+
system = fields.string(SYSTEM_RAM)
|
|
67
|
+
out.bytes("system", "\0\0".b + system.byteslice(2, SYSTEM_RAM - 2))
|
|
68
|
+
out.bytes("screen", fields.string(SCREEN_RAM))
|
|
69
|
+
segments.each { |segment| out.bytes("segment#{segment}", fields.string(SEGMENT)) }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|