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
|
@@ -147,25 +147,6 @@ module Badline
|
|
|
147
147
|
# vector, as U: does.
|
|
148
148
|
def reset! = reset(cold: true)
|
|
149
149
|
|
|
150
|
-
# The mounted storage's setup (Snapshot::StorageSetup), then the drive's
|
|
151
|
-
# RAM, its status and its open channels.
|
|
152
|
-
def save_state(out)
|
|
153
|
-
Snapshot::StorageSetup.write(@storage, out)
|
|
154
|
-
out.marker("TRAP DRIVE")
|
|
155
|
-
@memory.save_state(out)
|
|
156
|
-
@status.save_state(out)
|
|
157
|
-
@channels.save_state(out)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Takes the state after the storage's setup, into a drive mounted on
|
|
161
|
-
# the reopened storage.
|
|
162
|
-
def load_state(input)
|
|
163
|
-
input.marker("TRAP DRIVE")
|
|
164
|
-
@memory.load_state(input)
|
|
165
|
-
@status.load_state(input)
|
|
166
|
-
@channels.load_state(input, @memory)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
150
|
# Returns the next byte and whether it is the channel's last, or nil
|
|
170
151
|
# when there is nothing left to send.
|
|
171
152
|
def read(secondary)
|
|
@@ -6,17 +6,17 @@ module Badline
|
|
|
6
6
|
private
|
|
7
7
|
|
|
8
8
|
def active?
|
|
9
|
-
kernal_mapped? && @bus.peek(
|
|
9
|
+
kernal_mapped? && @bus.peek(zero_page.device) == DEVICE
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def filename
|
|
13
13
|
Storage.strip_drive_prefix(full_filename)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
# The file name FNADR points to, FNLEN bytes long.
|
|
17
17
|
def full_filename
|
|
18
|
-
pointer =
|
|
19
|
-
bytes = Array.new(@bus.peek(
|
|
18
|
+
pointer = peek_word(zero_page.filename_address)
|
|
19
|
+
bytes = Array.new(@bus.peek(zero_page.filename_length)) do |i|
|
|
20
20
|
@layout.filename_byte(@bus, (pointer + i) & 0xffff)
|
|
21
21
|
end
|
|
22
22
|
Storage.ascii(bytes)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module KernalTrap
|
|
5
|
+
class DOS
|
|
6
|
+
class Channel
|
|
7
|
+
# A channel's fields under `prefix`: whether it is a buffer in the
|
|
8
|
+
# drive's RAM, where it starts and ends, the pointer, the byte it
|
|
9
|
+
# leads with and its buffer's number, the error it reports once its
|
|
10
|
+
# bytes run out, and a file's bytes.
|
|
11
|
+
def save_fields(out, prefix)
|
|
12
|
+
out.text("#{prefix}kind", "channel").bool("#{prefix}writable", @writable).int("#{prefix}base", @base)
|
|
13
|
+
out.int("#{prefix}length", @length).int("#{prefix}pointer", @pointer).optional_int("#{prefix}lead", @lead)
|
|
14
|
+
out.optional_int("#{prefix}buffer", @buffer)
|
|
15
|
+
error = @error
|
|
16
|
+
out.ints("#{prefix}error", error) if error
|
|
17
|
+
out.bytes("#{prefix}bytes", @bytes) unless @writable
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# A buffer channel's bytes are the drive's `ram`.
|
|
21
|
+
def load_fields(input, prefix, ram)
|
|
22
|
+
@writable = input.bool("#{prefix}writable", false)
|
|
23
|
+
@base = input.int("#{prefix}base", 0)
|
|
24
|
+
@length = input.int("#{prefix}length", 0)
|
|
25
|
+
@pointer = input.int("#{prefix}pointer", 0)
|
|
26
|
+
@lead = input.optional_int("#{prefix}lead")
|
|
27
|
+
@buffer = input.optional_int("#{prefix}buffer")
|
|
28
|
+
error = input.ints("#{prefix}error", [])
|
|
29
|
+
@error = error.empty? ? nil : error.dup
|
|
30
|
+
@bytes = @writable ? ram : input.bytes("#{prefix}bytes", "".b).bytes
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class WriteFile
|
|
36
|
+
# A file open for writing, under `prefix`: its name, type and mode,
|
|
37
|
+
# the bytes written so far and the byte held back.
|
|
38
|
+
def save_fields(out, prefix)
|
|
39
|
+
out.text("#{prefix}kind", "write_file").bytes("#{prefix}name", @name.b).text("#{prefix}type", @type.to_s)
|
|
40
|
+
out.text("#{prefix}mode", @mode.to_s).bytes("#{prefix}bytes", @bytes).optional_int("#{prefix}held", @buffer)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.from_fields(input, prefix)
|
|
44
|
+
type = input.choice("#{prefix}type", TYPES.map(&:to_s), "prg")
|
|
45
|
+
mode = input.choice("#{prefix}mode", MODES.map(&:to_s), "write")
|
|
46
|
+
new(input.bytes("#{prefix}name", "".b), TYPES.find { |each| each.to_s == type } || :prg,
|
|
47
|
+
mode: MODES.find { |each| each.to_s == mode } || :write).tap do |file|
|
|
48
|
+
file.load_fields(input, prefix)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def load_fields(input, prefix)
|
|
53
|
+
@bytes = input.bytes("#{prefix}bytes", "".b).bytes
|
|
54
|
+
@buffer = input.optional_int("#{prefix}held")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Channels
|
|
59
|
+
# The channels open, by secondary address, each under channel<SA>.
|
|
60
|
+
def save_fields(out)
|
|
61
|
+
out.ints("channels", @channels.keys)
|
|
62
|
+
@channels.each { |secondary, channel| channel.save_fields(out, "channel#{secondary}.") }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def load_fields(input, ram)
|
|
66
|
+
@channels.clear
|
|
67
|
+
input.ints("channels", []).each do |secondary|
|
|
68
|
+
@channels[secondary] = Channels.channel_from_fields(input, "channel#{secondary}.", ram)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.channel_from_fields(input, prefix, ram)
|
|
73
|
+
return WriteFile.from_fields(input, prefix) if input.text("#{prefix}kind", "channel") == "write_file"
|
|
74
|
+
|
|
75
|
+
Channel.new.load_fields(input, prefix, ram)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Status
|
|
80
|
+
# The error channel, under status.
|
|
81
|
+
def save_fields(out) = @channel.save_fields(out, "status.")
|
|
82
|
+
|
|
83
|
+
def load_fields(input)
|
|
84
|
+
input.choice("status.kind", ["channel"], "channel")
|
|
85
|
+
@channel = Channel.new.load_fields(input, "status.", [])
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Memory
|
|
90
|
+
# The drive's RAM, and whether the BAM has been read.
|
|
91
|
+
def save_fields(out)
|
|
92
|
+
out.bytes("ram", @ram).bool("bam", @bam)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def load_fields(input)
|
|
96
|
+
input.bytes_into("ram", @ram)
|
|
97
|
+
@bam = input.bool("bam", @bam)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The storage device 8 serves.
|
|
102
|
+
attr_reader :storage
|
|
103
|
+
|
|
104
|
+
# The DOS's fields in a keyed snapshot module (BL-TRAPDOS), after the
|
|
105
|
+
# storage's under storage.*: its RAM, the error channel and the
|
|
106
|
+
# channels open.
|
|
107
|
+
def save_fields(out)
|
|
108
|
+
@memory.save_fields(out)
|
|
109
|
+
@status.save_fields(out)
|
|
110
|
+
@channels.save_fields(out)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def load_fields(input)
|
|
114
|
+
@memory.load_fields(input)
|
|
115
|
+
@status.load_fields(input)
|
|
116
|
+
@channels.load_fields(input, @memory.ram)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class Serial
|
|
121
|
+
# The serial trap's fields under `prefix`: whether device 8 listens
|
|
122
|
+
# or talks, on which channel, the frame it is in and the bytes it has
|
|
123
|
+
# gathered.
|
|
124
|
+
def save_fields(out, prefix)
|
|
125
|
+
out.bool("#{prefix}listening", @listening).bool("#{prefix}talking", @talking)
|
|
126
|
+
out.optional_int("#{prefix}listen_channel", @listen_channel)
|
|
127
|
+
out.optional_int("#{prefix}talk_channel", @talk_channel).int("#{prefix}frame", @frame)
|
|
128
|
+
out.bytes("#{prefix}buffer", @buffer)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def load_fields(input, prefix)
|
|
132
|
+
@listening = input.bool("#{prefix}listening", @listening)
|
|
133
|
+
@talking = input.bool("#{prefix}talking", @talking)
|
|
134
|
+
@listen_channel = input.optional_int("#{prefix}listen_channel")
|
|
135
|
+
@talk_channel = input.optional_int("#{prefix}talk_channel")
|
|
136
|
+
@frame = input.int("#{prefix}frame", @frame)
|
|
137
|
+
@buffer = input.bytes("#{prefix}buffer", "".b).bytes
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class Save
|
|
142
|
+
# Whether a SAVE is under way, under `prefix`.
|
|
143
|
+
def save_fields(out, prefix) = out.bool("#{prefix}saving", @saving)
|
|
144
|
+
|
|
145
|
+
def load_fields(input, prefix)
|
|
146
|
+
@saving = input.bool("#{prefix}saving", @saving)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -20,7 +20,11 @@ module Badline
|
|
|
20
20
|
# +banked+ says whether the 6510 port can bank the KERNAL out, and so
|
|
21
21
|
# whether RAM lies under every ROM and the I/O. +mmu+ says the C128's
|
|
22
22
|
# MMU maps the KERNAL instead, and that the file name and the file's
|
|
23
|
-
# bytes lie in the banks the KERNAL keeps at FNBANK and BA.
|
|
23
|
+
# bytes lie in the banks the KERNAL keeps at FNBANK and BA.
|
|
24
|
+
# +rom_select+ says the 264 series' TED selects ROM or RAM and its
|
|
25
|
+
# latch the ROM banks, so the KERNAL is in while ROM is selected with
|
|
26
|
+
# the KERNAL's bank, and the ROM reads the file name and the file's
|
|
27
|
+
# bytes from RAM, under any ROM, through its bank fetch at $07D9. Each
|
|
24
28
|
# serial byte starts a one-shot timer by writing the high byte of its
|
|
25
29
|
# count to +serial_timer+, and the control value TIMER_ONE_SHOT_START
|
|
26
30
|
# to +serial_timer_start+ where the timer has a control register to
|
|
@@ -29,16 +33,19 @@ module Badline
|
|
|
29
33
|
# +serial_timer+. The ROM lets go of each serial line by reading a
|
|
30
34
|
# port, masking the line's bit off and writing it back: ATN at
|
|
31
35
|
# +atn_port+ with +atn_mask+, then the clock and the data line.
|
|
36
|
+
# +zero_page+ is a ZeroPage, where the KERNAL keeps the file's
|
|
37
|
+
# parameters and the status the traps read and set.
|
|
32
38
|
Layout = Data.define(:load, :save, :searching_message, :loading_message, :load_byte_loop, :load_done,
|
|
33
39
|
:file_not_found_exit, :missing_file_name_exit, :saving_message, :clock_release,
|
|
34
40
|
:data_release, :save_done, :talk, :listen, :second, :tksa, :ciout, :untalk,
|
|
35
41
|
:unlisten, :acptr, :banked, :serial_timer, :serial_timer_start, :atn_port, :atn_mask,
|
|
36
|
-
:clock_port, :clock_mask, :data_port, :data_mask, :mmu) do
|
|
42
|
+
:clock_port, :clock_mask, :data_port, :data_mask, :mmu, :rom_select, :zero_page) do
|
|
37
43
|
# Whether the CPU reads the KERNAL ROM at the trapped addresses: on a
|
|
38
|
-
# banked machine, the 6510 port's banking bits select it,
|
|
39
|
-
# C128 the MMU.
|
|
44
|
+
# banked machine, the 6510 port's banking bits select it, on the
|
|
45
|
+
# C128 the MMU, and on the 264 series the ROM select and bank latch.
|
|
40
46
|
def kernal_mapped?(bus)
|
|
41
47
|
return bus.system_rom_mapped? if mmu
|
|
48
|
+
return bus.kernal_selected? if rom_select
|
|
42
49
|
|
|
43
50
|
!banked || bus.io_port.kernal?
|
|
44
51
|
end
|
|
@@ -55,15 +62,21 @@ module Badline
|
|
|
55
62
|
end
|
|
56
63
|
|
|
57
64
|
# The file name's byte at +address+, in the bank at FNBANK on the
|
|
58
|
-
# C128.
|
|
65
|
+
# C128 and in RAM on the 264 series.
|
|
59
66
|
def filename_byte(bus, address)
|
|
60
|
-
|
|
67
|
+
return bus.peek_bank(bus.peek(FILENAME_BANK), address) if mmu
|
|
68
|
+
return bus.ram_view.peek(address) if rom_select
|
|
69
|
+
|
|
70
|
+
bus.peek(address)
|
|
61
71
|
end
|
|
62
72
|
|
|
63
73
|
# The byte at +address+ a SAVE writes or a VERIFY compares, in the bank
|
|
64
|
-
# at BA on the C128.
|
|
74
|
+
# at BA on the C128 and in RAM on the 264 series.
|
|
65
75
|
def file_byte(bus, address)
|
|
66
|
-
|
|
76
|
+
return bus.peek_bank(bus.peek(BANK), address) if mmu
|
|
77
|
+
return bus.ram_view.peek(address) if rom_select
|
|
78
|
+
|
|
79
|
+
bus.peek(address)
|
|
67
80
|
end
|
|
68
81
|
|
|
69
82
|
# Starts the timer the ROM times a serial byte with, its count's high
|
|
@@ -95,6 +108,32 @@ module Badline
|
|
|
95
108
|
end
|
|
96
109
|
end
|
|
97
110
|
|
|
111
|
+
# The zero-page variables of a KERNAL's file routines, by address:
|
|
112
|
+
# +status+ (ST), +verify+ (VERCK, 0 for LOAD and 1 for VERIFY),
|
|
113
|
+
# +start_address+ (SAL), +end_address+ (EAL), +filename_length+
|
|
114
|
+
# (FNLEN), +secondary_address+ (SA), +device+ (FA), +filename_address+
|
|
115
|
+
# (FNADR), +save_start+ (STAL) and +load_address+ (MEMUSS, where LOAD
|
|
116
|
+
# puts a file loaded with secondary address 0). Each two-byte one names
|
|
117
|
+
# its low byte, and the high byte follows it.
|
|
118
|
+
ZeroPage = Data.define(:status, :verify, :start_address, :end_address, :filename_length, :secondary_address,
|
|
119
|
+
:device, :filename_address, :save_start, :load_address)
|
|
120
|
+
|
|
121
|
+
# The C64's file variables. The VIC-20 and C128 KERNALs keep theirs at
|
|
122
|
+
# the same addresses.
|
|
123
|
+
C64_ZERO_PAGE = ZeroPage.new(
|
|
124
|
+
status: 0x90, verify: 0x93, start_address: 0xac, end_address: 0xae, filename_length: 0xb7,
|
|
125
|
+
secondary_address: 0xb9, device: 0xba, filename_address: 0xbb, save_start: 0xc1, load_address: 0xc3
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# The 264 series KERNAL's file variables, as its LOAD ($F04A) and SAVE
|
|
129
|
+
# ($F1A4) use them: SAVE walks SAL at $9B from STAL at $B2 up to EAL at
|
|
130
|
+
# $9D, and LOAD ends with EAL at $9D, or starts it from MEMUSS at $B4
|
|
131
|
+
# for secondary address 0.
|
|
132
|
+
PLUS4_ZERO_PAGE = ZeroPage.new(
|
|
133
|
+
status: 0x90, verify: 0x93, start_address: 0x9b, end_address: 0x9d, filename_length: 0xab,
|
|
134
|
+
secondary_address: 0xad, device: 0xae, filename_address: 0xaf, save_start: 0xb2, load_address: 0xb4
|
|
135
|
+
)
|
|
136
|
+
|
|
98
137
|
# A CIA's control value that starts its timer: force load, one-shot,
|
|
99
138
|
# start.
|
|
100
139
|
TIMER_ONE_SHOT_START = 0x19
|
|
@@ -117,7 +156,7 @@ module Badline
|
|
|
117
156
|
ciout: 0xeddd, untalk: 0xedef, unlisten: 0xedfe, acptr: 0xee13,
|
|
118
157
|
banked: true, serial_timer: 0xdc07, serial_timer_start: 0xdc0f,
|
|
119
158
|
atn_port: 0xdd00, atn_mask: 0xf7, clock_port: 0xdd00, clock_mask: 0xef, data_port: 0xdd00, data_mask: 0xdf,
|
|
120
|
-
mmu: false
|
|
159
|
+
mmu: false, rom_select: false, zero_page: C64_ZERO_PAGE
|
|
121
160
|
)
|
|
122
161
|
|
|
123
162
|
# The VIC-20 KERNAL, 901486-07 (PAL). The NTSC one, 901486-06, differs
|
|
@@ -135,7 +174,7 @@ module Badline
|
|
|
135
174
|
ciout: 0xeee4, untalk: 0xeef6, unlisten: 0xef04, acptr: 0xef19,
|
|
136
175
|
banked: false, serial_timer: 0x9129, serial_timer_start: nil,
|
|
137
176
|
atn_port: 0x911f, atn_mask: 0x7f, clock_port: 0x912c, clock_mask: 0xfd, data_port: 0x912c, data_mask: 0xdf,
|
|
138
|
-
mmu: false
|
|
177
|
+
mmu: false, rom_select: false, zero_page: C64_ZERO_PAGE
|
|
139
178
|
)
|
|
140
179
|
|
|
141
180
|
# The C128 KERNAL, 318020-05, in C128 mode: the jump table's targets
|
|
@@ -156,7 +195,32 @@ module Badline
|
|
|
156
195
|
ciout: 0xe503, untalk: 0xe515, unlisten: 0xe526, acptr: 0xe43e,
|
|
157
196
|
banked: true, serial_timer: nil, serial_timer_start: nil,
|
|
158
197
|
atn_port: 0xdd00, atn_mask: 0xf7, clock_port: 0xdd00, clock_mask: 0xef, data_port: 0xdd00, data_mask: 0xdf,
|
|
159
|
-
mmu: true
|
|
198
|
+
mmu: true, rom_select: false, zero_page: C64_ZERO_PAGE
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# The 264 series KERNAL, 318004-05 (PAL), the same at every address
|
|
202
|
+
# here in the NTSC 318005-05: ILOAD's and ISAVE's default targets and
|
|
203
|
+
# the jump table's targets for the serial primitives, each of which
|
|
204
|
+
# tries a 1551's TCBM interface for devices 8 and 9 before the serial
|
|
205
|
+
# bus. LOAD prints SEARCHING FOR at $F160 and LOADING or VERIFYING at
|
|
206
|
+
# $F189, which ends CLC, retries a byte from $F09F and returns at $F0EB
|
|
207
|
+
# (LDX $9D, LDY $9E, RTS). FILE NOT FOUND leaves through $F27C and
|
|
208
|
+
# MISSING FILE NAME through $F288. SAVE prints SAVING at $F228 and
|
|
209
|
+
# returns at $F226 (CLC, RTS). The port at $01 drives the lines, ATN
|
|
210
|
+
# from bit 2, the clock from bit 1 ($E2B8 lets it go) and the data line
|
|
211
|
+
# from bit 0 ($E2C6). Its serial bytes are timed with TED timer 2, but
|
|
212
|
+
# its timeouts differ from the C64's, so the traps leave the timer
|
|
213
|
+
# alone.
|
|
214
|
+
PLUS4_LAYOUT = Layout.new(
|
|
215
|
+
load: 0xf04a, save: 0xf1a4,
|
|
216
|
+
searching_message: 0xf160, loading_message: 0xf189, load_byte_loop: 0xf09f, load_done: 0xf0eb,
|
|
217
|
+
file_not_found_exit: 0xf27c, missing_file_name_exit: 0xf288,
|
|
218
|
+
saving_message: 0xf228, clock_release: 0xe2b8, data_release: 0xe2c6, save_done: 0xf226,
|
|
219
|
+
talk: 0xedfa, listen: 0xee2c, second: 0xee4d, tksa: 0xee1a,
|
|
220
|
+
ciout: 0xecdf, untalk: 0xef3b, unlisten: 0xef23, acptr: 0xec8b,
|
|
221
|
+
banked: false, serial_timer: nil, serial_timer_start: nil,
|
|
222
|
+
atn_port: 0x01, atn_mask: 0xfb, clock_port: 0x01, clock_mask: 0xfd, data_port: 0x01, data_mask: 0xfe,
|
|
223
|
+
mmu: false, rom_select: true, zero_page: PLUS4_ZERO_PAGE
|
|
160
224
|
)
|
|
161
225
|
end
|
|
162
226
|
end
|
|
@@ -31,8 +31,8 @@ module Badline
|
|
|
31
31
|
def call
|
|
32
32
|
return unless active?
|
|
33
33
|
|
|
34
|
-
@bus.poke(
|
|
35
|
-
@bus.poke(
|
|
34
|
+
@bus.poke(zero_page.verify, @cpu.a)
|
|
35
|
+
@bus.poke(zero_page.status, 0x00)
|
|
36
36
|
name = filename
|
|
37
37
|
return @cpu.program_counter = @layout.missing_file_name_exit if Storage.parse_name(name).first.empty?
|
|
38
38
|
|
|
@@ -66,17 +66,17 @@ module Badline
|
|
|
66
66
|
# until RUN/STOP breaks the load, as on a real drive.
|
|
67
67
|
def finish(data, complete)
|
|
68
68
|
if data.empty?
|
|
69
|
-
@bus.poke(
|
|
69
|
+
@bus.poke(zero_page.status, EOI | READ_TIMEOUT)
|
|
70
70
|
leave_bus(ACPTR_TIMEOUT)
|
|
71
71
|
continue_with(@layout.searching_message, @layout.file_not_found_exit)
|
|
72
72
|
else
|
|
73
73
|
deliver(data)
|
|
74
|
-
@bus.poke(
|
|
74
|
+
@bus.poke(zero_page.status, @bus.peek(zero_page.status) & ~EOI) unless complete
|
|
75
75
|
leave_bus(ISOUR_TIMEOUT)
|
|
76
76
|
continue_with(@layout.searching_message, @layout.loading_message,
|
|
77
77
|
complete ? @layout.load_done : @layout.load_byte_loop)
|
|
78
78
|
end
|
|
79
|
-
@bus.poke(
|
|
79
|
+
@bus.poke(zero_page.secondary_address, LOAD_SECONDARY)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# A load ends with the UNLISTEN that closes the file, a byte sent,
|
|
@@ -91,9 +91,9 @@ module Badline
|
|
|
91
91
|
load? && data.length > 2 && load_address(data) < 0x0334
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
# A=0 is LOAD, A=1 is VERIFY, kept at
|
|
94
|
+
# A=0 is LOAD, A=1 is VERIFY, kept at VERCK
|
|
95
95
|
def load?
|
|
96
|
-
@bus.peek(
|
|
96
|
+
@bus.peek(zero_page.verify).zero?
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def deliver(data)
|
|
@@ -102,25 +102,23 @@ module Badline
|
|
|
102
102
|
payload = payload[0, 0x10000 - addr] if addr + payload.length > 0x10000
|
|
103
103
|
if load?
|
|
104
104
|
@layout.store_file(@bus, addr, payload)
|
|
105
|
-
@bus.poke(
|
|
105
|
+
@bus.poke(zero_page.status, EOI)
|
|
106
106
|
else
|
|
107
107
|
verify(addr, payload)
|
|
108
108
|
end
|
|
109
|
-
|
|
110
|
-
@bus.poke(0xae, low_byte(end_addr))
|
|
111
|
-
@bus.poke(0xaf, high_byte(end_addr))
|
|
109
|
+
poke_word(zero_page.end_address, (addr + payload.length) & 0xffff)
|
|
112
110
|
end
|
|
113
111
|
|
|
114
112
|
def verify(addr, payload)
|
|
115
113
|
match = payload.each_with_index.all? { |byte, i| @layout.file_byte(@bus, addr + i) == byte }
|
|
116
|
-
@bus.poke(
|
|
114
|
+
@bus.poke(zero_page.status, match ? EOI : EOI | VERIFY_MISMATCH)
|
|
117
115
|
end
|
|
118
116
|
|
|
119
|
-
# Secondary address
|
|
120
|
-
# stashed at
|
|
117
|
+
# Secondary address zero relocates to the caller-supplied address
|
|
118
|
+
# stashed at MEMUSS
|
|
121
119
|
def load_address(data)
|
|
122
|
-
if @bus.peek(
|
|
123
|
-
|
|
120
|
+
if @bus.peek(zero_page.secondary_address).zero?
|
|
121
|
+
peek_word(zero_page.load_address)
|
|
124
122
|
else
|
|
125
123
|
uint16(data[0], data[1].to_i)
|
|
126
124
|
end
|
|
@@ -26,6 +26,18 @@ module Badline
|
|
|
26
26
|
@layout.kernal_mapped?(@bus)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def zero_page = @layout.zero_page
|
|
30
|
+
|
|
31
|
+
# The two-byte value whose low byte is at +address+.
|
|
32
|
+
def peek_word(address)
|
|
33
|
+
uint16(@bus.peek(address), @bus.peek(address + 1))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def poke_word(address, value)
|
|
37
|
+
@bus.poke(address, low_byte(value))
|
|
38
|
+
@bus.poke(address + 1, high_byte(value))
|
|
39
|
+
end
|
|
40
|
+
|
|
29
41
|
# The ROM times each byte on the serial bus with a one-shot timer
|
|
30
42
|
# (Layout#time_serial_byte): ISOUR_TIMEOUT for a byte sent,
|
|
31
43
|
# ACPTR_TIMEOUT for one received.
|
|
@@ -18,7 +18,7 @@ module Badline
|
|
|
18
18
|
class Save < FileRoutine
|
|
19
19
|
SECONDARY = 0x61
|
|
20
20
|
|
|
21
|
-
# ST bit
|
|
21
|
+
# ST bit
|
|
22
22
|
DEVICE_NOT_PRESENT = 0x80
|
|
23
23
|
|
|
24
24
|
def initialize(cpu:, bus:, layout:, dos:)
|
|
@@ -27,23 +27,15 @@ module Badline
|
|
|
27
27
|
@saving = false
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def save_state(out)
|
|
31
|
-
out.boolean(@saving)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def load_state(input)
|
|
35
|
-
@saving = input.boolean?
|
|
36
|
-
end
|
|
37
|
-
|
|
38
30
|
def call
|
|
39
31
|
return unless active?
|
|
40
32
|
return finish if @saving
|
|
41
33
|
return unless @dos.saves?
|
|
42
34
|
|
|
43
|
-
@bus.poke(
|
|
35
|
+
@bus.poke(zero_page.secondary_address, SECONDARY)
|
|
44
36
|
return @cpu.program_counter = @layout.missing_file_name_exit if name.empty?
|
|
45
37
|
|
|
46
|
-
@bus.poke(
|
|
38
|
+
@bus.poke(zero_page.status, 0x00)
|
|
47
39
|
@saving = true
|
|
48
40
|
continue_with(@layout.saving_message, @layout.save)
|
|
49
41
|
end
|
|
@@ -57,19 +49,17 @@ module Badline
|
|
|
57
49
|
def finish
|
|
58
50
|
@saving = false
|
|
59
51
|
saved = @dos.save(name, payload, replace: full_filename.start_with?("@"))
|
|
60
|
-
@bus.poke(
|
|
61
|
-
|
|
62
|
-
@bus.poke(0xad, @bus.peek(0xaf))
|
|
52
|
+
@bus.poke(zero_page.status, DEVICE_NOT_PRESENT) unless saved
|
|
53
|
+
poke_word(zero_page.start_address, peek_word(zero_page.end_address))
|
|
63
54
|
@cpu.y = 0
|
|
64
55
|
@cpu.status.overflow = true
|
|
65
56
|
continue_with(@layout.clock_release, @layout.data_release, @layout.save_done)
|
|
66
57
|
end
|
|
67
58
|
|
|
68
|
-
# Start address at
|
|
69
|
-
# $AE/$AF (EAL)
|
|
59
|
+
# Start address at STAL, end address (exclusive) at EAL
|
|
70
60
|
def payload
|
|
71
|
-
start =
|
|
72
|
-
length = (
|
|
61
|
+
start = peek_word(zero_page.save_start)
|
|
62
|
+
length = (peek_word(zero_page.end_address) - start) & 0xffff
|
|
73
63
|
[low_byte(start), high_byte(start)] +
|
|
74
64
|
Array.new(length) { |i| @layout.file_byte(@bus, (start + i) & 0xffff) }
|
|
75
65
|
end
|
|
@@ -35,22 +35,6 @@ module Badline
|
|
|
35
35
|
@buffer = []
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# The frame under way, with the channels and the bytes it gathered.
|
|
39
|
-
# The device it answers is the machine's to set.
|
|
40
|
-
def save_state(out)
|
|
41
|
-
out.boolean(@listening).boolean(@talking).optional_int(@listen_channel).optional_int(@talk_channel)
|
|
42
|
-
out.int(@frame).blob(@buffer)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def load_state(input)
|
|
46
|
-
@listening = input.boolean?
|
|
47
|
-
@talking = input.boolean?
|
|
48
|
-
@listen_channel = input.optional_int
|
|
49
|
-
@talk_channel = input.optional_int
|
|
50
|
-
@frame = input.int
|
|
51
|
-
@buffer = input.blob
|
|
52
|
-
end
|
|
53
|
-
|
|
54
38
|
def install
|
|
55
39
|
routines.each do |address, routine|
|
|
56
40
|
@cpu.install_trap(address) { call_routine(routine) if kernal_mapped? }
|
|
@@ -205,7 +189,7 @@ module Badline
|
|
|
205
189
|
def update_status(bits)
|
|
206
190
|
return if bits.zero?
|
|
207
191
|
|
|
208
|
-
@bus.poke(
|
|
192
|
+
@bus.poke(zero_page.status, @bus.peek(zero_page.status) | bits)
|
|
209
193
|
end
|
|
210
194
|
end
|
|
211
195
|
end
|
data/lib/badline/kernal_trap.rb
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Machine
|
|
5
|
+
# Typing into the KERNAL's keyboard buffer once the machine has booted.
|
|
6
|
+
# The machine that includes it answers #keyboard_buffer, the Layout of
|
|
7
|
+
# the KERNAL it runs, and #ram.
|
|
8
|
+
module KeyboardBuffer
|
|
9
|
+
# Where a KERNAL keeps its keyboard buffer (KEYD), the count of
|
|
10
|
+
# characters in it (NDX), and how many it holds.
|
|
11
|
+
Layout = Data.define(:address, :count, :capacity)
|
|
12
|
+
|
|
13
|
+
# The C64's and the VIC-20's.
|
|
14
|
+
C64_LAYOUT = Layout.new(address: 0x0277, count: 0xc6, capacity: 10)
|
|
15
|
+
|
|
16
|
+
# BASIC 7.0's, in C128 mode.
|
|
17
|
+
C128_LAYOUT = Layout.new(address: 0x034a, count: 0xd0, capacity: 10)
|
|
18
|
+
|
|
19
|
+
# The 264 series KERNAL's, which sets its capacity at $053F to 10
|
|
20
|
+
# ($D86E).
|
|
21
|
+
PLUS4_LAYOUT = Layout.new(address: 0x0527, count: 0xef, capacity: 10)
|
|
22
|
+
|
|
23
|
+
def type_text(text)
|
|
24
|
+
codes = text.bytes.map { |b| ascii_to_petscii(b) }
|
|
25
|
+
on_init { @pending_keys = codes }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def feed_keyboard
|
|
31
|
+
buffer = keyboard_buffer
|
|
32
|
+
return unless ram.peek(buffer.count).zero?
|
|
33
|
+
|
|
34
|
+
chunk = @pending_keys.shift(buffer.capacity)
|
|
35
|
+
ram.write(buffer.address, chunk)
|
|
36
|
+
ram.poke(buffer.count, chunk.length)
|
|
37
|
+
@pending_keys = nil if @pending_keys.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ascii_to_petscii(byte)
|
|
41
|
+
case byte
|
|
42
|
+
when 0x61..0x7a then byte - 0x20
|
|
43
|
+
when 0x41..0x5a then byte + 0x80
|
|
44
|
+
else byte
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Machine
|
|
5
|
+
# Brings the machine to the point a snapshot file is saved at: the main
|
|
6
|
+
# CPU between instructions, or jammed, with no REU transfer holding the
|
|
7
|
+
# bus. C64, Vic20, C128 and Plus4 each include it, and answer
|
|
8
|
+
# #settled_for_snapshot? and #settle_chips_for_snapshot, which brings
|
|
9
|
+
# what each chip defers up to the cycle without running one.
|
|
10
|
+
module Settling
|
|
11
|
+
# The cycles settling may run: the longest instruction stretched by
|
|
12
|
+
# the VIC's longest hold on the bus, and an REU swap of 64K, two
|
|
13
|
+
# cycles a byte, stretched the same way.
|
|
14
|
+
LIMIT = 100 + 0x40000
|
|
15
|
+
|
|
16
|
+
# Runs the machine on, through its own #cycle!, to the settle point,
|
|
17
|
+
# and returns the cycles that took. A settled machine runs none.
|
|
18
|
+
def settle_for_snapshot!
|
|
19
|
+
ran = 0
|
|
20
|
+
while ran < LIMIT && !settled_for_snapshot?
|
|
21
|
+
cycle!
|
|
22
|
+
ran += 1
|
|
23
|
+
end
|
|
24
|
+
settle_chips_for_snapshot
|
|
25
|
+
ran
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|