From 9ff431ebc61511c33bd2353946739a7fa3578d37 Mon Sep 17 00:00:00 2001 From: transistor Date: Sat, 24 Feb 2024 13:02:09 -0800 Subject: [PATCH] Split clocks into `femtos` crate --- Cargo.lock | 33 + emulator/core/Cargo.toml | 1 + emulator/core/src/clock.rs | 335 ---------- emulator/core/src/devices.rs | 29 +- emulator/core/src/host/gfx.rs | 6 +- emulator/core/src/host/traits.rs | 19 +- emulator/core/src/lib.rs | 4 - emulator/core/src/memory.rs | 31 +- emulator/core/src/signals.rs | 8 +- emulator/core/src/system.rs | 17 +- emulator/cpus/m68k/Cargo.toml | 2 + emulator/cpus/m68k/src/execute.rs | 14 +- emulator/cpus/m68k/src/memory.rs | 14 +- emulator/cpus/m68k/src/state.rs | 7 +- emulator/cpus/m68k/src/tests.rs | 51 +- emulator/cpus/m68k/tests/decode_tests.rs | 8 +- emulator/cpus/m68k/tests/execute_tests.rs | 8 +- .../cpus/m68k/tests/musashi_timing_tests.rs | 10 +- emulator/cpus/m68k/tests/timing_tests.rs | 8 +- emulator/cpus/z80/Cargo.toml | 2 + emulator/cpus/z80/src/decode.rs | 10 +- emulator/cpus/z80/src/execute.rs | 10 +- emulator/cpus/z80/src/state.rs | 9 +- emulator/cpus/z80/tests/decode_tests.rs | 4 +- emulator/cpus/z80/tests/execute_tests.rs | 4 +- emulator/frontends/common/Cargo.toml | 2 + emulator/frontends/common/src/audio.rs | 22 +- emulator/frontends/common/src/cpal.rs | 6 +- emulator/frontends/console/Cargo.toml | 3 +- .../frontends/console/src/bin/moa-bench.rs | 3 +- emulator/frontends/console/src/lib.rs | 5 +- emulator/frontends/minifb/Cargo.toml | 1 + .../frontends/minifb/src/bin/moa-synth.rs | 12 +- emulator/frontends/minifb/src/lib.rs | 5 +- emulator/frontends/pixels/Cargo.lock | 16 + emulator/frontends/pixels/Cargo.toml | 1 + emulator/frontends/pixels/src/web.rs | 9 +- emulator/libraries/femtos/Cargo.toml | 14 + emulator/libraries/femtos/LICENSE-APACHE | 201 ++++++ emulator/libraries/femtos/LICENSE-MIT | 25 + emulator/libraries/femtos/README.md | 8 + emulator/libraries/femtos/src/lib.rs | 576 ++++++++++++++++++ emulator/peripherals/generic/Cargo.toml | 2 + emulator/peripherals/generic/src/ata.rs | 19 +- emulator/peripherals/mos/Cargo.toml | 2 + emulator/peripherals/mos/src/mos6522.rs | 20 +- emulator/peripherals/motorola/Cargo.toml | 2 + emulator/peripherals/motorola/src/mc68681.rs | 18 +- emulator/peripherals/yamaha/Cargo.toml | 2 + emulator/peripherals/yamaha/src/sn76489.rs | 27 +- emulator/peripherals/yamaha/src/ym2612.rs | 34 +- emulator/peripherals/zilog/Cargo.toml | 2 + emulator/peripherals/zilog/src/z8530.rs | 20 +- emulator/systems/computie/Cargo.toml | 2 + emulator/systems/computie/src/system.rs | 4 +- emulator/systems/genesis/Cargo.toml | 2 + .../genesis/src/peripherals/controllers.rs | 29 +- .../genesis/src/peripherals/coprocessor.rs | 24 +- .../systems/genesis/src/peripherals/ym7101.rs | 39 +- emulator/systems/genesis/src/system.rs | 4 +- emulator/systems/macintosh/Cargo.toml | 2 + .../systems/macintosh/src/peripherals/iwm.rs | 22 +- .../macintosh/src/peripherals/mainboard.rs | 21 +- .../macintosh/src/peripherals/video.rs | 8 +- emulator/systems/macintosh/src/system.rs | 4 +- emulator/systems/trs80/Cargo.toml | 2 + .../systems/trs80/src/peripherals/model1.rs | 30 +- emulator/systems/trs80/src/system.rs | 4 +- tests/harte_tests/Cargo.toml | 2 + tests/harte_tests/src/main.rs | 3 +- tests/rad_tests/Cargo.toml | 1 + tests/rad_tests/src/main.rs | 3 +- 72 files changed, 1264 insertions(+), 643 deletions(-) delete mode 100644 emulator/core/src/clock.rs create mode 100644 emulator/libraries/femtos/Cargo.toml create mode 100644 emulator/libraries/femtos/LICENSE-APACHE create mode 100644 emulator/libraries/femtos/LICENSE-MIT create mode 100644 emulator/libraries/femtos/README.md create mode 100644 emulator/libraries/femtos/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index da94fb7..52af98d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -460,6 +460,10 @@ dependencies = [ "instant", ] +[[package]] +name = "femtos" +version = "0.1.0" + [[package]] name = "flate2" version = "1.0.26" @@ -481,6 +485,7 @@ name = "harte_tests" version = "0.1.0" dependencies = [ "clap 3.2.25", + "femtos", "flate2", "moa_core", "moa_m68k", @@ -728,6 +733,8 @@ name = "moa_common" version = "0.1.0" dependencies = [ "cpal", + "femtos", + "log", "moa_core", "nix 0.25.1", ] @@ -737,6 +744,7 @@ name = "moa_console" version = "0.1.0" dependencies = [ "clap 4.3.3", + "femtos", "log", "moa_common", "moa_core", @@ -752,6 +760,7 @@ dependencies = [ name = "moa_core" version = "0.1.0" dependencies = [ + "femtos", "log", ] @@ -759,6 +768,8 @@ dependencies = [ name = "moa_m68k" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_parsing", ] @@ -768,6 +779,7 @@ name = "moa_minifb" version = "0.1.0" dependencies = [ "clap 4.3.3", + "femtos", "log", "minifb", "moa_common", @@ -791,6 +803,8 @@ dependencies = [ name = "moa_peripherals_generic" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", ] @@ -798,6 +812,8 @@ dependencies = [ name = "moa_peripherals_mos" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", ] @@ -805,6 +821,8 @@ dependencies = [ name = "moa_peripherals_motorola" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", ] @@ -812,7 +830,9 @@ dependencies = [ name = "moa_peripherals_yamaha" version = "0.1.0" dependencies = [ + "femtos", "lazy_static", + "log", "moa_audio", "moa_core", ] @@ -821,6 +841,8 @@ dependencies = [ name = "moa_peripherals_zilog" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", ] @@ -828,6 +850,8 @@ dependencies = [ name = "moa_systems_computie" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_m68k", "moa_peripherals_generic", @@ -838,6 +862,8 @@ dependencies = [ name = "moa_systems_genesis" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_m68k", "moa_peripherals_yamaha", @@ -848,6 +874,8 @@ dependencies = [ name = "moa_systems_macintosh" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_m68k", "moa_peripherals_mos", @@ -858,6 +886,8 @@ dependencies = [ name = "moa_systems_trs80" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_z80", ] @@ -866,6 +896,8 @@ dependencies = [ name = "moa_z80" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", ] @@ -1174,6 +1206,7 @@ name = "rad_tests" version = "0.1.0" dependencies = [ "clap 3.2.25", + "femtos", "flate2", "moa_core", "moa_z80", diff --git a/emulator/core/Cargo.toml b/emulator/core/Cargo.toml index 6e53376..3989536 100644 --- a/emulator/core/Cargo.toml +++ b/emulator/core/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" [dependencies] log = "0.4" +femtos = { path = "../libraries/femtos" } diff --git a/emulator/core/src/clock.rs b/emulator/core/src/clock.rs deleted file mode 100644 index f8a0013..0000000 --- a/emulator/core/src/clock.rs +++ /dev/null @@ -1,335 +0,0 @@ - -use std::time::Duration; -use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; - -/// Type to use for storing femtoseconds -/// -/// In webassembly, using u128 results in exceedingly slow runtimes, so we use u64 instead -/// which is enough for 5 hours of simulation time. -#[cfg(not(target_arch = "wasm32"))] -type Femtos = u128; -#[cfg(target_arch = "wasm32")] -type Femtos = u64; - -/// Represents a duration of time in femtoseconds -/// -/// The `ClockDuration` type is used to represent lengths of time and is -/// intentionally similar to `std::time::Duration`, but which records -/// time as femtoseconds to keep accurancy when dealing with partial -/// nanosecond clock divisons. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct ClockDuration { - femtos: Femtos, -} - -impl ClockDuration { - pub const ZERO: Self = Self::from_femtos(0); - pub const MAX: Self = Self::from_femtos(Femtos::MAX); - - pub const FEMTOS_PER_SEC: Femtos = 1_000_000_000_000_000; - pub const FEMTOS_PER_MILLISEC: Femtos = 1_000_000_000_000; - pub const FEMTOS_PER_MICROSEC: Femtos = 1_000_000_000; - pub const FEMTOS_PER_NANOSEC: Femtos = 1_000_000; - pub const FEMTOS_PER_PICOSEC: Femtos = 1_000; - - #[inline] - pub const fn from_secs(secs: u64) -> Self { - Self { - femtos: secs as Femtos * Self::FEMTOS_PER_SEC, - } - } - - #[inline] - pub const fn from_millis(millisecs: u64) -> Self { - Self { - femtos: millisecs as Femtos * Self::FEMTOS_PER_MILLISEC, - } - } - - #[inline] - pub const fn from_micros(microsecs: u64) -> Self { - Self { - femtos: microsecs as Femtos * Self::FEMTOS_PER_MICROSEC, - } - } - - #[inline] - pub const fn from_nanos(nanosecs: u64) -> Self { - Self { - femtos: nanosecs as Femtos * Self::FEMTOS_PER_NANOSEC, - } - } - - #[inline] - pub const fn from_picos(picosecs: u128) -> Self { - Self { - femtos: picosecs as Femtos * Self::FEMTOS_PER_PICOSEC, - } - } - - #[inline] - pub const fn from_femtos(femtos: Femtos) -> Self { - Self { - femtos, - } - } - - #[inline] - pub const fn as_secs(self) -> u64 { - (self.femtos / Self::FEMTOS_PER_SEC) as u64 - } - - #[inline] - pub const fn as_millis(self) -> u64 { - (self.femtos / Self::FEMTOS_PER_MILLISEC) as u64 - } - - #[inline] - pub const fn as_micros(self) -> u64 { - (self.femtos / Self::FEMTOS_PER_MICROSEC) as u64 - } - - #[inline] - pub const fn as_nanos(self) -> u64 { - (self.femtos / Self::FEMTOS_PER_NANOSEC) as u64 - } - - #[inline] - #[allow(clippy::unnecessary_cast)] - pub const fn as_picos(self) -> u128 { - (self.femtos / Self::FEMTOS_PER_PICOSEC) as u128 - } - - #[inline] - pub const fn as_femtos(self) -> Femtos { - self.femtos - } - - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - match self.femtos.checked_add(rhs.femtos) { - Some(femtos) => Some(Self::from_femtos(femtos)), - None => None, - } - } - - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - match self.femtos.checked_sub(rhs.femtos) { - Some(femtos) => Some(Self::from_femtos(femtos)), - None => None, - } - } -} - -impl Add for ClockDuration { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - self.checked_add(rhs).expect("clock duration overflow during addition") - } -} - -impl AddAssign for ClockDuration { - fn add_assign(&mut self, rhs: Self) { - *self = *self + rhs; - } -} - -impl Sub for ClockDuration { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - self.checked_sub(rhs).expect("clock duration overflow during subtraction") - } -} - -impl SubAssign for ClockDuration { - fn sub_assign(&mut self, rhs: Self) { - *self = *self - rhs; - } -} - -impl Mul for ClockDuration { - type Output = Self; - - fn mul(self, rhs: u64) -> Self::Output { - Self::from_femtos(self.femtos * rhs as Femtos) - } -} - -impl MulAssign for ClockDuration { - fn mul_assign(&mut self, rhs: u64) { - *self = Self::from_femtos(self.femtos * rhs as Femtos); - } -} - -impl Div for ClockDuration { - type Output = Self; - - fn div(self, rhs: u64) -> Self::Output { - Self::from_femtos(self.femtos / rhs as Femtos) - } -} - -impl DivAssign for ClockDuration { - fn div_assign(&mut self, rhs: u64) { - *self = Self::from_femtos(self.femtos / rhs as Femtos); - } -} - -impl Div for ClockDuration { - type Output = u64; - - fn div(self, rhs: ClockDuration) -> Self::Output { - (self.femtos / rhs.femtos) as u64 - } -} - - -impl From for Duration { - fn from(value: ClockDuration) -> Self { - Duration::from_nanos(value.as_nanos()) - } -} - -impl From for ClockDuration { - fn from(value: Duration) -> Self { - ClockDuration::from_nanos(value.as_nanos() as u64) - } -} - - -/// Represents time from the start of the simulation -/// -/// `ClockTime` is for representing the current running clock. It uses a -/// duration to represent the time from simulation start, and is monotonic. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct ClockTime(ClockDuration); - -impl ClockTime { - pub const START: Self = Self(ClockDuration::ZERO); - pub const FOREVER: Self = Self(ClockDuration::MAX); - - #[inline] - pub const fn as_duration(self) -> ClockDuration { - self.0 - } - - #[inline] - pub fn duration_since(self, other: Self) -> ClockDuration { - self.0 - other.0 - } - - #[inline] - pub const fn checked_add(self, duration: ClockDuration) -> Option { - match self.0.checked_add(duration) { - Some(duration) => Some(Self(duration)), - None => None, - } - } - - #[inline] - pub const fn checked_sub(self, duration: ClockDuration) -> Option { - match self.0.checked_sub(duration) { - Some(duration) => Some(Self(duration)), - None => None, - } - } -} - -impl Add for ClockTime { - type Output = Self; - - fn add(self, rhs: ClockDuration) -> Self::Output { - Self(self.0.add(rhs)) - } -} - -impl AddAssign for ClockTime { - fn add_assign(&mut self, rhs: ClockDuration) { - *self = Self(self.0.add(rhs)); - } -} - -/// Represents a frequency in Hz -/// -/// Clocks are usually given as a frequency, but durations are needed when dealing with clocks -/// and clock durations. This type makes it easier to create a clock of a given frequency and -/// convert it to a `ClockDuration` -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Frequency { - hertz: u32, -} - -impl Frequency { - #[inline] - pub const fn from_hz(hertz: u32) -> Self { - Self { - hertz, - } - } - - #[inline] - pub const fn from_khz(khz: u32) -> Self { - Self { - hertz: khz * 1_000, - } - } - - #[inline] - pub const fn from_mhz(mhz: u32) -> Self { - Self { - hertz: mhz * 1_000_000, - } - } - - #[inline] - pub const fn as_hz(self) -> u32 { - self.hertz - } - - #[inline] - pub const fn as_khz(self) -> u32 { - self.hertz / 1_000 - } - - #[inline] - pub const fn as_mhz(self) -> u32 { - self.hertz / 1_000_000 - } - - #[inline] - pub const fn period_duration(self) -> ClockDuration { - ClockDuration::from_femtos(ClockDuration::FEMTOS_PER_SEC / self.hertz as Femtos) - } -} - -impl Mul for Frequency { - type Output = Self; - - fn mul(self, rhs: u32) -> Self::Output { - Self::from_hz(self.hertz * rhs) - } -} - -impl MulAssign for Frequency { - fn mul_assign(&mut self, rhs: u32) { - *self = Self::from_hz(self.hertz * rhs); - } -} - -impl Div for Frequency { - type Output = Self; - - fn div(self, rhs: u32) -> Self::Output { - Self::from_hz(self.hertz / rhs) - } -} - -impl DivAssign for Frequency { - fn div_assign(&mut self, rhs: u32) { - *self = Self::from_hz(self.hertz / rhs); - } -} - diff --git a/emulator/core/src/devices.rs b/emulator/core/src/devices.rs index a76bd20..5b38e1a 100644 --- a/emulator/core/src/devices.rs +++ b/emulator/core/src/devices.rs @@ -2,8 +2,9 @@ use std::rc::Rc; use std::cell::{RefCell, RefMut, BorrowMutError}; use std::sync::atomic::{AtomicUsize, Ordering}; +use femtos::{Duration, Instant}; -use crate::{Error, System, ClockTime, ClockDuration}; +use crate::{Error, System}; /// A universal memory address used by the Addressable trait @@ -15,7 +16,7 @@ pub type Address = u64; /// with any device, the `on_error()` method will be called to display any state /// information that might be helpful for debugging. pub trait Steppable { - fn step(&mut self, system: &System) -> Result; + fn step(&mut self, system: &System) -> Result; fn on_error(&mut self, _system: &System) { } } @@ -28,73 +29,73 @@ pub trait Interruptable { /// A device that can be addressed to read data from or write data to the device. pub trait Addressable { fn size(&self) -> usize; - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error>; - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error>; + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error>; + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error>; #[inline] - fn read_u8(&mut self, clock: ClockTime, addr: Address) -> Result { + fn read_u8(&mut self, clock: Instant, addr: Address) -> Result { let mut data = [0; 1]; self.read(clock, addr, &mut data)?; Ok(data[0]) } #[inline] - fn read_beu16(&mut self, clock: ClockTime, addr: Address) -> Result { + fn read_beu16(&mut self, clock: Instant, addr: Address) -> Result { let mut data = [0; 2]; self.read(clock, addr, &mut data)?; Ok(read_beu16(&data)) } #[inline] - fn read_leu16(&mut self, clock: ClockTime, addr: Address) -> Result { + fn read_leu16(&mut self, clock: Instant, addr: Address) -> Result { let mut data = [0; 2]; self.read(clock, addr, &mut data)?; Ok(read_leu16(&data)) } #[inline] - fn read_beu32(&mut self, clock: ClockTime, addr: Address) -> Result { + fn read_beu32(&mut self, clock: Instant, addr: Address) -> Result { let mut data = [0; 4]; self.read(clock, addr, &mut data)?; Ok(read_beu32(&data)) } #[inline] - fn read_leu32(&mut self, clock: ClockTime, addr: Address) -> Result { + fn read_leu32(&mut self, clock: Instant, addr: Address) -> Result { let mut data = [0; 4]; self.read(clock, addr, &mut data)?; Ok(read_leu32(&data)) } #[inline] - fn write_u8(&mut self, clock: ClockTime, addr: Address, value: u8) -> Result<(), Error> { + fn write_u8(&mut self, clock: Instant, addr: Address, value: u8) -> Result<(), Error> { let data = [value]; self.write(clock, addr, &data) } #[inline] - fn write_beu16(&mut self, clock: ClockTime, addr: Address, value: u16) -> Result<(), Error> { + fn write_beu16(&mut self, clock: Instant, addr: Address, value: u16) -> Result<(), Error> { let mut data = [0; 2]; write_beu16(&mut data, value); self.write(clock, addr, &data) } #[inline] - fn write_leu16(&mut self, clock: ClockTime, addr: Address, value: u16) -> Result<(), Error> { + fn write_leu16(&mut self, clock: Instant, addr: Address, value: u16) -> Result<(), Error> { let mut data = [0; 2]; write_leu16(&mut data, value); self.write(clock, addr, &data) } #[inline] - fn write_beu32(&mut self, clock: ClockTime, addr: Address, value: u32) -> Result<(), Error> { + fn write_beu32(&mut self, clock: Instant, addr: Address, value: u32) -> Result<(), Error> { let mut data = [0; 4]; write_beu32(&mut data, value); self.write(clock, addr, &data) } #[inline] - fn write_leu32(&mut self, clock: ClockTime, addr: Address, value: u32) -> Result<(), Error> { + fn write_leu32(&mut self, clock: Instant, addr: Address, value: u32) -> Result<(), Error> { let mut data = [0; 4]; write_leu32(&mut data, value); self.write(clock, addr, &data) diff --git a/emulator/core/src/host/gfx.rs b/emulator/core/src/host/gfx.rs index 3a36618..ebb0603 100644 --- a/emulator/core/src/host/gfx.rs +++ b/emulator/core/src/host/gfx.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; +use femtos::Instant; -use crate::ClockTime; use crate::host::traits::ClockedQueue; pub const MASK_COLOUR: u32 = 0xFFFFFFFF; @@ -135,7 +135,7 @@ impl FrameSender { *self.encoding.lock().unwrap() } - pub fn add(&self, clock: ClockTime, frame: Frame) { + pub fn add(&self, clock: Instant, frame: Frame) { self.queue.push(clock, frame); } } @@ -155,7 +155,7 @@ impl FrameReceiver { *self.encoding.lock().unwrap() = encoding; } - pub fn latest(&self) -> Option<(ClockTime, Frame)> { + pub fn latest(&self) -> Option<(Instant, Frame)> { self.queue.pop_latest() } } diff --git a/emulator/core/src/host/traits.rs b/emulator/core/src/host/traits.rs index e85cf3d..42d382e 100644 --- a/emulator/core/src/host/traits.rs +++ b/emulator/core/src/host/traits.rs @@ -1,8 +1,9 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex}; +use femtos::Instant; -use crate::{ClockTime, Error}; +use crate::Error; use crate::host::gfx::FrameReceiver; use crate::host::audio::Sample; use crate::host::keys::KeyEvent; @@ -46,19 +47,19 @@ pub trait Tty { pub trait Audio { fn samples_per_second(&self) -> usize; - fn write_samples(&mut self, clock: ClockTime, buffer: &[Sample]); + fn write_samples(&mut self, clock: Instant, buffer: &[Sample]); } #[derive(Clone, Default)] -pub struct ClockedQueue(Arc>>, usize); +pub struct ClockedQueue(Arc>>, usize); impl ClockedQueue { pub fn new(max: usize) -> Self { Self(Arc::new(Mutex::new(VecDeque::new())), max) } - pub fn push(&self, clock: ClockTime, data: T) { + pub fn push(&self, clock: Instant, data: T) { let mut queue = self.0.lock().unwrap(); if queue.len() > self.1 { //log::warn!("dropping data from queue due to limit of {} items", self.1); @@ -67,19 +68,19 @@ impl ClockedQueue { queue.push_back((clock, data)); } - pub fn pop_next(&self) -> Option<(ClockTime, T)> { + pub fn pop_next(&self) -> Option<(Instant, T)> { self.0.lock().unwrap().pop_front() } - pub fn pop_latest(&self) -> Option<(ClockTime, T)> { + pub fn pop_latest(&self) -> Option<(Instant, T)> { self.0.lock().unwrap().drain(..).last() } - pub fn put_back(&self, clock: ClockTime, data: T) { + pub fn put_back(&self, clock: Instant, data: T) { self.0.lock().unwrap().push_front((clock, data)); } - pub fn peek_clock(&self) -> Option { + pub fn peek_clock(&self) -> Option { self.0.lock().unwrap().front().map(|(clock, _)| *clock) } @@ -96,6 +97,6 @@ impl Audio for DummyAudio { 48000 } - fn write_samples(&mut self, _clock: ClockTime, _buffer: &[Sample]) {} + fn write_samples(&mut self, _clock: Instant, _buffer: &[Sample]) {} } diff --git a/emulator/core/src/lib.rs b/emulator/core/src/lib.rs index 8066649..f1badcd 100644 --- a/emulator/core/src/lib.rs +++ b/emulator/core/src/lib.rs @@ -2,7 +2,6 @@ #[macro_use] mod error; -mod clock; mod debugger; mod devices; mod interrupts; @@ -12,9 +11,6 @@ mod system; pub mod host; -pub use log::{trace, debug, info, warn, error}; - -pub use crate::clock::{ClockTime, ClockDuration, Frequency}; pub use crate::debugger::{DebugControl, Debugger}; pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device}; pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable}; diff --git a/emulator/core/src/memory.rs b/emulator/core/src/memory.rs index f069c0e..e76553f 100644 --- a/emulator/core/src/memory.rs +++ b/emulator/core/src/memory.rs @@ -4,10 +4,9 @@ use std::cmp; use std::rc::Rc; use std::cell::RefCell; use std::fmt::Write; +use femtos::Instant; -use crate::info; use crate::error::Error; -use crate::clock::ClockTime; use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16}; @@ -56,12 +55,12 @@ impl Addressable for MemoryBlock { self.contents.len() } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { data.copy_from_slice(&self.contents[(addr as usize)..(addr as usize) + data.len()]); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { if self.read_only { return Err(Error::breakpoint(format!("Attempt to write to read-only memory at {:x} with data {:?}", addr, data))); } @@ -98,12 +97,12 @@ impl Addressable for AddressRepeater { self.range as usize } - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address; self.subdevice.borrow_mut().as_addressable().unwrap().read(clock, addr % size, data) } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address; self.subdevice.borrow_mut().as_addressable().unwrap().write(clock, addr % size, data) } @@ -141,11 +140,11 @@ impl Addressable for AddressTranslator { self.size } - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { self.subdevice.borrow_mut().as_addressable().unwrap().read(clock, (self.func)(addr), data) } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { self.subdevice.borrow_mut().as_addressable().unwrap().write(clock, (self.func)(addr), data) } } @@ -205,7 +204,7 @@ impl Bus { Err(Error::new(format!("No segment found at {:#010x}", addr))) } - pub fn dump_memory(&mut self, clock: ClockTime, mut addr: Address, mut count: Address) { + pub fn dump_memory(&mut self, clock: Instant, mut addr: Address, mut count: Address) { while count > 0 { let mut line = format!("{:#010x}: ", addr); @@ -248,11 +247,11 @@ impl Addressable for Bus { (block.base as usize) + block.size } - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { let (dev, relative_addr) = match self.get_device_at(addr, data.len()) { Ok(result) => result, Err(err) if self.ignore_unmapped => { - info!("{:?}", err); + log::info!("{:?}", err); return Ok(()) }, Err(err) => return Err(err), @@ -261,7 +260,7 @@ impl Addressable for Bus { result } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { if self.watchers.iter().any(|a| *a == addr) { println!("watch: writing to address {:#06x} with {:?}", addr, data); self.watcher_modified = true; @@ -270,7 +269,7 @@ impl Addressable for Bus { let (dev, relative_addr) = match self.get_device_at(addr, data.len()) { Ok(result) => result, Err(err) if self.ignore_unmapped => { - info!("{:?}", err); + log::info!("{:?}", err); return Ok(()) }, Err(err) => return Err(err), @@ -300,7 +299,7 @@ impl BusPort { } } - pub fn dump_memory(&mut self, clock: ClockTime, addr: Address, count: Address) { + pub fn dump_memory(&mut self, clock: Instant, addr: Address, count: Address) { self.subdevice.borrow_mut().dump_memory(clock, self.offset + (addr & self.address_mask), count) } @@ -320,7 +319,7 @@ impl Addressable for BusPort { self.subdevice.borrow().size() } - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { let addr = self.offset + (addr & self.address_mask); let mut subdevice = self.subdevice.borrow_mut(); for i in (0..data.len()).step_by(self.data_width as usize) { @@ -331,7 +330,7 @@ impl Addressable for BusPort { Ok(()) } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { let addr = self.offset + (addr & self.address_mask); let mut subdevice = self.subdevice.borrow_mut(); for i in (0..data.len()).step_by(self.data_width as usize) { diff --git a/emulator/core/src/signals.rs b/emulator/core/src/signals.rs index f083a9d..5ff371c 100644 --- a/emulator/core/src/signals.rs +++ b/emulator/core/src/signals.rs @@ -1,7 +1,7 @@ use std::cell::{Cell, RefCell, RefMut}; use std::rc::Rc; -use crate::ClockTime; +use femtos::Instant; pub trait Observable { fn set_observer(&self, f: F) @@ -125,12 +125,12 @@ impl Observable for ObservableEdgeSignal { pub trait SignalReceiver { - fn get_next(&self) -> (ClockTime, T); - fn get_at(clock: ClockTime) -> T; + fn get_next(&self) -> (Instant, T); + fn get_at(clock: Instant) -> T; } pub trait SignalDriver { - fn set_at(clock: ClockTime, value: T); + fn set_at(clock: Instant, value: T); } diff --git a/emulator/core/src/system.rs b/emulator/core/src/system.rs index 3b3659d..599636f 100644 --- a/emulator/core/src/system.rs +++ b/emulator/core/src/system.rs @@ -2,12 +2,13 @@ use std::rc::Rc; use std::cell::{RefCell, RefMut}; use std::collections::HashMap; +use femtos::{Instant, Duration}; -use crate::{Bus, EdgeSignal, Error, InterruptController, ClockTime, ClockDuration, Address, Device}; +use crate::{Bus, EdgeSignal, Error, InterruptController, Address, Device}; pub struct System { - pub clock: ClockTime, + pub clock: Instant, pub devices: HashMap, pub event_queue: Vec, @@ -23,7 +24,7 @@ pub struct System { impl Default for System { fn default() -> Self { Self { - clock: ClockTime::START, + clock: Instant::START, devices: HashMap::new(), event_queue: vec![], @@ -132,7 +133,7 @@ impl System { } /// Run the simulation until the given simulation clock time has been reached - pub fn run_until_clock(&mut self, clock: ClockTime) -> Result<(), Error> { + pub fn run_until_clock(&mut self, clock: Instant) -> Result<(), Error> { while self.clock < clock { self.step()?; } @@ -140,7 +141,7 @@ impl System { } /// Run the simulation for `elapsed` amount of simulation time - pub fn run_for_duration(&mut self, elapsed: ClockDuration) -> Result<(), Error> { + pub fn run_for_duration(&mut self, elapsed: Duration) -> Result<(), Error> { let target = self.clock + elapsed; while self.clock < target { @@ -151,7 +152,7 @@ impl System { /// Run the simulation forever, or until there is an error pub fn run_forever(&mut self) -> Result<(), Error> { - self.run_until_clock(ClockTime::FOREVER) + self.run_until_clock(Instant::FOREVER) } pub fn exit_error(&mut self) { @@ -200,14 +201,14 @@ impl System { pub struct NextStep { - pub next_clock: ClockTime, + pub next_clock: Instant, pub device: Device, } impl NextStep { pub fn new(device: Device) -> Self { Self { - next_clock: ClockTime::START, + next_clock: Instant::START, device, } } diff --git a/emulator/cpus/m68k/Cargo.toml b/emulator/cpus/m68k/Cargo.toml index 9e2a6e5..b6c783b 100644 --- a/emulator/cpus/m68k/Cargo.toml +++ b/emulator/cpus/m68k/Cargo.toml @@ -4,5 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_parsing = { path = "../../libraries/parsing" } diff --git a/emulator/cpus/m68k/src/execute.rs b/emulator/cpus/m68k/src/execute.rs index 4770e48..6e54d40 100644 --- a/emulator/cpus/m68k/src/execute.rs +++ b/emulator/cpus/m68k/src/execute.rs @@ -1,6 +1,7 @@ -use moa_core::debug; -use moa_core::{System, Error, ClockTime, ClockDuration, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; +use femtos::{Instant, Duration}; + +use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; use crate::state::{M68k, M68kType, ClockCycles, Status, Flags, Exceptions, InterruptPriority}; use crate::memory::{MemType, MemAccess}; @@ -32,7 +33,7 @@ pub enum Used { } impl Steppable for M68k { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let clocks = self.step_internal(system)?; Ok(self.frequency.period_duration() * clocks as u64) } @@ -78,7 +79,7 @@ impl M68k { } } - pub fn init_cycle(&mut self, clock: ClockTime) { + pub fn init_cycle(&mut self, clock: Instant) { self.current_clock = clock; self.decoder = M68kDecoder::new(self.cputype, self.is_supervisor(), self.state.pc); self.timing = M68kInstructionTiming::new(self.cputype, self.port.data_width()); @@ -116,7 +117,7 @@ impl M68k { let priority_mask = ((self.state.sr & Flags::IntMask as u16) >> 8) as u8; if (pending_ipl > priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl { - debug!("{} interrupt: {} @ {} ns", DEV_NAME, pending_ipl, system.clock.as_duration().as_nanos()); + log::debug!("{} interrupt: {} @ {} ns", DEV_NAME, pending_ipl, system.clock.as_duration().as_nanos()); self.state.current_ipl = self.state.pending_ipl; let ack_num = system.get_interrupt_controller().acknowledge(self.state.current_ipl as u8)?; self.exception(ack_num, true)?; @@ -132,7 +133,7 @@ impl M68k { } pub fn exception(&mut self, number: u8, is_interrupt: bool) -> Result<(), Error> { - debug!("{}: raising exception {}", DEV_NAME, number); + log::debug!("{}: raising exception {}", DEV_NAME, number); if number == Exceptions::BusError as u8 || number == Exceptions::AddressError as u8 { let result = self.setup_group0_exception(number); @@ -429,6 +430,7 @@ impl M68k { self.set_target_value(target, pair.0, size, Used::Twice)?; let last_bit = if count < size.in_bits() { pair.1 } else { false }; + //let last_bit = if count < size.in_bits() { pair.1 } else { get_msb(value, size) }; self.set_arithmetic_shift_flags(pair.0, count, last_bit, overflow, size); Ok(()) } diff --git a/emulator/cpus/m68k/src/memory.rs b/emulator/cpus/m68k/src/memory.rs index 568f1e3..ae21e39 100644 --- a/emulator/cpus/m68k/src/memory.rs +++ b/emulator/cpus/m68k/src/memory.rs @@ -1,5 +1,7 @@ -use moa_core::{Error, ClockTime, Address, Addressable, BusPort}; +use femtos::Instant; + +use moa_core::{Error, Address, Addressable, BusPort}; use crate::state::{M68k, Exceptions}; use crate::instructions::Size; @@ -90,8 +92,8 @@ impl MemoryRequest { pub struct M68kBusPort { pub port: BusPort, pub request: MemoryRequest, - pub cycle_start_clock: ClockTime, - pub current_clock: ClockTime, + pub cycle_start_clock: Instant, + pub current_clock: Instant, } @@ -104,8 +106,8 @@ impl M68kBusPort { Self { port, request: Default::default(), - cycle_start_clock: ClockTime::START, - current_clock: ClockTime::START, + cycle_start_clock: Instant::START, + current_clock: Instant::START, } } @@ -113,7 +115,7 @@ impl M68kBusPort { self.port.data_width() } - pub fn init_cycle(&mut self, clock: ClockTime) { + pub fn init_cycle(&mut self, clock: Instant) { self.cycle_start_clock = clock; self.current_clock = clock; } diff --git a/emulator/cpus/m68k/src/state.rs b/emulator/cpus/m68k/src/state.rs index ef75ab8..c92b6da 100644 --- a/emulator/cpus/m68k/src/state.rs +++ b/emulator/cpus/m68k/src/state.rs @@ -1,8 +1,9 @@ use std::rc::Rc; use std::cell::RefCell; +use femtos::{Instant, Frequency}; -use moa_core::{ClockTime, Address, Bus, BusPort, Frequency}; +use moa_core::{Address, Bus, BusPort}; use crate::decode::M68kDecoder; use crate::debugger::M68kDebugger; @@ -99,7 +100,7 @@ pub struct M68k { pub timing: M68kInstructionTiming, pub debugger: M68kDebugger, pub port: M68kBusPort, - pub current_clock: ClockTime, + pub current_clock: Instant, } impl Default for M68kState { @@ -131,7 +132,7 @@ impl M68k { timing: M68kInstructionTiming::new(cputype, port.data_width()), debugger: M68kDebugger::default(), port: M68kBusPort::new(port), - current_clock: ClockTime::START, + current_clock: Instant::START, } } diff --git a/emulator/cpus/m68k/src/tests.rs b/emulator/cpus/m68k/src/tests.rs index 812bd55..104497c 100644 --- a/emulator/cpus/m68k/src/tests.rs +++ b/emulator/cpus/m68k/src/tests.rs @@ -3,8 +3,9 @@ mod decode_unit_tests { use std::rc::Rc; use std::cell::RefCell; + use femtos::Instant; - use moa_core::{Bus, BusPort, ClockTime, Address, Addressable, MemoryBlock, Device}; + use moa_core::{Bus, BusPort, Address, Addressable, MemoryBlock, Device}; use crate::M68kType; use crate::instructions::{Target, Size, XRegister, BaseRegister, IndexRegister}; @@ -58,7 +59,7 @@ mod decode_unit_tests { let size = Size::Long; let expected = 0x12345678; - port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b010, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectAReg(2)); @@ -71,7 +72,7 @@ mod decode_unit_tests { let size = Size::Long; let expected = 0x12345678; - port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b011, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectARegInc(2)); @@ -84,7 +85,7 @@ mod decode_unit_tests { let size = Size::Long; let expected = 0x12345678; - port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b100, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectARegDec(2)); @@ -97,7 +98,7 @@ mod decode_unit_tests { let size = Size::Long; let offset = -8; - port.port.write_beu16(ClockTime::START, INIT_ADDR, (offset as i16) as u16).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b101, 0b100, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset)); @@ -111,8 +112,8 @@ mod decode_unit_tests { let offset = -8; let brief_extension = 0x3800 | (((offset as i8) as u8) as u16); - port.port.write_beu16(ClockTime::START, INIT_ADDR, brief_extension).unwrap(); - port.port.write_beu16(ClockTime::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset)); @@ -126,8 +127,8 @@ mod decode_unit_tests { let offset = -1843235 as i32; let brief_extension = 0xF330; - port.port.write_beu16(ClockTime::START, INIT_ADDR, brief_extension).unwrap(); - port.port.write_beu32(ClockTime::START, INIT_ADDR + 2, offset as u32).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset)); @@ -141,8 +142,8 @@ mod decode_unit_tests { let offset = -1843235 as i32; let brief_extension = 0xF3B0; - port.port.write_beu16(ClockTime::START, INIT_ADDR, brief_extension).unwrap(); - port.port.write_beu32(ClockTime::START, INIT_ADDR + 2, offset as u32).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::None, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset)); @@ -156,8 +157,8 @@ mod decode_unit_tests { let offset = -1843235 as i32; let brief_extension = 0xF370; - port.port.write_beu16(ClockTime::START, INIT_ADDR, brief_extension).unwrap(); - port.port.write_beu32(ClockTime::START, INIT_ADDR + 2, offset as u32).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), None, offset)); @@ -170,7 +171,7 @@ mod decode_unit_tests { let size = Size::Long; let offset = -8; - port.port.write_beu16(ClockTime::START, INIT_ADDR, (offset as i16) as u16).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b111, 0b010, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset)); @@ -184,8 +185,8 @@ mod decode_unit_tests { let offset = -8; let brief_extension = 0x3000 | (((offset as i8) as u8) as u16); - port.port.write_beu16(ClockTime::START, INIT_ADDR, brief_extension).unwrap(); - port.port.write_beu16(ClockTime::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b111, 0b011, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset)); @@ -199,8 +200,8 @@ mod decode_unit_tests { let offset = -1843235 as i32; let brief_extension = 0xF330; - port.port.write_beu16(ClockTime::START, INIT_ADDR, brief_extension).unwrap(); - port.port.write_beu32(ClockTime::START, INIT_ADDR + 2, offset as u32).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b111, 0b011, Some(size)).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset)); @@ -214,7 +215,7 @@ mod decode_unit_tests { let size = Size::Word; let expected = 0x1234; - port.port.write_beu16(ClockTime::START, INIT_ADDR, expected as u16).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b111, 0b000, Some(size)).unwrap(); assert_eq!(target, Target::IndirectMemory(expected, Size::Word)); @@ -227,7 +228,7 @@ mod decode_unit_tests { let size = Size::Word; let expected = 0x12345678; - port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b111, 0b001, Some(size)).unwrap(); assert_eq!(target, Target::IndirectMemory(expected, Size::Long)); @@ -240,7 +241,7 @@ mod decode_unit_tests { let size = Size::Word; let expected = 0x1234; - port.port.write_beu16(ClockTime::START, INIT_ADDR, expected as u16).unwrap(); + port.port.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap(); let target = decoder.get_mode_as_target(&mut port, 0b111, 0b100, Some(size)).unwrap(); assert_eq!(target, Target::Immediate(expected)); @@ -250,7 +251,7 @@ mod decode_unit_tests { #[cfg(test)] mod execute_unit_tests { - use moa_core::{System, MemoryBlock, BusPort, ClockTime, Frequency, Address, Addressable, Steppable, Device}; + use moa_core::{System, MemoryBlock, BusPort, Instant, Frequency, Address, Addressable, Steppable, Device}; use crate::{M68k, M68kType}; use crate::execute::Used; @@ -315,7 +316,7 @@ mod execute_unit_tests { let size = Size::Long; let expected = 0x12345678; let target = Target::IndirectAReg(2); - cpu.port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + cpu.port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); cpu.state.a_reg[2] = INIT_ADDR as u32; let result = cpu.get_target_value(target, size, Used::Once).unwrap(); @@ -329,7 +330,7 @@ mod execute_unit_tests { let size = Size::Long; let expected = 0x12345678; let target = Target::IndirectARegInc(2); - cpu.port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + cpu.port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); cpu.state.a_reg[2] = INIT_ADDR as u32; let result = cpu.get_target_value(target, size, Used::Once).unwrap(); @@ -344,7 +345,7 @@ mod execute_unit_tests { let size = Size::Long; let expected = 0x12345678; let target = Target::IndirectARegDec(2); - cpu.port.port.write_beu32(ClockTime::START, INIT_ADDR, expected).unwrap(); + cpu.port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); cpu.state.a_reg[2] = (INIT_ADDR as u32) + 4; let result = cpu.get_target_value(target, size, Used::Once).unwrap(); diff --git a/emulator/cpus/m68k/tests/decode_tests.rs b/emulator/cpus/m68k/tests/decode_tests.rs index c7ff40b..de1c417 100644 --- a/emulator/cpus/m68k/tests/decode_tests.rs +++ b/emulator/cpus/m68k/tests/decode_tests.rs @@ -1,5 +1,7 @@ -use moa_core::{System, MemoryBlock, BusPort, ClockTime, Frequency, Address, Addressable, Device}; +use femtos::{Instant, Frequency}; + +use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType}; use moa_m68k::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction}; @@ -68,8 +70,8 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) { let data = vec![0; 0x00100000]; let mem = MemoryBlock::new(data); system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 0, INIT_STACK as u32).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 4, INIT_ADDR as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); // Initialize the CPU and make sure it's in the expected state let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); diff --git a/emulator/cpus/m68k/tests/execute_tests.rs b/emulator/cpus/m68k/tests/execute_tests.rs index 892cb2b..cbe4a69 100644 --- a/emulator/cpus/m68k/tests/execute_tests.rs +++ b/emulator/cpus/m68k/tests/execute_tests.rs @@ -1,5 +1,7 @@ -use moa_core::{System, MemoryBlock, BusPort, ClockTime, Frequency, Address, Addressable, Steppable, Device}; +use femtos::{Instant, Frequency}; + +use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Steppable, Device}; use moa_m68k::{M68k, M68kType}; use moa_m68k::state::M68kState; @@ -39,8 +41,8 @@ fn init_execute_test(cputype: M68kType) -> (M68k, System) { let data = vec![0; 0x00100000]; let mem = MemoryBlock::new(data); system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 0, INIT_STACK as u32).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 4, INIT_ADDR as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); cpu.step(&system).unwrap(); diff --git a/emulator/cpus/m68k/tests/musashi_timing_tests.rs b/emulator/cpus/m68k/tests/musashi_timing_tests.rs index 7f29d2b..ab81461 100644 --- a/emulator/cpus/m68k/tests/musashi_timing_tests.rs +++ b/emulator/cpus/m68k/tests/musashi_timing_tests.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, MemoryBlock, BusPort, ClockTime, Frequency, Address, Addressable, Device}; +use femtos::{Instant, Frequency}; + +use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType}; use moa_m68k::instructions::{Instruction, Target, Size, Sign, Condition, XRegister, BaseRegister, IndexRegister, Direction}; @@ -16,12 +18,12 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) { let data = vec![0; 0x00100000]; let mem = MemoryBlock::new(data); system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 0, INIT_STACK as u32).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 4, INIT_ADDR as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); // Initialize the CPU and make sure it's in the expected state let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); - cpu.init_cycle(ClockTime::START); + cpu.init_cycle(Instant::START); assert_eq!(cpu.state.pc, INIT_ADDR as u32); assert_eq!(cpu.state.ssp, INIT_STACK as u32); diff --git a/emulator/cpus/m68k/tests/timing_tests.rs b/emulator/cpus/m68k/tests/timing_tests.rs index f943a12..9bf4231 100644 --- a/emulator/cpus/m68k/tests/timing_tests.rs +++ b/emulator/cpus/m68k/tests/timing_tests.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, MemoryBlock, BusPort, ClockTime, Frequency, Address, Addressable, Device}; +use femtos::{Instant, Frequency}; + +use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType}; use moa_m68k::instructions::{Instruction, Target, Size}; @@ -28,8 +30,8 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) { let data = vec![0; 0x00100000]; let mem = MemoryBlock::new(data); system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 0, INIT_STACK as u32).unwrap(); - system.get_bus().write_beu32(ClockTime::START, 4, INIT_ADDR as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); + system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); // Initialize the CPU and make sure it's in the expected state let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); diff --git a/emulator/cpus/z80/Cargo.toml b/emulator/cpus/z80/Cargo.toml index 3e2ae2f..8d7d0c6 100644 --- a/emulator/cpus/z80/Cargo.toml +++ b/emulator/cpus/z80/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } diff --git a/emulator/cpus/z80/src/decode.rs b/emulator/cpus/z80/src/decode.rs index 964ecf5..41a0662 100644 --- a/emulator/cpus/z80/src/decode.rs +++ b/emulator/cpus/z80/src/decode.rs @@ -1,11 +1,13 @@ -use moa_core::{Error, ClockTime, Address, Addressable}; +use femtos::Instant; + +use moa_core::{Error, Address, Addressable}; use crate::instructions::{Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction}; #[derive(Clone)] pub struct Z80Decoder { - pub clock: ClockTime, + pub clock: Instant, pub start: u16, pub end: u16, pub extra_instruction_bytes: u16, @@ -15,7 +17,7 @@ pub struct Z80Decoder { impl Default for Z80Decoder { fn default() -> Self { Self { - clock: ClockTime::START, + clock: Instant::START, start: 0, end: 0, extra_instruction_bytes: 0, @@ -25,7 +27,7 @@ impl Default for Z80Decoder { } impl Z80Decoder { - pub fn decode_at(&mut self, memory: &mut dyn Addressable, clock: ClockTime, start: u16) -> Result<(), Error> { + pub fn decode_at(&mut self, memory: &mut dyn Addressable, clock: Instant, start: u16) -> Result<(), Error> { self.clock = clock; self.start = start; self.end = start; diff --git a/emulator/cpus/z80/src/execute.rs b/emulator/cpus/z80/src/execute.rs index 1d4652c..8c8c61b 100644 --- a/emulator/cpus/z80/src/execute.rs +++ b/emulator/cpus/z80/src/execute.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, ClockTime, ClockDuration, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16}; +use femtos::{Instant, Duration}; + +use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16}; use crate::instructions::{Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction, UndocumentedCopy}; use crate::state::{Z80, Status, Flags}; @@ -19,7 +21,7 @@ enum RotateType { } impl Steppable for Z80 { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let clocks = if self.reset.get() { self.reset()? } else if self.bus_request.get() { @@ -56,12 +58,12 @@ impl Transmutable for Z80 { #[derive(Clone)] pub struct Z80Executor { - pub current_clock: ClockTime, + pub current_clock: Instant, pub took_branch: bool, } impl Z80Executor { - pub fn at_time(current_clock: ClockTime) -> Self { + pub fn at_time(current_clock: Instant) -> Self { Self { current_clock, took_branch: false, diff --git a/emulator/cpus/z80/src/state.rs b/emulator/cpus/z80/src/state.rs index 897dd87..deff808 100644 --- a/emulator/cpus/z80/src/state.rs +++ b/emulator/cpus/z80/src/state.rs @@ -1,8 +1,9 @@ use std::rc::Rc; use std::cell::RefCell; +use femtos::{Instant, Frequency}; -use moa_core::{ClockTime, Address, Bus, BusPort, Signal, Frequency}; +use moa_core::{Address, Bus, BusPort, Signal}; use crate::decode::Z80Decoder; use crate::debugger::Z80Debugger; @@ -112,7 +113,7 @@ impl Z80 { state: Z80State::default(), decoder: Z80Decoder::default(), debugger: Z80Debugger::default(), - executor: Z80Executor::at_time(ClockTime::START), + executor: Z80Executor::at_time(Instant::START), port, ioport, reset: Signal::new(false), @@ -131,10 +132,10 @@ impl Z80 { self.state = Z80State::default(); self.decoder = Z80Decoder::default(); self.debugger = Z80Debugger::default(); - self.executor = Z80Executor::at_time(ClockTime::START); + self.executor = Z80Executor::at_time(Instant::START); } - pub fn dump_state(&mut self, clock: ClockTime) { + pub fn dump_state(&mut self, clock: Instant) { println!("Status: {:?}", self.state.status); println!("PC: {:#06x}", self.state.pc); println!("SP: {:#06x}", self.state.sp); diff --git a/emulator/cpus/z80/tests/decode_tests.rs b/emulator/cpus/z80/tests/decode_tests.rs index 5504849..87b83e9 100644 --- a/emulator/cpus/z80/tests/decode_tests.rs +++ b/emulator/cpus/z80/tests/decode_tests.rs @@ -1,5 +1,7 @@ -use moa_core::{System, MemoryBlock, BusPort, Frequency, Address, Addressable, Device}; +use femtos::Frequency; + +use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_z80::{Z80, Z80Type}; use moa_z80::instructions::{Instruction, LoadTarget, Target, Register, RegisterPair, IndexRegister, IndexRegisterHalf}; diff --git a/emulator/cpus/z80/tests/execute_tests.rs b/emulator/cpus/z80/tests/execute_tests.rs index 58a929e..23ca97a 100644 --- a/emulator/cpus/z80/tests/execute_tests.rs +++ b/emulator/cpus/z80/tests/execute_tests.rs @@ -1,5 +1,7 @@ -use moa_core::{System, MemoryBlock, BusPort, Frequency, Address, Addressable, Device}; +use femtos::Frequency; + +use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_z80::{Z80, Z80Type}; use moa_z80::state::Z80State; diff --git a/emulator/frontends/common/Cargo.toml b/emulator/frontends/common/Cargo.toml index 2d521b0..e935fae 100644 --- a/emulator/frontends/common/Cargo.toml +++ b/emulator/frontends/common/Cargo.toml @@ -8,6 +8,8 @@ tty = ["nix"] audio = ["cpal"] [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } nix = { version = "0.25", optional = true } diff --git a/emulator/frontends/common/src/audio.rs b/emulator/frontends/common/src/audio.rs index b51264b..639135a 100644 --- a/emulator/frontends/common/src/audio.rs +++ b/emulator/frontends/common/src/audio.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; +use femtos::{Instant, Duration}; -use moa_core::{ClockTime, ClockDuration}; use moa_core::host::{Audio, Sample, AudioFrame, ClockedQueue}; @@ -38,7 +38,7 @@ impl AudioSource { self.id } - pub fn add_frame(&mut self, clock: ClockTime, buffer: &[Sample]) { + pub fn add_frame(&mut self, clock: Instant, buffer: &[Sample]) { let mut data = Vec::with_capacity(buffer.len()); for sample in buffer.iter() { data.push(*sample); @@ -56,7 +56,7 @@ impl Audio for AudioSource { self.sample_rate } - fn write_samples(&mut self, clock: ClockTime, buffer: &[Sample]) { + fn write_samples(&mut self, clock: Instant, buffer: &[Sample]) { self.add_frame(clock, buffer); } } @@ -106,11 +106,11 @@ impl AudioMixerInner { self.sample_rate } - pub fn sample_duration(&self) -> ClockDuration { - ClockDuration::from_secs(1) / self.sample_rate as u64 + pub fn sample_duration(&self) -> Duration { + Duration::from_secs(1) / self.sample_rate as u64 } - fn assemble_frame(&mut self, frame_start: ClockTime, frame_duration: ClockDuration) { + fn assemble_frame(&mut self, frame_start: Instant, frame_duration: Duration) { let sample_duration = self.sample_duration(); let samples = (frame_duration / sample_duration) as usize; @@ -150,8 +150,8 @@ impl AudioMixerInner { use moa_core::{Transmutable, Steppable, Error, System}; impl Steppable for AudioMixer { - fn step(&mut self, system: &System) -> Result { - let duration = ClockDuration::from_millis(1); + fn step(&mut self, system: &System) -> Result { + let duration = Duration::from_millis(1); // TODO should you make the clock be even further back to ensure the data is already written if let Some(start) = system.clock.checked_sub(duration) { self.borrow_mut().assemble_frame(start, duration); @@ -182,15 +182,15 @@ impl Default for AudioOutput { } impl AudioOutput { - pub fn add_frame(&self, clock: ClockTime, frame: AudioFrame) { + pub fn add_frame(&self, clock: Instant, frame: AudioFrame) { self.queue.push(clock, frame); } - pub fn put_back(&self, clock: ClockTime, frame: AudioFrame) { + pub fn put_back(&self, clock: Instant, frame: AudioFrame) { self.queue.put_back(clock, frame); } - pub fn receive(&self) -> Option<(ClockTime, AudioFrame)> { + pub fn receive(&self) -> Option<(Instant, AudioFrame)> { self.queue.pop_next() } diff --git a/emulator/frontends/common/src/cpal.rs b/emulator/frontends/common/src/cpal.rs index 46c1355..e503901 100644 --- a/emulator/frontends/common/src/cpal.rs +++ b/emulator/frontends/common/src/cpal.rs @@ -1,8 +1,6 @@ use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, StreamInstant, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}}; -use moa_core::{debug, error}; - use crate::audio::{AudioOutput, SAMPLE_RATE}; #[allow(dead_code)] @@ -41,7 +39,7 @@ impl CpalAudioOutput { output.put_back(clock, frame); } } else { - debug!("missed an audio frame"); + log::debug!("missed an audio frame"); break; } } @@ -51,7 +49,7 @@ impl CpalAudioOutput { &config, data_callback, move |err| { - error!("ERROR: {:?}", err); + log::error!("ERROR: {:?}", err); }, ).unwrap(); diff --git a/emulator/frontends/console/Cargo.toml b/emulator/frontends/console/Cargo.toml index 9fb768f..6e74418 100644 --- a/emulator/frontends/console/Cargo.toml +++ b/emulator/frontends/console/Cargo.toml @@ -5,9 +5,10 @@ edition = "2021" default-run = "moa-computie" [dependencies] -log = "^0.4" +log = "0.4" clap = "^4" simple_logger = "^2" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_common = { path = "../common", features = ["tty"] } diff --git a/emulator/frontends/console/src/bin/moa-bench.rs b/emulator/frontends/console/src/bin/moa-bench.rs index c6d869f..5391d09 100644 --- a/emulator/frontends/console/src/bin/moa-bench.rs +++ b/emulator/frontends/console/src/bin/moa-bench.rs @@ -1,8 +1,9 @@ use std::thread; use std::time::Duration; +use femtos::Frequency; -use moa_core::{System, Frequency, MemoryBlock, BusPort, Device}; +use moa_core::{System, MemoryBlock, BusPort, Device}; use moa_m68k::{M68k, M68kType}; use moa_peripherals_generic::AtaDevice; diff --git a/emulator/frontends/console/src/lib.rs b/emulator/frontends/console/src/lib.rs index 1f2cb21..240d870 100644 --- a/emulator/frontends/console/src/lib.rs +++ b/emulator/frontends/console/src/lib.rs @@ -1,8 +1,9 @@ use clap::{Command, Arg, ArgAction, ArgMatches}; use std::io::{self, Write}; +use femtos::Duration; -use moa_core::{Error, System, ClockDuration, DebugControl, Debugger}; +use moa_core::{Error, System, DebugControl, Debugger}; use moa_core::host::{Host, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender}; pub struct ConsoleFrontend; @@ -90,7 +91,7 @@ impl ConsoleFrontend { } } - match system.run_for_duration(ClockDuration::MAX - system.clock.as_duration()) { + match system.run_for_duration(Duration::MAX - system.clock.as_duration()) { Ok(()) => {}, Err(Error::Breakpoint(_)) => { run_debugger = true; diff --git a/emulator/frontends/minifb/Cargo.toml b/emulator/frontends/minifb/Cargo.toml index 804f8bc..5b830e3 100644 --- a/emulator/frontends/minifb/Cargo.toml +++ b/emulator/frontends/minifb/Cargo.toml @@ -9,6 +9,7 @@ log = "0.4" minifb = "^0.19" clap = "^4" simple_logger = "^2" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_common = { path = "../common", features = ["audio"] } diff --git a/emulator/frontends/minifb/src/bin/moa-synth.rs b/emulator/frontends/minifb/src/bin/moa-synth.rs index 9aa4a5e..69223ba 100644 --- a/emulator/frontends/minifb/src/bin/moa-synth.rs +++ b/emulator/frontends/minifb/src/bin/moa-synth.rs @@ -1,8 +1,10 @@ +use femtos::{Instant, Duration, Frequency}; + use moa_peripherals_yamaha::{Ym2612, Sn76489}; use moa_core::host::{self, Host, Frame, FrameSender, PixelEncoding, Key, KeyEvent, EventReceiver}; -use moa_core::{System, Error, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable, Device}; +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable, Device}; const SCREEN_WIDTH: u32 = 384; const SCREEN_HEIGHT: u32 = 128; @@ -22,7 +24,7 @@ impl SynthControl { } impl Steppable for SynthControl { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { if let Some(event) = self.key_receiver.receive() { match event.key { @@ -44,7 +46,7 @@ impl Steppable for SynthControl { let frame = Frame::new(SCREEN_WIDTH, SCREEN_HEIGHT, PixelEncoding::RGBA); self.frame_sender.add(system.clock, frame); - Ok(ClockDuration::from_micros(100)) + Ok(Duration::from_micros(100)) } } @@ -56,8 +58,8 @@ impl Transmutable for SynthControl { fn set_register(device: &mut dyn Addressable, bank: u8, reg: u8, data: u8) -> Result<(), Error> { let addr = (bank as Address) * 2; - device.write_u8(ClockTime::START, addr, reg)?; - device.write_u8(ClockTime::START, addr + 1, data)?; + device.write_u8(Instant::START, addr, reg)?; + device.write_u8(Instant::START, addr + 1, data)?; Ok(()) } diff --git a/emulator/frontends/minifb/src/lib.rs b/emulator/frontends/minifb/src/lib.rs index 3bc97d9..a47c7c8 100644 --- a/emulator/frontends/minifb/src/lib.rs +++ b/emulator/frontends/minifb/src/lib.rs @@ -6,8 +6,9 @@ use std::time::{Duration, Instant}; use minifb::{self, Key, MouseMode, MouseButton}; use clap::{Command, Arg, ArgAction, ArgMatches}; +use femtos::{Duration as FemtosDuration}; -use moa_core::{System, Error, ClockDuration, Device, Debugger, DebugControl}; +use moa_core::{System, Error, Device, Debugger, DebugControl}; use moa_core::host::{Host, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame, FrameReceiver}; use moa_common::{AudioMixer, AudioSource}; @@ -294,7 +295,7 @@ impl MiniFrontend { //let run_timer = Instant::now(); if let Some(system) = system.as_mut() { //system.run_for(nanoseconds_per_frame).unwrap(); - match system.run_for_duration(ClockDuration::from_nanos((frame_time.as_nanos() as f32 * speed) as u64)) { + match system.run_for_duration(FemtosDuration::from_nanos((frame_time.as_nanos() as f32 * speed) as u64)) { Ok(()) => {}, Err(Error::Breakpoint(_)) => { run_debugger = true; diff --git a/emulator/frontends/pixels/Cargo.lock b/emulator/frontends/pixels/Cargo.lock index b09af2e..d79998a 100644 --- a/emulator/frontends/pixels/Cargo.lock +++ b/emulator/frontends/pixels/Cargo.lock @@ -517,6 +517,10 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "femtos" +version = "0.1.0" + [[package]] name = "flate2" version = "1.0.26" @@ -909,6 +913,8 @@ name = "moa_common" version = "0.1.0" dependencies = [ "cpal", + "femtos", + "log", "moa_core", ] @@ -916,6 +922,7 @@ dependencies = [ name = "moa_core" version = "0.1.0" dependencies = [ + "femtos", "log", ] @@ -923,6 +930,8 @@ dependencies = [ name = "moa_m68k" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_parsing", ] @@ -938,7 +947,9 @@ dependencies = [ name = "moa_peripherals_yamaha" version = "0.1.0" dependencies = [ + "femtos", "lazy_static", + "log", "moa_audio", "moa_core", ] @@ -950,6 +961,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "env_logger", + "femtos", "instant", "log", "moa_common", @@ -968,6 +980,8 @@ dependencies = [ name = "moa_systems_genesis" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", "moa_m68k", "moa_peripherals_yamaha", @@ -978,6 +992,8 @@ dependencies = [ name = "moa_z80" version = "0.1.0" dependencies = [ + "femtos", + "log", "moa_core", ] diff --git a/emulator/frontends/pixels/Cargo.toml b/emulator/frontends/pixels/Cargo.toml index ca1a88d..e806d15 100644 --- a/emulator/frontends/pixels/Cargo.toml +++ b/emulator/frontends/pixels/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" log = "0.4" pixels = "0.12" winit = "0.28" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_common = { path = "../common", features = ["audio"] } diff --git a/emulator/frontends/pixels/src/web.rs b/emulator/frontends/pixels/src/web.rs index 25e6372..bece1e5 100644 --- a/emulator/frontends/pixels/src/web.rs +++ b/emulator/frontends/pixels/src/web.rs @@ -12,7 +12,8 @@ use web_sys::Event; use wasm_bindgen::JsCast; use wasm_bindgen::closure::Closure; -use moa_core::{ClockDuration, System, Device}; +use femtos::{Duration as FemtosDuration}; +use moa_core::{System, Device}; use moa_core::host::{ControllerInput, ControllerDevice, ControllerEvent, EventSender}; use crate::settings; @@ -138,7 +139,7 @@ pub fn load_system(handle: &mut HostHandle, load: LoadSystemFnHandle) -> SystemH #[wasm_bindgen] pub fn run_system_for(handle: &mut SystemHandle, nanos: u32) -> usize { let run_timer = Instant::now(); - let nanoseconds_per_frame = ClockDuration::from_nanos(nanos as u64); + let nanoseconds_per_frame = FemtosDuration::from_nanos(nanos as u64); //let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock; if let Err(err) = handle.0.run_for_duration(nanoseconds_per_frame) { log::error!("{:?}", err); @@ -265,7 +266,7 @@ fn update(emulator: Rc>) { }; let diff = run_timer.duration_since(last_update); - let nanoseconds_per_frame = ClockDuration::from_nanos(diff.as_nanos() as u64); + let nanoseconds_per_frame = FemtosDuration::from_nanos(diff.as_nanos() as u64); //let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock; if let Err(err) = emulator.borrow_mut().system.run_for_duration(nanoseconds_per_frame) { log::error!("{:?}", err); @@ -285,7 +286,7 @@ fn update(emulator: Rc>) { fn update(emulator: Rc>) { let run_timer = Instant::now(); let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as u64; - if let Err(err) = emulator.borrow_mut().system.run_for_duration(ClockDuration::from_nanos(nanoseconds_per_frame)) { + if let Err(err) = emulator.borrow_mut().system.run_for_duration(FemtosDuration::from_nanos(nanoseconds_per_frame)) { log::error!("{:?}", err); } log::info!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_timer.elapsed().as_millis()); diff --git a/emulator/libraries/femtos/Cargo.toml b/emulator/libraries/femtos/Cargo.toml new file mode 100644 index 0000000..ed22193 --- /dev/null +++ b/emulator/libraries/femtos/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "femtos" +version = "0.1.0" +edition = "2021" +rust-version = "1.60" +categories = ["no-std", "emulation", "simulation"] +keywords = ["femtoseconds", "time", "emulation", "simulation"] +description = "a femtosecond-based representation of time, duration, and frequency, for the purpose of simulation" +authors = ["transistor fet "] +license = "MIT OR Apache-2.0" +repository = "https://github.com/transistorfet/moa" + +[dependencies] + diff --git a/emulator/libraries/femtos/LICENSE-APACHE b/emulator/libraries/femtos/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/emulator/libraries/femtos/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/emulator/libraries/femtos/LICENSE-MIT b/emulator/libraries/femtos/LICENSE-MIT new file mode 100644 index 0000000..2149c50 --- /dev/null +++ b/emulator/libraries/femtos/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2024 transistor fet + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/emulator/libraries/femtos/README.md b/emulator/libraries/femtos/README.md new file mode 100644 index 0000000..27fbbba --- /dev/null +++ b/emulator/libraries/femtos/README.md @@ -0,0 +1,8 @@ +[![crates.io](https://img.shields.io/crates/v/femtos.svg)](https://crates.io/crates/femtos) +[![Documentation](https://docs.rs/femtos/badge.svg)](https://docs.rs/femtos) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) + +# `femtos` + +> A femtosecond representation of time, duration, and frequency + diff --git a/emulator/libraries/femtos/src/lib.rs b/emulator/libraries/femtos/src/lib.rs new file mode 100644 index 0000000..17fda66 --- /dev/null +++ b/emulator/libraries/femtos/src/lib.rs @@ -0,0 +1,576 @@ +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] +#![no_std] + +use core::time; +use core::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; + +/// Type to use for storing femtoseconds +/// +/// In webassembly, using u128 results in exceedingly slow runtimes, so we use u64 instead +/// which is enough for 5 hours of simulation time. +#[cfg(not(target_arch = "wasm32"))] +type Femtos = u128; +#[cfg(target_arch = "wasm32")] +type Femtos = u64; + +/// Represents a duration of time in femtoseconds +/// +/// The `Duration` type is used to represent lengths of time and is +/// intentionally similar to `std::time::Duration`, but which records +/// time as femtoseconds to keep accurancy when dealing with partial +/// nanosecond clock divisons. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Duration { + femtos: Femtos, +} + +impl Duration { + /// A duration of zero (0) time + pub const ZERO: Self = Self::from_femtos(0); + + /// A duration of the maximum possible length in femtoseconds (`Femtos::MAX`) + /// + /// This will be equivalent to either u64::MAX or u128::MAX femtoseconds + pub const MAX: Self = Self::from_femtos(Femtos::MAX); + + /// The number of femtoseconds in 1 second as `Femtos` + pub const FEMTOS_PER_SEC: Femtos = 1_000_000_000_000_000; + + /// The number of femtoseconds in 1 millisecond as `Femtos` + pub const FEMTOS_PER_MILLISEC: Femtos = 1_000_000_000_000; + + /// The number of femtoseconds in 1 microsecond as `Femtos` + pub const FEMTOS_PER_MICROSEC: Femtos = 1_000_000_000; + + /// The number of femtoseconds in 1 nanosecond as `Femtos` + pub const FEMTOS_PER_NANOSEC: Femtos = 1_000_000; + + /// The number of femtoseconds in 1 picosecond as `Femtos` + pub const FEMTOS_PER_PICOSEC: Femtos = 1_000; + + /// Creates a new `Duration` from the specified number of seconds + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_secs(123); + /// + /// assert_eq!(123, duration.as_secs()); + /// ``` + #[inline] + pub const fn from_secs(secs: u64) -> Self { + Self { + femtos: secs as Femtos * Self::FEMTOS_PER_SEC, + } + } + + /// Creates a new `Duration` from the specified number of milliseconds + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_millis(123); + /// + /// assert_eq!(123, duration.as_millis()); + /// ``` + #[inline] + pub const fn from_millis(millisecs: u64) -> Self { + Self { + femtos: millisecs as Femtos * Self::FEMTOS_PER_MILLISEC, + } + } + + /// Creates a new `Duration` from the specified number of microseconds + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_micros(123); + /// + /// assert_eq!(123, duration.as_micros()); + /// ``` + #[inline] + pub const fn from_micros(microsecs: u64) -> Self { + Self { + femtos: microsecs as Femtos * Self::FEMTOS_PER_MICROSEC, + } + } + + /// Creates a new `Duration` from the specified number of nanoseconds + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_nanos(123); + /// + /// assert_eq!(123, duration.as_nanos()); + /// ``` + #[inline] + pub const fn from_nanos(nanosecs: u64) -> Self { + Self { + femtos: nanosecs as Femtos * Self::FEMTOS_PER_NANOSEC, + } + } + + /// Creates a new `Duration` from the specified number of picoseconds + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_picos(123); + /// + /// assert_eq!(123, duration.as_picos()); + /// ``` + #[inline] + pub const fn from_picos(picosecs: u128) -> Self { + Self { + femtos: picosecs as Femtos * Self::FEMTOS_PER_PICOSEC, + } + } + + /// Creates a new `Duration` from the specified number of femtoseconds + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123); + /// + /// assert_eq!(123, duration.as_femtos()); + /// ``` + #[inline] + pub const fn from_femtos(femtos: Femtos) -> Self { + Self { + femtos, + } + } + + /// Returns the number of _whole_ seconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123_465_789_012_345_678); + /// assert_eq!(duration.as_secs(), 123); + /// ``` + #[inline] + pub const fn as_secs(self) -> u64 { + (self.femtos / Self::FEMTOS_PER_SEC) as u64 + } + + /// Returns the number of _whole_ milliseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123_465_789_012_345_678); + /// assert_eq!(duration.as_millis(), 123_465); + /// ``` + #[inline] + pub const fn as_millis(self) -> u64 { + (self.femtos / Self::FEMTOS_PER_MILLISEC) as u64 + } + + /// Returns the number of _whole_ microseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123_465_789_012_345_678); + /// assert_eq!(duration.as_micros(), 123_465_789); + /// ``` + #[inline] + pub const fn as_micros(self) -> u64 { + (self.femtos / Self::FEMTOS_PER_MICROSEC) as u64 + } + + /// Returns the number of _whole_ nanoseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123_465_789_012_345_678); + /// assert_eq!(duration.as_nanos(), 123_465_789_012); + /// ``` + #[inline] + pub const fn as_nanos(self) -> u64 { + (self.femtos / Self::FEMTOS_PER_NANOSEC) as u64 + } + + /// Returns the number of _whole_ picoseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123_465_789_012_345_678); + /// assert_eq!(duration.as_picos(), 123_465_789_012_345); + /// ``` + #[inline] + #[allow(clippy::unnecessary_cast)] + pub const fn as_picos(self) -> u128 { + (self.femtos / Self::FEMTOS_PER_PICOSEC) as u128 + } + + /// Returns the number of _whole_ femtoseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// let duration = Duration::from_femtos(123_465_789_012_345_678); + /// assert_eq!(duration.as_femtos(), 123_465_789_012_345_678); + /// ``` + #[inline] + pub const fn as_femtos(self) -> Femtos { + self.femtos + } + + /// Checked `Duration` addition. Computes `self + rhs`, returning [`None`] + /// if an overflow occured. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// assert_eq!(Duration::from_secs(1).checked_add(Duration::from_secs(1)), Some(Duration::from_secs(2))) + /// assert_eq!(Duration::from_secs(1).checked_add(Duration::from_femtos(Femtos::MAX)), None) + /// ``` + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + match self.femtos.checked_add(rhs.femtos) { + Some(femtos) => Some(Self::from_femtos(femtos)), + None => None, + } + } + + /// Checked `Duration` subtraction. Computes `self - rhs`, returning [`None`] + /// if an overflow occured. + /// + /// # Examples + /// + /// ``` + /// use femtos::Duration; + /// + /// assert_eq!(Duration::from_secs(1).checked_sub(Duration::from_secs(1)), Some(Duration::ZERO)) + /// assert_eq!(Duration::from_secs(1).checked_sub(Duration::from_femtos(2), None) + /// ``` + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + match self.femtos.checked_sub(rhs.femtos) { + Some(femtos) => Some(Self::from_femtos(femtos)), + None => None, + } + } +} + +impl Add for Duration { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + self.checked_add(rhs).expect("clock duration overflow during addition") + } +} + +impl AddAssign for Duration { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sub for Duration { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + self.checked_sub(rhs).expect("clock duration overflow during subtraction") + } +} + +impl SubAssign for Duration { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for Duration { + type Output = Self; + + fn mul(self, rhs: u64) -> Self::Output { + Self::from_femtos(self.femtos * rhs as Femtos) + } +} + +impl MulAssign for Duration { + fn mul_assign(&mut self, rhs: u64) { + *self = Self::from_femtos(self.femtos * rhs as Femtos); + } +} + +impl Div for Duration { + type Output = Self; + + fn div(self, rhs: u64) -> Self::Output { + Self::from_femtos(self.femtos / rhs as Femtos) + } +} + +impl DivAssign for Duration { + fn div_assign(&mut self, rhs: u64) { + *self = Self::from_femtos(self.femtos / rhs as Femtos); + } +} + +impl Div for Duration { + type Output = u64; + + fn div(self, rhs: Duration) -> Self::Output { + (self.femtos / rhs.femtos) as u64 + } +} + + +impl From for time::Duration { + fn from(value: Duration) -> Self { + time::Duration::from_nanos(value.as_nanos()) + } +} + +impl From for Duration { + fn from(value: time::Duration) -> Self { + Duration::from_nanos(value.as_nanos() as u64) + } +} + + +/// Represents time from the start of the simulation +/// +/// `Instant` is for representing the current running clock. It uses a +/// duration to represent the time from simulation start, and is monotonic. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Instant(Duration); + +impl Instant { + /// An `Instant` representing the start of time (t = 0) + pub const START: Self = Self(Duration::ZERO); + /// An `Instant` representing the greatest possible time (t = `Femtos::MAX`) + pub const FOREVER: Self = Self(Duration::MAX); + + /// Returns a `Duration` equivalent to the amount of time elapsed since the earliest + /// possible time (t = 0). + #[inline] + pub const fn as_duration(self) -> Duration { + self.0 + } + + /// Returns the `Duration` that has elapsed between this `Instant` and `other`. + /// + /// # Examples + /// + /// ``` + /// use femtos::{Instant, Duration}; + /// + /// let now = Instant::START + Duration::from_secs(1); + /// assert_eq!(now.duration_since(Instant::START), Duration::from_secs(1)); + /// ``` + #[inline] + pub fn duration_since(self, other: Self) -> Duration { + self.0 - other.0 + } + + /// Checked `Instant` addition. Computes `self + duration`, returning [`None`] + /// if an overflow occured. + /// + /// # Examples + /// + /// ``` + /// use femtos::Instant; + /// + /// assert_eq!(Instant::START.checked_add(Duration::from_secs(1)).as_duration(), Some(Duration::from_secs(1))) + /// assert_eq!(Instant::START.checked_add(Duration::from_femtos(Femtos::MAX)).as_duration(), None) + /// ``` + #[inline] + pub const fn checked_add(self, duration: Duration) -> Option { + match self.0.checked_add(duration) { + Some(duration) => Some(Self(duration)), + None => None, + } + } + + /// Checked `Instant` subtraction. Computes `self - duration`, returning [`None`] + /// if an overflow occured. + /// + /// # Examples + /// + /// ``` + /// use femtos::Instant; + /// + /// assert_eq!(Instant::FOREVER.checked_sub(Duration::from_secs(1)).as_duration(), Some(Duration::from_femtos(Femtos::MAX - 1))) + /// assert_eq!(Instant::START.checked_sub(Duration::from_secs(1)).as_duration(), None) + /// ``` + #[inline] + pub const fn checked_sub(self, duration: Duration) -> Option { + match self.0.checked_sub(duration) { + Some(duration) => Some(Self(duration)), + None => None, + } + } +} + +impl Add for Instant { + type Output = Self; + + fn add(self, rhs: Duration) -> Self::Output { + Self(self.0.add(rhs)) + } +} + +impl AddAssign for Instant { + fn add_assign(&mut self, rhs: Duration) { + *self = Self(self.0.add(rhs)); + } +} + +/// Represents a frequency in Hz +/// +/// Clocks are usually given as a frequency, but durations are needed when dealing with clocks +/// and clock durations. This type makes it easier to create a clock of a given frequency and +/// convert it to a `Duration` +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Frequency { + hertz: u32, +} + +impl Frequency { + /// Creates a new `Frequency` from the specified number of hertz + /// + /// # Examples + /// + /// ``` + /// use femtos::Frequency; + /// + /// Frequency::from_hz(123); + /// ``` + #[inline] + pub const fn from_hz(hertz: u32) -> Self { + Self { + hertz, + } + } + + /// Creates a new `Frequency` from the specified number of kilohertz + /// + /// # Examples + /// + /// ``` + /// use femtos::Frequency; + /// + /// Frequency::from_khz(123); + /// ``` + #[inline] + pub const fn from_khz(khz: u32) -> Self { + Self { + hertz: khz * 1_000, + } + } + + /// Creates a new `Frequency` from the specified number of megahertz + /// + /// # Examples + /// + /// ``` + /// use femtos::Frequency; + /// + /// Frequency::from_mhz(123); + /// ``` + #[inline] + pub const fn from_mhz(mhz: u32) -> Self { + Self { + hertz: mhz * 1_000_000, + } + } + + /// Returns the frequency is hertz + #[inline] + pub const fn as_hz(self) -> u32 { + self.hertz + } + + /// Returns the frequency is kilohertz + #[inline] + pub const fn as_khz(self) -> u32 { + self.hertz / 1_000 + } + + /// Returns the frequency is megahertz + #[inline] + pub const fn as_mhz(self) -> u32 { + self.hertz / 1_000_000 + } + + /// Returns the `Duration` equivalent to the time period between cycles of + /// the given `Frequency` + /// + /// # Examples + /// + /// ``` + /// use femtos::Frequency; + /// + /// assert_eq!(Frequency::from_hz(1).period_duration(), Duration::from_secs(1)); + /// ``` + #[inline] + pub const fn period_duration(self) -> Duration { + Duration::from_femtos(Duration::FEMTOS_PER_SEC / self.hertz as Femtos) + } +} + +impl Mul for Frequency { + type Output = Self; + + fn mul(self, rhs: u32) -> Self::Output { + Self::from_hz(self.hertz * rhs) + } +} + +impl MulAssign for Frequency { + fn mul_assign(&mut self, rhs: u32) { + *self = Self::from_hz(self.hertz * rhs); + } +} + +impl Div for Frequency { + type Output = Self; + + fn div(self, rhs: u32) -> Self::Output { + Self::from_hz(self.hertz / rhs) + } +} + +impl DivAssign for Frequency { + fn div_assign(&mut self, rhs: u32) { + *self = Self::from_hz(self.hertz / rhs); + } +} + diff --git a/emulator/peripherals/generic/Cargo.toml b/emulator/peripherals/generic/Cargo.toml index 53c2cea..1a97b2e 100644 --- a/emulator/peripherals/generic/Cargo.toml +++ b/emulator/peripherals/generic/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } diff --git a/emulator/peripherals/generic/src/ata.rs b/emulator/peripherals/generic/src/ata.rs index 3cda112..aaa046a 100644 --- a/emulator/peripherals/generic/src/ata.rs +++ b/emulator/peripherals/generic/src/ata.rs @@ -1,7 +1,8 @@ use std::fs; +use femtos::Instant; -use moa_core::{Error, ClockTime, Address, Addressable, Transmutable, debug}; +use moa_core::{Error, Address, Addressable, Transmutable}; const ATA_REG_DATA_WORD: Address = 0x20; @@ -57,7 +58,7 @@ impl Addressable for AtaDevice { 0x30 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { ATA_REG_DATA_WORD => { self.selected_count -= 2; @@ -84,14 +85,14 @@ impl Addressable for AtaDevice { ATA_REG_ERROR => { data[0] = self.last_error; }, - _ => { debug!("{}: reading from {:0x}", DEV_NAME, addr); }, + _ => { log::debug!("{}: reading from {:0x}", DEV_NAME, addr); }, } Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { ATA_REG_DRIVE_HEAD => { self.selected_sector |= ((data[0] & 0x1F) as u32) << 24; }, ATA_REG_CYL_HIGH => { self.selected_sector |= (data[0] as u32) << 16; }, @@ -100,11 +101,11 @@ impl Addressable for AtaDevice { ATA_REG_SECTOR_COUNT => { self.selected_count = (data[0] as u32) * ATA_SECTOR_SIZE; }, ATA_REG_COMMAND => { match data[0] { - ATA_CMD_READ_SECTORS => { debug!("{}: reading sector {:x}", DEV_NAME, self.selected_sector); }, - ATA_CMD_WRITE_SECTORS => { debug!("{}: writing sector {:x}", DEV_NAME, self.selected_sector); }, + ATA_CMD_READ_SECTORS => { log::debug!("{}: reading sector {:x}", DEV_NAME, self.selected_sector); }, + ATA_CMD_WRITE_SECTORS => { log::debug!("{}: writing sector {:x}", DEV_NAME, self.selected_sector); }, ATA_CMD_IDENTIFY => { }, ATA_CMD_SET_FEATURE => { }, - _ => { debug!("{}: unrecognized command {:x}", DEV_NAME, data[0]); }, + _ => { log::debug!("{}: unrecognized command {:x}", DEV_NAME, data[0]); }, } }, ATA_REG_FEATURE => { @@ -113,7 +114,7 @@ impl Addressable for AtaDevice { ATA_REG_DATA_BYTE => { // TODO implement writing }, - _ => { debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); }, + _ => { log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } Ok(()) } diff --git a/emulator/peripherals/mos/Cargo.toml b/emulator/peripherals/mos/Cargo.toml index 61e00be..264b9de 100644 --- a/emulator/peripherals/mos/Cargo.toml +++ b/emulator/peripherals/mos/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } diff --git a/emulator/peripherals/mos/src/mos6522.rs b/emulator/peripherals/mos/src/mos6522.rs index a5b3541..b223337 100644 --- a/emulator/peripherals/mos/src/mos6522.rs +++ b/emulator/peripherals/mos/src/mos6522.rs @@ -1,5 +1,7 @@ -use moa_core::{Error, System, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable, Signal, ObservableSignal, Observable, debug, warn}; +use femtos::{Instant, Duration}; + +use moa_core::{Error, System, Address, Addressable, Steppable, Transmutable, Signal, ObservableSignal, Observable}; const REG_OUTPUT_B: Address = 0x00; @@ -57,7 +59,7 @@ impl Addressable for Mos6522 { 0x10 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { REG_OUTPUT_B => { data[0] = self.port_b.borrow_mut().data; }, REG_OUTPUT_A => { data[0] = self.port_a.borrow_mut().data; }, @@ -66,15 +68,15 @@ impl Addressable for Mos6522 { REG_INT_FLAGS => { data[0] = self.interrupt_flags; }, REG_INT_ENABLE => { data[0] = self.interrupt_enable | 0x80; }, _ => { - warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); + log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); }, } - debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + log::debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { REG_OUTPUT_B => { self.port_b.borrow_mut().data = data[0]; self.port_b.notify(); }, REG_OUTPUT_A => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); }, @@ -91,7 +93,7 @@ impl Addressable for Mos6522 { }, REG_OUTPUT_A_NHS => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); }, _ => { - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } Ok(()) @@ -99,9 +101,9 @@ impl Addressable for Mos6522 { } impl Steppable for Mos6522 { - fn step(&mut self, _system: &System) -> Result { + fn step(&mut self, _system: &System) -> Result { - Ok(ClockDuration::from_micros(16_600)) + Ok(Duration::from_micros(16_600)) } } diff --git a/emulator/peripherals/motorola/Cargo.toml b/emulator/peripherals/motorola/Cargo.toml index 83df2c8..641b854 100644 --- a/emulator/peripherals/motorola/Cargo.toml +++ b/emulator/peripherals/motorola/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } diff --git a/emulator/peripherals/motorola/src/mc68681.rs b/emulator/peripherals/motorola/src/mc68681.rs index eebe962..5cce965 100644 --- a/emulator/peripherals/motorola/src/mc68681.rs +++ b/emulator/peripherals/motorola/src/mc68681.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, ClockTime, ClockDuration, Frequency, Address, Steppable, Addressable, Transmutable, debug}; +use femtos::{Instant, Duration, Frequency}; + +use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable}; use moa_core::host::Tty; @@ -205,7 +207,7 @@ impl MC68681 { } impl Steppable for MC68681 { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { if self.port_a.check_rx()? { self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, true); } @@ -250,7 +252,7 @@ impl Addressable for MC68681 { 0x30 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { REG_SRA_RD => { data[0] = self.port_a.status @@ -296,14 +298,14 @@ impl Addressable for MC68681 { } if addr != REG_SRA_RD && addr != REG_SRB_RD { - debug!("{}: read from {:0x} of {:0x}", DEV_NAME, addr, data[0]); + log::debug!("{}: read from {:0x} of {:0x}", DEV_NAME, addr, data[0]); } Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); match addr { REG_MR1A_MR2A | REG_MR1B_MR2B | REG_CSRA_WR | REG_CSRB_WR => { // NOTE we aren't simulating the serial speeds, so we aren't doing anything with these settings atm @@ -312,7 +314,7 @@ impl Addressable for MC68681 { self.acr = data[0]; } REG_TBA_WR => { - debug!("{}a: write {}", DEV_NAME, data[0] as char); + log::debug!("{}a: write {}", DEV_NAME, data[0] as char); self.port_a.send_byte(data[0]); self.set_interrupt_flag(ISR_CH_A_TX_READY, false); }, @@ -322,7 +324,7 @@ impl Addressable for MC68681 { } }, REG_TBB_WR => { - debug!("{}b: write {:x}", DEV_NAME, data[0]); + log::debug!("{}b: write {:x}", DEV_NAME, data[0]); self.port_b.send_byte(data[0]); self.set_interrupt_flag(ISR_CH_B_TX_READY, false); }, diff --git a/emulator/peripherals/yamaha/Cargo.toml b/emulator/peripherals/yamaha/Cargo.toml index 1873f24..edcfa6f 100644 --- a/emulator/peripherals/yamaha/Cargo.toml +++ b/emulator/peripherals/yamaha/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "^0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_audio = { path = "../../libraries/audio" } lazy_static = "1.4.0" diff --git a/emulator/peripherals/yamaha/src/sn76489.rs b/emulator/peripherals/yamaha/src/sn76489.rs index e8b11bc..58e796a 100644 --- a/emulator/peripherals/yamaha/src/sn76489.rs +++ b/emulator/peripherals/yamaha/src/sn76489.rs @@ -1,6 +1,7 @@ -use moa_core::{info, warn, debug}; -use moa_core::{System, Error, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable}; +use femtos::{Instant, Duration, Frequency}; + +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable}; use moa_core::host::{Host, Audio, Sample}; use moa_audio::SquareWave; @@ -30,13 +31,13 @@ impl ToneGenerator { self.on = true; self.attenuation = (attenuation << 1) as f32; } - info!("set attenuation to {} {}", self.attenuation, self.on); + log::info!("set attenuation to {} {}", self.attenuation, self.on); } fn set_counter(&mut self, count: usize) { let frequency = 3_579_545.0 / (count as f32 * 32.0); self.wave.set_frequency(frequency); - info!("set frequency to {}", frequency); + log::info!("set frequency to {}", frequency); } fn get_sample(&mut self) -> f32 { @@ -68,13 +69,13 @@ impl NoiseGenerator { self.on = true; self.attenuation = (attenuation << 1) as f32; } - info!("set attenuation to {} {}", self.attenuation, self.on); + log::info!("set attenuation to {} {}", self.attenuation, self.on); } fn set_control(&mut self, _bits: u8) { //let frequency = 3_579_545.0 / (count as f32 * 32.0); //self.wave.set_frequency(frequency); - //debug!("set frequency to {}", frequency); + //log::debug!("set frequency to {}", frequency); } fn get_sample(&mut self) -> f32 { @@ -107,7 +108,7 @@ impl Sn76489 { } impl Steppable for Sn76489 { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let rate = self.source.samples_per_second(); let samples = rate / 1000; @@ -130,7 +131,7 @@ impl Steppable for Sn76489 { } self.source.write_samples(system.clock, &buffer); - Ok(ClockDuration::from_millis(1)) // Every 1ms of simulated time + Ok(Duration::from_millis(1)) // Every 1ms of simulated time } } @@ -139,14 +140,14 @@ impl Addressable for Sn76489 { 0x01 } - fn read(&mut self, _clock: ClockTime, _addr: Address, _data: &mut [u8]) -> Result<(), Error> { - warn!("{}: !!! device can't be read", DEV_NAME); + fn read(&mut self, _clock: Instant, _addr: Address, _data: &mut [u8]) -> Result<(), Error> { + log::warn!("{}: !!! device can't be read", DEV_NAME); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { if addr != 0 { - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); return Ok(()); } @@ -172,7 +173,7 @@ impl Addressable for Sn76489 { _ => { }, } } - debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); Ok(()) } } diff --git a/emulator/peripherals/yamaha/src/ym2612.rs b/emulator/peripherals/yamaha/src/ym2612.rs index b297a47..71af736 100644 --- a/emulator/peripherals/yamaha/src/ym2612.rs +++ b/emulator/peripherals/yamaha/src/ym2612.rs @@ -18,9 +18,9 @@ use std::f32; use std::num::NonZeroU8; use std::collections::VecDeque; use lazy_static::lazy_static; +use femtos::{Instant, Duration, Frequency}; -use moa_core::{debug, warn}; -use moa_core::{System, Error, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable}; +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable}; use moa_core::host::{Host, Audio, Sample}; @@ -718,7 +718,7 @@ pub struct Ym2612 { selected_reg_0: Option, selected_reg_1: Option, - fm_clock_period: ClockDuration, + fm_clock_period: Duration, next_fm_clock: FmClock, envelope_clock: EnvelopeClock, @@ -772,10 +772,10 @@ impl Ym2612 { } impl Steppable for Ym2612 { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let rate = self.source.samples_per_second(); let samples = rate / 1000; - let sample_duration = ClockDuration::from_secs(1) / rate as u64; + let sample_duration = Duration::from_secs(1) / rate as u64; let mut sample = 0.0; let mut buffer = vec![Sample(0.0, 0.0); samples]; @@ -800,7 +800,7 @@ impl Steppable for Ym2612 { } self.source.write_samples(system.clock, &buffer); - Ok(ClockDuration::from_millis(1)) // Every 1ms of simulated time + Ok(Duration::from_millis(1)) // Every 1ms of simulated time } } @@ -826,12 +826,12 @@ impl Ym2612 { } impl Ym2612 { - pub fn set_register(&mut self, clock: ClockTime, bank: u8, reg: u8, data: u8) { + pub fn set_register(&mut self, clock: Instant, bank: u8, reg: u8, data: u8) { // Keep a copy for debugging purposes, and if the original values are needed self.registers[bank as usize * 256 + reg as usize] = data; println!("set {:x} to {:x}", bank as usize * 256 + reg as usize, data); - //warn!("{}: set reg {}{:x} to {:x}", DEV_NAME, bank, reg, data); + //log::warn!("{}: set reg {}{:x} to {:x}", DEV_NAME, bank, reg, data); match reg { 0x24 => { self.timer_a = (self.timer_a & 0x3) | ((data as u16) << 2); @@ -846,7 +846,7 @@ impl Ym2612 { //if (data >> 5) & 0x1 { // self.timer_b if data >> 6 == 0x01 { - warn!("{}: ch 3 special mode requested, but not implemented", DEV_NAME); + log::warn!("{}: ch 3 special mode requested, but not implemented", DEV_NAME); } }, @@ -856,7 +856,7 @@ impl Ym2612 { 0 | 1 | 2 => num, 4 | 5 | 6 => num - 1, _ => { - warn!("{}: attempted key on/off to invalid channel {}", DEV_NAME, num); + log::warn!("{}: attempted key on/off to invalid channel {}", DEV_NAME, num); return; }, }; @@ -969,7 +969,7 @@ impl Ym2612 { }, _ => { - warn!("{}: !!! unhandled write to register {:0x} with {:0x}", DEV_NAME, reg, data); + log::warn!("{}: !!! unhandled write to register {:0x} with {:0x}", DEV_NAME, reg, data); }, } } @@ -1020,22 +1020,22 @@ impl Addressable for Ym2612 { 0x04 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { 0 | 1 | 2 | 3 => { // Read the status byte (busy/overflow) data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8); } _ => { - warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); + log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); }, } - debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + log::debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { 0 => { self.selected_reg_0 = NonZeroU8::new(data[0]); @@ -1054,7 +1054,7 @@ impl Addressable for Ym2612 { } }, _ => { - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } Ok(()) diff --git a/emulator/peripherals/zilog/Cargo.toml b/emulator/peripherals/zilog/Cargo.toml index 70a518d..c49b55b 100644 --- a/emulator/peripherals/zilog/Cargo.toml +++ b/emulator/peripherals/zilog/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } diff --git a/emulator/peripherals/zilog/src/z8530.rs b/emulator/peripherals/zilog/src/z8530.rs index 0c74b43..9e3a55c 100644 --- a/emulator/peripherals/zilog/src/z8530.rs +++ b/emulator/peripherals/zilog/src/z8530.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable, warn, debug}; +use femtos::{Instant, Duration}; + +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable}; const DEV_NAME: &str = "z8530"; @@ -13,23 +15,23 @@ impl Addressable for Z8530 { 0x10 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { - warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); - debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { + log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); + log::debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); Ok(()) } } impl Steppable for Z8530 { - fn step(&mut self, _system: &System) -> Result { + fn step(&mut self, _system: &System) -> Result { - Ok(ClockDuration::from_secs(1)) + Ok(Duration::from_secs(1)) } } diff --git a/emulator/systems/computie/Cargo.toml b/emulator/systems/computie/Cargo.toml index b5e6a38..c5d67b7 100644 --- a/emulator/systems/computie/Cargo.toml +++ b/emulator/systems/computie/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_m68k = { path = "../../cpus/m68k" } moa_peripherals_generic = { path = "../../peripherals/generic" } diff --git a/emulator/systems/computie/src/system.rs b/emulator/systems/computie/src/system.rs index bcd22b6..e216482 100644 --- a/emulator/systems/computie/src/system.rs +++ b/emulator/systems/computie/src/system.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, Frequency, Debuggable, MemoryBlock, Device}; +use femtos::Frequency; + +use moa_core::{System, Error, Debuggable, MemoryBlock, Device}; use moa_core::host::Host; use moa_m68k::{M68k, M68kType}; diff --git a/emulator/systems/genesis/Cargo.toml b/emulator/systems/genesis/Cargo.toml index 715022f..27c505c 100644 --- a/emulator/systems/genesis/Cargo.toml +++ b/emulator/systems/genesis/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_peripherals_yamaha = { path = "../../peripherals/yamaha" } moa_m68k = { path = "../../cpus/m68k" } diff --git a/emulator/systems/genesis/src/peripherals/controllers.rs b/emulator/systems/genesis/src/peripherals/controllers.rs index eec06a7..348c11a 100644 --- a/emulator/systems/genesis/src/peripherals/controllers.rs +++ b/emulator/systems/genesis/src/peripherals/controllers.rs @@ -1,6 +1,7 @@ -use moa_core::{warn, info}; -use moa_core::{System, Error, ClockTime, ClockDuration, Signal, Address, Addressable, Steppable, Transmutable}; +use femtos::{Instant, Duration}; + +use moa_core::{System, Error, Signal, Address, Addressable, Steppable, Transmutable}; use moa_core::host::{self, Host, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver}; @@ -92,7 +93,7 @@ pub struct GenesisControllers { port_2: GenesisControllerPort, expansion: GenesisControllerPort, interrupt: Signal, - reset_timer: ClockDuration, + reset_timer: Duration, } impl GenesisControllers { @@ -106,7 +107,7 @@ impl GenesisControllers { port_2: GenesisControllerPort::default(), expansion: GenesisControllerPort::default(), interrupt: Signal::new(false), - reset_timer: ClockDuration::ZERO, + reset_timer: Duration::ZERO, }) } @@ -146,7 +147,7 @@ impl Addressable for GenesisControllers { 0x30 } - fn read(&mut self, _clock: ClockTime, mut addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, mut addr: Address, data: &mut [u8]) -> Result<(), Error> { // If the address is even, only the second byte (odd byte) will be meaningful let mut i = 0; if (addr % 2) == 0 { @@ -165,16 +166,16 @@ impl Addressable for GenesisControllers { REG_S_CTRL1 => { data[i] = self.port_1.s_ctrl | 0x02; }, REG_S_CTRL2 => { data[i] = self.port_2.s_ctrl | 0x02; }, REG_S_CTRL3 => { data[i] = self.expansion.s_ctrl | 0x02; }, - _ => { warn!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr); }, + _ => { log::warn!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr); }, } - info!("{}: read from register {:x} the value {:x}", DEV_NAME, addr, data[0]); + log::info!("{}: read from register {:x} the value {:x}", DEV_NAME, addr, data[0]); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - self.reset_timer = ClockDuration::ZERO; + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + self.reset_timer = Duration::ZERO; - info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + log::info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { REG_DATA1 => { self.port_1.set_data(data[0]); } REG_DATA2 => { self.port_2.set_data(data[0]); }, @@ -185,22 +186,22 @@ impl Addressable for GenesisControllers { REG_S_CTRL1 => { self.port_1.s_ctrl = data[0] & 0xF8; }, REG_S_CTRL2 => { self.port_2.s_ctrl = data[0] & 0xF8; }, REG_S_CTRL3 => { self.expansion.s_ctrl = data[0] & 0xF8; }, - _ => { warn!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr); }, + _ => { log::warn!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } Ok(()) } } impl Steppable for GenesisControllers { - fn step(&mut self, _system: &System) -> Result { - let duration = ClockDuration::from_micros(100); // Update every 100us + fn step(&mut self, _system: &System) -> Result { + let duration = Duration::from_micros(100); // Update every 100us while let Some(event) = self.receiver.receive() { self.process_event(event); } self.reset_timer += duration; - if self.reset_timer >= ClockDuration::from_micros(1_500) { + if self.reset_timer >= Duration::from_micros(1_500) { self.port_1.reset_count(); self.port_2.reset_count(); self.expansion.reset_count(); diff --git a/emulator/systems/genesis/src/peripherals/coprocessor.rs b/emulator/systems/genesis/src/peripherals/coprocessor.rs index 5788cff..2ca7f3f 100644 --- a/emulator/systems/genesis/src/peripherals/coprocessor.rs +++ b/emulator/systems/genesis/src/peripherals/coprocessor.rs @@ -1,9 +1,9 @@ use std::rc::Rc; use std::cell::{Cell, RefCell}; +use femtos::Instant; -use moa_core::{warn, info}; -use moa_core::{Bus, Signal, Error, ClockTime, Address, Addressable, Transmutable}; +use moa_core::{Bus, Signal, Error, Address, Addressable, Transmutable}; const DEV_NAME: &str = "coprocessor"; @@ -28,19 +28,19 @@ impl Addressable for CoprocessorCoordinator { 0x4000 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { 0x100 => { data[0] = if self.bus_request.get() && self.reset.get() { 0x01 } else { 0x00 }; }, - _ => { warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); }, + _ => { log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); }, } - info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + log::info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { 0x000 => { /* ROM vs DRAM mode */ }, 0x100 => { @@ -49,7 +49,7 @@ impl Addressable for CoprocessorCoordinator { 0x200 => { self.reset.set(data[0] == 0); }, - _ => { warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); }, + _ => { log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } Ok(()) } @@ -73,11 +73,11 @@ impl Addressable for CoprocessorBankRegister { 0x01 } - fn read(&mut self, _clock: ClockTime, _addr: Address, _data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, _addr: Address, _data: &mut [u8]) -> Result<(), Error> { Ok(()) } - fn write(&mut self, _clock: ClockTime, _addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, _clock: Instant, _addr: Address, data: &[u8]) -> Result<(), Error> { let value = ((self.base.get() >> 1) | ((data[0] as Address) << 23)) & 0xFF8000; //let value = ((self.base.get() << 1) | ((data[0] as Address) << 15)) & 0xFF8000; println!("New base is {:x}", value); @@ -115,11 +115,11 @@ impl Addressable for CoprocessorBankArea { 0x8000 } - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { self.bus.borrow_mut().read(clock, self.base.get() + addr, data) } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { self.bus.borrow_mut().write(clock, self.base.get() + addr, data) } } diff --git a/emulator/systems/genesis/src/peripherals/ym7101.rs b/emulator/systems/genesis/src/peripherals/ym7101.rs index 54e35de..e9d934f 100644 --- a/emulator/systems/genesis/src/peripherals/ym7101.rs +++ b/emulator/systems/genesis/src/peripherals/ym7101.rs @@ -1,6 +1,7 @@ -use moa_core::{debug, warn, error}; -use moa_core::{System, Error, EdgeSignal, ClockTime, ClockDuration, Frequency, Signal, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice}; +use femtos::{Instant, Duration, Frequency}; + +use moa_core::{System, Error, EdgeSignal, Signal, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice}; use moa_core::host::{self, Host, Pixel, PixelEncoding, Frame, FrameSender}; @@ -170,7 +171,7 @@ impl Ym7101Memory { 4 => Memory::Vsram, _ => Memory::Cram, }; - debug!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_dest_addr); + log::debug!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_dest_addr); if (self.transfer_type & 0x20) != 0 { if (self.transfer_type & 0x10) != 0 { self.set_dma_mode(DmaType::Copy); @@ -197,7 +198,7 @@ impl Ym7101Memory { } } self.transfer_dest_addr += self.transfer_auto_inc; - debug!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, data.len(), self.transfer_target, addr, data[0], data[1]); + log::debug!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, data.len(), self.transfer_target, addr, data[0], data[1]); Ok(()) } @@ -207,7 +208,7 @@ impl Ym7101Memory { self.transfer_fill_word = if data.len() >= 2 { read_beu16(data) } else { data[0] as u16 }; self.set_dma_mode(DmaType::Fill); } else { - debug!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.transfer_target, self.transfer_dest_addr, data); + log::debug!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.transfer_target, self.transfer_dest_addr, data); { let addr = self.transfer_dest_addr as usize; @@ -227,7 +228,7 @@ impl Ym7101Memory { (2, None) => { self.ctrl_port_buffer = Some(value) }, (2, Some(upper)) => self.setup_transfer(upper, read_beu16(data)), (4, None) => self.setup_transfer(value, read_beu16(&data[2..])), - _ => { error!("{}: !!! error when writing to control port with {} bytes of {:?}", DEV_NAME, data.len(), data); }, + _ => { log::error!("{}: !!! error when writing to control port with {} bytes of {:?}", DEV_NAME, data.len(), data); }, } Ok(()) } @@ -238,7 +239,7 @@ impl Ym7101Memory { match self.transfer_run { DmaType::Memory => { - debug!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.transfer_type, self.transfer_src_addr, self.transfer_target, self.transfer_dest_addr, self.transfer_remain); + log::debug!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.transfer_type, self.transfer_src_addr, self.transfer_target, self.transfer_dest_addr, self.transfer_remain); let mut bus = system.get_bus(); while self.transfer_remain > 0 { @@ -256,7 +257,7 @@ impl Ym7101Memory { } }, DmaType::Copy => { - debug!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, self.transfer_src_addr, self.transfer_dest_addr, self.transfer_remain); + log::debug!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, self.transfer_src_addr, self.transfer_dest_addr, self.transfer_remain); while self.transfer_remain > 0 { self.vram[self.transfer_dest_addr as usize] = self.vram[self.transfer_src_addr as usize]; self.transfer_dest_addr += self.transfer_auto_inc; @@ -265,14 +266,14 @@ impl Ym7101Memory { } }, DmaType::Fill => { - debug!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.transfer_dest_addr, self.transfer_remain, self.transfer_fill_word); + log::debug!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.transfer_dest_addr, self.transfer_remain, self.transfer_fill_word); while self.transfer_remain > 0 { self.vram[self.transfer_dest_addr as usize] = self.transfer_fill_word as u8; self.transfer_dest_addr += self.transfer_auto_inc; self.transfer_remain -= 1; } }, - _ => { warn!("{}: !!! error unexpected transfer mode {:x}", DEV_NAME, self.transfer_type); }, + _ => { log::warn!("{}: !!! error unexpected transfer mode {:x}", DEV_NAME, self.transfer_type); }, } self.set_dma_mode(DmaType::None); @@ -330,7 +331,7 @@ struct Ym7101State { sprites: Vec, sprites_by_line: Vec>, - last_clock: ClockTime, + last_clock: Instant, p_clock: u32, h_clock: u32, v_clock: u32, @@ -365,7 +366,7 @@ impl Default for Ym7101State { sprites: vec![], sprites_by_line: vec![], - last_clock: ClockTime::START, + last_clock: Instant::START, p_clock: 0, h_clock: 0, v_clock: 0, @@ -636,7 +637,7 @@ impl Sprite { } impl Steppable for Ym7101 { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let diff = system.clock.duration_since(self.state.last_clock).as_nanos() as u32; self.state.last_clock = system.clock; @@ -732,7 +733,7 @@ impl Ym7101 { fn set_register(&mut self, word: u16) { let reg = ((word & 0x1F00) >> 8) as usize; let data = (word & 0x00FF) as u8; - debug!("{}: register {:x} set to {:x}", DEV_NAME, reg, data); + log::debug!("{}: register {:x} set to {:x}", DEV_NAME, reg, data); self.update_register_value(reg, data); } @@ -808,14 +809,14 @@ impl Addressable for Ym7101 { 0x20 } - fn read(&mut self, _clock: ClockTime, mut addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, mut addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { // Read from Data Port 0x00 | 0x02 => self.state.memory.read_data_port(addr, data)?, // Read from Control Port 0x04 | 0x05 | 0x06 | 0x07 => { - debug!("{}: read status byte {:x}", DEV_NAME, self.state.status); + log::debug!("{}: read status byte {:x}", DEV_NAME, self.state.status); for item in data { *item = if (addr % 2) == 0 { (self.state.status >> 8) as u8 @@ -839,14 +840,14 @@ impl Addressable for Ym7101 { Ok(()) } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { match addr { // Write to Data Port 0x00 | 0x02 => self.state.memory.write_data_port(data)?, // Write to Control Port 0x04 | 0x06 => { - debug!("{}: write {} bytes to port {:x} with data {:?}", DEV_NAME, data.len(), addr, data); + log::debug!("{}: write {} bytes to port {:x} with data {:?}", DEV_NAME, data.len(), addr, data); let value = read_beu16(data); if (value & 0xC000) == 0x8000 { @@ -868,7 +869,7 @@ impl Addressable for Ym7101 { self.sn_sound.borrow_mut().as_addressable().unwrap().write(clock, 0, data)?; }, - _ => { warn!("{}: !!! unhandled write to {:x} with {:?}", DEV_NAME, addr, data); }, + _ => { log::warn!("{}: !!! unhandled write to {:x} with {:?}", DEV_NAME, addr, data); }, } Ok(()) } diff --git a/emulator/systems/genesis/src/system.rs b/emulator/systems/genesis/src/system.rs index 1399b7d..7f68a86 100644 --- a/emulator/systems/genesis/src/system.rs +++ b/emulator/systems/genesis/src/system.rs @@ -3,7 +3,9 @@ use std::mem; use std::rc::Rc; use std::cell::RefCell; -use moa_core::{System, Error, Frequency, MemoryBlock, Bus, Address, Addressable, Device}; +use femtos::Frequency; + +use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device}; use moa_core::host::Host; use moa_m68k::{M68k, M68kType}; diff --git a/emulator/systems/macintosh/Cargo.toml b/emulator/systems/macintosh/Cargo.toml index 6aa40ac..635bf6d 100644 --- a/emulator/systems/macintosh/Cargo.toml +++ b/emulator/systems/macintosh/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_m68k = { path = "../../cpus/m68k" } moa_peripherals_mos = { path = "../../peripherals/mos" } diff --git a/emulator/systems/macintosh/src/peripherals/iwm.rs b/emulator/systems/macintosh/src/peripherals/iwm.rs index 4717f4c..f8d3567 100644 --- a/emulator/systems/macintosh/src/peripherals/iwm.rs +++ b/emulator/systems/macintosh/src/peripherals/iwm.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable, info, warn}; +use femtos::{Instant, Duration}; + +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable}; //const CA0: u8 = 0x01; @@ -29,7 +31,7 @@ impl IWM { } else { self.state &= !mask; } - info!("{}: state is now {:x}", DEV_NAME, self.state); + log::info!("{}: state is now {:x}", DEV_NAME, self.state); } } @@ -38,7 +40,7 @@ impl Addressable for IWM { 0x10 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { self.flip_switches(addr); if (addr & 0x01) != 0 { @@ -64,17 +66,17 @@ impl Addressable for IWM { panic!(""); }, _ => { - warn!("{}: !!! unhandled read of {:0x} with state {:x}", DEV_NAME, addr, self.state); + log::warn!("{}: !!! unhandled read of {:0x} with state {:x}", DEV_NAME, addr, self.state); }, } - info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + log::info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { self.flip_switches(addr); - info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + log::info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); let i = data.len() - 1; match self.state & (Q7 | Q6 | ENABLE) { @@ -86,7 +88,7 @@ impl Addressable for IWM { self.mode = data[i] & 0x1f; }, _ => { - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } Ok(()) @@ -94,9 +96,9 @@ impl Addressable for IWM { } impl Steppable for IWM { - fn step(&mut self, _system: &System) -> Result { + fn step(&mut self, _system: &System) -> Result { // TODO implement - Ok(ClockDuration::from_secs(1)) + Ok(Duration::from_secs(1)) } } diff --git a/emulator/systems/macintosh/src/peripherals/mainboard.rs b/emulator/systems/macintosh/src/peripherals/mainboard.rs index 3f9249c..3e02a6b 100644 --- a/emulator/systems/macintosh/src/peripherals/mainboard.rs +++ b/emulator/systems/macintosh/src/peripherals/mainboard.rs @@ -1,8 +1,9 @@ use std::rc::Rc; use std::cell::RefCell; +use femtos::{Instant, Duration}; -use moa_core::{System, Bus, Error, Observable, ClockTime, ClockDuration, Address, Addressable, AddressRepeater, Steppable, Transmutable, Device}; +use moa_core::{System, Bus, Error, Observable, Address, Addressable, AddressRepeater, Steppable, Transmutable, Device}; use moa_peripherals_mos::Mos6522; use moa_peripherals_zilog::Z8530; @@ -18,7 +19,7 @@ pub struct Mainboard { iwm: IWM, via: Mos6522, phase_read: PhaseRead, - last_sec: ClockTime, + last_sec: Instant, } impl Mainboard { @@ -38,7 +39,7 @@ impl Mainboard { iwm, via, phase_read, - last_sec: ClockTime::START, + last_sec: Instant::START, }; mainboard.via.port_a.set_observer(move |port| { @@ -69,7 +70,7 @@ impl Addressable for Mainboard { 0x01000000 } - fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { if addr < 0x800000 { self.lower_bus.borrow_mut().read(clock, addr, data) } else if (0x900000..0xA00000).contains(&addr) { @@ -90,7 +91,7 @@ impl Addressable for Mainboard { } } - fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { if addr < 0x800000 { self.lower_bus.borrow_mut().write(clock, addr, data) } else if (0x900000..0xA00000).contains(&addr) { @@ -110,12 +111,12 @@ impl Addressable for Mainboard { } impl Steppable for Mainboard { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let elapsed = self.via.step(system)?; // TODO should this be 1 second, or a multiple of 979_200, which is an 8th of the CPU clock - if self.last_sec + ClockDuration::from_secs(1) > system.clock { - self.last_sec += ClockDuration::from_secs(1); + if self.last_sec + Duration::from_secs(1) > system.clock { + self.last_sec += Duration::from_secs(1); //let port_a = self.via.port_a.borrow_mut(); // TODO how will the ca1/ca2 cb1/cb2 pins work in the via system.get_interrupt_controller().set(true, 1, 25)?; @@ -146,13 +147,13 @@ impl Addressable for PhaseRead { 0x80000 } - fn read(&mut self, _clock: ClockTime, _addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, _addr: Address, data: &mut [u8]) -> Result<(), Error> { // TODO I'm not sure how this is supposed to work data[0] = 0x00; Ok(()) } - fn write(&mut self, _clock: ClockTime, _addr: Address, _data: &[u8]) -> Result<(), Error> { + fn write(&mut self, _clock: Instant, _addr: Address, _data: &[u8]) -> Result<(), Error> { // TODO I'm not sure how this is supposed to work Ok(()) } diff --git a/emulator/systems/macintosh/src/peripherals/video.rs b/emulator/systems/macintosh/src/peripherals/video.rs index 7cd93ea..596da9d 100644 --- a/emulator/systems/macintosh/src/peripherals/video.rs +++ b/emulator/systems/macintosh/src/peripherals/video.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable}; +use femtos::Duration; + +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable}; use moa_core::host::{self, Host, Frame, FrameSender, Pixel}; @@ -56,7 +58,7 @@ impl Iterator for BitIter { } impl Steppable for MacVideo { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let mut memory = system.get_bus(); let mut frame = Frame::new(SCRN_SIZE.0, SCRN_SIZE.1, self.frame_sender.encoding()); for y in 0..SCRN_SIZE.1 { @@ -67,7 +69,7 @@ impl Steppable for MacVideo { } self.frame_sender.add(system.clock, frame); - Ok(ClockDuration::from_micros(16_600)) + Ok(Duration::from_micros(16_600)) } } diff --git a/emulator/systems/macintosh/src/system.rs b/emulator/systems/macintosh/src/system.rs index a1e7175..d96ecf0 100644 --- a/emulator/systems/macintosh/src/system.rs +++ b/emulator/systems/macintosh/src/system.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, Frequency, MemoryBlock, Debuggable, Device}; +use femtos::Frequency; + +use moa_core::{System, Error, MemoryBlock, Debuggable, Device}; use moa_core::host::Host; use moa_m68k::{M68k, M68kType}; diff --git a/emulator/systems/trs80/Cargo.toml b/emulator/systems/trs80/Cargo.toml index bf6b8bd..dfc8da9 100644 --- a/emulator/systems/trs80/Cargo.toml +++ b/emulator/systems/trs80/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" +femtos = { path = "../../libraries/femtos" } moa_core = { path = "../../core" } moa_z80 = { path = "../../cpus/z80" } diff --git a/emulator/systems/trs80/src/peripherals/model1.rs b/emulator/systems/trs80/src/peripherals/model1.rs index c97f706..16f420f 100644 --- a/emulator/systems/trs80/src/peripherals/model1.rs +++ b/emulator/systems/trs80/src/peripherals/model1.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable, debug, warn}; +use femtos::{Instant, Duration}; + +use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable}; use moa_core::host::{self, Host, Frame, FrameSender, KeyEvent, EventReceiver}; use super::keymap; @@ -32,7 +34,7 @@ impl Addressable for Model1Keyboard { 0x420 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { if (0x20..=0xA0).contains(&addr) { let offset = addr - 0x20; data[0] = 0; @@ -46,25 +48,25 @@ impl Addressable for Model1Keyboard { if (offset & 0x80) != 0 { data[0] |= self.keyboard_mem[7]; } //info!("{}: read from keyboard {:x} of {:?}", DEV_NAME, addr, data); } else { - warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); + log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); } - debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + log::debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); Ok(()) } } impl Steppable for Model1Keyboard { - fn step(&mut self, _system: &System) -> Result { + fn step(&mut self, _system: &System) -> Result { while let Some(event) = self.receiver.receive() { keymap::record_key_press(&mut self.keyboard_mem, event.key, event.state); } - Ok(ClockDuration::from_millis(1)) + Ok(Duration::from_millis(1)) } } @@ -97,7 +99,7 @@ impl Model1Video { } impl Steppable for Model1Video { - fn step(&mut self, system: &System) -> Result { + fn step(&mut self, system: &System) -> Result { let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1, self.frame_sender.encoding()); for y in 0..16 { for x in 0..64 { @@ -108,7 +110,7 @@ impl Steppable for Model1Video { } self.frame_sender.add(system.clock, frame); - Ok(ClockDuration::from_micros(16_630)) + Ok(Duration::from_micros(16_630)) } } @@ -117,14 +119,14 @@ impl Addressable for Model1Video { 0x400 } - fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { data[0] = self.video_mem[addr as usize]; - debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + log::debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } - fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); self.video_mem[addr as usize] = data[0]; Ok(()) } diff --git a/emulator/systems/trs80/src/system.rs b/emulator/systems/trs80/src/system.rs index 180c541..e79de06 100644 --- a/emulator/systems/trs80/src/system.rs +++ b/emulator/systems/trs80/src/system.rs @@ -1,5 +1,7 @@ -use moa_core::{System, Error, Frequency, MemoryBlock, Device}; +use femtos::Frequency; + +use moa_core::{System, Error, MemoryBlock, Device}; use moa_core::host::Host; use moa_z80::{Z80, Z80Type}; diff --git a/tests/harte_tests/Cargo.toml b/tests/harte_tests/Cargo.toml index 798c818..9a6b67f 100644 --- a/tests/harte_tests/Cargo.toml +++ b/tests/harte_tests/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +femtos = { path = "../../emulator/libraries/femtos" } + moa_core = { path = "../../emulator/core" } moa_m68k = { path = "../../emulator/cpus/m68k" } serde = "1.0" diff --git a/tests/harte_tests/src/main.rs b/tests/harte_tests/src/main.rs index a2a1785..0c4490b 100644 --- a/tests/harte_tests/src/main.rs +++ b/tests/harte_tests/src/main.rs @@ -10,8 +10,9 @@ use std::fs::{self, File}; use clap::{Parser, ArgEnum}; use flate2::read::GzDecoder; use serde_derive::Deserialize; +use femtos::Frequency; -use moa_core::{System, Error, MemoryBlock, BusPort, Frequency, Address, Addressable, Steppable, Device}; +use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Steppable, Device}; use moa_m68k::{M68k, M68kType}; use moa_m68k::state::Status; diff --git a/tests/rad_tests/Cargo.toml b/tests/rad_tests/Cargo.toml index 9a88d32..82636a3 100644 --- a/tests/rad_tests/Cargo.toml +++ b/tests/rad_tests/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +femtos = { path = "../../emulator/libraries/femtos" } moa_core = { path = "../../emulator/core" } moa_z80 = { path = "../../emulator/cpus/z80" } serde = "1.0" diff --git a/tests/rad_tests/src/main.rs b/tests/rad_tests/src/main.rs index 68d1e6b..20b9555 100644 --- a/tests/rad_tests/src/main.rs +++ b/tests/rad_tests/src/main.rs @@ -12,8 +12,9 @@ use std::fs::{self, File}; use clap::Parser; use flate2::read::GzDecoder; use serde_derive::Deserialize; +use femtos::Frequency; -use moa_core::{System, Error, MemoryBlock, Bus, BusPort, Frequency, Address, Addressable, Steppable, Device}; +use moa_core::{System, Error, MemoryBlock, Bus, BusPort, Address, Addressable, Steppable, Device}; use moa_z80::{Z80, Z80Type}; use moa_z80::instructions::InterruptMode;