badline 0.1.0 → 0.2.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 +185 -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 +96 -25
- 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 +132 -16
- data/lib/badline/cia/interrupt_register.rb +102 -0
- data/lib/badline/cia/serial.rb +219 -0
- data/lib/badline/cia/timer.rb +43 -31
- data/lib/badline/cia.rb +138 -88
- data/lib/badline/color_memory.rb +14 -5
- data/lib/badline/computer.rb +97 -12
- data/lib/badline/control_ports.rb +54 -9
- data/lib/badline/cpu/addressing.rb +131 -0
- data/lib/badline/cpu/microcode.rb +169 -0
- data/lib/badline/cpu/operations.rb +88 -0
- data/lib/badline/cpu/stack_operations.rb +82 -0
- data/lib/badline/cpu.rb +109 -168
- 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 +51 -0
- data/lib/badline/kernal_trap/drive/status.rb +49 -0
- data/lib/badline/kernal_trap/drive.rb +277 -0
- data/lib/badline/kernal_trap/file.rb +5 -25
- data/lib/badline/kernal_trap/load.rb +86 -24
- data/lib/badline/kernal_trap/routine.rb +51 -0
- data/lib/badline/kernal_trap/save.rb +46 -14
- data/lib/badline/kernal_trap/serial.rb +171 -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/memory.rb +5 -0
- 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 +251 -23
- data/lib/badline/storage/crt_file.rb +18 -2
- data/lib/badline/storage/d64_image.rb +6 -0
- data/lib/badline/storage/d71_image.rb +4 -0
- data/lib/badline/storage/d81_image.rb +4 -0
- data/lib/badline/storage/disk_image.rb +99 -13
- data/lib/badline/storage/host_directory.rb +30 -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 +389 -55
- data/lib/badline.rb +5 -0
- data/vendor/.gitkeep +0 -0
- metadata +104 -4
data/lib/badline/vic/sprite.rb
CHANGED
|
@@ -1,27 +1,103 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/vic/sprite/internal_bus"
|
|
4
|
+
require "badline/vic/sprite/shifter"
|
|
5
|
+
|
|
3
6
|
module Badline
|
|
4
7
|
class VIC < Cycleable
|
|
8
|
+
# = Sprite
|
|
9
|
+
#
|
|
10
|
+
# The DMA/display state machine and the pixel sequencer for one sprite.
|
|
11
|
+
# The sequencer is a 24-bit shift register clocked once per pixel: the X
|
|
12
|
+
# comparator starts it, the expansion flip-flop gates the shift, and the
|
|
13
|
+
# multicolor flip-flop gates a two-bit output latch that feeds the color
|
|
14
|
+
# mux. Registers written mid-sprite therefore take hold from the pixel
|
|
15
|
+
# they reach, rather than re-decoding the row.
|
|
5
16
|
class Sprite
|
|
6
17
|
X_OFFSET = 104
|
|
7
|
-
ROWS = 21
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
# MCBASE and MC are six-bit byte counters into the sprite's 64-byte
|
|
20
|
+
# block. A sprite ends when MCBASE reaches exactly 63, which is why a
|
|
21
|
+
# mid-line $d017 write that steps it past 63 keeps it running.
|
|
22
|
+
LAST_MCBASE = 63
|
|
23
|
+
MC_MASK = 0x3f
|
|
24
|
+
|
|
25
|
+
# The X comparator matches the pixel before the sprite's first one.
|
|
26
|
+
COMPARE_OFFSET = X_OFFSET - 1
|
|
27
|
+
|
|
28
|
+
# 24 bits at two pixels each, plus slack for a mid-sprite expansion
|
|
29
|
+
# change stretching the tail.
|
|
30
|
+
MAX_SPAN = 64
|
|
31
|
+
|
|
32
|
+
# Latch codes are the multicolor bit pairs: 0 is transparent, 1 and 3
|
|
33
|
+
# are the shared colors, 2 the sprite's own.
|
|
34
|
+
SHARED0 = 1
|
|
35
|
+
OWN_COLOR = 2
|
|
36
|
+
SHARED1 = 3
|
|
37
|
+
|
|
38
|
+
# The s-accesses reload the shift register partway through the line,
|
|
39
|
+
# at raster pixel 459 for sprite 0 and 16 later for each sprite after
|
|
40
|
+
# it. A sprite still shifting there loses the rest of its row, and the
|
|
41
|
+
# comparator is ignored for the twelve pixels from it (the spritescan
|
|
42
|
+
# dump). Its output holds the last pixel for seven, through K+6
|
|
43
|
+
# (spritefetchbug).
|
|
44
|
+
RELOAD_X = 459
|
|
45
|
+
RELOAD_STEP = 16
|
|
46
|
+
RELOAD_DEAD = 12
|
|
47
|
+
RELOAD_HOLD = 7
|
|
48
|
+
|
|
49
|
+
# On the line of its last row, a sprite whose DMA ended at cycle 16
|
|
50
|
+
# loses its display at cycle 58, so no hit from this pixel on starts
|
|
51
|
+
# it (spritegap3). One already shifting runs on.
|
|
52
|
+
DISPLAY_OFF_X = 460
|
|
53
|
+
|
|
54
|
+
include Shifter
|
|
10
55
|
|
|
11
|
-
|
|
56
|
+
attr_reader :index, :leftmost, :span, :codes,
|
|
57
|
+
:reload_leftmost, :reload_span, :reload_codes
|
|
58
|
+
|
|
59
|
+
def initialize(index, registers, bank, width, bus = InternalBus.new(bank, width / 8))
|
|
12
60
|
@index = index
|
|
13
61
|
@registers = registers
|
|
14
62
|
@bank = bank
|
|
63
|
+
@bus = bus
|
|
15
64
|
@width = width
|
|
16
65
|
@bit = 1 << index
|
|
17
|
-
@
|
|
18
|
-
@
|
|
19
|
-
@
|
|
20
|
-
@
|
|
21
|
-
@
|
|
66
|
+
@dma = false
|
|
67
|
+
@display_on = false
|
|
68
|
+
@mcbase = 0
|
|
69
|
+
@mc = 0
|
|
70
|
+
@exp_ff = true
|
|
71
|
+
@crunched_mc = nil
|
|
72
|
+
@bits = 0
|
|
73
|
+
@row_ready = false
|
|
74
|
+
@codes = Array.new(MAX_SPAN + RELOAD_HOLD, 0)
|
|
75
|
+
@leftmost = 0
|
|
76
|
+
@span = 0
|
|
77
|
+
@reload_codes = Array.new(MAX_SPAN + RELOAD_HOLD, 0)
|
|
78
|
+
@reload_leftmost = 0
|
|
79
|
+
@reload_span = 0
|
|
80
|
+
@reload_x = (RELOAD_X + (RELOAD_STEP * index)) % width
|
|
81
|
+
@reload_next_line = RELOAD_X + (RELOAD_STEP * index) < width
|
|
82
|
+
@sr = 0
|
|
83
|
+
@latch = 0
|
|
84
|
+
@mc_flop = false
|
|
85
|
+
@xe_flop = false
|
|
86
|
+
@first_byte_lost = false
|
|
87
|
+
@blind = false
|
|
88
|
+
@show_from = 0
|
|
22
89
|
end
|
|
23
90
|
|
|
24
|
-
def displaying? = @
|
|
91
|
+
def displaying? = @dma
|
|
92
|
+
|
|
93
|
+
# True when this line has a row for the sequencer: its own, the next
|
|
94
|
+
# one for a sprite that reloads late in the line, or the last one for
|
|
95
|
+
# a sprite that reloads at its start.
|
|
96
|
+
def rendering?
|
|
97
|
+
return true if @row_ready
|
|
98
|
+
|
|
99
|
+
@reload_next_line ? @dma && @display_on : !@prev_bits.nil?
|
|
100
|
+
end
|
|
25
101
|
|
|
26
102
|
def enabled? = @registers[0x15].anybits?(@bit)
|
|
27
103
|
def multicolor? = @registers[0x1c].anybits?(@bit)
|
|
@@ -34,85 +110,219 @@ module Badline
|
|
|
34
110
|
msb | @registers[index * 2]
|
|
35
111
|
end
|
|
36
112
|
|
|
37
|
-
|
|
113
|
+
# The Y comparator is eight bits wide, so the PAL lines above 255
|
|
114
|
+
# match the coordinates 0-55 a second time (spritey).
|
|
115
|
+
def y_match?(line) = (line & 0xff) == @registers[(index * 2) + 1]
|
|
116
|
+
|
|
38
117
|
def color = @registers[0x27 + index] & 0x0f
|
|
39
118
|
|
|
40
|
-
|
|
41
|
-
|
|
119
|
+
# Cycle 16: MCBASE takes MC, which the three s-accesses stepped past
|
|
120
|
+
# the row, but only while the expansion flip-flop is set.
|
|
121
|
+
def advance_mcbase
|
|
122
|
+
mc = @crunched_mc || ((@mc + 3) & MC_MASK)
|
|
123
|
+
@crunched_mc = nil
|
|
124
|
+
@mcbase = mc if @exp_ff && @dma
|
|
125
|
+
end
|
|
42
126
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
127
|
+
# The end-of-sprite compare, a column after MCBASE moves. MCBASE has
|
|
128
|
+
# to land on 63 exactly, so a crunched sprite steps over it and runs
|
|
129
|
+
# on through the rest of its block. Only the DMA stops here; the
|
|
130
|
+
# display waits for cycle 58.
|
|
131
|
+
def finish_mcbase
|
|
132
|
+
@dma = false if @mcbase == LAST_MCBASE
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Clearing MxYE sets the expansion flip-flop at once. Cleared in
|
|
136
|
+
# cycle 15, while the flip-flop is still reset, it also crunches MC:
|
|
137
|
+
# the bits step to a mix of MC and MCBASE (Bauer §3.8, VICE
|
|
138
|
+
# `d017_store`) that cycle 16 then hands to MCBASE.
|
|
139
|
+
def clear_y_expansion(crunch)
|
|
140
|
+
return if @exp_ff
|
|
141
|
+
|
|
142
|
+
@exp_ff = true
|
|
143
|
+
return unless crunch && @dma
|
|
144
|
+
|
|
145
|
+
mc = (@mc + 3) & MC_MASK
|
|
146
|
+
@crunched_mc = (0x2a & @mcbase & mc) | (0x15 & (@mcbase | mc))
|
|
147
|
+
end
|
|
48
148
|
|
|
49
|
-
|
|
149
|
+
# Cycle 56, after the second Y compare: MxYE inverts the expansion
|
|
150
|
+
# flip-flop of a sprite with DMA running.
|
|
151
|
+
def toggle_expansion
|
|
152
|
+
@exp_ff = !@exp_ff if @dma && y_expanded?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Cycles 55 and 56: a Y/enable match starts the DMA and rewinds
|
|
156
|
+
# MCBASE, and returns true. Display is enabled separately in cycle 58,
|
|
157
|
+
# so the rows render from the following line on.
|
|
158
|
+
#
|
|
159
|
+
# On the VIC's side, BA falls at the sprite's own column — 55 for
|
|
160
|
+
# sprite 0, two later for each sprite after it, a column behind the
|
|
161
|
+
# window the CPU sees in VIC::SPRITE_BA_WINDOWS — or two columns after
|
|
162
|
+
# this compare, whichever is later. AEC follows three columns on, and
|
|
163
|
+
# the first of the three s-accesses runs in the column it arrives in:
|
|
164
|
+
# a DMA starting on the second compare therefore loses that access for
|
|
165
|
+
# sprite 0, alone among the eight in following the compares
|
|
166
|
+
# immediately (spriteenable2).
|
|
167
|
+
def check_dma(line, column)
|
|
168
|
+
return false if @dma || !enabled? || !y_match?(line)
|
|
169
|
+
|
|
170
|
+
@first_byte_lost = column + 2 > 55 + (2 * index)
|
|
171
|
+
@dma = true
|
|
172
|
+
@mcbase = 0
|
|
173
|
+
@exp_ff = true
|
|
174
|
+
end
|
|
50
175
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
176
|
+
# Cycle 58: MC is reloaded from MCBASE for the coming row, and a
|
|
177
|
+
# sprite with DMA running starts displaying only while MxE and Y still
|
|
178
|
+
# match — a write to either between the compares and here keeps the
|
|
179
|
+
# data fetch running invisibly. The display goes off only here, and
|
|
180
|
+
# only once the DMA has, so a DMA restarted on the line of its last
|
|
181
|
+
# row keeps a display that was already on (spriterestart).
|
|
182
|
+
def check_display(line)
|
|
183
|
+
@mc = @mcbase
|
|
184
|
+
if @dma
|
|
185
|
+
@display_on = true if enabled? && y_match?(line)
|
|
186
|
+
else
|
|
187
|
+
@display_on = false
|
|
55
188
|
end
|
|
189
|
+
blind_fetch if @blind && @display_on
|
|
190
|
+
end
|
|
56
191
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
192
|
+
# The address of the s-access that falls in phi1, the middle of the
|
|
193
|
+
# three, or nil when the sprite's DMA is off and the VIC idles there.
|
|
194
|
+
def phi1_address
|
|
195
|
+
(pointer * 64) + ((@mc + 1) & MC_MASK) if @dma
|
|
60
196
|
end
|
|
61
197
|
|
|
198
|
+
# The row fetched at the end of the previous line renders on this one.
|
|
199
|
+
# A lost first s-access reads back the $ff the CPU is still driving.
|
|
200
|
+
def start_line
|
|
201
|
+
@span = 0
|
|
202
|
+
@reload_span = 0
|
|
203
|
+
@prev_bits = (@bits if @row_ready && @dma)
|
|
204
|
+
@row_ready = false
|
|
205
|
+
@blind = !@dma && !@reload_next_line
|
|
206
|
+
@show_from = 0
|
|
207
|
+
lost = @first_byte_lost
|
|
208
|
+
@first_byte_lost = false
|
|
209
|
+
return unless @dma && @display_on
|
|
210
|
+
|
|
211
|
+
@bits = row_bits(@mc)
|
|
212
|
+
@bits |= 0xff << 16 if lost
|
|
213
|
+
@row_ready = true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Replay the line through the pixel sequencer, filling the code buffer
|
|
217
|
+
# from the X match on. A log of mid-line register writes makes the
|
|
218
|
+
# comparator and the shift register see the values each pixel was
|
|
219
|
+
# drawn with.
|
|
220
|
+
#
|
|
221
|
+
# The row can be shown once before the reload and once after it, so a
|
|
222
|
+
# sprite moved past the beam can fire a second time on the same line
|
|
223
|
+
# (spritex). A match inside the dead pixels shows nothing.
|
|
224
|
+
def sequence(log = nil)
|
|
225
|
+
@span = 0
|
|
226
|
+
@reload_span = 0
|
|
227
|
+
reload_bits = reload_row
|
|
228
|
+
return unless @row_ready || @prev_bits || reload_bits
|
|
229
|
+
|
|
230
|
+
log = nil if log.nil? || log.empty?
|
|
231
|
+
@stop_x = @dma ? nil : DISPLAY_OFF_X
|
|
232
|
+
comparator_hits(log).each { |start| sequence_hit(log, start, reload_bits) }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# The color at a raster position, for a sprite sequenced with the
|
|
236
|
+
# registers as they stand.
|
|
62
237
|
def pixel(raster_x)
|
|
63
|
-
|
|
238
|
+
code = code_at(raster_x, @leftmost, @span, @codes) ||
|
|
239
|
+
code_at(raster_x, @reload_leftmost, @reload_span, @reload_codes)
|
|
240
|
+
return nil if code.nil? || code.zero?
|
|
64
241
|
|
|
242
|
+
palette(@registers, Array.new(4, 0))[code]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Fill a four-entry lookup from latch code to color, against a view of
|
|
246
|
+
# the color registers.
|
|
247
|
+
def palette(view, into)
|
|
248
|
+
into[SHARED0] = view[0x25] & 0x0f
|
|
249
|
+
into[OWN_COLOR] = view[0x27 + index] & 0x0f
|
|
250
|
+
into[SHARED1] = view[0x26] & 0x0f
|
|
251
|
+
into
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
private
|
|
255
|
+
|
|
256
|
+
# On the line whose compare started the DMA, sprites 3-7 ran their
|
|
257
|
+
# s-accesses before it was on, so the shift register holds what the
|
|
258
|
+
# VIC saw on its bus instead of a row, and a hit from the display
|
|
259
|
+
# turning on shows it (sbsprf24).
|
|
260
|
+
def blind_fetch
|
|
261
|
+
@bits = @bus.row(index)
|
|
262
|
+
@row_ready = true
|
|
263
|
+
@show_from = DISPLAY_OFF_X
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def code_at(raster_x, leftmost, span, codes)
|
|
65
267
|
dist = raster_x - leftmost
|
|
66
268
|
dist += @width if dist.negative?
|
|
67
|
-
dist
|
|
269
|
+
codes[dist] if dist < span
|
|
68
270
|
end
|
|
69
271
|
|
|
70
|
-
|
|
272
|
+
def sequence_hit(log, start, reload_bits)
|
|
273
|
+
return if (@stop_x && start >= @stop_x) || start < @show_from
|
|
71
274
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if multicolor?
|
|
77
|
-
decode_multicolor(pixels, x_expanded?)
|
|
78
|
-
else
|
|
79
|
-
decode_hires(pixels, x_expanded?)
|
|
275
|
+
if start < @reload_x
|
|
276
|
+
sequence_early(log, start)
|
|
277
|
+
elsif start >= @reload_x + RELOAD_DEAD
|
|
278
|
+
sequence_reloaded(log, start, reload_bits)
|
|
80
279
|
end
|
|
81
280
|
end
|
|
82
281
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
i += 1
|
|
91
|
-
end
|
|
282
|
+
def sequence_early(log, start)
|
|
283
|
+
bits = early_row
|
|
284
|
+
return unless bits && @span.zero?
|
|
285
|
+
|
|
286
|
+
@leftmost = start
|
|
287
|
+
cut = reload_cut(start)
|
|
288
|
+
@span = cut_at_reload(cut, run(log, start, @codes, bits, cut), @codes)
|
|
92
289
|
end
|
|
93
290
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
291
|
+
def sequence_reloaded(log, start, bits)
|
|
292
|
+
return unless bits && @reload_span.zero?
|
|
293
|
+
|
|
294
|
+
@reload_leftmost = start
|
|
295
|
+
cut = reload_cut(start)
|
|
296
|
+
@reload_span = cut_at_reload(cut, run(log, start, @reload_codes, bits, cut), @reload_codes)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# The row in the shift register ahead of the reload: this line's for
|
|
300
|
+
# sprites 0-2, and for the others the one their reload at the start of
|
|
301
|
+
# the last line brought in.
|
|
302
|
+
def early_row = @reload_next_line ? current_row : @prev_bits
|
|
303
|
+
|
|
304
|
+
def current_row = (@bits if @row_ready)
|
|
305
|
+
|
|
306
|
+
# The row the reload brings in. Sprites 0-2 reload late in the line
|
|
307
|
+
# with the next line's row, so one that fires after it shows that row
|
|
308
|
+
# a line early (spritegap); the others reload at the start of the line
|
|
309
|
+
# with the row it already has.
|
|
310
|
+
def reload_row
|
|
311
|
+
return current_row unless @reload_next_line
|
|
312
|
+
return unless @dma && @display_on
|
|
313
|
+
|
|
314
|
+
bits = row_bits(@mc)
|
|
315
|
+
bits |= 0xff << 16 if @first_byte_lost
|
|
316
|
+
bits
|
|
109
317
|
end
|
|
110
318
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
319
|
+
# The three s-accesses step MC through the sprite's block, wrapping
|
|
320
|
+
# within it.
|
|
321
|
+
def row_bits(counter)
|
|
322
|
+
base = pointer * 64
|
|
323
|
+
(@bank.peek(base + counter) << 16) |
|
|
324
|
+
(@bank.peek(base + ((counter + 1) & MC_MASK)) << 8) |
|
|
325
|
+
@bank.peek(base + ((counter + 2) & MC_MASK))
|
|
116
326
|
end
|
|
117
327
|
|
|
118
328
|
def pointer = @bank.peek(@registers.screen_base + 0x3f8 + index)
|