1use core::fmt;
9use core::marker::PhantomData;
10use crate::clock::{Ts, VideoTs};
11use crate::memory::{ZxMemory, ScreenArray};
12use crate::video::{
13 pixel_line_offset, color_line_offset,
14 VideoFrame, CellCoords,
15 frame_cache::*
16};
17
18const COL_INK_HTS: &[Ts;COLUMNS] = &[1, 3, 9, 11, 17, 19, 25, 27, 33, 35, 41, 43, 49, 51, 57, 59, 65, 67, 73, 75, 81, 83, 89, 91, 97, 99, 105, 107, 113, 115, 121, 123];
21const COL_ATTR_HTS: &[Ts;COLUMNS] = &[2, 4, 10, 12, 18, 20, 26, 28, 34, 36, 42, 44, 50, 52, 58, 60, 66, 68, 74, 76, 82, 84, 90, 92, 98, 100, 106, 108, 114, 116, 122, 124];
22
23#[derive(Clone)]
35pub struct UlaFrameCache<V> {
36 pub frame_pixels: [(u32, [u8;COLUMNS]);PIXEL_LINES], pub frame_colors: [(u32, [u8;COLUMNS]);PIXEL_LINES],
38 pub frame_colors_coarse: [(u32, [u8;COLUMNS]);ATTR_ROWS], _video_frame: PhantomData<V>
40}
41
42pub struct UlaFrameRef<'a, V> {
44 pub screen: &'a ScreenArray,
45 pub frame_cache: &'a UlaFrameCache<V>,
46}
47
48pub(crate) struct UlaFrameLineIter<'a> {
49 pub column: usize,
50 pub ink_line: &'a[u8;COLUMNS],
51 pub attr_line: &'a[u8;COLUMNS],
52 pub frame_pixels: &'a(u32, [u8;COLUMNS]),
53 pub frame_colors: &'a(u32, [u8;COLUMNS]),
54 pub frame_colors_coarse: &'a(u32, [u8;COLUMNS])
55}
56
57pub struct UlaFrameProducer<'a, V> {
59 frame_ref: UlaFrameRef<'a, V>,
60 line: usize,
61 line_iter: UlaFrameLineIter<'a>
62}
63
64impl<'a, V> UlaFrameRef<'a, V> {
65 pub const fn new(screen: &'a ScreenArray, frame_cache: &'a UlaFrameCache<V>) -> Self {
66 UlaFrameRef { screen, frame_cache }
67 }
68}
69
70impl<'a> UlaFrameLineIter<'a> {
71 pub fn new<V>(line: usize, screen: &'a ScreenArray, fc: &'a UlaFrameCache<V>) -> Self {
72 let frame_pixels = &fc.frame_pixels[line];
73 let frame_colors = &fc.frame_colors[line];
74 let frame_colors_coarse = &fc.frame_colors_coarse[line >> 3];
75 let ink_line = ink_line_from(line, screen);
76 let attr_line = attr_line_from(line, screen);
77 UlaFrameLineIter {
78 column: 0,
79 ink_line,
80 attr_line,
81 frame_pixels,
82 frame_colors,
83 frame_colors_coarse
84 }
85 }
86}
87
88impl<'a, V> UlaFrameProducer<'a, V> {
89 pub fn new(screen: &'a ScreenArray, frame_cache: &'a UlaFrameCache<V>) -> Self {
90 let line = 0;
91 let line_iter = UlaFrameLineIter::new(line, screen, frame_cache);
92 let frame_ref = UlaFrameRef::new(screen, frame_cache);
93 UlaFrameProducer { line, frame_ref, line_iter }
94 }
95
96 pub fn swap_frame(&mut self, frame_ref: &mut UlaFrameRef<'a, V>) {
97 core::mem::swap(&mut self.frame_ref, frame_ref);
98 self.update_iter()
99 }
100
101 #[inline(always)]
102 pub fn column(&mut self) -> usize {
103 self.line_iter.column
104 }
105
106 #[inline(always)]
107 pub fn line(&mut self) -> usize {
108 self.line
109 }
110
111 pub fn update_iter(&mut self) {
112 let line = self.line;
113 let fc = &self.frame_ref.frame_cache;
114 self.line_iter.frame_pixels = &fc.frame_pixels[line];
115 self.line_iter.frame_colors = &fc.frame_colors[line];
116 self.line_iter.frame_colors_coarse = &fc.frame_colors_coarse[line >> 3];
117 self.line_iter.ink_line = ink_line_from(line, self.frame_ref.screen);
118 self.line_iter.attr_line = attr_line_from(line, self.frame_ref.screen);
119 }
120}
121
122impl<'a, V> VideoFrameDataIterator for UlaFrameProducer<'a, V> {
123 fn next_line(&mut self) {
124 let line = self.line + 1;
125 if line < PIXEL_LINES {
126 self.line = line;
127 self.update_iter();
128 self.line_iter.column = 0;
129 }
130 }
131}
132
133impl<'a, V> Iterator for UlaFrameProducer<'a, V> {
134 type Item = (u8, u8);
135
136 #[inline(always)]
137 fn next(&mut self) -> Option<Self::Item> {
138 self.line_iter.next()
139 }
140}
141
142impl<'a> Iterator for UlaFrameLineIter<'a> {
143 type Item = (u8, u8);
144
145 fn next(&mut self) -> Option<Self::Item> {
146 let column = self.column;
147 if column >= COLUMNS {
148 return None
149 }
150 self.column = column + 1;
151 let bitmask = 1 << column;
152 let ink: u8 = if self.frame_pixels.0 & bitmask != 0 {
153 self.frame_pixels.1[column & (COLUMNS - 1)]
154 }
155 else {
156 self.ink_line[column & (COLUMNS - 1)]
157 };
158 let attr: u8 = if self.frame_colors.0 & bitmask != 0 {
159 self.frame_colors.1[column & (COLUMNS - 1)]
160 }
161 else if self.frame_colors_coarse.0 & bitmask != 0 {
162 self.frame_colors_coarse.1[column & (COLUMNS - 1)]
163 }
164 else {
165 self.attr_line[column & (COLUMNS - 1)]
166 };
167 Some((ink, attr))
168 }
169}
170
171impl<V> Default for UlaFrameCache<V> {
172 fn default() -> Self {
173 UlaFrameCache {
174 frame_pixels: [(0, [0;COLUMNS]);PIXEL_LINES],
175 frame_colors: [(0, [0;COLUMNS]);PIXEL_LINES],
176 frame_colors_coarse: [(0, [0;COLUMNS]);ATTR_ROWS],
177 _video_frame: PhantomData
178 }
179 }
180}
181
182impl<V> fmt::Debug for UlaFrameCache<V> {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 let count = |frame: &[(u32, _)]| -> u32 {
185 frame.iter().map(|(mask,_)| mask.count_ones()).sum()
186 };
187 f.debug_struct("UlaFrameCache")
188 .field("frame_pixels", &count(&self.frame_pixels))
189 .field("frame_colors", &count(&self.frame_colors))
190 .field("frame_colors_coarse", &count(&self.frame_colors_coarse))
191 .finish()
192 }
193}
194
195impl<V> UlaFrameCache<V> {
196 pub fn clear(&mut self) {
197 for p in self.frame_pixels.iter_mut() {
198 p.0 = 0;
199 }
200 for p in self.frame_colors.iter_mut() {
201 p.0 = 0;
202 }
203 for p in self.frame_colors_coarse.iter_mut() {
204 p.0 = 0;
205 }
206 }
207}
208
209impl<V: VideoFrame> UlaFrameCache<V> {
210 #[inline(never)]
214 pub fn update_frame_pixels<M: ZxMemory>(
215 &mut self,
216 memory: &M,
217 CellCoords { column, row }: CellCoords,
218 addr: u16,
219 ts: VideoTs
220 )
221 {
222 let column = column as usize & 31;
223 let vy = ts.vc - V::VSL_PIXELS.start;
224 let y = Ts::from(row);
225 if y < vy || y == vy && ts.hc > COL_INK_HTS[column] {
226 let (mask, pixels) = &mut self.frame_pixels[row as usize];
227 let mbit = 1 << column;
228 if *mask & mbit == 0 {
229 pixels[column] = memory.read(addr);
230 *mask |= mbit;
231 }
232 }
233 }
234 #[inline(never)]
238 pub fn update_frame_colors<M: ZxMemory>(
239 &mut self,
240 memory: &M,
241 CellCoords { column, row }: CellCoords,
242 addr: u16,
243 ts: VideoTs
244 )
245 {
246 let column = column as usize & 31;
247 let vy = ts.vc - V::VSL_PIXELS.start;
248 let coarse_vy = vy >> 3;
249 let coarse_y = Ts::from(row);
250 if coarse_y < coarse_vy ||
251 coarse_y == coarse_vy &&
252 vy & 0b111 == 0b111 &&
253 ts.hc > COL_ATTR_HTS[column] {
254 let (mask, colors) = &mut self.frame_colors_coarse[row as usize];
255 let mbit = 1 << column;
256 if *mask & mbit == 0 {
257 *mask |= mbit;
258 colors[column] = memory.read(addr);
259 }
260 }
261 else if coarse_y == coarse_vy {
262 let line_top = (coarse_vy << 3) as usize;
263 let line_bot = if ts.hc > COL_ATTR_HTS[column] {
264 vy + 1
265 } else {
266 vy
267 } as usize;
268 if line_top < line_bot {
269 let memval = memory.read(addr);
270 let mbit = 1 << column;
271 for (mask, colors) in self.frame_colors[line_top..line_bot].iter_mut().rev() {
272 if *mask & mbit != 0 {
273 break;
274 }
275 *mask |= mbit;
276 colors[column] = memval;
277 }
278 }
279 }
280 }
281 pub fn apply_snow_interference(
283 &mut self,
284 screen: &ScreenArray,
285 CellCoords { column, row }: CellCoords,
286 snow: u8
287 )
288 {
289 let (row, column) = (row as usize, column as usize & 31);
290 let mbit = 1 << column;
291 let (mask, pixels) = &mut self.frame_pixels[row];
292 let offset_snow = (pixel_line_offset(row) & 0x1F00) | snow as usize;
293 pixels[column] = screen[offset_snow];
294 *mask |= mbit;
295
296 let offset = ATTRS_OFFSET + color_line_offset(row);
297 let offset_snow = (offset & 0x1F00) | snow as usize;
298 let (mask, colors) = &mut self.frame_colors[row];
299 colors[column] = screen[offset_snow];
300 *mask |= mbit;
301 let row_top = row & !7;
303 if row_top < row {
304 let memval = screen[offset];
305 for (mask, colors) in self.frame_colors[row_top..row].iter_mut().rev() {
306 if *mask & mbit != 0 {
307 break;
308 }
309 *mask |= mbit;
310 colors[column] = memval;
311 }
312 }
313 }
314}