badline 0.17.0 → 0.19.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 +193 -0
- data/CLAUDE.md +19 -7
- data/CONTRIBUTING.md +11 -1
- data/README.md +178 -26
- data/doc/library.md +5 -1
- data/doc/media.md +3 -2
- data/doc/pinned-behaviour.md +169 -1
- data/doc/snapshot-format.md +302 -35
- data/doc/snapshots.md +97 -26
- data/doc/vice.md +344 -0
- data/exe/badline-ruby +5 -0
- data/lib/badline/audio/renderer.rb +4 -8
- data/lib/badline/audio/terminal.rb +34 -2
- data/lib/badline/c128/keyed_state.rb +2 -0
- data/lib/badline/c128.rb +5 -0
- data/lib/badline/c64/bus/fittings.rb +6 -0
- data/lib/badline/c64/keyed_state.rb +2 -0
- data/lib/badline/c64.rb +7 -0
- data/lib/badline/cbm2/keyed_state.rb +4 -0
- data/lib/badline/cbm2/p500.rb +9 -3
- data/lib/badline/cbm2.rb +10 -3
- data/lib/badline/chips/cia/timer.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +29 -4
- data/lib/badline/chips/cpu/instruction_set.rb +1 -1
- data/lib/badline/chips/cpu/interrupts.rb +9 -0
- data/lib/badline/chips/cpu/keyed_state.rb +15 -9
- data/lib/badline/chips/cpu/microcode.rb +10 -0
- 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/timer.rb +14 -3
- data/lib/badline/chips/rriot.rb +8 -3
- data/lib/badline/chips/sid/decimator.rb +1 -1
- data/lib/badline/chips/sid/filter.rb +4 -4
- data/lib/badline/chips/sid/keyed_parts.rb +51 -18
- data/lib/badline/chips/sid/waveform.rb +6 -6
- data/lib/badline/chips/sid.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +12 -2
- data/lib/badline/chips/vic_i/light_pen.rb +89 -0
- data/lib/badline/chips/vic_i.rb +91 -23
- data/lib/badline/data_path.rb +3 -3
- data/lib/badline/drives/ieee_models.rb +54 -0
- data/lib/badline/drives/model1001.rb +37 -0
- data/lib/badline/drives/model1541/sector_reader.rb +20 -3
- data/lib/badline/drives/model1541.rb +1 -0
- data/lib/badline/drives/model1551.rb +1 -0
- data/lib/badline/drives/model1571.rb +1 -0
- data/lib/badline/drives/model1581.rb +1 -0
- data/lib/badline/drives/model2031.rb +7 -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 +195 -0
- data/lib/badline/drives/model4040/parts.rb +20 -0
- data/lib/badline/drives/model4040.rb +195 -0
- data/lib/badline/drives/model8050/board.rb +79 -0
- data/lib/badline/drives/model8050/dos_board.rb +134 -0
- data/lib/badline/drives/model8050/fdc_bus.rb +7 -0
- data/lib/badline/drives/model8050/idle.rb +148 -31
- data/lib/badline/drives/model8050/keyed_state.rb +5 -2
- data/lib/badline/drives/model8050.rb +32 -130
- data/lib/badline/drives/model9060/idle.rb +169 -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 +197 -0
- data/lib/badline/frontend/app.rb +8 -0
- data/lib/badline/frontend/boot.rb +3 -2
- data/lib/badline/frontend/controls.rb +62 -6
- data/lib/badline/frontend/keys.rb +36 -0
- 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 +42 -12
- data/lib/badline/frontend/menu/menu_pages.rb +97 -2
- data/lib/badline/frontend/menu/menu_ports.rb +38 -1
- data/lib/badline/frontend/menu/pause_menu.rb +5 -1
- data/lib/badline/frontend/pet_keyboard.rb +23 -1
- 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 +31 -2
- data/lib/badline/frontend.rb +3 -0
- data/lib/badline/input/joystick.rb +15 -2
- 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 +118 -0
- data/lib/badline/kim1/teletype.rb +193 -0
- data/lib/badline/kim1.rb +194 -0
- data/lib/badline/machine/ieee_drives.rb +94 -117
- data/lib/badline/machine/keyboard_buffer.rb +20 -3
- data/lib/badline/machine/line_sound.rb +122 -0
- data/lib/badline/machine.rb +49 -15
- 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/true_drive.rb +52 -45
- data/lib/badline/media/vic20_media.rb +4 -2
- data/lib/badline/media.rb +16 -6
- data/lib/badline/memory.rb +2 -4
- data/lib/badline/options/family.rb +98 -8
- data/lib/badline/options/help.rb +44 -12
- data/lib/badline/options/table.rb +44 -12
- data/lib/badline/options/validation.rb +44 -8
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus.rb +16 -2
- 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 +92 -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 +221 -0
- data/lib/badline/pet.rb +23 -3
- data/lib/badline/plus4/keyed_state.rb +2 -0
- data/lib/badline/plus4.rb +5 -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/9060/dos-3.0.rom +0 -0
- data/lib/badline/roms/9060/sasi-300515-revb.rom +0 -0
- data/lib/badline/roms/README +176 -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/container.rb +8 -4
- data/lib/badline/snapshot/field_doc/markdown.rb +1 -1
- data/lib/badline/snapshot/field_doc.rb +35 -10
- data/lib/badline/snapshot/image.rb +19 -7
- 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/pet.rb +14 -7
- data/lib/badline/snapshot/vice/pet_cpu.rb +39 -5
- data/lib/badline/snapshot/vice/pet_crtc.rb +15 -2
- data/lib/badline/snapshot/vice/pet_dongle.rb +72 -0
- data/lib/badline/snapshot/vice/pet_drive8050.rb +188 -68
- data/lib/badline/snapshot/vice/pet_mem.rb +55 -5
- data/lib/badline/snapshot/vice/pet_restore.rb +40 -11
- data/lib/badline/snapshot/vice/vic20.rb +70 -0
- data/lib/badline/snapshot/vice/vic20_cpu.rb +61 -0
- data/lib/badline/snapshot/vice/vic20_mem.rb +85 -0
- data/lib/badline/snapshot/vice/vic20_restore.rb +124 -0
- data/lib/badline/snapshot/vice/vic20_vici.rb +200 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/snapshot.rb +8 -4
- data/lib/badline/status.rb +30 -30
- data/lib/badline/storage/d67_image.rb +55 -0
- data/lib/badline/storage/d90_image.rb +180 -0
- data/lib/badline/storage/kim_tape.rb +254 -0
- data/lib/badline/storage/papertape.rb +90 -0
- data/lib/badline/storage.rb +4 -0
- data/lib/badline/support/kim1_checkpoint.rb +41 -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/port_wiring.rb +11 -0
- data/lib/badline/vic20.rb +30 -12
- data/lib/badline.rb +1 -0
- metadata +85 -1
|
@@ -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 and the 6702.
|
|
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. The 6702 is
|
|
25
|
+
# in the settings from their fifth byte (Dongle).
|
|
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,46 @@ 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
|
+
if superpet
|
|
81
|
+
settings[SETTINGS_CPU] = SETTINGS_M6809 if machine.m6809_running?
|
|
82
|
+
dongle = machine.dongle
|
|
83
|
+
settings[Dongle::AT, Dongle::LENGTH] = Dongle.export(dongle.settled_registers, dongle.latch)
|
|
84
|
+
end
|
|
59
85
|
fields.bytes(settings).section(NAME, MAJOR, MINOR)
|
|
60
86
|
end
|
|
61
87
|
|
|
88
|
+
def latches(board) = (board.bank << 4) | LATCHES | (board.socket.zero? ? BANK_RAM : 0)
|
|
89
|
+
|
|
90
|
+
# Whether a SuperPET's settings say the 6809 has the bus.
|
|
91
|
+
def m6809?(section)
|
|
92
|
+
data = section.data
|
|
93
|
+
offset = data.bytesize - SETTINGS.length + SETTINGS_CPU
|
|
94
|
+
offset >= 0 && data.getbyte(offset).to_i.anybits?(SETTINGS_M6809)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# A SuperPET's bank latch, socket bit, 64K board and 6702 by their
|
|
98
|
+
# BL-SUPERPET keys, in a keyed module's writer.
|
|
99
|
+
def import_superpet(section, out)
|
|
100
|
+
fields = FieldReader.new(section).skip(4)
|
|
101
|
+
latches = fields.byte
|
|
102
|
+
fields.skip(fields_before_board(section))
|
|
103
|
+
out.int("bank_latch", latches >> 4).int("socket", latches.anybits?(BANK_RAM) ? 0 : 1)
|
|
104
|
+
out.int("system_latch", 0).bytes("ram", fields.string(EXPANSION))
|
|
105
|
+
fields.skip(Dongle::AT)
|
|
106
|
+
Dongle.import(fields.bytes(Dongle::LENGTH), out)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The RAM and the screen RAM before a SuperPET's board.
|
|
110
|
+
def fields_before_board(section) = (ram_kb(section) * 1024) + 0x800
|
|
111
|
+
|
|
62
112
|
# The board's number in the header.
|
|
63
113
|
def board_of(section) = FieldReader.new(section).byte
|
|
64
114
|
|
|
@@ -8,18 +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. A snapshot with a true
|
|
12
|
-
#
|
|
13
|
-
#
|
|
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.
|
|
14
15
|
module Restore
|
|
15
16
|
# MAINCPU and PETMEM hold the machine itself, so a version badline
|
|
16
17
|
# doesn't read fails the restore. The others are left out and
|
|
17
18
|
# reported.
|
|
18
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
|
|
19
23
|
# The host's media, which a restore keeps.
|
|
20
24
|
KEPT = %w[BL-TAPE1 BL-TAPE2 BL-TRAPDOS].freeze
|
|
21
|
-
# The disks in a true drive's drives,
|
|
22
|
-
|
|
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
|
|
23
28
|
|
|
24
29
|
module_function
|
|
25
30
|
|
|
@@ -53,7 +58,8 @@ module Badline
|
|
|
53
58
|
applied = []
|
|
54
59
|
ignored = []
|
|
55
60
|
sections = []
|
|
56
|
-
|
|
61
|
+
names = read_names(computer)
|
|
62
|
+
names.each do |name|
|
|
57
63
|
section = container[name]
|
|
58
64
|
next if section.nil?
|
|
59
65
|
|
|
@@ -72,17 +78,20 @@ module Badline
|
|
|
72
78
|
end
|
|
73
79
|
sections += drive(container, applied, ignored, kept_disks(container, computer, disks))
|
|
74
80
|
computer.load_modules(Keyed::Modules.new(modules(container, computer, applied) + sections))
|
|
75
|
-
Report.new(applied:, ignored: ignored + left_out(container, applied))
|
|
81
|
+
Report.new(applied:, ignored: ignored + left_out(container, names, applied))
|
|
76
82
|
end
|
|
77
83
|
|
|
84
|
+
def read_names(computer) = computer.superpet? ? READ + SUPERPET_READ : READ
|
|
85
|
+
|
|
78
86
|
# The disks badline's true drive has, which stay in the drive the
|
|
79
|
-
# snapshot puts on the bus when it is of the same model
|
|
87
|
+
# snapshot puts on the bus when it is of the same model, or of the
|
|
88
|
+
# same xpet type.
|
|
80
89
|
def kept_disks(container, computer, disks)
|
|
81
90
|
drive = computer.true_drive
|
|
82
91
|
return [] if drive.nil?
|
|
83
92
|
|
|
84
93
|
if Drive8050.present?(container)
|
|
85
|
-
Drive8050.model(container)
|
|
94
|
+
Drive8050.same_type?(Drive8050.model(container), drive.model_name) ? disks : []
|
|
86
95
|
else
|
|
87
96
|
drive.model_name == "2031" ? disks.first(1) : []
|
|
88
97
|
end
|
|
@@ -113,6 +122,7 @@ module Badline
|
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
applied.concat(Drive8050::NAMES)
|
|
125
|
+
model = Drive8050.hard_disk_model(disks) if Drive8050.hard_disk?(model)
|
|
116
126
|
Drive8050.import(container, Drive8050.reference(model), disks)
|
|
117
127
|
end
|
|
118
128
|
|
|
@@ -134,6 +144,8 @@ module Badline
|
|
|
134
144
|
when PETMem::NAME then PETMem.reads?(section)
|
|
135
145
|
when CRTC::NAME then CRTC.reads?(section)
|
|
136
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)
|
|
137
149
|
else PIAs.reads?(section)
|
|
138
150
|
end
|
|
139
151
|
end
|
|
@@ -160,9 +172,26 @@ module Badline
|
|
|
160
172
|
if applied.include?(VIA::NAME)
|
|
161
173
|
sections << Keyed.section("BL-VIA") { |out| VIA.import(container[VIA::NAME], out) }
|
|
162
174
|
end
|
|
175
|
+
sections += superpet(container, applied) if computer.superpet?
|
|
163
176
|
sections + video
|
|
164
177
|
end
|
|
165
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
|
+
|
|
166
195
|
def machine(out, container, computer, drive)
|
|
167
196
|
out.text("family", "pet")
|
|
168
197
|
PETSetup.of(computer).write_fields(out)
|
|
@@ -203,8 +232,8 @@ module Badline
|
|
|
203
232
|
[Keyed.section("BL-CRTC") { |out| CRTC.copy(fields, out) }]
|
|
204
233
|
end
|
|
205
234
|
|
|
206
|
-
def left_out(container, applied)
|
|
207
|
-
container.sections.reject { |section|
|
|
235
|
+
def left_out(container, names, applied)
|
|
236
|
+
container.sections.reject { |section| names.include?(section.name) || applied.include?(section.name) }
|
|
208
237
|
.map { |section| "#{section}: badline doesn't read this module, left out" }
|
|
209
238
|
end
|
|
210
239
|
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/snapshot/vice/vic20_cpu"
|
|
4
|
+
require "badline/snapshot/vice/vic20_mem"
|
|
5
|
+
require "badline/snapshot/vice/vic20_vici"
|
|
6
|
+
require "badline/snapshot/vice/vic20_restore"
|
|
7
|
+
|
|
8
|
+
module Badline
|
|
9
|
+
module Snapshot
|
|
10
|
+
module Vice
|
|
11
|
+
# xvic's modules for the VIC-20, PAL and NTSC: the CPU, the RAM, the
|
|
12
|
+
# VIC-I with the colour RAM, and both VIAs, written from badline's
|
|
13
|
+
# machine and read into it. Each module's file says what maps and
|
|
14
|
+
# what is approximated. The VIAs' are xpet's (PET::VIA). They go
|
|
15
|
+
# through the machine's own keyed fields (Keyed), which a save writes
|
|
16
|
+
# and a load reads.
|
|
17
|
+
module Vic20
|
|
18
|
+
# The modules xvic 3.10 resets the machine without, each as xvic
|
|
19
|
+
# leaves them with nothing attached: no cartridge, no file system
|
|
20
|
+
# drive open, the cassette port on with a datasette in it, the
|
|
21
|
+
# control port on with the joystick released, and nothing in the
|
|
22
|
+
# user port. With no drive modules xvic keeps its own drives as they
|
|
23
|
+
# are.
|
|
24
|
+
CARTRIDGE = ["VIC20CART", 2, 1, [0xff, 0xff, 0xff, 0xff, 0x00]].freeze
|
|
25
|
+
PERIPHERALS = [
|
|
26
|
+
["FSDRIVE", 0, 0, Array.new(258, 0)], ["TAPEPORT", 1, 0, [0x01, 0x01]], ["JOYPORT0", 0, 0, [0x01]],
|
|
27
|
+
["JOYSTICK0", 1, 2, [0x00, 0x00]], ["USERPORT", 1, 0, [0x01, 0x00]]
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
# The modules xvic 3.10 needs, in its order, describing `machine`,
|
|
33
|
+
# which Machine::Settling has brought between instructions, from
|
|
34
|
+
# its keyed modules, `sections`.
|
|
35
|
+
def export(machine, sections)
|
|
36
|
+
readers = {}
|
|
37
|
+
sections.each { |section| readers[section.name] = Keyed::Reader.parse(section) }
|
|
38
|
+
[MainCPU.export(machine), Mem.export(machine, reader(readers, "BL-MACHINE")), section(*CARTRIDGE),
|
|
39
|
+
VICI.export(machine, reader(readers, "BL-VICI"), reader(readers, "BL-COLORRAM")),
|
|
40
|
+
PET::VIA.export(reader(readers, "BL-VIA1"), "VIA1"), PET::VIA.export(reader(readers, "BL-VIA2"), "VIA2")] +
|
|
41
|
+
PERIPHERALS.map { |entry| section(*entry) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def section(name, major, minor, bytes) = FieldWriter.new.bytes(bytes).section(name, major, minor)
|
|
45
|
+
|
|
46
|
+
def reader(readers, name) = readers[name] || Keyed::Reader.missing(name)
|
|
47
|
+
|
|
48
|
+
# How the machine xvic saved was built: the RAM from VIC20MEM's
|
|
49
|
+
# blocks, and the model from the lines of the VIC-I's picture.
|
|
50
|
+
def setup(container)
|
|
51
|
+
memory = container[Mem::NAME]
|
|
52
|
+
raise FormatError, "the snapshot has no #{Mem::NAME} module" if memory.nil?
|
|
53
|
+
raise FormatError, "#{memory}: badline doesn't read this version" unless Mem.reads?(memory)
|
|
54
|
+
|
|
55
|
+
ram = Mem.ram_configuration(memory)
|
|
56
|
+
raise FormatError, "#{memory}: RAM in blocks badline doesn't fit together" if ram.nil?
|
|
57
|
+
|
|
58
|
+
Vic20Setup.new(ram:, model: model(container))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def model(container)
|
|
62
|
+
vic = container[VICI::NAME]
|
|
63
|
+
return "pal" if vic.nil? || !VICI.reads?(vic)
|
|
64
|
+
|
|
65
|
+
VICI.model(vic) || raise(FormatError, "#{vic}: a picture of a size badline's VIC-Is don't draw")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module Vic20
|
|
7
|
+
# The 6502 as xvic 3.10 saves it, MAINCPU 1.4, 83 bytes: the clock,
|
|
8
|
+
# the registers, four dwords (the last opcode and three xvic keeps
|
|
9
|
+
# at 0 between instructions), five clocks of the IRQ and NMI
|
|
10
|
+
# bookkeeping, then the IRQ and NMI source counts and the pending
|
|
11
|
+
# interrupts. It is x64sc's MAINCPU 1.4 without the BA flags and
|
|
12
|
+
# the two clocks at its end, and xpet's MAINCPU 1.3 with one more
|
|
13
|
+
# dword. The layout was inferred from xvic's snapshots against its
|
|
14
|
+
# monitor's registers and clock.
|
|
15
|
+
#
|
|
16
|
+
# Written: the clock and the registers, which between instructions
|
|
17
|
+
# are the whole CPU, with VIA 2 counted as an IRQ source while it
|
|
18
|
+
# pulls the line, and VIA 1 as an NMI source. VICE's interrupt
|
|
19
|
+
# clocks are written as long past.
|
|
20
|
+
#
|
|
21
|
+
# Read: the clock and the registers. The CPU starts on an
|
|
22
|
+
# instruction boundary, as xvic's snapshot is taken on one.
|
|
23
|
+
module MainCPU
|
|
24
|
+
NAME = "MAINCPU"
|
|
25
|
+
MAJOR = 1
|
|
26
|
+
MINOR = 4
|
|
27
|
+
SIZE = 83
|
|
28
|
+
IK_IRQ = 0x02
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR && section.data.bytesize == SIZE
|
|
33
|
+
|
|
34
|
+
def export(machine)
|
|
35
|
+
cpu = machine.cpu
|
|
36
|
+
fields = FieldWriter.new.qword(machine.cycles)
|
|
37
|
+
[cpu.a, cpu.x, cpu.y, cpu.stack_pointer].each { |register| fields.byte(register) }
|
|
38
|
+
fields.word(cpu.program_counter).byte(cpu.p)
|
|
39
|
+
4.times { fields.dword(0) }
|
|
40
|
+
5.times { fields.qword(0) }
|
|
41
|
+
irqs = machine.via2.irq? ? 1 : 0
|
|
42
|
+
fields.dword(irqs).dword(machine.via1.irq? ? 1 : 0).dword(irqs.positive? ? IK_IRQ : 0)
|
|
43
|
+
fields.section(NAME, MAJOR, MINOR)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The clock and the registers by their BL-CPU keys, in a keyed
|
|
47
|
+
# module's writer, and the clock.
|
|
48
|
+
def import(section, out)
|
|
49
|
+
fields = FieldReader.new(section)
|
|
50
|
+
clock = fields.qword
|
|
51
|
+
out.int("a", fields.byte).int("x", fields.byte).int("y", fields.byte).int("sp", fields.byte)
|
|
52
|
+
out.int("pc", fields.word).int("p", fields.byte).int("cycles", clock)
|
|
53
|
+
clock
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def clock(section) = FieldReader.new(section).qword
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module Vic20
|
|
7
|
+
# The VIC-20's RAM as xvic 3.10 saves it, VIC20MEM 1.1: a byte of
|
|
8
|
+
# the expansion blocks that hold RAM (bit 0 the 3K at $0400, bits
|
|
9
|
+
# 1-3 BLK1-BLK3, bit 5 BLK5), the bytes the CPU's and the VIC's
|
|
10
|
+
# data lines last held, a byte badline doesn't model (0, 1 or 6 in
|
|
11
|
+
# xvic's snapshots, not following the blocks), then the RAM: $0000-$03FF, $1000-$1FFF, the 3K at
|
|
12
|
+
# $0400-$0FFF, and BLK1, BLK2, BLK3 and BLK5 in turn, each only if
|
|
13
|
+
# it holds RAM. The layout was inferred from xvic's snapshots of
|
|
14
|
+
# each -memory setting, with each block filled with a byte of its
|
|
15
|
+
# own and the data lines left at a byte the monitor wrote. Colour
|
|
16
|
+
# RAM is in the VIC-I's module (VICI).
|
|
17
|
+
#
|
|
18
|
+
# Written and read: the RAM and both data lines' bytes. The fourth
|
|
19
|
+
# byte is written as 0, and not read.
|
|
20
|
+
module Mem
|
|
21
|
+
NAME = "VIC20MEM"
|
|
22
|
+
MAJOR = 1
|
|
23
|
+
MINOR = 1
|
|
24
|
+
# Each expansion block's bit in the first byte, and the bytes it
|
|
25
|
+
# holds, as Badline::Vic20::Bus::BLOCKS names them.
|
|
26
|
+
BLOCK_BITS = { ram123: 0x01, blk1: 0x02, blk2: 0x04, blk3: 0x08, blk5: 0x20 }.freeze
|
|
27
|
+
INTERNAL = [[0x0000, 0x0400], [0x1000, 0x1000]].freeze
|
|
28
|
+
|
|
29
|
+
module_function
|
|
30
|
+
|
|
31
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
32
|
+
|
|
33
|
+
# The blocks the first byte names, or nil for bits no block has.
|
|
34
|
+
def blocks(section)
|
|
35
|
+
bits = FieldReader.new(section).byte
|
|
36
|
+
names = BLOCK_BITS.select { |_, bit| bits.anybits?(bit) }.keys
|
|
37
|
+
names.sum { |name| BLOCK_BITS[name] } == bits ? names : nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The RAM configuration (Badline::Vic20::Bus::RAM_CONFIGURATIONS)
|
|
41
|
+
# whose blocks the module has, or nil.
|
|
42
|
+
def ram_configuration(section)
|
|
43
|
+
names = blocks(section)
|
|
44
|
+
return nil if names.nil?
|
|
45
|
+
|
|
46
|
+
Badline::Vic20::Bus::RAM_CONFIGURATIONS.find { |_, blocks| blocks.sort == names.sort }&.first
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The regions the RAM comes in, as [address, length].
|
|
50
|
+
def regions(names)
|
|
51
|
+
INTERNAL + Badline::Vic20::Bus::BLOCKS.filter_map do |name, (page, pages)|
|
|
52
|
+
[page << 8, pages << 8] if names.include?(name)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The module for `machine`, with the data lines' bytes from its
|
|
57
|
+
# BL-MACHINE fields, `machine_fields`.
|
|
58
|
+
def export(machine, machine_fields)
|
|
59
|
+
names = Badline::Vic20::Bus::RAM_CONFIGURATIONS.fetch(machine.ram_configuration)
|
|
60
|
+
fields = FieldWriter.new.byte(names.sum { |name| BLOCK_BITS[name] })
|
|
61
|
+
fields.byte(machine_fields.int("bus.data", 0)).byte(machine_fields.int("bus.video_data", 0))
|
|
62
|
+
fields.byte(0)
|
|
63
|
+
ram = machine.ram
|
|
64
|
+
regions(names).each do |address, length|
|
|
65
|
+
fields.bytes(Array.new(length) { |offset| ram.peek(address + offset) })
|
|
66
|
+
end
|
|
67
|
+
fields.section(NAME, MAJOR, MINOR)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The RAM into `ram`, the machine's 48K at power-on, and the data
|
|
71
|
+
# lines' bytes into BL-MACHINE's writer `out`.
|
|
72
|
+
def import(section, ram, out)
|
|
73
|
+
fields = FieldReader.new(section)
|
|
74
|
+
names = blocks(section) || []
|
|
75
|
+
fields.skip(1)
|
|
76
|
+
out.int("bus.data", fields.byte).int("bus.video_data", fields.byte)
|
|
77
|
+
fields.skip(1)
|
|
78
|
+
regions(names).each { |address, length| ram[address, length] = fields.bytes(length) }
|
|
79
|
+
ram
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module Vic20
|
|
7
|
+
# Restores a VIC-20 from a snapshot xvic wrote. The modules badline
|
|
8
|
+
# reads become the machine's own keyed modules (Keyed), which it
|
|
9
|
+
# loads as it loads its own snapshot. Whatever they don't set stays
|
|
10
|
+
# as the machine has it at power-on: the VIC-I's sound and picture,
|
|
11
|
+
# which it paints afresh, a cartridge, the datasette's tape, the
|
|
12
|
+
# disk device 8 serves and a true drive, which xvic's drive modules
|
|
13
|
+
# don't reach.
|
|
14
|
+
module Restore
|
|
15
|
+
# MAINCPU and VIC20MEM hold the machine itself, so a version badline
|
|
16
|
+
# doesn't read fails the restore. The others are left out and
|
|
17
|
+
# reported.
|
|
18
|
+
READ = [MainCPU::NAME, Mem::NAME, VICI::NAME, "VIA1", "VIA2"].freeze
|
|
19
|
+
REQUIRED = [MainCPU::NAME, Mem::NAME].freeze
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Applies the modules badline reads to `computer`, and returns a
|
|
24
|
+
# Report. The snapshot goes into a new machine first, so one that
|
|
25
|
+
# fails leaves `computer` as it was, and one of a machine built
|
|
26
|
+
# another way fails too.
|
|
27
|
+
def import(container, computer)
|
|
28
|
+
unless computer.family == :vic20
|
|
29
|
+
raise FormatError, "the snapshot is of a VIC-20, not a #{Image::FAMILIES.fetch(computer.family.to_s)}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
setup = Vic20.setup(container)
|
|
33
|
+
ours = Vic20Setup.new(ram: computer.ram_configuration, model: computer.model)
|
|
34
|
+
raise FormatError, "the snapshot is of a VIC-20 with #{setup}, not #{ours}" unless setup == ours
|
|
35
|
+
|
|
36
|
+
apply(container, setup.build)
|
|
37
|
+
computer.power_cycle!
|
|
38
|
+
report = apply(container, computer)
|
|
39
|
+
kept = computer.mounted? ? ["the disk mounted in device 8 stays mounted"] : []
|
|
40
|
+
Report.new(applied: report.applied, ignored: report.ignored + kept)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Applies the modules badline reads to `computer`, a machine built
|
|
44
|
+
# as the snapshot's was, and returns the Report.
|
|
45
|
+
def apply(container, computer)
|
|
46
|
+
applied = []
|
|
47
|
+
ignored = []
|
|
48
|
+
READ.each do |name|
|
|
49
|
+
section = container[name]
|
|
50
|
+
next if section.nil?
|
|
51
|
+
|
|
52
|
+
if reads?(section)
|
|
53
|
+
applied << name
|
|
54
|
+
else
|
|
55
|
+
raise FormatError, "#{section}: badline doesn't read this version" if REQUIRED.include?(name)
|
|
56
|
+
|
|
57
|
+
ignored << "#{section}: badline doesn't read this version, left out"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
REQUIRED.each do |name|
|
|
61
|
+
raise FormatError, "the snapshot has no #{name} module" unless applied.include?(name)
|
|
62
|
+
end
|
|
63
|
+
computer.load_modules(Keyed::Modules.new(modules(container, computer, applied)))
|
|
64
|
+
Report.new(applied:, ignored: ignored + left_out(container, applied))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def reads?(section)
|
|
68
|
+
case section.name
|
|
69
|
+
when MainCPU::NAME then MainCPU.reads?(section)
|
|
70
|
+
when Mem::NAME then Mem.reads?(section)
|
|
71
|
+
when VICI::NAME then VICI.reads?(section)
|
|
72
|
+
else PET::VIA.reads?(section)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The machine's keyed modules: its own as they stand, with those
|
|
77
|
+
# the modules `applied` names stand for in their place.
|
|
78
|
+
def modules(container, computer, applied)
|
|
79
|
+
own = {}
|
|
80
|
+
computer.save_modules.each { |section| own[section.name] = section }
|
|
81
|
+
ram = Keyed::Reader.parse(own.fetch("BL-RAM")).bytes("ram", "".b).bytes
|
|
82
|
+
own["BL-MACHINE"] = Keyed.section("BL-MACHINE") do |out|
|
|
83
|
+
out.text("family", "vic20")
|
|
84
|
+
Vic20Setup.new(ram: computer.ram_configuration, model: computer.model).write_fields(out)
|
|
85
|
+
out.int("cycles", MainCPU.clock(container[MainCPU::NAME]))
|
|
86
|
+
out.bool("nmi_asserted", via_irq?(container, "VIA1", applied))
|
|
87
|
+
Mem.import(container[Mem::NAME], ram, out)
|
|
88
|
+
end
|
|
89
|
+
own["BL-RAM"] = Keyed.section("BL-RAM") { |out| out.bytes("ram", ram) }
|
|
90
|
+
own["BL-CPU"] = Keyed.section("BL-CPU") { |out| MainCPU.import(container[MainCPU::NAME], out) }
|
|
91
|
+
vic(container, computer, applied, own)
|
|
92
|
+
%w[VIA1 VIA2].each do |name|
|
|
93
|
+
next unless applied.include?(name)
|
|
94
|
+
|
|
95
|
+
own["BL-#{name}"] = Keyed.section("BL-#{name}") { |out| PET::VIA.import(container[name], out) }
|
|
96
|
+
end
|
|
97
|
+
own.values
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def vic(container, computer, applied, own)
|
|
101
|
+
return unless applied.include?(VICI::NAME)
|
|
102
|
+
|
|
103
|
+
section = container[VICI::NAME]
|
|
104
|
+
own["BL-VICI"] = Keyed.section("BL-VICI") { |out| VICI.import(section, out, computer.vic.profile) }
|
|
105
|
+
own["BL-COLORRAM"] = Keyed.section("BL-COLORRAM") { |out| out.bytes("ram", VICI.colors(section)) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Whether the VIA pulls its interrupt line: a flag it has enabled.
|
|
109
|
+
def via_irq?(container, name, applied)
|
|
110
|
+
return false unless applied.include?(name)
|
|
111
|
+
|
|
112
|
+
bytes = FieldReader.new(container[name]).bytes(PET::VIA::SIZE)
|
|
113
|
+
bytes[0x12].anybits?(bytes[0x13] & 0x7f)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def left_out(container, applied)
|
|
117
|
+
container.sections.reject { |section| applied.include?(section.name) }
|
|
118
|
+
.map { |section| "#{section}: badline doesn't read this module, left out" }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|