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
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CIA
|
|
6
|
+
class Timer
|
|
7
|
+
# A timer's fields in a keyed snapshot module (Snapshot::Keyed),
|
|
8
|
+
# under `prefix`: the counter, the latch and the pipeline between
|
|
9
|
+
# them. The fast-path flags are worked out again on load.
|
|
10
|
+
module KeyedState
|
|
11
|
+
def save_fields(out, prefix)
|
|
12
|
+
out.int("#{prefix}counter", @counter).int("#{prefix}latch", @latch).int("#{prefix}pipe", @pipe)
|
|
13
|
+
out.int("#{prefix}load_delay", @load_delay).bool("#{prefix}reload", @reload)
|
|
14
|
+
out.bool("#{prefix}underflowed", @underflowed).int("#{prefix}oneshot_linger", @oneshot_linger)
|
|
15
|
+
out.bool("#{prefix}toggle", @toggle)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def load_fields(input, prefix)
|
|
19
|
+
@counter = input.word("#{prefix}counter", @counter)
|
|
20
|
+
@latch = input.word("#{prefix}latch", @latch)
|
|
21
|
+
@pipe = input.masked("#{prefix}pipe", @pipe, 0b11)
|
|
22
|
+
@load_delay = input.int("#{prefix}load_delay", @load_delay)
|
|
23
|
+
@reload = input.bool("#{prefix}reload", @reload)
|
|
24
|
+
@underflowed = input.bool("#{prefix}underflowed", @underflowed)
|
|
25
|
+
@oneshot_linger = input.int("#{prefix}oneshot_linger", @oneshot_linger)
|
|
26
|
+
@toggle = input.bool("#{prefix}toggle", @toggle)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def restored! = settle
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
include KeyedState
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class InterruptRegister
|
|
36
|
+
# The interrupt register's fields in a keyed snapshot module, under
|
|
37
|
+
# `prefix`: the mask, the flags, an assert on its way and the reads
|
|
38
|
+
# whose acknowledge is still landing.
|
|
39
|
+
module KeyedState
|
|
40
|
+
def save_fields(out, prefix)
|
|
41
|
+
out.int("#{prefix}mask", @mask.value).int("#{prefix}status", @status.value)
|
|
42
|
+
out.int("#{prefix}pending", @pending)
|
|
43
|
+
out.optional_int("#{prefix}read", @read).optional_int("#{prefix}read_last_cycle", @read_last_cycle)
|
|
44
|
+
out.optional_int("#{prefix}read_two_cycles_ago", @read_two_cycles_ago)
|
|
45
|
+
out.bool("#{prefix}timer_b_bug", @timer_b_bug)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def load_fields(input, prefix)
|
|
49
|
+
@mask.value = input.byte("#{prefix}mask", @mask.value)
|
|
50
|
+
@status.value = input.byte("#{prefix}status", @status.value)
|
|
51
|
+
@pending = input.int("#{prefix}pending", @pending)
|
|
52
|
+
@read = input.optional_int("#{prefix}read")
|
|
53
|
+
@read_last_cycle = input.optional_int("#{prefix}read_last_cycle")
|
|
54
|
+
@read_two_cycles_ago = input.optional_int("#{prefix}read_two_cycles_ago")
|
|
55
|
+
@timer_b_bug = input.bool("#{prefix}timer_b_bug", @timer_b_bug)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Quiet when nothing is due and no read is recent enough to matter.
|
|
59
|
+
def restored!
|
|
60
|
+
@quiet = @pending.zero? && @read.nil? && @read_last_cycle.nil? && @read_two_cycles_ago.nil?
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
include KeyedState
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Serial
|
|
68
|
+
# The serial port's fields in a keyed snapshot module, under
|
|
69
|
+
# `prefix`: the registers, the bit count, the delay lines, the CNT
|
|
70
|
+
# and SP levels, and whether the next cycle can be skipped, which
|
|
71
|
+
# can lag what the delay lines show by a cycle.
|
|
72
|
+
module KeyedState
|
|
73
|
+
def save_fields(out, prefix)
|
|
74
|
+
out.int("#{prefix}data", @data).int("#{prefix}shift", @shift).int("#{prefix}steps", @steps)
|
|
75
|
+
out.optional_int("#{prefix}empty_in", @empty_in).optional_int("#{prefix}busy_in", @busy_in)
|
|
76
|
+
out.bool("#{prefix}busy", @busy).bool("#{prefix}abandoned", @abandoned)
|
|
77
|
+
out.optional_int("#{prefix}pending", @pending).bool("#{prefix}underflow_high", @underflow_high)
|
|
78
|
+
out.bool("#{prefix}in_flight", @in_flight).int("#{prefix}flight_up", @flight_up)
|
|
79
|
+
out.int("#{prefix}flight_down", @flight_down).bool("#{prefix}cnt", @cnt).bool("#{prefix}cnt_in", @cnt_in)
|
|
80
|
+
out.bool("#{prefix}sp_in", @sp_in).bool("#{prefix}sp_out", @sp_out).bool("#{prefix}idle", @idle)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def load_fields(input, prefix)
|
|
84
|
+
@data = input.byte("#{prefix}data", @data)
|
|
85
|
+
@shift = input.byte("#{prefix}shift", @shift)
|
|
86
|
+
@steps = input.int("#{prefix}steps", @steps)
|
|
87
|
+
@empty_in = input.optional_int("#{prefix}empty_in")
|
|
88
|
+
@busy_in = input.optional_int("#{prefix}busy_in")
|
|
89
|
+
@busy = input.bool("#{prefix}busy", @busy)
|
|
90
|
+
@abandoned = input.bool("#{prefix}abandoned", @abandoned)
|
|
91
|
+
@pending = input.optional_int("#{prefix}pending")
|
|
92
|
+
load_line_fields(input, prefix)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def restored! = nil
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def load_line_fields(input, prefix)
|
|
100
|
+
@underflow_high = input.bool("#{prefix}underflow_high", @underflow_high)
|
|
101
|
+
@in_flight = input.bool("#{prefix}in_flight", @in_flight)
|
|
102
|
+
@flight_up = input.int("#{prefix}flight_up", @flight_up)
|
|
103
|
+
@flight_down = input.int("#{prefix}flight_down", @flight_down)
|
|
104
|
+
@cnt = input.bool("#{prefix}cnt", @cnt)
|
|
105
|
+
@cnt_in = input.bool("#{prefix}cnt_in", @cnt_in)
|
|
106
|
+
@sp_in = input.bool("#{prefix}sp_in", @sp_in)
|
|
107
|
+
@sp_out = input.bool("#{prefix}sp_out", @sp_out)
|
|
108
|
+
@idle = input.bool("#{prefix}idle", @idle)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
include KeyedState
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class TimeOfDay
|
|
116
|
+
# The TOD clock's fields in a keyed snapshot module, under `prefix`:
|
|
117
|
+
# the clock, the alarm and a latched read field by field, the
|
|
118
|
+
# pulses towards the next tenth, and the mains divider's phase in
|
|
119
|
+
# cycle·Hz (it reaches the clock rate when a pulse arrives). The
|
|
120
|
+
# 50/60 Hz divider comes from CRA bit 7 on load.
|
|
121
|
+
module KeyedState
|
|
122
|
+
def save_fields(out, prefix)
|
|
123
|
+
out.int("#{prefix}mains_phase", @accumulator).int("#{prefix}pulses", @pulses)
|
|
124
|
+
out.bool("#{prefix}stopped", @stopped).bool("#{prefix}alarm_pending", @alarm_pending)
|
|
125
|
+
FIELDS.each do |field|
|
|
126
|
+
out.int("#{prefix}clock.#{field}", @clock[field]).int("#{prefix}alarm.#{field}", @alarm[field])
|
|
127
|
+
end
|
|
128
|
+
latch = @latch
|
|
129
|
+
FIELDS.each { |field| out.int("#{prefix}latch.#{field}", latch[field]) } if latch
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def load_fields(input, prefix)
|
|
133
|
+
@accumulator = input.int("#{prefix}mains_phase", @accumulator)
|
|
134
|
+
@pulses = input.int("#{prefix}pulses", @pulses)
|
|
135
|
+
@stopped = input.bool("#{prefix}stopped", @stopped)
|
|
136
|
+
@alarm_pending = input.bool("#{prefix}alarm_pending", @alarm_pending)
|
|
137
|
+
FIELDS.each do |field|
|
|
138
|
+
@clock[field] = input.byte("#{prefix}clock.#{field}", @clock[field])
|
|
139
|
+
@alarm[field] = input.byte("#{prefix}alarm.#{field}", @alarm[field])
|
|
140
|
+
end
|
|
141
|
+
load_latch_fields(input, prefix)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def restored!(fifty_hz)
|
|
145
|
+
self.fifty_hz = fifty_hz
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def load_latch_fields(input, prefix)
|
|
151
|
+
return @latch = nil if input.optional_int("#{prefix}latch.hours").nil?
|
|
152
|
+
|
|
153
|
+
@latch = FIELDS.to_h { |field| [field, input.byte("#{prefix}latch.#{field}", 0)] }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
include KeyedState
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The CIA's fields in a keyed snapshot module (Snapshot::Keyed): the
|
|
161
|
+
# ports, the control registers, the PB4 and CNT levels, and its parts
|
|
162
|
+
# under ta., tb., icr., sdr. and tod.
|
|
163
|
+
module KeyedState
|
|
164
|
+
def save_fields(out)
|
|
165
|
+
out.int("pra", @data_port_a).int("prb", @data_port_b).int("ddra", @data_dir_a).int("ddrb", @data_dir_b)
|
|
166
|
+
out.int("cra", @control_a.value).int("crb", @control_b.value)
|
|
167
|
+
out.bool("pb4_high", @port_b4_high).bool("pb4_driven_high", @port_b4_driven_high)
|
|
168
|
+
out.bool("cnt_high", @cnt_high).bool("cnt_rise", @cnt_rise)
|
|
169
|
+
@ta.save_fields(out, "ta.")
|
|
170
|
+
@tb.save_fields(out, "tb.")
|
|
171
|
+
@icr.save_fields(out, "icr.")
|
|
172
|
+
@serial.save_fields(out, "sdr.")
|
|
173
|
+
@tod.save_fields(out, "tod.")
|
|
174
|
+
out
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def load_fields(input)
|
|
178
|
+
@data_port_a = input.byte("pra", @data_port_a)
|
|
179
|
+
@data_port_b = input.byte("prb", @data_port_b)
|
|
180
|
+
@data_dir_a = input.byte("ddra", @data_dir_a)
|
|
181
|
+
@data_dir_b = input.byte("ddrb", @data_dir_b)
|
|
182
|
+
@control_a.value = input.byte("cra", @control_a.value)
|
|
183
|
+
@control_b.value = input.byte("crb", @control_b.value)
|
|
184
|
+
@port_b4_high = input.bool("pb4_high", @port_b4_high)
|
|
185
|
+
@port_b4_driven_high = input.bool("pb4_driven_high", @port_b4_driven_high)
|
|
186
|
+
@cnt_high = input.bool("cnt_high", @cnt_high)
|
|
187
|
+
@cnt_rise = input.bool("cnt_rise", @cnt_rise)
|
|
188
|
+
load_part_fields(input)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def restored!
|
|
192
|
+
@ta.restored!
|
|
193
|
+
@tb.restored!
|
|
194
|
+
@icr.restored!
|
|
195
|
+
@serial.restored!
|
|
196
|
+
@tod.restored!(@control_a.clock_frequency?)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
|
|
201
|
+
def load_part_fields(input)
|
|
202
|
+
@ta.load_fields(input, "ta.")
|
|
203
|
+
@tb.load_fields(input, "tb.")
|
|
204
|
+
@icr.load_fields(input, "icr.")
|
|
205
|
+
@serial.load_fields(input, "sdr.")
|
|
206
|
+
@tod.load_fields(input, "tod.")
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
include KeyedState
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/chips/cia/serial/saved_state"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
4
|
module Chips
|
|
7
5
|
class CIA
|
|
@@ -18,8 +16,6 @@ module Badline
|
|
|
18
16
|
# rather than waiting for the sixteenth underflow, and a byte written
|
|
19
17
|
# from that point on goes out on the next one.
|
|
20
18
|
class Serial
|
|
21
|
-
include SavedState
|
|
22
|
-
|
|
23
19
|
BITS = 8
|
|
24
20
|
|
|
25
21
|
# A byte written to the data register reaches the shift register one
|
|
@@ -32,28 +32,6 @@ module Badline
|
|
|
32
32
|
@match = enabled ? 4 : 5
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
# The clock and the alarm, field by field in FIELDS order, the latch,
|
|
36
|
-
# and the divider's phase.
|
|
37
|
-
def save_state(out)
|
|
38
|
-
out.int(@accumulator).int(@pulses).int(@match).boolean(@stopped).boolean(@alarm_pending)
|
|
39
|
-
FIELDS.each { |field| out.int(@clock[field]).int(@alarm[field]) }
|
|
40
|
-
out.boolean(!@latch.nil?)
|
|
41
|
-
FIELDS.each { |field| out.int(@latch[field]) } if @latch
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def load_state(input)
|
|
45
|
-
@accumulator = input.int
|
|
46
|
-
@pulses = input.int
|
|
47
|
-
@match = input.int
|
|
48
|
-
@stopped = input.boolean?
|
|
49
|
-
@alarm_pending = input.boolean?
|
|
50
|
-
FIELDS.each do |field|
|
|
51
|
-
@clock[field] = input.int
|
|
52
|
-
@alarm[field] = input.int
|
|
53
|
-
end
|
|
54
|
-
@latch = input.boolean? ? FIELDS.to_h { |field| [field, input.int] } : nil
|
|
55
|
-
end
|
|
56
|
-
|
|
57
35
|
# Whether the clock is stopped, or gets no pulses on its pin, with no
|
|
58
36
|
# alarm to raise, so its cycles move only the divider's phase.
|
|
59
37
|
def quiet? = (@stopped || @mains_hz.zero?) && !@alarm_pending
|
|
@@ -69,26 +69,6 @@ module Badline
|
|
|
69
69
|
settle
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# The counter, the latch and the pipeline between them. The control
|
|
73
|
-
# register is the CIA's to save.
|
|
74
|
-
def save_state(out)
|
|
75
|
-
out.int(@counter).int(@latch).int(@pipe).int(@load_delay).boolean(@reload).boolean(@underflowed)
|
|
76
|
-
out.int(@oneshot_linger).boolean(@toggle).boolean(@settled).boolean(@empty)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def load_state(input)
|
|
80
|
-
@counter = input.int
|
|
81
|
-
@latch = input.int
|
|
82
|
-
@pipe = input.int
|
|
83
|
-
@load_delay = input.int
|
|
84
|
-
@reload = input.boolean?
|
|
85
|
-
@underflowed = input.boolean?
|
|
86
|
-
@oneshot_linger = input.int
|
|
87
|
-
@toggle = input.boolean?
|
|
88
|
-
@settled = input.boolean?
|
|
89
|
-
@empty = input.boolean?
|
|
90
|
-
end
|
|
91
|
-
|
|
92
72
|
def write_control(value)
|
|
93
73
|
@toggle = true if value.anybits?(0x01) && !started?
|
|
94
74
|
@oneshot_linger = 2 if control.run_mode? && value.nobits?(0x08)
|
data/lib/badline/chips/cia.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/chips/cia/time_of_day"
|
|
3
4
|
require "badline/chips/cia/interrupt_register"
|
|
4
5
|
require "badline/chips/cia/serial"
|
|
5
6
|
require "badline/chips/cia/timer"
|
|
6
|
-
require "badline/chips/cia/saved_state"
|
|
7
7
|
require "badline/chips/cia/fast_forward"
|
|
8
|
+
require "badline/chips/cia/keyed_parts"
|
|
8
9
|
|
|
9
10
|
module Badline
|
|
10
11
|
module Chips
|
|
11
12
|
# CIA (Complex Interface Adapter) chip
|
|
12
13
|
class CIA
|
|
13
14
|
include Addressable
|
|
14
|
-
include SavedState
|
|
15
15
|
include FastForward
|
|
16
16
|
|
|
17
17
|
# The original NMOS 6526 of the breadbin C64, and the HMOS 6526A of the
|
|
@@ -5,7 +5,7 @@ require "badline/chips/cpu/addressing"
|
|
|
5
5
|
require "badline/chips/cpu/operations"
|
|
6
6
|
require "badline/chips/cpu/stack_operations"
|
|
7
7
|
require "badline/chips/cpu/boundary"
|
|
8
|
-
require "badline/chips/cpu/
|
|
8
|
+
require "badline/chips/cpu/keyed_state"
|
|
9
9
|
|
|
10
10
|
module Badline
|
|
11
11
|
module Chips
|
|
@@ -23,7 +23,7 @@ module Badline
|
|
|
23
23
|
include Operations
|
|
24
24
|
include StackOperations
|
|
25
25
|
include Boundary
|
|
26
|
-
include
|
|
26
|
+
include KeyedState
|
|
27
27
|
|
|
28
28
|
attr_reader :memory, :instructions, :boundary_crossed, :cycles, :pending_write
|
|
29
29
|
attr_accessor :program_counter, :stack_pointer, :status, :a, :x, :y, :nmi, :irq
|
|
@@ -67,6 +67,22 @@ module Badline
|
|
|
67
67
|
end_sequence
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# Drops the instruction or interrupt sequence in flight and the
|
|
71
|
+
# interrupt lines' pipeline, so the next cycle fetches an opcode at the
|
|
72
|
+
# program counter, and sets the cycle count. A snapshot from VICE,
|
|
73
|
+
# taken between instructions, restores through here.
|
|
74
|
+
def resume(cycles)
|
|
75
|
+
@irq_sample = @irq_pending = false
|
|
76
|
+
@nmi_sample = @nmi_pending = false
|
|
77
|
+
@skip_poll = false
|
|
78
|
+
@interrupt = nil
|
|
79
|
+
@brk = false
|
|
80
|
+
@stalled_at = nil
|
|
81
|
+
@cycles = cycles
|
|
82
|
+
end_sequence
|
|
83
|
+
@pending_write = false
|
|
84
|
+
end
|
|
85
|
+
|
|
70
86
|
def p
|
|
71
87
|
status.value
|
|
72
88
|
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
# Where a CPU caught inside an instruction is, as a snapshot names it:
|
|
7
|
+
# an opcode's instruction, an IRQ or NMI sequence, or jammed.
|
|
8
|
+
POSITIONS = %w[boundary op interrupt jam].freeze
|
|
9
|
+
|
|
10
|
+
# The CPU's fields in a keyed snapshot module (Snapshot::Keyed): the
|
|
11
|
+
# registers, the interrupt lines' pipeline, the counts and, inside an
|
|
12
|
+
# instruction, where it is in hardware terms (pos.*), rather than as
|
|
13
|
+
# the microcode lays it out.
|
|
14
|
+
module KeyedState
|
|
15
|
+
def save_fields(out)
|
|
16
|
+
out.int("pc", @program_counter).int("sp", @stack_pointer).int("a", @a).int("x", @x).int("y", @y)
|
|
17
|
+
out.int("p", @status.value).bool("nmi", @nmi).bool("so_high", @so_high)
|
|
18
|
+
out.bool("irq.sample", @irq_sample).bool("irq.pending", @irq_pending).bool("irq.boundary", @boundary_irq)
|
|
19
|
+
out.bool("nmi.sample", @nmi_sample).bool("nmi.pending", @nmi_pending).bool("nmi.boundary", @boundary_nmi)
|
|
20
|
+
out.bool("skip_poll", @skip_poll).optional_int("stalled", stalled_ago)
|
|
21
|
+
out.int("cycles", @cycles).int("instructions", @instructions)
|
|
22
|
+
save_position(out) unless boundary?
|
|
23
|
+
out
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load_fields(input)
|
|
27
|
+
@program_counter = input.word("pc", @program_counter)
|
|
28
|
+
@stack_pointer = input.byte("sp", @stack_pointer)
|
|
29
|
+
@a = input.byte("a", @a)
|
|
30
|
+
@x = input.byte("x", @x)
|
|
31
|
+
@y = input.byte("y", @y)
|
|
32
|
+
@status.value = input.byte("p", @status.value)
|
|
33
|
+
load_lines(input)
|
|
34
|
+
@cycles = input.int("cycles", @cycles)
|
|
35
|
+
@instructions = input.int("instructions", @instructions)
|
|
36
|
+
stalled = input.optional_int("stalled")
|
|
37
|
+
@stalled_at = stalled.nil? ? nil : @cycles - stalled
|
|
38
|
+
load_position(input)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# What the fields leave to be worked out: whether the next step
|
|
42
|
+
# writes.
|
|
43
|
+
def restored!
|
|
44
|
+
@pending_write = @writes[@index]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Cycles since the CPU last stalled, when that was this cycle or the
|
|
50
|
+
# one before, the only ones an instruction looks back at.
|
|
51
|
+
def stalled_ago
|
|
52
|
+
stalled_at = @stalled_at
|
|
53
|
+
return nil if stalled_at.nil?
|
|
54
|
+
|
|
55
|
+
ago = @cycles - stalled_at
|
|
56
|
+
ago.between?(0, 1) ? ago : nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def load_lines(input)
|
|
60
|
+
@nmi = input.bool("nmi", @nmi)
|
|
61
|
+
@so_high = input.bool("so_high", @so_high)
|
|
62
|
+
@irq_sample = input.bool("irq.sample", @irq_sample)
|
|
63
|
+
@irq_pending = input.bool("irq.pending", @irq_pending)
|
|
64
|
+
@boundary_irq = input.bool("irq.boundary", @boundary_irq)
|
|
65
|
+
@nmi_sample = input.bool("nmi.sample", @nmi_sample)
|
|
66
|
+
@nmi_pending = input.bool("nmi.pending", @nmi_pending)
|
|
67
|
+
@boundary_nmi = input.bool("nmi.boundary", @boundary_nmi)
|
|
68
|
+
@skip_poll = input.bool("skip_poll", @skip_poll)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def save_position(out)
|
|
72
|
+
source = plan_source
|
|
73
|
+
if source == JAMMED
|
|
74
|
+
out.text("pos.seq", "jam")
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
out.text("pos.seq", source == INTERRUPT ? "interrupt" : "op")
|
|
79
|
+
out.int("pos.op", source) unless source == INTERRUPT
|
|
80
|
+
out.int("pos.t", position_t(source)).bool("pos.crossed", @boundary_crossed)
|
|
81
|
+
out.bool("pos.branch_taken", @branch_taken == true).bool("pos.brk", @brk)
|
|
82
|
+
out.optional_int("pos.vector", @interrupt)
|
|
83
|
+
out.optional_int("pos.address", @address).optional_int("pos.dummy_address", @dummy_address)
|
|
84
|
+
out.optional_int("pos.value", @value).optional_int("pos.rmw", @rmw_result)
|
|
85
|
+
out.optional_int("pos.pointer", @pointer)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The cycles of the instruction or sequence run so far, counting the
|
|
89
|
+
# opcode fetch as 1 and leaving out stalled cycles.
|
|
90
|
+
def position_t(source)
|
|
91
|
+
return @index if source == INTERRUPT
|
|
92
|
+
|
|
93
|
+
MICROCODE.fetch(source).t_at(@index, @boundary_crossed)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def load_position(input)
|
|
97
|
+
case input.choice("pos.seq", POSITIONS, "boundary")
|
|
98
|
+
when "op" then load_instruction(input)
|
|
99
|
+
when "interrupt" then load_interrupt(input)
|
|
100
|
+
when "jam" then load_jam
|
|
101
|
+
else load_boundary
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def load_instruction(input)
|
|
106
|
+
micro = MICROCODE.fetch(input.byte("pos.op", 0xea))
|
|
107
|
+
use_plan(micro.plan, micro.writes)
|
|
108
|
+
@operation = micro.operation
|
|
109
|
+
@optional_dummy = micro.optional_dummy
|
|
110
|
+
@boundary_crossed = input.bool("pos.crossed", false)
|
|
111
|
+
@index = micro.index_at(input.int("pos.t", 1), @boundary_crossed).clamp(1, micro.plan.length - 1)
|
|
112
|
+
load_latches(input)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def load_interrupt(input)
|
|
116
|
+
use_plan(INTERRUPT_PLAN, INTERRUPT_WRITES)
|
|
117
|
+
@boundary_crossed = input.bool("pos.crossed", false)
|
|
118
|
+
@index = input.int("pos.t", 1).clamp(1, INTERRUPT_PLAN.length - 1)
|
|
119
|
+
load_latches(input)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def load_jam
|
|
123
|
+
use_plan(JAMMED_PLAN, JAMMED_WRITES)
|
|
124
|
+
@index = 0
|
|
125
|
+
clear_latches
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def load_boundary
|
|
129
|
+
use_plan(FETCH_PLAN, FETCH_WRITES)
|
|
130
|
+
@index = 0
|
|
131
|
+
clear_latches
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def load_latches(input)
|
|
135
|
+
@branch_taken = input.bool("pos.branch_taken", false)
|
|
136
|
+
@brk = input.bool("pos.brk", false)
|
|
137
|
+
@interrupt = input.optional_int("pos.vector")
|
|
138
|
+
@address = input.optional_int("pos.address")
|
|
139
|
+
@dummy_address = input.optional_int("pos.dummy_address")
|
|
140
|
+
@value = input.optional_int("pos.value")
|
|
141
|
+
@rmw_result = input.optional_int("pos.rmw")
|
|
142
|
+
@pointer = input.optional_int("pos.pointer")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The working registers no step reads before the next instruction
|
|
146
|
+
# sets them.
|
|
147
|
+
def clear_latches
|
|
148
|
+
@boundary_crossed = false
|
|
149
|
+
@branch_taken = false
|
|
150
|
+
@brk = false
|
|
151
|
+
@interrupt = nil
|
|
152
|
+
@address = nil
|
|
153
|
+
@dummy_address = nil
|
|
154
|
+
@value = nil
|
|
155
|
+
@rmw_result = nil
|
|
156
|
+
@pointer = nil
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Where the plan running comes from: an opcode's microcode, or FETCH,
|
|
160
|
+
# INTERRUPT or JAMMED below it.
|
|
161
|
+
def plan_source
|
|
162
|
+
return FETCH if @plan.equal?(FETCH_PLAN)
|
|
163
|
+
return INTERRUPT if @plan.equal?(INTERRUPT_PLAN)
|
|
164
|
+
return JAMMED if @plan.equal?(JAMMED_PLAN)
|
|
165
|
+
|
|
166
|
+
MICROCODE.index { |micro| micro.plan.equal?(@plan) && micro.operation == @operation } ||
|
|
167
|
+
MICROCODE.index { |micro| micro.plan.equal?(@plan) }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def use_plan(plan, writes)
|
|
171
|
+
@plan = plan
|
|
172
|
+
@writes = writes
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -117,6 +117,22 @@ module Badline
|
|
|
117
117
|
end.freeze
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
+
# The plan step that follows `cycles` cycles of the instruction, counting
|
|
121
|
+
# the opcode fetch as 1: one further on once an indexed read has
|
|
122
|
+
# skipped its dummy read, which it does when the index add didn't
|
|
123
|
+
# cross a page.
|
|
124
|
+
def index_at(cycles, crossed) = skips_dummy?(cycles, crossed) ? cycles + 1 : cycles
|
|
125
|
+
|
|
126
|
+
# The cycles run before plan step `index`, as index_at counts them.
|
|
127
|
+
def t_at(index, crossed) = skips_dummy?(index - 1, crossed) ? index - 1 : index
|
|
128
|
+
|
|
129
|
+
def skips_dummy?(cycles, crossed)
|
|
130
|
+
return false unless optional_dummy && !crossed
|
|
131
|
+
|
|
132
|
+
dummy = plan.index(:op_idx_dummy)
|
|
133
|
+
!dummy.nil? && cycles >= dummy
|
|
134
|
+
end
|
|
135
|
+
|
|
120
136
|
def self.write_mask(plan)
|
|
121
137
|
plan.map { |step| WRITE_STEPS.include?(step) }.freeze
|
|
122
138
|
end
|
|
@@ -6,7 +6,8 @@ module Badline
|
|
|
6
6
|
# The I/O port of the 6510 and the 8502 at $00 (direction) and $01
|
|
7
7
|
# (data). An input pin reads its pull-up, P4 reads the datasette's sense
|
|
8
8
|
# line, and a pin without a pull-up holds the charge last driven onto
|
|
9
|
-
# it. P5 drives the datasette motor through an inverter: low runs it
|
|
9
|
+
# it. P5 drives the datasette motor through an inverter: low runs it,
|
|
10
|
+
# and P3 drives its write line.
|
|
10
11
|
#
|
|
11
12
|
# The 6510 has pull-ups on P0-P2 and P4 and leaves P3, P6 and P7
|
|
12
13
|
# floating. The 8502 adds a CAPS LOCK pin on P6, high while the key is
|
|
@@ -38,6 +39,7 @@ module Badline
|
|
|
38
39
|
@floating = 0x00
|
|
39
40
|
@change_handler = nil
|
|
40
41
|
@status = PortStatus.new(%i[basic kernal io tape_out tape_switch tape_motor], value: input_value)
|
|
42
|
+
@datasette.write_level = @status.tape_out?
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
# Calls the block after every write to the port, for the bus to remap
|
|
@@ -68,19 +70,33 @@ module Badline
|
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
# Sets the registers and the charge, as a snapshot restores them, and
|
|
71
|
-
# drives the
|
|
73
|
+
# drives the datasette and calls the change handler as a write does.
|
|
72
74
|
def restore(ddr, data, floating)
|
|
73
75
|
load(ddr, data, floating)
|
|
74
76
|
update
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
# Sets the registers and the charge, as a snapshot loads them, without
|
|
78
|
-
# driving the
|
|
80
|
+
# driving the datasette or calling the change handler. The write
|
|
81
|
+
# line takes P3's level without recording an edge.
|
|
79
82
|
def load(ddr, data, floating)
|
|
80
83
|
@ddr = ddr
|
|
81
84
|
@data = data
|
|
82
85
|
@floating = floating
|
|
83
86
|
refresh
|
|
87
|
+
@datasette.write_level = @status.tape_out?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The registers and the charge in a keyed snapshot module
|
|
91
|
+
# (Snapshot::Keyed), under `prefix`. What a read sees is worked out
|
|
92
|
+
# again on load, and the bus maps its banks afterwards.
|
|
93
|
+
def save_fields(out, prefix)
|
|
94
|
+
out.int("#{prefix}ddr", @ddr).int("#{prefix}data", @data).int("#{prefix}floating", @floating)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def load_fields(input, prefix)
|
|
98
|
+
load(input.byte("#{prefix}ddr", @ddr), input.byte("#{prefix}data", @data),
|
|
99
|
+
input.byte("#{prefix}floating", @floating))
|
|
84
100
|
end
|
|
85
101
|
|
|
86
102
|
# Holds CAPS LOCK down, or lets it up.
|
|
@@ -101,6 +117,7 @@ module Badline
|
|
|
101
117
|
@floating = (@floating & ~driven) | (@data & driven)
|
|
102
118
|
refresh
|
|
103
119
|
@datasette.motor = !@status.tape_motor?
|
|
120
|
+
@datasette.write_line = @status.tape_out?
|
|
104
121
|
@change_handler&.call
|
|
105
122
|
end
|
|
106
123
|
|
data/lib/badline/chips/cpu.rb
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/chips/cpu/port"
|
|
4
|
+
require "badline/chips/cpu/instruction"
|
|
5
|
+
require "badline/chips/cpu/instruction_set"
|
|
6
|
+
require "badline/chips/cpu/interrupts"
|
|
7
|
+
require "badline/chips/cpu/traps"
|
|
3
8
|
require "badline/chips/cpu/core"
|
|
4
9
|
|
|
5
10
|
module Badline
|
|
@@ -23,17 +23,19 @@ module Badline
|
|
|
23
23
|
# Cycles until the current window closes, counting the next one as 1.
|
|
24
24
|
attr_reader :cycles_to_close
|
|
25
25
|
|
|
26
|
-
# How far the current window has got
|
|
26
|
+
# How far the current window has got, in a keyed snapshot module
|
|
27
|
+
# (Snapshot::Keyed) under `prefix`. The clock and the rate are the
|
|
27
28
|
# host's.
|
|
28
|
-
def
|
|
29
|
-
out.int(@phase).int(@sum).int(@count)
|
|
29
|
+
def save_fields(out, prefix)
|
|
30
|
+
out.int("#{prefix}phase", @phase).int("#{prefix}sum", @sum).int("#{prefix}count", @count)
|
|
31
|
+
out.int("#{prefix}cycles_to_close", @cycles_to_close)
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
def
|
|
33
|
-
@phase = input.int
|
|
34
|
-
@sum = input.int
|
|
35
|
-
@count = input.int
|
|
36
|
-
@cycles_to_close = input.int
|
|
34
|
+
def load_fields(input, prefix)
|
|
35
|
+
@phase = input.int("#{prefix}phase", @phase)
|
|
36
|
+
@sum = input.int("#{prefix}sum", @sum)
|
|
37
|
+
@count = input.int("#{prefix}count", @count)
|
|
38
|
+
@cycles_to_close = input.int("#{prefix}cycles_to_close", @cycles_to_close)
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Returns the averaged sample when these cycles close a window, and
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "badline/chips/sid/envelope/saved_state"
|
|
4
|
-
|
|
5
3
|
module Badline
|
|
6
4
|
module Chips
|
|
7
5
|
class SID
|
|
@@ -28,11 +26,8 @@ module Badline
|
|
|
28
26
|
EXPONENTIAL_PERIODS = { 0xff => 1, 0x5d => 2, 0x36 => 4, 0x1a => 8,
|
|
29
27
|
0x0e => 16, 0x06 => 30, 0x00 => 1 }.freeze
|
|
30
28
|
|
|
31
|
-
# The states
|
|
29
|
+
# The states, which VICE's SIDEXTENDED numbers by index.
|
|
32
30
|
STATES = %i[attack decay_sustain release].freeze
|
|
33
|
-
|
|
34
|
-
include SavedState
|
|
35
|
-
|
|
36
31
|
attr_reader :state, :counter, :env3
|
|
37
32
|
|
|
38
33
|
def initialize
|