badline 0.13.0 → 0.14.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 +49 -1
- data/CLAUDE.md +27 -7
- data/CONTRIBUTING.md +4 -3
- data/README.md +8 -5
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +29 -0
- data/doc/snapshot-format.md +1047 -0
- data/doc/snapshots.md +72 -39
- data/lib/badline/audio/bare_player.rb +50 -17
- data/lib/badline/audio/machine_player.rb +15 -9
- data/lib/badline/audio/renderer.rb +32 -21
- data/lib/badline/c128/banks.rb +6 -3
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +17 -3
- data/lib/badline/c128/drives.rb +6 -25
- data/lib/badline/c128/keyed_modules.rb +117 -0
- data/lib/badline/c128/keyed_state.rb +91 -0
- data/lib/badline/c128/keys.rb +9 -0
- data/lib/badline/c128/mmu.rb +9 -8
- data/lib/badline/c128/mmu_pages.rb +20 -0
- data/lib/badline/c128/modes.rb +19 -9
- data/lib/badline/c128/z80_turns.rb +1 -1
- data/lib/badline/c128.rb +8 -8
- data/lib/badline/c64/attachments.rb +0 -44
- data/lib/badline/c64/banked_ram/plus256k.rb +18 -10
- data/lib/badline/c64/banked_ram/plus60k.rb +8 -6
- data/lib/badline/c64/banked_ram/unexpanded.rb +2 -2
- data/lib/badline/c64/bus/debug_register.rb +32 -0
- data/lib/badline/c64/bus/keyed_state.rb +41 -0
- data/lib/badline/c64/bus.rb +2 -1
- data/lib/badline/c64/cartridge/action_replay.rb +0 -13
- data/lib/badline/c64/cartridge/easy_flash.rb +0 -15
- data/lib/badline/c64/cartridge/epyx_fastload.rb +0 -8
- data/lib/badline/c64/cartridge/final_cartridge3.rb +0 -12
- data/lib/badline/c64/cartridge/flash.rb +3 -3
- data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
- data/lib/badline/c64/cartridge/geo_ram.rb +0 -16
- data/lib/badline/c64/cartridge/kcs_power.rb +0 -8
- data/lib/badline/c64/cartridge/keyed_state.rb +393 -0
- data/lib/badline/c64/cartridge/pagefox.rb +0 -10
- data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
- data/lib/badline/c64/cartridge/rgcd.rb +0 -10
- data/lib/badline/c64/cartridge/super_games.rb +0 -10
- data/lib/badline/c64/cartridge/zaxxon.rb +0 -16
- data/lib/badline/c64/cartridge.rb +1 -27
- data/lib/badline/c64/kernal_traps.rb +28 -25
- data/lib/badline/c64/keyed_modules.rb +111 -0
- data/lib/badline/c64/keyed_state.rb +68 -0
- data/lib/badline/c64/reu/dma.rb +0 -4
- data/lib/badline/c64/reu/keyed_state.rb +140 -0
- data/lib/badline/c64/reu/ram.rb +0 -15
- data/lib/badline/c64/reu.rb +1 -3
- data/lib/badline/c64/true_drives.rb +56 -5
- data/lib/badline/c64.rb +7 -3
- data/lib/badline/chips/cia/interrupt_register.rb +0 -17
- data/lib/badline/chips/cia/keyed_parts.rb +213 -0
- data/lib/badline/chips/cia/serial.rb +0 -4
- data/lib/badline/chips/cia/time_of_day.rb +0 -22
- data/lib/badline/chips/cia/timer.rb +0 -20
- data/lib/badline/chips/cia.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +18 -2
- data/lib/badline/chips/cpu/keyed_state.rb +177 -0
- data/lib/badline/chips/cpu/microcode.rb +16 -0
- data/lib/badline/chips/cpu/port.rb +20 -3
- data/lib/badline/chips/cpu.rb +5 -0
- data/lib/badline/chips/sid/decimator.rb +10 -8
- data/lib/badline/chips/sid/envelope.rb +1 -6
- data/lib/badline/chips/sid/filter.rb +0 -14
- data/lib/badline/chips/sid/keyed_parts.rb +286 -0
- data/lib/badline/chips/sid/voice.rb +0 -10
- data/lib/badline/chips/sid/waveform.rb +0 -2
- data/lib/badline/chips/sid.rb +1 -2
- data/lib/badline/chips/ted/beam.rb +119 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +143 -0
- data/lib/badline/chips/tia.rb +49 -0
- data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
- data/lib/badline/chips/vdc/painter.rb +0 -12
- data/lib/badline/chips/vdc/raster.rb +0 -16
- data/lib/badline/chips/vdc/window.rb +0 -9
- data/lib/badline/chips/vdc.rb +1 -2
- data/lib/badline/chips/via/control_lines.rb +0 -15
- data/lib/badline/chips/via/interrupt_register.rb +0 -9
- data/lib/badline/chips/via/keyed_parts.rb +163 -0
- data/lib/badline/chips/via/shift_register.rb +0 -14
- data/lib/badline/chips/via/timer1.rb +0 -13
- data/lib/badline/chips/via/timer2.rb +0 -18
- data/lib/badline/chips/via.rb +1 -2
- data/lib/badline/chips/vic/border_mask.rb +0 -10
- data/lib/badline/chips/vic/collisions.rb +0 -11
- data/lib/badline/chips/vic/color_patches.rb +0 -11
- data/lib/badline/chips/vic/display_state.rb +0 -19
- data/lib/badline/chips/vic/graphics_shifter.rb +0 -4
- data/lib/badline/chips/vic/keyed_parts.rb +228 -0
- data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
- data/lib/badline/chips/vic/keyed_state.rb +102 -0
- data/lib/badline/chips/vic/register_log.rb +0 -18
- data/lib/badline/chips/vic/registers.rb +0 -9
- data/lib/badline/chips/vic/sequencer.rb +0 -2
- data/lib/badline/chips/vic/sprite/internal_bus.rb +0 -11
- data/lib/badline/chips/vic/sprite.rb +0 -2
- data/lib/badline/chips/vic/sprites.rb +0 -3
- data/lib/badline/chips/vic.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
- data/lib/badline/chips/vic_i/painter.rb +0 -4
- data/lib/badline/chips/vic_i/sound.rb +38 -46
- data/lib/badline/chips/vic_i.rb +2 -2
- data/lib/badline/chips/z80/core.rb +2 -2
- data/lib/badline/chips/z80/keyed_state.rb +73 -0
- data/lib/badline/chips.rb +10 -0
- data/lib/badline/core.rb +4 -21
- data/lib/badline/drives/datasette/keyed_state.rb +38 -0
- data/lib/badline/drives/datasette.rb +40 -7
- data/lib/badline/drives/engine/core.rb +3 -0
- data/lib/badline/drives/engine/serial_lines.rb +4 -7
- data/lib/badline/drives/model1541/bus.rb +0 -13
- data/lib/badline/drives/model1541/disk.rb +9 -8
- data/lib/badline/drives/model1541/keyed_state.rb +251 -0
- data/lib/badline/drives/model1541/mechanism.rb +0 -2
- data/lib/badline/drives/model1541.rb +1 -2
- data/lib/badline/drives/model1571/bus.rb +0 -13
- data/lib/badline/drives/model1571/keyed_state.rb +108 -0
- data/lib/badline/drives/model1571/wd1770.rb +0 -14
- data/lib/badline/drives/model1571.rb +1 -2
- data/lib/badline/drives/model1581/bus.rb +0 -13
- data/lib/badline/drives/model1581/disk.rb +0 -4
- data/lib/badline/drives/model1581/keyed_state.rb +280 -0
- data/lib/badline/drives/model1581/mechanism.rb +0 -21
- data/lib/badline/drives/model1581/slot.rb +0 -18
- data/lib/badline/drives/model1581/wd1772.rb +0 -2
- data/lib/badline/drives/model1581.rb +2 -2
- data/lib/badline/drives.rb +8 -0
- data/lib/badline/frontend/app.rb +3 -3
- data/lib/badline/frontend/boot.rb +1 -1
- data/lib/badline/frontend/controls.rb +19 -3
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
- data/lib/badline/frontend/snapshots.rb +12 -1
- data/lib/badline/frontend/timeline.rb +28 -10
- data/lib/badline/input/matrix_typist.rb +98 -0
- data/lib/badline/input.rb +1 -0
- data/lib/badline/kernal_trap/chrout.rb +45 -0
- data/lib/badline/kernal_trap/dos/channel.rb +0 -27
- data/lib/badline/kernal_trap/dos/channels.rb +0 -25
- data/lib/badline/kernal_trap/dos/memory.rb +0 -9
- data/lib/badline/kernal_trap/dos/status.rb +0 -8
- data/lib/badline/kernal_trap/dos/write_file.rb +0 -18
- data/lib/badline/kernal_trap/dos.rb +0 -19
- data/lib/badline/kernal_trap/file_routine.rb +4 -4
- data/lib/badline/kernal_trap/keyed_state.rb +150 -0
- data/lib/badline/kernal_trap/layout.rb +75 -11
- data/lib/badline/kernal_trap/load.rb +14 -16
- data/lib/badline/kernal_trap/routine.rb +12 -0
- data/lib/badline/kernal_trap/save.rb +8 -18
- data/lib/badline/kernal_trap/serial.rb +1 -17
- data/lib/badline/kernal_trap.rb +2 -0
- data/lib/badline/machine/keyboard_buffer.rb +49 -0
- data/lib/badline/machine/settling.rb +29 -0
- data/lib/badline/machine.rb +5 -2
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/options/family.rb +5 -2
- data/lib/badline/options/help.rb +42 -16
- data/lib/badline/options/table.rb +2 -0
- data/lib/badline/options/validation.rb +2 -0
- data/lib/badline/options.rb +6 -1
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +312 -0
- data/lib/badline/plus4/cpu.rb +10 -0
- data/lib/badline/plus4/cpu_port.rb +93 -0
- data/lib/badline/plus4/keyboard_port.rb +51 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +164 -0
- data/lib/badline/roms/README +38 -0
- data/lib/badline/roms/plus4/basic.rom +0 -0
- data/lib/badline/roms/plus4/function-high.rom +0 -0
- data/lib/badline/roms/plus4/function-low.rom +0 -0
- data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
- data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
- data/lib/badline/snapshot/c128_setup.rb +9 -20
- data/lib/badline/snapshot/c64_setup.rb +28 -24
- data/lib/badline/snapshot/container.rb +4 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
- data/lib/badline/snapshot/field_doc.rb +109 -0
- data/lib/badline/snapshot/image.rb +134 -28
- data/lib/badline/snapshot/keyed/cursor.rb +40 -0
- data/lib/badline/snapshot/keyed/modules.rb +66 -0
- data/lib/badline/snapshot/keyed/reader.rb +188 -0
- data/lib/badline/snapshot/keyed/recorder.rb +88 -0
- data/lib/badline/snapshot/keyed/report.rb +26 -0
- data/lib/badline/snapshot/keyed/writer.rb +55 -0
- data/lib/badline/snapshot/keyed.rb +64 -0
- data/lib/badline/snapshot/machine_state.rb +17 -64
- data/lib/badline/snapshot/storage_setup.rb +29 -34
- data/lib/badline/snapshot/vic20_setup.rb +6 -15
- data/lib/badline/snapshot/vice/reu1764.rb +2 -2
- data/lib/badline/snapshot/vice.rb +7 -31
- data/lib/badline/snapshot.rb +43 -20
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/g64_image.rb +8 -3
- data/lib/badline/storage/tap.rb +43 -15
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +3 -0
- data/lib/badline/vic20/bus/keyed_state.rb +52 -0
- data/lib/badline/vic20/bus.rb +1 -2
- data/lib/badline/vic20/keyed_modules.rb +73 -0
- data/lib/badline/vic20/keyed_state.rb +45 -0
- data/lib/badline/vic20.rb +5 -5
- data/lib/badline.rb +1 -0
- metadata +61 -42
- data/lib/badline/c128/bus/saved_state.rb +0 -53
- data/lib/badline/c128/saved_state.rb +0 -117
- data/lib/badline/c64/bus/saved_state.rb +0 -68
- data/lib/badline/c64/cartridge/flash/saved_state.rb +0 -40
- data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +0 -47
- data/lib/badline/c64/cartridge/saved_state.rb +0 -63
- data/lib/badline/c64/reu/dma/saved_state.rb +0 -52
- data/lib/badline/c64/reu/saved_state.rb +0 -55
- data/lib/badline/c64/saved_state.rb +0 -112
- data/lib/badline/chips/cia/saved_state.rb +0 -49
- data/lib/badline/chips/cia/serial/saved_state.rb +0 -45
- data/lib/badline/chips/cpu/saved_state.rb +0 -117
- data/lib/badline/chips/sid/envelope/saved_state.rb +0 -70
- data/lib/badline/chips/sid/filter/saved_state.rb +0 -42
- data/lib/badline/chips/sid/saved_state.rb +0 -52
- data/lib/badline/chips/sid/waveform/saved_state.rb +0 -68
- data/lib/badline/chips/vdc/saved_state.rb +0 -36
- data/lib/badline/chips/via/saved_state.rb +0 -44
- data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +0 -40
- data/lib/badline/chips/vic/saved_state.rb +0 -82
- data/lib/badline/chips/vic/sequencer/saved_state.rb +0 -91
- data/lib/badline/chips/vic/sprite/saved_state.rb +0 -66
- data/lib/badline/chips/vic/sprites/saved_state.rb +0 -59
- data/lib/badline/chips/vic_i/painter/saved_state.rb +0 -40
- data/lib/badline/chips/vic_i/saved_state.rb +0 -55
- data/lib/badline/chips/z80/saved_state.rb +0 -71
- data/lib/badline/chrout_trap.rb +0 -43
- data/lib/badline/debug_register.rb +0 -28
- data/lib/badline/drives/datasette/saved_state.rb +0 -37
- data/lib/badline/drives/model1541/disk/saved_state.rb +0 -88
- data/lib/badline/drives/model1541/mechanism/saved_state.rb +0 -72
- data/lib/badline/drives/model1541/saved_state.rb +0 -44
- data/lib/badline/drives/model1571/saved_state.rb +0 -47
- data/lib/badline/drives/model1581/disk/saved_state.rb +0 -89
- data/lib/badline/drives/model1581/saved_state.rb +0 -42
- data/lib/badline/drives/model1581/wd1772/saved_state.rb +0 -68
- data/lib/badline/keyboard_buffer.rb +0 -33
- data/lib/badline/snapshot/setup.rb +0 -31
- data/lib/badline/snapshot/state.rb +0 -179
- data/lib/badline/vic20/bus/saved_state.rb +0 -35
- data/lib/badline/vic20/saved_state.rb +0 -110
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# A cartridge's fields in a keyed snapshot module (Snapshot::Keyed,
|
|
7
|
+
# BL-CART): the CRT image it was built from and its jumpers, which
|
|
8
|
+
# build it again (Cartridge.from_fields), the EXROM and GAME lines,
|
|
9
|
+
# its pull on NMI, the button, and where ROML and ROMH point, each by
|
|
10
|
+
# the name of the bank or view it shows (window_labels), and whatever
|
|
11
|
+
# each mapper adds.
|
|
12
|
+
module KeyedState
|
|
13
|
+
def save_fields(out)
|
|
14
|
+
save_setup_fields(out)
|
|
15
|
+
out.int("exrom", @exrom).int("game", @game).bool("nmi", @nmi).bool("button", @button == true)
|
|
16
|
+
save_mapper_fields(out)
|
|
17
|
+
out
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load_fields(input)
|
|
21
|
+
@exrom = input.masked("exrom", @exrom, 1)
|
|
22
|
+
@game = input.masked("game", @game, 1)
|
|
23
|
+
@nmi = input.bool("nmi", @nmi)
|
|
24
|
+
@button = input.bool("button", false)
|
|
25
|
+
load_mapper_fields(input)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def save_setup_fields(out)
|
|
31
|
+
out.bytes("crt", Storage::CRTFile.encode(@crt))
|
|
32
|
+
save_jumper_fields(out)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def save_jumper_fields(_out); end
|
|
36
|
+
|
|
37
|
+
def save_mapper_fields(out)
|
|
38
|
+
save_window_fields(out)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def load_mapper_fields(input)
|
|
42
|
+
load_window_fields(input)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The name of each of `windows`, in the same order: bank<N> for the
|
|
46
|
+
# mapper's banks, and empty for erased ROM.
|
|
47
|
+
def window_labels
|
|
48
|
+
banks = @banks
|
|
49
|
+
return [] unless banks
|
|
50
|
+
|
|
51
|
+
[*Array.new(banks.length) { |number| "bank#{number}" }, "empty"]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def save_window_fields(out)
|
|
55
|
+
choices = windows
|
|
56
|
+
return unless choices
|
|
57
|
+
|
|
58
|
+
labels = window_labels
|
|
59
|
+
save_window(out, "roml", @roml, choices, labels)
|
|
60
|
+
save_window(out, "romh", @romh, choices, labels)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def save_window(out, key, window, choices, labels)
|
|
64
|
+
return if window.nil?
|
|
65
|
+
|
|
66
|
+
out.text(key, labels.fetch(window_index(window, choices)))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def load_window_fields(input)
|
|
70
|
+
choices = windows
|
|
71
|
+
return unless choices
|
|
72
|
+
|
|
73
|
+
labels = window_labels
|
|
74
|
+
@roml = window_named(input.choice("roml", [*labels, "none"], "none"), choices, labels)
|
|
75
|
+
@romh = window_named(input.choice("romh", [*labels, "none"], "none"), choices, labels)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def window_named(label, choices, labels)
|
|
79
|
+
index = labels.index(label)
|
|
80
|
+
index.nil? ? nil : choices.fetch(index)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
include KeyedState
|
|
85
|
+
|
|
86
|
+
class << self
|
|
87
|
+
# A cartridge built afresh from the CRT image and the jumpers a
|
|
88
|
+
# keyed snapshot module holds, before load_fields puts its state in.
|
|
89
|
+
def from_fields(input)
|
|
90
|
+
kb = input.int("geo_ram_kb", 0)
|
|
91
|
+
return GeoRAM.new(size: kb) if kb.positive?
|
|
92
|
+
|
|
93
|
+
crt = Storage::CRTFile.new(bytes: input.bytes("crt", "".b))
|
|
94
|
+
mapper_for(crt).from_jumper_fields(crt, input)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def from_jumper_fields(crt, _input) = new(crt)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Two banks of ROM, one behind each window: roml<N> and romh<N>.
|
|
101
|
+
module SplitBanks
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def window_labels
|
|
105
|
+
[*Array.new(@roml_banks.length) { |number| "roml#{number}" },
|
|
106
|
+
*Array.new(@romh_banks.length) { |number| "romh#{number}" }, "empty"]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class SuperGames
|
|
111
|
+
include SplitBanks
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def save_mapper_fields(out)
|
|
116
|
+
super
|
|
117
|
+
out.bool("locked", @locked)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def load_mapper_fields(input)
|
|
121
|
+
super
|
|
122
|
+
@locked = input.bool("locked", @locked)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Comal80
|
|
127
|
+
include SplitBanks
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class FinalCartridge3
|
|
131
|
+
include SplitBanks
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def save_mapper_fields(out)
|
|
136
|
+
super
|
|
137
|
+
out.bool("register_visible", @register_visible).bool("phi1_ultimax", @phi1_ultimax)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def load_mapper_fields(input)
|
|
141
|
+
super
|
|
142
|
+
@io_bank = @roml
|
|
143
|
+
@register_visible = input.bool("register_visible", @register_visible)
|
|
144
|
+
@phi1_ultimax = input.bool("phi1_ultimax", @phi1_ultimax)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class RGCD
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
def save_mapper_fields(out)
|
|
152
|
+
super
|
|
153
|
+
out.bool("disabled", @disabled)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def load_mapper_fields(input)
|
|
157
|
+
super
|
|
158
|
+
@disabled = input.bool("disabled", @disabled)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
class Zaxxon
|
|
163
|
+
class High
|
|
164
|
+
def save_fields(out)
|
|
165
|
+
out.int("high_bank", [*@banks, EMPTY_BANK].index { |bank| bank.equal?(@bank) }.to_i)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def load_fields(input)
|
|
169
|
+
choices = [*@banks, EMPTY_BANK]
|
|
170
|
+
@bank = choices.fetch(input.int("high_bank", 0).clamp(0, choices.length - 1))
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
def save_mapper_fields(out) = @romh.save_fields(out)
|
|
177
|
+
|
|
178
|
+
def load_mapper_fields(input) = @romh.load_fields(input)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
class EpyxFastload
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
# How long ago the capacitor was discharged, on the machine's clock.
|
|
185
|
+
def save_mapper_fields(out)
|
|
186
|
+
out.int("discharged_ago", now_cycles - @discharged_at)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def load_mapper_fields(input)
|
|
190
|
+
@discharged_at = now_cycles - input.int("discharged_ago", now_cycles - @discharged_at)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def now_cycles
|
|
194
|
+
clock = @clock
|
|
195
|
+
clock ? clock.call : 0
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class ActionReplay
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
def window_labels
|
|
203
|
+
["through", "isolated", "contended", *Array.new(@rom_banks.length) { |number| "rom#{number}" }, "empty"]
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def save_mapper_fields(out)
|
|
207
|
+
super
|
|
208
|
+
out.text("rom", window_labels.fetch(window_index(@rom, windows))).bool("active", @active)
|
|
209
|
+
out.bool("io2_ram", @io2_ram).bytes("ram", @ram_data)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def load_mapper_fields(input)
|
|
213
|
+
super
|
|
214
|
+
labels = window_labels
|
|
215
|
+
@rom = window_named(input.choice("rom", labels, "rom0"), windows, labels)
|
|
216
|
+
@active = input.bool("active", @active)
|
|
217
|
+
@io2_ram = input.bool("io2_ram", @io2_ram)
|
|
218
|
+
input.bytes_into("ram", @ram_data)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
class KCSPower
|
|
223
|
+
private
|
|
224
|
+
|
|
225
|
+
def save_mapper_fields(out) = out.bytes("io_ram", @io_ram)
|
|
226
|
+
|
|
227
|
+
def load_mapper_fields(input) = input.bytes_into("io_ram", @io_ram)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
class Pagefox
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
def window_labels
|
|
234
|
+
[*Array.new(@roml_banks.length) { |number| "roml#{number}" },
|
|
235
|
+
*Array.new(@romh_banks.length) { |number| "romh#{number}" },
|
|
236
|
+
*Array.new(@ram_banks.length) { |number| "ram#{number}" }, "open_bus", "empty"]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def save_mapper_fields(out)
|
|
240
|
+
super
|
|
241
|
+
@ram_banks.each_with_index { |ram, number| out.bytes("ram#{number}.data", ram.data) }
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def load_mapper_fields(input)
|
|
245
|
+
super
|
|
246
|
+
@ram_banks.each_with_index { |ram, number| input.bytes_into("ram#{number}.data", ram.data) }
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
class GeoRAM
|
|
251
|
+
private
|
|
252
|
+
|
|
253
|
+
def save_setup_fields(out) = out.int("geo_ram_kb", @size)
|
|
254
|
+
|
|
255
|
+
def save_mapper_fields(out)
|
|
256
|
+
out.bytes("ram", @ram_data).int("block", @block).int("page", @page).int("window", @window)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def load_mapper_fields(input)
|
|
260
|
+
input.bytes_into("ram", @ram_data)
|
|
261
|
+
@block = input.int("block", @block)
|
|
262
|
+
@page = input.int("page", @page)
|
|
263
|
+
@window = input.int("window", @window)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
class Flash
|
|
268
|
+
# The array, the command state by name, how far through a command
|
|
269
|
+
# sequence, the toggle bit, a timed operation's end in cycles from
|
|
270
|
+
# now, and the windows handed out so far, under `prefix`.
|
|
271
|
+
module KeyedState
|
|
272
|
+
STATE_NAMES = STATES.map(&:to_s).freeze
|
|
273
|
+
|
|
274
|
+
def save_fields(out, prefix)
|
|
275
|
+
out.bytes("#{prefix}data", @data).text("#{prefix}state", @state.to_s)
|
|
276
|
+
out.text("#{prefix}base_state", @base_state.to_s).int("#{prefix}cycle", @cycle)
|
|
277
|
+
out.int("#{prefix}toggle", @toggle)
|
|
278
|
+
done_at = @done_at
|
|
279
|
+
out.int("#{prefix}done_in", done_at - @clock.call) unless done_at.nil?
|
|
280
|
+
out.optional_int("#{prefix}programmed", @programmed).optional_int("#{prefix}remaining", @remaining)
|
|
281
|
+
sectors = @erasing_sectors
|
|
282
|
+
out.ints("#{prefix}erasing_sectors", sectors) if sectors
|
|
283
|
+
out.ints("#{prefix}windows", @windows.keys).ints("#{prefix}status_windows", @status_windows.keys)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def load_fields(input, prefix)
|
|
287
|
+
input.bytes_into("#{prefix}data", @data)
|
|
288
|
+
@state = state_named(input.choice("#{prefix}state", STATE_NAMES, "read"))
|
|
289
|
+
@base_state = state_named(input.choice("#{prefix}base_state", STATE_NAMES, "read"))
|
|
290
|
+
@cycle = input.int("#{prefix}cycle", @cycle)
|
|
291
|
+
@toggle = input.int("#{prefix}toggle", @toggle)
|
|
292
|
+
done_in = input.optional_int("#{prefix}done_in")
|
|
293
|
+
@done_at = done_in.nil? ? nil : @clock.call + done_in
|
|
294
|
+
@programmed = input.optional_int("#{prefix}programmed")
|
|
295
|
+
@remaining = input.optional_int("#{prefix}remaining")
|
|
296
|
+
sectors = input.ints("#{prefix}erasing_sectors", [])
|
|
297
|
+
@erasing_sectors = sectors.empty? ? nil : sectors.dup
|
|
298
|
+
rebuild_windows(input.ints("#{prefix}windows", []), input.ints("#{prefix}status_windows", []))
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
private
|
|
302
|
+
|
|
303
|
+
def state_named(name)
|
|
304
|
+
STATES.find { |state| state.to_s == name } || :read
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
include KeyedState
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
class GMod2
|
|
312
|
+
private
|
|
313
|
+
|
|
314
|
+
def save_mapper_fields(out)
|
|
315
|
+
out.int("bank", @bank).bool("flash_writes", @flash_writes)
|
|
316
|
+
@flash.save_fields(out, "flash.")
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def load_mapper_fields(input)
|
|
320
|
+
@bank = input.int("bank", @bank)
|
|
321
|
+
@flash_writes = input.bool("flash_writes", @flash_writes)
|
|
322
|
+
@flash.load_fields(input, "flash.")
|
|
323
|
+
@roml = @flash.window(@bank * BANK_SIZE)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
class EasyFlash
|
|
328
|
+
private
|
|
329
|
+
|
|
330
|
+
def save_mapper_fields(out)
|
|
331
|
+
out.int("bank", @bank).int("control", @control).bytes("io_ram", @io_ram)
|
|
332
|
+
@low_flash.save_fields(out, "flash_low.")
|
|
333
|
+
@high_flash.save_fields(out, "flash_high.")
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def load_mapper_fields(input)
|
|
337
|
+
@bank = input.int("bank", @bank)
|
|
338
|
+
@control = input.byte("control", @control)
|
|
339
|
+
input.bytes_into("io_ram", @io_ram)
|
|
340
|
+
@low_flash.load_fields(input, "flash_low.")
|
|
341
|
+
@high_flash.load_fields(input, "flash_high.")
|
|
342
|
+
map_bank
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
class RetroReplay
|
|
347
|
+
CONFIG_NAMES = CONTROL_MODES.map(&:to_s).freeze
|
|
348
|
+
|
|
349
|
+
# The flash and bank jumpers, as the board has them set.
|
|
350
|
+
def self.from_jumper_fields(crt, input)
|
|
351
|
+
new(crt, flash_jumper: input.bool("jumper.flash", false), bank_jumper: input.bool("jumper.bank", false))
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
private
|
|
355
|
+
|
|
356
|
+
def save_jumper_fields(out)
|
|
357
|
+
out.bool("jumper.flash", @flash_jumper).bool("jumper.bank", @bank_jumper)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def save_mapper_fields(out)
|
|
361
|
+
out.bool("active", @active).bool("frozen", @frozen).bool("phi1_ultimax", @phi1_ultimax)
|
|
362
|
+
out.bool("ram_enabled", @ram_enabled).bool("ram_at_a000", @ram_at_a000).bool("allow_bank", @allow_bank)
|
|
363
|
+
out.bool("no_freeze", @no_freeze).bool("reu_mapping", @reu_mapping).bool("write_once", @write_once)
|
|
364
|
+
out.int("bank", @bank).text("config", @config.to_s)
|
|
365
|
+
@ram_data.each_with_index { |data, number| out.bytes("ram#{number}", data) }
|
|
366
|
+
@flash.save_fields(out, "flash.")
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def load_mapper_fields(input)
|
|
370
|
+
load_flag_fields(input)
|
|
371
|
+
@bank = input.int("bank", @bank)
|
|
372
|
+
config = input.choice("config", CONFIG_NAMES, "rom8k")
|
|
373
|
+
@config = CONTROL_MODES.find { |mode| mode.to_s == config } || :rom8k
|
|
374
|
+
@ram_data.each_with_index { |data, number| input.bytes_into("ram#{number}", data) }
|
|
375
|
+
@flash.load_fields(input, "flash.")
|
|
376
|
+
map_windows
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def load_flag_fields(input)
|
|
380
|
+
@active = input.bool("active", @active)
|
|
381
|
+
@frozen = input.bool("frozen", @frozen)
|
|
382
|
+
@phi1_ultimax = input.bool("phi1_ultimax", @phi1_ultimax)
|
|
383
|
+
@ram_enabled = input.bool("ram_enabled", @ram_enabled)
|
|
384
|
+
@ram_at_a000 = input.bool("ram_at_a000", @ram_at_a000)
|
|
385
|
+
@allow_bank = input.bool("allow_bank", @allow_bank)
|
|
386
|
+
@no_freeze = input.bool("no_freeze", @no_freeze)
|
|
387
|
+
@reu_mapping = input.bool("reu_mapping", @reu_mapping)
|
|
388
|
+
@write_once = input.bool("write_once", @write_once)
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
@@ -30,16 +30,6 @@ module Badline
|
|
|
30
30
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
|
-
def save_mapper(out)
|
|
34
|
-
super
|
|
35
|
-
@ram_banks.each { |ram| out.blob(ram.data) }
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def load_mapper(input)
|
|
39
|
-
super
|
|
40
|
-
@ram_banks.each { |ram| input.blob_into(ram.data) }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
33
|
def windows = [*@roml_banks, *@romh_banks, *@ram_banks, @open_bus || EMPTY_BANK, EMPTY_BANK]
|
|
44
34
|
|
|
45
35
|
def select(value)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/c64/cartridge/retro_replay/saved_state"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
4
|
class C64
|
|
7
5
|
class Cartridge
|
|
@@ -47,7 +45,6 @@ module Badline
|
|
|
47
45
|
# the half in bit 5.
|
|
48
46
|
class RetroReplay < Cartridge
|
|
49
47
|
include Freezer
|
|
50
|
-
include SavedState
|
|
51
48
|
|
|
52
49
|
CONTROL_MODES = %i[rom8k rom16k off ultimax].freeze
|
|
53
50
|
IO1_PAGE = 0x1e00
|
|
@@ -55,11 +52,6 @@ module Badline
|
|
|
55
52
|
|
|
56
53
|
attr_reader :flash
|
|
57
54
|
|
|
58
|
-
def self.from_jumpers(crt, input)
|
|
59
|
-
input.boolean?
|
|
60
|
-
new(crt, flash_jumper: input.boolean?, bank_jumper: input.boolean?)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
55
|
def initialize(crt, flash_jumper: false, bank_jumper: false)
|
|
64
56
|
@nordic = crt.subtype == 1
|
|
65
57
|
@flash_jumper = flash_jumper
|
|
@@ -29,16 +29,6 @@ module Badline
|
|
|
29
29
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
|
-
def save_mapper(out)
|
|
33
|
-
super
|
|
34
|
-
out.boolean(@disabled)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def load_mapper(input)
|
|
38
|
-
super
|
|
39
|
-
@disabled = input.boolean?
|
|
40
|
-
end
|
|
41
|
-
|
|
42
32
|
def select(value)
|
|
43
33
|
value ^= 0x07 if @hucky
|
|
44
34
|
if value.anybits?(0x08)
|
|
@@ -21,16 +21,6 @@ module Badline
|
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
def save_mapper(out)
|
|
25
|
-
super
|
|
26
|
-
out.boolean(@locked)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def load_mapper(input)
|
|
30
|
-
super
|
|
31
|
-
@locked = input.boolean?
|
|
32
|
-
end
|
|
33
|
-
|
|
34
24
|
def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
|
|
35
25
|
|
|
36
26
|
def select(value)
|
|
@@ -32,14 +32,6 @@ module Badline
|
|
|
32
32
|
def peek(addr)
|
|
33
33
|
@bank.peek(addr)
|
|
34
34
|
end
|
|
35
|
-
|
|
36
|
-
def save_state(out)
|
|
37
|
-
out.int([*@banks, EMPTY_BANK].index { |bank| bank.equal?(@bank) })
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def load_state(input)
|
|
41
|
-
@bank = [*@banks, EMPTY_BANK].fetch(input.int)
|
|
42
|
-
end
|
|
43
35
|
end
|
|
44
36
|
|
|
45
37
|
def reset
|
|
@@ -48,14 +40,6 @@ module Badline
|
|
|
48
40
|
|
|
49
41
|
private
|
|
50
42
|
|
|
51
|
-
def save_mapper(out)
|
|
52
|
-
@romh.save_state(out)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def load_mapper(input)
|
|
56
|
-
@romh.load_state(input)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
43
|
def install_chips(chips)
|
|
60
44
|
roml, romh = banks_from(chips)
|
|
61
45
|
@romh = High.new(romh)
|
|
@@ -27,15 +27,11 @@ require "badline/c64/cartridge/final_cartridge3"
|
|
|
27
27
|
require "badline/c64/cartridge/retro_replay"
|
|
28
28
|
require "badline/c64/cartridge/kcs_power"
|
|
29
29
|
require "badline/c64/cartridge/geo_ram"
|
|
30
|
-
require "badline/c64/cartridge/saved_state"
|
|
31
30
|
|
|
32
31
|
module Badline
|
|
33
32
|
class C64
|
|
34
33
|
class Cartridge
|
|
35
|
-
include SavedState
|
|
36
|
-
|
|
37
34
|
class UnsupportedTypeError < StandardError; end
|
|
38
|
-
|
|
39
35
|
ROMH_START = 0xa000
|
|
40
36
|
BANK_SIZE = 0x2000
|
|
41
37
|
|
|
@@ -70,22 +66,6 @@ module Badline
|
|
|
70
66
|
mapper_for(crt).new(crt, **)
|
|
71
67
|
end
|
|
72
68
|
|
|
73
|
-
# A cartridge built afresh from what save_setup wrote, before
|
|
74
|
-
# load_state puts its state in.
|
|
75
|
-
def from_setup(input)
|
|
76
|
-
return GeoRAM.new(size: input.int) if input.int == GEO_RAM_SETUP
|
|
77
|
-
|
|
78
|
-
crt = Storage::CRTFile.new(bytes: input.string)
|
|
79
|
-
mapper_for(crt).from_jumpers(crt, input)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# A cartridge from its image and what save_jumpers wrote: whether
|
|
83
|
-
# there are jumpers, then their settings on a mapper that has them.
|
|
84
|
-
def from_jumpers(crt, input)
|
|
85
|
-
input.boolean?
|
|
86
|
-
new(crt)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
69
|
def mapper_for(crt)
|
|
90
70
|
HARDWARE_TYPES.fetch(crt.hardware_type) do
|
|
91
71
|
raise UnsupportedTypeError,
|
|
@@ -94,11 +74,6 @@ module Badline
|
|
|
94
74
|
end
|
|
95
75
|
end
|
|
96
76
|
|
|
97
|
-
# What save_setup writes first: a cartridge from a CRT image, or a
|
|
98
|
-
# GEO-RAM.
|
|
99
|
-
CRT_SETUP = 0
|
|
100
|
-
GEO_RAM_SETUP = 1
|
|
101
|
-
|
|
102
77
|
# Keeps the image it was built from, so a snapshot's machine can build
|
|
103
78
|
# the same cartridge before the snapshot's state goes into it.
|
|
104
79
|
def initialize(crt)
|
|
@@ -191,8 +166,6 @@ module Badline
|
|
|
191
166
|
raise(ArgumentError, "#{self.class.name}: a window that isn't among its banks")
|
|
192
167
|
end
|
|
193
168
|
|
|
194
|
-
def window_at(index, windows) = index.negative? ? nil : windows.fetch(index)
|
|
195
|
-
|
|
196
169
|
def nmi=(level)
|
|
197
170
|
return if @nmi == level
|
|
198
171
|
|
|
@@ -256,3 +229,4 @@ module Badline
|
|
|
256
229
|
end
|
|
257
230
|
end
|
|
258
231
|
end
|
|
232
|
+
require "badline/c64/cartridge/keyed_state"
|
|
@@ -36,13 +36,38 @@ module Badline
|
|
|
36
36
|
# traps, or an empty one.
|
|
37
37
|
def mounted_path = @dos.nil? ? "" : @dos.path
|
|
38
38
|
|
|
39
|
-
# Records what the KERNAL prints through CHROUT (
|
|
39
|
+
# Records what the KERNAL prints through CHROUT (KernalTrap::Chrout).
|
|
40
40
|
def capture_output
|
|
41
|
-
@capture_output ||=
|
|
42
|
-
cpu.install_trap(
|
|
41
|
+
@capture_output ||= KernalTrap::Chrout.new(cpu:, bus: trap_bus, layout: trap_layout).tap do |trap|
|
|
42
|
+
cpu.install_trap(KernalTrap::Chrout::ADDRESS) { trap.call }
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Device 8 through the KERNAL traps in a keyed snapshot module
|
|
47
|
+
# (BL-TRAPDOS): the storage under storage.*, the DOS, the serial
|
|
48
|
+
# trap under serial.* and the SAVE trap under save.*. Nothing mounted
|
|
49
|
+
# writes nothing.
|
|
50
|
+
def save_trap_dos_fields(out)
|
|
51
|
+
dos = @dos
|
|
52
|
+
return unless dos
|
|
53
|
+
|
|
54
|
+
Snapshot::StorageSetup.write_fields(dos.storage, out)
|
|
55
|
+
dos.save_fields(out)
|
|
56
|
+
@serial_trap&.save_fields(out, "serial.")
|
|
57
|
+
@save_trap&.save_fields(out, "save.")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# `detached` mounts a disk image write-protected and no directory.
|
|
61
|
+
def load_trap_dos_fields(input, detached: false)
|
|
62
|
+
unmount
|
|
63
|
+
return if input.missing?
|
|
64
|
+
|
|
65
|
+
mount(Snapshot::StorageSetup.read_fields(input, detached:))
|
|
66
|
+
@dos&.load_fields(input)
|
|
67
|
+
@serial_trap&.load_fields(input, "serial.")
|
|
68
|
+
@save_trap&.load_fields(input, "save.")
|
|
69
|
+
end
|
|
70
|
+
|
|
46
71
|
private
|
|
47
72
|
|
|
48
73
|
def install_kernal_traps(layout)
|
|
@@ -62,28 +87,6 @@ module Badline
|
|
|
62
87
|
@serial_trap = nil
|
|
63
88
|
@save_trap = nil
|
|
64
89
|
end
|
|
65
|
-
|
|
66
|
-
# The disk device 8 serves through the traps, with the traps' own
|
|
67
|
-
# state: open channels, the drive's status and RAM, a SAVE under way.
|
|
68
|
-
def save_trap_dos(out)
|
|
69
|
-
out.boolean(!@dos.nil?)
|
|
70
|
-
return unless @dos
|
|
71
|
-
|
|
72
|
-
@dos.save_state(out)
|
|
73
|
-
@serial_trap.save_state(out)
|
|
74
|
-
@save_trap.save_state(out)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def load_trap_dos(input)
|
|
78
|
-
return unmount unless input.boolean?
|
|
79
|
-
|
|
80
|
-
storage = Snapshot::StorageSetup.read(input)
|
|
81
|
-
unmount
|
|
82
|
-
mount(storage)
|
|
83
|
-
@dos.load_state(input)
|
|
84
|
-
@serial_trap.load_state(input)
|
|
85
|
-
@save_trap.load_state(input)
|
|
86
|
-
end
|
|
87
90
|
end
|
|
88
91
|
end
|
|
89
92
|
end
|