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,336 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module PET
|
|
7
|
+
# A true unit with an interface processor on device 8, as xpet 3.10,
|
|
8
|
+
# xcbm2 and xcbm5x0 save it with true drive emulation: an 8050, an
|
|
9
|
+
# 8250, an SFD-1001, a 4040, a 3040, a 2040, or a D9060 or D9090,
|
|
10
|
+
# which xpet's type 9000 stands for. Each is DRIVE8 2.0 with the
|
|
11
|
+
# unit's drives, DRIVE9 to DRIVE11 with none, DRIVECPU0 1.3, the
|
|
12
|
+
# interface processor and its RAM, RIOT1D0 and RIOT2D0 0.0, its two
|
|
13
|
+
# 6532s, and FDC0 0.0. xpet runs the floppy controller and the hard
|
|
14
|
+
# disk's SASI host at a high level, so their processors, VIAs and
|
|
15
|
+
# 6530s aren't in the snapshot: FDC0 holds the controller's state,
|
|
16
|
+
# its last track in byte 6.
|
|
17
|
+
#
|
|
18
|
+
# DRIVE8 holds a block of 139 bytes for each of the unit's drives,
|
|
19
|
+
# then 6 for the unit: each block has its head's half track, twice
|
|
20
|
+
# the track, at $10 and the unit's type as a word at $28 (TYPES).
|
|
21
|
+
# The 1001 and the hard disks have one block, and a hard disk's head
|
|
22
|
+
# rests at HARD_DISK_TRACK. DRIVECPU0 is laid out as the 2031's
|
|
23
|
+
# (Drive2031), its RAM the IP's page 0, the 6532s' 128 bytes each,
|
|
24
|
+
# then the 4K of common RAM. A RIOT module holds port A's output and
|
|
25
|
+
# direction registers, port B's, the edge control (bit 0 the PA7
|
|
26
|
+
# interrupt, bit 1 a rising edge), the flags (bit 7 the timer's
|
|
27
|
+
# interrupt, which xpet raises IRQ for, bit 6 PA7's), the timer's
|
|
28
|
+
# counter, its divider as a word, two bytes xpet leaves 0, and
|
|
29
|
+
# whether the timer interrupts.
|
|
30
|
+
#
|
|
31
|
+
# Written, once the controller, or the SASI host, is in its idle
|
|
32
|
+
# loop between instructions (PET#settle_for_vice!): the IP's
|
|
33
|
+
# registers and RAM, the 6532s, the heads' tracks, and FDC0 as xpet
|
|
34
|
+
# leaves an idle controller with the track badline's controller last
|
|
35
|
+
# stepped to. A unit whose controller is still busy is left out of
|
|
36
|
+
# xpet's modules. Read: the IP's registers and RAM and the 6532s,
|
|
37
|
+
# with the controller as a unit switched on has it in its idle loop,
|
|
38
|
+
# its heads on track 1, from where its next job steps them. A job
|
|
39
|
+
# under way in xpet's controller starts again from there. The disks
|
|
40
|
+
# in the drives stay, and a hard disk's image with them, which also
|
|
41
|
+
# says whether the unit is a D9060 or a D9090.
|
|
42
|
+
module Drive8050
|
|
43
|
+
UNIT = "DRIVE8"
|
|
44
|
+
CPU = Drive2031::CPU
|
|
45
|
+
RIOTS = %w[RIOT1D0 RIOT2D0].freeze
|
|
46
|
+
FDC = "FDC0"
|
|
47
|
+
NAMES = [UNIT, CPU, *RIOTS, FDC].freeze
|
|
48
|
+
# xpet's drive types by DRIVE8's type word, for the units written
|
|
49
|
+
# and read here.
|
|
50
|
+
TYPES = { 0x1f72 => "8050", 0x203a => "8250", 0x03e9 => "1001", 0x0fc8 => "4040", 0x0be0 => "3040",
|
|
51
|
+
0x07f8 => "2040", 0x2328 => "9060" }.freeze
|
|
52
|
+
# The type word each model is written with.
|
|
53
|
+
WORDS = TYPES.invert.merge("9090" => 0x2328).freeze
|
|
54
|
+
# The units that are hard disks, and the DOS 2.1 and DOS 1 units
|
|
55
|
+
# on the 4040's board.
|
|
56
|
+
HARD_DISKS = %w[9060 9090].freeze
|
|
57
|
+
DOS2_BOARD = %w[4040 3040 2040].freeze
|
|
58
|
+
# The drives each unit has, a block of DRIVE8 each.
|
|
59
|
+
DRIVE_COUNTS = { "8050" => 2, "8250" => 2, "1001" => 1, "4040" => 2, "3040" => 2, "2040" => 2, "9060" => 1,
|
|
60
|
+
"9090" => 1 }.freeze
|
|
61
|
+
# A drive's block in DRIVE8, and its fields.
|
|
62
|
+
BLOCK = 0x8b
|
|
63
|
+
HALF_TRACK = 0x10
|
|
64
|
+
TYPE = 0x28
|
|
65
|
+
# The half track xpet keeps a hard disk's head on.
|
|
66
|
+
HARD_DISK_TRACK = 0x98
|
|
67
|
+
# The unit's bytes after the drives' blocks.
|
|
68
|
+
TAIL = 6
|
|
69
|
+
# DRIVE8 as xpet saves an idle 8050, by the bytes that aren't 0,
|
|
70
|
+
# but for the type and the drive count at COUNT: the first drive's
|
|
71
|
+
# block, and from BLOCK on the second's with a byte past it.
|
|
72
|
+
IDLE = { 0x00 => 0x01, 0x02 => 0x01, 0x0e => 0x01, 0x0f => 0x01, 0x10 => 0x4c, 0x1a => 0x01, 0x20 => 0x55,
|
|
73
|
+
0x30 => 0x06, 0x6f => 0xcd, 0x70 => 0xab, 0x71 => 0x34, 0x72 => 0x12 }.freeze
|
|
74
|
+
IDLE_SECOND = { 0x0e => 0x01, 0x0f => 0x01, 0x10 => 0x24, 0x1a => 0x01, 0x20 => 0x55, 0x8f => 0x01 }.freeze
|
|
75
|
+
COUNT = 0x01
|
|
76
|
+
ZERO_PAGE = 0x80
|
|
77
|
+
COMMON = 0x1000
|
|
78
|
+
# FDC0 as xpet leaves an idle controller, and where the track goes.
|
|
79
|
+
FDC_IDLE = [0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00].freeze
|
|
80
|
+
FDC_TRACK = 6
|
|
81
|
+
# Where badline's controller keeps drive 0's track, in the 6530's
|
|
82
|
+
# RAM: the 8050's, and the 4040's in the low six bits.
|
|
83
|
+
TRACK_AT = 0x21
|
|
84
|
+
DOS2_TRACK_AT = 0x03
|
|
85
|
+
DOS2_TRACK = 0x3f
|
|
86
|
+
DIVIDERS = [1, 8, 64, 1024].freeze
|
|
87
|
+
IRQ_EDGE = 0x01
|
|
88
|
+
RISING = 0x02
|
|
89
|
+
TIMER_FLAG = 0x80
|
|
90
|
+
PA7_FLAG = 0x40
|
|
91
|
+
# The hard disk's controller, which holds the image's path.
|
|
92
|
+
SASI = "BL-D8-SASI"
|
|
93
|
+
|
|
94
|
+
module_function
|
|
95
|
+
|
|
96
|
+
# Whether the snapshot has a true unit of these.
|
|
97
|
+
def present?(container) = !container[FDC].nil? && !container[CPU].nil?
|
|
98
|
+
|
|
99
|
+
def reads?(section)
|
|
100
|
+
case section.name
|
|
101
|
+
when CPU then Drive2031.reads?(section)
|
|
102
|
+
when UNIT then section.major == Drive2031::DRIVE_MAJOR && section.minor == Drive2031::DRIVE_MINOR
|
|
103
|
+
else section.major.zero? && section.minor.zero?
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The model DRIVE8 names, or nil. Type 9000 reads as the D9060.
|
|
108
|
+
def model(container)
|
|
109
|
+
unit = container[UNIT]
|
|
110
|
+
return nil if unit.nil? || unit.data.bytesize < BLOCK
|
|
111
|
+
|
|
112
|
+
model = TYPES[FieldReader.new(unit).skip(TYPE).word]
|
|
113
|
+
model if model && unit.data.bytesize >= size(model)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Whether xpet saves the two models as one type.
|
|
117
|
+
def same_type?(model, other) = WORDS.key?(model) && WORDS[model] == WORDS[other]
|
|
118
|
+
|
|
119
|
+
def hard_disk?(model) = HARD_DISKS.include?(model)
|
|
120
|
+
|
|
121
|
+
# The hard disk whose image the kept modules `disks` hold: a
|
|
122
|
+
# D9090 for one with six heads, and otherwise a D9060.
|
|
123
|
+
def hard_disk_model(disks)
|
|
124
|
+
own = disks.find { |section| section.name == SASI }
|
|
125
|
+
own && Keyed::Reader.parse(own).int("disk_heads", 4) == 6 ? "9090" : "9060"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# DRIVE8's size for the model.
|
|
129
|
+
def size(model) = (BLOCK * DRIVE_COUNTS.fetch(model)) + TAIL
|
|
130
|
+
|
|
131
|
+
# xpet's modules for the unit whose BL-D8 modules `readers` holds,
|
|
132
|
+
# and none without one, or with its controller away from its idle
|
|
133
|
+
# loop.
|
|
134
|
+
def export(machine, readers)
|
|
135
|
+
drive = readers["BL-D8"]
|
|
136
|
+
model = drive&.text("model", "")
|
|
137
|
+
return [] unless WORDS.key?(model) && idle?(model, readers)
|
|
138
|
+
|
|
139
|
+
[unit(model, readers), *Drive2031::UNITS.drop(1).map { |name| none(name) },
|
|
140
|
+
Drive2031.cpu_section(machine, readers.fetch("BL-D8-IPCPU"), ram(readers)),
|
|
141
|
+
riot(RIOTS[0], readers.fetch("BL-D8-RIOT1")), riot(RIOTS[1], readers.fetch("BL-D8-RIOT2")),
|
|
142
|
+
fdc(model, readers)]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Whether both processors are between instructions, the
|
|
146
|
+
# controller at the start of its idle loop.
|
|
147
|
+
def idle?(model, readers)
|
|
148
|
+
controller = readers.fetch(hard_disk?(model) ? "BL-D8-SASICPU" : "BL-D8-FDCCPU")
|
|
149
|
+
!position?(controller) && !position?(readers.fetch("BL-D8-IPCPU")) &&
|
|
150
|
+
controller.int("pc", 0) == controller_loop(model)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Where the model's controller, or its SASI host, waits for jobs.
|
|
154
|
+
def controller_loop(model)
|
|
155
|
+
case model
|
|
156
|
+
when "9060", "9090" then Drives::Model9060::Idle::SASI_LOOP
|
|
157
|
+
when "2040" then Drives::Model2040::FDC_LOOP
|
|
158
|
+
when "4040", "3040" then Drives::Model4040::FDC_LOOP
|
|
159
|
+
else Drives::Model8050::Idle::FDC_LOOP
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def position?(cpu) = !cpu.text("pos.seq", "").empty?
|
|
164
|
+
|
|
165
|
+
def unit(model, readers)
|
|
166
|
+
count = DRIVE_COUNTS.fetch(model)
|
|
167
|
+
bytes = Array.new(size(model), 0)
|
|
168
|
+
IDLE.each { |offset, value| bytes[offset] = value }
|
|
169
|
+
IDLE_SECOND.each { |offset, value| bytes[BLOCK + offset] = value } if count > 1
|
|
170
|
+
bytes[COUNT] = count
|
|
171
|
+
tracks = half_tracks(model, readers)
|
|
172
|
+
count.times do |drive|
|
|
173
|
+
at = BLOCK * drive
|
|
174
|
+
bytes[at + HALF_TRACK] = tracks[drive]
|
|
175
|
+
bytes[at + TYPE, 2] = [WORDS.fetch(model)].pack("v").bytes
|
|
176
|
+
end
|
|
177
|
+
FieldWriter.new.bytes(bytes).section(UNIT, Drive2031::DRIVE_MAJOR, Drive2031::DRIVE_MINOR)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Each drive's head as xpet's half track: the 8050's cylinder on
|
|
181
|
+
# from 1, the 4040's half track on from track 1, and a hard
|
|
182
|
+
# disk's where xpet keeps it.
|
|
183
|
+
def half_tracks(model, readers)
|
|
184
|
+
return [HARD_DISK_TRACK] if hard_disk?(model)
|
|
185
|
+
|
|
186
|
+
mechanism = readers.fetch("BL-D8-MECH")
|
|
187
|
+
if DOS2_BOARD.include?(model)
|
|
188
|
+
mechanism.ints("half_tracks", [0, 0]).map { |half_track| half_track + 2 }
|
|
189
|
+
else
|
|
190
|
+
mechanism.ints("cylinders", [0, 0]).map { |cylinder| (cylinder + 1) * 2 }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def none(name) = Drive2031.unit(Drive2031::NONE, name)
|
|
195
|
+
|
|
196
|
+
# The IP's RAM as xpet lays it out: page 0 from both 6532s, then
|
|
197
|
+
# the common RAM.
|
|
198
|
+
def ram(readers)
|
|
199
|
+
[readers.fetch("BL-D8-RIOT1"), readers.fetch("BL-D8-RIOT2")].flat_map do |riot|
|
|
200
|
+
filled(riot.bytes("ram", "".b).bytes, ZERO_PAGE)
|
|
201
|
+
end + filled(readers.fetch("BL-D8-COMMON").bytes("ram", "".b).bytes, COMMON)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def filled(bytes, length) = Array.new(length) { |index| bytes[index] || 0 }
|
|
205
|
+
|
|
206
|
+
def riot(name, input)
|
|
207
|
+
edge = input.bool("edge_irq_enabled", false) ? IRQ_EDGE : 0
|
|
208
|
+
edge |= RISING if input.bool("rising_edge", false)
|
|
209
|
+
timer = input.bool("timer.flag", false) && input.bool("timer.irq_enabled", false)
|
|
210
|
+
flags = (timer ? TIMER_FLAG : 0) | (input.bool("pa7_flag", false) ? PA7_FLAG : 0)
|
|
211
|
+
divider = DIVIDERS.fetch(input.int("timer.interval", 0) & 0x03)
|
|
212
|
+
fields = FieldWriter.new.byte(input.int("ora", 0)).byte(input.int("ddra", 0)).byte(input.int("orb", 0))
|
|
213
|
+
fields.byte(input.int("ddrb", 0)).byte(edge).byte(flags).byte(input.int("timer.counter", 0)).word(divider)
|
|
214
|
+
fields.zeros(2).flag(input.bool("timer.irq_enabled", false)).section(name, 0, 0)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# FDC0 with the controller's track, and a hard disk's with none.
|
|
218
|
+
def fdc(model, readers)
|
|
219
|
+
bytes = FDC_IDLE.dup
|
|
220
|
+
unless hard_disk?(model)
|
|
221
|
+
ram = readers.fetch("BL-D8-RRIOT").bytes("ram", "".b)
|
|
222
|
+
bytes[FDC_TRACK] = if DOS2_BOARD.include?(model)
|
|
223
|
+
ram.getbyte(DOS2_TRACK_AT).to_i & DOS2_TRACK
|
|
224
|
+
else
|
|
225
|
+
ram.getbyte(TRACK_AT).to_i
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
FieldWriter.new.bytes(bytes).section(FDC, 0, 0)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# The unit's keyed modules, named for device 8, from the
|
|
232
|
+
# snapshot's: the IP's side from xpet's, the controller's side
|
|
233
|
+
# from `reference`, a unit's in its idle loop, and `disks`, the
|
|
234
|
+
# drives' disk modules badline keeps, or a hard disk's controller
|
|
235
|
+
# with its image.
|
|
236
|
+
def import(container, reference, disks)
|
|
237
|
+
cpu = container[CPU]
|
|
238
|
+
page = FieldReader.new(cpu).skip(Drive2031::RAM_AT).string(ZERO_PAGE * 2)
|
|
239
|
+
common = FieldReader.new(cpu).skip(Drive2031::RAM_AT + (ZERO_PAGE * 2)).string(COMMON)
|
|
240
|
+
sections = [Keyed.section("BL-D8") { |out| unit_fields(container, reference, out) },
|
|
241
|
+
Keyed.section("BL-D8-COMMON") { |out| out.bytes("ram", common) },
|
|
242
|
+
riot_section("BL-D8-RIOT1", container[RIOTS[0]], page.byteslice(0, ZERO_PAGE)),
|
|
243
|
+
riot_section("BL-D8-RIOT2", container[RIOTS[1]], page.byteslice(ZERO_PAGE, ZERO_PAGE)),
|
|
244
|
+
Keyed.section("BL-D8-IPCPU") { |out| Drive2031.registers(cpu, out) }]
|
|
245
|
+
sections + controller_side(reference, disks)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# The controller's side, which comes from a unit in its idle loop:
|
|
249
|
+
# a floppy unit's, and a hard disk's SASI host.
|
|
250
|
+
CONTROLLER = %w[BL-D8-FDCCPU BL-D8-VIA BL-D8-RRIOT BL-D8-MECH].freeze
|
|
251
|
+
HOST = %w[BL-D8-SASICPU BL-D8-SASIRAM BL-D8-VIA].freeze
|
|
252
|
+
|
|
253
|
+
def controller_side(reference, disks)
|
|
254
|
+
base = reference.find { |section| section.name == SASI }
|
|
255
|
+
return reference.select { |section| CONTROLLER.include?(section.name) } + disks if base.nil?
|
|
256
|
+
|
|
257
|
+
reference.select { |section| HOST.include?(section.name) } + [controller(base, disks)]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# The hard disk's controller as `base` has it, with the image
|
|
261
|
+
# badline's kept in `disks`.
|
|
262
|
+
def controller(base, disks)
|
|
263
|
+
kept = disks.find { |section| section.name == SASI }
|
|
264
|
+
return base if kept.nil?
|
|
265
|
+
|
|
266
|
+
target = Drives::Model9060::SASITarget.new
|
|
267
|
+
target.load_fields(Keyed::Reader.parse(base))
|
|
268
|
+
own = Keyed::Reader.parse(kept)
|
|
269
|
+
path = own.text("path", "")
|
|
270
|
+
if File.file?(path)
|
|
271
|
+
target.disk = Storage::D90Image.new(path, read_only: own.bool("read_only", false),
|
|
272
|
+
heads: own.int("disk_heads", 4))
|
|
273
|
+
end
|
|
274
|
+
Keyed.section(SASI) { |out| target.save_fields(out) }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# The unit's own fields: its model and clock, with the controller's
|
|
278
|
+
# last byte from `reference`, or a hard disk's SASI lines.
|
|
279
|
+
def unit_fields(container, reference, out)
|
|
280
|
+
own = Keyed::Reader.parse(reference.find { |section| section.name == "BL-D8" })
|
|
281
|
+
model = own.text("model", "8050")
|
|
282
|
+
out.text("model", model).int("phase", 0).int("cycles", FieldReader.new(container[CPU]).qword)
|
|
283
|
+
return out.int("fdc_data", own.int("fdc_data", 0)) unless hard_disk?(model)
|
|
284
|
+
|
|
285
|
+
out.int("sasi_data", own.int("sasi_data", 0)).bool("sasi.ack_high", own.bool("sasi.ack_high", true))
|
|
286
|
+
out.bool("sasi.reset_high", own.bool("sasi.reset_high", false))
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def riot_section(name, section, ram)
|
|
290
|
+
fields = FieldReader.new(section)
|
|
291
|
+
ora, ddra, orb, ddrb, edge, flags, counter = fields.bytes(7)
|
|
292
|
+
divider = fields.word
|
|
293
|
+
fields.skip(2)
|
|
294
|
+
irq = fields.flag?
|
|
295
|
+
Keyed.section(name) do |out|
|
|
296
|
+
out.bytes("ram", ram).int("ora", ora).int("ddra", ddra).int("orb", orb).int("ddrb", ddrb)
|
|
297
|
+
out.bool("edge_irq_enabled", edge.anybits?(IRQ_EDGE)).bool("rising_edge", edge.anybits?(RISING))
|
|
298
|
+
out.bool("pa7_flag", flags.anybits?(PA7_FLAG)).int("timer.counter", counter)
|
|
299
|
+
out.int("timer.interval", DIVIDERS.index(divider) || 0).int("timer.prescale", [divider, 1].max)
|
|
300
|
+
out.bool("timer.flag", flags.anybits?(TIMER_FLAG)).bool("timer.expired", flags.anybits?(TIMER_FLAG))
|
|
301
|
+
out.bool("timer.irq_enabled", irq)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# A unit of `model` switched on and run into its controller's idle
|
|
306
|
+
# loop, both processors between instructions, as keyed modules
|
|
307
|
+
# named for device 8, run once a model.
|
|
308
|
+
def reference(model)
|
|
309
|
+
@references ||= {}
|
|
310
|
+
@references[model] ||= booted(model)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def booted(model)
|
|
314
|
+
unit = new_unit(model)
|
|
315
|
+
unit.boot_cycles.times { unit.host_cycle! }
|
|
316
|
+
unit.cycle! until unit.settled_for_vice?
|
|
317
|
+
unit.save_modules([], "BL-D8")
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def new_unit(model)
|
|
321
|
+
case model
|
|
322
|
+
when "8250" then Drives::Model8250.new
|
|
323
|
+
when "1001" then Drives::Model1001.new
|
|
324
|
+
when "4040" then Drives::Model4040.new
|
|
325
|
+
when "3040" then Drives::Model3040.new
|
|
326
|
+
when "2040" then Drives::Model2040.new
|
|
327
|
+
when "9060" then Drives::Model9060.new
|
|
328
|
+
when "9090" then Drives::Model9090.new
|
|
329
|
+
else Drives::Model8050.new
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
@@ -5,14 +5,24 @@ module Badline
|
|
|
5
5
|
module Vice
|
|
6
6
|
module PET
|
|
7
7
|
# PETMEM 1.3: the board, the RAM below $8000 in K and the 8096's
|
|
8
|
-
# control register at $FFF0,
|
|
9
|
-
# as the board has, or on the 8296
|
|
10
|
-
#
|
|
11
|
-
# the 2001
|
|
8
|
+
# control register at $FFF0, a byte for the SuperPET's latches, then
|
|
9
|
+
# the RAM, the screen RAM, 1K or 2K as the board has, or on the 8296
|
|
10
|
+
# its 32K from $8000, an 8096's, 8296's or SuperPET's 64K board, and
|
|
11
|
+
# 24 bytes of xpet's own settings, which name the 2001, and on the
|
|
12
|
+
# SuperPET the CPU that has the bus.
|
|
12
13
|
#
|
|
13
14
|
# The board is 0 for the discrete video, 1 for a 40-column CRTC, 2
|
|
14
15
|
# for an 80-column one, 3 for the SuperPET, 4 for the 8096 and 5 for
|
|
15
16
|
# the 8296. xpet's ROMs aren't in the snapshot.
|
|
17
|
+
#
|
|
18
|
+
# The SuperPET's latch byte holds the bank at $9000 in its high
|
|
19
|
+
# nibble, and sets bit 0 while the bank RAM is there rather than the
|
|
20
|
+
# empty socket, and bit 2 always. Its settings hold 1 at offset 2
|
|
21
|
+
# while the 6809 has the bus. Both were inferred from xpet 3.10's
|
|
22
|
+
# snapshots after writes to the bank latch and $EFFE, and in both
|
|
23
|
+
# modes. xpet has no system latch, so the bank latch's bit 7 and the
|
|
24
|
+
# system latch aren't saved, and a restore clears them. Nor is the
|
|
25
|
+
# 6702: a restore leaves it as the RES line does.
|
|
16
26
|
module PETMem
|
|
17
27
|
NAME = "PETMEM"
|
|
18
28
|
MAJOR = 1
|
|
@@ -30,6 +40,14 @@ module Badline
|
|
|
30
40
|
SETTINGS = [0x00, 0x00, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00,
|
|
31
41
|
0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00].freeze
|
|
32
42
|
SETTINGS_2001 = 0x03
|
|
43
|
+
# The SuperPET's settings byte for the CPU, and its value while the
|
|
44
|
+
# 6809 has the bus.
|
|
45
|
+
SETTINGS_CPU = 2
|
|
46
|
+
SETTINGS_M6809 = 0x01
|
|
47
|
+
# The SuperPET's latch byte: the bit always set, and the one for
|
|
48
|
+
# the bank RAM at $9000.
|
|
49
|
+
LATCHES = 0x04
|
|
50
|
+
BANK_RAM = 0x01
|
|
33
51
|
EXPANSION = 0x10000
|
|
34
52
|
|
|
35
53
|
module_function
|
|
@@ -37,6 +55,7 @@ module Badline
|
|
|
37
55
|
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
38
56
|
|
|
39
57
|
def board(model)
|
|
58
|
+
return SUPERPET if model.superpet?
|
|
40
59
|
return CBM8296 if model.cbm8296?
|
|
41
60
|
return CBM8096 if model.expansion
|
|
42
61
|
|
|
@@ -50,15 +69,40 @@ module Badline
|
|
|
50
69
|
def export(machine)
|
|
51
70
|
bus = machine.bus
|
|
52
71
|
expansion = bus.expansion
|
|
72
|
+
superpet = machine.superpet?
|
|
53
73
|
fields = FieldWriter.new.byte(board(machine.model)).byte(0).byte(machine.ram_kb)
|
|
54
|
-
fields.byte(expansion ? expansion.control : 0).byte(HEADER_END)
|
|
74
|
+
fields.byte(expansion ? expansion.control : 0).byte(superpet ? latches(machine.board) : HEADER_END)
|
|
55
75
|
fields.bytes(machine.ram.snapshot).bytes(bus.screen.cells)
|
|
56
76
|
fields.bytes(expansion.cells) if expansion
|
|
77
|
+
fields.bytes(machine.board.cells) if superpet
|
|
57
78
|
settings = SETTINGS.dup
|
|
58
79
|
settings[1] = SETTINGS_2001 if machine.model.basic1?
|
|
80
|
+
settings[SETTINGS_CPU] = SETTINGS_M6809 if superpet && machine.m6809_running?
|
|
59
81
|
fields.bytes(settings).section(NAME, MAJOR, MINOR)
|
|
60
82
|
end
|
|
61
83
|
|
|
84
|
+
def latches(board) = (board.bank << 4) | LATCHES | (board.socket.zero? ? BANK_RAM : 0)
|
|
85
|
+
|
|
86
|
+
# Whether a SuperPET's settings say the 6809 has the bus.
|
|
87
|
+
def m6809?(section)
|
|
88
|
+
data = section.data
|
|
89
|
+
offset = data.bytesize - SETTINGS.length + SETTINGS_CPU
|
|
90
|
+
offset >= 0 && data.getbyte(offset).to_i.anybits?(SETTINGS_M6809)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# A SuperPET's bank latch, socket bit and 64K board by their
|
|
94
|
+
# BL-SUPERPET keys, in a keyed module's writer.
|
|
95
|
+
def import_superpet(section, out)
|
|
96
|
+
fields = FieldReader.new(section).skip(4)
|
|
97
|
+
latches = fields.byte
|
|
98
|
+
fields.skip(fields_before_board(section))
|
|
99
|
+
out.int("bank_latch", latches >> 4).int("socket", latches.anybits?(BANK_RAM) ? 0 : 1)
|
|
100
|
+
out.int("system_latch", 0).bytes("ram", fields.string(EXPANSION))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The RAM and the screen RAM before a SuperPET's board.
|
|
104
|
+
def fields_before_board(section) = (ram_kb(section) * 1024) + 0x800
|
|
105
|
+
|
|
62
106
|
# The board's number in the header.
|
|
63
107
|
def board_of(section) = FieldReader.new(section).byte
|
|
64
108
|
|
|
@@ -8,14 +8,23 @@ module Badline
|
|
|
8
8
|
# reads become the PET's own keyed modules (Keyed), which it loads
|
|
9
9
|
# as it loads its own snapshot. Whatever they don't set stays as the
|
|
10
10
|
# machine has it at power-on, and the datasettes' tapes and the
|
|
11
|
-
# disk device 8 serves stay as they are.
|
|
11
|
+
# disk device 8 serves stay as they are. A snapshot with a true drive
|
|
12
|
+
# puts one on the bus, with the disks badline's drive of that model
|
|
13
|
+
# had in it (Drive2031, Drive8050), and one without takes badline's
|
|
14
|
+
# off.
|
|
12
15
|
module Restore
|
|
13
16
|
# MAINCPU and PETMEM hold the machine itself, so a version badline
|
|
14
17
|
# doesn't read fails the restore. The others are left out and
|
|
15
18
|
# reported.
|
|
16
19
|
READ = [MainCPU::NAME, PETMem::NAME, CRTC::NAME, "PIA1", "PIA2", VIA::NAME].freeze
|
|
20
|
+
# The SuperPET's too: its 6809 and its ACIA. A SuperPET's board
|
|
21
|
+
# comes from PETMEM.
|
|
22
|
+
SUPERPET_READ = [CPU6809::NAME, CBM2::ACIA::NAME].freeze
|
|
17
23
|
# The host's media, which a restore keeps.
|
|
18
24
|
KEPT = %w[BL-TAPE1 BL-TAPE2 BL-TRAPDOS].freeze
|
|
25
|
+
# The disks in a true drive's drives, and a hard disk's controller
|
|
26
|
+
# with its image, which a restore keeps.
|
|
27
|
+
DISKS = %w[BL-D8-DISK BL-D8-DISK1 BL-D8-SASI].freeze
|
|
19
28
|
|
|
20
29
|
module_function
|
|
21
30
|
|
|
@@ -33,9 +42,13 @@ module Badline
|
|
|
33
42
|
raise FormatError, "the snapshot is of a #{setup}, not a #{ours}" unless setup == ours
|
|
34
43
|
|
|
35
44
|
apply(container, setup.build)
|
|
45
|
+
had_drive = computer.true_drive
|
|
36
46
|
computer.power_cycle!
|
|
37
47
|
report = apply(container, computer)
|
|
38
48
|
kept = computer.mounted? ? ["the disk mounted in device 8 stays mounted"] : []
|
|
49
|
+
if had_drive && !computer.true_drive.equal?(had_drive)
|
|
50
|
+
kept << "the #{had_drive.model_name} on device #{had_drive.device} is taken off the bus"
|
|
51
|
+
end
|
|
39
52
|
Report.new(applied: report.applied, ignored: report.ignored + kept)
|
|
40
53
|
end
|
|
41
54
|
|
|
@@ -45,7 +58,8 @@ module Badline
|
|
|
45
58
|
applied = []
|
|
46
59
|
ignored = []
|
|
47
60
|
sections = []
|
|
48
|
-
|
|
61
|
+
names = read_names(computer)
|
|
62
|
+
names.each do |name|
|
|
49
63
|
section = container[name]
|
|
50
64
|
next if section.nil?
|
|
51
65
|
|
|
@@ -57,9 +71,69 @@ module Badline
|
|
|
57
71
|
ignored << "#{section}: badline doesn't read this version, left out"
|
|
58
72
|
end
|
|
59
73
|
end
|
|
60
|
-
|
|
74
|
+
disks = []
|
|
75
|
+
computer.save_modules.each do |section|
|
|
76
|
+
sections << section if KEPT.include?(section.name)
|
|
77
|
+
disks << section if DISKS.include?(section.name)
|
|
78
|
+
end
|
|
79
|
+
sections += drive(container, applied, ignored, kept_disks(container, computer, disks))
|
|
61
80
|
computer.load_modules(Keyed::Modules.new(modules(container, computer, applied) + sections))
|
|
62
|
-
Report.new(applied:, ignored: ignored + left_out(container))
|
|
81
|
+
Report.new(applied:, ignored: ignored + left_out(container, names, applied))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def read_names(computer) = computer.superpet? ? READ + SUPERPET_READ : READ
|
|
85
|
+
|
|
86
|
+
# The disks badline's true drive has, which stay in the drive the
|
|
87
|
+
# snapshot puts on the bus when it is of the same model, or of the
|
|
88
|
+
# same xpet type.
|
|
89
|
+
def kept_disks(container, computer, disks)
|
|
90
|
+
drive = computer.true_drive
|
|
91
|
+
return [] if drive.nil?
|
|
92
|
+
|
|
93
|
+
if Drive8050.present?(container)
|
|
94
|
+
Drive8050.same_type?(Drive8050.model(container), drive.model_name) ? disks : []
|
|
95
|
+
else
|
|
96
|
+
drive.model_name == "2031" ? disks.first(1) : []
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The true drive's modules where the snapshot has one badline
|
|
101
|
+
# reads, or none.
|
|
102
|
+
def drive(container, applied, ignored, disks)
|
|
103
|
+
return drive8050(container, applied, ignored, disks) if Drive8050.present?(container)
|
|
104
|
+
return [] unless Drive2031.present?(container)
|
|
105
|
+
|
|
106
|
+
names = [Drive2031::UNITS.first, Drive2031::CPU, Drive2031::VIA1, Drive2031::VIA2]
|
|
107
|
+
return [] unless readable?(container, names, "2031", ignored) { |section| Drive2031.reads?(section) }
|
|
108
|
+
|
|
109
|
+
applied.concat(names)
|
|
110
|
+
Drive2031.import(container, disks.first)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def drive8050(container, applied, ignored, disks)
|
|
114
|
+
model = Drive8050.model(container)
|
|
115
|
+
return [] unless readable?(container, Drive8050::NAMES, model || "8050", ignored) do |section|
|
|
116
|
+
Drive8050.reads?(section)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if model.nil?
|
|
120
|
+
ignored << "the true drive: badline doesn't read its #{Drive8050::UNIT}'s type, left out"
|
|
121
|
+
return []
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
applied.concat(Drive8050::NAMES)
|
|
125
|
+
model = Drive8050.hard_disk_model(disks) if Drive8050.hard_disk?(model)
|
|
126
|
+
Drive8050.import(container, Drive8050.reference(model), disks)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Whether the snapshot has every module `names` lists at a version
|
|
130
|
+
# badline reads, saying which it doesn't in `ignored`.
|
|
131
|
+
def readable?(container, names, model, ignored, &reads)
|
|
132
|
+
unread = names.map { |name| container[name] }.reject { |section| section && reads.call(section) }
|
|
133
|
+
return true if unread.empty?
|
|
134
|
+
|
|
135
|
+
ignored << "the true #{model}: badline doesn't read #{unread.compact.join(', ')}, left out"
|
|
136
|
+
false
|
|
63
137
|
end
|
|
64
138
|
|
|
65
139
|
def required?(name) = [MainCPU::NAME, PETMem::NAME].include?(name)
|
|
@@ -70,6 +144,8 @@ module Badline
|
|
|
70
144
|
when PETMem::NAME then PETMem.reads?(section)
|
|
71
145
|
when CRTC::NAME then CRTC.reads?(section)
|
|
72
146
|
when VIA::NAME then VIA.reads?(section)
|
|
147
|
+
when CPU6809::NAME then CPU6809.reads?(section)
|
|
148
|
+
when CBM2::ACIA::NAME then CBM2::ACIA.reads?(section)
|
|
73
149
|
else PIAs.reads?(section)
|
|
74
150
|
end
|
|
75
151
|
end
|
|
@@ -96,9 +172,26 @@ module Badline
|
|
|
96
172
|
if applied.include?(VIA::NAME)
|
|
97
173
|
sections << Keyed.section("BL-VIA") { |out| VIA.import(container[VIA::NAME], out) }
|
|
98
174
|
end
|
|
175
|
+
sections += superpet(container, applied) if computer.superpet?
|
|
99
176
|
sections + video
|
|
100
177
|
end
|
|
101
178
|
|
|
179
|
+
# The SuperPET's board, and its 6809 while it has the bus and its
|
|
180
|
+
# ACIA where the snapshot has them. The 6809 the 6502 has the bus
|
|
181
|
+
# from, and a module left out, stay as the machine has them at
|
|
182
|
+
# power-on.
|
|
183
|
+
def superpet(container, applied)
|
|
184
|
+
cycles = FieldReader.new(container[MainCPU::NAME]).qword
|
|
185
|
+
memory = container[PETMem::NAME]
|
|
186
|
+
sections = [Keyed.section("BL-SUPERPET") { |out| PETMem.import_superpet(memory, out) }]
|
|
187
|
+
if applied.include?(CPU6809::NAME) && PETMem.m6809?(memory)
|
|
188
|
+
sections << Keyed.section("BL-M6809") { |out| CPU6809.import(container[CPU6809::NAME], out, cycles) }
|
|
189
|
+
end
|
|
190
|
+
return sections unless applied.include?(CBM2::ACIA::NAME)
|
|
191
|
+
|
|
192
|
+
sections << Keyed.section("BL-ACIA") { |out| CBM2::ACIA.import(container[CBM2::ACIA::NAME], out, cycles) }
|
|
193
|
+
end
|
|
194
|
+
|
|
102
195
|
def machine(out, container, computer, drive)
|
|
103
196
|
out.text("family", "pet")
|
|
104
197
|
PETSetup.of(computer).write_fields(out)
|
|
@@ -135,12 +228,12 @@ module Badline
|
|
|
135
228
|
CRTC.import(crtc, out, CRTC.columns(model), CRTC.attributes(model))
|
|
136
229
|
end
|
|
137
230
|
cycles = (CRTC::CRTC_LEAD + CRTC.column_of(crtc)) % CRTC.frame_cycles(CRTC.registers(crtc))
|
|
138
|
-
fields = CRTC.run_on(computer, Keyed::Reader.parse(counters), cycles)
|
|
231
|
+
fields = CRTC.run_on(CRTC.fresh_crtc(computer), Keyed::Reader.parse(counters), cycles)
|
|
139
232
|
[Keyed.section("BL-CRTC") { |out| CRTC.copy(fields, out) }]
|
|
140
233
|
end
|
|
141
234
|
|
|
142
|
-
def left_out(container)
|
|
143
|
-
container.sections.reject { |section|
|
|
235
|
+
def left_out(container, names, applied)
|
|
236
|
+
container.sections.reject { |section| names.include?(section.name) || applied.include?(section.name) }
|
|
144
237
|
.map { |section| "#{section}: badline doesn't read this module, left out" }
|
|
145
238
|
end
|
|
146
239
|
end
|
|
@@ -44,8 +44,8 @@ module Badline
|
|
|
44
44
|
|
|
45
45
|
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
46
46
|
|
|
47
|
-
# The VIA's module, from its BL-VIA fields in `input`.
|
|
48
|
-
def export(input)
|
|
47
|
+
# The VIA's module, under `name`, from its BL-VIA fields in `input`.
|
|
48
|
+
def export(input, name = NAME)
|
|
49
49
|
fields = FieldWriter.new
|
|
50
50
|
%w[ora ddra orb ddrb].each { |key| fields.byte(input.int(key, 0)) }
|
|
51
51
|
fields.word(input.int("t1.latch", 0xffff)).word(timer1(input))
|
|
@@ -58,7 +58,7 @@ module Badline
|
|
|
58
58
|
fields.byte(input.bool("t1.pb7", true) ? 0x80 : 0)
|
|
59
59
|
fields.byte(ticks(input))
|
|
60
60
|
fields.byte(LEVELS | (input.bool("ca.c2_output", true) ? 0x80 : 0)).zeros(2)
|
|
61
|
-
fields.byte(input.bool("t2.armed", false) ? 1 : 0).zeros(2).section(
|
|
61
|
+
fields.byte(input.bool("t2.armed", false) ? 1 : 0).zeros(2).section(name, MAJOR, MINOR)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# Timer 1's counter as a read on the next cycle finds it: as it
|
|
@@ -96,13 +96,14 @@ module Badline
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
# The shift register from its mode, data and the ticks into its
|
|
99
|
-
# byte (#ticks), with the last bit put out on CB2
|
|
99
|
+
# byte (#ticks), with the last bit put out on CB2, or CB2 high
|
|
100
|
+
# as it is at power-on while the register is off.
|
|
100
101
|
def shift_register(out, mode, data, ticks)
|
|
101
102
|
ticks = [ticks, TICKS].min
|
|
102
103
|
bits = ticks == TICKS ? 0 : 8 - (ticks / 2)
|
|
103
104
|
bits = 8 if mode == 4 && ticks < TICKS
|
|
104
105
|
out.int("sr.mode", mode).int("sr.data", data).int("sr.bits", bits).int("sr.delay", 0)
|
|
105
|
-
out.bool("sr.clock", ticks.even?).bool("sr.cb2", ticks.zero? || data.anybits?(0x01))
|
|
106
|
+
out.bool("sr.clock", ticks.even?).bool("sr.cb2", mode.zero? || ticks.zero? || data.anybits?(0x01))
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
# Whether the shift register clocks off timer 2.
|
|
@@ -38,7 +38,8 @@ module Badline
|
|
|
38
38
|
|
|
39
39
|
def import(section, computer)
|
|
40
40
|
registers = FieldReader.new(section).skip(4).bytes(0x20)
|
|
41
|
-
|
|
41
|
+
sid = computer.sid
|
|
42
|
+
registers.first(0x19).each_with_index { |value, reg| sid.poke(sid.start + reg, value) }
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
end
|