Split clocks into `femtos` crate

This commit is contained in:
transistor 2024-02-24 13:02:09 -08:00
parent 3965a95c8c
commit 9ff431ebc6
72 changed files with 1264 additions and 643 deletions

33
Cargo.lock generated
View File

@ -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",

View File

@ -5,4 +5,5 @@ edition = "2021"
[dependencies]
log = "0.4"
femtos = { path = "../libraries/femtos" }

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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()
}
}

View File

@ -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]) {}
}

View File

@ -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};

View File

@ -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) {

View File

@ -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);
}

View File

@ -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,
}
}

View File

@ -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" }

View File

@ -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(())
}

View File

@ -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;
}

View File

@ -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,
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -4,4 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4"
femtos = { path = "../../libraries/femtos" }
moa_core = { path = "../../core" }

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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};

View File

@ -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;

View File

@ -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 }

View File

@ -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()
}

View File

@ -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();

View File

@ -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"] }

View File

@ -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;

View File

@ -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;

View File

@ -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"] }

View File

@ -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(())
}

View File

@ -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;

View File

@ -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",
]

View File

@ -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"] }

View File

@ -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());

View File

@ -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]

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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);
}
}

View File

@ -4,4 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4"
femtos = { path = "../../libraries/femtos" }
moa_core = { path = "../../core" }

View File

@ -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(())
}

View File

@ -4,4 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4"
femtos = { path = "../../libraries/femtos" }
moa_core = { path = "../../core" }

View File

@ -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))
}
}

View File

@ -4,4 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4"
femtos = { path = "../../libraries/femtos" }
moa_core = { path = "../../core" }

View File

@ -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);
},

View File

@ -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"

View File

@ -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(())
}
}

View File

@ -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(())

View File

@ -4,4 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4"
femtos = { path = "../../libraries/femtos" }
moa_core = { path = "../../core" }

View File

@ -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))
}
}

View File

@ -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" }

View File

@ -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};

View File

@ -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" }

View File

@ -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();

View File

@ -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)
}
}

View File

@ -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(())
}

View File

@ -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};

View File

@ -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" }

View File

@ -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))
}
}

View File

@ -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(())
}

View File

@ -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))
}
}

View File

@ -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};

View File

@ -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" }

View File

@ -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(())
}

View File

@ -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};

View File

@ -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"

View File

@ -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;

View File

@ -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"

View File

@ -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;