1#![macro_use]
10use core::fmt;
11
12mod audio;
13mod earmic;
14pub mod frame_cache;
15mod io;
16mod video;
17mod video_ntsc;
18mod plus;
19mod cpuext;
20#[cfg(feature = "formats")]
21mod screen;
22
23use core::num::Wrapping;
24
25#[allow(unused_imports)]
26use log::{error, warn, info, debug, trace};
27
28use crate::z80emu::{*, host::Result};
29#[cfg(feature = "snapshot")]
30use serde::{Serialize, Deserialize};
31
32use crate::bus::{BusDevice, VFNullDevice};
33use crate::chip::{
34 UlaControl, FrameState, ControlUnit, MemoryAccess, EarMic, ReadEarMode
35};
36use crate::video::{BorderColor, VideoFrame};
37use crate::memory::{ZxMemory, MemoryExtension, NoMemoryExtension};
38use crate::peripherals::ZXKeyboardMap;
39use crate::clock::{
40 FTs, VFrameTs, VFrameTsCounter, MemoryContention,
41 VideoTsData1, VideoTsData2, VideoTsData3
42};
43use frame_cache::UlaFrameCache;
44
45pub use cpuext::*;
46pub use video::UlaVideoFrame;
47pub use video_ntsc::UlaNTSCVidFrame;
48
49pub type UlaNTSC<M, B=VFNullDevice<UlaNTSCVidFrame>, X=NoMemoryExtension> = Ula<M, B, X, UlaNTSCVidFrame>;
51pub type UlaPAL<M, B=VFNullDevice<UlaVideoFrame>, X=NoMemoryExtension> = Ula<M, B, X, UlaVideoFrame>;
53
54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
56pub struct UlaMemoryContention;
57
58#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
68#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
69#[derive(Clone)]
70pub struct Ula<M, B, X, V> {
71 pub(super) frames: Wrapping<u64>, #[cfg_attr(feature = "snapshot", serde(bound = "V: VideoFrame"))]
73 pub(super) tsc: VFrameTs<V>, pub(super) memory: M,
75 pub(super) bus: B,
76 pub(super) memext: X,
78 #[cfg_attr(feature = "snapshot", serde(skip))]
80 keyboard: ZXKeyboardMap,
81 read_ear_mode: ReadEarMode,
82 late_timings: bool,
83 #[cfg(feature = "boxed_frame_cache")]
85 #[cfg_attr(feature = "snapshot", serde(skip))]
86 pub(super) frame_cache: Box<UlaFrameCache<V>>,
87
88 #[cfg(not(feature = "boxed_frame_cache"))]
89 #[cfg_attr(feature = "snapshot", serde(skip))]
90 pub(super) frame_cache: UlaFrameCache<V>,
91
92 #[cfg_attr(feature = "snapshot", serde(skip))]
93 border_out_changes: Vec<VideoTsData3>, pub(super) border: BorderColor, pub(super) last_border: BorderColor, #[cfg_attr(feature = "snapshot", serde(skip))]
98 ear_in_changes: Vec<VideoTsData1>, prev_ear_in: bool, ear_in_last_index: usize, read_ear_in_count: Wrapping<u32>, #[cfg_attr(feature = "snapshot", serde(skip))]
103 earmic_out_changes: Vec<VideoTsData2>, prev_earmic_ts: FTs, prev_earmic_data: EarMic, last_earmic_data: EarMic, }
108
109impl MemoryContention for UlaMemoryContention {
110 #[inline(always)]
111 fn is_contended_address(self, address: u16) -> bool {
112 address & 0xC000 == 0x4000
113 }
114}
115
116impl<M, B, X, V: VideoFrame> FrameState for Ula<M, B, X, V> {
117 fn current_frame(&self) -> u64 {
118 self.frames.0
119 }
120
121 fn set_frame_counter(&mut self, fc: u64) {
122 self.frames = Wrapping(fc);
123 }
124
125 fn frame_tstate(&self) -> (u64, FTs) {
126 self.tsc.into_frame_tstates(self.frames.0)
127 }
128
129 fn current_tstate(&self) -> FTs {
130 self.tsc.into_tstates()
131 }
132
133 fn set_frame_tstate(&mut self, ts: FTs) {
134 let ts = ts.rem_euclid(V::FRAME_TSTATES_COUNT);
135 let tsc = VFrameTs::from_tstates(ts);
136 self.tsc = tsc
137 }
138
139 fn is_frame_over(&self) -> bool {
140 self.tsc.is_eof()
141 }
142}
143
144impl<M, B, X, V> UlaControl for Ula<M, B, X, V> {
145 fn has_late_timings(&self) -> bool {
146 self.late_timings
147 }
148
149 fn set_late_timings(&mut self, late_timings: bool) {
150 self.late_timings = late_timings;
151 }
152}
153
154impl<M, B, X, V> Default for Ula<M, B, X, V>
155where M: Default,
156 B: Default,
157 X: Default
158{
159 fn default() -> Self {
160 Ula {
161 frames: Wrapping(0), tsc: VFrameTs::default(),
163 memory: M::default(),
164 bus: B::default(),
165 memext: X::default(),
166 keyboard: ZXKeyboardMap::empty(),
168 read_ear_mode: ReadEarMode::Issue3,
169 late_timings: false,
170 frame_cache: Default::default(),
172 border_out_changes: Vec::new(),
173 border: BorderColor::WHITE, last_border: BorderColor::WHITE, ear_in_changes: Vec::new(),
177 prev_ear_in: false,
178 ear_in_last_index: 0,
179 read_ear_in_count: Wrapping(0),
180 earmic_out_changes: Vec::new(),
181 prev_earmic_ts: FTs::min_value(),
182 prev_earmic_data: EarMic::empty(),
183 last_earmic_data: EarMic::empty(),
184 }
185 }
186}
187
188impl<M, B, X, V> fmt::Debug for Ula<M, B, X, V>
189 where M: ZxMemory,
190 B: BusDevice,
191 X: MemoryExtension,
192 V: VideoFrame
193{
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 f.debug_struct("Ula")
196 .field("frames", &self.frames.0)
197 .field("tsc", &self.tsc)
198 .field("memory", &self.memory.mem_ref().len())
199 .field("bus", &self.bus)
200 .field("memext", &self.memext)
201 .field("keyboard", &self.keyboard)
202 .field("read_ear_mode", &self.read_ear_mode)
203 .field("late_timings", &self.late_timings)
204 .field("frame_cache", &self.frame_cache)
205 .field("border_out_changes", &self.border_out_changes.len())
206 .field("border", &self.border)
207 .field("last_border", &self.last_border)
208 .field("prev_ear_in", &self.prev_ear_in)
209 .field("ear_in_changes", &self.ear_in_changes.len())
210 .field("read_ear_in_count", &self.read_ear_in_count.0)
211 .field("earmic_out_changes", &self.earmic_out_changes.len())
212 .field("prev_earmic_data", &self.prev_earmic_data)
213 .field("last_earmic_data", &self.last_earmic_data)
214 .finish()
215 }
216}
217
218impl<M, B, X, V> MemoryAccess for Ula<M, B, X, V>
219 where M: ZxMemory, X: MemoryExtension
220{
221 type Memory = M;
222 type MemoryExt = X;
223
224 #[inline(always)]
225 fn memory_ext_ref(&self) -> &Self::MemoryExt {
226 &self.memext
227 }
228 #[inline(always)]
229 fn memory_ext_mut(&mut self) -> &mut Self::MemoryExt {
230 &mut self.memext
231 }
232 #[inline(always)]
233 fn memory_mut(&mut self) -> &mut Self::Memory {
234 &mut self.memory
235 }
236 #[inline(always)]
237 fn memory_ref(&self) -> &Self::Memory {
238 &self.memory
239 }
240
241 fn memory_with_ext_mut(&mut self) -> (&mut Self::Memory, &mut Self::MemoryExt) {
242 (&mut self.memory, &mut self.memext)
243 }
244}
245
246impl<M, B, X, V> ControlUnit for Ula<M, B, X, V>
247 where M: ZxMemory,
248 B: BusDevice,
249 B::Timestamp: From<VFrameTs<V>>,
250 X: MemoryExtension,
251 V: VideoFrame
252{
253 type BusDevice = B;
254
255 fn bus_device_mut(&mut self) -> &mut Self::BusDevice {
256 &mut self.bus
257 }
258
259 fn bus_device_ref(&self) -> &Self::BusDevice {
260 &self.bus
261 }
262
263 fn into_bus_device(self) -> Self::BusDevice {
264 self.bus
265 }
266
267 fn reset<C: Cpu>(&mut self, cpu: &mut C, hard: bool) {
268 if hard {
269 cpu.reset();
270 self.bus.reset(self.tsc.into());
271 self.memory.reset();
272 }
273 else {
274 const DEBUG: Option<CpuDebugFn> = None;
275 let mut vtsc = VFrameTsCounter::from_vframe_ts(VFrameTs::<V>::default(), UlaMemoryContention);
276 let _ = cpu.execute_instruction(self, &mut vtsc, DEBUG, opconsts::RST_00H_OPCODE);
277 }
278 }
279
280 fn nmi<C: Cpu>(&mut self, cpu: &mut C) -> bool {
281 self.ula_nmi(cpu)
282 }
283
284 fn execute_next_frame<C: Cpu>(&mut self, cpu: &mut C) {
285 while !self.ula_execute_next_frame_with_breaks(cpu) {}
286 }
287
288 fn ensure_next_frame(&mut self) {
289 self.ensure_next_frame_vtsc();
290 }
291
292 fn execute_single_step<C: Cpu, F: FnOnce(CpuDebug)>(
293 &mut self,
294 cpu: &mut C,
295 debug: Option<F>
296 ) -> Result<(),()>
297 {
298 self.ula_execute_single_step(cpu, debug)
299 }
300}
301
302impl<M, B, X, V> UlaControlExt for Ula<M, B, X, V>
303 where M: ZxMemory,
304 B: BusDevice,
305 B::Timestamp: From<VFrameTs<V>>,
306 V: VideoFrame
307{
308 fn prepare_next_frame<C: MemoryContention>(
309 &mut self,
310 mut vtsc: VFrameTsCounter<V, C>
311 ) -> VFrameTsCounter<V, C>
312 {
313 self.bus.next_frame(VFrameTs::<V>::EOF.into());
314 self.frames += Wrapping(1);
315 self.cleanup_video_frame_data();
316 self.cleanup_earmic_frame_data();
317 vtsc.wrap_frame();
318 self.tsc = vtsc.into();
319 vtsc
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use crate::memory::Memory64k;
326 use crate::video::Video;
327 use super::*;
328 type TestUla = UlaPAL::<Memory64k>;
329
330 #[test]
331 fn test_ula() {
332 assert_eq!(<TestUla as Video>::VideoFrame::FRAME_TSTATES_COUNT, 69888);
333 let ula = TestUla::default();
334 let clock = ula.current_video_clock();
335 for addr in 0x4000..0x8000 {
336 assert_eq!(clock.is_contended_address(addr), true);
337 }
338 for addr in (0x0000..0x4000).chain(0x8000..=0xFFFF) {
339 assert_eq!(clock.is_contended_address(addr), false);
340 }
341 }
342}