badline 0.5.0 → 0.6.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 +24 -0
- data/CLAUDE.md +23 -23
- data/CONTRIBUTING.md +5 -0
- data/README.md +59 -1
- data/doc/pinned-behaviour.md +4 -2
- data/exe/badline-ruby +2 -1
- data/lib/badline/address_bus/saved_state.rb +72 -0
- data/lib/badline/address_bus.rb +1 -0
- data/lib/badline/audio/stream.rb +6 -0
- 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 +24 -2
- data/lib/badline/computer/attachments.rb +132 -0
- data/lib/badline/computer/saved_state.rb +116 -0
- data/lib/badline/computer.rb +8 -31
- data/lib/badline/cpu/microcode.rb +8 -0
- data/lib/badline/cpu/saved_state.rb +115 -0
- data/lib/badline/cpu.rb +2 -0
- data/lib/badline/datasette.rb +27 -0
- data/lib/badline/drive1541/bus.rb +89 -13
- data/lib/badline/drive1541/disk.rb +7 -2
- data/lib/badline/drive1541/disk_state.rb +80 -0
- data/lib/badline/drive1541/idle.rb +9 -57
- data/lib/badline/drive1541/mechanism.rb +2 -0
- data/lib/badline/drive1541/mechanism_state.rb +67 -0
- data/lib/badline/drive1541/orbit.rb +81 -0
- data/lib/badline/drive1541/rotation.rb +10 -0
- data/lib/badline/drive1541/serial_port.rb +10 -0
- data/lib/badline/drive1541/sleep.rb +114 -0
- data/lib/badline/drive1541.rb +52 -2
- data/lib/badline/gui/application.rb +41 -14
- data/lib/badline/gui/gamepads.rb +7 -0
- data/lib/badline/gui/snapshots.rb +67 -0
- data/lib/badline/gui.rb +1 -0
- data/lib/badline/iec_bus.rb +16 -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 +11 -0
- data/lib/badline/options.rb +3 -2
- 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.rb +36 -0
- data/lib/badline/reu/ram.rb +15 -0
- data/lib/badline/reu/saved_state.rb +52 -0
- data/lib/badline/reu.rb +4 -0
- data/lib/badline/sdl/events.rb +2 -0
- 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/state.rb +66 -0
- data/lib/badline/sid/waveform.rb +2 -0
- 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/fast_forward.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 +19 -0
- data/lib/badline/via/timer2.rb +24 -0
- data/lib/badline/via.rb +2 -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 +94 -10
- data/lib/badline.rb +1 -0
- metadata +37 -1
|
@@ -55,6 +55,21 @@ module Badline
|
|
|
55
55
|
|
|
56
56
|
def active_c1_edge?(high) = high == @control.anybits?(0x01)
|
|
57
57
|
|
|
58
|
+
# The lines' levels and mode. Which flags they set, and whether reads
|
|
59
|
+
# handshake, are the port's wiring.
|
|
60
|
+
def save_state(out)
|
|
61
|
+
out.int(@control).boolean(@c1_high).boolean(@c2_high).boolean(@handshake_low).int(@pulse).boolean(@c2_output)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def load_state(input)
|
|
65
|
+
@control = input.int
|
|
66
|
+
@c1_high = input.boolean?
|
|
67
|
+
@c2_high = input.boolean?
|
|
68
|
+
@handshake_low = input.boolean?
|
|
69
|
+
@pulse = input.int
|
|
70
|
+
@c2_output = input.boolean?
|
|
71
|
+
end
|
|
72
|
+
|
|
58
73
|
# Everything the lines hold, for comparing them at two points.
|
|
59
74
|
def state = [@control, @c1_high, @c2_high, @handshake_low, @pulse, @c2_output]
|
|
60
75
|
|
|
@@ -26,6 +26,21 @@ module Badline
|
|
|
26
26
|
@t2.fast_forward(cycles)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# The counters an orbit (see Drive1541::Idle) has to bring back: those
|
|
30
|
+
# of the armed timers, and of the timers +touched+ names (bit 0 for
|
|
31
|
+
# timer 1, bit 1 for timer 2). The others only count.
|
|
32
|
+
def counter_state(touched)
|
|
33
|
+
[(@t1.count_state if @t1.armed? || touched.anybits?(1)),
|
|
34
|
+
(@t2.count_state if @t2.armed? || touched.anybits?(2))]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Runs +cycles+ cycles of whole orbits at once: the counters
|
|
38
|
+
# counter_state leaves out move on, and the rest come back round.
|
|
39
|
+
def skip_orbits(cycles, touched)
|
|
40
|
+
@t1.fast_forward(cycles) unless @t1.armed? || touched.anybits?(1)
|
|
41
|
+
@t2.fast_forward(cycles) unless @t2.armed? || touched.anybits?(2)
|
|
42
|
+
end
|
|
43
|
+
|
|
29
44
|
# Everything the VIA holds but its counters, for comparing it at two
|
|
30
45
|
# points. Timer 1's PB7 level counts only while ACR bit 7 puts it on
|
|
31
46
|
# the pin: nothing reads it otherwise.
|
|
@@ -67,6 +67,19 @@ module Badline
|
|
|
67
67
|
# changes it from cycle to cycle.
|
|
68
68
|
def clocking? = @bits.nonzero? && internal_clock?
|
|
69
69
|
|
|
70
|
+
def save_state(out)
|
|
71
|
+
out.int(@mode).int(@data).int(@bits).boolean(@clock).boolean(@cb2).boolean(@cb2_input)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def load_state(input)
|
|
75
|
+
@mode = input.int
|
|
76
|
+
@data = input.int
|
|
77
|
+
@bits = input.int
|
|
78
|
+
@clock = input.boolean?
|
|
79
|
+
@cb2 = input.boolean?
|
|
80
|
+
@cb2_input = input.boolean?
|
|
81
|
+
end
|
|
82
|
+
|
|
70
83
|
# Everything the register holds, for comparing it at two points.
|
|
71
84
|
def state = [@mode, @data, @bits, @clock, @cb2, @cb2_input]
|
|
72
85
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class VIA
|
|
5
|
+
# Saving and restoring a VIA for a snapshot.
|
|
6
|
+
module State
|
|
7
|
+
# The registers, the timers, the shift register, the interrupt flags
|
|
8
|
+
# and the control lines. The peripheral is the drive's wiring.
|
|
9
|
+
def save_state(out)
|
|
10
|
+
out.int(@ora).int(@orb).int(@ddra).int(@ddrb).int(@latch_a).int(@latch_b).int(@acr).int(@pcr)
|
|
11
|
+
out.boolean(@pb6_high).boolean(@sr_uses_t2)
|
|
12
|
+
@t1.save_state(out)
|
|
13
|
+
@t2.save_state(out)
|
|
14
|
+
@shift_register.save_state(out)
|
|
15
|
+
@ifr.save_state(out)
|
|
16
|
+
@ca.save_state(out)
|
|
17
|
+
@cb.save_state(out)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Puts the state back without telling the peripheral, which restores
|
|
21
|
+
# its own.
|
|
22
|
+
def load_state(input)
|
|
23
|
+
@ora = input.int
|
|
24
|
+
@orb = input.int
|
|
25
|
+
@ddra = input.int
|
|
26
|
+
@ddrb = input.int
|
|
27
|
+
@latch_a = input.int
|
|
28
|
+
@latch_b = input.int
|
|
29
|
+
@acr = input.int
|
|
30
|
+
@pcr = input.int
|
|
31
|
+
@pb6_high = input.boolean?
|
|
32
|
+
@sr_uses_t2 = input.boolean?
|
|
33
|
+
@t1.load_state(input)
|
|
34
|
+
@t2.load_state(input)
|
|
35
|
+
@shift_register.load_state(input)
|
|
36
|
+
@ifr.load_state(input)
|
|
37
|
+
@ca.load_state(input)
|
|
38
|
+
@cb.load_state(input)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/badline/via/timer1.rb
CHANGED
|
@@ -92,10 +92,29 @@ 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]
|
|
98
111
|
|
|
112
|
+
# Whether a timeout sets the flag.
|
|
113
|
+
def armed? = @armed
|
|
114
|
+
|
|
115
|
+
# The count, for comparing the timer at two points where it matters.
|
|
116
|
+
def count_state = [@counter, @hold, @reload]
|
|
117
|
+
|
|
99
118
|
private
|
|
100
119
|
|
|
101
120
|
# The counter reloads either way. Armed, PB7 inverts and the flag
|
data/lib/badline/via/timer2.rb
CHANGED
|
@@ -84,10 +84,34 @@ 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]
|
|
90
108
|
|
|
109
|
+
# Whether the next underflow sets the flag.
|
|
110
|
+
def armed? = @armed
|
|
111
|
+
|
|
112
|
+
# The count, for comparing the timer at two points where it matters.
|
|
113
|
+
def count_state = [@counter, @low_underflowed]
|
|
114
|
+
|
|
91
115
|
private
|
|
92
116
|
|
|
93
117
|
# A cycle while counting pulses, or held after a load or a low byte
|
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
|
|
|
@@ -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
|