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