badline 0.5.1 → 0.6.1
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 +52 -0
- data/CLAUDE.md +10 -9
- data/README.md +82 -10
- data/doc/pinned-behaviour.md +131 -30
- data/exe/badline-ruby +20 -23
- data/lib/badline/address_bus/saved_state.rb +72 -0
- data/lib/badline/address_bus.rb +11 -1
- data/lib/badline/audio/cli.rb +18 -5
- data/lib/badline/audio/console.rb +2 -1
- data/lib/badline/audio.rb +0 -3
- data/lib/badline/cartridge/action_replay.rb +15 -0
- data/lib/badline/cartridge/comal80.rb +8 -0
- data/lib/badline/cartridge/dinamic.rb +8 -0
- data/lib/badline/cartridge/easy_flash.rb +20 -1
- data/lib/badline/cartridge/epyx_fastload.rb +8 -0
- data/lib/badline/cartridge/final_cartridge3.rb +12 -0
- data/lib/badline/cartridge/flash.rb +41 -0
- data/lib/badline/cartridge/fun_play.rb +8 -0
- data/lib/badline/cartridge/g_mod2.rb +12 -0
- data/lib/badline/cartridge/game_system.rb +8 -0
- data/lib/badline/cartridge/geo_ram.rb +16 -0
- data/lib/badline/cartridge/magic_desk.rb +8 -0
- data/lib/badline/cartridge/ocean.rb +8 -0
- data/lib/badline/cartridge/pagefox.rb +12 -0
- data/lib/badline/cartridge/retro_replay.rb +44 -1
- data/lib/badline/cartridge/rgcd.rb +10 -0
- data/lib/badline/cartridge/super_games.rb +10 -0
- data/lib/badline/cartridge/zaxxon.rb +16 -0
- data/lib/badline/cartridge.rb +72 -0
- data/lib/badline/cia/interrupt_register.rb +17 -0
- data/lib/badline/cia/serial.rb +44 -0
- data/lib/badline/cia/state.rb +47 -0
- data/lib/badline/cia/timer.rb +33 -0
- data/lib/badline/cia.rb +8 -0
- data/lib/badline/computer/attachments.rb +132 -0
- data/lib/badline/computer/saved_state.rb +116 -0
- data/lib/badline/computer.rb +13 -33
- data/lib/badline/cpu/boundary.rb +54 -0
- data/lib/badline/cpu/core.rb +165 -0
- data/lib/badline/cpu/microcode.rb +8 -0
- data/lib/badline/cpu/saved_state.rb +115 -0
- data/lib/badline/cpu.rb +3 -212
- data/lib/badline/datasette.rb +27 -0
- data/lib/badline/drive1541/bus.rb +13 -0
- data/lib/badline/drive1541/cpu.rb +29 -0
- data/lib/badline/drive1541/disk.rb +7 -2
- data/lib/badline/drive1541/disk_state.rb +80 -0
- data/lib/badline/drive1541/mechanism.rb +2 -0
- data/lib/badline/drive1541/mechanism_state.rb +67 -0
- data/lib/badline/drive1541/rotation.rb +10 -0
- data/lib/badline/drive1541/serial_port.rb +10 -0
- data/lib/badline/drive1541.rb +35 -0
- data/lib/badline/ffi.rb +157 -0
- data/lib/badline/frontend/app.rb +225 -0
- data/lib/badline/frontend/audio_sink.rb +72 -0
- data/lib/badline/frontend/boot.rb +52 -0
- data/lib/badline/frontend/controls.rb +163 -0
- data/lib/badline/frontend/drive_led.rb +51 -0
- data/lib/badline/frontend/frame_rate.rb +83 -0
- data/lib/badline/frontend/gamepads.rb +76 -0
- data/lib/badline/frontend/keys.rb +48 -0
- data/lib/badline/frontend/pacer.rb +100 -0
- data/lib/badline/frontend/pad_port.rb +61 -0
- data/lib/badline/frontend/screen.rb +65 -0
- data/lib/badline/frontend/screenshot.rb +22 -0
- data/lib/badline/frontend/snapshots.rb +60 -0
- data/lib/badline/frontend/sound.rb +153 -0
- data/lib/badline/frontend/timeline.rb +171 -0
- data/lib/badline/frontend.rb +18 -0
- data/lib/badline/kernal_trap/channel.rb +27 -0
- data/lib/badline/kernal_trap/drive/channels.rb +25 -0
- data/lib/badline/kernal_trap/drive/memory.rb +9 -0
- data/lib/badline/kernal_trap/drive/status.rb +8 -0
- data/lib/badline/kernal_trap/drive/write_file.rb +21 -0
- data/lib/badline/kernal_trap/drive.rb +19 -0
- data/lib/badline/kernal_trap/save.rb +8 -0
- data/lib/badline/kernal_trap/serial.rb +16 -0
- data/lib/badline/memory.rb +8 -0
- data/lib/badline/options/event.rb +94 -0
- data/lib/badline/options/help.rb +78 -0
- data/lib/badline/options/option.rb +29 -0
- data/lib/badline/options/table.rb +62 -0
- data/lib/badline/options.rb +181 -144
- data/lib/badline/ram_expansion/plus256k.rb +16 -0
- data/lib/badline/ram_expansion/plus60k.rb +12 -0
- data/lib/badline/ram_expansion/unexpanded.rb +6 -0
- data/lib/badline/reu/dma/saved_state.rb +50 -0
- data/lib/badline/reu/dma.rb +56 -11
- data/lib/badline/reu/ram.rb +15 -0
- data/lib/badline/reu/saved_state.rb +53 -0
- data/lib/badline/reu.rb +23 -10
- data/lib/badline/sdl.rb +123 -51
- data/lib/badline/sid/envelope/state.rb +68 -0
- data/lib/badline/sid/envelope.rb +7 -0
- data/lib/badline/sid/filter.rb +38 -0
- data/lib/badline/sid/voice.rb +10 -0
- data/lib/badline/sid/waveform/noise_writeback.rb +33 -11
- data/lib/badline/sid/waveform/state.rb +66 -0
- data/lib/badline/sid/waveform.rb +4 -3
- data/lib/badline/sid.rb +34 -0
- data/lib/badline/snapshot/container.rb +117 -0
- data/lib/badline/snapshot/fields.rb +54 -0
- data/lib/badline/snapshot/image.rb +70 -0
- data/lib/badline/snapshot/machine_state.rb +77 -0
- data/lib/badline/snapshot/setup.rb +58 -0
- data/lib/badline/snapshot/state.rb +178 -0
- data/lib/badline/snapshot/vice/c64_mem.rb +53 -0
- data/lib/badline/snapshot/vice/cias.rb +89 -0
- data/lib/badline/snapshot/vice/cias_import.rb +70 -0
- data/lib/badline/snapshot/vice/main_cpu.rb +76 -0
- data/lib/badline/snapshot/vice/peripherals.rb +47 -0
- data/lib/badline/snapshot/vice/restore.rb +125 -0
- data/lib/badline/snapshot/vice/sid_extended.rb +96 -0
- data/lib/badline/snapshot/vice/sid_registers.rb +46 -0
- data/lib/badline/snapshot/vice/vicii.rb +119 -0
- data/lib/badline/snapshot/vice/vicii_import.rb +131 -0
- data/lib/badline/snapshot/vice.rb +74 -0
- data/lib/badline/snapshot.rb +52 -0
- data/lib/badline/storage/crt_file.rb +9 -3
- data/lib/badline/storage/d64_image.rb +2 -0
- data/lib/badline/storage/d71_image.rb +2 -0
- data/lib/badline/storage/d81_image.rb +2 -0
- data/lib/badline/storage/disk_image.rb +11 -2
- data/lib/badline/storage/host_directory.rb +5 -0
- data/lib/badline/storage/t64.rb +6 -0
- data/lib/badline/storage/tap.rb +10 -1
- data/lib/badline/storage/unavailable.rb +20 -0
- data/lib/badline/storage.rb +34 -0
- data/lib/badline/time_of_day.rb +56 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/via/control_lines.rb +15 -0
- data/lib/badline/via/interrupt_register.rb +9 -0
- data/lib/badline/via/shift_register.rb +13 -0
- data/lib/badline/via/state.rb +42 -0
- data/lib/badline/via/timer1.rb +13 -0
- data/lib/badline/via/timer2.rb +18 -0
- data/lib/badline/via.rb +2 -0
- data/lib/badline/vic/bank.rb +21 -0
- data/lib/badline/vic/border_mask.rb +10 -0
- data/lib/badline/vic/collisions.rb +11 -0
- data/lib/badline/vic/color_patches.rb +11 -0
- data/lib/badline/vic/display_state.rb +29 -1
- data/lib/badline/vic/graphics_shifter.rb +26 -0
- data/lib/badline/vic/register_log.rb +18 -0
- data/lib/badline/vic/registers.rb +13 -2
- data/lib/badline/vic/sequencer.rb +12 -0
- data/lib/badline/vic/sequencer_state.rb +89 -0
- data/lib/badline/vic/sprite/internal_bus.rb +11 -0
- data/lib/badline/vic/sprite/saved_state.rb +64 -0
- data/lib/badline/vic/sprite.rb +2 -0
- data/lib/badline/vic/sprites.rb +3 -0
- data/lib/badline/vic/sprites_state.rb +57 -0
- data/lib/badline/vic.rb +109 -16
- data/lib/badline.rb +1 -0
- metadata +59 -18
- data/.claude/hooks/session-start.sh +0 -57
- data/.claude/settings.json +0 -14
- data/lib/badline/audio/sdl_sink.rb +0 -70
- data/lib/badline/audio/stream.rb +0 -117
- data/lib/badline/gui/application.rb +0 -216
- data/lib/badline/gui/drive_led_pane.rb +0 -29
- data/lib/badline/gui/gamepads.rb +0 -97
- data/lib/badline/gui/joy_map.rb +0 -27
- data/lib/badline/gui/key_map.rb +0 -39
- data/lib/badline/gui/palette.rb +0 -35
- data/lib/badline/gui/pane.rb +0 -36
- data/lib/badline/gui/screen_pane.rb +0 -46
- data/lib/badline/gui/window.rb +0 -61
- data/lib/badline/gui.rb +0 -13
- data/lib/badline/sdl/events.rb +0 -47
- data/lib/badline/sdl/functions.rb +0 -74
- data/vendor/.gitkeep +0 -0
data/lib/badline/via/timer1.rb
CHANGED
|
@@ -92,6 +92,19 @@ module Badline
|
|
|
92
92
|
@counter = @reload ? 0xffff : @latch - phase + 1
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
def save_state(out)
|
|
96
|
+
out.int(@counter).int(@latch).boolean(@hold).boolean(@reload).boolean(@armed).boolean(@pb7)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def load_state(input)
|
|
100
|
+
@counter = input.int
|
|
101
|
+
@latch = input.int
|
|
102
|
+
@hold = input.boolean?
|
|
103
|
+
@reload = input.boolean?
|
|
104
|
+
@armed = input.boolean?
|
|
105
|
+
@pb7 = input.boolean?
|
|
106
|
+
end
|
|
107
|
+
|
|
95
108
|
# What the counter and PB7 don't hold, for comparing the timer at two
|
|
96
109
|
# points.
|
|
97
110
|
def state = [@latch, @armed]
|
data/lib/badline/via/timer2.rb
CHANGED
|
@@ -84,6 +84,24 @@ module Badline
|
|
|
84
84
|
@low_underflowed = @counter.allbits?(0xff)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def save_state(out)
|
|
88
|
+
out.int(@counter).int(@latch_low)
|
|
89
|
+
[@hold, @low_reload, @armed, @low_underflowed, @count_pulses, @counting_pulses].each do |flag|
|
|
90
|
+
out.boolean(flag)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def load_state(input)
|
|
95
|
+
@counter = input.int
|
|
96
|
+
@latch_low = input.int
|
|
97
|
+
@hold = input.boolean?
|
|
98
|
+
@low_reload = input.boolean?
|
|
99
|
+
@armed = input.boolean?
|
|
100
|
+
@low_underflowed = input.boolean?
|
|
101
|
+
@count_pulses = input.boolean?
|
|
102
|
+
@counting_pulses = input.boolean?
|
|
103
|
+
end
|
|
104
|
+
|
|
87
105
|
# What the counter and the low byte's underflow don't hold, for
|
|
88
106
|
# comparing the timer at two points.
|
|
89
107
|
def state = [@latch_low, @armed, @hold, @low_reload, @count_pulses, @counting_pulses]
|
data/lib/badline/via.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "badline/via/control_lines"
|
|
|
4
4
|
require "badline/via/fast_forward"
|
|
5
5
|
require "badline/via/interrupt_register"
|
|
6
6
|
require "badline/via/shift_register"
|
|
7
|
+
require "badline/via/state"
|
|
7
8
|
require "badline/via/timer1"
|
|
8
9
|
require "badline/via/timer2"
|
|
9
10
|
|
|
@@ -18,6 +19,7 @@ module Badline
|
|
|
18
19
|
class VIA
|
|
19
20
|
include Addressable
|
|
20
21
|
include FastForward
|
|
22
|
+
include State
|
|
21
23
|
|
|
22
24
|
attr_reader :start, :peripheral, :shift_register, :acr, :pcr
|
|
23
25
|
|
data/lib/badline/vic/bank.rb
CHANGED
|
@@ -13,9 +13,26 @@ module Badline
|
|
|
13
13
|
|
|
14
14
|
attr_reader :address_bus
|
|
15
15
|
|
|
16
|
+
# The bank lines as #sample_lines last saw them.
|
|
17
|
+
attr_accessor :lines
|
|
18
|
+
|
|
16
19
|
def initialize(address_bus = nil)
|
|
17
20
|
addressable_at(0x0000, length: 2**14)
|
|
18
21
|
@address_bus = address_bus || AddressBus.new
|
|
22
|
+
@lines = 0b11
|
|
23
|
+
@swapped = false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# On the 8565, a port A write that swaps the two bank lines, driving
|
|
27
|
+
# one high as the other goes low, shows the VIC both lines low for the
|
|
28
|
+
# cycle after it: every access in that cycle reads bank 3. A swap that
|
|
29
|
+
# the DDR makes, releasing a line to float high, shows nothing.
|
|
30
|
+
# Called once a cycle, ahead of the cycle's accesses.
|
|
31
|
+
def sample_lines
|
|
32
|
+
cia2 = @address_bus.cia2
|
|
33
|
+
lines = cia2.port_a_lines & 0b11
|
|
34
|
+
@swapped = swap?(@lines, lines) && cia2.port_registers[2].allbits?(0b11)
|
|
35
|
+
@lines = lines
|
|
19
36
|
end
|
|
20
37
|
|
|
21
38
|
def peek(offset)
|
|
@@ -66,7 +83,11 @@ module Badline
|
|
|
66
83
|
end
|
|
67
84
|
end
|
|
68
85
|
|
|
86
|
+
def swap?(from, to) = (from ^ to) == 0b11 && from.anybits?(0b11) && to.anybits?(0b11)
|
|
87
|
+
|
|
69
88
|
def bank_switch_register
|
|
89
|
+
return 0 if @swapped
|
|
90
|
+
|
|
70
91
|
@address_bus.cia2.port_a_lines & 0b11
|
|
71
92
|
end
|
|
72
93
|
end
|
|
@@ -27,6 +27,16 @@ module Badline
|
|
|
27
27
|
@groups.fill(FULL)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def save_state(out)
|
|
31
|
+
out.ints(@groups).booleans(@mask).blob(@snapshot)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def load_state(input)
|
|
35
|
+
input.ints_into(@groups)
|
|
36
|
+
input.booleans_into(@mask)
|
|
37
|
+
input.blob_into(@snapshot)
|
|
38
|
+
end
|
|
39
|
+
|
|
30
40
|
def at?(pixel_x)
|
|
31
41
|
case @groups[pixel_x >> 3]
|
|
32
42
|
when FULL then true
|
|
@@ -36,6 +36,17 @@ module Badline
|
|
|
36
36
|
@cleared_data = 0
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def save_state(out)
|
|
40
|
+
out.blob(@hits).int(@folded).int(@cleared_sprite).int(@cleared_data)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def load_state(input)
|
|
44
|
+
input.blob_into(@hits)
|
|
45
|
+
@folded = input.int
|
|
46
|
+
@cleared_sprite = input.int
|
|
47
|
+
@cleared_data = input.int
|
|
48
|
+
end
|
|
49
|
+
|
|
39
50
|
def start_line
|
|
40
51
|
@folded = 0
|
|
41
52
|
@cleared_sprite = carry(@cleared_sprite)
|
|
@@ -17,6 +17,17 @@ module Badline
|
|
|
17
17
|
|
|
18
18
|
def clear = @patches.clear
|
|
19
19
|
|
|
20
|
+
# Each patch as its four numbers: the boundary pixel, the register,
|
|
21
|
+
# the old colour and the new.
|
|
22
|
+
def save_state(out)
|
|
23
|
+
out.int(@patches.length)
|
|
24
|
+
@patches.each { |patch| out.ints(patch) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load_state(input)
|
|
28
|
+
@patches.replace(Array.new(input.int) { input.ints })
|
|
29
|
+
end
|
|
30
|
+
|
|
20
31
|
def log(reg, old, value, boundary_x)
|
|
21
32
|
@patches << [boundary_x, reg, @grey_dots ? GREY_DOT : old & 0x0f, value & 0x0f]
|
|
22
33
|
end
|
|
@@ -30,7 +30,7 @@ module Badline
|
|
|
30
30
|
|
|
31
31
|
GRAPHICS_COLUMNS = Array.new(65) { |column| column.between?(GRAPHICS_FIRST, GRAPHICS_LAST) }.freeze
|
|
32
32
|
|
|
33
|
-
attr_reader :vc_base, :vc, :vmli, :rc
|
|
33
|
+
attr_reader :vc_base, :vc, :vmli, :rc, :bad_lines_enabled
|
|
34
34
|
|
|
35
35
|
# The wrap column is the last column of a rasterline, 62 on the 6569,
|
|
36
36
|
# which already compares against the line about to start, the same
|
|
@@ -50,6 +50,15 @@ module Badline
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def display? = @display
|
|
53
|
+
|
|
54
|
+
# Sets VCBASE, VC, RC and VMLI, in that order, and the display and
|
|
55
|
+
# bad-line flags, as a VICE snapshot gives them.
|
|
56
|
+
def restore_counters(counters, display, bad_lines_enabled)
|
|
57
|
+
@vc_base, @vc, @rc, @vmli = counters
|
|
58
|
+
@display = display
|
|
59
|
+
@bad_lines_enabled = bad_lines_enabled
|
|
60
|
+
end
|
|
61
|
+
|
|
53
62
|
def idle? = !@display
|
|
54
63
|
def bad_line? = !@ba.nil?
|
|
55
64
|
|
|
@@ -63,6 +72,25 @@ module Badline
|
|
|
63
72
|
@matched && column >= FETCH_FIRST && column <= DMA_LAST
|
|
64
73
|
end
|
|
65
74
|
|
|
75
|
+
def save_state(out)
|
|
76
|
+
out.int(@vc_base).int(@vc).int(@vmli).int(@rc).boolean(@display).boolean(@bad_lines_enabled)
|
|
77
|
+
out.boolean(@matched).optional_int(@ba).int(@line_bits).boolean(@den_line).boolean(@in_window)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def load_state(input)
|
|
81
|
+
@vc_base = input.int
|
|
82
|
+
@vc = input.int
|
|
83
|
+
@vmli = input.int
|
|
84
|
+
@rc = input.int
|
|
85
|
+
@display = input.boolean?
|
|
86
|
+
@bad_lines_enabled = input.boolean?
|
|
87
|
+
@matched = input.boolean?
|
|
88
|
+
@ba = input.optional_int
|
|
89
|
+
@line_bits = input.int
|
|
90
|
+
@den_line = input.boolean?
|
|
91
|
+
@in_window = input.boolean?
|
|
92
|
+
end
|
|
93
|
+
|
|
66
94
|
def new_frame
|
|
67
95
|
@vc_base = 0
|
|
68
96
|
@bad_lines_enabled = false
|
|
@@ -50,6 +50,24 @@ module Badline
|
|
|
50
50
|
@late_read = false
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def save_state(out)
|
|
54
|
+
out.int(@next_ecm_bmm).int(@black_below).ints(@colors).booleans(@fg)
|
|
55
|
+
out.int(@gbuf).int(@vbuf).int(@cbuf).int(@flop).int(@pixel)
|
|
56
|
+
out.int(@mode_lookup).int(@mode_read).boolean(@late_mcm).boolean(@late_read)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def load_state(input)
|
|
60
|
+
@next_ecm_bmm = input.int
|
|
61
|
+
@black_below = input.int
|
|
62
|
+
input.ints_into(@colors)
|
|
63
|
+
input.booleans_into(@fg)
|
|
64
|
+
load_buffers(input)
|
|
65
|
+
@mode_lookup = input.int
|
|
66
|
+
@mode_read = input.int
|
|
67
|
+
@late_mcm = input.boolean?
|
|
68
|
+
@late_read = input.boolean?
|
|
69
|
+
end
|
|
70
|
+
|
|
53
71
|
# Rebuilds the state at the end of a group that painted whole bytes:
|
|
54
72
|
# the byte loaded at pixel shift under an unchanging mode.
|
|
55
73
|
def prime(data, screencode, color, shift, mode)
|
|
@@ -78,6 +96,14 @@ module Badline
|
|
|
78
96
|
|
|
79
97
|
private
|
|
80
98
|
|
|
99
|
+
def load_buffers(input)
|
|
100
|
+
@gbuf = input.int
|
|
101
|
+
@vbuf = input.int
|
|
102
|
+
@cbuf = input.int
|
|
103
|
+
@flop = input.int
|
|
104
|
+
@pixel = input.int
|
|
105
|
+
end
|
|
106
|
+
|
|
81
107
|
def step_mode(pixel, mode)
|
|
82
108
|
case pixel
|
|
83
109
|
when 0 then start_group
|
|
@@ -19,6 +19,24 @@ module Badline
|
|
|
19
19
|
# The pixel at which the next logged write takes effect.
|
|
20
20
|
attr_reader :next_x
|
|
21
21
|
|
|
22
|
+
# The entries as pixel, register and value in turn, the seed with -1
|
|
23
|
+
# for a register the line hasn't logged, and a next pixel past the
|
|
24
|
+
# last entry as -1.
|
|
25
|
+
def save_state(out)
|
|
26
|
+
out.ints(@entries.flatten)
|
|
27
|
+
out.ints(@seed.map { |value| value.nil? ? -1 : value })
|
|
28
|
+
out.ints(@values).int(@cursor).int(@next_x.infinite? ? -1 : @next_x)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def load_state(input)
|
|
32
|
+
@entries.replace(input.ints.each_slice(3).to_a)
|
|
33
|
+
@seed.replace(input.ints.map { |value| value.negative? ? nil : value })
|
|
34
|
+
input.ints_into(@values)
|
|
35
|
+
@cursor = input.int
|
|
36
|
+
next_x = input.int
|
|
37
|
+
@next_x = next_x.negative? ? Float::INFINITY : next_x
|
|
38
|
+
end
|
|
39
|
+
|
|
22
40
|
def empty? = @entries.empty?
|
|
23
41
|
def length = @entries.length
|
|
24
42
|
|
|
@@ -143,6 +143,15 @@ module Badline
|
|
|
143
143
|
|
|
144
144
|
def latch_raster_irq! = latch_irq!(0x01)
|
|
145
145
|
|
|
146
|
+
def save_state(out)
|
|
147
|
+
out.blob(@bytes).boolean(@irq_line)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def load_state(input)
|
|
151
|
+
input.blob_into(@bytes)
|
|
152
|
+
@irq_line = input.boolean?
|
|
153
|
+
end
|
|
154
|
+
|
|
146
155
|
def collide!(reg, bits)
|
|
147
156
|
return false if bits.zero?
|
|
148
157
|
|
|
@@ -151,12 +160,14 @@ module Badline
|
|
|
151
160
|
was_zero
|
|
152
161
|
end
|
|
153
162
|
|
|
154
|
-
|
|
155
|
-
|
|
163
|
+
# Works the IRQ line out from $D019 and $D01A again, after the bytes
|
|
164
|
+
# were set directly.
|
|
156
165
|
def update_irq_line
|
|
157
166
|
@irq_line = (@bytes[0x19] & @bytes[0x1a]).anybits?(0x0f)
|
|
158
167
|
end
|
|
159
168
|
|
|
169
|
+
private
|
|
170
|
+
|
|
160
171
|
def read_clear(reg)
|
|
161
172
|
@bytes[reg].tap { @bytes[reg] = 0 }
|
|
162
173
|
end
|
|
@@ -5,6 +5,7 @@ require "badline/vic/color_patches"
|
|
|
5
5
|
require "badline/vic/graphics_mode"
|
|
6
6
|
require "badline/vic/graphics_shifter"
|
|
7
7
|
require "badline/vic/sequencer_output"
|
|
8
|
+
require "badline/vic/sequencer_state"
|
|
8
9
|
|
|
9
10
|
module Badline
|
|
10
11
|
class VIC < Cycleable
|
|
@@ -13,6 +14,7 @@ module Badline
|
|
|
13
14
|
# Turns fetched graphics data into output pixels.
|
|
14
15
|
class Sequencer
|
|
15
16
|
include Output
|
|
17
|
+
include SavedState
|
|
16
18
|
|
|
17
19
|
BORDER_Y_BOUNDS = [
|
|
18
20
|
[55, 247].freeze,
|
|
@@ -149,6 +151,16 @@ module Badline
|
|
|
149
151
|
# slot before is the byte the previous group loaded.
|
|
150
152
|
attr_reader :ring_data, :ring_char, :ring_color
|
|
151
153
|
|
|
154
|
+
# The border flip-flops: vertical, the vertical one armed to set, and
|
|
155
|
+
# the main one.
|
|
156
|
+
def borders = [@vertical_border, @vertical_armed, @main_border]
|
|
157
|
+
|
|
158
|
+
def restore_borders(vertical, armed, main)
|
|
159
|
+
@vertical_border = vertical
|
|
160
|
+
@vertical_armed = armed
|
|
161
|
+
@main_border = main
|
|
162
|
+
end
|
|
163
|
+
|
|
152
164
|
# Paints the byte in the slot through the current mode. An idle access
|
|
153
165
|
# latches 0/0 and one that made no access passes on zero data with the
|
|
154
166
|
# kept values; under a closed border neither has foreground. A group
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class VIC < Cycleable
|
|
5
|
+
class Sequencer
|
|
6
|
+
# The sequencer's save_state and load_state.
|
|
7
|
+
module SavedState
|
|
8
|
+
FG_SHIFTER = -1
|
|
9
|
+
PAIR_CODES = 256
|
|
10
|
+
|
|
11
|
+
# The line buffers, the border flip-flops, the g-access ring and the
|
|
12
|
+
# pixel pipeline. The foreground masks the groups point at go as
|
|
13
|
+
# FG_SHIFTER for the shift register's, or their data byte in
|
|
14
|
+
# GraphicsMode's HIRES_FG, or that plus 256 in its PAIR_FG. The left
|
|
15
|
+
# vertical border compare goes as -1 before it has run.
|
|
16
|
+
def save_state(out)
|
|
17
|
+
out.int(@line).boolean(@first_csel).int(@left_vertical_border.nil? ? -1 : bit(@left_vertical_border))
|
|
18
|
+
out.blob(@colors).booleans(@fg)
|
|
19
|
+
@border_mask.save_state(out)
|
|
20
|
+
out.ints(@cur_colors).int(fg_code(@cur_fg)).ints(@prev_colors).int(fg_code(@prev_fg))
|
|
21
|
+
out.boolean(@vertical_border).boolean(@vertical_armed).boolean(@main_border)
|
|
22
|
+
@color_patches.save_state(out)
|
|
23
|
+
@shifter.save_state(out)
|
|
24
|
+
out.int(@xscroll).int(@last_shift).int(@last_mode).boolean(@settling).boolean(@prev_fresh).boolean(@check)
|
|
25
|
+
out.ints(@ring_data).ints(@ring_char).ints(@ring_color)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def load_state(input)
|
|
29
|
+
load_line(input)
|
|
30
|
+
@border_mask.load_state(input)
|
|
31
|
+
input.ints_into(@cur_colors)
|
|
32
|
+
@cur_fg = fg_pattern(input.int)
|
|
33
|
+
input.ints_into(@prev_colors)
|
|
34
|
+
@prev_fg = fg_pattern(input.int)
|
|
35
|
+
@vertical_border = input.boolean?
|
|
36
|
+
@vertical_armed = input.boolean?
|
|
37
|
+
@main_border = input.boolean?
|
|
38
|
+
@color_patches.load_state(input)
|
|
39
|
+
@shifter.load_state(input)
|
|
40
|
+
load_pipeline(input)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def bit(flag) = flag ? 1 : 0
|
|
46
|
+
|
|
47
|
+
def load_line(input)
|
|
48
|
+
@line = input.int
|
|
49
|
+
@first_csel = input.boolean?
|
|
50
|
+
left = input.int
|
|
51
|
+
@left_vertical_border = left.negative? ? nil : left == 1
|
|
52
|
+
input.blob_into(@colors)
|
|
53
|
+
input.booleans_into(@fg)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def load_pipeline(input)
|
|
57
|
+
@xscroll = input.int
|
|
58
|
+
@last_shift = input.int
|
|
59
|
+
@last_mode = input.int
|
|
60
|
+
@settling = input.boolean?
|
|
61
|
+
@prev_fresh = input.boolean?
|
|
62
|
+
@check = input.boolean?
|
|
63
|
+
input.ints_into(@ring_data)
|
|
64
|
+
input.ints_into(@ring_char)
|
|
65
|
+
input.ints_into(@ring_color)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def fg_code(pattern)
|
|
69
|
+
return FG_SHIFTER if pattern.equal?(@shifter.fg)
|
|
70
|
+
|
|
71
|
+
hires = GraphicsMode::HIRES_FG.index { |fg| fg.equal?(pattern) }
|
|
72
|
+
return hires if hires
|
|
73
|
+
|
|
74
|
+
pair = GraphicsMode::PAIR_FG.index { |fg| fg.equal?(pattern) }
|
|
75
|
+
raise ArgumentError, "a foreground mask the sequencer doesn't know" unless pair
|
|
76
|
+
|
|
77
|
+
PAIR_CODES + pair
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def fg_pattern(code)
|
|
81
|
+
return @shifter.fg if code == FG_SHIFTER
|
|
82
|
+
return GraphicsMode::HIRES_FG.fetch(code) if code < PAIR_CODES
|
|
83
|
+
|
|
84
|
+
GraphicsMode::PAIR_FG.fetch(code - PAIR_CODES)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -25,6 +25,17 @@ module Badline
|
|
|
25
25
|
@line = Array.new(columns, -1)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def save_state(out)
|
|
29
|
+
out.int(@lines).int(@ghost).ints(@value).ints(@line)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def load_state(input)
|
|
33
|
+
@lines = input.int
|
|
34
|
+
@ghost = input.int
|
|
35
|
+
input.ints_into(@value)
|
|
36
|
+
input.ints_into(@line)
|
|
37
|
+
end
|
|
38
|
+
|
|
28
39
|
# Sprites 3-7 fetch in Bauer cycles 1-10, so the idle byte is taken
|
|
29
40
|
# as the line starts.
|
|
30
41
|
def start_line(sprites_enabled)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class VIC < Cycleable
|
|
5
|
+
class Sprite
|
|
6
|
+
# The sprite's save_state and load_state.
|
|
7
|
+
module SavedState
|
|
8
|
+
# The shift register, MC, MCBASE, the expansion flip-flop and whether
|
|
9
|
+
# the sprite shows, for a VICE snapshot.
|
|
10
|
+
attr_reader :sr, :mc, :mcbase, :exp_ff, :display_on
|
|
11
|
+
|
|
12
|
+
# Sets MC, MCBASE, the expansion flip-flop, whether the sprite's DMA
|
|
13
|
+
# runs and whether it shows, as a VICE snapshot gives them.
|
|
14
|
+
def restore_counters(counter, base, expanded, dma, shows)
|
|
15
|
+
@mc = counter
|
|
16
|
+
@mcbase = base
|
|
17
|
+
@exp_ff = expanded
|
|
18
|
+
@dma = dma
|
|
19
|
+
@display_on = shows
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def save_state(out)
|
|
23
|
+
out.boolean(@dma).boolean(@display_on).int(@mcbase).int(@mc).boolean(@exp_ff).optional_int(@crunched_mc)
|
|
24
|
+
out.int(@bits).boolean(@row_ready).optional_int(@prev_bits).boolean(@first_byte_lost).boolean(@blind)
|
|
25
|
+
out.int(@show_from).optional_int(@stop_x)
|
|
26
|
+
out.ints(@codes).int(@leftmost).int(@span).ints(@reload_codes).int(@reload_leftmost).int(@reload_span)
|
|
27
|
+
out.int(@sr).int(@latch).boolean(@mc_flop).boolean(@xe_flop)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def load_state(input)
|
|
31
|
+
load_counters(input)
|
|
32
|
+
@show_from = input.int
|
|
33
|
+
@stop_x = input.optional_int
|
|
34
|
+
input.ints_into(@codes)
|
|
35
|
+
@leftmost = input.int
|
|
36
|
+
@span = input.int
|
|
37
|
+
input.ints_into(@reload_codes)
|
|
38
|
+
@reload_leftmost = input.int
|
|
39
|
+
@reload_span = input.int
|
|
40
|
+
@sr = input.int
|
|
41
|
+
@latch = input.int
|
|
42
|
+
@mc_flop = input.boolean?
|
|
43
|
+
@xe_flop = input.boolean?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def load_counters(input)
|
|
49
|
+
@dma = input.boolean?
|
|
50
|
+
@display_on = input.boolean?
|
|
51
|
+
@mcbase = input.int
|
|
52
|
+
@mc = input.int
|
|
53
|
+
@exp_ff = input.boolean?
|
|
54
|
+
@crunched_mc = input.optional_int
|
|
55
|
+
@bits = input.int
|
|
56
|
+
@row_ready = input.boolean?
|
|
57
|
+
@prev_bits = input.optional_int
|
|
58
|
+
@first_byte_lost = input.boolean?
|
|
59
|
+
@blind = input.boolean?
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/badline/vic/sprite.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "badline/vic/sprite/internal_bus"
|
|
4
4
|
require "badline/vic/sprite/shifter"
|
|
5
|
+
require "badline/vic/sprite/saved_state"
|
|
5
6
|
require "badline/vic/sprite/timing"
|
|
6
7
|
|
|
7
8
|
module Badline
|
|
@@ -51,6 +52,7 @@ module Badline
|
|
|
51
52
|
DISPLAY_OFF_X = 460
|
|
52
53
|
|
|
53
54
|
include Shifter
|
|
55
|
+
include SavedState
|
|
54
56
|
|
|
55
57
|
attr_reader :index, :leftmost, :span, :codes,
|
|
56
58
|
:reload_leftmost, :reload_span, :reload_codes
|
data/lib/badline/vic/sprites.rb
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
require "badline/vic/collisions"
|
|
4
4
|
require "badline/vic/register_log"
|
|
5
5
|
require "badline/vic/sprite"
|
|
6
|
+
require "badline/vic/sprites_state"
|
|
6
7
|
|
|
7
8
|
module Badline
|
|
8
9
|
class VIC < Cycleable
|
|
9
10
|
class Sprites
|
|
11
|
+
include SavedState
|
|
12
|
+
|
|
10
13
|
# Pixels between the start of the cycle following a write and the
|
|
11
14
|
# point the new value shows. Each signal reaches the output on its own
|
|
12
15
|
# path: the colors feed the final mux, the sequencer inputs — X
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class VIC < Cycleable
|
|
5
|
+
class Sprites
|
|
6
|
+
# The sprites' save_state and load_state.
|
|
7
|
+
module SavedState
|
|
8
|
+
# Whether any sprite's DMA runs, set after Sprite#restore_counters.
|
|
9
|
+
def update_any_dma
|
|
10
|
+
@any_dma = @sprites.any?(&:displaying?)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# The sprites, their shared bus, the collisions and the register log,
|
|
14
|
+
# then the line's coverage and the pixels carried into the next line,
|
|
15
|
+
# each as x, colour and priority.
|
|
16
|
+
def save_state(out)
|
|
17
|
+
out.boolean(@any_dma).boolean(@stopped_dma).boolean(@carried).int(@lo).int(@hi).int(@sequenced)
|
|
18
|
+
out.blob(@win_color).booleans(@win_priority).ints(@palette)
|
|
19
|
+
[@carry, @carry_next].each { |carry| carry.each { |pixels| save_carry(pixels, out) } }
|
|
20
|
+
@sprites.each { |sprite| sprite.save_state(out) }
|
|
21
|
+
@bus.save_state(out)
|
|
22
|
+
@collisions.save_state(out)
|
|
23
|
+
@log.save_state(out)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load_state(input)
|
|
27
|
+
@any_dma = input.boolean?
|
|
28
|
+
@stopped_dma = input.boolean?
|
|
29
|
+
@carried = input.boolean?
|
|
30
|
+
@lo = input.int
|
|
31
|
+
@hi = input.int
|
|
32
|
+
@sequenced = input.int
|
|
33
|
+
input.blob_into(@win_color)
|
|
34
|
+
input.booleans_into(@win_priority)
|
|
35
|
+
input.ints_into(@palette)
|
|
36
|
+
[@carry, @carry_next].each { |carry| carry.each { |pixels| load_carry(pixels, input) } }
|
|
37
|
+
@sprites.each { |sprite| sprite.load_state(input) }
|
|
38
|
+
@bus.load_state(input)
|
|
39
|
+
@collisions.load_state(input)
|
|
40
|
+
@log.load_state(input)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def save_carry(pixels, out)
|
|
46
|
+
out.int(pixels.length / 3)
|
|
47
|
+
pixels.each_slice(3) { |x, color, priority| out.int(x).int(color).boolean(priority) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def load_carry(pixels, input)
|
|
51
|
+
pixels.clear
|
|
52
|
+
input.int.times { pixels.push(input.int, input.int, input.boolean?) }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|