1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
/*
    Copyright (C) 2020-2022  Rafal Michalski

    This file is part of SPECTRUSTY, a Rust library for building emulators.

    For the full copyright notice, see the lib.rs file.
*/
//! Chipset emulation building blocks.
use core::num::NonZeroU32;
use core::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;

use z80emu::{CpuDebug, Cpu, host::Result};

use crate::bus::BusDevice;
use crate::clock::FTs;
use crate::memory::{ZxMemory, MemoryExtension};

mod flags;
pub use flags::*;

/// A trait for directly accessing an emulated memory implementation and memory extensions.
pub trait MemoryAccess {
    type Memory: ZxMemory;
    type MemoryExt: MemoryExtension;
    /// Returns a read-only reference to the memory extension.
    fn memory_ext_ref(&self) -> &Self::MemoryExt;
    /// Returns a mutable reference to the memory extension.
    fn memory_ext_mut(&mut self) -> &mut Self::MemoryExt;
    /// Returns a reference to the memory.
    fn memory_ref(&self) -> &Self::Memory;    
    /// Returns a mutable reference to the memory.
    fn memory_mut(&mut self) -> &mut Self::Memory;
    /// Returns mutable references to both the memory and the memory extension.
    fn memory_with_ext_mut(&mut self) -> (&mut Self::Memory, &mut Self::MemoryExt);
}

/// The trait for reading and modifying the state of frame and cycle counters.
pub trait FrameState {
    /// Returns the value of the current execution frame counter. The [FrameState] implementation should
    /// count passing frames infinitely wrapping at 2^64.
    fn current_frame(&self) -> u64;
    /// Sets the frame counter to the specified value.
    fn set_frame_counter(&mut self, fc: u64);
    /// Returns a normalized frame counter and a T-state counter as a tuple.
    ///
    /// T-states are counted from 0 at the start of each frame.
    /// This method never returns the T-state counter value below 0 or past the frame counter limit.
    fn frame_tstate(&self) -> (u64, FTs);
    /// Returns the current value of the T-state counter.
    /// 
    /// Unlike in [FrameState::frame_tstate], values return by this method can sometimes be negative as well as
    /// exceeding the maximum number of T-states per frame.
    fn current_tstate(&self) -> FTs;
    /// Sets the T-state counter to the specified value modulo `<Self as Video>::FRAME_TSTATES_COUNT`.
    fn set_frame_tstate(&mut self, ts: FTs);
    /// Returns `true` if the value of the current T-state counter has reached a certain arbitrary limit which
    /// is very close to the maximum number of T-states per frame.
    fn is_frame_over(&self) -> bool;
}

/// This trait provides the interface for running the emulation and accessing instances of peripheral devices.
///
/// It's being implemented by the emulators of core chipsets.
pub trait ControlUnit {
    /// A type of a chain of emulated peripheral devices should be declared here.
    ///
    /// This determines which devices, the emulated computer will be able to interact with.
    ///
    /// An associated constant: [BusDevice::Timestamp] must match the associated type declared for
    /// implementations of [z80emu] traits such as [Io::Timestamp] or [Memory::Timestamp].
    ///
    /// [z80emu]: crate::z80emu::host
    /// [Io::Timestamp]: crate::z80emu::Io::Timestamp
    /// [Memory::Timestamp]: crate::z80emu::Memory::Timestamp
    type BusDevice: BusDevice;
    /// Returns a mutable reference to the instance of the first bus device in the device chain.
    fn bus_device_mut(&mut self) -> &mut Self::BusDevice;
    /// Returns a reference to the the instance of the first bus device in the device chain.
    fn bus_device_ref(&self) -> &Self::BusDevice;
    /// Destructs self and returns the instance of the bus device.
    fn into_bus_device(self) -> Self::BusDevice;
    /// Performs a system reset.
    ///
    /// When `hard` is:
    /// * `true` emulates a **RESET** signal being active for the `cpu` and all bus devices.
    /// * `false` executes a `RST 0` instruction on the `cpu` but without forwarding the clock counter.
    ///
    /// In any case, this operation is always instant.
    fn reset<C: Cpu>(&mut self, cpu: &mut C, hard: bool);
    /// Triggers a non-maskable interrupt. Returns `true` if **NMI** was accepted.
    ///
    /// Returns `false` when the `cpu` has just executed an `EI` instruction or one of `0xDD`, `0xFD` prefixes.
    /// In this instance, calling this method is a no-op, and it returns `false`.
    ///
    /// For more details see [z80emu::Cpu::nmi][crate::z80emu::Cpu::nmi].
    fn nmi<C: Cpu>(&mut self, cpu: &mut C) -> bool;
    /// Conditionally prepares the internal state for the next frame and executes instructions on the `cpu`
    /// as fast as possible, until the near end of that frame.
    fn execute_next_frame<C: Cpu>(&mut self, cpu: &mut C);
    /// Conditionally prepares the internal state for the next frame, advances the frame counter, and wraps
    /// the T-state counter if it is near the end of a frame.
    ///
    /// This method should be called after all side effects (e.g. video and audio rendering) have been taken care of.
    /// Usually, implementations will clear internal data from a previous frame.
    ///
    /// Both [ControlUnit::execute_next_frame] and [ControlUnit::execute_single_step] invoke this method internally,
    /// so the only reason to call this method from the emulator program would be to make sure internal buffers
    /// are empty before feeding the implementation with external data that will be consumed by devices during
    /// the next frame.
    fn ensure_next_frame(&mut self);
    /// Executes a single instruction on the `cpu` with the option to pass a debugging function.
    ///
    /// If the T-state counter value is near the end of a frame, prepares the internal state for the next frame
    /// before executing the next instruction.
    fn execute_single_step<C: Cpu,
                           F: FnOnce(CpuDebug)>(
            &mut self,
            cpu: &mut C,
            debug: Option<F>
    ) -> Result<(), ()>;
}

/// A trait for reading the MIC line output.
pub trait MicOut<'a> {
    type PulseIter: Iterator<Item=NonZeroU32> + 'a;
    /// Returns a frame buffered MIC output as a pulse iterator.
    fn mic_out_pulse_iter(&'a self) -> Self::PulseIter;
}

/// A trait for feeding the EAR line input.
pub trait EarIn {
    /// Sets `EAR IN` bit state after the provided interval in ∆ T-states counted from the last recorded change.
    fn set_ear_in(&mut self, ear_in: bool, delta_fts: u32);
    /// Feeds the `EAR IN` buffer with changes.
    ///
    /// The provided iterator should yield time intervals measured in T-state ∆ differences after which the state
    /// of the `EAR IN` bit should be toggled.
    ///
    /// `max_frames_threshold` may be optionally provided as a number of frames to limit the buffered changes.
    /// This is useful if the given iterator provides data largely exceeding the duration of a single frame.
    fn feed_ear_in<I: Iterator<Item=NonZeroU32>>(&mut self, fts_deltas: I, max_frames_threshold: Option<usize>);
    /// Removes all buffered so far `EAR IN` changes.
    ///
    /// Changes are usually consumed only when a call is made to [crate::chip::ControlUnit::ensure_next_frame].
    /// Provide the current value of `EAR IN` bit as `ear_in`.
    ///
    /// This may be useful when tape data is already buffered but the user decided to stop the tape playback
    /// immediately.
    fn purge_ear_in_changes(&mut self, ear_in: bool);
    /// Returns the counter of how many times the EAR input line was read since the beginning of the current frame.
    ///
    /// This can be used to help to implement the autoloading of tape data.
    fn read_ear_in_count(&self) -> u32;
    /// Returns the current mode.
    fn read_ear_mode(&self) -> ReadEarMode {
        ReadEarMode::Clear
    }
    /// Changes the current mode.
    fn set_read_ear_mode(&mut self, _mode: ReadEarMode) {}
}

/// A helper trait for accessing parameters of well-known host configurations.
pub trait HostConfig {
    /// The number of CPU cycles (T-states) per second.
    const CPU_HZ: u32;
    /// The number of CPU cycles (T-states) in a single execution frame.
    const FRAME_TSTATES: FTs;
    /// Returns the CPU rate (T-states / second) after multiplying it by the `multiplier`.
    #[inline]
    fn effective_cpu_rate(multiplier: f64) -> f64 {
        Self::CPU_HZ as f64 * multiplier
    }
    /// Returns the duration of a single execution frame in nanoseconds after multiplying
    /// the CPU rate by the `multiplier`.
    #[inline]
    fn effective_frame_duration_nanos(multiplier: f64) -> u32 {
        let cpu_rate = Self::effective_cpu_rate(multiplier).round() as u32;
        nanos_from_frame_tc_cpu_hz(Self::FRAME_TSTATES as u32, cpu_rate) as u32
    }
    /// Returns the duration of a single execution frame after multiplying the CPU rate by
    /// the `multiplier`.
    #[inline]
    fn effective_frame_duration(multiplier: f64) -> Duration {
        let cpu_rate = Self::effective_cpu_rate(multiplier).round() as u32;
        duration_from_frame_tc_cpu_hz(Self::FRAME_TSTATES as u32, cpu_rate)
    }
    /// Returns the duration of a single execution frame in nanoseconds.
    #[inline]
    fn frame_duration_nanos() -> u32 {
        nanos_from_frame_tc_cpu_hz(Self::FRAME_TSTATES as u32, Self::CPU_HZ) as u32
    }
    /// Returns the duration of a single execution frame.
    #[inline]
    fn frame_duration() -> Duration {
        duration_from_frame_tc_cpu_hz(Self::FRAME_TSTATES as u32, Self::CPU_HZ)
    }
}

/// A generic trait, useful for chipset implementations based on other underlying implementations.
pub trait InnerAccess {
    type Inner;
    /// Returns a reference to the inner chipset.
    fn inner_ref(&self) -> &Self::Inner;
    /// Returns a mutable reference to the inner chipset.
    fn inner_mut(&mut self) -> &mut Self::Inner;
    /// Destructs `self` and returns the inner chipset.
    fn into_inner(self) -> Self::Inner;
}

// pub const fn duration_from_frame_time(frame_time: f64) -> std::time::Duration {
//     const NANOS_PER_SEC: f64 = 1_000_000_000.0;
//     let nanos: u64 = (frame_time * NANOS_PER_SEC) as u64;
//     std::time::Duration::from_nanos(nanos)
// }

/// Returns the number of nanoseconds from the number of T-states in a single frame and a CPU clock rate.
pub const fn nanos_from_frame_tc_cpu_hz(frame_ts_count: u32, cpu_hz: u32) -> u64 {
    const NANOS_PER_SEC: u64 = 1_000_000_000;
    frame_ts_count as u64 * NANOS_PER_SEC / cpu_hz as u64
}

/// Returns a duration from the number of T-states in a single frame and a CPU clock rate.
pub const fn duration_from_frame_tc_cpu_hz(frame_ts_count: u32, cpu_hz: u32) -> Duration {
    let nanos = nanos_from_frame_tc_cpu_hz(frame_ts_count, cpu_hz);
    Duration::from_nanos(nanos)
}

/// A tool for synchronizing emulation with a running thread.
#[cfg(not(target_arch = "wasm32"))]
pub struct ThreadSyncTimer {
    /// The start time of a current synchronization period.
    pub time: Instant,
    /// The desired duration of a single synchronization period.
    pub frame_duration: Duration,
}

#[cfg(not(target_arch = "wasm32"))]
// #[allow(clippy::new_without_default)]
impl ThreadSyncTimer {
    /// Pass the real time duration of a desired synchronization period (usually a duration of a video frame).
    pub fn new(frame_duration_nanos: u32) -> Self {
        let frame_duration = Duration::from_nanos(frame_duration_nanos as u64);
        ThreadSyncTimer { time: Instant::now(), frame_duration }
    }
    /// Sets [ThreadSyncTimer::frame_duration] from the provided `frame_duration_nanos`.
    pub fn set_frame_duration(&mut self, frame_duration_nanos: u32) {
        self.frame_duration = Duration::from_nanos(frame_duration_nanos as u64);
    }
    /// Restarts the synchronization period. Useful e.g. for resuming paused emulation.
    pub fn restart(&mut self) -> Instant {
        core::mem::replace(&mut self.time, Instant::now())
    }
    /// Calculates the difference between the desired duration of a synchronization period
    /// and the real time that has passed from the start of the current period and levels
    /// the difference by calling [std::thread::sleep].
    ///
    /// This method may be called at the end of each iteration of an emulation loop to
    /// synchronize the running thread with a desired iteration period.
    ///
    /// Returns `Ok` if the thread is in sync with the emulation. In this instance the value
    /// of [ThreadSyncTimer::frame_duration] is being added to [ThreadSyncTimer::time] to mark
    /// the beginning of a new period.
    ///
    /// Returns `Err(missed_periods)` if the elapsed time exceeds the desired period duration.
    /// In this intance the start of a new period is set to [Instant::now].
    pub fn synchronize_thread_to_frame(&mut self) -> core::result::Result<(), u32> {
        let frame_duration = self.frame_duration;
        let now = Instant::now();
        let elapsed = now.duration_since(self.time);
        if let Some(duration) = frame_duration.checked_sub(elapsed) {
            std::thread::sleep(duration);
            self.time += frame_duration;
            Ok(())
        }
        else {
            let missed_frames = (elapsed.as_secs_f64() / frame_duration.as_secs_f64()).trunc() as u32;
            self.time = now;
            Err(missed_frames)
        }
    }
    /// Returns `Some(excess_duration)` if the time elapsed from the beginning of the current
    /// period exceeds or is equal to the desired duration of a synchronization period.
    /// Otherwise returns `None`.
    ///
    /// The value returned is the excess time that has elapsed above the desired duration.
    /// If the time elapsed equals to the `frame_duration` the returned value equals to zero.
    ///
    /// In case `Some` variant is returned the value of [ThreadSyncTimer::frame_duration] is
    /// being added to [ThreadSyncTimer::time] to mark the beginning of a new period.
    pub fn check_frame_elapsed(&mut self) -> Option<Duration> {
        let frame_duration = self.frame_duration;
        let elapsed = self.time.elapsed();
        if let Some(duration) = elapsed.checked_sub(frame_duration) {
            self.time += frame_duration;
            return Some(duration)
        }
        None
    }
}

#[cfg(target_arch = "wasm32")]
pub struct AnimationFrameSyncTimer {
    pub time: f64,
    pub frame_duration_millis: f64,
}

#[cfg(target_arch = "wasm32")]
const NANOS_PER_MILLISEC: f64 = 1_000_000.0;

#[cfg(target_arch = "wasm32")]
impl AnimationFrameSyncTimer {
    const MAX_FRAME_LAG_THRESHOLD: u32 = 10;
    /// Pass the `DOMHighResTimeStamp` as a first and the real time duration of a desired synchronization
    /// period (usually a duration of a video frame) as a second argument.
    pub fn new(time: f64, frame_duration_nanos: u32) -> Self {
        let frame_duration_millis = frame_duration_nanos as f64 / NANOS_PER_MILLISEC;
        AnimationFrameSyncTimer { time, frame_duration_millis }
    }
     /// Sets [AnimationFrameSyncTimer::frame_duration_millis] from the provided `frame_duration_nanos`.
    pub fn set_frame_duration(&mut self, frame_duration_nanos: u32) {
        self.frame_duration_millis = frame_duration_nanos as f64 / NANOS_PER_MILLISEC;
    }
   /// Restarts the synchronizaiton period. Useful for e.g. resuming paused emulation.
    ///
    /// Pass the value of `DOMHighResTimeStamp` as the argument.
    pub fn restart(&mut self, time: f64) -> f64 {
        core::mem::replace(&mut self.time, time)
    }
    /// Returns `Ok(number_of_frames)` required to be rendered to synchronize with the native animation frame rate.
    ///
    /// Pass the value of DOMHighResTimeStamp obtained from a callback argument invoked by `request_animation_frame`.
    ///
    /// Returns `Err(time)` if the time elapsed between this and previous call to this method is larger
    /// than the duration of number of frames specified by [MAX_FRAME_LAG_THRESHOLD]. In this instance the
    /// previous value of `time` is returned.
    pub fn num_frames_to_synchronize(&mut self, time: f64) -> core::result::Result<u32, f64> {
        let frame_duration_millis = self.frame_duration_millis;
        let time_elapsed = time - self.time;
        if time_elapsed > frame_duration_millis {
            let nframes = (time_elapsed / frame_duration_millis).trunc() as u32;
            if nframes <= Self::MAX_FRAME_LAG_THRESHOLD {
                self.time = frame_duration_millis.mul_add(nframes as f64, self.time);
                Ok(nframes)
            }
            else {
                Err(self.restart(time))
            }
        }
        else {
            Ok(0)
        }
            
    }
    /// Returns `Some(excess_duration_millis)` if the `time` elapsed from the beginning of the current
    /// period exceeds or is equal to the desired duration of a synchronization period.
    /// Otherwise returns `None`.
    ///
    /// The value returned is the excess time that has elapsed above the desired duration.
    /// If the time elapsed equals to the `frame_duration` the returned value equals to zero.
    ///
    /// In case `Some` variant is returned the value of [AnimationFrameSyncTimer::frame_duration] is
    /// being added to [AnimationFrameSyncTimer::time] to mark the beginning of a new period.
   pub fn check_frame_elapsed(&mut self, time: f64) -> Option<f64> {
        let frame_duration_millis = self.frame_duration_millis;
        let elapsed = time - self.time;
        let excess_duration_millis = elapsed - frame_duration_millis;
        if excess_duration_millis >= 0.0 {
            self.time += frame_duration_millis;
            return Some(excess_duration_millis)
        }
        None
    }
}

impl<U, I> FrameState for U
    where U: InnerAccess<Inner=I>,
          I: FrameState
{
    fn current_frame(&self) -> u64 {
        self.inner_ref().current_frame()
    }

    fn set_frame_counter(&mut self, fc: u64) {
        self.inner_mut().set_frame_counter(fc)
    }

    fn frame_tstate(&self) -> (u64, FTs) {
        self.inner_ref().frame_tstate()
    }

    fn current_tstate(&self) -> FTs {
        self.inner_ref().current_tstate()
    }

    fn set_frame_tstate(&mut self, ts: FTs) {
        self.inner_mut().set_frame_tstate(ts)
    }

    fn is_frame_over(&self) -> bool {
        self.inner_ref().is_frame_over()
    }
}