1use core::iter::StepBy;
9use core::ops::Range;
10
11#[cfg(feature = "snapshot")]
12use serde::{Serialize, Deserialize};
13
14use crate::clock::{VideoTs, Ts, VFrameTsCounter};
15use crate::chip::{
16 ula128::{Ula128VidFrame, video::create_ula128_renderer}
17};
18use crate::video::{
19 BorderSize, BorderColor, PixelBuffer, Palette,
20 VideoFrame, Video,
21 frame_cache::{pixel_address_coords, color_address_coords}
22};
23use super::{Ula3, Ula3MemContention};
24
25#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
27#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
28pub struct Ula3VidFrame;
29
30impl VideoFrame for Ula3VidFrame {
31 const HTS_RANGE: Range<Ts> = Ula128VidFrame::HTS_RANGE;
33 const VSL_BORDER_TOP: Ts = Ula128VidFrame::VSL_BORDER_TOP;
35 const VSL_PIXELS: Range<Ts> = Ula128VidFrame::VSL_PIXELS;
37 const VSL_BORDER_BOT: Ts = Ula128VidFrame::VSL_BORDER_BOT;
39 const VSL_COUNT: Ts = Ula128VidFrame::VSL_COUNT;
41
42 type BorderHtsIter = StepBy<Range<Ts>>;
43
44 #[inline(always)]
45 fn border_whole_line_hts_iter(border_size: BorderSize) -> Self::BorderHtsIter {
46 Ula128VidFrame::border_whole_line_hts_iter(border_size)
47 }
48 #[inline(always)]
49 fn border_left_hts_iter(border_size: BorderSize) -> Self::BorderHtsIter {
50 Ula128VidFrame::border_left_hts_iter(border_size)
51 }
52 #[inline(always)]
53 fn border_right_hts_iter(border_size: BorderSize) -> Self::BorderHtsIter {
54 Ula128VidFrame::border_right_hts_iter(border_size)
55 }
56
57 #[inline]
58 fn is_contended_line_no_mreq(_vsl: Ts) -> bool {
59 false
60 }
61
62 #[inline]
63 fn contention(hc: Ts) -> Ts {
64 if (-3..125).contains(&hc) {
65 let ct = (hc + 2) & 7;
66 if ct != 0 {
67 return hc + 8 - ct;
68 }
69 }
70 hc
71 }
72}
73
74impl<D, X> Video for Ula3<D, X> {
75 type VideoFrame = Ula3VidFrame;
76 type Contention = Ula3MemContention;
77
78 #[inline]
79 fn border_color(&self) -> BorderColor {
80 self.ula.border_color()
81 }
82
83 fn set_border_color(&mut self, border: BorderColor) {
84 self.ula.set_border_color(border)
85 }
86
87 fn render_video_frame<'a, B: PixelBuffer<'a>, P: Palette<Pixel=B::Pixel>>(
88 &mut self,
89 buffer: &'a mut [u8],
90 pitch: usize,
91 border_size: BorderSize
92 )
93 {
94 create_ula128_renderer(border_size,
95 &mut self.ula,
96 self.beg_screen_shadow,
97 &self.shadow_frame_cache,
98 &mut self.screen_changes)
99 .render_pixels::<B, P, Self::VideoFrame>(buffer, pitch)
100 }
101
102 fn visible_screen_bank(&self) -> usize {
103 self.cur_screen_shadow.into()
104 }
105
106 fn current_video_ts(&self) -> VideoTs {
107 self.ula.current_video_ts()
108 }
109
110 fn current_video_clock(&self) -> VFrameTsCounter<Self::VideoFrame, Self::Contention> {
111 let contention = self.memory_contention();
112 VFrameTsCounter::from_video_ts(self.ula.current_video_ts(), contention)
113 }
114
115 fn set_video_ts(&mut self, vts: VideoTs) {
116 self.ula.set_video_ts(vts);
117 }
118
119 fn flash_state(&self) -> bool {
120 self.ula.flash_state()
121 }
122}
123
124impl<B, X> Ula3<B, X> {
125 #[inline]
126 pub(super) fn update_frame_cache(&mut self, addr: u16, ts: VideoTs) {
127 let maybe_shadow = match addr {
128 0x4000..=0x5AFF => self.page1_screen_shadow_bank(),
129 0xC000..=0xDAFF => self.page3_screen_shadow_bank(),
130 _ => return
131 };
132 let frame_cache = match maybe_shadow {
133 Some(false) => &mut self.ula.frame_cache,
134 Some(true) => &mut self.shadow_frame_cache,
135 None => return
136 };
137 if addr & 0x1800 != 0x1800 {
138 let coords = pixel_address_coords(addr);
139 frame_cache.update_frame_pixels(&self.ula.memory, coords, addr, ts);
140 }
141 else {
142 let coords = color_address_coords(addr);
143 frame_cache.update_frame_colors(&self.ula.memory, coords, addr, ts);
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use crate::clock::{TimestampOps, VFrameTs};
151 use super::*;
152 type TestVideoFrame = Ula3VidFrame;
153 type TestVFTs = VFrameTs<TestVideoFrame>;
154
155 #[test]
156 fn test_contention() {
157 let vts0 = TestVFTs::new(0, 0);
158 let tstates = [(14361, 14362),
159 (14362, 14362),
160 (14363, 14370),
161 (14364, 14370),
162 (14365, 14370),
163 (14366, 14370),
164 (14367, 14370),
165 (14368, 14370)];
166 for offset in (0..16).map(|x| x * 8i32) {
167 for (testing, target) in tstates.iter().copied() {
168 let mut vts: TestVFTs = vts0 + testing + offset as u32;
169 vts.hc = TestVideoFrame::contention(vts.hc);
170 assert_eq!(vts.normalized(),
171 TestVFTs::from_tstates(target + offset));
172 }
173 }
174 let refts = tstates[0].0 as i32;
175 for ts in (refts - 100..refts)
176 .chain(refts + 128..refts+TestVideoFrame::HTS_COUNT as i32) {
177 let vts = TestVFTs::from_tstates(ts);
178 assert_eq!(TestVideoFrame::contention(vts.hc), vts.hc);
179 }
180 }
181
182 #[test]
183 fn test_video_frame_vts_utils() {
184 assert_eq!(TestVFTs::EOF, TestVFTs::from_tstates(TestVideoFrame::FRAME_TSTATES_COUNT));
185 let items = [(( 0, -73), -73, ( 0, 70835), false, true , ( 0, -73)),
186 (( 0, 0), 0, ( 1, 0), false, true , ( 0, 0)),
187 (( 0, -1), -1, ( 0, 70907), false, true , ( 0, -1)),
188 (( -1, 0), -228, ( 0, 70680), false, true , ( -1, 0)),
189 (( 1, 0), 228, ( 1, 228), false, true , ( 1, 0)),
190 ((311, -1), 70907, ( 1, 70907), true , true , (311, -1)),
191 ((311, 0), 70908, ( 2, 0), true , true , (311, 0)),
192 (( 0, 228), 228, ( 1, 228), false, false, ( 1, 0)),
193 ((622,-227),141589, ( 2, 70681), true, false, (621, 1))];
194 for ((vc, hc), fts, (nfr, nfts), eof, is_norm, (nvc, nhc)) in items.iter().copied() {
195 let vts = TestVFTs::new(vc, hc);
196 let nvts = TestVFTs::new(nvc, nhc);
197 assert_eq!(TestVideoFrame::vc_hc_to_tstates(vc, hc), fts);
198 assert_eq!(vts.into_tstates(), fts);
199 assert_eq!(TestVFTs::from_tstates(fts), nvts);
200 assert_eq!(vts.into_frame_tstates(1), (nfr, nfts));
201 assert_eq!(vts.is_eof(), eof);
202 assert_eq!(vts.is_normalized(), is_norm);
203 assert_eq!(vts.normalized(), nvts);
204 }
205 assert_eq!(TestVFTs::max_value(), TestVFTs::new(i16::max_value(), 154));
206 assert_eq!(TestVFTs::min_value(), TestVFTs::new(i16::min_value(), -73));
207
208 let items = [(( 0, 0), 0, ( 0, 0)),
209 (( 0, 0), 1, ( 0, 1)),
210 (( -1, 154), 1, ( 0, -73)),
211 (( 0, 0), 228, ( 1, 0)),
212 (( -1, 1), 227, ( 0, 0)),
213 (( 0, 0), 70908, (311, 0)),
214 (( 1, -1), 70908, (312, -1)),
215 (( 2, 228), 70908, (314, 0))];
216 for ((vc0, hc0), delta, (vc1, hc1)) in items.iter().copied() {
217 let vts0 = TestVFTs::new(vc0, hc0);
218 let vts1 = TestVFTs::new(vc1, hc1);
219 assert_eq!(vts0 + delta, vts1);
220 assert_eq!(vts1.diff_from(vts0), delta as i32);
221 assert_eq!(vts0.diff_from(vts1), -(delta as i32));
222 }
223 let items = [(( 311, 0), ( 0, 0)),
224 (( 311, -73), ( 0, -73)),
225 (( 621, 154), ( 310, 154)),
226 (( 0, 228), ( -311, 228)),
227 ((-32767, -32768), (-32768, -32768)),
228 ((-32768, -32768), (-32768, -32768))];
229 for ((vc0, hc0), (vc1, hc1)) in items.iter().copied() {
230 let vts0 = TestVFTs::new(vc0, hc0);
231 let vts1 = TestVFTs::new(vc1, hc1);
232 assert_eq!(vts0.saturating_sub_frame(), vts1);
233 }
234 let items = [(( 0, 0), ( 0, 0), ( 0, 0), ( 0, 0)),
235 (( 1, 1), ( 1, 1), ( 0, 0), ( 2, 2)),
236 (( 1, 1), ( -1, -1), ( 2, 2), ( 0, 0)),
237 (( 1, 154), ( 1, 1), ( 0, 153), ( 3, -73)),
238 ((-32768, -73), ( 1, 1), (-32768, -73), (-32767, -72)),
239 ((-32768, -73), (-32768, -73), ( 0, 0), (-32768, -73)),
240 (( 32767, 154), ( 1, 1), ( 32766, 153), ( 32767, 154)),
241 (( 32767, 154), ( 32767, 154), ( 0, 0), ( 32767, 154))];
242 for ((vc0, hc0), (vc1, hc1), (svc, shc), (avc, ahc)) in items.iter().copied() {
243 let vts0 = TestVFTs::new(vc0, hc0);
244 let vts1 = TestVFTs::new(vc1, hc1);
245 let subvts = TestVFTs::new(svc, shc);
246 let addvts = TestVFTs::new(avc, ahc);
247 assert_eq!(vts0.saturating_sub(vts1), subvts);
248 assert_eq!(vts0.saturating_add(vts1), addvts);
249 assert_eq!(vts1.saturating_add(vts0), addvts);
250 }
251 }
252}