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,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module P500
|
|
7
|
+
# VIC-II 1.3 as xcbm5x0 writes it, which runs VICE's line-based
|
|
8
|
+
# VIC-II rather than x64sc's, so the module is laid out otherwise
|
|
9
|
+
# than x64sc's under the same name: whether bad lines may happen,
|
|
10
|
+
# whether this line is one, the vertical border, the colour
|
|
11
|
+
# buffer, colour RAM, whether the sequencer is idle, the light
|
|
12
|
+
# pen's trigger and position, the video matrix buffer, the sprites
|
|
13
|
+
# whose DMA starts next line, a word xcbm5x0 keeps at $00100000,
|
|
14
|
+
# the beam's cycle and line, the registers, the sprite collisions
|
|
15
|
+
# pending, the sprites' DMA, the VIC's bank, VC, the matrix's
|
|
16
|
+
# width, VCBASE, then sprite and drawing fields with the bank
|
|
17
|
+
# again, the line its picture draws next and its size, and the
|
|
18
|
+
# picture. xcbm5x0's cycle in the line is badline's column.
|
|
19
|
+
#
|
|
20
|
+
# Written: the registers, colour RAM, the buffers, the beam with
|
|
21
|
+
# its counters and the sprites' DMA as badline's VIC has them, the
|
|
22
|
+
# bank as TPI2's PC6 and PC7 pick it, and the rest as xcbm5x0
|
|
23
|
+
# leaves it. The picture is left blank, as xcbm5x0 paints it again.
|
|
24
|
+
#
|
|
25
|
+
# Read: the registers, colour RAM, the buffers, the beam with VC,
|
|
26
|
+
# VCBASE and whether the sequencer is idle, the light pen and the
|
|
27
|
+
# sprites' DMA, through the x64sc module's reader (VICII::Import).
|
|
28
|
+
# The row counter follows from the line and YSCROLL, and the
|
|
29
|
+
# sprites' counters and the borders' flip-flops start afresh.
|
|
30
|
+
# xcbm5x0's VIC isn't x64sc's, so a program badline runs on from its
|
|
31
|
+
# snapshot reads the raster a cycle or so off now and then.
|
|
32
|
+
module VICII
|
|
33
|
+
NAME = Vice::VICII::NAME
|
|
34
|
+
MAJOR = 1
|
|
35
|
+
MINOR = 3
|
|
36
|
+
ALLOW_BAD_LINES = 0x000
|
|
37
|
+
BAD_LINE = 0x001
|
|
38
|
+
BLANK = 0x002
|
|
39
|
+
COLOR_BUFFER = 0x003
|
|
40
|
+
COLOR_RAM = 0x02b
|
|
41
|
+
IDLE = 0x42b
|
|
42
|
+
LIGHT_PEN = 0x42c
|
|
43
|
+
MATRIX_BUFFER = 0x42f
|
|
44
|
+
RAM_BASE = 0x458
|
|
45
|
+
CYCLE = 0x45c
|
|
46
|
+
LINE = 0x45d
|
|
47
|
+
REGISTERS = 0x45f
|
|
48
|
+
SPRITE_DMA = 0x4a0
|
|
49
|
+
BANK = 0x4a2
|
|
50
|
+
VC = 0x4a4
|
|
51
|
+
VC_ADD = 0x4a6
|
|
52
|
+
VC_BASE = 0x4a7
|
|
53
|
+
VIDEO_INT = 0x4a9
|
|
54
|
+
DRAW_FLAGS = 0x4cd
|
|
55
|
+
FETCH_BANK = 0x4cf
|
|
56
|
+
DRAWN_LINE = 0x4d1
|
|
57
|
+
PICTURE = 0x4d5
|
|
58
|
+
HEADER = 0x4e1
|
|
59
|
+
# The picture's size: PAL's and NTSC's.
|
|
60
|
+
SIZES = { 312 => [504, 312], 263 => [520, 275] }.freeze
|
|
61
|
+
|
|
62
|
+
module_function
|
|
63
|
+
|
|
64
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
65
|
+
|
|
66
|
+
# The lines xcbm5x0's picture has, which name its region.
|
|
67
|
+
def picture_lines(section) = FieldReader.new(section).skip(PICTURE + 4).dword
|
|
68
|
+
|
|
69
|
+
def export(machine)
|
|
70
|
+
vic = machine.vic
|
|
71
|
+
width, height = SIZES.fetch(vic.region.lines_per_frame)
|
|
72
|
+
bytes = Array.new(HEADER + (width * (height + 4)), 0)
|
|
73
|
+
sequencer(vic, bytes)
|
|
74
|
+
bytes[COLOR_RAM, 0x400] = Array.new(0x400) { |offset| machine.color_ram.nibble(0xd400 + offset) }
|
|
75
|
+
beam(vic, bytes)
|
|
76
|
+
bank = ((machine.tpi2.port_c_output >> 6) & 0x03) << 14
|
|
77
|
+
[BANK, FETCH_BANK].each { |at| word(bytes, at, bank) }
|
|
78
|
+
[RAM_BASE + 2, DRAW_FLAGS].each { |at| bytes[at] = 0x10 }
|
|
79
|
+
bytes[VIDEO_INT] = 1
|
|
80
|
+
[vic.rasterline, width, height, width].each_with_index do |value, n|
|
|
81
|
+
dword(bytes, DRAWN_LINE + (4 * n), value)
|
|
82
|
+
end
|
|
83
|
+
FieldWriter.new.bytes(bytes).section(NAME, MAJOR, MINOR)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The sequencer's state and the buffers it reads.
|
|
87
|
+
def sequencer(vic, bytes)
|
|
88
|
+
state = vic.display_state
|
|
89
|
+
bytes[ALLOW_BAD_LINES] = state.bad_lines_enabled ? 1 : 0
|
|
90
|
+
bytes[BAD_LINE] = state.bad_line_condition? ? 1 : 0
|
|
91
|
+
bytes[BLANK] = vic.sequencer.borders.first ? 1 : 0
|
|
92
|
+
bytes[COLOR_BUFFER, 40] = vic.color_buffer.map { |value| value || 0 }
|
|
93
|
+
bytes[IDLE] = state.display? ? 0 : 1
|
|
94
|
+
bytes[MATRIX_BUFFER, 40] = vic.character_buffer.map { |value| value || 0 }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The beam, the registers and the counters.
|
|
98
|
+
def beam(vic, bytes)
|
|
99
|
+
state = vic.display_state
|
|
100
|
+
bytes[CYCLE] = vic.column
|
|
101
|
+
word(bytes, LINE, vic.rasterline)
|
|
102
|
+
bytes[REGISTERS, 0x40] = vic.register_file
|
|
103
|
+
bytes[SPRITE_DMA] = 8.times.sum { |n| vic.sprites[n].displaying? ? 1 << n : 0 }
|
|
104
|
+
word(bytes, VC, state.vc)
|
|
105
|
+
bytes[VC_ADD] = 40
|
|
106
|
+
word(bytes, VC_BASE, state.vc_base)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def word(bytes, at, value) = bytes[at, 2] = [value & 0xffff].pack("v").bytes
|
|
110
|
+
|
|
111
|
+
def dword(bytes, at, value) = bytes[at, 4] = [value].pack("V").bytes
|
|
112
|
+
|
|
113
|
+
def import(section, vic, color_ram)
|
|
114
|
+
Import.new(section, vic.region).apply_to(vic, color_ram, 0xd400)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Reads xcbm5x0's module into the fields x64sc's reader puts into
|
|
118
|
+
# badline's VIC.
|
|
119
|
+
class Import < Vice::VICII::Import
|
|
120
|
+
# x64sc's cycle is badline's column plus one, its cycle 0 the
|
|
121
|
+
# last column of the line before (VICII.vice_cycle).
|
|
122
|
+
def initialize(section, region)
|
|
123
|
+
super(nil)
|
|
124
|
+
bytes = section.data.bytes
|
|
125
|
+
@registers = bytes[REGISTERS, 0x40]
|
|
126
|
+
line = bytes[LINE] | (bytes[LINE + 1] << 8)
|
|
127
|
+
@cycle = (bytes[CYCLE] + 1) % region.cycles_per_line
|
|
128
|
+
@line = @cycle.zero? ? (line + 1) % region.lines_per_frame : line
|
|
129
|
+
@frame_start = false
|
|
130
|
+
@irq_status = @registers[0x19]
|
|
131
|
+
@raster_match = false
|
|
132
|
+
@character_buffer = bytes[MATRIX_BUFFER, 40]
|
|
133
|
+
@color_buffer = bytes[COLOR_BUFFER, 40]
|
|
134
|
+
read_old_counters(bytes)
|
|
135
|
+
@color_ram = bytes[COLOR_RAM, 0x400]
|
|
136
|
+
@sprite_mc = Array.new(8, 0)
|
|
137
|
+
@sprite_mcbase = Array.new(8, 0)
|
|
138
|
+
@sprite_exp_ff = 0
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def read_old_counters(bytes)
|
|
144
|
+
@bad_lines_enabled = !bytes[ALLOW_BAD_LINES].zero?
|
|
145
|
+
@collisions = [@registers[0x1e], @registers[0x1f]]
|
|
146
|
+
@display = bytes[IDLE].zero?
|
|
147
|
+
@vc_base = bytes[VC_BASE] | (bytes[VC_BASE + 1] << 8)
|
|
148
|
+
@vc = bytes[VC] | (bytes[VC + 1] << 8)
|
|
149
|
+
@rc = @display ? (@line - @registers[0x11]) & 0x07 : 7
|
|
150
|
+
@vmli = 0
|
|
151
|
+
@lp_low = false
|
|
152
|
+
@lp_triggered = !bytes[LIGHT_PEN].zero?
|
|
153
|
+
@fetch_d011 = @registers[0x11]
|
|
154
|
+
@dma_bits = bytes[SPRITE_DMA]
|
|
155
|
+
@display_bits = @dma_bits
|
|
156
|
+
blank = !bytes[BLANK].zero?
|
|
157
|
+
@vertical_border = blank
|
|
158
|
+
@vertical_armed = false
|
|
159
|
+
@main_border = blank
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/snapshot/vice/cbm2_acia"
|
|
3
4
|
require "badline/snapshot/vice/pet_cpu"
|
|
4
5
|
require "badline/snapshot/vice/pet_mem"
|
|
5
6
|
require "badline/snapshot/vice/pet_pias"
|
|
6
7
|
require "badline/snapshot/vice/pet_via"
|
|
7
8
|
require "badline/snapshot/vice/pet_crtc"
|
|
9
|
+
require "badline/snapshot/vice/pet_drive"
|
|
10
|
+
require "badline/snapshot/vice/pet_drive8050"
|
|
8
11
|
require "badline/snapshot/vice/pet_restore"
|
|
9
12
|
|
|
10
13
|
module Badline
|
|
11
14
|
module Snapshot
|
|
12
15
|
module Vice
|
|
13
|
-
# xpet's modules for the PET: the CPU, the RAM, the CRTC, both PIAs
|
|
14
|
-
# and
|
|
16
|
+
# xpet's modules for the PET: the CPU, the RAM, the CRTC, both PIAs,
|
|
17
|
+
# the VIA and a true 2031, written from badline's machine and read
|
|
18
|
+
# into it. Each
|
|
15
19
|
# module's file says what maps and what is approximated. They go
|
|
16
20
|
# through the PET's own keyed fields (Keyed), which a save writes and a
|
|
17
21
|
# load reads, so they read no chip's insides.
|
|
@@ -19,8 +23,8 @@ module Badline
|
|
|
19
23
|
# The models badline builds by the board PETMEM names (PETMem): the
|
|
20
24
|
# discrete board is a 2001's where xpet's settings say so, and a
|
|
21
25
|
# 3032 otherwise.
|
|
22
|
-
MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::
|
|
23
|
-
PETMem::CBM8296 => "8296" }.freeze
|
|
26
|
+
MODELS = { PETMem::CRTC40 => "4032", PETMem::CRTC80 => "8032", PETMem::SUPERPET => "superpet",
|
|
27
|
+
PETMem::CBM8096 => "8096", PETMem::CBM8296 => "8296" }.freeze
|
|
24
28
|
# The modules xpet resets the machine without, for the parts badline
|
|
25
29
|
# doesn't model in VICE's terms, each as xpet leaves them with
|
|
26
30
|
# nothing attached: no file system drive open, both cassette ports
|
|
@@ -45,8 +49,10 @@ module Badline
|
|
|
45
49
|
[MainCPU.export(machine), CPU6809.export(machine), PETMem.export(machine),
|
|
46
50
|
CRTC.export(machine, reader(readers, "BL-CRTC"), reader(readers, "BL-VIDEO")),
|
|
47
51
|
PIAs.export("PIA1", machine.pia1, reader(readers, "BL-PIA1")),
|
|
48
|
-
PIAs.export("PIA2", machine.pia2, reader(readers, "BL-PIA2")), VIA.export(reader(readers, "BL-VIA"))
|
|
49
|
-
|
|
52
|
+
PIAs.export("PIA2", machine.pia2, reader(readers, "BL-PIA2")), VIA.export(reader(readers, "BL-VIA")),
|
|
53
|
+
*Drive2031.export(machine, readers), *Drive8050.export(machine, readers)] +
|
|
54
|
+
PERIPHERALS.map { |name, major, minor, bytes| FieldWriter.new.bytes(bytes).section(name, major, minor) } +
|
|
55
|
+
(machine.superpet? ? [CBM2::ACIA.export(reader(readers, "BL-ACIA"), machine.cycles)] : [])
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
def reader(readers, name) = readers[name] || Keyed::Reader.missing(name)
|
|
@@ -57,7 +63,9 @@ module Badline
|
|
|
57
63
|
# xpet doesn't save the keyboard, the ROMs or an 8296's jumpers, so
|
|
58
64
|
# a discrete board is a 3032 with the graphics keyboard unless it is
|
|
59
65
|
# a 2001, each model has its own ROMs, and an 8296 none of its
|
|
60
|
-
# jumpers, as xpet's own has.
|
|
66
|
+
# jumpers, as xpet's own has. Nor does it save the SuperPET's
|
|
67
|
+
# switches: its CPU switch is at the CPU that has the bus, and its
|
|
68
|
+
# RAM switch at R/W, which is the only position xpet has.
|
|
61
69
|
def setup(container)
|
|
62
70
|
memory = memory(container)
|
|
63
71
|
ram_kb = PETMem.ram_kb(memory)
|
|
@@ -71,8 +79,11 @@ module Badline
|
|
|
71
79
|
crtc = nil unless crtc.nil? || CRTC.reads?(crtc)
|
|
72
80
|
refresh_hz = refresh_hz(crtc, profile)
|
|
73
81
|
profile = Badline::PET::Model.upgraded(profile, nil, editor(crtc, profile, refresh_hz))
|
|
82
|
+
superpet = profile.superpet?
|
|
83
|
+
cpu_switch = PETMem.m6809?(memory) ? "6809" : "6502"
|
|
74
84
|
PETSetup.new(model: name, ram_kb:, refresh_hz:, basic: profile.basic, editor: profile.editor(refresh_hz),
|
|
75
|
-
kernal: profile.kernal, characters: profile.characters, jumpers: ""
|
|
85
|
+
kernal: profile.kernal, characters: profile.characters, jumpers: "",
|
|
86
|
+
cpu_switch: superpet ? cpu_switch : "", ram_switch: superpet ? "rw" : "")
|
|
76
87
|
end
|
|
77
88
|
|
|
78
89
|
def memory(container)
|
|
@@ -90,8 +101,7 @@ module Badline
|
|
|
90
101
|
name = MODELS[board]
|
|
91
102
|
return name unless name.nil?
|
|
92
103
|
|
|
93
|
-
|
|
94
|
-
raise FormatError, "#{memory}: #{what}, which badline doesn't build"
|
|
104
|
+
raise FormatError, "#{memory}: board #{board}, which badline doesn't build"
|
|
95
105
|
end
|
|
96
106
|
|
|
97
107
|
# The editor's frame rate, from the cycles the CRTC's registers set
|
|
@@ -26,13 +26,20 @@ module Badline
|
|
|
26
26
|
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
27
27
|
|
|
28
28
|
def export(machine)
|
|
29
|
+
sources = [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?]
|
|
30
|
+
sources << machine.acia_irq? if machine.superpet?
|
|
31
|
+
write(machine, sources.count(true))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The module for `machine`, whose CPU's IRQ line `irqs` sources
|
|
35
|
+
# pull.
|
|
36
|
+
def write(machine, irqs)
|
|
29
37
|
cpu = machine.cpu
|
|
30
38
|
fields = FieldWriter.new.qword(machine.cycles)
|
|
31
39
|
[cpu.a, cpu.x, cpu.y, cpu.stack_pointer].each { |register| fields.byte(register) }
|
|
32
40
|
fields.word(cpu.program_counter).byte(cpu.p)
|
|
33
41
|
fields.dword(0).dword(0).dword(0)
|
|
34
42
|
5.times { fields.qword(0) }
|
|
35
|
-
irqs = [machine.pia1.irq?, machine.pia2.irq?, machine.via.irq?].count(true)
|
|
36
43
|
fields.dword(irqs).dword(0).dword(irqs.positive? ? IK_IRQ : 0)
|
|
37
44
|
fields.section(NAME, MAJOR, MINOR)
|
|
38
45
|
end
|
|
@@ -49,16 +56,46 @@ module Badline
|
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
# The SuperPET's 6809, CPU6809 1.1, which xpet saves on every model
|
|
52
|
-
# and won't load a snapshot without: the clock
|
|
53
|
-
#
|
|
54
|
-
#
|
|
59
|
+
# and won't load a snapshot without: the clock, X, Y, U, S and PC,
|
|
60
|
+
# DP, CC, A and B, then five bytes. xpet 3.10's layout was inferred
|
|
61
|
+
# from its snapshots of a 6809 running a loop that loads each
|
|
62
|
+
# register with a value of its own; the five bytes are 0 in every
|
|
63
|
+
# snapshot xpet wrote.
|
|
64
|
+
#
|
|
65
|
+
# Written: the clock and the registers, the SuperPET's 6809's
|
|
66
|
+
# between instructions while it has the bus, and otherwise the
|
|
67
|
+
# registers of a 6809 that hasn't run, as xpet leaves them. Read:
|
|
68
|
+
# the registers, with the 6809 on an instruction boundary, as
|
|
69
|
+
# xpet's snapshot is taken on one.
|
|
55
70
|
module CPU6809
|
|
56
71
|
NAME = "CPU6809"
|
|
72
|
+
MAJOR = 1
|
|
73
|
+
MINOR = 1
|
|
74
|
+
# The CC an xpet without a SuperPET board saves.
|
|
75
|
+
IDLE_CC = 0x04
|
|
57
76
|
|
|
58
77
|
module_function
|
|
59
78
|
|
|
79
|
+
def reads?(section) = section.major == MAJOR && section.minor == MINOR
|
|
80
|
+
|
|
60
81
|
def export(machine)
|
|
61
|
-
FieldWriter.new.qword(machine.cycles)
|
|
82
|
+
fields = FieldWriter.new.qword(machine.cycles)
|
|
83
|
+
unless machine.superpet? && machine.m6809_running?
|
|
84
|
+
return fields.zeros(11).byte(IDLE_CC).zeros(7).section(NAME, MAJOR, MINOR)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
cpu = machine.m6809
|
|
88
|
+
[cpu.x, cpu.y, cpu.u, cpu.s, cpu.pc].each { |register| fields.word(register) }
|
|
89
|
+
fields.byte(cpu.dp).byte(cpu.cc).byte(cpu.a).byte(cpu.b).zeros(5).section(NAME, MAJOR, MINOR)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# The registers by their BL-M6809 keys, in a keyed module's writer,
|
|
93
|
+
# with the 6809's cycle count the machine's `cycles`.
|
|
94
|
+
def import(section, out, cycles)
|
|
95
|
+
fields = FieldReader.new(section).skip(8)
|
|
96
|
+
%w[x y u s pc].each { |name| out.int(name, fields.word) }
|
|
97
|
+
%w[dp cc a b].each { |name| out.int(name, fields.byte) }
|
|
98
|
+
out.int("cycles", cycles).bool("nmi.armed", true)
|
|
62
99
|
end
|
|
63
100
|
end
|
|
64
101
|
end
|
|
@@ -6,7 +6,7 @@ module Badline
|
|
|
6
6
|
module PET
|
|
7
7
|
# CRTC 1.2, which xpet saves on every model, the discrete boards'
|
|
8
8
|
# video too, as a 6545 with the registers fixed: the board's screen
|
|
9
|
-
# settings, the registers, the one selected, the beam's character,
|
|
9
|
+
# settings, the character set VIA CA2 picks, the registers, the one selected, the beam's character,
|
|
10
10
|
# row and line in the row, whether the frame is in its vertical
|
|
11
11
|
# adjust, the row's start in screen RAM, the lines of vertical sync
|
|
12
12
|
# left, whether the rows show, the picture's size, the lines in the
|
|
@@ -36,6 +36,13 @@ module Badline
|
|
|
36
36
|
SETTINGS = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x1f].freeze
|
|
37
37
|
# The 8296's, whose screen RAM spans 4K.
|
|
38
38
|
SETTINGS_8296 = [0xff, 0x0f, 0x00, 0x20, 0x00, 0x20, 0xff, 0x0f, 0xff, 0x1f].freeze
|
|
39
|
+
# The SuperPET's, whose character ROM spans 4K, inferred from
|
|
40
|
+
# xpet 3.10's SuperPET snapshots.
|
|
41
|
+
SETTINGS_SUPERPET = [0xff, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0xff, 0x3f].freeze
|
|
42
|
+
# The word after the settings while VIA CA2 is high, picking the
|
|
43
|
+
# character ROM's second set, as xpet 3.10 writes it and paints
|
|
44
|
+
# the picture from on every PET board.
|
|
45
|
+
SECOND_SET = 0x1000
|
|
39
46
|
# The 2001's and 3000 series' video as xpet's CRTC runs it: 64
|
|
40
47
|
# cycles a line, 32 rows of 8 lines and 4 more, the first 25 rows
|
|
41
48
|
# shown.
|
|
@@ -82,9 +89,10 @@ module Badline
|
|
|
82
89
|
beam = if discrete
|
|
83
90
|
discrete_beam(video_input)
|
|
84
91
|
else
|
|
85
|
-
crtc_beam(run_on(machine, crtc_input, -CRTC_LEAD % frame_cycles(registers)),
|
|
92
|
+
crtc_beam(run_on(fresh_crtc(machine), crtc_input, -CRTC_LEAD % frame_cycles(registers)),
|
|
93
|
+
registers)
|
|
86
94
|
end
|
|
87
|
-
fields = FieldWriter.new.bytes(model.
|
|
95
|
+
fields = FieldWriter.new.bytes(settings(model)).word(machine.via.ca2_output ? SECOND_SET : 0)
|
|
88
96
|
fields.byte(0).byte(columns(model))
|
|
89
97
|
fields.byte(model.basic1? ? 1 : 0).bytes(registers).byte(discrete ? 0 : crtc_input.int("selected", 0))
|
|
90
98
|
beam_fields(fields, beam, registers, model)
|
|
@@ -92,6 +100,12 @@ module Badline
|
|
|
92
100
|
fields.section(NAME, MAJOR, MINOR)
|
|
93
101
|
end
|
|
94
102
|
|
|
103
|
+
def settings(model)
|
|
104
|
+
return SETTINGS_8296 if model.cbm8296?
|
|
105
|
+
|
|
106
|
+
model.superpet? ? SETTINGS_SUPERPET : SETTINGS
|
|
107
|
+
end
|
|
108
|
+
|
|
95
109
|
def crtc_registers(input)
|
|
96
110
|
registers = input.bytes("registers", "".b).bytes
|
|
97
111
|
Array.new(REGISTERS) { |index| registers[index] || 0 }
|
|
@@ -119,10 +133,12 @@ module Badline
|
|
|
119
133
|
|
|
120
134
|
def frame_cycles(registers) = (registers[0] + 1) * frame_lines(registers)
|
|
121
135
|
|
|
122
|
-
# The
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
# The CRTC of a machine built as `machine` was, at power-on.
|
|
137
|
+
def fresh_crtc(machine) = PETSetup.of(machine).build.crtc
|
|
138
|
+
|
|
139
|
+
# The BL-CRTC fields of `crtc`, a CRTC of its own, loaded with those
|
|
140
|
+
# in `input` and run `cycles` cycles.
|
|
141
|
+
def run_on(crtc, input, cycles)
|
|
126
142
|
crtc.load_fields(input)
|
|
127
143
|
cycles.times { crtc.cycle! }
|
|
128
144
|
Keyed::Reader.parse(Keyed.section("BL-CRTC") { |out| crtc.save_fields(out) })
|
|
@@ -135,16 +151,13 @@ module Badline
|
|
|
135
151
|
%w[adjusting row_display column_display].each { |key| out.bool(key, input.bool(key, false)) }
|
|
136
152
|
end
|
|
137
153
|
|
|
138
|
-
# Both move a row's start on at R1 of its last line
|
|
139
|
-
# moves it again where the vertical adjust runs past R9 lines,
|
|
140
|
-
# which xpet's doesn't.
|
|
154
|
+
# Both move a row's start on at R1 of its last line.
|
|
141
155
|
def crtc_beam(input, registers)
|
|
142
156
|
column = input.int("column", 0)
|
|
143
157
|
row = input.int("row", 0)
|
|
144
158
|
line = input.int("line", 0)
|
|
145
159
|
adjusting = input.bool("adjusting", false)
|
|
146
160
|
start = input.int("row_start", 0)
|
|
147
|
-
start = (start - registers[1]) & 0x3fff if adjusting && moved?(line, column, registers)
|
|
148
161
|
rows = registers[4] + 1
|
|
149
162
|
lines = registers[9] + 1
|
|
150
163
|
Beam.new(column:, row: adjusting ? rows : row, raster: adjusting ? 0 : line,
|
|
@@ -212,12 +225,7 @@ module Badline
|
|
|
212
225
|
adjusting = beam.row >= rows
|
|
213
226
|
line = adjusting ? beam.frame_line - (rows * (registers[9] + 1)) : beam.raster
|
|
214
227
|
start = start_address(beam.start, registers, columns, attributes)
|
|
215
|
-
if adjusting
|
|
216
|
-
start += registers[1] if line > registers[9]
|
|
217
|
-
elsif moved?(line, column_of(section), registers)
|
|
218
|
-
start -= registers[1]
|
|
219
|
-
end
|
|
220
|
-
start &= 0x3fff
|
|
228
|
+
start = (start - registers[1]) & 0x3fff if !adjusting && moved?(line, column_of(section), registers)
|
|
221
229
|
out.bytes("registers", registers.first(Chips::CRTC::MASKS.length)).int("selected", selected(section) & 0x1f)
|
|
222
230
|
out.int("column", 0).int("line", [line, 0].max).int("row", adjusting ? rows - 1 : beam.row)
|
|
223
231
|
out.int("since_hsync", registers[0] + 1 - registers[2]).int("vsync_lines", beam.vsync)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Snapshot
|
|
5
|
+
module Vice
|
|
6
|
+
module PET
|
|
7
|
+
# A true 2031 on device 8, as xpet 3.10, xcbm2 and xcbm5x0 save it with true drive
|
|
8
|
+
# emulation: DRIVE8 to DRIVE11 2.0, one a unit, DRIVECPU0 1.3, the
|
|
9
|
+
# drive's 6502 and its RAM, and its VIAs as VIA 2.2 modules,
|
|
10
|
+
# 2031VIA1D0 at $1800 on the IEEE-488 bus and VIA2D0 at $1C00 on
|
|
11
|
+
# the mechanism.
|
|
12
|
+
#
|
|
13
|
+
# DRIVECPU0 holds the clock, the registers, the last opcode, the
|
|
14
|
+
# main CPU's clock, more clocks of the drive's interrupt bookkeeping,
|
|
15
|
+
# then the 2K of RAM from $5C and twelve bytes after it. DRIVE8
|
|
16
|
+
# holds the head's half track at $10, with track 18 at 36 as badline
|
|
17
|
+
# numbers them, the speed zone its VIA set at $24, $47 and $4B, the
|
|
18
|
+
# drive's type as a word at $28, and from $2C the disk's place in
|
|
19
|
+
# its turn and the clock it was last moved on at. The rest of it,
|
|
20
|
+
# the read electronics among it, badline doesn't read. xpet's head
|
|
21
|
+
# rests on a half track two phases on from where badline's stepper
|
|
22
|
+
# puts it (SLIP).
|
|
23
|
+
#
|
|
24
|
+
# Written: the registers and the RAM, the VIAs as the PET's VIA is
|
|
25
|
+
# written with the stepper's phase moved to xpet's, and DRIVE8 as
|
|
26
|
+
# xpet saves an idle 2031 but for the head's half track, the zone,
|
|
27
|
+
# and the disk at the start of its turn as of the snapshot's clock.
|
|
28
|
+
# The interrupt clocks are written as long past. The snapshot is
|
|
29
|
+
# settled with the drive's CPU between instructions too
|
|
30
|
+
# (PET#settle_for_vice!). One taken while the drive is mid-transfer
|
|
31
|
+
# on the bus may stall there in xpet, as xpet's own snapshots taken
|
|
32
|
+
# while it searches for a file do.
|
|
33
|
+
#
|
|
34
|
+
# Read: the registers, the RAM, the VIAs as the PET's VIA is read,
|
|
35
|
+
# and the head's half track, with the motor, the LED and the zone
|
|
36
|
+
# from VIA 2's port B and the stepper's phase as xpet's (SLIP). The
|
|
37
|
+
# disk turns on from where badline's left it, and the disk in the
|
|
38
|
+
# drive stays.
|
|
39
|
+
module Drive2031
|
|
40
|
+
UNITS = %w[DRIVE8 DRIVE9 DRIVE10 DRIVE11].freeze
|
|
41
|
+
DRIVE_MAJOR = 2
|
|
42
|
+
DRIVE_MINOR = 0
|
|
43
|
+
CPU = "DRIVECPU0"
|
|
44
|
+
CPU_MAJOR = 1
|
|
45
|
+
CPU_MINOR = 3
|
|
46
|
+
VIA1 = "2031VIA1D0"
|
|
47
|
+
VIA2 = "VIA2D0"
|
|
48
|
+
NAMES = [*UNITS, CPU, VIA1, VIA2].freeze
|
|
49
|
+
# Where DRIVECPU0's RAM starts, and its length.
|
|
50
|
+
RAM_AT = 0x5c
|
|
51
|
+
RAM_SIZE = 0x800
|
|
52
|
+
TAIL = 12
|
|
53
|
+
HALF_TRACK = 0x10
|
|
54
|
+
ZONES = [0x24, 0x47, 0x4b].freeze
|
|
55
|
+
SIZE = 145
|
|
56
|
+
# The disk's place in its turn, and the clock it was last moved on at.
|
|
57
|
+
TURN = 0x2c
|
|
58
|
+
# DRIVE8 as xpet saves an idle 2031, and a unit with no drive, by
|
|
59
|
+
# the bytes that aren't 0.
|
|
60
|
+
IDLE = { 0x00 => 0x01, 0x01 => 0x01, 0x02 => 0x01, 0x0f => 0x01, 0x10 => 0x25, 0x1a => 0x01, 0x1b => 0xdf,
|
|
61
|
+
0x1f => 0x54, 0x20 => 0x55, 0x24 => 0x03, 0x28 => 0xef, 0x29 => 0x07, 0x2c => 0x60, 0x2d => 0x41,
|
|
62
|
+
0x2e => 0x0f, 0x30 => 0x17, 0x31 => 0x51, 0x32 => 0x0f, 0x40 => 0x54, 0x41 => 0x01, 0x42 => 0x54,
|
|
63
|
+
0x47 => 0x03, 0x4b => 0x03, 0x6f => 0xcd, 0x70 => 0xab, 0x71 => 0x34, 0x72 => 0x12 }.freeze
|
|
64
|
+
NONE = { 0x00 => 0x01, 0x01 => 0x01, 0x02 => 0x01, 0x0e => 0x01, 0x0f => 0x01, 0x10 => 0x24, 0x1a => 0x01,
|
|
65
|
+
0x20 => 0x55, 0x6f => 0xcd, 0x70 => 0xab, 0x71 => 0x34, 0x72 => 0x12, 0x8f => 0x01 }.freeze
|
|
66
|
+
SLIP = 2
|
|
67
|
+
MOTOR = 0x04
|
|
68
|
+
LED = 0x08
|
|
69
|
+
|
|
70
|
+
module_function
|
|
71
|
+
|
|
72
|
+
# Whether the snapshot has a true 2031.
|
|
73
|
+
def present?(container) = !container[CPU].nil? && !container[VIA1].nil?
|
|
74
|
+
|
|
75
|
+
def reads?(section)
|
|
76
|
+
case section.name
|
|
77
|
+
when CPU then section.major == CPU_MAJOR && section.minor == CPU_MINOR
|
|
78
|
+
when VIA1, VIA2 then VIA.reads?(section)
|
|
79
|
+
else section.major == DRIVE_MAJOR && section.minor == DRIVE_MINOR
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# xpet's modules for the 2031 whose BL-D8 modules `readers` holds,
|
|
84
|
+
# and none without one or for an 8050 or 8250, which they leave
|
|
85
|
+
# out.
|
|
86
|
+
def export(machine, readers)
|
|
87
|
+
drive = readers["BL-D8"]
|
|
88
|
+
return [] if drive.nil? || drive.text("model", "2031") != "2031"
|
|
89
|
+
|
|
90
|
+
via2 = readers.fetch("BL-D8-VIA2")
|
|
91
|
+
mechanism = readers.fetch("BL-D8-MECH")
|
|
92
|
+
zone = ((via2.int("orb", 0) | ~via2.int("ddrb", 0)) >> 5) & 0x03
|
|
93
|
+
idle = filled(IDLE)
|
|
94
|
+
idle[HALF_TRACK] = mechanism.int("half_track", 36)
|
|
95
|
+
ZONES.each { |offset| idle[offset] = zone }
|
|
96
|
+
idle[TURN, 8] = [0, machine.cycles & 0xffff_ffff].pack("VV").bytes
|
|
97
|
+
[FieldWriter.new.bytes(idle).section(UNITS.first, DRIVE_MAJOR, DRIVE_MINOR),
|
|
98
|
+
*UNITS.drop(1).map { |name| unit(NONE, name) },
|
|
99
|
+
cpu(machine, readers.fetch("BL-D8-CPU"), readers.fetch("BL-D8-RAM")),
|
|
100
|
+
stepper(VIA.export(via2, VIA2), mechanism.int("slip", 0)),
|
|
101
|
+
VIA.export(readers.fetch("BL-D8-VIA1"), VIA1)]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# VIA 2's port B with the stepper's phase as xpet's head takes it
|
|
105
|
+
# where badline's head is: SLIP on from badline's.
|
|
106
|
+
def stepper(section, slip)
|
|
107
|
+
bytes = section.data.bytes
|
|
108
|
+
bytes[2] = (bytes[2] & 0xfc) | ((bytes[2] - slip + SLIP) & 0x03)
|
|
109
|
+
section.with(data: bytes.pack("C*"))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def unit(bytes, name) = FieldWriter.new.bytes(filled(bytes)).section(name, DRIVE_MAJOR, DRIVE_MINOR)
|
|
113
|
+
|
|
114
|
+
def filled(bytes) = Array.new(SIZE) { |offset| bytes.fetch(offset, 0) }
|
|
115
|
+
|
|
116
|
+
def cpu(machine, input, ram)
|
|
117
|
+
bytes = ram.bytes("ram", "".b).bytes
|
|
118
|
+
cpu_section(machine, input, Array.new(RAM_SIZE) { |address| bytes[address] || 0 })
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# The drive's clock, at its 1 MHz, for the machine's at its own.
|
|
122
|
+
def drive_clock(machine) = machine.cycles * Drives::Model2031::CLOCK_HZ / machine.timing.clock_hz
|
|
123
|
+
|
|
124
|
+
# DRIVECPU0 for a drive CPU whose BL- fields `input` holds, with
|
|
125
|
+
# `ram` the drive's RAM as xpet lays it out: the drive's clock,
|
|
126
|
+
# the registers, the machine's clock, the drive's clock again, and
|
|
127
|
+
# the RAM.
|
|
128
|
+
def cpu_section(machine, input, ram)
|
|
129
|
+
clock = drive_clock(machine)
|
|
130
|
+
fields = FieldWriter.new.qword(clock)
|
|
131
|
+
%w[a x y sp].each { |key| fields.byte(input.int(key, 0)) }
|
|
132
|
+
fields.word(input.int("pc", 0)).byte(input.int("p", 0x20)).dword(0)
|
|
133
|
+
fields.qword(machine.cycles).zeros(16).qword(clock).zeros(1).qword(0).zeros(8).qword(0)
|
|
134
|
+
fields.zeros(16).bytes(ram).zeros(TAIL)
|
|
135
|
+
fields.section(CPU, CPU_MAJOR, CPU_MINOR)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# The 2031's keyed modules, named for device 8, from the
|
|
139
|
+
# snapshot's, with `disk` the drive's disk module badline keeps,
|
|
140
|
+
# or nil.
|
|
141
|
+
def import(container, disk)
|
|
142
|
+
via2 = container[VIA2]
|
|
143
|
+
sections = [Keyed.section("BL-D8") { |out| drive(container, out) },
|
|
144
|
+
Keyed.section("BL-D8-RAM") { |out| out.bytes("ram", ram(container[CPU])) },
|
|
145
|
+
Keyed.section("BL-D8-VIA1") { |out| VIA.import(container[VIA1], out) },
|
|
146
|
+
Keyed.section("BL-D8-VIA2") { |out| VIA.import(via2, out) },
|
|
147
|
+
Keyed.section("BL-D8-CPU") { |out| registers(container[CPU], out) },
|
|
148
|
+
Keyed.section("BL-D8-MECH") { |out| mechanism(container, via2, out) }]
|
|
149
|
+
disk ? sections + [disk] : sections
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def drive(container, out)
|
|
153
|
+
out.text("model", "2031").int("phase", 0).int("cycles", FieldReader.new(container[CPU]).qword)
|
|
154
|
+
out.bool("so_pending", false)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def ram(section) = FieldReader.new(section).skip(RAM_AT).string(RAM_SIZE)
|
|
158
|
+
|
|
159
|
+
def registers(section, out)
|
|
160
|
+
fields = FieldReader.new(section)
|
|
161
|
+
clock = fields.qword
|
|
162
|
+
out.int("a", fields.byte).int("x", fields.byte).int("y", fields.byte).int("sp", fields.byte)
|
|
163
|
+
out.int("pc", fields.word).int("p", fields.byte).int("cycles", clock)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# The head where DRIVE8 has it, and the stepper's phase as VIA 2
|
|
167
|
+
# drives it, which the half track's low two bits stand for.
|
|
168
|
+
def mechanism(container, via2, out)
|
|
169
|
+
half_track = FieldReader.new(container[UNITS.first]).skip(HALF_TRACK).byte
|
|
170
|
+
bytes = FieldReader.new(via2).bytes(4)
|
|
171
|
+
port_b = bytes[2] | (~bytes[3] & 0xff)
|
|
172
|
+
out.int("half_track", half_track).int("slip", SLIP)
|
|
173
|
+
out.int("zone", (port_b >> 5) & 0x03)
|
|
174
|
+
out.bool("motor", port_b.anybits?(MOTOR)).bool("led", port_b.anybits?(LED))
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|