badline 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -1
- data/CLAUDE.md +42 -12
- data/CONTRIBUTING.md +4 -3
- data/README.md +76 -13
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +174 -0
- data/doc/snapshot-format.md +1197 -0
- data/doc/snapshots.md +90 -40
- data/lib/badline/audio/bare_player.rb +46 -17
- data/lib/badline/audio/machine_player.rb +6 -10
- data/lib/badline/audio/renderer.rb +32 -21
- data/lib/badline/c128/banks.rb +7 -6
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +36 -4
- data/lib/badline/c128/drives.rb +6 -25
- data/lib/badline/c128/keyed_modules.rb +117 -0
- data/lib/badline/c128/keyed_state.rb +91 -0
- data/lib/badline/c128/keys.rb +9 -0
- data/lib/badline/c128/mmu.rb +9 -8
- data/lib/badline/c128/mmu_pages.rb +27 -0
- data/lib/badline/c128/modes.rb +19 -9
- data/lib/badline/c128/z80_turns.rb +1 -1
- data/lib/badline/c128.rb +8 -8
- data/lib/badline/c64/attachments.rb +0 -44
- data/lib/badline/c64/banked_ram/plus256k.rb +18 -10
- data/lib/badline/c64/banked_ram/plus60k.rb +8 -6
- data/lib/badline/c64/banked_ram/unexpanded.rb +2 -2
- data/lib/badline/c64/bus/debug_register.rb +32 -0
- data/lib/badline/c64/bus/extra_sids.rb +80 -0
- data/lib/badline/c64/bus/keyed_state.rb +41 -0
- data/lib/badline/c64/bus.rb +3 -1
- data/lib/badline/c64/cartridge/action_replay.rb +6 -15
- data/lib/badline/c64/cartridge/comal80.rb +1 -0
- data/lib/badline/c64/cartridge/dinamic.rb +3 -1
- data/lib/badline/c64/cartridge/easy_flash.rb +0 -15
- data/lib/badline/c64/cartridge/epyx_fastload.rb +0 -8
- data/lib/badline/c64/cartridge/final_cartridge3.rb +1 -12
- data/lib/badline/c64/cartridge/flash.rb +3 -3
- data/lib/badline/c64/cartridge/fun_play.rb +2 -1
- data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
- data/lib/badline/c64/cartridge/game_system.rb +2 -1
- data/lib/badline/c64/cartridge/geo_ram.rb +0 -16
- data/lib/badline/c64/cartridge/kcs_power.rb +0 -8
- data/lib/badline/c64/cartridge/keyed_state.rb +514 -0
- data/lib/badline/c64/cartridge/magic_desk.rb +2 -1
- data/lib/badline/c64/cartridge/ocean.rb +1 -0
- data/lib/badline/c64/cartridge/pagefox.rb +1 -10
- data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
- data/lib/badline/c64/cartridge/rgcd.rb +2 -11
- data/lib/badline/c64/cartridge/super_games.rb +3 -12
- data/lib/badline/c64/cartridge/zaxxon.rb +1 -16
- data/lib/badline/c64/cartridge.rb +4 -30
- data/lib/badline/c64/kernal_traps.rb +28 -25
- data/lib/badline/c64/keyed_modules.rb +115 -0
- data/lib/badline/c64/keyed_state.rb +68 -0
- data/lib/badline/c64/reu/dma.rb +0 -4
- data/lib/badline/c64/reu/keyed_state.rb +140 -0
- data/lib/badline/c64/reu/ram.rb +0 -15
- data/lib/badline/c64/reu.rb +1 -3
- data/lib/badline/c64/true_drives.rb +56 -5
- data/lib/badline/c64.rb +7 -3
- data/lib/badline/chips/cia/interrupt_register.rb +0 -17
- data/lib/badline/chips/cia/keyed_parts.rb +213 -0
- data/lib/badline/chips/cia/serial.rb +0 -4
- data/lib/badline/chips/cia/time_of_day.rb +0 -22
- data/lib/badline/chips/cia/timer.rb +0 -20
- data/lib/badline/chips/cia.rb +2 -2
- data/lib/badline/chips/cpu/core.rb +18 -2
- data/lib/badline/chips/cpu/keyed_state.rb +177 -0
- data/lib/badline/chips/cpu/microcode.rb +16 -0
- data/lib/badline/chips/cpu/port.rb +20 -3
- data/lib/badline/chips/cpu.rb +5 -0
- data/lib/badline/chips/sid/decimator.rb +10 -8
- data/lib/badline/chips/sid/envelope.rb +1 -6
- data/lib/badline/chips/sid/filter.rb +0 -14
- data/lib/badline/chips/sid/keyed_parts.rb +286 -0
- data/lib/badline/chips/sid/voice.rb +0 -10
- data/lib/badline/chips/sid/waveform.rb +0 -2
- data/lib/badline/chips/sid.rb +1 -2
- data/lib/badline/chips/ted/beam.rb +118 -0
- data/lib/badline/chips/ted/fetches.rb +199 -0
- data/lib/badline/chips/ted/keyed_state.rb +156 -0
- data/lib/badline/chips/ted/modes.rb +134 -0
- data/lib/badline/chips/ted/palette.rb +43 -0
- data/lib/badline/chips/ted/picture.rb +182 -0
- data/lib/badline/chips/ted/sound/output.rb +86 -0
- data/lib/badline/chips/ted/sound.rb +284 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +190 -0
- data/lib/badline/chips/tia.rb +61 -0
- data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
- data/lib/badline/chips/vdc/painter.rb +0 -12
- data/lib/badline/chips/vdc/raster.rb +0 -16
- data/lib/badline/chips/vdc/window.rb +0 -9
- data/lib/badline/chips/vdc.rb +1 -2
- data/lib/badline/chips/via/control_lines.rb +0 -15
- data/lib/badline/chips/via/interrupt_register.rb +0 -9
- data/lib/badline/chips/via/keyed_parts.rb +163 -0
- data/lib/badline/chips/via/shift_register.rb +0 -14
- data/lib/badline/chips/via/timer1.rb +0 -13
- data/lib/badline/chips/via/timer2.rb +0 -18
- data/lib/badline/chips/via.rb +1 -2
- data/lib/badline/chips/vic/border_mask.rb +0 -10
- data/lib/badline/chips/vic/collisions.rb +0 -11
- data/lib/badline/chips/vic/color_patches.rb +0 -11
- data/lib/badline/chips/vic/display_state.rb +0 -19
- data/lib/badline/chips/vic/graphics_shifter.rb +0 -4
- data/lib/badline/chips/vic/keyed_parts.rb +228 -0
- data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
- data/lib/badline/chips/vic/keyed_state.rb +102 -0
- data/lib/badline/chips/vic/register_log.rb +0 -18
- data/lib/badline/chips/vic/registers.rb +0 -9
- data/lib/badline/chips/vic/sequencer.rb +0 -2
- data/lib/badline/chips/vic/sprite/internal_bus.rb +0 -11
- data/lib/badline/chips/vic/sprite.rb +0 -2
- data/lib/badline/chips/vic/sprites.rb +0 -3
- data/lib/badline/chips/vic.rb +6 -2
- data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
- data/lib/badline/chips/vic_i/painter.rb +0 -4
- data/lib/badline/chips/vic_i/sound.rb +38 -46
- data/lib/badline/chips/vic_i.rb +2 -2
- data/lib/badline/chips/z80/core.rb +2 -2
- data/lib/badline/chips/z80/keyed_state.rb +73 -0
- data/lib/badline/chips.rb +10 -0
- data/lib/badline/core.rb +4 -21
- data/lib/badline/drives/datasette/keyed_state.rb +43 -0
- data/lib/badline/drives/datasette.rb +62 -12
- data/lib/badline/drives/engine/core.rb +3 -0
- data/lib/badline/drives/engine/serial_lines.rb +4 -7
- data/lib/badline/drives/model1541/bus.rb +0 -13
- data/lib/badline/drives/model1541/disk.rb +9 -8
- data/lib/badline/drives/model1541/keyed_state.rb +251 -0
- data/lib/badline/drives/model1541/mechanism.rb +0 -2
- data/lib/badline/drives/model1541.rb +1 -2
- data/lib/badline/drives/model1571/bus.rb +0 -13
- data/lib/badline/drives/model1571/keyed_state.rb +108 -0
- data/lib/badline/drives/model1571/wd1770.rb +0 -14
- data/lib/badline/drives/model1571.rb +1 -2
- data/lib/badline/drives/model1581/bus.rb +0 -13
- data/lib/badline/drives/model1581/disk.rb +0 -4
- data/lib/badline/drives/model1581/keyed_state.rb +280 -0
- data/lib/badline/drives/model1581/mechanism.rb +0 -21
- data/lib/badline/drives/model1581/slot.rb +0 -18
- data/lib/badline/drives/model1581/wd1772.rb +0 -2
- data/lib/badline/drives/model1581.rb +2 -2
- data/lib/badline/drives.rb +8 -0
- data/lib/badline/frontend/app.rb +3 -3
- data/lib/badline/frontend/boot.rb +4 -2
- data/lib/badline/frontend/controls.rb +25 -5
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/keys.rb +14 -2
- data/lib/badline/frontend/media_slots.rb +11 -1
- data/lib/badline/frontend/menu/menu_dialogs.rb +4 -3
- data/lib/badline/frontend/menu/menu_media.rb +13 -3
- data/lib/badline/frontend/menu/menu_pages.rb +8 -8
- data/lib/badline/frontend/menu/menu_ports.rb +13 -1
- data/lib/badline/frontend/screen.rb +33 -12
- data/lib/badline/frontend/screens.rb +2 -1
- data/lib/badline/frontend/snapshots.rb +12 -1
- data/lib/badline/frontend/sound.rb +4 -4
- data/lib/badline/frontend/timeline.rb +43 -12
- data/lib/badline/input/matrix_typist.rb +98 -0
- data/lib/badline/input.rb +1 -0
- data/lib/badline/kernal_trap/chrout.rb +45 -0
- data/lib/badline/kernal_trap/dos/channel.rb +0 -27
- data/lib/badline/kernal_trap/dos/channels.rb +0 -25
- data/lib/badline/kernal_trap/dos/memory.rb +0 -9
- data/lib/badline/kernal_trap/dos/status.rb +0 -8
- data/lib/badline/kernal_trap/dos/write_file.rb +0 -18
- data/lib/badline/kernal_trap/dos.rb +0 -19
- data/lib/badline/kernal_trap/file_routine.rb +4 -4
- data/lib/badline/kernal_trap/keyed_state.rb +150 -0
- data/lib/badline/kernal_trap/layout.rb +75 -11
- data/lib/badline/kernal_trap/load.rb +14 -16
- data/lib/badline/kernal_trap/routine.rb +12 -0
- data/lib/badline/kernal_trap/save.rb +8 -18
- data/lib/badline/kernal_trap/serial.rb +1 -17
- data/lib/badline/kernal_trap.rb +2 -0
- data/lib/badline/machine/keyboard_buffer.rb +49 -0
- data/lib/badline/machine/settling.rb +29 -0
- data/lib/badline/machine.rb +29 -6
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/media/plus4_media.rb +68 -0
- data/lib/badline/media/program.rb +20 -6
- data/lib/badline/media.rb +33 -12
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/options/family.rb +6 -2
- data/lib/badline/options/help.rb +66 -16
- data/lib/badline/options/table.rb +17 -2
- data/lib/badline/options/validation.rb +26 -2
- data/lib/badline/options.rb +42 -9
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +375 -0
- data/lib/badline/plus4/cassette_sense.rb +21 -0
- data/lib/badline/plus4/cpu.rb +10 -0
- data/lib/badline/plus4/cpu_port.rb +93 -0
- data/lib/badline/plus4/keyboard_port.rb +76 -0
- data/lib/badline/plus4/keyed_modules.rb +99 -0
- data/lib/badline/plus4/keyed_parts.rb +143 -0
- data/lib/badline/plus4/keyed_state.rb +41 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +240 -0
- data/lib/badline/roms/README +38 -0
- data/lib/badline/roms/plus4/basic.rom +0 -0
- data/lib/badline/roms/plus4/function-high.rom +0 -0
- data/lib/badline/roms/plus4/function-low.rom +0 -0
- data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
- data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
- data/lib/badline/snapshot/c128_setup.rb +9 -20
- data/lib/badline/snapshot/c64_setup.rb +28 -24
- data/lib/badline/snapshot/container.rb +6 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +113 -0
- data/lib/badline/snapshot/field_doc.rb +148 -0
- data/lib/badline/snapshot/image.rb +143 -28
- data/lib/badline/snapshot/keyed/cursor.rb +40 -0
- data/lib/badline/snapshot/keyed/modules.rb +66 -0
- data/lib/badline/snapshot/keyed/reader.rb +188 -0
- data/lib/badline/snapshot/keyed/recorder.rb +88 -0
- data/lib/badline/snapshot/keyed/report.rb +26 -0
- data/lib/badline/snapshot/keyed/writer.rb +55 -0
- data/lib/badline/snapshot/keyed.rb +64 -0
- data/lib/badline/snapshot/machine_state.rb +17 -64
- data/lib/badline/snapshot/plus4_setup.rb +39 -0
- data/lib/badline/snapshot/storage_setup.rb +29 -34
- data/lib/badline/snapshot/vic20_setup.rb +6 -15
- data/lib/badline/snapshot/vice/reu1764.rb +2 -2
- data/lib/badline/snapshot/vice.rb +7 -31
- data/lib/badline/snapshot.rb +48 -20
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/g64_image.rb +9 -4
- data/lib/badline/storage/tap.rb +57 -20
- data/lib/badline/support/checkpoint.rb +3 -2
- data/lib/badline/support/plus4_checkpoint.rb +42 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +3 -0
- data/lib/badline/vic20/bus/keyed_state.rb +52 -0
- data/lib/badline/vic20/bus.rb +1 -2
- data/lib/badline/vic20/keyed_modules.rb +73 -0
- data/lib/badline/vic20/keyed_state.rb +45 -0
- data/lib/badline/vic20.rb +5 -5
- data/lib/badline.rb +1 -0
- metadata +75 -42
- data/lib/badline/c128/bus/saved_state.rb +0 -53
- data/lib/badline/c128/saved_state.rb +0 -117
- data/lib/badline/c64/bus/saved_state.rb +0 -68
- data/lib/badline/c64/cartridge/flash/saved_state.rb +0 -40
- data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +0 -47
- data/lib/badline/c64/cartridge/saved_state.rb +0 -63
- data/lib/badline/c64/reu/dma/saved_state.rb +0 -52
- data/lib/badline/c64/reu/saved_state.rb +0 -55
- data/lib/badline/c64/saved_state.rb +0 -112
- data/lib/badline/chips/cia/saved_state.rb +0 -49
- data/lib/badline/chips/cia/serial/saved_state.rb +0 -45
- data/lib/badline/chips/cpu/saved_state.rb +0 -117
- data/lib/badline/chips/sid/envelope/saved_state.rb +0 -70
- data/lib/badline/chips/sid/filter/saved_state.rb +0 -42
- data/lib/badline/chips/sid/saved_state.rb +0 -52
- data/lib/badline/chips/sid/waveform/saved_state.rb +0 -68
- data/lib/badline/chips/vdc/saved_state.rb +0 -36
- data/lib/badline/chips/via/saved_state.rb +0 -44
- data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +0 -40
- data/lib/badline/chips/vic/saved_state.rb +0 -82
- data/lib/badline/chips/vic/sequencer/saved_state.rb +0 -91
- data/lib/badline/chips/vic/sprite/saved_state.rb +0 -66
- data/lib/badline/chips/vic/sprites/saved_state.rb +0 -59
- data/lib/badline/chips/vic_i/painter/saved_state.rb +0 -40
- data/lib/badline/chips/vic_i/saved_state.rb +0 -55
- data/lib/badline/chips/z80/saved_state.rb +0 -71
- data/lib/badline/chrout_trap.rb +0 -43
- data/lib/badline/debug_register.rb +0 -28
- data/lib/badline/drives/datasette/saved_state.rb +0 -37
- data/lib/badline/drives/model1541/disk/saved_state.rb +0 -88
- data/lib/badline/drives/model1541/mechanism/saved_state.rb +0 -72
- data/lib/badline/drives/model1541/saved_state.rb +0 -44
- data/lib/badline/drives/model1571/saved_state.rb +0 -47
- data/lib/badline/drives/model1581/disk/saved_state.rb +0 -89
- data/lib/badline/drives/model1581/saved_state.rb +0 -42
- data/lib/badline/drives/model1581/wd1772/saved_state.rb +0 -68
- data/lib/badline/keyboard_buffer.rb +0 -33
- data/lib/badline/snapshot/setup.rb +0 -31
- data/lib/badline/snapshot/state.rb +0 -179
- data/lib/badline/vic20/bus/saved_state.rb +0 -35
- data/lib/badline/vic20/saved_state.rb +0 -110
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model1581
|
|
6
|
+
class WD1772
|
|
7
|
+
PHASE_NAMES = %w[idle spin_up stepped settled search read_data write_gate write_data read_track track_index
|
|
8
|
+
write_track motor_idle index_interrupt settle_search read_address write_track_start
|
|
9
|
+
starting].freeze
|
|
10
|
+
MODE_NAMES = %w[read write address verify_track].freeze
|
|
11
|
+
|
|
12
|
+
# The controller in a keyed snapshot module (BL-D8-FDC): its own
|
|
13
|
+
# cycle count, the step of the command running and what the search
|
|
14
|
+
# is for by name, the registers and lines, the command's counters,
|
|
15
|
+
# and when its next step, its deadline and the next ID field fall,
|
|
16
|
+
# in its cycles from now, each absent when it never does.
|
|
17
|
+
def save_fields(out)
|
|
18
|
+
out.int("now", @now).text("phase", PHASE_NAMES.fetch(@phase)).text("mode", MODE_NAMES.fetch(@mode))
|
|
19
|
+
[["due_in", @due], ["deadline_in", @deadline], ["id_in", @id_at]].each do |key, at|
|
|
20
|
+
out.int(key, at - @now) unless at == NEVER
|
|
21
|
+
end
|
|
22
|
+
out.int("command", @command).int("track", @track).int("sector", @sector).int("data", @data)
|
|
23
|
+
out.int("status", @status).int("steps", @steps).int("count", @count).int("after_spin_up", @after_spin_up)
|
|
24
|
+
out.int("found", @found).int("index", @index).int("crc", @crc).int("write_id", @write_id).int("size", @size)
|
|
25
|
+
save_flag_fields(out)
|
|
26
|
+
out.bytes("bytes", @bytes).ints("syncs", @syncs.map { |sync| sync ? 1 : 0 })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def load_fields(input)
|
|
30
|
+
@now = input.int("now", @now)
|
|
31
|
+
@phase = PHASE_NAMES.index(input.choice("phase", PHASE_NAMES, "idle")).to_i
|
|
32
|
+
@mode = MODE_NAMES.index(input.choice("mode", MODE_NAMES, "read")).to_i
|
|
33
|
+
@due = at_fields(input, "due_in")
|
|
34
|
+
@deadline = at_fields(input, "deadline_in")
|
|
35
|
+
@id_at = at_fields(input, "id_in")
|
|
36
|
+
load_register_fields(input)
|
|
37
|
+
load_flag_fields(input)
|
|
38
|
+
@bytes = input.bytes("bytes", "".b).bytes
|
|
39
|
+
@syncs = input.ints("syncs", []).map { |sync| sync == 1 }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def at_fields(input, key)
|
|
45
|
+
cycles = input.optional_int(key)
|
|
46
|
+
cycles.nil? ? NEVER : @now + cycles
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def load_register_fields(input)
|
|
50
|
+
@command = input.byte("command", @command)
|
|
51
|
+
@track = input.byte("track", @track)
|
|
52
|
+
@sector = input.byte("sector", @sector)
|
|
53
|
+
@data = input.byte("data", @data)
|
|
54
|
+
@status = input.byte("status", @status)
|
|
55
|
+
@steps = input.int("steps", @steps)
|
|
56
|
+
@count = input.int("count", @count)
|
|
57
|
+
@after_spin_up = input.int("after_spin_up", @after_spin_up)
|
|
58
|
+
@found = input.int("found", @found)
|
|
59
|
+
@index = input.int("index", @index)
|
|
60
|
+
@crc = input.int("crc", @crc)
|
|
61
|
+
@write_id = input.int("write_id", @write_id)
|
|
62
|
+
@size = input.int("size", @size)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def save_flag_fields(out)
|
|
66
|
+
out.bool("type1", @type1).bool("drq", @drq).bool("intrq", @intrq).bool("motor_on", @motor_on)
|
|
67
|
+
out.bool("inward", @inward).bool("crc_good", @crc_good).bool("gate_open", @gate_open).bool("late", @late)
|
|
68
|
+
out.bool("settling", @settling)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def load_flag_fields(input)
|
|
72
|
+
@type1 = input.bool("type1", @type1)
|
|
73
|
+
@drq = input.bool("drq", @drq)
|
|
74
|
+
@intrq = input.bool("intrq", @intrq)
|
|
75
|
+
@motor_on = input.bool("motor_on", @motor_on)
|
|
76
|
+
@inward = input.bool("inward", @inward)
|
|
77
|
+
@crc_good = input.bool("crc_good", @crc_good)
|
|
78
|
+
@gate_open = input.bool("gate_open", @gate_open)
|
|
79
|
+
@late = input.bool("late", @late)
|
|
80
|
+
@settling = input.bool("settling", @settling)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class Mechanism
|
|
85
|
+
# The head, the motor and the disk-change switch in a keyed snapshot
|
|
86
|
+
# module (BL-D8-MECH), with the disk's angle as it stands `now`, on
|
|
87
|
+
# the controller's clock.
|
|
88
|
+
def save_fields(out, now)
|
|
89
|
+
out.int("cylinder", @cylinder).int("side", @side).int("angle", angle(now)).bool("motor", @motor)
|
|
90
|
+
out.bool("changed", @changed)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Puts `disk` under the head without flushing the one there, as a
|
|
94
|
+
# snapshot load replaces it.
|
|
95
|
+
def load_fields(input, disk, now)
|
|
96
|
+
@disk = disk
|
|
97
|
+
@cylinder = input.int("cylinder", @cylinder)
|
|
98
|
+
@side = input.masked("side", @side, 1)
|
|
99
|
+
@angle = input.int("angle", @angle)
|
|
100
|
+
@since = now
|
|
101
|
+
@motor = input.bool("motor", @motor)
|
|
102
|
+
@changed = input.bool("changed", @changed)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class Disk
|
|
107
|
+
# A disk's fields in a keyed snapshot module (BL-D8-DISK): the image
|
|
108
|
+
# file's path and how it was opened, the image as last flushed,
|
|
109
|
+
# embedded, each track the head wrote since the disk went in, sector
|
|
110
|
+
# by sector as the WD1772 sees them, and the tracks written since the
|
|
111
|
+
# last flush.
|
|
112
|
+
module KeyedState
|
|
113
|
+
# The image's path, expanded, and whether it was opened read-only,
|
|
114
|
+
# for a disk from Disk.open. Nil for a disk made another way.
|
|
115
|
+
attr_reader :path, :read_only
|
|
116
|
+
|
|
117
|
+
# Notes the image's path and how it was opened.
|
|
118
|
+
def opened(path, read_only)
|
|
119
|
+
@path = File.expand_path(path)
|
|
120
|
+
@read_only = read_only
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def save_fields(out)
|
|
125
|
+
path = @path
|
|
126
|
+
out.text("path", path) if path
|
|
127
|
+
out.bool("read_only", @read_only == true)
|
|
128
|
+
image = @image
|
|
129
|
+
out.text("image.path", image.path.to_s).bytes("image", image.contents) if image
|
|
130
|
+
changed = @changed.keys.sort
|
|
131
|
+
out.ints("changed", changed)
|
|
132
|
+
changed.each { |index| save_track_fields(out, "track#{index}.", @tracks[index]) }
|
|
133
|
+
out.ints("written", @written.keys)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def load_track_fields(input)
|
|
137
|
+
@changed.clear
|
|
138
|
+
input.ints("changed", []).each do |index|
|
|
139
|
+
@tracks[index] = load_track_sectors(input, "track#{index}.")
|
|
140
|
+
@changed[index] = true
|
|
141
|
+
end
|
|
142
|
+
@written.clear
|
|
143
|
+
input.ints("written", []).each { |index| @written[index] = true }
|
|
144
|
+
self
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def save_track_fields(out, prefix, track)
|
|
150
|
+
out.int("#{prefix}sectors", track.sectors.length)
|
|
151
|
+
track.sectors.each_with_index do |sector, number|
|
|
152
|
+
save_field_fields(out, "#{prefix}sector#{number}.id.", sector.id)
|
|
153
|
+
data = sector.data
|
|
154
|
+
save_field_fields(out, "#{prefix}sector#{number}.data.", data) if data
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def save_field_fields(out, prefix, field)
|
|
159
|
+
out.int("#{prefix}mark", field.mark).bytes("#{prefix}bytes", field.bytes)
|
|
160
|
+
out.bool("#{prefix}good", field.good).bool("#{prefix}deleted", field.deleted)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def load_track_sectors(input, prefix)
|
|
164
|
+
Track.new(Array.new(input.int("#{prefix}sectors", 0)) do |number|
|
|
165
|
+
id = load_field_fields(input, "#{prefix}sector#{number}.id.")
|
|
166
|
+
data = input.optional_int("#{prefix}sector#{number}.data.mark")
|
|
167
|
+
Track::Sector.new(id, data.nil? ? nil : load_field_fields(input, "#{prefix}sector#{number}.data."))
|
|
168
|
+
end)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def load_field_fields(input, prefix)
|
|
172
|
+
Track::Field.new(input.int("#{prefix}mark", 0), input.bytes("#{prefix}bytes", "".b).bytes,
|
|
173
|
+
input.bool("#{prefix}good", true), input.bool("#{prefix}deleted", false))
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
include KeyedState
|
|
178
|
+
|
|
179
|
+
# The disk a keyed snapshot module describes: its embedded image,
|
|
180
|
+
# with the tracks the module holds laid over it. The image takes
|
|
181
|
+
# writes back to its file only while that file is there and the
|
|
182
|
+
# reader isn't `detached`. With no image, the disk is the tracks
|
|
183
|
+
# alone.
|
|
184
|
+
def self.from_fields(input, detached: false)
|
|
185
|
+
path = input.text("path", "")
|
|
186
|
+
read_only = input.bool("read_only", false)
|
|
187
|
+
bytes = input.bytes("image", "".b).bytes
|
|
188
|
+
image_path = input.text("image.path", "")
|
|
189
|
+
disk = bytes.empty? ? Disk.new : Disk.new(image_from(image_path, bytes, read_only || detached))
|
|
190
|
+
disk.opened(path, read_only || bytes.empty?) unless path.empty?
|
|
191
|
+
disk.load_track_fields(input)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.image_from(path, bytes, read_only)
|
|
195
|
+
Storage::D81Image.new(File.file?(path) ? path : "", read_only:, bytes:)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class Bus
|
|
200
|
+
# The drive's RAM and the byte last on its data bus, in a keyed
|
|
201
|
+
# snapshot module (BL-D8-RAM).
|
|
202
|
+
def save_fields(out)
|
|
203
|
+
out.int("data", @data).bytes("ram", @ram.read(0, @ram.length))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def load_fields(input)
|
|
207
|
+
@data = input.byte("data", @data)
|
|
208
|
+
ram = @ram.read(0, @ram.length)
|
|
209
|
+
@ram.write(0, input.bytes_into("ram", ram))
|
|
210
|
+
@watching = false
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# The drive's own fields in a keyed snapshot module (BL-D8): its
|
|
215
|
+
# model, its clock phase and cycle count, the ATN level it last saw
|
|
216
|
+
# and the host's lines as the serial port latched them. The fast
|
|
217
|
+
# serial direction and the ports' lines come from the 8520 once it
|
|
218
|
+
# is loaded.
|
|
219
|
+
module KeyedState
|
|
220
|
+
def save_fields(out)
|
|
221
|
+
settle!
|
|
222
|
+
out.text("model", model_name).int("phase", @phase).int("cycles", @cycles).bool("atn", @atn)
|
|
223
|
+
@serial_port.save_fields(out, "serial.")
|
|
224
|
+
out
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def load_fields(input)
|
|
228
|
+
settle!
|
|
229
|
+
input.choice("model", [model_name], model_name)
|
|
230
|
+
@phase = input.int("phase", @phase)
|
|
231
|
+
@cycles = input.int("cycles", @cycles)
|
|
232
|
+
@atn = input.bool("atn", @atn)
|
|
233
|
+
@serial_port.load_fields(input, "serial.")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def restored!
|
|
237
|
+
@fast_serial_out = @cia.port_b_lines.anybits?(FAST_SERIAL_OUT)
|
|
238
|
+
ports_written
|
|
239
|
+
push_fast_output(@fast_serial_out ? fast_pins : 0)
|
|
240
|
+
forget_orbits
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# The drive as modules of its own, as the 1541's, with the 8520 and
|
|
244
|
+
# the WD1772 in place of the VIAs.
|
|
245
|
+
def save_modules(sections, name)
|
|
246
|
+
sections << Snapshot::Keyed.section(name) { |out| save_fields(out) }
|
|
247
|
+
sections << Snapshot::Keyed.section("#{name}-RAM") { |out| @bus.save_fields(out) }
|
|
248
|
+
sections << Snapshot::Keyed.section("#{name}-CIA") { |out| @cia.save_fields(out) }
|
|
249
|
+
sections << Snapshot::Keyed.section("#{name}-CPU") { |out| @cpu.save_fields(out) }
|
|
250
|
+
sections << Snapshot::Keyed.section("#{name}-FDC") { |out| @fdc.save_fields(out) }
|
|
251
|
+
now = @fdc.now
|
|
252
|
+
sections << Snapshot::Keyed.section("#{name}-MECH") { |out| @mechanism.save_fields(out, now) }
|
|
253
|
+
disk = @mechanism.disk
|
|
254
|
+
sections << Snapshot::Keyed.section("#{name}-DISK") { |out| disk.save_fields(out) } if disk
|
|
255
|
+
sections
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# The drive from its modules, `input` being its own. The mechanism
|
|
259
|
+
# takes the disk's angle on the WD1772's clock, so it loads after
|
|
260
|
+
# the controller.
|
|
261
|
+
def load_modules(modules, input, name)
|
|
262
|
+
load_fields(input)
|
|
263
|
+
@bus.load_fields(modules.required("#{name}-RAM"))
|
|
264
|
+
@cia.load_fields(modules.required("#{name}-CIA"))
|
|
265
|
+
@cpu.load_fields(modules.required("#{name}-CPU"))
|
|
266
|
+
@fdc.load_fields(modules.required("#{name}-FDC"))
|
|
267
|
+
disk = modules.optional("#{name}-DISK")
|
|
268
|
+
mechanism = modules.required("#{name}-MECH")
|
|
269
|
+
@mechanism.load_fields(mechanism, disk.nil? ? nil : Disk.from_fields(disk, detached: modules.detached?),
|
|
270
|
+
@fdc.now)
|
|
271
|
+
@cia.restored!
|
|
272
|
+
@cpu.restored!
|
|
273
|
+
restored!
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
include KeyedState
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
@@ -92,27 +92,6 @@ module Badline
|
|
|
92
92
|
def write(track)
|
|
93
93
|
@disk&.write(@cylinder, @side, track)
|
|
94
94
|
end
|
|
95
|
-
|
|
96
|
-
# The disk (Disk#save_state) and the motor, the head and the turn.
|
|
97
|
-
def save_state(out)
|
|
98
|
-
out.boolean(!@disk.nil?)
|
|
99
|
-
@disk&.save_state(out)
|
|
100
|
-
out.int(@cylinder).int(@side).int(@angle).int(@since).boolean(@motor).boolean(@changed)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Puts the state back, reusing the disk in when it's the one the state
|
|
104
|
-
# names (Disk::SavedState.load). A disk taken out is flushed.
|
|
105
|
-
def load_state(input)
|
|
106
|
-
disk = input.boolean? ? Disk::SavedState.load(input, @disk) : nil
|
|
107
|
-
flush unless disk.equal?(@disk)
|
|
108
|
-
@disk = disk
|
|
109
|
-
@cylinder = input.int
|
|
110
|
-
@side = input.int
|
|
111
|
-
@angle = input.int
|
|
112
|
-
@since = input.int
|
|
113
|
-
@motor = input.boolean?
|
|
114
|
-
@changed = input.boolean?
|
|
115
|
-
end
|
|
116
95
|
end
|
|
117
96
|
end
|
|
118
97
|
end
|
|
@@ -55,24 +55,6 @@ module Badline
|
|
|
55
55
|
|
|
56
56
|
# Whether the 1581 is device 8.
|
|
57
57
|
def drive1581_on_device8? = @drive1581&.device == KernalTrap::Routine::DEVICE
|
|
58
|
-
|
|
59
|
-
# The 1581 on the serial bus, with its device number.
|
|
60
|
-
def save_drive1581(out)
|
|
61
|
-
out.boolean(!@drive1581.nil?)
|
|
62
|
-
return unless @drive1581
|
|
63
|
-
|
|
64
|
-
out.int(@drive1581.device)
|
|
65
|
-
@drive1581.save_state(out)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def load_drive1581(input)
|
|
69
|
-
return detach_drive1581 unless input.boolean?
|
|
70
|
-
|
|
71
|
-
device = input.int
|
|
72
|
-
detach_drive1581 if @drive1581 && @drive1581.device != device
|
|
73
|
-
attach_drive1581(Model1581.new(device:)) unless @drive1581
|
|
74
|
-
@drive1581.load_state(input)
|
|
75
|
-
end
|
|
76
58
|
end
|
|
77
59
|
end
|
|
78
60
|
end
|
|
@@ -6,7 +6,6 @@ require "badline/drives/model1581/wd1772/stepping"
|
|
|
6
6
|
require "badline/drives/model1581/wd1772/sectors"
|
|
7
7
|
require "badline/drives/model1581/wd1772/sector_writes"
|
|
8
8
|
require "badline/drives/model1581/wd1772/tracks"
|
|
9
|
-
require "badline/drives/model1581/wd1772/saved_state"
|
|
10
9
|
|
|
11
10
|
module Badline
|
|
12
11
|
module Drives
|
|
@@ -45,7 +44,6 @@ module Badline
|
|
|
45
44
|
include Sectors
|
|
46
45
|
include SectorWrites
|
|
47
46
|
include Tracks
|
|
48
|
-
include SavedState
|
|
49
47
|
|
|
50
48
|
BYTE = 64
|
|
51
49
|
START = 32
|
|
@@ -9,7 +9,8 @@ require "badline/drives/model1581/wd1772"
|
|
|
9
9
|
require "badline/drives/model1581/cpu"
|
|
10
10
|
require "badline/drives/model1581/serial_port"
|
|
11
11
|
require "badline/drives/model1581/bus"
|
|
12
|
-
require "badline/drives/model1581/
|
|
12
|
+
require "badline/drives/model1581/keyed_state"
|
|
13
|
+
require "badline/drives/model1581/slot"
|
|
13
14
|
|
|
14
15
|
module Badline
|
|
15
16
|
module Drives
|
|
@@ -39,7 +40,6 @@ module Badline
|
|
|
39
40
|
class Model1581
|
|
40
41
|
include Engine::Core
|
|
41
42
|
include Engine::FastSerial
|
|
42
|
-
include SavedState
|
|
43
43
|
|
|
44
44
|
CLOCK_HZ = 2_000_000
|
|
45
45
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The drives and the serial bus badline/core's machines share. The C128's
|
|
4
|
+
# 1571 stays out of the core: badline/c128 requires it.
|
|
5
|
+
require "badline/drives/datasette"
|
|
6
|
+
require "badline/drives/serial_bus"
|
|
7
|
+
require "badline/drives/model1541"
|
|
8
|
+
require "badline/drives/model1581"
|
data/lib/badline/frontend/app.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Badline
|
|
|
21
21
|
@timeline = timeline
|
|
22
22
|
@snapshots = Snapshots.new(computer, options)
|
|
23
23
|
@screens = Screens.new(computer)
|
|
24
|
-
@led = DriveLed.
|
|
24
|
+
@led = DriveLed.new(computer)
|
|
25
25
|
@controls = Controls.new(computer)
|
|
26
26
|
@window = MachineWindow.new(@screens.screen, vsync: @pacer.vsync?)
|
|
27
27
|
@pacer.fit(@window.refresh_rate) if @pacer.vsync?
|
|
@@ -128,7 +128,7 @@ module Badline
|
|
|
128
128
|
@computer = computer
|
|
129
129
|
@screens.computer = computer
|
|
130
130
|
fit_window
|
|
131
|
-
@led = DriveLed.
|
|
131
|
+
@led = DriveLed.new(computer)
|
|
132
132
|
@controls.computer = computer
|
|
133
133
|
@gamepads.computer = computer
|
|
134
134
|
@sound.switch(computer.sound_source, computer.timing.clock_hz)
|
|
@@ -220,7 +220,7 @@ module Badline
|
|
|
220
220
|
renderer = @window.renderer
|
|
221
221
|
SDL.SDL_RenderClear(renderer)
|
|
222
222
|
SDL.SDL_RenderCopy(renderer, @window.texture, nil, nil)
|
|
223
|
-
@led
|
|
223
|
+
@led.draw(renderer, @window.width, @window.height)
|
|
224
224
|
@timeline.screenshots(@frames + 1).each { |path| Screenshot.write(renderer, path) }
|
|
225
225
|
SDL.SDL_RenderPresent(renderer)
|
|
226
226
|
end
|
|
@@ -10,7 +10,7 @@ module Badline
|
|
|
10
10
|
# attach, an --at key names nothing or an event fails, which it warns
|
|
11
11
|
# about.
|
|
12
12
|
def self.run(options)
|
|
13
|
-
error = Timeline.error(options.timeline)
|
|
13
|
+
error = Timeline.error(options.timeline, options.family)
|
|
14
14
|
unless error.empty?
|
|
15
15
|
warn "#{options.program}: #{error}"
|
|
16
16
|
return 1
|
|
@@ -55,11 +55,13 @@ module Badline
|
|
|
55
55
|
|
|
56
56
|
# A new machine built from the options (Machine.for_options), with
|
|
57
57
|
# `media`, if not nil, attached and started as the command line does,
|
|
58
|
-
# its disk writable if `writable
|
|
58
|
+
# its disk writable if `writable`, after the Plus/4's cartridges the
|
|
59
|
+
# options put in its slots. The VIC-20 and the Plus/4 raise
|
|
59
60
|
# Machine::Unsupported for a .sid tune.
|
|
60
61
|
def self.start(options, media, writable)
|
|
61
62
|
computer = build(options, media)
|
|
62
63
|
Media::TrueDrive.plug(computer) if options.true_drive?
|
|
64
|
+
options.cartridges.each { |slot, path| puts Media.attach(computer, path, cartridge: { slot: }) }
|
|
63
65
|
unless media.nil?
|
|
64
66
|
puts Media.attach(computer, media, autostart: options.autostart?, subtune: options.subtune,
|
|
65
67
|
disk: { read_only: !writable })
|
|
@@ -29,6 +29,7 @@ module Badline
|
|
|
29
29
|
@arrows_port = 2
|
|
30
30
|
@mouse = Input::Mouse1351.new
|
|
31
31
|
@paddles = Input::Paddles.new
|
|
32
|
+
@held = {}
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# Routes the input to another machine, the pot device going into its
|
|
@@ -73,10 +74,8 @@ module Badline
|
|
|
73
74
|
def key(scancode, down)
|
|
74
75
|
if joystick_mode? && Keys.joystick?(scancode)
|
|
75
76
|
joystick_key(scancode, down)
|
|
76
|
-
elsif @computer.family == :c128
|
|
77
|
-
c128_key(Keys.c128_key(scancode, @computer.mode), down)
|
|
78
77
|
else
|
|
79
|
-
|
|
78
|
+
machine_key(scancode, down)
|
|
80
79
|
end
|
|
81
80
|
end
|
|
82
81
|
|
|
@@ -104,6 +103,27 @@ module Badline
|
|
|
104
103
|
|
|
105
104
|
private
|
|
106
105
|
|
|
106
|
+
# A host key lets go of the key it pressed, though the C128 has
|
|
107
|
+
# switched between C64 and C128 mode since and maps it elsewhere.
|
|
108
|
+
def machine_key(scancode, down)
|
|
109
|
+
@held[scancode] = host_key(scancode) if down || !@held.key?(scancode)
|
|
110
|
+
key = @held[scancode]
|
|
111
|
+
@held.delete(scancode) unless down
|
|
112
|
+
if @computer.family == :c128
|
|
113
|
+
c128_key(key, down)
|
|
114
|
+
else
|
|
115
|
+
c64_key(key, down)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def host_key(scancode)
|
|
120
|
+
case @computer.family
|
|
121
|
+
when :c128 then Keys.c128_key(scancode, @computer.mode)
|
|
122
|
+
when :plus4 then Keys.plus4_key(scancode)
|
|
123
|
+
else Keys.c64_key(scancode)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
107
127
|
# RESTORE isn't in the key matrix, so it goes to the machine instead.
|
|
108
128
|
def c64_key(key, down)
|
|
109
129
|
if key == :restore
|
|
@@ -180,8 +200,8 @@ module Badline
|
|
|
180
200
|
end
|
|
181
201
|
|
|
182
202
|
def release_all
|
|
183
|
-
Keys::ARROWS.each_key { |scancode| @computer.keyboard.release(
|
|
184
|
-
Keys::WASD.each_key { |scancode| @computer.keyboard.release(
|
|
203
|
+
Keys::ARROWS.each_key { |scancode| @computer.keyboard.release(host_key(scancode)) }
|
|
204
|
+
Keys::WASD.each_key { |scancode| @computer.keyboard.release(host_key(scancode)) }
|
|
185
205
|
Keys::ARROWS.each_value do |direction|
|
|
186
206
|
@computer.joystick1.release(direction)
|
|
187
207
|
@computer.joystick2.release(direction)
|
|
@@ -2,48 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Frontend
|
|
5
|
-
# The true drive's
|
|
6
|
-
# border: bright while the DOS lights it, as it
|
|
7
|
-
# open or after an error, and dim otherwise
|
|
5
|
+
# The true drive's LEDs, drawn over the bottom right corner of the
|
|
6
|
+
# border: the red activity LED, bright while the DOS lights it, as it
|
|
7
|
+
# does while a file is open or after an error, and dim otherwise, and
|
|
8
|
+
# left of it the green power LED, which a 1581 flashes after an error.
|
|
9
|
+
# They follow whichever true drive the machine has, one plugged in
|
|
10
|
+
# after start among them, and are drawn only while it has one.
|
|
8
11
|
class DriveLed
|
|
9
12
|
WIDTH = 12
|
|
10
13
|
HEIGHT = 4
|
|
11
14
|
|
|
15
|
+
# The power LED's distance left of the activity LED.
|
|
16
|
+
POWER_OFFSET = WIDTH + 4
|
|
17
|
+
|
|
12
18
|
LIT = [0xff, 0x20, 0x20].freeze
|
|
13
19
|
DARK = [0x40, 0x00, 0x00].freeze
|
|
20
|
+
POWER_LIT = [0x20, 0xff, 0x20].freeze
|
|
21
|
+
POWER_DARK = [0x00, 0x40, 0x00].freeze
|
|
14
22
|
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
drive ? new(drive) : nil
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Sets SDL.led_rect to where the LED goes on a screen `width` by
|
|
22
|
-
# `height`.
|
|
23
|
-
def self.place(width, height)
|
|
23
|
+
# Sets SDL.led_rect to where the activity LED goes on a screen `width`
|
|
24
|
+
# by `height`, or with `offset` that many pixels left of it.
|
|
25
|
+
def self.place(width, height, offset = 0)
|
|
24
26
|
rect = SDL.led_rect
|
|
25
|
-
SDL.rect_x(rect, width - WIDTH - 8)
|
|
27
|
+
SDL.rect_x(rect, width - WIDTH - 8 - offset)
|
|
26
28
|
SDL.rect_y(rect, height - HEIGHT - 6)
|
|
27
29
|
SDL.rect_w(rect, WIDTH)
|
|
28
30
|
SDL.rect_h(rect, HEIGHT)
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
def initialize(
|
|
32
|
-
@
|
|
33
|
+
def initialize(computer)
|
|
34
|
+
@computer = computer
|
|
33
35
|
end
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
# Whether the machine has a true drive, and so LEDs to draw.
|
|
38
|
+
def shown? = !@computer.true_drive.nil?
|
|
39
|
+
|
|
40
|
+
def lit?
|
|
41
|
+
drive = @computer.true_drive
|
|
42
|
+
drive.nil? ? false : drive.led_on?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def power_lit?
|
|
46
|
+
drive = @computer.true_drive
|
|
47
|
+
drive.nil? ? false : drive.power_led_on?
|
|
48
|
+
end
|
|
36
49
|
|
|
37
50
|
def color = lit? ? LIT : DARK
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
def power_color = power_lit? ? POWER_LIT : POWER_DARK
|
|
53
|
+
|
|
54
|
+
# Fills the LEDs' rectangles on a screen `width` by `height` in their
|
|
55
|
+
# colours, then puts back the black that RenderClear clears with.
|
|
41
56
|
def draw(renderer, width, height)
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
return unless shown?
|
|
58
|
+
|
|
59
|
+
fill(renderer, width, height, 0, color)
|
|
60
|
+
fill(renderer, width, height, POWER_OFFSET, power_color)
|
|
61
|
+
SDL.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def fill(renderer, width, height, offset, rgb)
|
|
67
|
+
DriveLed.place(width, height, offset)
|
|
44
68
|
SDL.SDL_SetRenderDrawColor(renderer, rgb[0], rgb[1], rgb[2], 255)
|
|
45
69
|
SDL.SDL_RenderFillRect(renderer, SDL.led_rect)
|
|
46
|
-
SDL.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255)
|
|
47
70
|
end
|
|
48
71
|
end
|
|
49
72
|
end
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Frontend
|
|
5
5
|
# SDL scancodes follow key positions on a US layout. They map onto the C64
|
|
6
|
-
# keys, the C128's own keys and, in joystick mode, the
|
|
7
|
-
# directions.
|
|
6
|
+
# keys, the C128's and the 264's own keys and, in joystick mode, the
|
|
7
|
+
# joystick directions.
|
|
8
8
|
module Keys
|
|
9
9
|
RETURN = 40
|
|
10
10
|
ESCAPE = 41
|
|
@@ -56,6 +56,15 @@ module Badline
|
|
|
56
56
|
63 => :forty_eighty, 72 => :forty_eighty, 78 => :run_stop
|
|
57
57
|
}.freeze
|
|
58
58
|
|
|
59
|
+
# The 264's keys where they differ from the C64's: ESC on Esc, with
|
|
60
|
+
# RUN/STOP on Page Down as on the C128, F1-F3 on F1-F3, HELP on F4,
|
|
61
|
+
# Insert or Help, a cursor key on each arrow, and either SHIFT on the
|
|
62
|
+
# one SHIFT. Page Up, RESTORE on the C64, presses nothing.
|
|
63
|
+
PLUS4_OTHERS = {
|
|
64
|
+
41 => :esc, 78 => :run_stop, 58 => :f1, 59 => :f2, 60 => :f3, 61 => :help, 73 => :help, 117 => :help,
|
|
65
|
+
79 => :crsr_right, 80 => :crsr_left, 81 => :crsr_down, 82 => :crsr_up, 229 => :lshift, 75 => :none
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
59
68
|
ARROWS = { 44 => :fire, 228 => :fire, 79 => :right, 80 => :left, 81 => :down, 82 => :up }.freeze
|
|
60
69
|
WASD = { 225 => :fire, 7 => :right, 4 => :left, 22 => :down, 26 => :up }.freeze
|
|
61
70
|
|
|
@@ -77,6 +86,9 @@ module Badline
|
|
|
77
86
|
C128_OTHERS[scancode] || c64_key(scancode)
|
|
78
87
|
end
|
|
79
88
|
|
|
89
|
+
# The 264's key for the scancode: one of PLUS4_OTHERS or the C64's.
|
|
90
|
+
def self.plus4_key(scancode) = PLUS4_OTHERS[scancode] || c64_key(scancode)
|
|
91
|
+
|
|
80
92
|
# The character the key types on a US layout: a letter, a capital
|
|
81
93
|
# with `shift`, or a digit, and "" for any other key.
|
|
82
94
|
def self.character(scancode, shift)
|
|
@@ -31,8 +31,18 @@ module Badline
|
|
|
31
31
|
drive.nil? ? computer.unmount : drive.insert(nil)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# Whether the expansion port has a cartridge in, on the Plus/4 in
|
|
35
|
+
# either cartridge's slots.
|
|
36
|
+
def cartridge?(computer)
|
|
37
|
+
return !computer.cartridge_slots.empty? if computer.family == :plus4
|
|
38
|
+
|
|
39
|
+
!computer.address_bus.cartridge.nil?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Takes the cartridge out with the power off, on the Plus/4 both.
|
|
35
43
|
def remove_cartridge(computer)
|
|
44
|
+
return computer.detach_cartridges if computer.family == :plus4
|
|
45
|
+
|
|
36
46
|
computer.address_bus.detach_cartridge
|
|
37
47
|
computer.power_cycle!
|
|
38
48
|
end
|
|
@@ -8,8 +8,8 @@ module Badline
|
|
|
8
8
|
# name. A disk or tape picked goes straight in, and a save loads.
|
|
9
9
|
class MenuDialogs
|
|
10
10
|
EXTENSIONS = {
|
|
11
|
-
disk:
|
|
12
|
-
|
|
11
|
+
disk: Media::Extensions::DEVICE8, tape: Media::Extensions.of(%i[tape]),
|
|
12
|
+
cartridge: Media::Extensions.of(%i[cartridge]), program: Media::Extensions::ALL, snapshot: %w[.vsf]
|
|
13
13
|
}.freeze
|
|
14
14
|
TITLES = { disk: "INSERT A DISK", tape: "INSERT A TAPE", cartridge: "INSERT A CARTRIDGE",
|
|
15
15
|
program: "QUICK OPEN", snapshot: "LOAD A SAVE" }.freeze
|
|
@@ -37,7 +37,8 @@ module Badline
|
|
|
37
37
|
# :program, or :snapshot, which starts in the saves folder.
|
|
38
38
|
def browse(kind)
|
|
39
39
|
@browsing = kind
|
|
40
|
-
|
|
40
|
+
extensions = kind == :cartridge ? @media.cartridge_extensions : EXTENSIONS.fetch(kind)
|
|
41
|
+
@browser.open(kind == :snapshot ? Badline.data_folder("saves") : @browse_from, extensions)
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
# Asks for a save's name, starting from `name`.
|