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
data/lib/badline/chips/vic.rb
CHANGED
|
@@ -5,14 +5,13 @@ require "badline/chips/vic/registers"
|
|
|
5
5
|
require "badline/chips/vic/display_state"
|
|
6
6
|
require "badline/chips/vic/sequencer"
|
|
7
7
|
require "badline/chips/vic/sprites"
|
|
8
|
-
require "badline/chips/vic/
|
|
8
|
+
require "badline/chips/vic/keyed_parts"
|
|
9
9
|
|
|
10
10
|
module Badline
|
|
11
11
|
module Chips
|
|
12
12
|
class VIC
|
|
13
13
|
include Addressable
|
|
14
14
|
include IntegerHelper
|
|
15
|
-
include SavedState
|
|
16
15
|
|
|
17
16
|
# The 6569 of the breadbin C64, the HMOS 8565 of the C64C, and the
|
|
18
17
|
# C128's VIC-IIe, the 8566 (PAL) and 8564 (NTSC). The VIC-IIe draws as
|
|
@@ -229,6 +228,11 @@ module Badline
|
|
|
229
228
|
@sequencer.start_vertical_border(@rasterline == @last_line ? 0 : @rasterline + 1) if hooks.anybits?(HOOK_LAST)
|
|
230
229
|
end
|
|
231
230
|
|
|
231
|
+
# Latches the sprite collisions up to the beam, as a read of $D01E or
|
|
232
|
+
# $D01F would, so the registers stand as the hardware has them when a
|
|
233
|
+
# snapshot is saved.
|
|
234
|
+
def fold_collisions! = @sprites.collide_upto(@column * 8, @sequencer.fg)
|
|
235
|
+
|
|
232
236
|
# The IRQ line is held asserted while any enabled latch bit is set in
|
|
233
237
|
# $D019/$D01A, until the program acknowledges it by writing to $D019.
|
|
234
238
|
def interrupted?
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class VICI
|
|
6
|
+
class Sound
|
|
7
|
+
# The sound's fields in a keyed snapshot module, under `prefix`:
|
|
8
|
+
# the voices' shift registers, the noise LFSR, the level, and when
|
|
9
|
+
# each voice next shifts, in cycles from the machine's clock, with
|
|
10
|
+
# how far behind the clock the voices last ran. The volume and the
|
|
11
|
+
# voices' frequencies come from the VIC's registers on load.
|
|
12
|
+
module KeyedState
|
|
13
|
+
def save_fields(out, prefix)
|
|
14
|
+
now = @clock.cycles
|
|
15
|
+
run_to(now)
|
|
16
|
+
out.ints("#{prefix}shift_registers", @shift_registers).int("#{prefix}lfsr", @lfsr)
|
|
17
|
+
out.int("#{prefix}level", @level).int("#{prefix}ran_ago", now - @time)
|
|
18
|
+
out.ints("#{prefix}next_shifts", @next_shifts.map { |due| due - now })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def load_fields(input, prefix)
|
|
22
|
+
now = @clock.cycles
|
|
23
|
+
input.ints_into("#{prefix}shift_registers", @shift_registers)
|
|
24
|
+
@lfsr = input.masked("#{prefix}lfsr", @lfsr, 0xffff)
|
|
25
|
+
@level = input.int("#{prefix}level", @level)
|
|
26
|
+
@time = now - input.int("#{prefix}ran_ago", now - @time)
|
|
27
|
+
relative = input.ints("#{prefix}next_shifts", @next_shifts.map { |due| due - now })
|
|
28
|
+
VOICES.times { |voice| @next_shifts[voice] = now + relative.fetch(voice, 0) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# $900A to $900E as written.
|
|
32
|
+
def restore_registers(registers)
|
|
33
|
+
@volume = registers[0x0e] & 0x0f
|
|
34
|
+
VOICES.times do |voice|
|
|
35
|
+
value = registers[0x0a + voice]
|
|
36
|
+
@frequencies[voice] = value & 0x7f
|
|
37
|
+
@enabled[voice] = value >= 0x80
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
include KeyedState
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Painter
|
|
46
|
+
# The lines' fetches and the line going out, as this painter lays
|
|
47
|
+
# them out (VICI::LINE_LAYOUT), and the picture.
|
|
48
|
+
module KeyedState
|
|
49
|
+
def save_fields(out)
|
|
50
|
+
out.int("line.row", @row).int("line.painted", @painted).bool("line.written", @written)
|
|
51
|
+
out.bytes("line.patterns", @patterns).bytes("line.colors", @colors)
|
|
52
|
+
out.ints("line.counts", @counts).ints("line.starts", @starts).bytes("display", @display)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def load_fields(input, layout)
|
|
56
|
+
input.bytes_into("display", @display)
|
|
57
|
+
return unless layout
|
|
58
|
+
|
|
59
|
+
@row = input.int("line.row", @row)
|
|
60
|
+
@painted = input.int("line.painted", @painted)
|
|
61
|
+
@written = input.bool("line.written", @written)
|
|
62
|
+
input.bytes_into("line.patterns", @patterns)
|
|
63
|
+
input.bytes_into("line.colors", @colors)
|
|
64
|
+
input.ints_into("line.counts", @counts)
|
|
65
|
+
input.ints_into("line.starts", @starts)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The colours as $900E and $900F hold them, every line to be
|
|
69
|
+
# painted afresh, and the whole display changed.
|
|
70
|
+
def restored!(registers)
|
|
71
|
+
@border = registers[0x0f] & 0x07
|
|
72
|
+
@background = registers[0x0f] >> 4
|
|
73
|
+
@reverse = registers[0x0f].nobits?(0x08)
|
|
74
|
+
@aux = registers[0x0e] >> 4
|
|
75
|
+
@stale.fill(true)
|
|
76
|
+
@memo_keys.fill(-1)
|
|
77
|
+
@dirty_lines.fill(true)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
include KeyedState
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The shape of the painter's per-line fields. A snapshot whose
|
|
85
|
+
# line.layout differs leaves them at power-on.
|
|
86
|
+
LINE_LAYOUT = 1
|
|
87
|
+
|
|
88
|
+
# The VIC-I's fields in a keyed snapshot module (Snapshot::Keyed): the
|
|
89
|
+
# registers, the beam, the text window's counters and the fetch in
|
|
90
|
+
# flight, the painter's lines under line.*, the picture, and the sound
|
|
91
|
+
# under sound.* What the registers decode to, where the line's fetches
|
|
92
|
+
# end and the next event are worked out on load.
|
|
93
|
+
module KeyedState
|
|
94
|
+
def save_fields(out)
|
|
95
|
+
out.bytes("registers", @registers).int("raster", @rasterline).int("column", @column)
|
|
96
|
+
out.bool("window_open", @window_open).bool("window_done", @window_done).int("row_start", @row_start)
|
|
97
|
+
out.int("matrix", @matrix).int("char_line", @char_line).int("text_rows", @text_rows)
|
|
98
|
+
out.int("fetch_from", @fetch_from).int("fetched", @fetched).int("code", @code)
|
|
99
|
+
out.int("line.layout", LINE_LAYOUT)
|
|
100
|
+
@painter.save_fields(out)
|
|
101
|
+
@sound&.save_fields(out, "sound.")
|
|
102
|
+
out
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def load_fields(input)
|
|
106
|
+
input.bytes_into("registers", @registers)
|
|
107
|
+
@rasterline = input.int("raster", @rasterline)
|
|
108
|
+
@column = input.int("column", @column)
|
|
109
|
+
@window_open = input.bool("window_open", @window_open)
|
|
110
|
+
@window_done = input.bool("window_done", @window_done)
|
|
111
|
+
@row_start = input.int("row_start", @row_start)
|
|
112
|
+
@matrix = input.int("matrix", @matrix)
|
|
113
|
+
@char_line = input.int("char_line", @char_line)
|
|
114
|
+
@text_rows = input.int("text_rows", @text_rows)
|
|
115
|
+
@fetch_from = input.int("fetch_from", @fetch_from)
|
|
116
|
+
@fetched = input.int("fetched", @fetched)
|
|
117
|
+
@code = input.byte("code", @code)
|
|
118
|
+
line = input.int("line.layout", 0) == LINE_LAYOUT
|
|
119
|
+
input.adjust("line in progress from another renderer, restarted") unless line || input.missing?
|
|
120
|
+
@painter.load_fields(input, line)
|
|
121
|
+
@sound&.load_fields(input, "sound.")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def restored!
|
|
125
|
+
@origin_x = @registers[0] & 0x7f
|
|
126
|
+
@columns = @registers[2] & 0x7f
|
|
127
|
+
@origin_y = @registers[1]
|
|
128
|
+
rows_written(@registers[3])
|
|
129
|
+
bases_written
|
|
130
|
+
@slot = @rasterline * Painter::SLOTS
|
|
131
|
+
@fetch_to = 0
|
|
132
|
+
fetches_end if @window_open
|
|
133
|
+
schedule
|
|
134
|
+
@painter.restored!(@registers)
|
|
135
|
+
@sound&.restore_registers(@registers)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
include KeyedState
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/chips/vic_i/painter/saved_state"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
4
|
module Chips
|
|
7
5
|
class VICI
|
|
@@ -28,8 +26,6 @@ module Badline
|
|
|
28
26
|
# nibble's low three bits) and 11 the auxiliary colour, whatever
|
|
29
27
|
# $900F bit 3 says (MOS 6560/6561 datasheet, "Color operating modes").
|
|
30
28
|
class Painter
|
|
31
|
-
include SavedState
|
|
32
|
-
|
|
33
29
|
# The characters a line holds room for. A line fetches at most 34.
|
|
34
30
|
SLOTS = 40
|
|
35
31
|
|
|
@@ -30,10 +30,11 @@ module Badline
|
|
|
30
30
|
# The output is the voices that are high, plus a little more, times the
|
|
31
31
|
# volume, so a volume write steps the output even with every voice off.
|
|
32
32
|
#
|
|
33
|
-
# The voices
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
33
|
+
# The voices catch up to the current cycle whenever a write lands, the
|
|
34
|
+
# samples are drained or #catch_up! asks, one shift at a time, whether
|
|
35
|
+
# or not #record collects the output, so their state doesn't depend on
|
|
36
|
+
# whether the host plays it. Unrecorded, while every voice is off and
|
|
37
|
+
# shifted out to silence, they skip to the cycle at once.
|
|
37
38
|
class Sound
|
|
38
39
|
VOICES = 4
|
|
39
40
|
NOISE = 3
|
|
@@ -86,14 +87,15 @@ module Badline
|
|
|
86
87
|
# second, for #drain_samples. `clock_hz` is the rate the cycles are
|
|
87
88
|
# taken to run at.
|
|
88
89
|
def record(rate:, clock_hz: @clock_hz)
|
|
89
|
-
|
|
90
|
-
run_to(now)
|
|
91
|
-
resume(now)
|
|
90
|
+
run_to(@clock.cycles)
|
|
92
91
|
@output = Output.new(clock_hz:, rate:, level: @level)
|
|
93
92
|
end
|
|
94
93
|
|
|
95
94
|
def recording? = !@output.nil?
|
|
96
95
|
|
|
96
|
+
# Runs the voices up to the current cycle.
|
|
97
|
+
def catch_up! = run_to(@clock.cycles)
|
|
98
|
+
|
|
97
99
|
# The samples recorded since the last drain.
|
|
98
100
|
def drain_samples
|
|
99
101
|
output = @output
|
|
@@ -116,29 +118,7 @@ module Badline
|
|
|
116
118
|
@level = level_now
|
|
117
119
|
end
|
|
118
120
|
|
|
119
|
-
#
|
|
120
|
-
# Recording is the host's, and starts the voices again from the
|
|
121
|
-
# cycle it starts at.
|
|
122
|
-
def save_state(out)
|
|
123
|
-
run_to(@clock.cycles)
|
|
124
|
-
out.marker("VIC20 SOUND").int(@volume).ints(@frequencies).booleans(@enabled)
|
|
125
|
-
out.ints(@shift_registers).ints(@next_shifts).int(@lfsr).int(@level).int(@time)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def load_state(input)
|
|
129
|
-
input.marker("VIC20 SOUND")
|
|
130
|
-
@volume = input.int
|
|
131
|
-
input.ints_into(@frequencies)
|
|
132
|
-
input.booleans_into(@enabled)
|
|
133
|
-
input.ints_into(@shift_registers)
|
|
134
|
-
input.ints_into(@next_shifts)
|
|
135
|
-
@lfsr = input.int
|
|
136
|
-
@level = input.int
|
|
137
|
-
@time = input.int
|
|
138
|
-
resume(@clock.cycles) if recording?
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# A voice's shift register as of the current cycle, while recording.
|
|
121
|
+
# A voice's shift register as of the current cycle.
|
|
142
122
|
def shift_register(voice)
|
|
143
123
|
run_to(@clock.cycles)
|
|
144
124
|
@shift_registers[voice]
|
|
@@ -151,35 +131,47 @@ module Badline
|
|
|
151
131
|
|
|
152
132
|
private
|
|
153
133
|
|
|
154
|
-
# Moves each shift that the cycles since the voices last ran have
|
|
155
|
-
# passed to its next turn from `now`, keeping its phase.
|
|
156
|
-
def resume(now)
|
|
157
|
-
@time = now
|
|
158
|
-
VOICES.times do |voice|
|
|
159
|
-
due = @next_shifts[voice]
|
|
160
|
-
next if due >= now
|
|
161
|
-
|
|
162
|
-
interval = period(voice) * DIVIDERS[voice]
|
|
163
|
-
@next_shifts[voice] = due + ((now - due + interval - 1) / interval * interval)
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
134
|
# Runs the voices up to the start of cycle `target`, the output holding
|
|
168
135
|
# each level for the cycles it lasts.
|
|
169
136
|
def run_to(target)
|
|
170
137
|
time = @time
|
|
138
|
+
return if target <= time
|
|
139
|
+
|
|
171
140
|
output = @output
|
|
172
|
-
return if output.nil?
|
|
141
|
+
return skip_to(target) if output.nil? && silent?
|
|
173
142
|
|
|
174
143
|
following = next_shift
|
|
175
144
|
while following < target
|
|
176
|
-
output
|
|
145
|
+
output&.hold(@level, following - time)
|
|
177
146
|
time = following
|
|
178
147
|
shift_due(time)
|
|
179
148
|
@level = level_now
|
|
180
149
|
following = next_shift
|
|
181
150
|
end
|
|
182
|
-
output
|
|
151
|
+
output&.hold(@level, target - time)
|
|
152
|
+
@time = target
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Whether every voice is off and shifted out to where its shifts
|
|
156
|
+
# change nothing: the tone voices' registers empty and the noise
|
|
157
|
+
# voice's LFSR full of ones.
|
|
158
|
+
def silent?
|
|
159
|
+
enabled = @enabled
|
|
160
|
+
registers = @shift_registers
|
|
161
|
+
!(enabled[0] || enabled[1] || enabled[2] || enabled[NOISE]) &&
|
|
162
|
+
registers[0].zero? && registers[1].zero? && registers[2].zero? && @lfsr == 0xffff
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Moves each shift before `target` to its next turn from there,
|
|
166
|
+
# keeping its phase.
|
|
167
|
+
def skip_to(target)
|
|
168
|
+
VOICES.times do |voice|
|
|
169
|
+
due = @next_shifts[voice]
|
|
170
|
+
next if due >= target
|
|
171
|
+
|
|
172
|
+
interval = period(voice) * DIVIDERS[voice]
|
|
173
|
+
@next_shifts[voice] = due + ((target - due + interval - 1) / interval * interval)
|
|
174
|
+
end
|
|
183
175
|
@time = target
|
|
184
176
|
end
|
|
185
177
|
|
data/lib/badline/chips/vic_i.rb
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "badline/chips/vic_i/painter"
|
|
4
4
|
require "badline/chips/vic_i/text_window"
|
|
5
|
-
require "badline/chips/vic_i/
|
|
5
|
+
require "badline/chips/vic_i/sound"
|
|
6
|
+
require "badline/chips/vic_i/keyed_parts"
|
|
6
7
|
|
|
7
8
|
module Badline
|
|
8
9
|
module Chips
|
|
@@ -41,7 +42,6 @@ module Badline
|
|
|
41
42
|
# into the line lands 48 pixels in. Lines 0-27 are blanked.
|
|
42
43
|
class VICI
|
|
43
44
|
include TextWindow
|
|
44
|
-
include SavedState
|
|
45
45
|
|
|
46
46
|
# What a VIC-I sets for the machine: the clock, the raster's cycles
|
|
47
47
|
# per line and lines per frame, and how many lines at the top of the
|
|
@@ -14,7 +14,7 @@ require "badline/chips/z80/extended_page"
|
|
|
14
14
|
require "badline/chips/z80/block_transfers"
|
|
15
15
|
require "badline/chips/z80/block_io"
|
|
16
16
|
require "badline/chips/z80/interrupt_acceptance"
|
|
17
|
-
require "badline/chips/z80/
|
|
17
|
+
require "badline/chips/z80/keyed_state"
|
|
18
18
|
|
|
19
19
|
module Badline
|
|
20
20
|
module Chips
|
|
@@ -44,7 +44,7 @@ module Badline
|
|
|
44
44
|
include BlockTransfers
|
|
45
45
|
include BlockIO
|
|
46
46
|
include InterruptAcceptance
|
|
47
|
-
include
|
|
47
|
+
include KeyedState
|
|
48
48
|
|
|
49
49
|
attr_reader :bus, :cycles, :nmi
|
|
50
50
|
attr_accessor :int
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class Z80
|
|
6
|
+
# The Z80's fields in a keyed snapshot module (Snapshot::Keyed),
|
|
7
|
+
# between instructions, where it always is after the C128's cycle:
|
|
8
|
+
# the registers, the T-state count and the interrupt latches.
|
|
9
|
+
module KeyedState
|
|
10
|
+
def save_fields(out)
|
|
11
|
+
out.int("a", @a).int("f", @f).int("b", @b).int("c", @c).int("d", @d).int("e", @e).int("h", @h).int("l", @l)
|
|
12
|
+
out.int("af_alt", @af_alt).int("bc_alt", @bc_alt).int("de_alt", @de_alt).int("hl_alt", @hl_alt)
|
|
13
|
+
out.int("i", @i).int("r", @r).int("pc", @pc).int("sp", @sp).int("ix", @ix).int("iy", @iy).int("wz", @wz)
|
|
14
|
+
out.int("q", @q).int("last_q", @last_q).int("im", @im).int("cycles", @cycles)
|
|
15
|
+
out.bool("iff1", @iff1).bool("iff2", @iff2).bool("halted", @halted).bool("after_ei", @after_ei)
|
|
16
|
+
out.bool("after_ld_a_ir", @after_ld_a_ir).bool("int", @int).bool("nmi", @nmi)
|
|
17
|
+
out.bool("nmi_pending", @nmi_pending)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load_fields(input)
|
|
21
|
+
load_main_fields(input)
|
|
22
|
+
load_special_fields(input)
|
|
23
|
+
@cycles = input.int("cycles", @cycles)
|
|
24
|
+
load_latch_fields(input)
|
|
25
|
+
@prefix = 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def restored! = nil
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def load_main_fields(input)
|
|
33
|
+
@a = input.byte("a", @a)
|
|
34
|
+
@f = input.byte("f", @f)
|
|
35
|
+
@b = input.byte("b", @b)
|
|
36
|
+
@c = input.byte("c", @c)
|
|
37
|
+
@d = input.byte("d", @d)
|
|
38
|
+
@e = input.byte("e", @e)
|
|
39
|
+
@h = input.byte("h", @h)
|
|
40
|
+
@l = input.byte("l", @l)
|
|
41
|
+
@af_alt = input.word("af_alt", @af_alt)
|
|
42
|
+
@bc_alt = input.word("bc_alt", @bc_alt)
|
|
43
|
+
@de_alt = input.word("de_alt", @de_alt)
|
|
44
|
+
@hl_alt = input.word("hl_alt", @hl_alt)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def load_special_fields(input)
|
|
48
|
+
@i = input.byte("i", @i)
|
|
49
|
+
@r = input.byte("r", @r)
|
|
50
|
+
@pc = input.word("pc", @pc)
|
|
51
|
+
@sp = input.word("sp", @sp)
|
|
52
|
+
@ix = input.word("ix", @ix)
|
|
53
|
+
@iy = input.word("iy", @iy)
|
|
54
|
+
@wz = input.word("wz", @wz)
|
|
55
|
+
@q = input.byte("q", @q)
|
|
56
|
+
@last_q = input.byte("last_q", @last_q)
|
|
57
|
+
@im = input.int("im", @im)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def load_latch_fields(input)
|
|
61
|
+
@iff1 = input.bool("iff1", @iff1)
|
|
62
|
+
@iff2 = input.bool("iff2", @iff2)
|
|
63
|
+
@halted = input.bool("halted", @halted)
|
|
64
|
+
@after_ei = input.bool("after_ei", @after_ei)
|
|
65
|
+
@after_ld_a_ir = input.bool("after_ld_a_ir", @after_ld_a_ir)
|
|
66
|
+
@int = input.bool("int", @int)
|
|
67
|
+
@nmi = input.bool("nmi", @nmi)
|
|
68
|
+
@nmi_pending = input.bool("nmi_pending", @nmi_pending)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The chips badline/core's machines share. The C128's own, the Z80 and the
|
|
4
|
+
# VDC, stay out of the core: badline/c128 requires them.
|
|
5
|
+
require "badline/chips/cpu"
|
|
6
|
+
require "badline/chips/cia"
|
|
7
|
+
require "badline/chips/via"
|
|
8
|
+
require "badline/chips/sid"
|
|
9
|
+
require "badline/chips/vic"
|
|
10
|
+
require "badline/chips/vic_i"
|
data/lib/badline/core.rb
CHANGED
|
@@ -15,29 +15,13 @@ require "badline/memory"
|
|
|
15
15
|
require "badline/c64/color_memory"
|
|
16
16
|
require "badline/rom"
|
|
17
17
|
require "badline/c64/banked_ram"
|
|
18
|
-
require "badline/chips/cpu/port"
|
|
19
|
-
require "badline/c64/bus"
|
|
20
|
-
require "badline/chips/cpu/instruction"
|
|
21
|
-
require "badline/chips/cpu/instruction_set"
|
|
22
18
|
require "badline/status"
|
|
23
19
|
require "badline/input"
|
|
24
|
-
require "badline/
|
|
25
|
-
require "badline/
|
|
26
|
-
require "badline/
|
|
27
|
-
require "badline/chips/via"
|
|
28
|
-
require "badline/chips/sid"
|
|
29
|
-
require "badline/debug_register"
|
|
20
|
+
require "badline/chips"
|
|
21
|
+
require "badline/drives"
|
|
22
|
+
require "badline/c64/bus"
|
|
30
23
|
require "badline/c64/reu"
|
|
31
|
-
require "badline/
|
|
32
|
-
require "badline/chips/cpu/traps"
|
|
33
|
-
require "badline/chips/cpu"
|
|
34
|
-
require "badline/chips/vic"
|
|
35
|
-
require "badline/keyboard_buffer"
|
|
36
|
-
require "badline/drives/serial_bus"
|
|
37
|
-
require "badline/drives/model1541"
|
|
38
|
-
require "badline/drives/model1581"
|
|
39
|
-
require "badline/drives/model1581/slot"
|
|
40
|
-
require "badline/snapshot/state"
|
|
24
|
+
require "badline/snapshot/keyed"
|
|
41
25
|
require "badline/snapshot/c64_setup"
|
|
42
26
|
require "badline/snapshot/storage_setup"
|
|
43
27
|
require "badline/machine"
|
|
@@ -47,5 +31,4 @@ require "badline/storage"
|
|
|
47
31
|
require "badline/sid_tune"
|
|
48
32
|
require "badline/c64/cartridge"
|
|
49
33
|
require "badline/kernal_trap"
|
|
50
|
-
require "badline/chrout_trap"
|
|
51
34
|
require "badline/media"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Datasette
|
|
6
|
+
# The datasette's fields in a keyed snapshot module (Snapshot::Keyed,
|
|
7
|
+
# BL-TAPE): the keys, the motor, the countdown to the next pulse, and
|
|
8
|
+
# a tape in it under tape.*: its path, its bytes and how far it has
|
|
9
|
+
# played. A tape that isn't in leaves tape.* out. A datasette that
|
|
10
|
+
# times the read line's level adds when in the countdown the line
|
|
11
|
+
# rises, and whether a recording's low half wave is on the tape. The
|
|
12
|
+
# sense and flag handlers don't fire on load.
|
|
13
|
+
module KeyedState
|
|
14
|
+
def save_fields(out)
|
|
15
|
+
out.bool("playing", @playing).bool("motor", @motor).int("countdown", @countdown)
|
|
16
|
+
out.int("rise_at", @rise_at).bool("low_recorded", @low_recorded) if @level
|
|
17
|
+
tape = @tape
|
|
18
|
+
return out unless tape
|
|
19
|
+
|
|
20
|
+
out.text("tape.path", tape.path.to_s).bytes("tape.bytes", tape.bytes).int("tape.position", tape.position)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The tape goes back in from the bytes the module holds, without its
|
|
24
|
+
# host file, unless the tape in is the same one.
|
|
25
|
+
def load_fields(input)
|
|
26
|
+
@playing = input.bool("playing", @playing)
|
|
27
|
+
@motor = input.bool("motor", @motor)
|
|
28
|
+
@countdown = input.int("countdown", @countdown)
|
|
29
|
+
@rise_at = input.int("rise_at", @rise_at)
|
|
30
|
+
@low_recorded = input.bool("low_recorded", @low_recorded)
|
|
31
|
+
bytes = input.bytes("tape.bytes", "".b).bytes
|
|
32
|
+
return @tape = nil if bytes.empty?
|
|
33
|
+
|
|
34
|
+
path = input.text("tape.path", "")
|
|
35
|
+
@tape = Storage::TAP.new(path, bytes:) unless @tape&.path.to_s == path && @tape&.bytes == bytes
|
|
36
|
+
@tape&.position = input.int("tape.position", 0)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
include KeyedState
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/drives/datasette/
|
|
3
|
+
require "badline/drives/datasette/keyed_state"
|
|
4
4
|
|
|
5
5
|
module Badline
|
|
6
6
|
module Drives
|
|
7
7
|
# The 1530 datasette. Plays a pulse stream into CIA 1's FLAG pin while the
|
|
8
8
|
# motor runs with a key pressed, and reports the keys through the cassette
|
|
9
9
|
# sense line on the $01 port. With RECORD down too, it records the
|
|
10
|
-
# machine's write line onto the tape instead.
|
|
10
|
+
# machine's write line onto the tape instead. Built with +level: true+,
|
|
11
|
+
# it also times the read line's level, for a machine that reads that
|
|
12
|
+
# (#read_high?) rather than the falling edges.
|
|
11
13
|
class Datasette
|
|
12
|
-
include SavedState
|
|
13
|
-
|
|
14
14
|
# What inserting a tape raises on a machine without a cassette port.
|
|
15
15
|
class Missing < ArgumentError; end
|
|
16
|
-
|
|
17
16
|
attr_reader :tape
|
|
18
17
|
attr_writer :motor
|
|
19
18
|
|
|
20
|
-
def initialize(tape = nil)
|
|
19
|
+
def initialize(tape = nil, level: false)
|
|
21
20
|
@tape = tape
|
|
21
|
+
@level = level
|
|
22
22
|
@connected = true
|
|
23
23
|
@playing = false
|
|
24
24
|
@recording = false
|
|
@@ -26,6 +26,8 @@ module Badline
|
|
|
26
26
|
@since = 0
|
|
27
27
|
@motor = false
|
|
28
28
|
@countdown = 0
|
|
29
|
+
@rise_at = 0
|
|
30
|
+
@low_recorded = false
|
|
29
31
|
@flag_handler = nil
|
|
30
32
|
@sense_handler = nil
|
|
31
33
|
end
|
|
@@ -64,6 +66,7 @@ module Badline
|
|
|
64
66
|
def rewind
|
|
65
67
|
@tape&.rewind
|
|
66
68
|
@countdown = 0
|
|
69
|
+
@rise_at = 0
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def play! = press(true)
|
|
@@ -76,6 +79,7 @@ module Badline
|
|
|
76
79
|
press(true)
|
|
77
80
|
@recording = true
|
|
78
81
|
@since = 0
|
|
82
|
+
@low_recorded = false
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
def recording? = @recording
|
|
@@ -83,14 +87,27 @@ module Badline
|
|
|
83
87
|
# The machine's tape write line. While the deck records with its motor
|
|
84
88
|
# running, each rising edge ends a pulse on the tape, as long as the
|
|
85
89
|
# time since the edge before, and plays back as a falling edge on the
|
|
86
|
-
# read line.
|
|
90
|
+
# read line. A datasette built with +level: true+ records a version 2
|
|
91
|
+
# tape's half waves instead: a rising edge ends the low half and a
|
|
92
|
+
# falling edge the high half after it, so the tape plays back the
|
|
93
|
+
# levels it recorded (#read_high?).
|
|
87
94
|
def write_line=(high)
|
|
88
|
-
|
|
95
|
+
edge = high != @write_high
|
|
89
96
|
@write_high = high
|
|
90
|
-
return unless
|
|
97
|
+
return unless edge && @recording && running?
|
|
98
|
+
|
|
99
|
+
if @level && @tape.half_waves?
|
|
100
|
+
record_half_wave(high)
|
|
101
|
+
elsif high
|
|
102
|
+
@tape.record_pulse(@since)
|
|
103
|
+
@since = 0
|
|
104
|
+
end
|
|
105
|
+
end
|
|
91
106
|
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
# Sets the write line's level without recording an edge, as a
|
|
108
|
+
# snapshot loads it.
|
|
109
|
+
def write_level=(high)
|
|
110
|
+
@write_high = high
|
|
94
111
|
end
|
|
95
112
|
|
|
96
113
|
def playing? = @playing
|
|
@@ -102,11 +119,22 @@ module Badline
|
|
|
102
119
|
|
|
103
120
|
def running? = @playing && @motor && !@tape.nil?
|
|
104
121
|
|
|
122
|
+
# The tape read line's level, on a datasette built with +level: true+.
|
|
123
|
+
# Each pulse holds it low for its first half and high for its second,
|
|
124
|
+
# up to the falling edge that ends it. A version 2 tape times each
|
|
125
|
+
# half, and a version 0 or 1 tape's pulse plays as two equal halves.
|
|
126
|
+
# It reads high while the tape isn't playing.
|
|
127
|
+
def read_high?
|
|
128
|
+
return true unless running? && !@recording
|
|
129
|
+
|
|
130
|
+
@countdown.positive? && @countdown <= @rise_at
|
|
131
|
+
end
|
|
132
|
+
|
|
105
133
|
def cycle!
|
|
106
134
|
return unless running?
|
|
107
135
|
return @since += 1 if @recording
|
|
108
136
|
|
|
109
|
-
|
|
137
|
+
next_pulse if @countdown.zero?
|
|
110
138
|
return if @countdown.zero?
|
|
111
139
|
|
|
112
140
|
@countdown -= 1
|
|
@@ -120,6 +148,28 @@ module Badline
|
|
|
120
148
|
|
|
121
149
|
private
|
|
122
150
|
|
|
151
|
+
def next_pulse
|
|
152
|
+
return @countdown = @tape.next_pulse.to_i unless @level
|
|
153
|
+
return next_half_waves if @tape.half_waves?
|
|
154
|
+
|
|
155
|
+
@countdown = @tape.next_pulse.to_i
|
|
156
|
+
@rise_at = @countdown / 2
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def next_half_waves
|
|
160
|
+
first = @tape.next_half_wave.to_i
|
|
161
|
+
@rise_at = @tape.next_half_wave.to_i
|
|
162
|
+
@countdown = first + @rise_at
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# A high half wave goes on the tape only after the low half before it,
|
|
166
|
+
# so the tape starts with a whole pulse.
|
|
167
|
+
def record_half_wave(rose)
|
|
168
|
+
@tape.record_half_wave(@since) if rose || @low_recorded
|
|
169
|
+
@low_recorded = rose
|
|
170
|
+
@since = 0
|
|
171
|
+
end
|
|
172
|
+
|
|
123
173
|
def press(down)
|
|
124
174
|
return if down == @playing || !@connected
|
|
125
175
|
|
|
@@ -86,6 +86,9 @@ module Badline
|
|
|
86
86
|
# is the same at the end of every pass the drive sleeps through.
|
|
87
87
|
def led_on? = @mechanism.led_on?
|
|
88
88
|
|
|
89
|
+
# The power LED, which stays lit.
|
|
90
|
+
def power_led_on? = true
|
|
91
|
+
|
|
89
92
|
# Stores what the head wrote since the motor last stopped in the
|
|
90
93
|
# disk's image, as the motor stopping does.
|
|
91
94
|
def flush
|