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
|
@@ -87,6 +87,13 @@ module Badline
|
|
|
87
87
|
insert(:disk, set[index + step])
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
# The extensions of the cartridges the machine takes: .crt, and on
|
|
91
|
+
# the Plus/4 raw .bin ROM images too.
|
|
92
|
+
def cartridge_extensions
|
|
93
|
+
extensions = Media::Extensions.of(%i[cartridge])
|
|
94
|
+
@computer.family == :plus4 ? extensions + [Media::Plus4Media::ROM_IMAGE] : extensions
|
|
95
|
+
end
|
|
96
|
+
|
|
90
97
|
# Swaps the cartridge for the one at `path`, which power cycles the
|
|
91
98
|
# machine, and says whether it went in.
|
|
92
99
|
def attach_cartridge(path)
|
|
@@ -95,10 +102,11 @@ module Badline
|
|
|
95
102
|
|
|
96
103
|
# What a save of the machine is named after: the cartridge, the disk,
|
|
97
104
|
# the tape, or BASIC, without the bracketed parts of a file's name. A
|
|
98
|
-
# VIC-20's are named after its disk, or else the machine
|
|
105
|
+
# VIC-20's are named after its disk, or else the machine, and a
|
|
106
|
+
# Plus/4's after its disk or tape.
|
|
99
107
|
def game_name
|
|
100
108
|
vic20 = @computer.family == :vic20
|
|
101
|
-
name = vic20 ? "" : cartridge_name
|
|
109
|
+
name = vic20 || @computer.family == :plus4 ? "" : cartridge_name
|
|
102
110
|
disk = disk_path
|
|
103
111
|
name = File.basename(disk, File.extname(disk)) if name.empty? && !disk.empty?
|
|
104
112
|
name = tape_name if name.empty? && !vic20
|
|
@@ -114,8 +122,10 @@ module Badline
|
|
|
114
122
|
end
|
|
115
123
|
|
|
116
124
|
# The name the cartridge in the expansion port gives itself, or an
|
|
117
|
-
# empty one.
|
|
125
|
+
# empty one. A Plus/4's names the slots with a ROM in them.
|
|
118
126
|
def cartridge_name
|
|
127
|
+
return @computer.cartridge_slots.map(&:upcase).join(", ") if @computer.family == :plus4
|
|
128
|
+
|
|
119
129
|
cartridge = @computer.address_bus.cartridge
|
|
120
130
|
cartridge.nil? ? "" : cartridge.name
|
|
121
131
|
end
|
|
@@ -89,11 +89,7 @@ module Badline
|
|
|
89
89
|
|
|
90
90
|
def vic20? = @computer.family == :vic20
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
# shows.
|
|
94
|
-
def note
|
|
95
|
-
@painter.text(@left, @top, "NOT ON THE VIC-20 YET", MenuTheme::DIM)
|
|
96
|
-
end
|
|
92
|
+
def plus4? = @computer.family == :plus4
|
|
97
93
|
|
|
98
94
|
def draw_drive
|
|
99
95
|
media = @media
|
|
@@ -128,10 +124,12 @@ module Badline
|
|
|
128
124
|
def draw_expansion
|
|
129
125
|
return info("RAM EXPANSION", RAM_NAMES.fetch(@computer.ram_configuration)) if vic20?
|
|
130
126
|
|
|
131
|
-
|
|
132
|
-
info("CARTRIDGE", cartridge.nil? ? "" : cartridge.name)
|
|
127
|
+
info("CARTRIDGE", @media.cartridge_name)
|
|
133
128
|
row("INSERT...", :insert_cartridge)
|
|
134
129
|
row("REMOVE", :remove_cartridge)
|
|
130
|
+
return if plus4?
|
|
131
|
+
|
|
132
|
+
cartridge = @computer.address_bus.cartridge
|
|
135
133
|
row("FREEZE", :freeze) if !cartridge.nil? && cartridge.freezer? && @computer.family == :c64
|
|
136
134
|
return if @reu_kb.nil?
|
|
137
135
|
|
|
@@ -167,9 +165,11 @@ module Badline
|
|
|
167
165
|
end
|
|
168
166
|
end
|
|
169
167
|
|
|
170
|
-
# The C128 names its model and the mode it runs in first
|
|
168
|
+
# The C128 names its model and the mode it runs in first, and the
|
|
169
|
+
# Plus/4 its model and RAM.
|
|
171
170
|
def draw_power
|
|
172
171
|
info("MACHINE", "#{@computer.model.name.upcase}, #{@computer.mode.to_s.upcase} MODE") if c128?
|
|
172
|
+
info("MACHINE", @computer.model_label) if plus4?
|
|
173
173
|
row("RESET", :reset)
|
|
174
174
|
row("POWER CYCLE", :power_cycle)
|
|
175
175
|
skip
|
|
@@ -5,7 +5,7 @@ module Badline
|
|
|
5
5
|
# The pause menu's PORTS page, which MenuPages draws: what each C64
|
|
6
6
|
# control port has plugged in, a joystick or a pot device, and whether
|
|
7
7
|
# the keys type or drive the joysticks. The VIC-20 has one port, a
|
|
8
|
-
# joystick's.
|
|
8
|
+
# joystick's, and the Plus/4 two joysticks and no pot devices.
|
|
9
9
|
module MenuPorts
|
|
10
10
|
PORT_DEVICES = %i[joystick mouse paddles].freeze
|
|
11
11
|
PORT_NAMES = %w[JOY MOUSE PADDLES].freeze
|
|
@@ -16,6 +16,7 @@ module Badline
|
|
|
16
16
|
|
|
17
17
|
def draw_ports
|
|
18
18
|
return draw_vic20_port if @computer.family == :vic20
|
|
19
|
+
return draw_plus4_ports if @computer.family == :plus4
|
|
19
20
|
|
|
20
21
|
toggle("PORT 1", :port1, [PORT_NAMES, PORT_ACTIONS[0], PORT_DEVICES.index(port_device(1))])
|
|
21
22
|
toggle("PORT 2", :port2, [PORT_NAMES, PORT_ACTIONS[1], PORT_DEVICES.index(port_device(2))])
|
|
@@ -37,6 +38,17 @@ module Badline
|
|
|
37
38
|
info("WASD, LEFT SHIFT", "JOYSTICK")
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
# The Plus/4's two joysticks, one for each set of keys.
|
|
42
|
+
def draw_plus4_ports
|
|
43
|
+
return unless draw_keys("PLUS/4")
|
|
44
|
+
|
|
45
|
+
row("SWAP JOYSTICKS", :swap)
|
|
46
|
+
skip
|
|
47
|
+
arrows = @controls.arrows_port
|
|
48
|
+
info("ARROWS, SPACE", "JOYSTICK #{arrows}")
|
|
49
|
+
info("WASD, LEFT SHIFT", "JOYSTICK #{3 - arrows}")
|
|
50
|
+
end
|
|
51
|
+
|
|
40
52
|
# The toggle between the keyboard named and the joysticks, and
|
|
41
53
|
# whether the keys drive the joysticks.
|
|
42
54
|
def draw_keys(keyboard)
|
|
@@ -4,9 +4,10 @@ module Badline
|
|
|
4
4
|
module Frontend
|
|
5
5
|
# The visible part of the video chip's display, the crop of the
|
|
6
6
|
# machine's timing, repacked for an SDL texture in XRGB8888 in the
|
|
7
|
-
# chip's palette
|
|
8
|
-
# words, as badline/ffi does on CRuby, so
|
|
9
|
-
# neighbouring pixels, the left one in the low
|
|
7
|
+
# chip's palette, of 16 colours or the TED's 128. Spinel hands an Array
|
|
8
|
+
# of Integers to C as 64-bit words, as badline/ffi does on CRuby, so
|
|
9
|
+
# each word carries two neighbouring pixels, the left one in the low
|
|
10
|
+
# half.
|
|
10
11
|
#
|
|
11
12
|
# Each pixel shows `pixel_width` of the window's square pixels wide,
|
|
12
13
|
# or with `dots`, that many pixels share one, as the VDC's dots do. The
|
|
@@ -14,18 +15,21 @@ module Badline
|
|
|
14
15
|
# least MIN_WIDTH window pixels wide, and at least as high as PAL's C64
|
|
15
16
|
# crop, 272 lines, so a C64 shows in the same window whatever its
|
|
16
17
|
# region. A crop with fewer lines or pixels sits in the middle of the
|
|
17
|
-
# texture, between black bands. The VIC-20's 284 by 284 crop
|
|
18
|
-
# own.
|
|
18
|
+
# texture, between black bands. The VIC-20's 284 by 284 crop and the
|
|
19
|
+
# TED's 384 by 288 fill their own.
|
|
20
|
+
#
|
|
21
|
+
# With `frame_lines`, the crop's lines run on past the frame's last
|
|
22
|
+
# line to its first, as the TED's do: its top border is the end of
|
|
23
|
+
# the frame.
|
|
19
24
|
class Screen
|
|
20
25
|
MIN_WIDTH = Region::PAL.crop[2]
|
|
21
26
|
MIN_HEIGHT = Region::PAL.crop[3]
|
|
22
27
|
|
|
23
28
|
attr_reader :pixels, :width, :height
|
|
24
29
|
|
|
25
|
-
def initialize(video, crop, pixel_width: 1, dots: 1)
|
|
30
|
+
def initialize(video, crop, pixel_width: 1, dots: 1, frame_lines: 0)
|
|
26
31
|
@video = video
|
|
27
32
|
@col_offset = crop[0]
|
|
28
|
-
@row_offset = crop[1]
|
|
29
33
|
@lines = crop[3]
|
|
30
34
|
@columns = crop[2] & ~1
|
|
31
35
|
@pixel_width = pixel_width
|
|
@@ -37,8 +41,10 @@ module Badline
|
|
|
37
41
|
@pair_count = @columns / 2
|
|
38
42
|
@margin = (@row_words - @pair_count) / 2
|
|
39
43
|
@band = (@height - @lines) / 2
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
top = crop[1]
|
|
45
|
+
wrap = frame_lines.zero? ? top + @lines : frame_lines
|
|
46
|
+
@sources = Array.new(@lines) { |row| (row + top) % wrap }
|
|
47
|
+
pair_up(video.palette)
|
|
42
48
|
@pixels = Array.new(@height * @row_words, 0)
|
|
43
49
|
pack_all
|
|
44
50
|
end
|
|
@@ -54,9 +60,10 @@ module Badline
|
|
|
54
60
|
video = @video
|
|
55
61
|
dirty = video.dirty_lines
|
|
56
62
|
display = video.display
|
|
63
|
+
sources = @sources
|
|
57
64
|
row = 0
|
|
58
65
|
while row < @lines
|
|
59
|
-
pack_row(display, row) if dirty[row
|
|
66
|
+
pack_row(display, row) if dirty[sources[row]]
|
|
60
67
|
row += 1
|
|
61
68
|
end
|
|
62
69
|
video.clear_dirty_lines!
|
|
@@ -64,6 +71,18 @@ module Badline
|
|
|
64
71
|
|
|
65
72
|
private
|
|
66
73
|
|
|
74
|
+
# The packed word for each pair of palette indices, the left one in
|
|
75
|
+
# the low bits.
|
|
76
|
+
def pair_up(palette)
|
|
77
|
+
colors = palette.size
|
|
78
|
+
shift = 4
|
|
79
|
+
shift += 1 while (1 << shift) < colors
|
|
80
|
+
mask = colors - 1
|
|
81
|
+
@shift = shift
|
|
82
|
+
@mask = mask
|
|
83
|
+
@pairs = Array.new(colors << shift) { |pair| palette[pair & mask] | (palette[(pair >> shift) & mask] << 32) }
|
|
84
|
+
end
|
|
85
|
+
|
|
67
86
|
# Repacks every line, as a screen built for a display the chip has
|
|
68
87
|
# already drawn starts with all of it.
|
|
69
88
|
def pack_all
|
|
@@ -78,11 +97,13 @@ module Badline
|
|
|
78
97
|
def pack_row(display, row)
|
|
79
98
|
pixels = @pixels
|
|
80
99
|
pairs = @pairs
|
|
81
|
-
|
|
100
|
+
mask = @mask
|
|
101
|
+
shift = @shift
|
|
102
|
+
from = (@sources[row] * @stride) + @col_offset
|
|
82
103
|
to = ((row + @band) * @row_words) + @margin
|
|
83
104
|
last = to + @pair_count
|
|
84
105
|
while to < last
|
|
85
|
-
pixels[to] = pairs[(display[from] &
|
|
106
|
+
pixels[to] = pairs[(display[from] & mask) | ((display[from + 1] & mask) << shift)]
|
|
86
107
|
from += 2
|
|
87
108
|
to += 1
|
|
88
109
|
end
|
|
@@ -70,7 +70,8 @@ module Badline
|
|
|
70
70
|
@screen = Screen.new(vdc, vdc.crop, dots: 2)
|
|
71
71
|
else
|
|
72
72
|
timing = @computer.timing
|
|
73
|
-
@screen = Screen.new(@computer.video, timing.crop,
|
|
73
|
+
@screen = Screen.new(@computer.video, timing.crop,
|
|
74
|
+
pixel_width: timing.pixel_width, frame_lines: timing.lines_per_frame)
|
|
74
75
|
end
|
|
75
76
|
end
|
|
76
77
|
|
|
@@ -20,6 +20,7 @@ module Badline
|
|
|
20
20
|
def initialize(computer, options)
|
|
21
21
|
@computer = computer
|
|
22
22
|
@at_finish = options.save_snapshot
|
|
23
|
+
@vice = options.vice?
|
|
23
24
|
@autosave = options.frames.zero?
|
|
24
25
|
@autosave_at = AUTOSAVE_FRAMES
|
|
25
26
|
end
|
|
@@ -128,11 +129,21 @@ module Badline
|
|
|
128
129
|
end
|
|
129
130
|
|
|
130
131
|
def finish
|
|
131
|
-
|
|
132
|
+
return if @at_finish.empty?
|
|
133
|
+
|
|
134
|
+
@vice ? export(@at_finish) : save(@at_finish)
|
|
132
135
|
end
|
|
133
136
|
|
|
134
137
|
private
|
|
135
138
|
|
|
139
|
+
# Saves the machine with VICE's modules too, and warns when it can't.
|
|
140
|
+
def export(path)
|
|
141
|
+
@computer.export_snapshot(path)
|
|
142
|
+
puts "Saved #{path} for badline and VICE"
|
|
143
|
+
rescue Snapshot::FormatError, SystemCallError => e
|
|
144
|
+
warn "badline: #{path}: #{e.message}"
|
|
145
|
+
end
|
|
146
|
+
|
|
136
147
|
def restore_from(path)
|
|
137
148
|
@computer = Snapshot.load(path) { |line| puts line }
|
|
138
149
|
puts "Restored #{name_of(path)} from #{path}"
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Frontend
|
|
5
|
-
# The machine's sound source, the C64's SID
|
|
6
|
-
# SDL's audio queue, as mono signed 16-bit samples.
|
|
7
|
-
# #record(rate:, clock_hz:) and #drain_samples.
|
|
5
|
+
# The machine's sound source, the C64's SID, the VIC-20's VIC or the
|
|
6
|
+
# Plus/4's TED, on SDL's audio queue, as mono signed 16-bit samples.
|
|
7
|
+
# Each takes #record(rate:, clock_hz:) and #drain_samples.
|
|
8
8
|
# Once the device plays, it is the clock: #wait holds each frame until
|
|
9
9
|
# the queue is down to `AHEAD`, so the machine runs at the device's pace.
|
|
10
10
|
#
|
|
@@ -16,7 +16,7 @@ module Badline
|
|
|
16
16
|
#
|
|
17
17
|
# Each frame's samples go to a mono AudioDevice. The chip's cycles are
|
|
18
18
|
# converted at `clock_hz`, the machine's clock, so an NTSC or Drean
|
|
19
|
-
# machine plays at its own pitch.
|
|
19
|
+
# machine plays at its own pitch, and the TED's at its single clock.
|
|
20
20
|
class Sound
|
|
21
21
|
RATE = 44_100
|
|
22
22
|
AHEAD = 0.08
|
|
@@ -21,10 +21,13 @@ module Badline
|
|
|
21
21
|
@failed = false
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
# The C128's two locking keys, outside its matrix.
|
|
25
|
+
LOCKS = %w[caps_lock forty_eighty].freeze
|
|
26
|
+
|
|
27
|
+
# What's wrong with the events for the family's machine, the first
|
|
28
|
+
# key or menu page that names nothing, or an empty string.
|
|
29
|
+
def self.error(events, family)
|
|
30
|
+
event = events.find { |candidate| candidate.action == "key" && !key?(candidate.argument, family) }
|
|
28
31
|
return "no such key: #{event.argument}" unless event.nil?
|
|
29
32
|
|
|
30
33
|
event = events.find { |candidate| candidate.action == "menu" && !page?(candidate.argument) }
|
|
@@ -35,12 +38,30 @@ module Badline
|
|
|
35
38
|
# sound. An empty one is its first.
|
|
36
39
|
def self.page?(name) = name.empty? || PauseMenu::PAGES.any? { |page| page.to_s == name }
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
# Whether the family's machine has a key of the name. The Plus/4 has
|
|
42
|
+
# no RESTORE.
|
|
43
|
+
def self.key?(name, family)
|
|
44
|
+
(name == "restore" && family != :plus4) || (family == :c128 && LOCKS.include?(name)) ||
|
|
45
|
+
!keyboard_key(name, family).nil? || !joystick_direction(name).nil?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The family's Keyboard key for a name such as space, return or a,
|
|
49
|
+
# on the C128 esc or keypad1, or on the Plus/4 help or crsr_up. The
|
|
50
|
+
# shifted cursor keys, such as cursor_up, are the C64's and the
|
|
51
|
+
# C128's.
|
|
52
|
+
def self.keyboard_key(name, family)
|
|
53
|
+
key = keyboard_matrix(family).flatten.find { |candidate| candidate.to_s == name }
|
|
54
|
+
return key unless key.nil? && family != :plus4
|
|
55
|
+
|
|
56
|
+
Input::Keyboard::COMBINATIONS.keys.find { |candidate| candidate.to_s == name }
|
|
57
|
+
end
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
def self.keyboard_matrix(family)
|
|
60
|
+
case family
|
|
61
|
+
when :c128 then C128::KEYBOARD_MATRIX
|
|
62
|
+
when :plus4 then Plus4::KeyboardPort::MATRIX
|
|
63
|
+
else Input::Keyboard::C64_MATRIX
|
|
64
|
+
end
|
|
44
65
|
end
|
|
45
66
|
|
|
46
67
|
# The direction of joy1-NAME or joy2-NAME, such as up or fire.
|
|
@@ -130,6 +151,8 @@ module Badline
|
|
|
130
151
|
|
|
131
152
|
def press(computer, event)
|
|
132
153
|
name = event.argument
|
|
154
|
+
return lock(computer, name) if LOCKS.include?(name)
|
|
155
|
+
|
|
133
156
|
if name == "restore"
|
|
134
157
|
computer.press_restore
|
|
135
158
|
return hold(event)
|
|
@@ -137,13 +160,21 @@ module Badline
|
|
|
137
160
|
|
|
138
161
|
direction = Timeline.joystick_direction(name)
|
|
139
162
|
if direction.nil?
|
|
140
|
-
computer.keyboard.press(Timeline.keyboard_key(name))
|
|
163
|
+
computer.keyboard.press(Timeline.keyboard_key(name, computer.family))
|
|
141
164
|
else
|
|
142
165
|
joystick(computer, name).press(direction)
|
|
143
166
|
end
|
|
144
167
|
hold(event)
|
|
145
168
|
end
|
|
146
169
|
|
|
170
|
+
# CAPS LOCK and 40/80 DISPLAY lock: a push puts one down if it was up,
|
|
171
|
+
# and up if it was down.
|
|
172
|
+
def lock(computer, name)
|
|
173
|
+
return unless computer.family == :c128
|
|
174
|
+
|
|
175
|
+
name == "caps_lock" ? computer.push_caps_lock : computer.push_display_key
|
|
176
|
+
end
|
|
177
|
+
|
|
147
178
|
# The VIC-20 and the C128 have no cartridge with a button yet.
|
|
148
179
|
def freeze(computer, event)
|
|
149
180
|
return unless computer.family == :c64
|
|
@@ -164,7 +195,7 @@ module Badline
|
|
|
164
195
|
|
|
165
196
|
direction = Timeline.joystick_direction(name)
|
|
166
197
|
if direction.nil?
|
|
167
|
-
computer.keyboard.release(Timeline.keyboard_key(name))
|
|
198
|
+
computer.keyboard.release(Timeline.keyboard_key(name, computer.family))
|
|
168
199
|
else
|
|
169
200
|
joystick(computer, name).release(direction)
|
|
170
201
|
end
|
|
@@ -197,7 +228,7 @@ module Badline
|
|
|
197
228
|
|
|
198
229
|
def inserted?(computer, what)
|
|
199
230
|
return !computer.datasette.tape.nil? if what == "tape"
|
|
200
|
-
return
|
|
231
|
+
return MediaSlots.cartridge?(computer) if what == "cartridge"
|
|
201
232
|
|
|
202
233
|
MediaSlots.disk?(computer)
|
|
203
234
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
# Types PETSCII codes on a Keyboard's matrix, one key at a time, for
|
|
6
|
+
# software that scans the keys itself instead of reading a KERNAL's
|
|
7
|
+
# keyboard buffer: each code's keys go down for HOLD cycles, then up for
|
|
8
|
+
# GAP cycles. Codes it has no key for are skipped.
|
|
9
|
+
class MatrixTypist
|
|
10
|
+
HOLD = 100_000
|
|
11
|
+
GAP = 40_000
|
|
12
|
+
|
|
13
|
+
# The keys typed without SHIFT, by PETSCII code.
|
|
14
|
+
PLAIN = {
|
|
15
|
+
0x0d => :return, 0x20 => :space, 0x2a => :*, 0x2b => :+, 0x2c => :",", 0x2d => :-, 0x2e => :".",
|
|
16
|
+
0x2f => :/, 0x3a => :":", 0x3b => :";", 0x3d => :"=", 0x40 => :"@"
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
# The keys typed with SHIFT, by PETSCII code.
|
|
20
|
+
SHIFTED = {
|
|
21
|
+
0x21 => :"1", 0x22 => :"2", 0x23 => :"3", 0x24 => :"4", 0x25 => :"5", 0x26 => :"6", 0x27 => :"7",
|
|
22
|
+
0x28 => :"8", 0x29 => :"9", 0x3c => :",", 0x3e => :".", 0x3f => :/, 0x5b => :":", 0x5d => :";"
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
DIGITS = %i[0 1 2 3 4 5 6 7 8 9].freeze
|
|
26
|
+
LETTERS = %i[a b c d e f g h i j k l m n o p q r s t u v w x y z].freeze
|
|
27
|
+
|
|
28
|
+
# The keys that type the PETSCII code, SHIFT first, or none.
|
|
29
|
+
def self.keys(code)
|
|
30
|
+
return [DIGITS[code - 0x30]] if code.between?(0x30, 0x39)
|
|
31
|
+
return [LETTERS[code - 0x41]] if code.between?(0x41, 0x5a)
|
|
32
|
+
return [:lshift, LETTERS[code - 0xc1]] if code.between?(0xc1, 0xda)
|
|
33
|
+
return [PLAIN[code]] if PLAIN.key?(code)
|
|
34
|
+
return [:lshift, SHIFTED[code]] if SHIFTED.key?(code)
|
|
35
|
+
|
|
36
|
+
[]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize(keyboard)
|
|
40
|
+
@keyboard = keyboard
|
|
41
|
+
@held = []
|
|
42
|
+
@code = 0
|
|
43
|
+
@wait = 0
|
|
44
|
+
@typing = false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Whether it is part way through codes: from the first key down until
|
|
48
|
+
# the last one is up and its gap has passed.
|
|
49
|
+
def typing? = @typing
|
|
50
|
+
|
|
51
|
+
# A cycle of typing +codes+, taking each code off its front as its keys
|
|
52
|
+
# go down.
|
|
53
|
+
def cycle!(codes)
|
|
54
|
+
@wait -= 1 if @wait.positive?
|
|
55
|
+
return unless @wait.zero?
|
|
56
|
+
|
|
57
|
+
if @held.empty?
|
|
58
|
+
press(codes)
|
|
59
|
+
else
|
|
60
|
+
@held.each { |key| @keyboard.release(key) }
|
|
61
|
+
@held = []
|
|
62
|
+
@wait = GAP
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Where it is in its typing, in a keyed snapshot module under `prefix`:
|
|
67
|
+
# the code whose keys are down, if any, the cycles left before they
|
|
68
|
+
# go up or the next code's go down, and whether it is part way
|
|
69
|
+
# through. The keys go down again on load.
|
|
70
|
+
def save_fields(out, prefix)
|
|
71
|
+
out.optional_int("#{prefix}code", @held.empty? ? nil : @code)
|
|
72
|
+
out.int("#{prefix}wait", @wait).bool("#{prefix}typing", @typing)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def load_fields(input, prefix)
|
|
76
|
+
@held.each { |key| @keyboard.release(key) }
|
|
77
|
+
code = input.optional_int("#{prefix}code")
|
|
78
|
+
@code = code || 0
|
|
79
|
+
@held = code.nil? ? [] : MatrixTypist.keys(code)
|
|
80
|
+
@held.each { |key| @keyboard.press(key) }
|
|
81
|
+
@wait = input.int("#{prefix}wait", @wait)
|
|
82
|
+
@typing = input.bool("#{prefix}typing", @typing)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def press(codes)
|
|
88
|
+
@typing = !codes.empty?
|
|
89
|
+
return unless @typing
|
|
90
|
+
|
|
91
|
+
@code = codes.shift
|
|
92
|
+
@held = MatrixTypist.keys(@code)
|
|
93
|
+
@held.each { |key| @keyboard.press(key) }
|
|
94
|
+
@wait = @held.empty? ? 1 : HOLD
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/badline/input.rb
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module KernalTrap
|
|
5
|
+
# Observe-only PC trap on the KERNAL CHROUT routine ($FFD2). Records each
|
|
6
|
+
# character written while the layout finds the KERNAL mapped in, then lets execution fall
|
|
7
|
+
# through to the ROM, so the screen output is unaffected. Used for headless
|
|
8
|
+
# capture of program output.
|
|
9
|
+
class Chrout
|
|
10
|
+
ADDRESS = 0xffd2
|
|
11
|
+
|
|
12
|
+
attr_reader :output
|
|
13
|
+
|
|
14
|
+
# The KERNAL layout whose mapping the trap checks.
|
|
15
|
+
attr_writer :layout
|
|
16
|
+
|
|
17
|
+
def initialize(cpu:, bus:, layout:)
|
|
18
|
+
@cpu = cpu
|
|
19
|
+
@bus = bus
|
|
20
|
+
@layout = layout
|
|
21
|
+
@output = +""
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call
|
|
25
|
+
@output << ascii(@cpu.a) if @layout.kernal_mapped?(@bus)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def inspect
|
|
29
|
+
"#<#{self.class.name} output=#{@output.inspect}>"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def ascii(byte)
|
|
35
|
+
case byte
|
|
36
|
+
when 0x0d then "\n"
|
|
37
|
+
when 0x41..0x5a then (byte + 0x20).chr
|
|
38
|
+
when 0xc1..0xda then (byte - 0x80).chr
|
|
39
|
+
when 0x20..0x7e then byte.chr
|
|
40
|
+
else ""
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -81,33 +81,6 @@ module Badline
|
|
|
81
81
|
data ? new(data) : new([], error: [ILLEGAL_TRACK_OR_SECTOR, track, sector])
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
# Reads what save_state wrote after the flag that marks a file open
|
|
85
|
-
# for writing (Channels.channel_from_state). A buffer channel
|
|
86
|
-
# takes its block in `ram`, the drive's RAM, again.
|
|
87
|
-
def self.from_state(input, ram)
|
|
88
|
-
new.tap { |channel| channel.load_state(input, ram) }
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# A buffer channel's bytes are the drive's RAM, which the drive
|
|
92
|
-
# writes itself.
|
|
93
|
-
def save_state(out)
|
|
94
|
-
out.boolean(false).boolean(@writable).int(@base).int(@length).int(@pointer)
|
|
95
|
-
out.optional_int(@lead).optional_int(@buffer).boolean(!@error.nil?)
|
|
96
|
-
out.ints(@error) if @error
|
|
97
|
-
out.blob(@bytes) unless @writable
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def load_state(input, ram)
|
|
101
|
-
@writable = input.boolean?
|
|
102
|
-
@base = input.int
|
|
103
|
-
@length = input.int
|
|
104
|
-
@pointer = input.int
|
|
105
|
-
@lead = input.optional_int
|
|
106
|
-
@buffer = input.optional_int
|
|
107
|
-
@error = input.boolean? ? input.ints : nil
|
|
108
|
-
@bytes = @writable ? ram : input.blob
|
|
109
|
-
end
|
|
110
|
-
|
|
111
84
|
def replace(bytes, length = bytes.length)
|
|
112
85
|
@bytes = bytes
|
|
113
86
|
rewind(length)
|
|
@@ -22,31 +22,6 @@ module Badline
|
|
|
22
22
|
|
|
23
23
|
def clear = @channels.clear
|
|
24
24
|
|
|
25
|
-
# Each open channel with its secondary address, in the order they
|
|
26
|
-
# opened.
|
|
27
|
-
def save_state(out)
|
|
28
|
-
out.int(@channels.length)
|
|
29
|
-
@channels.each do |secondary, channel|
|
|
30
|
-
out.int(secondary)
|
|
31
|
-
channel.save_state(out)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Buffer channels take their block in the drive's RAM again.
|
|
36
|
-
def load_state(input, memory)
|
|
37
|
-
@channels.clear
|
|
38
|
-
input.int.times do
|
|
39
|
-
secondary = input.int
|
|
40
|
-
@channels[secondary] = Channels.channel_from_state(input, memory.ram)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# A channel save_state wrote: a file open for writing, or a Channel
|
|
45
|
-
# whose buffer, if it has one, is in `ram`.
|
|
46
|
-
def self.channel_from_state(input, ram)
|
|
47
|
-
input.boolean? ? WriteFile.from_state(input) : Channel.from_state(input, ram)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
25
|
# Opens a buffer channel on the buffer a "#n" name asks for, or on
|
|
51
26
|
# the highest free one. Returns nil when that buffer isn't free,
|
|
52
27
|
# which leaves a channel asking again for its own buffer open.
|
|
@@ -62,15 +62,6 @@ module Badline
|
|
|
62
62
|
load_buffer(Channels::BAM_BUFFER, block) if block
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def save_state(out)
|
|
66
|
-
out.blob(@ram).boolean(@bam)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def load_state(input)
|
|
70
|
-
input.blob_into(@ram)
|
|
71
|
-
@bam = input.boolean?
|
|
72
|
-
end
|
|
73
|
-
|
|
74
65
|
# Whether the BAM has been read since the last reset.
|
|
75
66
|
def bam? = @bam
|
|
76
67
|
|
|
@@ -14,14 +14,6 @@ module Badline
|
|
|
14
14
|
TYPES = %i[del seq prg usr rel].freeze
|
|
15
15
|
MODES = %i[write replace append].freeze
|
|
16
16
|
|
|
17
|
-
# Reads what save_state wrote after the flag that marks a file
|
|
18
|
-
# open for writing.
|
|
19
|
-
def self.from_state(input)
|
|
20
|
-
name = input.string
|
|
21
|
-
type = TYPES.fetch(input.int).to_sym
|
|
22
|
-
new(name, type, mode: MODES.fetch(input.int).to_sym).tap { |file| file.load_state(input) }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
17
|
def initialize(name, type, mode: :write)
|
|
26
18
|
@name = name
|
|
27
19
|
@type = type
|
|
@@ -29,16 +21,6 @@ module Badline
|
|
|
29
21
|
@bytes = []
|
|
30
22
|
end
|
|
31
23
|
|
|
32
|
-
def save_state(out)
|
|
33
|
-
out.boolean(true).string(@name).int(TYPES.index(@type)).int(MODES.index(@mode))
|
|
34
|
-
out.blob(@bytes).optional_int(@buffer)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def load_state(input)
|
|
38
|
-
@bytes = input.blob
|
|
39
|
-
@buffer = input.optional_int
|
|
40
|
-
end
|
|
41
|
-
|
|
42
24
|
def write(bytes) = @bytes.concat(bytes)
|
|
43
25
|
|
|
44
26
|
def writable? = true
|