Split clocks into `femtos` crate
This commit is contained in:
parent
3965a95c8c
commit
9ff431ebc6
|
@ -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",
|
||||
|
|
|
@ -5,4 +5,5 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = { path = "../libraries/femtos" }
|
||||
|
||||
|
|
|
@ -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<Self> {
|
||||
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<Self> {
|
||||
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<u64> for ClockDuration {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: u64) -> Self::Output {
|
||||
Self::from_femtos(self.femtos * rhs as Femtos)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<u64> for ClockDuration {
|
||||
fn mul_assign(&mut self, rhs: u64) {
|
||||
*self = Self::from_femtos(self.femtos * rhs as Femtos);
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u64> for ClockDuration {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: u64) -> Self::Output {
|
||||
Self::from_femtos(self.femtos / rhs as Femtos)
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<u64> for ClockDuration {
|
||||
fn div_assign(&mut self, rhs: u64) {
|
||||
*self = Self::from_femtos(self.femtos / rhs as Femtos);
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<ClockDuration> for ClockDuration {
|
||||
type Output = u64;
|
||||
|
||||
fn div(self, rhs: ClockDuration) -> Self::Output {
|
||||
(self.femtos / rhs.femtos) as u64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<ClockDuration> for Duration {
|
||||
fn from(value: ClockDuration) -> Self {
|
||||
Duration::from_nanos(value.as_nanos())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Duration> 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<Self> {
|
||||
match self.0.checked_add(duration) {
|
||||
Some(duration) => Some(Self(duration)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn checked_sub(self, duration: ClockDuration) -> Option<Self> {
|
||||
match self.0.checked_sub(duration) {
|
||||
Some(duration) => Some(Self(duration)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<ClockDuration> for ClockTime {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: ClockDuration) -> Self::Output {
|
||||
Self(self.0.add(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<ClockDuration> 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<u32> for Frequency {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: u32) -> Self::Output {
|
||||
Self::from_hz(self.hertz * rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<u32> for Frequency {
|
||||
fn mul_assign(&mut self, rhs: u32) {
|
||||
*self = Self::from_hz(self.hertz * rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u32> for Frequency {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: u32) -> Self::Output {
|
||||
Self::from_hz(self.hertz / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<u32> for Frequency {
|
||||
fn div_assign(&mut self, rhs: u32) {
|
||||
*self = Self::from_hz(self.hertz / rhs);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ClockDuration, Error>;
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error>;
|
||||
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<u8, Error> {
|
||||
fn read_u8(&mut self, clock: Instant, addr: Address) -> Result<u8, Error> {
|
||||
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<u16, Error> {
|
||||
fn read_beu16(&mut self, clock: Instant, addr: Address) -> Result<u16, Error> {
|
||||
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<u16, Error> {
|
||||
fn read_leu16(&mut self, clock: Instant, addr: Address) -> Result<u16, Error> {
|
||||
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<u32, Error> {
|
||||
fn read_beu32(&mut self, clock: Instant, addr: Address) -> Result<u32, Error> {
|
||||
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<u32, Error> {
|
||||
fn read_leu32(&mut self, clock: Instant, addr: Address) -> Result<u32, Error> {
|
||||
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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T>(Arc<Mutex<VecDeque<(ClockTime, T)>>>, usize);
|
||||
pub struct ClockedQueue<T>(Arc<Mutex<VecDeque<(Instant, T)>>>, usize);
|
||||
|
||||
impl<T: Clone> ClockedQueue<T> {
|
||||
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<T: Clone> ClockedQueue<T> {
|
|||
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<ClockTime> {
|
||||
pub fn peek_clock(&self) -> Option<Instant> {
|
||||
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]) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::cell::{Cell, RefCell, RefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ClockTime;
|
||||
use femtos::Instant;
|
||||
|
||||
pub trait Observable<T> {
|
||||
fn set_observer<F>(&self, f: F)
|
||||
|
@ -125,12 +125,12 @@ impl Observable<bool> for ObservableEdgeSignal {
|
|||
|
||||
|
||||
pub trait SignalReceiver<T> {
|
||||
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<T> {
|
||||
fn set_at(clock: ClockTime, value: T);
|
||||
fn set_at(clock: Instant, value: T);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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<String, Device>,
|
||||
pub event_queue: Vec<NextStep>,
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -4,4 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = { path = "../../libraries/femtos" }
|
||||
moa_core = { path = "../../core" }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
let duration = ClockDuration::from_millis(1);
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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<RefCell<Emulator>>) {
|
|||
};
|
||||
|
||||
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<RefCell<Emulator>>) {
|
|||
fn update(emulator: Rc<RefCell<Emulator>>) {
|
||||
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());
|
||||
|
|
|
@ -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 <trans@jabberwocky.ca>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/transistorfet/moa"
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||
|
|
@ -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<Self> {
|
||||
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<Self> {
|
||||
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<u64> for Duration {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: u64) -> Self::Output {
|
||||
Self::from_femtos(self.femtos * rhs as Femtos)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<u64> for Duration {
|
||||
fn mul_assign(&mut self, rhs: u64) {
|
||||
*self = Self::from_femtos(self.femtos * rhs as Femtos);
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u64> for Duration {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: u64) -> Self::Output {
|
||||
Self::from_femtos(self.femtos / rhs as Femtos)
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<u64> for Duration {
|
||||
fn div_assign(&mut self, rhs: u64) {
|
||||
*self = Self::from_femtos(self.femtos / rhs as Femtos);
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Duration> for Duration {
|
||||
type Output = u64;
|
||||
|
||||
fn div(self, rhs: Duration) -> Self::Output {
|
||||
(self.femtos / rhs.femtos) as u64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<Duration> for time::Duration {
|
||||
fn from(value: Duration) -> Self {
|
||||
time::Duration::from_nanos(value.as_nanos())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<time::Duration> 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<Self> {
|
||||
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<Self> {
|
||||
match self.0.checked_sub(duration) {
|
||||
Some(duration) => Some(Self(duration)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Duration> for Instant {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Duration) -> Self::Output {
|
||||
Self(self.0.add(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Duration> 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<u32> for Frequency {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: u32) -> Self::Output {
|
||||
Self::from_hz(self.hertz * rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<u32> for Frequency {
|
||||
fn mul_assign(&mut self, rhs: u32) {
|
||||
*self = Self::from_hz(self.hertz * rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u32> for Frequency {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: u32) -> Self::Output {
|
||||
Self::from_hz(self.hertz / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<u32> for Frequency {
|
||||
fn div_assign(&mut self, rhs: u32) {
|
||||
*self = Self::from_hz(self.hertz / rhs);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,4 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = { path = "../../libraries/femtos" }
|
||||
moa_core = { path = "../../core" }
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -4,4 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = { path = "../../libraries/femtos" }
|
||||
moa_core = { path = "../../core" }
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||
|
||||
Ok(ClockDuration::from_micros(16_600))
|
||||
Ok(Duration::from_micros(16_600))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,4 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = { path = "../../libraries/femtos" }
|
||||
moa_core = { path = "../../core" }
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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);
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NonZeroU8>,
|
||||
selected_reg_1: Option<NonZeroU8>,
|
||||
|
||||
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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
|
|
|
@ -4,4 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = { path = "../../libraries/femtos" }
|
||||
moa_core = { path = "../../core" }
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||
|
||||
Ok(ClockDuration::from_secs(1))
|
||||
Ok(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<bool>,
|
||||
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<ClockDuration, Error> {
|
||||
let duration = ClockDuration::from_micros(100); // Update every 100us
|
||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||
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();
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Sprite>,
|
||||
sprites_by_line: Vec<Vec<usize>>,
|
||||
|
||||
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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||
// TODO implement
|
||||
Ok(ClockDuration::from_secs(1))
|
||||
Ok(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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" }
|
||||
|
||||
|
|
|
@ -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<ClockDuration, Error> {
|
||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||
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<ClockDuration, Error> {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue