badline 0.13.0 → 0.15.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 +72 -1
- data/CLAUDE.md +42 -12
- data/CONTRIBUTING.md +4 -3
- data/README.md +76 -13
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +174 -0
- data/doc/snapshot-format.md +1197 -0
- data/doc/snapshots.md +90 -40
- data/lib/badline/audio/bare_player.rb +46 -17
- data/lib/badline/audio/machine_player.rb +6 -10
- data/lib/badline/audio/renderer.rb +32 -21
- data/lib/badline/c128/banks.rb +7 -6
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +36 -4
- 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 +27 -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/extra_sids.rb +80 -0
- data/lib/badline/c64/bus/keyed_state.rb +41 -0
- data/lib/badline/c64/bus.rb +3 -1
- data/lib/badline/c64/cartridge/action_replay.rb +6 -15
- data/lib/badline/c64/cartridge/comal80.rb +1 -0
- data/lib/badline/c64/cartridge/dinamic.rb +3 -1
- 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 +1 -12
- data/lib/badline/c64/cartridge/flash.rb +3 -3
- data/lib/badline/c64/cartridge/fun_play.rb +2 -1
- data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
- data/lib/badline/c64/cartridge/game_system.rb +2 -1
- 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 +514 -0
- data/lib/badline/c64/cartridge/magic_desk.rb +2 -1
- data/lib/badline/c64/cartridge/ocean.rb +1 -0
- data/lib/badline/c64/cartridge/pagefox.rb +1 -10
- data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
- data/lib/badline/c64/cartridge/rgcd.rb +2 -11
- data/lib/badline/c64/cartridge/super_games.rb +3 -12
- data/lib/badline/c64/cartridge/zaxxon.rb +1 -16
- data/lib/badline/c64/cartridge.rb +4 -30
- data/lib/badline/c64/kernal_traps.rb +28 -25
- data/lib/badline/c64/keyed_modules.rb +115 -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 +118 -0
- data/lib/badline/chips/ted/fetches.rb +199 -0
- data/lib/badline/chips/ted/keyed_state.rb +156 -0
- data/lib/badline/chips/ted/modes.rb +134 -0
- data/lib/badline/chips/ted/palette.rb +43 -0
- data/lib/badline/chips/ted/picture.rb +182 -0
- data/lib/badline/chips/ted/sound/output.rb +86 -0
- data/lib/badline/chips/ted/sound.rb +284 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +190 -0
- data/lib/badline/chips/tia.rb +61 -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 +43 -0
- data/lib/badline/drives/datasette.rb +62 -12
- 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 +4 -2
- data/lib/badline/frontend/controls.rb +25 -5
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/keys.rb +14 -2
- data/lib/badline/frontend/media_slots.rb +11 -1
- data/lib/badline/frontend/menu/menu_dialogs.rb +4 -3
- data/lib/badline/frontend/menu/menu_media.rb +13 -3
- data/lib/badline/frontend/menu/menu_pages.rb +8 -8
- data/lib/badline/frontend/menu/menu_ports.rb +13 -1
- data/lib/badline/frontend/screen.rb +33 -12
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/snapshots.rb +12 -1
- data/lib/badline/frontend/sound.rb +4 -4
- data/lib/badline/frontend/timeline.rb +43 -12
- 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 +29 -6
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/media/plus4_media.rb +68 -0
- data/lib/badline/media/program.rb +20 -6
- data/lib/badline/media.rb +33 -12
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/options/family.rb +6 -2
- data/lib/badline/options/help.rb +66 -16
- data/lib/badline/options/table.rb +17 -2
- data/lib/badline/options/validation.rb +26 -2
- data/lib/badline/options.rb +42 -9
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +375 -0
- data/lib/badline/plus4/cassette_sense.rb +21 -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 +76 -0
- data/lib/badline/plus4/keyed_modules.rb +99 -0
- data/lib/badline/plus4/keyed_parts.rb +143 -0
- data/lib/badline/plus4/keyed_state.rb +41 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +240 -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 +6 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +113 -0
- data/lib/badline/snapshot/field_doc.rb +148 -0
- data/lib/badline/snapshot/image.rb +143 -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/plus4_setup.rb +39 -0
- 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 +48 -20
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/g64_image.rb +9 -4
- data/lib/badline/storage/tap.rb +57 -20
- data/lib/badline/support/checkpoint.rb +3 -2
- data/lib/badline/support/plus4_checkpoint.rb +42 -0
- 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 +75 -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
data/lib/badline/roms/README
CHANGED
|
@@ -132,3 +132,41 @@ c128/character.rom
|
|
|
132
132
|
The character generator ROM, 390059-01 (characters.390059-01.bin),
|
|
133
133
|
8 KB: the C64's set, the same as character.rom, followed by the C128's.
|
|
134
134
|
SHA-1 29ed066d513f2d5c09ff26d9166ba23c2afb2b3f.
|
|
135
|
+
|
|
136
|
+
Commodore 264 series ROMs
|
|
137
|
+
=========================
|
|
138
|
+
|
|
139
|
+
Downloaded from
|
|
140
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/plus4/
|
|
141
|
+
for the Plus/4, the C16 and the C116, which share BASIC and the KERNAL.
|
|
142
|
+
Nothing maps them yet.
|
|
143
|
+
|
|
144
|
+
plus4/basic.rom
|
|
145
|
+
---------------
|
|
146
|
+
BASIC 3.5, 318006-01 (basic.318006-01.bin), for $8000-$BFFF in ROM
|
|
147
|
+
bank 0.
|
|
148
|
+
SHA-1 161c96b4ad20f3a4f2321808e37a5ded26a135dd.
|
|
149
|
+
|
|
150
|
+
plus4/kernal-pal.rom
|
|
151
|
+
--------------------
|
|
152
|
+
The PAL KERNAL, 318004-05 (kernal.318004-05.bin), for $C000-$FFFF in
|
|
153
|
+
ROM bank 0, with the character set from $D000.
|
|
154
|
+
SHA-1 7c7e07f016391174a557e790c4ef1cbe33512cdb.
|
|
155
|
+
|
|
156
|
+
plus4/kernal-ntsc.rom
|
|
157
|
+
---------------------
|
|
158
|
+
The NTSC KERNAL, 318005-05 (kernal.318005-05.bin), for $C000-$FFFF in
|
|
159
|
+
ROM bank 0.
|
|
160
|
+
SHA-1 a3d9e5be091b98de39a046ab167fb7632d053682.
|
|
161
|
+
|
|
162
|
+
plus4/function-low.rom
|
|
163
|
+
----------------------
|
|
164
|
+
The Plus/4's 3-plus-1 function ROM, low half, 317053-01
|
|
165
|
+
(3-plus-1.317053-01.bin), for $8000-$BFFF in ROM bank 1.
|
|
166
|
+
SHA-1 3b69f6e7cb4c18bb08e203fb18b7dabfa853390f.
|
|
167
|
+
|
|
168
|
+
plus4/function-high.rom
|
|
169
|
+
-----------------------
|
|
170
|
+
The Plus/4's 3-plus-1 function ROM, high half, 317054-01
|
|
171
|
+
(3-plus-1.317054-01.bin), for $C000-$FFFF in ROM bank 1.
|
|
172
|
+
SHA-1 0ad7ac2db7da692d972e586ca0dfd747d82c7693.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -7,31 +7,20 @@ module Badline
|
|
|
7
7
|
C128Setup = Data.define(:model, :sid_model, :mode)
|
|
8
8
|
|
|
9
9
|
class C128Setup
|
|
10
|
-
MARKER = "C128"
|
|
11
10
|
SID_MODELS = %i[mos6581 mos8580].freeze
|
|
12
11
|
MODES = %i[c64 c128].freeze
|
|
13
12
|
|
|
14
|
-
# The
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
input.marker(MARKER)
|
|
18
|
-
input.check_stamp
|
|
19
|
-
read(input)
|
|
13
|
+
# The model, the SID and the mode by name, in a keyed snapshot module.
|
|
14
|
+
def write_fields(out)
|
|
15
|
+
out.text("model", model).text("sid_model", sid_model.to_s).text("mode", mode.to_s)
|
|
20
16
|
end
|
|
21
17
|
|
|
22
|
-
def self.
|
|
23
|
-
model = input.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
new(model:, sid_model:, mode: MODES.fetch(input.int))
|
|
29
|
-
rescue IndexError
|
|
30
|
-
raise FormatError, "the state names a SID or a mode badline doesn't know"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def write(out)
|
|
34
|
-
out.string(model).int(SID_MODELS.index(sid_model)).int(MODES.index(mode))
|
|
18
|
+
def self.read_fields(input)
|
|
19
|
+
model = input.choice("model", C128::Model::ALL.map(&:name), "c128")
|
|
20
|
+
sid = input.choice("sid_model", SID_MODELS.map(&:to_s), "mos6581")
|
|
21
|
+
mode = input.choice("mode", MODES.map(&:to_s), "c64")
|
|
22
|
+
new(model:, sid_model: SID_MODELS.find { |name| name.to_s == sid } || :mos6581,
|
|
23
|
+
mode: MODES.find { |name| name.to_s == mode } || :c64)
|
|
35
24
|
end
|
|
36
25
|
|
|
37
26
|
# A machine built this way, at power-on.
|
|
@@ -9,7 +9,6 @@ module Badline
|
|
|
9
9
|
:board)
|
|
10
10
|
|
|
11
11
|
class C64Setup
|
|
12
|
-
MARKER = "COMPUTER"
|
|
13
12
|
SID_MODELS = %i[mos6581 mos8580].freeze
|
|
14
13
|
REGIONS = [Region::PAL, Region::NTSC, Region::NTSC_OLD, Region::DREAN].freeze
|
|
15
14
|
RAM_EXPANSIONS = [:none, *C64::BankedRAM::TYPES.keys].freeze
|
|
@@ -22,23 +21,6 @@ module Badline
|
|
|
22
21
|
kernal: address_bus.kernal, datasette: address_bus.datasette.connected?, board: address_bus.board)
|
|
23
22
|
end
|
|
24
23
|
|
|
25
|
-
# The setup a C64's State starts with.
|
|
26
|
-
def self.from(state)
|
|
27
|
-
input = StateReader.new(state)
|
|
28
|
-
input.marker(MARKER)
|
|
29
|
-
input.check_stamp
|
|
30
|
-
read(input)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def self.read(input)
|
|
34
|
-
new(vic_model: Chips::VIC::MODELS.fetch(input.int), cia_model: Chips::CIA::MODELS.fetch(input.int),
|
|
35
|
-
sid_model: SID_MODELS.fetch(input.int), region: REGIONS.fetch(input.int),
|
|
36
|
-
ram_expansion: ram_expansion(RAM_EXPANSIONS.fetch(input.int)), reu: reu(input.int),
|
|
37
|
-
kernal: KERNALS.fetch(input.int), datasette: input.boolean?, board: BOARDS.fetch(input.int))
|
|
38
|
-
rescue IndexError
|
|
39
|
-
raise FormatError, "the state names a chip model, region, RAM expansion, KERNAL or board badline doesn't know"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
24
|
def self.ram_expansion(name) = name == :none ? nil : name
|
|
43
25
|
|
|
44
26
|
# An REU's size in K, written as 0 for none.
|
|
@@ -46,14 +28,36 @@ module Badline
|
|
|
46
28
|
return nil if size_kb.zero?
|
|
47
29
|
return size_kb if C64::REU::SIZES_KB.include?(size_kb)
|
|
48
30
|
|
|
49
|
-
raise FormatError, "the
|
|
31
|
+
raise FormatError, "the snapshot names a #{size_kb}K REU, which badline doesn't build"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The setup in a keyed snapshot module (Snapshot::Keyed), with each
|
|
35
|
+
# model, the region, the RAM expansion, the KERNAL and the board by
|
|
36
|
+
# name.
|
|
37
|
+
def write_fields(out)
|
|
38
|
+
out.text("vic_model", vic_model.to_s).text("cia_model", cia_model.to_s).text("sid_model", sid_model.to_s)
|
|
39
|
+
out.text("region", region.name.to_s).text("ram_expansion", (ram_expansion || :none).to_s)
|
|
40
|
+
out.int("reu_kb", reu || 0).text("kernal", kernal.to_s).bool("datasette", datasette)
|
|
41
|
+
out.text("board", board.to_s)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The setup a keyed snapshot module names, a C64's at power-on for
|
|
45
|
+
# anything it leaves out or names that badline doesn't build.
|
|
46
|
+
def self.read_fields(input)
|
|
47
|
+
region = named(input, "region", REGIONS.map(&:name), :pal)
|
|
48
|
+
new(vic_model: named(input, "vic_model", Chips::VIC::MODELS, :mos6569),
|
|
49
|
+
cia_model: named(input, "cia_model", Chips::CIA::MODELS, :mos6526),
|
|
50
|
+
sid_model: named(input, "sid_model", SID_MODELS, :mos6581),
|
|
51
|
+
region: REGIONS.find { |each| each.name == region } || Region::PAL,
|
|
52
|
+
ram_expansion: ram_expansion(named(input, "ram_expansion", RAM_EXPANSIONS, :none)),
|
|
53
|
+
reu: reu(input.int("reu_kb", 0)), kernal: named(input, "kernal", KERNALS, :c64),
|
|
54
|
+
datasette: input.bool("datasette", true), board: named(input, "board", BOARDS, :c64))
|
|
50
55
|
end
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
out.int(KERNALS.index(kernal)).boolean(datasette).int(BOARDS.index(board))
|
|
57
|
+
# The Symbol of `names` the text at `key` names.
|
|
58
|
+
def self.named(input, key, names, default)
|
|
59
|
+
text = input.choice(key, names.map(&:to_s), default.to_s)
|
|
60
|
+
names.find { |name| name.to_s == text } || default
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
# A machine built this way, at power-on.
|
|
@@ -4,6 +4,10 @@ require "zlib"
|
|
|
4
4
|
|
|
5
5
|
module Badline
|
|
6
6
|
module Snapshot
|
|
7
|
+
# A file that isn't a snapshot badline reads, is cut short, or is of a
|
|
8
|
+
# machine built another way.
|
|
9
|
+
class FormatError < StandardError; end
|
|
10
|
+
|
|
7
11
|
# One module of a snapshot: its name, its version and its payload,
|
|
8
12
|
# without the 22-byte module header.
|
|
9
13
|
Section = Data.define(:name, :major, :minor, :data) do
|
|
@@ -30,6 +34,8 @@ module Badline
|
|
|
30
34
|
VIC20 = "VIC20"
|
|
31
35
|
# x128 names its machine C128.
|
|
32
36
|
C128 = "C128"
|
|
37
|
+
# xplus4 names its machine PLUS4, for the C16 too.
|
|
38
|
+
PLUS4 = "PLUS4"
|
|
33
39
|
GZIP_MAGIC = "\x1f\x8b".b
|
|
34
40
|
|
|
35
41
|
# The VICE version block, when VICE wrote the file: four version
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module FieldDoc
|
|
6
|
+
# The doc's markdown: one table per module, of each key, its type and
|
|
7
|
+
# its default, with keys that differ only in a unit's number, such as
|
|
8
|
+
# voice1 to voice3, on one row.
|
|
9
|
+
class Markdown
|
|
10
|
+
NAMES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4" }.freeze
|
|
11
|
+
TAG_TYPES = { Keyed::INT => "int", Keyed::BOOL => "bool", Keyed::BYTES => "bytes", Keyed::INTS => "ints",
|
|
12
|
+
Keyed::TEXT => "text" }.freeze
|
|
13
|
+
|
|
14
|
+
# A row: the key, the type, and the defaults each family whose
|
|
15
|
+
# machines read or write it reads it with.
|
|
16
|
+
Row = Struct.new(:key, :type, :defaults)
|
|
17
|
+
|
|
18
|
+
INTRO = <<~MARKDOWN
|
|
19
|
+
# Snapshot format
|
|
20
|
+
|
|
21
|
+
This file is generated by `rake snapshot:fields` from the code that
|
|
22
|
+
loads a snapshot. Don't edit it by hand. doc/snapshots.md covers
|
|
23
|
+
what a snapshot holds and which versions load.
|
|
24
|
+
|
|
25
|
+
A snapshot is a VICE `.vsf` container. The machine is in modules
|
|
26
|
+
named `BL-` plus a unit, each a deflated list of records: a key, a
|
|
27
|
+
type and a value. The types are `int` (a zigzag BER integer), `bool`,
|
|
28
|
+
`bytes`, `ints` (a list of integers) and `text`. A load skips a key
|
|
29
|
+
it doesn't know, and a key that isn't there leaves the value a
|
|
30
|
+
machine built afresh has: the default below. `word`, `byte` and
|
|
31
|
+
`int & 0x..` are ints cut to the latch's width. A text that lists
|
|
32
|
+
its values takes one of them. An optional int is absent when the
|
|
33
|
+
latch holds nothing. A true drive's modules are named from its
|
|
34
|
+
device number, `BL-D8` to `BL-D11`, and the SIDs past the first
|
|
35
|
+
are `BL-SID2` onwards, in the order they were fitted. Keys with
|
|
36
|
+
`<N>` stand for each numbered unit. A default of "written only" is
|
|
37
|
+
a key the load doesn't read, or reads only when the snapshot has
|
|
38
|
+
it. Where the machines differ, the default is given for each, and a
|
|
39
|
+
key only some of them have names them.
|
|
40
|
+
MARKDOWN
|
|
41
|
+
|
|
42
|
+
def initialize(loads)
|
|
43
|
+
@modules = {}
|
|
44
|
+
loads.each { |load| add(load) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
sections = @modules.map do |name, rows|
|
|
49
|
+
families = rows.values.flat_map { |row| row.defaults.keys }.uniq
|
|
50
|
+
["## #{name}", "", "Written by the #{families.map { |family| NAMES.fetch(family) }.join(', ')}.", "",
|
|
51
|
+
"| Key | Type | Default |", "| --- | --- | --- |",
|
|
52
|
+
*rows.values.map { |row| line(row, families) }].join("\n")
|
|
53
|
+
end
|
|
54
|
+
"#{INTRO}\n#{sections.join("\n\n")}\n"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def add(load)
|
|
60
|
+
load.fields.each do |name, fields|
|
|
61
|
+
numbered = numbered_keys(load.written[name].keys + fields.map(&:key))
|
|
62
|
+
fields.each { |field| note(name, numbered.fetch(field.key, field.key), field.type, load.family, field) }
|
|
63
|
+
read = fields.to_set(&:key)
|
|
64
|
+
load.written[name].each do |key, tag|
|
|
65
|
+
next if read.include?(key)
|
|
66
|
+
|
|
67
|
+
note(name, numbered.fetch(key, key), TAG_TYPES.fetch(tag, "?"), load.family, nil)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def note(name, key, type, family, field)
|
|
73
|
+
row = (@modules[name] ||= {})[key] ||= Row.new(key, type, {})
|
|
74
|
+
(row.defaults[family] ||= []) << (field ? brief(field.default, field.type) : "written only")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Keys that differ only in a number, as the same key with <N>: each
|
|
78
|
+
# key whose number another key in the module shares the rest of.
|
|
79
|
+
def numbered_keys(keys)
|
|
80
|
+
groups = keys.uniq.group_by { |key| key.gsub(/(?<=[a-z])\d+(?=\.|\z)/, "<N>") }
|
|
81
|
+
groups.each_with_object({}) do |(general, members), numbered|
|
|
82
|
+
members.each { |key| numbered[key] = general } if members.length > 1
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def line(row, families)
|
|
87
|
+
each = row.defaults.transform_values { |defaults| summary(defaults.uniq) }
|
|
88
|
+
default = if each.values.uniq.length == 1 && each.length == families.length then each.values.first
|
|
89
|
+
else each.map { |family, value| "#{NAMES.fetch(family)}: #{value}" }.join("; ")
|
|
90
|
+
end
|
|
91
|
+
"| `#{row.key}` | #{row.type} | #{default} |"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def summary(defaults) = defaults.length > 3 ? "varies" : defaults.join(" or ")
|
|
95
|
+
|
|
96
|
+
def brief(value, type)
|
|
97
|
+
case value
|
|
98
|
+
when nil then "absent"
|
|
99
|
+
when Array then list(value)
|
|
100
|
+
when String then type.start_with?("bytes") ? list(value.bytes) : value.inspect
|
|
101
|
+
else value.to_s
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def list(values)
|
|
106
|
+
return values.inspect if values.length <= 8
|
|
107
|
+
|
|
108
|
+
values.uniq.length == 1 ? "#{values.length} × #{values.first}" : "#{values.length} values"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline"
|
|
4
|
+
require "badline/c128"
|
|
5
|
+
require "badline/plus4"
|
|
6
|
+
|
|
7
|
+
module Badline
|
|
8
|
+
module Snapshot
|
|
9
|
+
# doc/snapshot-format.md, written by running the code: each family's
|
|
10
|
+
# machine is saved with its attachments, and its modules loaded into a
|
|
11
|
+
# machine built the same way at power-on through Keyed::Recorders,
|
|
12
|
+
# which note every key read with its type and the value the new machine
|
|
13
|
+
# had, its default. `rake snapshot:fields` writes it, and a spec checks
|
|
14
|
+
# that the file matches.
|
|
15
|
+
module FieldDoc
|
|
16
|
+
PATH = File.expand_path("../../../doc/snapshot-format.md", __dir__)
|
|
17
|
+
|
|
18
|
+
# Modules read through Recorders.
|
|
19
|
+
class RecordingModules < Keyed::Modules
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def parse(section) = Keyed::Recorder.parse(section)
|
|
23
|
+
|
|
24
|
+
def missing(name) = Keyed::Recorder.missing(name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# One machine saved and loaded: the family it belongs to, and for
|
|
28
|
+
# each module the keys its records hold, with their tags, and the fields the load read.
|
|
29
|
+
Load = Data.define(:family, :written, :fields)
|
|
30
|
+
|
|
31
|
+
# The cartridges by CRT hardware type: EasyFlash, then a mapper of
|
|
32
|
+
# each kind of register: Ocean's bank, Comal-80's bank for both
|
|
33
|
+
# windows, Zaxxon's ROMH bank, and the Action Replay's and the
|
|
34
|
+
# Pagefox's control registers.
|
|
35
|
+
CARTRIDGES = [32, 5, 21, 18, 1, 53].freeze
|
|
36
|
+
|
|
37
|
+
# The machines the doc is made from: each family at power-on, the C64
|
|
38
|
+
# with each kind of expansion, cartridge and media, and a second SID,
|
|
39
|
+
# and the 264 series' models, its TCBM interface, cartridge, tape and
|
|
40
|
+
# true drive.
|
|
41
|
+
def self.machines
|
|
42
|
+
{
|
|
43
|
+
"c64" => [C64.new, C64.new(ram_expansion: :plus256k), C64.new(reu: 512),
|
|
44
|
+
*CARTRIDGES.map { |type| with_cartridge(type) }, with_trap_dos, with_tape, with_drive(:d64),
|
|
45
|
+
with_drive(:d81), with_extra_sids],
|
|
46
|
+
"vic20" => [Vic20.new, with_rom],
|
|
47
|
+
"c128" => [C128.new(mode: :c128), with_drive(:d71)],
|
|
48
|
+
"plus4" => [Plus4.new, Plus4.new(model: "c16"), Plus4.new(tcbm: true), plus4_with_cartridge,
|
|
49
|
+
plus4_with_tape, plus4_with_drive]
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.blank(length) = Array.new(length, 0)
|
|
54
|
+
|
|
55
|
+
def self.with_cartridge(type)
|
|
56
|
+
chips = [0x8000, 0xa000].map do |address|
|
|
57
|
+
Storage::CRTFile::Chip.new(chip_type: 2, bank: 0, address:, data: blank(0x2000))
|
|
58
|
+
end
|
|
59
|
+
image = Storage::CRTFile::Image.new(hardware_type: type, subtype: 0, exrom: 1, game: 0, name: "DOC", chips:)
|
|
60
|
+
C64.new.tap do |machine|
|
|
61
|
+
machine.attach_cartridge(C64::Cartridge.from_crt(Storage::CRTFile.new(bytes: Storage::CRTFile.encode(image))))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The SID player's machine for a tune on two SIDs.
|
|
66
|
+
def self.with_extra_sids
|
|
67
|
+
C64.new.tap { |machine| machine.address_bus.add_sid(Chips::SID.new(at: 0xd420)) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.with_trap_dos
|
|
71
|
+
C64.new.tap { |machine| machine.mount(Storage::D64Image.new("", read_only: true, bytes: blank(174_848))) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.with_tape
|
|
75
|
+
bytes = "C64-TAPE-RAW".bytes + [1, 0, 0, 0, 4, 0, 0, 0, 0x30, 0x30, 0x30, 0x30]
|
|
76
|
+
C64.new.tap { |machine| machine.datasette.insert(Storage::TAP.new("", bytes:)) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.with_drive(kind)
|
|
80
|
+
case kind
|
|
81
|
+
when :d81
|
|
82
|
+
C64.new.tap do |machine|
|
|
83
|
+
machine.plug_drive1581.insert(Drives::Model1581::Disk.new(Storage::D81Image.new("", bytes: blank(819_200))))
|
|
84
|
+
end
|
|
85
|
+
when :d71
|
|
86
|
+
C128.new(mode: :c128).tap do |machine|
|
|
87
|
+
image = Storage::D71Image.new("", bytes: blank(349_696))
|
|
88
|
+
machine.plug_true_drive.insert(Drives::Model1541::Disk.decode(image))
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
C64.new.tap do |machine|
|
|
92
|
+
image = Storage::D64Image.new("", bytes: blank(174_848))
|
|
93
|
+
machine.plug_true_drive.insert(Drives::Model1541::Disk.decode(image))
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# A Plus/4 with a cartridge in each of cartridge 1's slots.
|
|
99
|
+
def self.plus4_with_cartridge
|
|
100
|
+
chips = [0x8000, 0xc000].map do |address|
|
|
101
|
+
Storage::CRTFile::Chip.new(chip_type: 0, bank: 0, address:, data: blank(0x4000))
|
|
102
|
+
end
|
|
103
|
+
Plus4.new.tap { |machine| machine.attach_cartridge(chips) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# A C16 with a version 2 tape in its datasette, which times the read
|
|
107
|
+
# line's level.
|
|
108
|
+
def self.plus4_with_tape
|
|
109
|
+
bytes = "C16-TAPE-RAW".bytes + [2, 2, 0, 0, 4, 0, 0, 0, 0x30, 0x30, 0x30, 0x30]
|
|
110
|
+
Plus4.new(model: "c16").tap { |machine| machine.datasette.insert(Storage::TAP.new("", bytes:)) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.plus4_with_drive
|
|
114
|
+
Plus4.new.tap do |machine|
|
|
115
|
+
image = Storage::D64Image.new("", bytes: blank(174_848))
|
|
116
|
+
machine.plug_true_drive.insert(Drives::Model1541::Disk.decode(image))
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.with_rom
|
|
121
|
+
Vic20.new.tap { |machine| machine.bus.map_rom(0xa000, blank(0x2000)) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Saves `machine` and loads it into a new one through Recorders.
|
|
125
|
+
def self.load(family, machine)
|
|
126
|
+
modules = RecordingModules.new(Image.of(machine).sections)
|
|
127
|
+
Image.setup(modules.machine).build.load_modules(modules)
|
|
128
|
+
modules.machine.text("written_by", "")
|
|
129
|
+
written = {}
|
|
130
|
+
fields = {}
|
|
131
|
+
modules.readers.each do |reader|
|
|
132
|
+
written[reader.name] = reader.tags
|
|
133
|
+
fields[reader.name] = reader.fields
|
|
134
|
+
end
|
|
135
|
+
Load.new(family:, written:, fields:)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def self.loads = machines.flat_map { |family, list| list.map { |machine| load(family, machine) } }
|
|
139
|
+
|
|
140
|
+
# The doc, as markdown.
|
|
141
|
+
def self.markdown(loads = self.loads) = Markdown.new(loads).to_s
|
|
142
|
+
|
|
143
|
+
def self.write(path = PATH) = File.write(path, markdown)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
require "badline/snapshot/field_doc/markdown"
|
|
@@ -2,37 +2,115 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Snapshot
|
|
5
|
-
# What a restore applied and what it left out: module names
|
|
6
|
-
# for each thing left out, saying why.
|
|
7
|
-
|
|
5
|
+
# What a restore applied and what it left out: module names and a line
|
|
6
|
+
# for each thing left out or changed, saying why. For a badline
|
|
7
|
+
# snapshot, `modules` holds each BL-* module's Keyed::Report, which
|
|
8
|
+
# give the fields per module this badline doesn't know, the modules it
|
|
9
|
+
# expected and didn't find, and the values it read other than as
|
|
10
|
+
# written, and `written_by` the badline version that saved it.
|
|
11
|
+
class Report
|
|
12
|
+
attr_reader :applied, :ignored, :written_by
|
|
13
|
+
|
|
14
|
+
def initialize(applied:, ignored:, modules: [], written_by: "")
|
|
15
|
+
@applied = applied
|
|
16
|
+
@ignored = ignored
|
|
17
|
+
@modules = modules
|
|
18
|
+
@written_by = written_by
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The keys nothing read, by module, for the modules that had any.
|
|
22
|
+
def unknown
|
|
23
|
+
@modules.reject { |report| report.unknown.empty? }.to_h { |report| [report.name, report.unknown] }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The modules the snapshot didn't have.
|
|
27
|
+
def missing = @modules.select(&:missing).map(&:name)
|
|
8
28
|
|
|
9
|
-
|
|
29
|
+
# A line for each value read other than as written.
|
|
30
|
+
def adjusted = @modules.flat_map { |report| report.adjusted.map { |line| "#{report.name}: #{line}" } }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# A snapshot: the container and its modules, read from a file or taken
|
|
34
|
+
# of a machine in memory (.of).
|
|
10
35
|
class Image
|
|
36
|
+
MACHINE = "BL-MACHINE"
|
|
37
|
+
FAMILIES = { "c64" => "C64", "vic20" => "VIC-20", "c128" => "C128", "plus4" => "Plus/4" }.freeze
|
|
38
|
+
|
|
11
39
|
attr_reader :container
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
# `name` is what a refusal calls the snapshot, such as its file's name.
|
|
42
|
+
def initialize(container, name: "the snapshot")
|
|
14
43
|
@container = container
|
|
44
|
+
@name = name
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The machine's snapshot, saved as a file saves it: the machine runs
|
|
48
|
+
# on to the end of the instruction its CPU is in, and of an REU
|
|
49
|
+
# transfer under way (Machine::Settling), then writes its BL-*
|
|
50
|
+
# modules.
|
|
51
|
+
def self.of(computer)
|
|
52
|
+
computer.settle_for_snapshot!
|
|
53
|
+
new(Container.new(computer.save_modules, machine: machine_name(computer)))
|
|
54
|
+
end
|
|
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 Plus/4, whose
|
|
58
|
+
# snapshots badline doesn't write for xplus4.
|
|
59
|
+
def self.exported(computer)
|
|
60
|
+
raise FormatError, "badline doesn't export a Plus/4's snapshot for VICE" if computer.family == :plus4
|
|
61
|
+
|
|
62
|
+
computer.settle_for_snapshot!
|
|
63
|
+
sections = computer.save_modules
|
|
64
|
+
sections = Vice.export(computer) + sections if computer.family == :c64
|
|
65
|
+
new(Container.new(sections, machine: machine_name(computer)))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The machine as VICE's emulators name it.
|
|
69
|
+
def self.machine_name(computer)
|
|
70
|
+
case computer.family
|
|
71
|
+
when :vic20 then Container::VIC20
|
|
72
|
+
when :c128 then Container::C128
|
|
73
|
+
when :plus4 then Container::PLUS4
|
|
74
|
+
else Container::MACHINE
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# How the machine was built, from the family BL-MACHINE, read by
|
|
79
|
+
# `machine`, names.
|
|
80
|
+
def self.setup(machine)
|
|
81
|
+
case machine.choice("family", FAMILIES.keys, "c64")
|
|
82
|
+
when "vic20" then Vic20Setup.read_fields(machine)
|
|
83
|
+
when "c128" then C128Setup.read_fields(machine)
|
|
84
|
+
when "plus4" then Plus4Setup.read_fields(machine)
|
|
85
|
+
else C64Setup.read_fields(machine)
|
|
86
|
+
end
|
|
15
87
|
end
|
|
16
88
|
|
|
17
89
|
def sections = @container.sections
|
|
18
90
|
|
|
19
|
-
|
|
20
|
-
def badline? = !section.nil?
|
|
91
|
+
def to_s = @container.to_s
|
|
21
92
|
|
|
22
|
-
|
|
23
|
-
|
|
93
|
+
def ==(other) = other.is_a?(Image) && to_s == other.to_s
|
|
94
|
+
|
|
95
|
+
# Whether badline wrote it, with the whole machine in BL-* modules.
|
|
96
|
+
def badline? = !@container[MACHINE].nil?
|
|
24
97
|
|
|
25
98
|
# A new machine, built as the snapshot's was and restored from it. A
|
|
26
|
-
# badline snapshot restores from its
|
|
27
|
-
#
|
|
28
|
-
# modules
|
|
29
|
-
#
|
|
30
|
-
|
|
99
|
+
# badline snapshot restores from its BL-* modules alone, and a VICE
|
|
100
|
+
# one through the modules badline reads. xvic's, x128's and xplus4's own
|
|
101
|
+
# modules aren't read. Yields a line for each thing left out, or
|
|
102
|
+
# warns it without a block. `detached` restores it without the host's
|
|
103
|
+
# files (Keyed::Modules).
|
|
104
|
+
def load(detached: false, &)
|
|
105
|
+
refuse_old_layout
|
|
31
106
|
raise FormatError, "badline reads only its own VIC-20 snapshots, not xvic's" if vic20? && !badline?
|
|
32
107
|
raise FormatError, "badline reads only its own C128 snapshots, not x128's" if c128? && !badline?
|
|
108
|
+
raise FormatError, "badline reads only its own Plus/4 snapshots, not xplus4's" if plus4? && !badline?
|
|
109
|
+
return load_vice(&) unless badline?
|
|
33
110
|
|
|
34
|
-
|
|
35
|
-
|
|
111
|
+
modules = Keyed::Modules.new(sections, detached:)
|
|
112
|
+
computer = opening { Image.setup(modules.machine).build.load_modules(modules) }
|
|
113
|
+
tell(report(modules, computer), &)
|
|
36
114
|
computer
|
|
37
115
|
end
|
|
38
116
|
|
|
@@ -42,25 +120,58 @@ module Badline
|
|
|
42
120
|
# Whether the machine is a C128, as the container names it.
|
|
43
121
|
def c128? = @container.machine == Container::C128
|
|
44
122
|
|
|
45
|
-
#
|
|
46
|
-
|
|
123
|
+
# Whether the machine is of the 264 series, as the container names it.
|
|
124
|
+
def plus4? = @container.machine == Container::PLUS4
|
|
125
|
+
|
|
126
|
+
# Restores `computer` from the snapshot, and returns the Report. The
|
|
127
|
+
# snapshot goes into a new machine first, so one that fails, or is of
|
|
128
|
+
# a machine built another way, leaves `computer` as it was.
|
|
47
129
|
def restore(computer, &)
|
|
48
|
-
|
|
130
|
+
refuse_old_layout
|
|
131
|
+
report = badline? ? restore_modules(computer) : Vice::Restore.import(@container, computer)
|
|
49
132
|
tell(report, &)
|
|
50
133
|
report
|
|
51
134
|
end
|
|
52
135
|
|
|
53
136
|
private
|
|
54
137
|
|
|
55
|
-
def
|
|
138
|
+
def load_vice(&)
|
|
139
|
+
computer = Vice.setup(@container).build
|
|
140
|
+
tell(Vice::Restore.apply(@container, computer), &)
|
|
141
|
+
computer
|
|
142
|
+
end
|
|
56
143
|
|
|
57
|
-
|
|
58
|
-
|
|
144
|
+
# The snapshot goes into a new machine first, so one that fails
|
|
145
|
+
# leaves `computer` as it was.
|
|
146
|
+
def restore_modules(computer)
|
|
147
|
+
modules = Keyed::Modules.new(sections)
|
|
148
|
+
family = modules.machine.choice("family", FAMILIES.keys, "c64")
|
|
149
|
+
unless family == computer.family.to_s
|
|
150
|
+
raise FormatError, "the snapshot is of a #{FAMILIES.fetch(family, family)}, " \
|
|
151
|
+
"not a #{FAMILIES.fetch(computer.family.to_s)}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
trial = Keyed::Modules.new(sections)
|
|
155
|
+
opening { Image.setup(trial.machine).build.load_modules(trial) }
|
|
156
|
+
opening { computer.load_modules(modules) }
|
|
157
|
+
report(modules, computer)
|
|
59
158
|
end
|
|
60
159
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
160
|
+
# A snapshot from a badline before the keyed modules, which only the
|
|
161
|
+
# version that saved it reads.
|
|
162
|
+
def refuse_old_layout
|
|
163
|
+
old = @container[MachineState::NAME]
|
|
164
|
+
return if old.nil? || badline?
|
|
165
|
+
|
|
166
|
+
version = MachineState.written_by(old)
|
|
167
|
+
layout = "in the old snapshot layout, which this badline can't read."
|
|
168
|
+
raise FormatError, "#{@name} was saved by an earlier badline #{layout}" if version.empty?
|
|
169
|
+
|
|
170
|
+
raise FormatError, "#{@name} was saved by badline #{version} #{layout} It still opens in badline #{version}."
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def tell(report)
|
|
174
|
+
report.ignored.each { |line| block_given? ? yield(line) : warn("badline: #{line}") }
|
|
64
175
|
end
|
|
65
176
|
|
|
66
177
|
def opening
|
|
@@ -69,10 +180,14 @@ module Badline
|
|
|
69
180
|
raise FormatError, "a file the snapshot names won't open: #{e.message}"
|
|
70
181
|
end
|
|
71
182
|
|
|
72
|
-
def
|
|
183
|
+
def report(modules, computer)
|
|
184
|
+
written_by = modules.machine.text("written_by", "")
|
|
185
|
+
reports = modules.readers.map(&:report)
|
|
186
|
+
ignored = reports.flat_map(&:lines)
|
|
187
|
+
ignored.concat(modules.unread.map { |name| "#{name}: badline doesn't read this module, left out" })
|
|
73
188
|
lost = computer.init_handlers_lost
|
|
74
|
-
ignored
|
|
75
|
-
Report.new(applied:
|
|
189
|
+
ignored << "#{lost} on_init handler(s) the saved machine had yet to run, left out" if lost.positive?
|
|
190
|
+
Report.new(applied: reports.reject(&:missing).map(&:name), ignored:, modules: reports, written_by:)
|
|
76
191
|
end
|
|
77
192
|
end
|
|
78
193
|
end
|