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::*;
pub trait MemoryAccess {
type Memory: ZxMemory;
type MemoryExt: MemoryExtension;
fn memory_ext_ref(&self) -> &Self::MemoryExt;
fn memory_ext_mut(&mut self) -> &mut Self::MemoryExt;
fn memory_ref(&self) -> &Self::Memory;
fn memory_mut(&mut self) -> &mut Self::Memory;
fn memory_with_ext_mut(&mut self) -> (&mut Self::Memory, &mut Self::MemoryExt);
}
pub trait FrameState {
fn current_frame(&self) -> u64;
fn set_frame_counter(&mut self, fc: u64);
fn frame_tstate(&self) -> (u64, FTs);
fn current_tstate(&self) -> FTs;
fn set_frame_tstate(&mut self, ts: FTs);
fn is_frame_over(&self) -> bool;
}
pub trait ControlUnit {
type BusDevice: BusDevice;
fn bus_device_mut(&mut self) -> &mut Self::BusDevice;
fn bus_device_ref(&self) -> &Self::BusDevice;
fn into_bus_device(self) -> Self::BusDevice;
fn reset<C: Cpu>(&mut self, cpu: &mut C, hard: bool);
fn nmi<C: Cpu>(&mut self, cpu: &mut C) -> bool;
fn execute_next_frame<C: Cpu>(&mut self, cpu: &mut C);
fn ensure_next_frame(&mut self);
fn execute_single_step<C: Cpu,
F: FnOnce(CpuDebug)>(
&mut self,
cpu: &mut C,
debug: Option<F>
) -> Result<(), ()>;
}
pub trait MicOut<'a> {
type PulseIter: Iterator<Item=NonZeroU32> + 'a;
fn mic_out_pulse_iter(&'a self) -> Self::PulseIter;
}
pub trait EarIn {
fn set_ear_in(&mut self, ear_in: bool, delta_fts: u32);
fn feed_ear_in<I: Iterator<Item=NonZeroU32>>(&mut self, fts_deltas: I, max_frames_threshold: Option<usize>);
fn purge_ear_in_changes(&mut self, ear_in: bool);
fn read_ear_in_count(&self) -> u32;
fn read_ear_mode(&self) -> ReadEarMode {
ReadEarMode::Clear
}
fn set_read_ear_mode(&mut self, _mode: ReadEarMode) {}
}
pub trait HostConfig {
const CPU_HZ: u32;
const FRAME_TSTATES: FTs;
#[inline]
fn effective_cpu_rate(multiplier: f64) -> f64 {
Self::CPU_HZ as f64 * 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
}
#[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)
}
#[inline]
fn frame_duration_nanos() -> u32 {
nanos_from_frame_tc_cpu_hz(Self::FRAME_TSTATES as u32, Self::CPU_HZ) as u32
}
#[inline]
fn frame_duration() -> Duration {
duration_from_frame_tc_cpu_hz(Self::FRAME_TSTATES as u32, Self::CPU_HZ)
}
}
pub trait InnerAccess {
type Inner;
fn inner_ref(&self) -> &Self::Inner;
fn inner_mut(&mut self) -> &mut Self::Inner;
fn into_inner(self) -> Self::Inner;
}
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
}
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)
}
#[cfg(not(target_arch = "wasm32"))]
pub struct ThreadSyncTimer {
pub time: Instant,
pub frame_duration: Duration,
}
#[cfg(not(target_arch = "wasm32"))]
impl ThreadSyncTimer {
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 }
}
pub fn set_frame_duration(&mut self, frame_duration_nanos: u32) {
self.frame_duration = Duration::from_nanos(frame_duration_nanos as u64);
}
pub fn restart(&mut self) -> Instant {
core::mem::replace(&mut self.time, 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)
}
}
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;
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 }
}
pub fn set_frame_duration(&mut self, frame_duration_nanos: u32) {
self.frame_duration_millis = frame_duration_nanos as f64 / NANOS_PER_MILLISEC;
}
pub fn restart(&mut self, time: f64) -> f64 {
core::mem::replace(&mut self.time, time)
}
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)
}
}
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()
}
}