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
|
@@ -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,118 @@
|
|
|
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 DMA a line makes: none, the attributes or the pointers.
|
|
50
|
+
NO_DMA = 0
|
|
51
|
+
ATTRIBUTE_DMA = 1
|
|
52
|
+
POINTER_DMA = 2
|
|
53
|
+
|
|
54
|
+
# The single clock cycle in the line, 0-56.
|
|
55
|
+
attr_reader :column
|
|
56
|
+
|
|
57
|
+
# The raster line.
|
|
58
|
+
attr_reader :line
|
|
59
|
+
|
|
60
|
+
# How many cycles the CPU runs in this cycle: 0 while the TED holds
|
|
61
|
+
# the bus for DMA, 1 at single clock and 2 at double clock.
|
|
62
|
+
attr_reader :cpu_slots
|
|
63
|
+
|
|
64
|
+
# Whether BA is low, from 3 cycles before DMA to its end.
|
|
65
|
+
def ba_low? = @ba_low
|
|
66
|
+
|
|
67
|
+
# Lines a frame, by $FF07 bit 6.
|
|
68
|
+
def lines_per_frame = @registers[0x07].anybits?(0x40) ? 262 : 312
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def reset_beam
|
|
73
|
+
@column = 0
|
|
74
|
+
@line = 0
|
|
75
|
+
@ba_low = false
|
|
76
|
+
@cpu_slots = 1
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def move_beam
|
|
80
|
+
column = @column + 1
|
|
81
|
+
column = 0 if column == CYCLES_PER_LINE
|
|
82
|
+
@column = column
|
|
83
|
+
return unless column == LINE_STEP_COLUMN
|
|
84
|
+
|
|
85
|
+
line = @line + 1
|
|
86
|
+
if line >= lines_per_frame
|
|
87
|
+
line = 0
|
|
88
|
+
end_frame
|
|
89
|
+
end
|
|
90
|
+
step_row_line(line)
|
|
91
|
+
step_scanline(line)
|
|
92
|
+
self.line = line
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# The clock for the cycle. Freeze leaves single clock, without DMA.
|
|
96
|
+
def decide_clock
|
|
97
|
+
column = @column
|
|
98
|
+
if @dma_kind != NO_DMA && (column >= BA_COLUMN || column <= DMA_END_COLUMN) && !frozen?
|
|
99
|
+
@ba_low = true
|
|
100
|
+
@cpu_slots = column >= DMA_COLUMN || column <= DMA_END_COLUMN ? 0 : 1
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
@ba_low = false
|
|
105
|
+
@cpu_slots = single_clock?(column) ? 1 : 2
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def single_clock?(column)
|
|
109
|
+
return true if REFRESH_COLUMNS.include?(column) || @registers[0x13].anybits?(0x02) || frozen?
|
|
110
|
+
|
|
111
|
+
display_window? && !DOUBLE_COLUMNS.include?(column)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def display_window? = @registers[0x06].anybits?(0x10) && @line <= LAST_DISPLAY_LINE
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class TED
|
|
6
|
+
# What the TED reads for the picture, a single clock cycle at a time,
|
|
7
|
+
# and the counters that say where it reads.
|
|
8
|
+
#
|
|
9
|
+
# The two DMA lines of a character row (Beam) fetch a byte a cycle
|
|
10
|
+
# in phi2, in the 40 cycles of the character window, columns 54-56
|
|
11
|
+
# and 0-36: the attributes on the line before the row, from the video
|
|
12
|
+
# matrix base ($FF14 bits 3-7) with A10 clear, and the character
|
|
13
|
+
# pointers on the row's first line, with A10 set (the datasheet's
|
|
14
|
+
# video matrix and attribute addresses). The attributes go to a
|
|
15
|
+
# buffer of their own, and each takes hold as its character's
|
|
16
|
+
# pointer is fetched, so the row before shows its own colours to its
|
|
17
|
+
# last line, as xplus4 shows them.
|
|
18
|
+
#
|
|
19
|
+
# On every line of a row, from the cycle after each pointer's, the
|
|
20
|
+
# TED fetches the character's pattern byte in phi1, columns 55-56
|
|
21
|
+
# and 0-37: from the character base ($FF13 bits 2-7) with the
|
|
22
|
+
# pointer and the row's line counter, or from the bitmap base ($FF12
|
|
23
|
+
# bits 3-5) with the character position and the line counter in
|
|
24
|
+
# bitmap mode. With $FF07 bit 7 clear the pointer's bit 7 asks for
|
|
25
|
+
# reverse and the character base supplies A10; extended colour mode
|
|
26
|
+
# forces A9 and A10 low. The byte stays on the bus, where a read of
|
|
27
|
+
# an address nothing answers finds it (Plus/4 openio's outrun2).
|
|
28
|
+
#
|
|
29
|
+
# The matrix fetches see ROM or RAM as the CPU does at $8000-$FFFF,
|
|
30
|
+
# and the pattern fetches ROM with $FF12 bit 2 set or RAM with it
|
|
31
|
+
# clear (Plus4::Bus#ted_fetch and #ted_pattern_fetch).
|
|
32
|
+
#
|
|
33
|
+
# The counters: the character position reload ($FF1A-$FF1B), the
|
|
34
|
+
# character position the row starts at, which steps by 40 at the end
|
|
35
|
+
# of a row's eighth line, at horizontal count 290; and the vertical
|
|
36
|
+
# sub-address ($FF1F bits 0-2), the row's line, which steps with the
|
|
37
|
+
# line count on the display window's lines once the frame's first
|
|
38
|
+
# row is under way, wrapping from 7 to 0 whatever the DMA lines do.
|
|
39
|
+
# The attribute line reads the row the reload points to after its
|
|
40
|
+
# own latch: 40 on from it on a row's eighth line, the reload itself
|
|
41
|
+
# otherwise. The pointer line is the line after an attribute line. A
|
|
42
|
+
# frame starts with both counters at 0 and no row under way, and the
|
|
43
|
+
# frame's first pointer line puts a row under way to the end of the
|
|
44
|
+
# frame. The display window's lines with a row under way and the
|
|
45
|
+
# display on fetch pattern bytes.
|
|
46
|
+
module Fetches
|
|
47
|
+
# The first and last cycles of the pattern fetches.
|
|
48
|
+
PATTERN_COLUMN = 55
|
|
49
|
+
PATTERN_END_COLUMN = 37
|
|
50
|
+
|
|
51
|
+
# The cycle that latches the next row's character position.
|
|
52
|
+
LATCH_COLUMN = 36
|
|
53
|
+
|
|
54
|
+
# The cycle that counts the blink counter, horizontal count 336.
|
|
55
|
+
BLINK_COLUMN = 42
|
|
56
|
+
|
|
57
|
+
CHARACTERS = 40
|
|
58
|
+
|
|
59
|
+
# The pattern bytes, pointers and attributes of the characters on
|
|
60
|
+
# the line being fetched.
|
|
61
|
+
attr_reader :patterns, :pointers, :attributes
|
|
62
|
+
|
|
63
|
+
# The character position reload, the row's line, and whether a row
|
|
64
|
+
# is under way.
|
|
65
|
+
attr_reader :char_position, :row_line, :row_active
|
|
66
|
+
|
|
67
|
+
# The bus the TED fetches from, the machine's.
|
|
68
|
+
def connect(bus)
|
|
69
|
+
@bus = bus
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def reset_fetches
|
|
75
|
+
@patterns = Array.new(CHARACTERS, 0)
|
|
76
|
+
@pointers = Array.new(CHARACTERS, 0)
|
|
77
|
+
@attributes = Array.new(CHARACTERS, 0)
|
|
78
|
+
@fetched_attributes = Array.new(CHARACTERS, 0)
|
|
79
|
+
@char_position = 0
|
|
80
|
+
@line_position = 0
|
|
81
|
+
@row_line = 0
|
|
82
|
+
@row_active = false
|
|
83
|
+
@dma_kind = Beam::NO_DMA
|
|
84
|
+
@pattern_fetches = false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The fetches and counters of the cycle in +column+.
|
|
88
|
+
def fetch(column)
|
|
89
|
+
if column <= Beam::DMA_END_COLUMN || column >= Beam::DMA_COLUMN
|
|
90
|
+
window_fetch(column)
|
|
91
|
+
elsif column == PATTERN_END_COLUMN
|
|
92
|
+
pattern_fetch(column) if @pattern_fetches
|
|
93
|
+
elsif column == Beam::BA_COLUMN
|
|
94
|
+
start_line_fetches
|
|
95
|
+
elsif column == BLINK_COLUMN
|
|
96
|
+
count_blink
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# A cycle of the character window.
|
|
101
|
+
def window_fetch(column)
|
|
102
|
+
dma_fetch(column) unless @dma_kind == Beam::NO_DMA
|
|
103
|
+
pattern_fetch(column) if @pattern_fetches && column != Beam::DMA_COLUMN
|
|
104
|
+
latch_char_position if column == LATCH_COLUMN
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The blink counter counts a frame on one line of the vertical
|
|
108
|
+
# blank, the line xplus4 counts it on.
|
|
109
|
+
def count_blink
|
|
110
|
+
return unless @line == (lines_per_frame == 312 ? 256 : 228)
|
|
111
|
+
|
|
112
|
+
@blink = (@blink + 1) & 0x0f
|
|
113
|
+
blink_carry if @blink.zero?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Column 51: what the line fetches, decided where BA is.
|
|
117
|
+
def start_line_fetches
|
|
118
|
+
@dma_kind = dma_kind
|
|
119
|
+
@row_active = true if @dma_kind == Beam::POINTER_DMA
|
|
120
|
+
@line_position = @char_position
|
|
121
|
+
@pattern_fetches = @row_active && @line <= Beam::LAST_DISPLAY_LINE && display_enabled? && !@bus.nil?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# The attributes on a line whose low three bits match $FF06's
|
|
125
|
+
# scroll, below line 203, and the pointers on the line after one
|
|
126
|
+
# that fetched attributes (the datasheet's DMA timing, step 8),
|
|
127
|
+
# whatever the scroll says by then.
|
|
128
|
+
def dma_kind
|
|
129
|
+
return Beam::NO_DMA unless display_enabled?
|
|
130
|
+
return Beam::POINTER_DMA if @dma_kind == Beam::ATTRIBUTE_DMA
|
|
131
|
+
return Beam::NO_DMA unless @line < Beam::LAST_DISPLAY_LINE
|
|
132
|
+
|
|
133
|
+
(@line - @registers[0x06]).nobits?(0x07) ? Beam::ATTRIBUTE_DMA : Beam::NO_DMA
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def display_enabled? = @registers[0x06].anybits?(0x10)
|
|
137
|
+
|
|
138
|
+
def dma_fetch(column)
|
|
139
|
+
bus = @bus
|
|
140
|
+
return if bus.nil?
|
|
141
|
+
|
|
142
|
+
index = column >= Beam::DMA_COLUMN ? column - Beam::DMA_COLUMN : column + (Beam::CYCLES_PER_LINE - Beam::DMA_COLUMN)
|
|
143
|
+
matrix = (@registers[0x14] & 0xf8) << 8
|
|
144
|
+
if @dma_kind == Beam::ATTRIBUTE_DMA
|
|
145
|
+
position = latching? ? @char_position + CHARACTERS : @char_position
|
|
146
|
+
@fetched_attributes[index] = bus.ted_fetch(matrix | ((position + index) & 0x3ff))
|
|
147
|
+
else
|
|
148
|
+
@pointers[index] = bus.ted_fetch(matrix | 0x400 | ((@char_position + index) & 0x3ff))
|
|
149
|
+
@attributes[index] = @fetched_attributes[index]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def pattern_fetch(column)
|
|
154
|
+
index = column >= PATTERN_COLUMN ? column - PATTERN_COLUMN : column + (Beam::CYCLES_PER_LINE - PATTERN_COLUMN)
|
|
155
|
+
@patterns[index] = @bus.ted_pattern_fetch(pattern_address(index), @registers[0x12].anybits?(0x04))
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def pattern_address(index)
|
|
159
|
+
return bitmap_address(index) if @registers[0x06].anybits?(0x20)
|
|
160
|
+
|
|
161
|
+
pointer = @pointers[index]
|
|
162
|
+
base = @registers[0x13] & 0xf8
|
|
163
|
+
if @registers[0x06].anybits?(0x40)
|
|
164
|
+
pointer &= 0x3f
|
|
165
|
+
elsif !@registers[0x07].anybits?(0x80)
|
|
166
|
+
pointer = (pointer & 0x7f) | ((@registers[0x13] & 0x04) << 5)
|
|
167
|
+
end
|
|
168
|
+
(base << 8) | (pointer << 3) | @row_line
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def bitmap_address(index)
|
|
172
|
+
((@registers[0x12] & 0x38) << 10) | (((@line_position + index) & 0x3ff) << 3) | @row_line
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# The end of a row's eighth line moves the reload on to the next
|
|
176
|
+
# row.
|
|
177
|
+
def latch_char_position
|
|
178
|
+
@char_position = (@char_position + CHARACTERS) & 0x3ff if latching?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Whether the line ends a row, and latches the next row's position.
|
|
182
|
+
def latching? = @row_active && @row_line == 7 && @line <= Beam::LAST_DISPLAY_LINE
|
|
183
|
+
|
|
184
|
+
# The line count steps to +line+: the row's next line, up to the
|
|
185
|
+
# last line of the display window.
|
|
186
|
+
def step_row_line(line)
|
|
187
|
+
@row_line = (@row_line + 1) & 0x07 if @row_active && line <= Beam::LAST_DISPLAY_LINE
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# The end of the frame.
|
|
191
|
+
def end_fetch_frame
|
|
192
|
+
@char_position = 0
|
|
193
|
+
@row_line = 0
|
|
194
|
+
@row_active = false
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|