badline 0.13.0 → 0.14.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 +49 -1
- data/CLAUDE.md +27 -7
- data/CONTRIBUTING.md +4 -3
- data/README.md +8 -5
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +29 -0
- data/doc/snapshot-format.md +1047 -0
- data/doc/snapshots.md +72 -39
- data/lib/badline/audio/bare_player.rb +50 -17
- data/lib/badline/audio/machine_player.rb +15 -9
- data/lib/badline/audio/renderer.rb +32 -21
- data/lib/badline/c128/banks.rb +6 -3
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +17 -3
- 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 +20 -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/keyed_state.rb +41 -0
- data/lib/badline/c64/bus.rb +2 -1
- data/lib/badline/c64/cartridge/action_replay.rb +0 -13
- 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 +0 -12
- data/lib/badline/c64/cartridge/flash.rb +3 -3
- data/lib/badline/c64/cartridge/g_mod2.rb +0 -12
- 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 +393 -0
- data/lib/badline/c64/cartridge/pagefox.rb +0 -10
- data/lib/badline/c64/cartridge/retro_replay.rb +0 -8
- data/lib/badline/c64/cartridge/rgcd.rb +0 -10
- data/lib/badline/c64/cartridge/super_games.rb +0 -10
- data/lib/badline/c64/cartridge/zaxxon.rb +0 -16
- data/lib/badline/c64/cartridge.rb +1 -27
- data/lib/badline/c64/kernal_traps.rb +28 -25
- data/lib/badline/c64/keyed_modules.rb +111 -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 +119 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +143 -0
- data/lib/badline/chips/tia.rb +49 -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 +38 -0
- data/lib/badline/drives/datasette.rb +40 -7
- 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 +1 -1
- data/lib/badline/frontend/controls.rb +19 -3
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
- data/lib/badline/frontend/snapshots.rb +12 -1
- data/lib/badline/frontend/timeline.rb +28 -10
- 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 +5 -2
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/options/family.rb +5 -2
- data/lib/badline/options/help.rb +42 -16
- data/lib/badline/options/table.rb +2 -0
- data/lib/badline/options/validation.rb +2 -0
- data/lib/badline/options.rb +6 -1
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +312 -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 +51 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +164 -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 +4 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
- data/lib/badline/snapshot/field_doc.rb +109 -0
- data/lib/badline/snapshot/image.rb +134 -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/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 +43 -20
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/g64_image.rb +8 -3
- data/lib/badline/storage/tap.rb +43 -15
- 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 +61 -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
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/chips/sid/filter/saved_state"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
4
|
module Chips
|
|
7
5
|
class SID
|
|
@@ -13,8 +11,6 @@ module Badline
|
|
|
13
11
|
# and the outputs selected by MODE/VOL are summed back together with the
|
|
14
12
|
# unfiltered ones.
|
|
15
13
|
class Filter
|
|
16
|
-
include SavedState
|
|
17
|
-
|
|
18
14
|
# Cutoff frequency in Hz at each chip's breakpoints. The 6581 curve was
|
|
19
15
|
# sampled from a real chip by reSID and bends sharply around $300; the
|
|
20
16
|
# 8580's is close to a straight line from 0 to 12.5 kHz. The curve
|
|
@@ -95,16 +91,6 @@ module Badline
|
|
|
95
91
|
@output = 0
|
|
96
92
|
end
|
|
97
93
|
|
|
98
|
-
def save_state(out)
|
|
99
|
-
out.int(@lowpass).int(@highpass).int(@output)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def load_state(input)
|
|
103
|
-
@lowpass = input.int
|
|
104
|
-
@highpass = input.int
|
|
105
|
-
@output = input.int
|
|
106
|
-
end
|
|
107
|
-
|
|
108
94
|
# Takes the input in 1/2^Filter::FRACTION units, and reads the
|
|
109
95
|
# output out in whole ones. A step of several cycles moves each pole
|
|
110
96
|
# at most onto its input.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class SID
|
|
6
|
+
class Waveform
|
|
7
|
+
# An oscillator's fields in a keyed snapshot module, under `prefix`:
|
|
8
|
+
# the phase, the noise register and its pipeline, the delayed
|
|
9
|
+
# shapers, the sync edge, the floating output and the pulse level.
|
|
10
|
+
# What the registers set comes from them on load.
|
|
11
|
+
module KeyedState
|
|
12
|
+
# The fields reSID keeps for an oscillator between cycles, for VICE's
|
|
13
|
+
# SIDEXTENDED module: the phase, the noise register, its shift pipeline
|
|
14
|
+
# and reset countdown, the floating output's countdown and the pulse
|
|
15
|
+
# level.
|
|
16
|
+
def resid_fields
|
|
17
|
+
[@accumulator, @shift_register, @shift_pipeline, @shift_register_reset, @floating_ttl, @pulse]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Takes the fields resid_fields gives from `fields`, from `at` on.
|
|
21
|
+
def restore_resid_fields(fields, at)
|
|
22
|
+
@accumulator = fields[at]
|
|
23
|
+
@shift_register = fields[at + 1]
|
|
24
|
+
@shift_pipeline = fields[at + 2]
|
|
25
|
+
@shift_register_reset = fields[at + 3]
|
|
26
|
+
@floating_ttl = fields[at + 4]
|
|
27
|
+
@pulse = fields[at + 5]
|
|
28
|
+
@stale = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def save_fields(out, prefix)
|
|
32
|
+
out.int("#{prefix}accumulator", @accumulator).int("#{prefix}shift_register", @shift_register)
|
|
33
|
+
out.int("#{prefix}shift_register_reset", @shift_register_reset)
|
|
34
|
+
out.int("#{prefix}shift_pipeline", @shift_pipeline).int("#{prefix}delayed_triangle", @delayed_triangle)
|
|
35
|
+
out.int("#{prefix}delayed_sawtooth", @delayed_sawtooth).bool("#{prefix}msb_rising", @msb_rising)
|
|
36
|
+
out.int("#{prefix}floating", @floating).int("#{prefix}floating_ttl", @floating_ttl)
|
|
37
|
+
out.int("#{prefix}pulse", @pulse)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load_fields(input, prefix)
|
|
41
|
+
@accumulator = input.masked("#{prefix}accumulator", @accumulator, 0xffffff)
|
|
42
|
+
@shift_register = input.masked("#{prefix}shift_register", @shift_register, 0x7fffff)
|
|
43
|
+
@shift_register_reset = input.int("#{prefix}shift_register_reset", @shift_register_reset)
|
|
44
|
+
@shift_pipeline = input.int("#{prefix}shift_pipeline", @shift_pipeline)
|
|
45
|
+
@delayed_triangle = input.masked("#{prefix}delayed_triangle", @delayed_triangle, 0xfff)
|
|
46
|
+
@delayed_sawtooth = input.masked("#{prefix}delayed_sawtooth", @delayed_sawtooth, 0xfff)
|
|
47
|
+
@msb_rising = input.bool("#{prefix}msb_rising", @msb_rising)
|
|
48
|
+
@floating = input.masked("#{prefix}floating", @floating, 0xfff)
|
|
49
|
+
@floating_ttl = input.int("#{prefix}floating_ttl", @floating_ttl)
|
|
50
|
+
@pulse = input.masked("#{prefix}pulse", @pulse, 0xfff)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The frequency, pulse width and control bits as the registers
|
|
54
|
+
# hold them, without the test bit's or the waveform change's
|
|
55
|
+
# effects on the noise register, which already happened.
|
|
56
|
+
def restore_registers(frequency, pulse_width, control)
|
|
57
|
+
@frequency = frequency
|
|
58
|
+
@pulse_width = pulse_width & 0xfff
|
|
59
|
+
@selected = (control >> 4) & 0x0f
|
|
60
|
+
@test = control.anybits?(0x08)
|
|
61
|
+
@ring = control.anybits?(0x04)
|
|
62
|
+
@sync = control.anybits?(0x02)
|
|
63
|
+
@stale = true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
include KeyedState
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Envelope
|
|
71
|
+
# The envelope generator's fields in a keyed snapshot module, under
|
|
72
|
+
# `prefix`: the state, the counters and pipelines between them. The
|
|
73
|
+
# rates and the gate come from the registers on load.
|
|
74
|
+
module KeyedState
|
|
75
|
+
# The fields reSID keeps for an envelope between cycles, for VICE's
|
|
76
|
+
# SIDEXTENDED module: the rate and exponential counters, the level, the
|
|
77
|
+
# state as an index into STATES, whether it holds at zero, the two
|
|
78
|
+
# periods and the pipeline.
|
|
79
|
+
def resid_fields
|
|
80
|
+
[@rate_counter, @exponential_counter, @counter, STATES.index(@state).to_i, @hold_zero ? 1 : 0,
|
|
81
|
+
@rate_period, @exponential_period, @envelope_pipeline]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Takes the fields resid_fields gives from `fields`, from `at` on.
|
|
85
|
+
def restore_resid_fields(fields, at)
|
|
86
|
+
@rate_counter = fields[at]
|
|
87
|
+
@exponential_counter = fields[at + 1]
|
|
88
|
+
@counter = @env3 = fields[at + 2]
|
|
89
|
+
state = fields[at + 3]
|
|
90
|
+
@state = @next_state = STATES.fetch(state.clamp(0, STATES.length - 1)).to_sym
|
|
91
|
+
@hold_zero = fields[at + 4] != 0
|
|
92
|
+
@rate_period = fields[at + 5]
|
|
93
|
+
@exponential_period = fields[at + 6]
|
|
94
|
+
@envelope_pipeline = fields[at + 7]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def save_fields(out, prefix)
|
|
98
|
+
out.text("#{prefix}state", @state.to_s).text("#{prefix}next_state", @next_state.to_s)
|
|
99
|
+
out.int("#{prefix}counter", @counter).int("#{prefix}env3", @env3)
|
|
100
|
+
out.int("#{prefix}rate_counter", @rate_counter)
|
|
101
|
+
out.int("#{prefix}rate_period", @rate_period).bool("#{prefix}reset_rate_counter", @reset_rate_counter)
|
|
102
|
+
out.int("#{prefix}exponential_counter", @exponential_counter)
|
|
103
|
+
out.int("#{prefix}exponential_period", @exponential_period)
|
|
104
|
+
out.int("#{prefix}state_pipeline", @state_pipeline).int("#{prefix}envelope_pipeline", @envelope_pipeline)
|
|
105
|
+
out.int("#{prefix}exponential_pipeline", @exponential_pipeline).bool("#{prefix}hold_zero", @hold_zero)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def load_fields(input, prefix)
|
|
109
|
+
@state = state_named(input.choice("#{prefix}state", STATE_NAMES, @state.to_s))
|
|
110
|
+
@next_state = state_named(input.choice("#{prefix}next_state", STATE_NAMES, @next_state.to_s))
|
|
111
|
+
@counter = input.byte("#{prefix}counter", @counter)
|
|
112
|
+
@env3 = input.byte("#{prefix}env3", @env3)
|
|
113
|
+
@rate_counter = input.masked("#{prefix}rate_counter", @rate_counter, 0x7fff)
|
|
114
|
+
@rate_period = input.int("#{prefix}rate_period", @rate_period)
|
|
115
|
+
@reset_rate_counter = input.bool("#{prefix}reset_rate_counter", @reset_rate_counter)
|
|
116
|
+
load_pipeline_fields(input, prefix)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The rates and the gate as the registers hold them.
|
|
120
|
+
def restore_registers(attack_decay, sustain_release, control)
|
|
121
|
+
@attack = (attack_decay >> 4) & 0x0f
|
|
122
|
+
@decay = attack_decay & 0x0f
|
|
123
|
+
@sustain = (sustain_release >> 4) & 0x0f
|
|
124
|
+
@release = sustain_release & 0x0f
|
|
125
|
+
@gate = control.anybits?(0x01)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def load_pipeline_fields(input, prefix)
|
|
131
|
+
@exponential_counter = input.int("#{prefix}exponential_counter", @exponential_counter)
|
|
132
|
+
@exponential_period = input.int("#{prefix}exponential_period", @exponential_period)
|
|
133
|
+
@state_pipeline = input.int("#{prefix}state_pipeline", @state_pipeline)
|
|
134
|
+
@envelope_pipeline = input.int("#{prefix}envelope_pipeline", @envelope_pipeline)
|
|
135
|
+
@exponential_pipeline = input.int("#{prefix}exponential_pipeline", @exponential_pipeline)
|
|
136
|
+
@hold_zero = input.bool("#{prefix}hold_zero", @hold_zero)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def state_named(name)
|
|
140
|
+
case name
|
|
141
|
+
when "attack" then :attack
|
|
142
|
+
when "decay_sustain" then :decay_sustain
|
|
143
|
+
else :release
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
STATE_NAMES = STATES.map(&:to_s).freeze
|
|
149
|
+
|
|
150
|
+
include KeyedState
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class Voice
|
|
154
|
+
# A voice's oscillator under `prefix`wave. and its envelope under
|
|
155
|
+
# `prefix`env.
|
|
156
|
+
module KeyedState
|
|
157
|
+
def save_fields(out, prefix)
|
|
158
|
+
@waveform.save_fields(out, "#{prefix}wave.")
|
|
159
|
+
@envelope.save_fields(out, "#{prefix}env.")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def load_fields(input, prefix)
|
|
163
|
+
@waveform.load_fields(input, "#{prefix}wave.")
|
|
164
|
+
@envelope.load_fields(input, "#{prefix}env.")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
include KeyedState
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class Filter
|
|
172
|
+
class External
|
|
173
|
+
# The output filter's integrators and output, under `prefix`.
|
|
174
|
+
module KeyedState
|
|
175
|
+
def save_fields(out, prefix)
|
|
176
|
+
out.int("#{prefix}lowpass", @lowpass).int("#{prefix}highpass", @highpass).int("#{prefix}output", @output)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def load_fields(input, prefix)
|
|
180
|
+
@lowpass = input.int("#{prefix}lowpass", @lowpass)
|
|
181
|
+
@highpass = input.int("#{prefix}highpass", @highpass)
|
|
182
|
+
@output = input.int("#{prefix}output", @output)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
include KeyedState
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# The filter's integrators and its last input, under `prefix`, and
|
|
190
|
+
# the output filter's under ext. The cutoff, resonance, routing,
|
|
191
|
+
# mode and volume come from the registers on load.
|
|
192
|
+
module KeyedState
|
|
193
|
+
def save_fields(out, prefix)
|
|
194
|
+
out.int("#{prefix}highpass", @highpass).int("#{prefix}bandpass", @bandpass)
|
|
195
|
+
out.int("#{prefix}lowpass", @lowpass).int("#{prefix}unfiltered", @unfiltered).int("#{prefix}input", @input)
|
|
196
|
+
@external.save_fields(out, "ext.")
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def load_fields(input, prefix)
|
|
200
|
+
@highpass = input.int("#{prefix}highpass", @highpass)
|
|
201
|
+
@bandpass = input.int("#{prefix}bandpass", @bandpass)
|
|
202
|
+
@lowpass = input.int("#{prefix}lowpass", @lowpass)
|
|
203
|
+
@unfiltered = input.int("#{prefix}unfiltered", @unfiltered)
|
|
204
|
+
@input = input.int("#{prefix}input", @input)
|
|
205
|
+
@external.load_fields(input, "ext.")
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# $D415 to $D418 as written.
|
|
209
|
+
def restore_registers(cutoff_low, cutoff_high, resonance, mode_volume)
|
|
210
|
+
self.cutoff = (cutoff_high << 3) | (cutoff_low & 0x07)
|
|
211
|
+
write_resonance(resonance)
|
|
212
|
+
write_mode_volume(mode_volume)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
include KeyedState
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# The SID's fields in a keyed snapshot module (Snapshot::Keyed): the
|
|
220
|
+
# registers as written, the data bus latch and its decay, each
|
|
221
|
+
# voice's under voice1. to voice3., and the filter's under filter.
|
|
222
|
+
# and ext. Saving catches the DSP up first, so no span or deferred
|
|
223
|
+
# write is left to save. What the registers decode to is worked out
|
|
224
|
+
# from them on load.
|
|
225
|
+
module KeyedState
|
|
226
|
+
def save_fields(out)
|
|
227
|
+
catch_up
|
|
228
|
+
out.bytes("registers", @registers.read(0, 0x20)).int("bus_value", @bus_value).int("bus_ttl", @bus_ttl)
|
|
229
|
+
@voice1.save_fields(out, "voice1.")
|
|
230
|
+
@voice2.save_fields(out, "voice2.")
|
|
231
|
+
@voice3.save_fields(out, "voice3.")
|
|
232
|
+
@filter.save_fields(out, "filter.")
|
|
233
|
+
out
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def load_fields(input)
|
|
237
|
+
@registers.write(0, input.bytes_into("registers", @registers.read(0, 0x20)))
|
|
238
|
+
@bus_value = input.byte("bus_value", @bus_value)
|
|
239
|
+
@bus_ttl = input.int("bus_ttl", @bus_ttl)
|
|
240
|
+
@pending_cycles = 0
|
|
241
|
+
@deferred_writes.clear
|
|
242
|
+
@voice1.load_fields(input, "voice1.")
|
|
243
|
+
@voice2.load_fields(input, "voice2.")
|
|
244
|
+
@voice3.load_fields(input, "voice3.")
|
|
245
|
+
@filter.load_fields(input, "filter.")
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# How far the recording has got into the sample it is averaging,
|
|
249
|
+
# under `prefix`, for a host that carries on recording from a
|
|
250
|
+
# snapshot: it calls #record, then #load_recording_fields.
|
|
251
|
+
def save_recording_fields(out, prefix)
|
|
252
|
+
decimator = @decimator
|
|
253
|
+
raise ArgumentError, "the SID isn't recording" if decimator.nil?
|
|
254
|
+
|
|
255
|
+
decimator.save_fields(out, prefix)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def load_recording_fields(input, prefix)
|
|
259
|
+
decimator = @decimator
|
|
260
|
+
raise ArgumentError, "the SID isn't recording" if decimator.nil?
|
|
261
|
+
|
|
262
|
+
decimator.load_fields(input, prefix)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def restored!
|
|
266
|
+
restore_voice(@voice1, 0x00)
|
|
267
|
+
restore_voice(@voice2, 0x07)
|
|
268
|
+
restore_voice(@voice3, 0x0e)
|
|
269
|
+
@filter.restore_registers(register(0x15), register(0x16), register(0x17), register(0x18))
|
|
270
|
+
update_span_rules
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
def restore_voice(voice, base)
|
|
276
|
+
control = register(base + 4)
|
|
277
|
+
voice.waveform.restore_registers(register(base) | (register(base + 1) << 8),
|
|
278
|
+
register(base + 2) | (register(base + 3) << 8), control)
|
|
279
|
+
voice.envelope.restore_registers(register(base + 5), register(base + 6), control)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
include KeyedState
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
@@ -45,16 +45,6 @@ module Badline
|
|
|
45
45
|
@envelope = Envelope.new
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def save_state(out)
|
|
49
|
-
@waveform.save_state(out)
|
|
50
|
-
@envelope.save_state(out)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def load_state(input)
|
|
54
|
-
@waveform.load_state(input)
|
|
55
|
-
@envelope.load_state(input)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
48
|
def control=(value)
|
|
59
49
|
@waveform.control = value
|
|
60
50
|
@envelope.control = value
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "badline/chips/sid/waveform/combined"
|
|
4
4
|
require "badline/chips/sid/waveform/fast_forward"
|
|
5
5
|
require "badline/chips/sid/waveform/noise_writeback"
|
|
6
|
-
require "badline/chips/sid/waveform/saved_state"
|
|
7
6
|
|
|
8
7
|
module Badline
|
|
9
8
|
module Chips
|
|
@@ -29,7 +28,6 @@ module Badline
|
|
|
29
28
|
class Waveform
|
|
30
29
|
include FastForward
|
|
31
30
|
include NoiseWriteback
|
|
32
|
-
include SavedState
|
|
33
31
|
|
|
34
32
|
# All bits high at power up, odd ones stored inverted (SID/oscinit).
|
|
35
33
|
POWER_ON_ACCUMULATOR = 0x555555
|
data/lib/badline/chips/sid.rb
CHANGED
|
@@ -6,7 +6,7 @@ require "badline/chips/sid/voice"
|
|
|
6
6
|
require "badline/chips/sid/filter"
|
|
7
7
|
require "badline/chips/sid/decimator"
|
|
8
8
|
require "badline/chips/sid/catch_up"
|
|
9
|
-
require "badline/chips/sid/
|
|
9
|
+
require "badline/chips/sid/keyed_parts"
|
|
10
10
|
|
|
11
11
|
module Badline
|
|
12
12
|
module Chips
|
|
@@ -37,7 +37,6 @@ module Badline
|
|
|
37
37
|
class SID
|
|
38
38
|
include Addressable
|
|
39
39
|
include CatchUp
|
|
40
|
-
include SavedState
|
|
41
40
|
|
|
42
41
|
VOICES = 3
|
|
43
42
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class TED
|
|
6
|
+
# The raster and the clock the TED gives the CPU, a single clock
|
|
7
|
+
# cycle at a time.
|
|
8
|
+
#
|
|
9
|
+
# A line is 456 dots, horizontal counts 0-455, and a single clock
|
|
10
|
+
# cycle 8 of them, so a line is 57 cycles; #column counts them, the
|
|
11
|
+
# cycle at count 8 * column. The line count steps at count 376, in
|
|
12
|
+
# column 47, and runs 0-311 on PAL and 0-261 with $FF07 bit 6 set
|
|
13
|
+
# (NTSC). The datasheet's horizontal and vertical decode tables give
|
|
14
|
+
# those counts.
|
|
15
|
+
#
|
|
16
|
+
# The CPU runs once in a single clock cycle and twice in a double
|
|
17
|
+
# clock one. The datasheet's "System Clock Doubling": in the display
|
|
18
|
+
# window's lines the clock is single from count 400 round to 344,
|
|
19
|
+
# columns 50-56 and 0-42, and double in columns 43-49. The window is
|
|
20
|
+
# lines 0-203: the datasheet says 0-204, but xplus4 runs line 204 at
|
|
21
|
+
# double clock, as the lines below it. In the other lines it is double
|
|
22
|
+
# but for the five refresh cycles at counts 304-344, columns 38-42,
|
|
23
|
+
# which are single everywhere. With $FF06 bit 4 clear (blank) every
|
|
24
|
+
# line is double but for refresh, and $FF13 bit 1 or $FF07 bit 5
|
|
25
|
+
# (freeze) makes every cycle single.
|
|
26
|
+
#
|
|
27
|
+
# So a display line gives the CPU 64 cycles and a border line 109,
|
|
28
|
+
# the counts measured on real machines.
|
|
29
|
+
#
|
|
30
|
+
# DMA: each character row takes the bus for 40 cycles on two lines,
|
|
31
|
+
# the line before the row for the attributes and the row's first
|
|
32
|
+
# line for the character pointers (the datasheet's register 20). A
|
|
33
|
+
# row starts on the line after one whose low three bits match $FF06
|
|
34
|
+
# bits 0-2, as the KERNAL's scroll of 3 puts the first row on line 4,
|
|
35
|
+
# where the datasheet's 25-row window starts, and a row starts only
|
|
36
|
+
# in lines 0-203. xplus4 takes the bus on the same lines. The fetches run in columns 54-56 and 0-36, and BA
|
|
37
|
+
# goes low 3 cycles before the first, in column 51, so the CPU stops
|
|
38
|
+
# at its next read. A DMA line leaves the CPU 24 cycles.
|
|
39
|
+
module Beam
|
|
40
|
+
CYCLES_PER_LINE = 57
|
|
41
|
+
LINE_STEP_COLUMN = 47
|
|
42
|
+
BA_COLUMN = 51
|
|
43
|
+
DMA_COLUMN = 54
|
|
44
|
+
DMA_END_COLUMN = 36
|
|
45
|
+
REFRESH_COLUMNS = (38..42)
|
|
46
|
+
DOUBLE_COLUMNS = (43..49)
|
|
47
|
+
LAST_DISPLAY_LINE = 203
|
|
48
|
+
|
|
49
|
+
# The single clock cycle in the line, 0-56.
|
|
50
|
+
attr_reader :column
|
|
51
|
+
|
|
52
|
+
# The raster line.
|
|
53
|
+
attr_reader :line
|
|
54
|
+
|
|
55
|
+
# How many cycles the CPU runs in this cycle: 0 while the TED holds
|
|
56
|
+
# the bus for DMA, 1 at single clock and 2 at double clock.
|
|
57
|
+
attr_reader :cpu_slots
|
|
58
|
+
|
|
59
|
+
# Whether BA is low, from 3 cycles before DMA to its end.
|
|
60
|
+
def ba_low? = @ba_low
|
|
61
|
+
|
|
62
|
+
# Lines a frame, by $FF07 bit 6.
|
|
63
|
+
def lines_per_frame = @registers[0x07].anybits?(0x40) ? 262 : 312
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def reset_beam
|
|
68
|
+
@column = 0
|
|
69
|
+
@line = 0
|
|
70
|
+
@dma_line = false
|
|
71
|
+
@ba_low = false
|
|
72
|
+
@cpu_slots = 1
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def move_beam
|
|
76
|
+
column = @column + 1
|
|
77
|
+
column = 0 if column == CYCLES_PER_LINE
|
|
78
|
+
@column = column
|
|
79
|
+
return unless column == LINE_STEP_COLUMN
|
|
80
|
+
|
|
81
|
+
line = @line + 1
|
|
82
|
+
line = 0 if line >= lines_per_frame
|
|
83
|
+
self.line = line
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def decide_clock
|
|
87
|
+
column = @column
|
|
88
|
+
@dma_line = dma_line? if column == BA_COLUMN
|
|
89
|
+
if @dma_line && (column >= BA_COLUMN || column <= DMA_END_COLUMN)
|
|
90
|
+
@ba_low = true
|
|
91
|
+
@cpu_slots = column >= DMA_COLUMN || column <= DMA_END_COLUMN ? 0 : 1
|
|
92
|
+
return
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@ba_low = false
|
|
96
|
+
@cpu_slots = single_clock?(column) ? 1 : 2
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def single_clock?(column)
|
|
100
|
+
return true if REFRESH_COLUMNS.include?(column) || @registers[0x13].anybits?(0x02) || frozen?
|
|
101
|
+
|
|
102
|
+
display_window? && !DOUBLE_COLUMNS.include?(column)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def display_window? = @registers[0x06].anybits?(0x10) && @line <= LAST_DISPLAY_LINE
|
|
106
|
+
|
|
107
|
+
def dma_line?
|
|
108
|
+
return false unless @registers[0x06].anybits?(0x10)
|
|
109
|
+
|
|
110
|
+
phase = (@line - (@registers[0x06] & 0x07)) & 0x07
|
|
111
|
+
return false if phase > 1
|
|
112
|
+
|
|
113
|
+
row = phase.zero? ? @line + 1 : @line
|
|
114
|
+
row <= LAST_DISPLAY_LINE
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class TED
|
|
6
|
+
# The three 16-bit timers at $FF00-$FF05, which count down once a
|
|
7
|
+
# single clock cycle whatever clock the CPU runs at. Each raises its
|
|
8
|
+
# interrupt flag as it counts down to zero. Timer 1's next count then
|
|
9
|
+
# loads the value last written to it, and timers 2 and 3 roll over to
|
|
10
|
+
# $FFFF.
|
|
11
|
+
#
|
|
12
|
+
# A write to a timer's low byte stops it until its high byte is
|
|
13
|
+
# written. Timer 1's writes set its reload value and its count
|
|
14
|
+
# together, and timers 2 and 3's set the count.
|
|
15
|
+
module Timers
|
|
16
|
+
# The interrupt flag each timer raises in $FF09.
|
|
17
|
+
TIMER_FLAGS = [0x08, 0x10, 0x40].freeze
|
|
18
|
+
|
|
19
|
+
# The counts, the reload value of timer 1, and which timers run.
|
|
20
|
+
attr_reader :timer_counts, :timer_running
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def reset_timers
|
|
25
|
+
@timer_counts = [0, 0, 0]
|
|
26
|
+
@timer_running = [false, false, false]
|
|
27
|
+
@timer1_reload = 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def count_timers
|
|
31
|
+
i = 0
|
|
32
|
+
while i < 3
|
|
33
|
+
count_timer(i) if @timer_running[i]
|
|
34
|
+
i += 1
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def count_timer(index)
|
|
39
|
+
count = @timer_counts[index]
|
|
40
|
+
if count.zero? && index.zero?
|
|
41
|
+
@timer_counts[0] = @timer1_reload
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
count = (count - 1) & 0xffff
|
|
46
|
+
@timer_counts[index] = count
|
|
47
|
+
@interrupt_flags |= TIMER_FLAGS[index] if count.zero?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def read_timer(register)
|
|
51
|
+
count = @timer_counts[register >> 1]
|
|
52
|
+
register.even? ? count & 0xff : count >> 8
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def write_timer(register, value)
|
|
56
|
+
index = register >> 1
|
|
57
|
+
count = @timer_counts[index]
|
|
58
|
+
count = register.even? ? (count & 0xff00) | value : (count & 0x00ff) | (value << 8)
|
|
59
|
+
@timer_counts[index] = count
|
|
60
|
+
@timer1_reload = count if index.zero?
|
|
61
|
+
@timer_running[index] = register.odd?
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|