badline 0.1.0 → 0.2.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 +167 -0
- data/CLAUDE.md +217 -0
- data/CODE_OF_CONDUCT.md +92 -0
- data/CONTRIBUTING.md +51 -0
- data/README.md +224 -31
- data/doc/pinned-behaviour.md +1117 -0
- data/exe/badline +22 -2
- data/exe/badline-sid +34 -0
- data/lib/badline/address_bus.rb +88 -23
- data/lib/badline/audio/aiff.rb +36 -0
- data/lib/badline/audio/bare_player.rb +129 -0
- data/lib/badline/audio/cli.rb +133 -0
- data/lib/badline/audio/console.rb +75 -0
- data/lib/badline/audio/jukebox.rb +82 -0
- data/lib/badline/audio/machine_player.rb +57 -0
- data/lib/badline/audio/options.rb +137 -0
- data/lib/badline/audio/pcm_writer.rb +57 -0
- data/lib/badline/audio/playback.rb +108 -0
- data/lib/badline/audio/renderer.rb +85 -0
- data/lib/badline/audio/sdl_sink.rb +102 -0
- data/lib/badline/audio/wav.rb +23 -0
- data/lib/badline/audio.rb +14 -0
- data/lib/badline/cartridge/action_replay.rb +111 -0
- data/lib/badline/cartridge/atomic_power.rb +24 -0
- data/lib/badline/cartridge/bank.rb +81 -0
- data/lib/badline/cartridge/comal80.rb +48 -0
- data/lib/badline/cartridge/dinamic.rb +34 -0
- data/lib/badline/cartridge/easy_flash.rb +122 -0
- data/lib/badline/cartridge/epyx_fastload.rb +71 -0
- data/lib/badline/cartridge/final_cartridge3.rb +77 -0
- data/lib/badline/cartridge/flash.rb +265 -0
- data/lib/badline/cartridge/freezer.rb +31 -0
- data/lib/badline/cartridge/fun_play.rb +38 -0
- data/lib/badline/cartridge/g_mod2.rb +62 -0
- data/lib/badline/cartridge/game_system.rb +40 -0
- data/lib/badline/cartridge/kcs_power.rb +72 -0
- data/lib/badline/cartridge/mach5.rb +34 -0
- data/lib/badline/cartridge/magic_desk.rb +6 -3
- data/lib/badline/cartridge/ocean.rb +11 -12
- data/lib/badline/cartridge/pagefox.rb +56 -0
- data/lib/badline/cartridge/retro_replay.rb +276 -0
- data/lib/badline/cartridge/rex_utility.rb +33 -0
- data/lib/badline/cartridge/rgcd.rb +48 -0
- data/lib/badline/cartridge/simons_basic.rb +40 -0
- data/lib/badline/cartridge/standard.rb +3 -19
- data/lib/badline/cartridge/super_games.rb +36 -0
- data/lib/badline/cartridge/westermann.rb +34 -0
- data/lib/badline/cartridge/zaxxon.rb +50 -0
- data/lib/badline/cartridge.rb +131 -15
- data/lib/badline/cia/interrupt_register.rb +88 -0
- data/lib/badline/cia/serial.rb +219 -0
- data/lib/badline/cia/timer.rb +43 -31
- data/lib/badline/cia.rb +109 -74
- data/lib/badline/color_memory.rb +14 -5
- data/lib/badline/computer.rb +68 -8
- data/lib/badline/control_ports.rb +54 -9
- data/lib/badline/cpu/addressing.rb +131 -0
- data/lib/badline/cpu/microcode.rb +171 -0
- data/lib/badline/cpu/operations.rb +88 -0
- data/lib/badline/cpu/stack_operations.rb +82 -0
- data/lib/badline/cpu.rb +108 -167
- data/lib/badline/cycleable.rb +3 -0
- data/lib/badline/datasette.rb +81 -0
- data/lib/badline/debug_register.rb +28 -0
- data/lib/badline/gui/application.rb +79 -18
- data/lib/badline/gui/gamepads.rb +97 -0
- data/lib/badline/gui/joy_map.rb +15 -7
- data/lib/badline/gui.rb +1 -0
- data/lib/badline/input/mouse1351.rb +41 -0
- data/lib/badline/input/paddles.rb +36 -0
- data/lib/badline/input.rb +4 -0
- data/lib/badline/instruction_set/arithmetic.rb +16 -21
- data/lib/badline/instruction_set/bitwise.rb +17 -22
- data/lib/badline/instruction_set/branch.rb +20 -19
- data/lib/badline/instruction_set/flag.rb +7 -7
- data/lib/badline/instruction_set/illegal.rb +14 -22
- data/lib/badline/instruction_set/inc_dec.rb +8 -10
- data/lib/badline/instruction_set/stack.rb +5 -57
- data/lib/badline/instruction_set/transfer.rb +9 -9
- data/lib/badline/instruction_set.rb +9 -31
- data/lib/badline/interrupts.rb +104 -0
- data/lib/badline/joystick.rb +9 -9
- data/lib/badline/kernal_trap/channel.rb +33 -0
- data/lib/badline/kernal_trap/drive.rb +239 -0
- data/lib/badline/kernal_trap/file.rb +4 -24
- data/lib/badline/kernal_trap/load.rb +2 -2
- data/lib/badline/kernal_trap/routine.rb +34 -0
- data/lib/badline/kernal_trap/save.rb +1 -1
- data/lib/badline/kernal_trap/serial.rb +139 -0
- data/lib/badline/kernal_trap.rb +4 -0
- data/lib/badline/keyboard.rb +40 -16
- data/lib/badline/media.rb +48 -10
- data/lib/badline/roms/eapi/LICENSE.md +15 -0
- data/lib/badline/roms/eapi/README +41 -0
- data/lib/badline/roms/eapi/eapi-am29f040-14 +0 -0
- data/lib/badline/sid/decimator.rb +46 -0
- data/lib/badline/sid/envelope.rb +232 -0
- data/lib/badline/sid/filter.rb +213 -0
- data/lib/badline/sid/voice.rb +61 -0
- data/lib/badline/sid/waveform/combined.rb +126 -0
- data/lib/badline/sid/waveform/fast_forward.rb +105 -0
- data/lib/badline/sid/waveform/noise_writeback.rb +82 -0
- data/lib/badline/sid/waveform.rb +289 -0
- data/lib/badline/sid.rb +274 -7
- data/lib/badline/status.rb +32 -27
- data/lib/badline/storage/crt_file.rb +18 -2
- data/lib/badline/storage/disk_image.rb +52 -5
- data/lib/badline/storage/host_directory.rb +18 -4
- data/lib/badline/storage/sid_file.rb +274 -0
- data/lib/badline/storage/song_lengths.rb +65 -0
- data/lib/badline/storage/t64.rb +67 -0
- data/lib/badline/storage/tap.rb +68 -0
- data/lib/badline/storage.rb +19 -0
- data/lib/badline/time_of_day.rb +105 -52
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic/bank.rb +14 -6
- data/lib/badline/vic/border_mask.rb +76 -0
- data/lib/badline/vic/collisions.rb +104 -0
- data/lib/badline/vic/color_patches.rb +33 -0
- data/lib/badline/vic/display_state.rb +92 -29
- data/lib/badline/vic/graphics_mode.rb +78 -53
- data/lib/badline/vic/graphics_shifter.rb +140 -0
- data/lib/badline/vic/register_log.rb +63 -0
- data/lib/badline/vic/registers.rb +3 -0
- data/lib/badline/vic/sequencer.rb +163 -137
- data/lib/badline/vic/sequencer_output.rb +116 -0
- data/lib/badline/vic/sprite/internal_bus.rb +49 -0
- data/lib/badline/vic/sprite/shifter.rb +129 -0
- data/lib/badline/vic/sprite.rb +275 -65
- data/lib/badline/vic/sprites.rb +220 -58
- data/lib/badline/vic.rb +373 -49
- data/lib/badline.rb +5 -0
- data/vendor/.gitkeep +0 -0
- metadata +103 -4
|
@@ -15,6 +15,11 @@ module Badline
|
|
|
15
15
|
Array.new(8) { |i| (data >> (6 - (i & ~1))).allbits?(0b10) }.freeze
|
|
16
16
|
end.freeze
|
|
17
17
|
|
|
18
|
+
# The same patterns as masks, all bits set for a foreground pixel.
|
|
19
|
+
HIRES_MASK = Array.new(256) do |data|
|
|
20
|
+
Array.new(8) { |i| data.anybits?(1 << (7 - i)) ? -1 : 0 }.freeze
|
|
21
|
+
end.freeze
|
|
22
|
+
|
|
18
23
|
NO_FG = HIRES_FG[0]
|
|
19
24
|
|
|
20
25
|
module Hires
|
|
@@ -23,117 +28,137 @@ module Badline
|
|
|
23
28
|
colors = seq.cur_colors
|
|
24
29
|
return colors.fill(background) if data.zero?
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
mask = HIRES_MASK[data]
|
|
32
|
+
flip = color ^ background
|
|
33
|
+
colors[0] = background ^ (flip & mask[0])
|
|
34
|
+
colors[1] = background ^ (flip & mask[1])
|
|
35
|
+
colors[2] = background ^ (flip & mask[2])
|
|
36
|
+
colors[3] = background ^ (flip & mask[3])
|
|
37
|
+
colors[4] = background ^ (flip & mask[4])
|
|
38
|
+
colors[5] = background ^ (flip & mask[5])
|
|
39
|
+
colors[6] = background ^ (flip & mask[6])
|
|
40
|
+
colors[7] = background ^ (flip & mask[7])
|
|
31
41
|
end
|
|
32
42
|
end
|
|
33
43
|
|
|
34
|
-
# Decodes 2-bit pixel pairs into double-wide pixels
|
|
35
|
-
#
|
|
44
|
+
# Decodes 2-bit pixel pairs into double-wide pixels, each pair indexing
|
|
45
|
+
# the four colours in the mode's palette.
|
|
36
46
|
module Multicolor
|
|
37
47
|
def paint_pairs(data, seq)
|
|
38
48
|
seq.cur_fg = PAIR_FG[data]
|
|
39
49
|
colors = seq.cur_colors
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
palette = @palette
|
|
51
|
+
colors[0] = colors[1] = palette[data >> 6]
|
|
52
|
+
colors[2] = colors[3] = palette[(data >> 4) & 0b11]
|
|
53
|
+
colors[4] = colors[5] = palette[(data >> 2) & 0b11]
|
|
54
|
+
colors[6] = colors[7] = palette[data & 0b11]
|
|
45
55
|
end
|
|
46
56
|
end
|
|
47
57
|
|
|
58
|
+
# Each mode paints the byte a g-access read with the screen byte and
|
|
59
|
+
# colour nibble it latched. The VIC reads the byte itself, in the
|
|
60
|
+
# g-access column. #fg gives the foreground mask #paint would leave,
|
|
61
|
+
# without the colours.
|
|
48
62
|
class Text
|
|
49
63
|
include Hires
|
|
50
64
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
data = seq.bank.peek(registers.char_base + (screencode * 8) + row)
|
|
54
|
-
paint_hires(data, color, registers.background, seq)
|
|
65
|
+
def paint(data, _screencode, color, seq)
|
|
66
|
+
paint_hires(data, color, seq.registers.background, seq)
|
|
55
67
|
end
|
|
68
|
+
|
|
69
|
+
def fg(data, _screencode, _color) = HIRES_FG[data]
|
|
56
70
|
end
|
|
57
71
|
|
|
58
72
|
class MulticolorText
|
|
59
73
|
include Hires
|
|
60
74
|
include Multicolor
|
|
61
75
|
|
|
62
|
-
def
|
|
76
|
+
def initialize
|
|
77
|
+
@palette = Array.new(4, 0)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def paint(data, _screencode, color, seq)
|
|
63
81
|
registers = seq.registers
|
|
64
|
-
data = seq.bank.peek(registers.char_base + (screencode * 8) + row)
|
|
65
82
|
if color.anybits?(0x08)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
palette = @palette
|
|
84
|
+
palette[0] = registers.background(0)
|
|
85
|
+
palette[1] = registers.background(1)
|
|
86
|
+
palette[2] = registers.background(2)
|
|
87
|
+
palette[3] = color & 0x07
|
|
88
|
+
paint_pairs(data, seq)
|
|
69
89
|
else
|
|
70
90
|
paint_hires(data, color & 0x07, registers.background, seq)
|
|
71
91
|
end
|
|
72
92
|
end
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def multicolor_pixel(pair, color, registers)
|
|
77
|
-
case pair
|
|
78
|
-
when 0b00 then registers.background(0)
|
|
79
|
-
when 0b01 then registers.background(1)
|
|
80
|
-
when 0b10 then registers.background(2)
|
|
81
|
-
else color & 0x07
|
|
82
|
-
end
|
|
83
|
-
end
|
|
94
|
+
def fg(data, _screencode, color) = color.anybits?(0x08) ? PAIR_FG[data] : HIRES_FG[data]
|
|
84
95
|
end
|
|
85
96
|
|
|
86
97
|
class ExtendedBackgroundText
|
|
87
98
|
include Hires
|
|
88
99
|
|
|
89
|
-
def
|
|
90
|
-
|
|
91
|
-
background = registers.background((screencode >> 6) & 0b11)
|
|
92
|
-
data = seq.bank.peek(registers.char_base + ((screencode & 0x3f) * 8) + row)
|
|
100
|
+
def paint(data, screencode, color, seq)
|
|
101
|
+
background = seq.registers.background((screencode >> 6) & 0b11)
|
|
93
102
|
paint_hires(data, color, background, seq)
|
|
94
103
|
end
|
|
104
|
+
|
|
105
|
+
def fg(data, _screencode, _color) = HIRES_FG[data]
|
|
95
106
|
end
|
|
96
107
|
|
|
97
108
|
class Bitmap
|
|
98
109
|
include Hires
|
|
99
110
|
|
|
100
|
-
def
|
|
101
|
-
data = seq.bank.peek(seq.registers.bitmap_base + (cell * 8) + row)
|
|
111
|
+
def paint(data, screencode, _color, seq)
|
|
102
112
|
foreground = (screencode >> 4) & 0x0f
|
|
103
113
|
background = screencode & 0x0f
|
|
104
114
|
paint_hires(data, foreground, background, seq)
|
|
105
115
|
end
|
|
116
|
+
|
|
117
|
+
def fg(data, _screencode, _color) = HIRES_FG[data]
|
|
106
118
|
end
|
|
107
119
|
|
|
108
120
|
class MulticolorBitmap
|
|
109
121
|
include Multicolor
|
|
110
122
|
|
|
111
|
-
def
|
|
112
|
-
|
|
113
|
-
data = seq.bank.peek(registers.bitmap_base + (cell * 8) + row)
|
|
114
|
-
paint_pairs(data, seq) do |pair|
|
|
115
|
-
multicolor_pixel(pair, screencode, color, registers)
|
|
116
|
-
end
|
|
123
|
+
def initialize
|
|
124
|
+
@palette = Array.new(4, 0)
|
|
117
125
|
end
|
|
118
126
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
else color & 0x0f
|
|
127
|
-
end
|
|
127
|
+
def paint(data, screencode, color, seq)
|
|
128
|
+
palette = @palette
|
|
129
|
+
palette[0] = seq.registers.background(0)
|
|
130
|
+
palette[1] = (screencode >> 4) & 0x0f
|
|
131
|
+
palette[2] = screencode & 0x0f
|
|
132
|
+
palette[3] = color & 0x0f
|
|
133
|
+
paint_pairs(data, seq)
|
|
128
134
|
end
|
|
135
|
+
|
|
136
|
+
def fg(data, _screencode, _color) = PAIR_FG[data]
|
|
129
137
|
end
|
|
130
138
|
|
|
131
139
|
class Null
|
|
132
|
-
def
|
|
140
|
+
def paint(_data, _screencode, _color, seq)
|
|
133
141
|
seq.cur_fg = NO_FG
|
|
134
142
|
seq.cur_colors.fill(0)
|
|
135
143
|
end
|
|
144
|
+
|
|
145
|
+
def fg(_data, _screencode, _color) = NO_FG
|
|
136
146
|
end
|
|
147
|
+
|
|
148
|
+
NULL_MODE = Null.new
|
|
149
|
+
|
|
150
|
+
# Indexed by ECM/BMM/MCM; the three invalid combinations decode to
|
|
151
|
+
# black.
|
|
152
|
+
MODES = [
|
|
153
|
+
Text.new, # 000 standard text
|
|
154
|
+
MulticolorText.new, # 001 multicolour text
|
|
155
|
+
Bitmap.new, # 010 standard bitmap
|
|
156
|
+
MulticolorBitmap.new, # 011 multicolour bitmap
|
|
157
|
+
ExtendedBackgroundText.new, # 100 ECM text
|
|
158
|
+
NULL_MODE, # 101 invalid
|
|
159
|
+
NULL_MODE, # 110 invalid
|
|
160
|
+
NULL_MODE # 111 invalid
|
|
161
|
+
].freeze
|
|
137
162
|
end
|
|
138
163
|
end
|
|
139
164
|
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class VIC < Cycleable
|
|
5
|
+
# The graphics shift register, pixel by pixel, for the groups where a
|
|
6
|
+
# mode or XSCROLL change lands (VICE x64sc `draw_graphics`). The
|
|
7
|
+
# sequencer paints whole bytes everywhere else, which comes to the same
|
|
8
|
+
# thing while nothing changes.
|
|
9
|
+
#
|
|
10
|
+
# The byte a group draws loads at pixel XSCROLL. ECM and BMM take hold at
|
|
11
|
+
# pixel 4 as they rise and at pixel 6 as they fall (the 6569's colour
|
|
12
|
+
# latency). MCM takes hold at pixel 4 for the colour lookup but only at
|
|
13
|
+
# pixel 7 for how the register is read, where a rising MCM also resets
|
|
14
|
+
# the multicolour flip-flop. An MCM that falls out of the invalid
|
|
15
|
+
# ECM+MCM mode is a pixel later on both counts: the lookup changes at
|
|
16
|
+
# pixel 5 and the read at the next group's pixel 0. The mode bits are
|
|
17
|
+
# the sequencer's: ECM 4, BMM 2, MCM 1.
|
|
18
|
+
class GraphicsShifter
|
|
19
|
+
ECM_BMM = 0b110
|
|
20
|
+
ECM_MCM = 0b101
|
|
21
|
+
BMM = 0b010
|
|
22
|
+
|
|
23
|
+
# The group #draw painted, and which of its pixels are foreground.
|
|
24
|
+
attr_reader :colors, :fg
|
|
25
|
+
|
|
26
|
+
def initialize(registers)
|
|
27
|
+
@registers = registers
|
|
28
|
+
@colors = Array.new(8, 0)
|
|
29
|
+
@fg = Array.new(8, false)
|
|
30
|
+
@gbuf = 0
|
|
31
|
+
@vbuf = 0
|
|
32
|
+
@cbuf = 0
|
|
33
|
+
@flop = 0
|
|
34
|
+
@pixel = 0
|
|
35
|
+
@mode_lookup = 0
|
|
36
|
+
@mode_read = 0
|
|
37
|
+
@late_mcm = false
|
|
38
|
+
@late_read = false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Rebuilds the state at the end of a group that painted whole bytes:
|
|
42
|
+
# the byte loaded at pixel shift under an unchanging mode.
|
|
43
|
+
def prime(data, screencode, color, shift, mode)
|
|
44
|
+
@mode_lookup = mode
|
|
45
|
+
@mode_read = mode & 1
|
|
46
|
+
@late_read = false
|
|
47
|
+
load(data, screencode, color)
|
|
48
|
+
(8 - shift).times { read_pixel }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Draws one group, loading the byte at pixel shift.
|
|
52
|
+
def draw(data, screencode, color, shift, mode)
|
|
53
|
+
i = 0
|
|
54
|
+
while i < 8
|
|
55
|
+
step_mode(i, mode)
|
|
56
|
+
load(data, screencode, color) if i == shift
|
|
57
|
+
pixel = read_pixel
|
|
58
|
+
@colors[i] = lookup(pixel)
|
|
59
|
+
@fg[i] = pixel >= 2
|
|
60
|
+
i += 1
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def step_mode(pixel, mode)
|
|
67
|
+
case pixel
|
|
68
|
+
when 0 then end_late_read
|
|
69
|
+
when 4 then step_lookup(mode)
|
|
70
|
+
when 5 then @mode_lookup &= ~1 if @late_mcm
|
|
71
|
+
when 6 then @mode_lookup &= mode | 1
|
|
72
|
+
when 7 then step_read
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def step_lookup(mode)
|
|
77
|
+
@late_mcm = @mode_lookup.allbits?(ECM_MCM) && mode.nobits?(1)
|
|
78
|
+
@mode_lookup = (@mode_lookup & ~1) | (mode & 1) | (@late_mcm ? 1 : 0) | (mode & ECM_BMM)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def step_read
|
|
82
|
+
return @late_read = true if @late_mcm
|
|
83
|
+
|
|
84
|
+
mcm = @mode_lookup & 1
|
|
85
|
+
@flop = 0 if mcm == 1 && @mode_read.zero?
|
|
86
|
+
@mode_read = mcm
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def end_late_read
|
|
90
|
+
return unless @late_read
|
|
91
|
+
|
|
92
|
+
@mode_read = 0
|
|
93
|
+
@late_read = false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def load(data, screencode, color)
|
|
97
|
+
@gbuf = data
|
|
98
|
+
@vbuf = screencode
|
|
99
|
+
@cbuf = color & 0x0f
|
|
100
|
+
@flop = 1
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def read_pixel
|
|
104
|
+
multicolor = multicolor?
|
|
105
|
+
if @mode_read == 1 && multicolor
|
|
106
|
+
@pixel = @gbuf >> 6 if @flop == 1
|
|
107
|
+
elsif @gbuf.anybits?(0x80)
|
|
108
|
+
@pixel = multicolor && @mode_read.zero? ? 2 : 3
|
|
109
|
+
else
|
|
110
|
+
@pixel = 0
|
|
111
|
+
end
|
|
112
|
+
@gbuf = (@gbuf << 1) & 0xff
|
|
113
|
+
@flop ^= 1
|
|
114
|
+
@pixel
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def multicolor? = @mode_lookup.anybits?(BMM) || @cbuf.anybits?(0x08)
|
|
118
|
+
|
|
119
|
+
def lookup(pixel)
|
|
120
|
+
case @mode_lookup
|
|
121
|
+
when 0 then pixel < 2 ? @registers.background : @cbuf
|
|
122
|
+
when 1 then pixel == 3 ? @cbuf & 0x07 : @registers.background(pixel)
|
|
123
|
+
when 2 then pixel < 2 ? @vbuf & 0x0f : @vbuf >> 4
|
|
124
|
+
when 3 then multicolor_bitmap(pixel)
|
|
125
|
+
when 4 then pixel < 2 ? @registers.background(@vbuf >> 6) : @cbuf
|
|
126
|
+
else 0
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def multicolor_bitmap(pixel)
|
|
131
|
+
case pixel
|
|
132
|
+
when 0 then @registers.background
|
|
133
|
+
when 1 then @vbuf >> 4
|
|
134
|
+
when 2 then @vbuf & 0x0f
|
|
135
|
+
else @cbuf
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class VIC < Cycleable
|
|
5
|
+
# Mid-line writes to the sprite registers, keyed by the pixel at which
|
|
6
|
+
# they become visible. Compositing replays the line through the log, so
|
|
7
|
+
# each pixel is drawn with the register values that were in effect when
|
|
8
|
+
# the VIC reached it.
|
|
9
|
+
class RegisterLog
|
|
10
|
+
def initialize(registers)
|
|
11
|
+
@registers = registers
|
|
12
|
+
@entries = []
|
|
13
|
+
@seed = []
|
|
14
|
+
@values = Array.new(2**6, 0)
|
|
15
|
+
@cursor = 0
|
|
16
|
+
@next_x = 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The pixel at which the next logged write takes effect.
|
|
20
|
+
attr_reader :next_x
|
|
21
|
+
|
|
22
|
+
def empty? = @entries.empty?
|
|
23
|
+
def length = @entries.length
|
|
24
|
+
|
|
25
|
+
def clear
|
|
26
|
+
return if @entries.empty?
|
|
27
|
+
|
|
28
|
+
@entries.clear
|
|
29
|
+
@seed.clear
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def log(pixel_x, reg, old, value)
|
|
33
|
+
@seed[reg] = old if @seed[reg].nil?
|
|
34
|
+
@entries << [pixel_x, reg, value]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def prepare
|
|
38
|
+
@entries.sort_by! { |entry| entry[0] }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Restart the cursor before the line's first pixel, with every logged
|
|
42
|
+
# register back at the value it held when the line started.
|
|
43
|
+
def rewind
|
|
44
|
+
@values.length.times { |reg| @values[reg] = @registers[reg] }
|
|
45
|
+
@seed.each_with_index { |value, reg| @values[reg] = value unless value.nil? }
|
|
46
|
+
@cursor = 0
|
|
47
|
+
@next_x = @entries.empty? ? Float::INFINITY : @entries[0][0]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def [](reg) = @values[reg]
|
|
51
|
+
|
|
52
|
+
# Apply every write visible at or before pixel_x.
|
|
53
|
+
def advance(pixel_x)
|
|
54
|
+
while @cursor < @entries.length && @entries[@cursor][0] <= pixel_x
|
|
55
|
+
entry = @entries[@cursor]
|
|
56
|
+
@values[entry[1]] = entry[2]
|
|
57
|
+
@cursor += 1
|
|
58
|
+
end
|
|
59
|
+
@next_x = @cursor < @entries.length ? @entries[@cursor][0] : Float::INFINITY
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|