mirror of
https://github.com/transistorfet/moa.git
synced 2024-05-29 04:41:29 +00:00
Added new clock types similar to Duration
This commit is contained in:
parent
f298d1b341
commit
07a675fab5
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ junk/
|
||||||
|
|
||||||
perf.data
|
perf.data
|
||||||
perf.data.old
|
perf.data.old
|
||||||
|
flamegraph*.svg
|
||||||
|
|
||||||
binaries/*/*.asm
|
binaries/*/*.asm
|
||||||
binaries/*/*.bin
|
binaries/*/*.bin
|
||||||
|
|
265
emulator/core/src/clock.rs
Normal file
265
emulator/core/src/clock.rs
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
/// Clock time and duration types for simulation with femtosecond accurancy
|
||||||
|
///
|
||||||
|
|
||||||
|
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 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]
|
||||||
|
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 fn checked_add(self, rhs: Self) -> Option<Self> {
|
||||||
|
self.femtos.checked_add(rhs.femtos).map(Self::from_femtos)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||||
|
self.femtos.checked_sub(rhs.femtos).map(Self::from_femtos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for ClockDuration {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
self.checked_add(rhs).expect("clock duration overflow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for ClockDuration {
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
*self = self.checked_add(rhs).expect("clock duration overflow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for ClockDuration {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
self.checked_sub(rhs).expect("clock duration overflow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign for ClockDuration {
|
||||||
|
fn sub_assign(&mut self, rhs: Self) {
|
||||||
|
*self = self.checked_sub(rhs).expect("clock duration overflow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// 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 fn as_duration(self) -> ClockDuration {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn duration_since(self, other: Self) -> ClockDuration {
|
||||||
|
self.0 - other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ impl Debugger {
|
||||||
pub fn run_debugger(&mut self, system: &System, target: TransmutableBox) -> Result<(), Error> {
|
pub fn run_debugger(&mut self, system: &System, target: TransmutableBox) -> Result<(), Error> {
|
||||||
let mut target = target.borrow_mut();
|
let mut target = target.borrow_mut();
|
||||||
let debug_obj = target.as_debuggable().unwrap();
|
let debug_obj = target.as_debuggable().unwrap();
|
||||||
println!("@ {} ns", system.clock);
|
println!("@ {} ns", system.clock.as_duration().as_nanos());
|
||||||
debug_obj.print_current_step(system)?;
|
debug_obj.print_current_step(system)?;
|
||||||
|
|
||||||
if self.trace_only {
|
if self.trace_only {
|
||||||
|
|
|
@ -4,14 +4,9 @@ use std::cell::RefCell;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
|
use crate::clock::ClockDuration;
|
||||||
|
|
||||||
|
|
||||||
/// The time in nanoseconds that have elapsed since the start of the simulation
|
|
||||||
pub type Clock = u64;
|
|
||||||
|
|
||||||
/// The time in nanoseconds until the `step()` method should be called again
|
|
||||||
pub type ClockElapsed = u64;
|
|
||||||
|
|
||||||
/// A universal memory address used by the Addressable trait
|
/// A universal memory address used by the Addressable trait
|
||||||
pub type Address = u64;
|
pub type Address = u64;
|
||||||
|
|
||||||
|
@ -21,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<ClockElapsed, Error>;
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error>;
|
||||||
fn on_error(&mut self, _system: &System) { }
|
fn on_error(&mut self, _system: &System) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,3 +202,11 @@ pub fn wrap_transmutable<T: Transmutable + 'static>(value: T) -> TransmutableBox
|
||||||
Rc::new(RefCell::new(Box::new(value)))
|
Rc::new(RefCell::new(Box::new(value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Device(TransmutableBox);
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::host::traits::{BlitableSurface, ClockedQueue, WindowUpdater};
|
use crate::host::traits::{BlitableSurface, ClockedQueue, WindowUpdater};
|
||||||
use crate::Clock;
|
use crate::ClockTime;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
pub const MASK_COLOUR: u32 = 0xFFFFFFFF;
|
pub const MASK_COLOUR: u32 = 0xFFFFFFFF;
|
||||||
|
@ -92,13 +92,13 @@ impl BlitableSurface for Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blit<B: Iterator<Item = u32>>(&mut self, pos_x: u32, pos_y: u32, mut bitmap: B, width: u32, height: u32) {
|
fn blit<B: Iterator<Item = Pixel>>(&mut self, pos_x: u32, pos_y: u32, mut bitmap: B, width: u32, height: u32) {
|
||||||
for y in pos_y..(pos_y + height) {
|
for y in pos_y..(pos_y + height) {
|
||||||
for x in pos_x..(pos_x + width) {
|
for x in pos_x..(pos_x + width) {
|
||||||
match bitmap.next().unwrap() {
|
match bitmap.next().unwrap() {
|
||||||
MASK_COLOUR => {}
|
Pixel::Mask => {}
|
||||||
value if x < self.width && y < self.height => {
|
value if x < self.width && y < self.height => {
|
||||||
self.bitmap[(x + (y * self.width)) as usize] = value;
|
self.bitmap[(x + (y * self.width)) as usize] = value.encode(self.encoding);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ impl BlitableSurface for Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self, value: u32) {
|
fn clear(&mut self, value: Pixel) {
|
||||||
let value = if value == MASK_COLOUR { 0 } else { value };
|
let value = value.encode(self.encoding);
|
||||||
self.bitmap.iter_mut().for_each(|pixel| *pixel = value);
|
self.bitmap.iter_mut().for_each(|pixel| *pixel = value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,11 +132,11 @@ impl FrameQueue {
|
||||||
*self.encoding.lock().unwrap()
|
*self.encoding.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&self, clock: Clock, frame: Frame) {
|
pub fn add(&self, clock: ClockTime, frame: Frame) {
|
||||||
self.queue.push(clock, frame);
|
self.queue.push(clock, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn latest(&self) -> Option<(Clock, Frame)> {
|
pub fn latest(&self) -> Option<(ClockTime, Frame)> {
|
||||||
self.queue.pop_latest()
|
self.queue.pop_latest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::{Clock, Error};
|
use crate::{ClockTime, Error};
|
||||||
use crate::host::gfx::{PixelEncoding, Pixel, Frame};
|
use crate::host::gfx::{PixelEncoding, Pixel, Frame};
|
||||||
use crate::host::keys::KeyEvent;
|
use crate::host::keys::KeyEvent;
|
||||||
use crate::host::controllers::{ControllerDevice, ControllerEvent};
|
use crate::host::controllers::{ControllerDevice, ControllerEvent};
|
||||||
|
@ -62,7 +62,7 @@ pub trait MouseUpdater: Send {
|
||||||
pub trait Audio {
|
pub trait Audio {
|
||||||
fn samples_per_second(&self) -> usize;
|
fn samples_per_second(&self) -> usize;
|
||||||
fn space_available(&self) -> usize;
|
fn space_available(&self) -> usize;
|
||||||
fn write_samples(&mut self, clock: Clock, buffer: &[f32]);
|
fn write_samples(&mut self, clock: ClockTime, buffer: &[f32]);
|
||||||
fn flush(&mut self);
|
fn flush(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,8 @@ pub trait BlitableSurface {
|
||||||
fn set_size(&mut self, width: u32, height: u32);
|
fn set_size(&mut self, width: u32, height: u32);
|
||||||
fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel);
|
fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel);
|
||||||
fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32);
|
fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32);
|
||||||
fn blit<B: Iterator<Item=u32>>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32);
|
fn blit<B: Iterator<Item=Pixel>>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32);
|
||||||
fn clear(&mut self, value: u32);
|
fn clear(&mut self, value: Pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,26 +99,26 @@ impl<T: Copy> HostData<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct ClockedQueue<T>(Arc<Mutex<VecDeque<(Clock, T)>>>);
|
pub struct ClockedQueue<T>(Arc<Mutex<VecDeque<(ClockTime, T)>>>);
|
||||||
|
|
||||||
impl<T: Clone> ClockedQueue<T> {
|
impl<T: Clone> ClockedQueue<T> {
|
||||||
pub fn push(&self, clock: Clock, data: T) {
|
pub fn push(&self, clock: ClockTime, data: T) {
|
||||||
self.0.lock().unwrap().push_back((clock, data));
|
self.0.lock().unwrap().push_back((clock, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_next(&self) -> Option<(Clock, T)> {
|
pub fn pop_next(&self) -> Option<(ClockTime, T)> {
|
||||||
self.0.lock().unwrap().pop_front()
|
self.0.lock().unwrap().pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_latest(&self) -> Option<(Clock, T)> {
|
pub fn pop_latest(&self) -> Option<(ClockTime, T)> {
|
||||||
self.0.lock().unwrap().drain(..).last()
|
self.0.lock().unwrap().drain(..).last()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpop(&mut self, clock: Clock, data: T) {
|
pub fn unpop(&mut self, clock: ClockTime, data: T) {
|
||||||
self.0.lock().unwrap().push_front((clock, data));
|
self.0.lock().unwrap().push_front((clock, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek_clock(&self) -> Option<Clock> {
|
pub fn peek_clock(&self) -> Option<ClockTime> {
|
||||||
self.0.lock().unwrap().front().map(|(clock, _)| *clock)
|
self.0.lock().unwrap().front().map(|(clock, _)| *clock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ impl Audio for DummyAudio {
|
||||||
4800
|
4800
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_samples(&mut self, _clock: Clock, _buffer: &[f32]) {}
|
fn write_samples(&mut self, _clock: ClockTime, _buffer: &[f32]) {}
|
||||||
|
|
||||||
fn flush(&mut self) {}
|
fn flush(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
|
mod clock;
|
||||||
mod debugger;
|
mod debugger;
|
||||||
mod devices;
|
mod devices;
|
||||||
mod interrupts;
|
mod interrupts;
|
||||||
|
@ -14,8 +15,9 @@ pub mod timers;
|
||||||
|
|
||||||
pub use log::{trace, debug, info, warn, error};
|
pub use log::{trace, debug, info, warn, error};
|
||||||
|
|
||||||
|
pub use crate::clock::{ClockTime, ClockDuration, Frequency};
|
||||||
pub use crate::debugger::Debugger;
|
pub use crate::debugger::Debugger;
|
||||||
pub use crate::devices::{Clock, ClockElapsed, Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox};
|
pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox};
|
||||||
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};
|
||||||
pub use crate::error::{Error, ErrorType};
|
pub use crate::error::{Error, ErrorType};
|
||||||
pub use crate::interrupts::InterruptController;
|
pub use crate::interrupts::InterruptController;
|
||||||
|
|
|
@ -8,11 +8,12 @@ use crate::debugger::Debugger;
|
||||||
use crate::signals::EdgeSignal;
|
use crate::signals::EdgeSignal;
|
||||||
use crate::error::{Error, ErrorType};
|
use crate::error::{Error, ErrorType};
|
||||||
use crate::interrupts::InterruptController;
|
use crate::interrupts::InterruptController;
|
||||||
use crate::devices::{Clock, ClockElapsed, Address, TransmutableBox};
|
use crate::clock::{ClockTime, ClockDuration};
|
||||||
|
use crate::devices::{Address, TransmutableBox};
|
||||||
|
|
||||||
|
|
||||||
pub struct System {
|
pub struct System {
|
||||||
pub clock: Clock,
|
pub clock: ClockTime,
|
||||||
pub devices: HashMap<String, TransmutableBox>,
|
pub devices: HashMap<String, TransmutableBox>,
|
||||||
pub event_queue: Vec<NextStep>,
|
pub event_queue: Vec<NextStep>,
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ pub struct System {
|
||||||
impl Default for System {
|
impl Default for System {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
clock: 0,
|
clock: ClockTime::START,
|
||||||
devices: HashMap::new(),
|
devices: HashMap::new(),
|
||||||
event_queue: vec![],
|
event_queue: vec![],
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ impl System {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_for(&mut self, elapsed: ClockElapsed) -> Result<(), Error> {
|
pub fn run_for(&mut self, elapsed: ClockDuration) -> Result<(), Error> {
|
||||||
let target = self.clock + elapsed;
|
let target = self.clock + elapsed;
|
||||||
|
|
||||||
while self.clock < target {
|
while self.clock < target {
|
||||||
|
@ -147,7 +148,7 @@ impl System {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_loop(&mut self) {
|
pub fn run_loop(&mut self) {
|
||||||
self.run_for(u64::MAX).unwrap();
|
self.run_for(ClockDuration::from_nanos(u64::MAX)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit_error(&mut self) {
|
pub fn exit_error(&mut self) {
|
||||||
|
@ -188,14 +189,14 @@ impl System {
|
||||||
|
|
||||||
|
|
||||||
pub struct NextStep {
|
pub struct NextStep {
|
||||||
pub next_clock: Clock,
|
pub next_clock: ClockTime,
|
||||||
pub device: TransmutableBox,
|
pub device: TransmutableBox,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NextStep {
|
impl NextStep {
|
||||||
pub fn new(device: TransmutableBox) -> Self {
|
pub fn new(device: TransmutableBox) -> Self {
|
||||||
Self {
|
Self {
|
||||||
next_clock: 0,
|
next_clock: ClockTime::START,
|
||||||
device,
|
device,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
use moa_core::debug;
|
use moa_core::debug;
|
||||||
use moa_core::{System, Error, ErrorType, ClockElapsed, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable};
|
use moa_core::{System, Error, ErrorType, ClockDuration, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable};
|
||||||
|
|
||||||
use crate::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority, FunctionCode, MemType, MemAccess};
|
use crate::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority, FunctionCode, MemType, MemAccess};
|
||||||
use crate::instructions::{
|
use crate::instructions::{
|
||||||
|
@ -30,7 +30,7 @@ pub enum Used {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for M68k {
|
impl Steppable for M68k {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
self.step_internal(system)
|
self.step_internal(system)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ impl M68k {
|
||||||
self.state.status != Status::Stopped
|
self.state.status != Status::Stopped
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step_internal(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
pub fn step_internal(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
match self.state.status {
|
match self.state.status {
|
||||||
Status::Init => self.init(),
|
Status::Init => self.init(),
|
||||||
Status::Stopped => Err(Error::new("CPU stopped")),
|
Status::Stopped => Err(Error::new("CPU stopped")),
|
||||||
|
@ -73,7 +73,7 @@ impl M68k {
|
||||||
// TODO match arm conditional is temporary: illegal instructions generate a top level error in order to debug and fix issues with decode
|
// TODO match arm conditional is temporary: illegal instructions generate a top level error in order to debug and fix issues with decode
|
||||||
//Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
|
//Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
|
||||||
self.exception(native as u8, false)?;
|
self.exception(native as u8, false)?;
|
||||||
Ok(4)
|
Ok(self.frequency.period_duration() * 4)
|
||||||
},
|
},
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
|
@ -81,14 +81,14 @@ impl M68k {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<ClockElapsed, Error> {
|
pub fn init(&mut self) -> Result<ClockDuration, Error> {
|
||||||
self.state.ssp = self.port.read_beu32(0)?;
|
self.state.ssp = self.port.read_beu32(0)?;
|
||||||
self.state.pc = self.port.read_beu32(4)?;
|
self.state.pc = self.port.read_beu32(4)?;
|
||||||
self.state.status = Status::Running;
|
self.state.status = Status::Running;
|
||||||
Ok(16)
|
Ok(self.frequency.period_duration() * 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle_one(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
pub fn cycle_one(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
self.timer.cycle.start();
|
self.timer.cycle.start();
|
||||||
self.decode_next()?;
|
self.decode_next()?;
|
||||||
self.execute_current()?;
|
self.execute_current()?;
|
||||||
|
@ -99,7 +99,7 @@ impl M68k {
|
||||||
|
|
||||||
self.check_pending_interrupts(system)?;
|
self.check_pending_interrupts(system)?;
|
||||||
self.check_breakpoints(system);
|
self.check_breakpoints(system);
|
||||||
Ok((1_000_000_000 / self.frequency as u64) * self.timing.calculate_clocks(false, 1) as ClockElapsed)
|
Ok(self.frequency.period_duration() * self.timing.calculate_clocks(false, 1) as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_pending_interrupts(&mut self, system: &System) -> Result<(), Error> {
|
pub fn check_pending_interrupts(&mut self, system: &System) -> Result<(), Error> {
|
||||||
|
@ -115,7 +115,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);
|
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)?;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{Address, BusPort};
|
use moa_core::{Address, BusPort, Frequency};
|
||||||
use moa_core::timers::CpuTimer;
|
use moa_core::timers::CpuTimer;
|
||||||
|
|
||||||
use crate::instructions::Size;
|
use crate::instructions::Size;
|
||||||
|
@ -125,7 +125,7 @@ pub struct M68kState {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct M68k {
|
pub struct M68k {
|
||||||
pub cputype: M68kType,
|
pub cputype: M68kType,
|
||||||
pub frequency: u32,
|
pub frequency: Frequency,
|
||||||
pub state: M68kState,
|
pub state: M68kState,
|
||||||
pub decoder: M68kDecoder,
|
pub decoder: M68kDecoder,
|
||||||
pub timing: M68kInstructionTiming,
|
pub timing: M68kInstructionTiming,
|
||||||
|
@ -155,7 +155,7 @@ impl Default for M68kState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl M68k {
|
impl M68k {
|
||||||
pub fn new(cputype: M68kType, frequency: u32, port: BusPort) -> M68k {
|
pub fn new(cputype: M68kType, frequency: Frequency, port: BusPort) -> M68k {
|
||||||
M68k {
|
M68k {
|
||||||
cputype,
|
cputype,
|
||||||
frequency,
|
frequency,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, ErrorType, ClockElapsed, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
|
use moa_core::{System, Error, ErrorType, ClockDuration, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
|
||||||
|
|
||||||
use crate::decode::{Condition, Instruction, LoadTarget, Target, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction};
|
use crate::decode::{Condition, Instruction, LoadTarget, Target, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction};
|
||||||
use crate::state::{Z80, Status, Flags, Register};
|
use crate::state::{Z80, Status, Flags, Register};
|
||||||
|
@ -19,19 +19,16 @@ enum RotateType {
|
||||||
|
|
||||||
|
|
||||||
impl Steppable for Z80 {
|
impl Steppable for Z80 {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
let clocks = if self.reset.get() {
|
let clocks = if self.reset.get() {
|
||||||
//println!("RESET");
|
|
||||||
self.reset()?
|
self.reset()?
|
||||||
} else if self.bus_request.get() {
|
} else if self.bus_request.get() {
|
||||||
//println!("BUS REQ");
|
|
||||||
4
|
4
|
||||||
} else {
|
} else {
|
||||||
//println!("RUNNING {:?}", self.decoder.instruction);
|
|
||||||
self.step_internal(system)?
|
self.step_internal(system)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((1_000_000_000 / self.frequency as ClockElapsed) * clocks as ClockElapsed)
|
Ok(self.frequency.period_duration() * clocks as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_error(&mut self, _system: &System) {
|
fn on_error(&mut self, _system: &System) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{Address, BusPort, Signal};
|
use moa_core::{Address, BusPort, Signal, Frequency};
|
||||||
|
|
||||||
use crate::decode::Z80Decoder;
|
use crate::decode::Z80Decoder;
|
||||||
use crate::debugger::Z80Debugger;
|
use crate::debugger::Z80Debugger;
|
||||||
|
@ -106,7 +106,7 @@ impl Z80State {
|
||||||
|
|
||||||
pub struct Z80 {
|
pub struct Z80 {
|
||||||
pub cputype: Z80Type,
|
pub cputype: Z80Type,
|
||||||
pub frequency: u32,
|
pub frequency: Frequency,
|
||||||
pub state: Z80State,
|
pub state: Z80State,
|
||||||
pub decoder: Z80Decoder,
|
pub decoder: Z80Decoder,
|
||||||
pub debugger: Z80Debugger,
|
pub debugger: Z80Debugger,
|
||||||
|
@ -116,7 +116,7 @@ pub struct Z80 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Z80 {
|
impl Z80 {
|
||||||
pub fn new(cputype: Z80Type, frequency: u32, port: BusPort) -> Self {
|
pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cputype,
|
cputype,
|
||||||
frequency,
|
frequency,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use moa_core::Clock;
|
use moa_core::{ClockTime, ClockDuration};
|
||||||
use moa_core::host::{Audio, ClockedQueue};
|
use moa_core::host::{Audio, ClockedQueue};
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ impl AudioSource {
|
||||||
self.frame_size / 2
|
self.frame_size / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_frame(&mut self, clock: Clock, buffer: &[f32]) {
|
pub fn add_frame(&mut self, clock: ClockTime, buffer: &[f32]) {
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
for sample in buffer.iter() {
|
for sample in buffer.iter() {
|
||||||
// TODO this is here to keep it quiet for testing, but should be removed later
|
// TODO this is here to keep it quiet for testing, but should be removed later
|
||||||
|
@ -81,7 +81,7 @@ impl Audio for AudioSource {
|
||||||
self.space_available()
|
self.space_available()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_samples(&mut self, clock: Clock, buffer: &[f32]) {
|
fn write_samples(&mut self, clock: ClockTime, buffer: &[f32]) {
|
||||||
self.add_frame(clock, buffer);
|
self.add_frame(clock, buffer);
|
||||||
self.flush();
|
self.flush();
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ pub struct AudioMixer {
|
||||||
sample_rate: usize,
|
sample_rate: usize,
|
||||||
frame_size: usize,
|
frame_size: usize,
|
||||||
sequence_num: usize,
|
sequence_num: usize,
|
||||||
clock: Clock,
|
clock: ClockTime,
|
||||||
sources: Vec<ClockedQueue<AudioFrame>>,
|
sources: Vec<ClockedQueue<AudioFrame>>,
|
||||||
buffer_underrun: bool,
|
buffer_underrun: bool,
|
||||||
output: Arc<Mutex<AudioOutput>>,
|
output: Arc<Mutex<AudioOutput>>,
|
||||||
|
@ -108,7 +108,7 @@ impl AudioMixer {
|
||||||
sample_rate,
|
sample_rate,
|
||||||
frame_size: 1280,
|
frame_size: 1280,
|
||||||
sequence_num: 0,
|
sequence_num: 0,
|
||||||
clock: 0,
|
clock: ClockTime::START,
|
||||||
sources: vec![],
|
sources: vec![],
|
||||||
buffer_underrun: false,
|
buffer_underrun: false,
|
||||||
output: AudioOutput::new(),
|
output: AudioOutput::new(),
|
||||||
|
@ -132,8 +132,8 @@ impl AudioMixer {
|
||||||
self.sample_rate
|
self.sample_rate
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nanos_per_sample(&self) -> Clock {
|
pub fn sample_duration(&self) -> ClockDuration {
|
||||||
1_000_000_000 as Clock / self.sample_rate as Clock
|
ClockDuration::from_secs(1) / self.sample_rate as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_size(&self) -> usize {
|
pub fn frame_size(&self) -> usize {
|
||||||
|
@ -157,12 +157,12 @@ impl AudioMixer {
|
||||||
pub fn assemble_frame(&mut self) {
|
pub fn assemble_frame(&mut self) {
|
||||||
self.frame_size = self.output.lock().unwrap().frame_size;
|
self.frame_size = self.output.lock().unwrap().frame_size;
|
||||||
|
|
||||||
let nanos_per_sample = self.nanos_per_sample();
|
let sample_duration = self.sample_duration();
|
||||||
let mut data: Vec<(f32, f32)> = vec![(0.0, 0.0); self.frame_size];
|
let mut data: Vec<(f32, f32)> = vec![(0.0, 0.0); self.frame_size];
|
||||||
|
|
||||||
if self.buffer_underrun {
|
if self.buffer_underrun {
|
||||||
self.buffer_underrun = false;
|
self.buffer_underrun = false;
|
||||||
self.clock += nanos_per_sample * data.len() as Clock;
|
self.clock += sample_duration * data.len() as u64;
|
||||||
let empty_frame = AudioFrame { data };
|
let empty_frame = AudioFrame { data };
|
||||||
self.output.lock().unwrap().add_frame(empty_frame.clone());
|
self.output.lock().unwrap().add_frame(empty_frame.clone());
|
||||||
self.output.lock().unwrap().add_frame(empty_frame);
|
self.output.lock().unwrap().add_frame(empty_frame);
|
||||||
|
@ -189,7 +189,7 @@ impl AudioMixer {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let start = (((clock - self.clock) / nanos_per_sample) as usize).min(data.len() - 1);
|
let start = ((clock.duration_since(self.clock) / sample_duration) as usize).min(data.len() - 1);
|
||||||
let length = frame.data.len().min(data.len() - start);
|
let length = frame.data.len().min(data.len() - start);
|
||||||
|
|
||||||
data[start..start + length].iter_mut()
|
data[start..start + length].iter_mut()
|
||||||
|
@ -201,14 +201,14 @@ impl AudioMixer {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if length < frame.data.len() {
|
if length < frame.data.len() {
|
||||||
let adjusted_clock = clock + nanos_per_sample * length as Clock;
|
let adjusted_clock = clock + sample_duration * length as u64;
|
||||||
//println!("unpopping at clock {}, length {}", adjusted_clock, frame.data.len() - length);
|
//println!("unpopping at clock {}, length {}", adjusted_clock, frame.data.len() - length);
|
||||||
source.unpop(adjusted_clock, AudioFrame { data: frame.data[length..].to_vec() });
|
source.unpop(adjusted_clock, AudioFrame { data: frame.data[length..].to_vec() });
|
||||||
}
|
}
|
||||||
i = start + length;
|
i = start + length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.clock += nanos_per_sample * data.len() as Clock;
|
self.clock += sample_duration * data.len() as u64;
|
||||||
|
|
||||||
self.output.lock().unwrap().add_frame(AudioFrame { data });
|
self.output.lock().unwrap().add_frame(AudioFrame { data });
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ use std::sync::mpsc;
|
||||||
|
|
||||||
use moa_peripherals_yamaha::{Ym2612, Sn76489};
|
use moa_peripherals_yamaha::{Ym2612, Sn76489};
|
||||||
|
|
||||||
use moa_core::host::gfx::{Frame, FrameQueue};
|
use moa_core::host::gfx::{Frame, FrameQueue, PixelEncoding};
|
||||||
use moa_core::host::{Host, WindowUpdater, KeyboardUpdater, Key, KeyEvent /*, MouseUpdater, MouseState, MouseEvent*/};
|
use moa_core::host::{Host, WindowUpdater, KeyboardUpdater, Key, KeyEvent /*, MouseUpdater, MouseState, MouseEvent*/};
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
|
use moa_core::{System, Error, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
|
||||||
|
|
||||||
|
|
||||||
pub struct SynthControlsUpdater(mpsc::Sender<KeyEvent>);
|
pub struct SynthControlsUpdater(mpsc::Sender<KeyEvent>);
|
||||||
|
@ -37,7 +37,7 @@ impl SynthControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for SynthControl {
|
impl Steppable for SynthControl {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
if let Ok(event) = self.receiver.try_recv() {
|
if let Ok(event) = self.receiver.try_recv() {
|
||||||
|
|
||||||
match event.key {
|
match event.key {
|
||||||
|
@ -57,10 +57,10 @@ impl Steppable for SynthControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = self.queue.max_size();
|
let size = self.queue.max_size();
|
||||||
let frame = Frame::new(size.0, size.1);
|
let frame = Frame::new(size.0, size.1, PixelEncoding::RGBA);
|
||||||
self.queue.add(system.clock, frame);
|
self.queue.add(system.clock, frame);
|
||||||
|
|
||||||
Ok(33_000_000)
|
Ok(ClockDuration::from_millis(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,11 +104,11 @@ fn main() {
|
||||||
let control = wrap_transmutable(SynthControl::new(queue.clone(), receiver));
|
let control = wrap_transmutable(SynthControl::new(queue.clone(), receiver));
|
||||||
system.add_device("control", control)?;
|
system.add_device("control", control)?;
|
||||||
|
|
||||||
let ym_sound = wrap_transmutable(Ym2612::create(host)?);
|
let ym_sound = wrap_transmutable(Ym2612::create(host, Frequency::from_hz(7_670_454))?);
|
||||||
initialize_ym(ym_sound.clone())?;
|
initialize_ym(ym_sound.clone())?;
|
||||||
system.add_addressable_device(0x00, ym_sound)?;
|
system.add_addressable_device(0x00, ym_sound)?;
|
||||||
|
|
||||||
let sn_sound = wrap_transmutable(Sn76489::create(host)?);
|
let sn_sound = wrap_transmutable(Sn76489::create(host, Frequency::from_hz(3_579_545))?);
|
||||||
system.add_addressable_device(0x10, sn_sound)?;
|
system.add_addressable_device(0x10, sn_sound)?;
|
||||||
|
|
||||||
host.add_window(Box::new(queue))?;
|
host.add_window(Box::new(queue))?;
|
||||||
|
|
|
@ -14,7 +14,7 @@ fn main() {
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut options = Trs80Options::default();
|
let mut options = Trs80Options::default();
|
||||||
if let Some(filename) = matches.value_of("rom") {
|
if let Some(filename) = matches.value_of("ROM") {
|
||||||
options.rom = filename.to_string();
|
options.rom = filename.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
|
||||||
use minifb::{self, Key, MouseMode, MouseButton};
|
use minifb::{self, Key, MouseMode, MouseButton};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
|
||||||
use moa_core::{System, Error};
|
use moa_core::{System, Error, ClockDuration};
|
||||||
use moa_core::host::{Host, ControllerUpdater, KeyboardUpdater, KeyEvent, MouseUpdater, MouseState, WindowUpdater, Audio, ControllerDevice};
|
use moa_core::host::{Host, ControllerUpdater, KeyboardUpdater, KeyEvent, MouseUpdater, MouseState, WindowUpdater, Audio, ControllerDevice};
|
||||||
use moa_core::host::gfx::{PixelEncoding, Frame};
|
use moa_core::host::gfx::{PixelEncoding, Frame};
|
||||||
|
|
||||||
|
@ -257,22 +257,23 @@ impl MiniFrontend {
|
||||||
|
|
||||||
// Limit to max ~60 fps update rate
|
// Limit to max ~60 fps update rate
|
||||||
window.limit_update_rate(Some(Duration::from_micros(16600)));
|
window.limit_update_rate(Some(Duration::from_micros(16600)));
|
||||||
|
//let nanoseconds_per_frame = (16_600_000 as f32 * speed) as u64;
|
||||||
|
|
||||||
let mut update_timer = Instant::now();
|
let mut update_timer = Instant::now();
|
||||||
let mut last_frame = Frame::new(size.0, size.1, PixelEncoding::ARGB);
|
let mut last_frame = Frame::new(size.0, size.1, PixelEncoding::ARGB);
|
||||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||||
let frame_time = update_timer.elapsed();
|
let frame_time = update_timer.elapsed();
|
||||||
update_timer = Instant::now();
|
update_timer = Instant::now();
|
||||||
//println!("new frame after {:?}us", frame_time.as_micros());
|
println!("new frame after {:?}us", frame_time.as_micros());
|
||||||
|
|
||||||
//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();
|
||||||
system.run_for((frame_time.as_nanos() as f32 * speed) as u64).unwrap();
|
system.run_for(ClockDuration::from_nanos((frame_time.as_nanos() as f32 * speed) as u64)).unwrap();
|
||||||
//system.run_until_break().unwrap();
|
//system.run_until_break().unwrap();
|
||||||
}
|
}
|
||||||
//let sim_time = run_timer.elapsed().as_micros();
|
let sim_time = run_timer.elapsed().as_micros();
|
||||||
//println!("ran simulation for {:?}us in {:?}us (avg: {:?}us)", frame_time.as_micros(), sim_time, frame_time.as_micros() as f64 / sim_time as f64);
|
println!("ran simulation for {:?}us in {:?}us (avg: {:?}us)", frame_time.as_micros(), sim_time, frame_time.as_micros() as f64 / sim_time as f64);
|
||||||
|
|
||||||
if let Some(keys) = window.get_keys_pressed(minifb::KeyRepeat::No) {
|
if let Some(keys) = window.get_keys_pressed(minifb::KeyRepeat::No) {
|
||||||
for key in keys {
|
for key in keys {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use winit::dpi::LogicalSize;
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
use winit::window::{Window, WindowBuilder};
|
use winit::window::{Window, WindowBuilder};
|
||||||
|
|
||||||
use moa_core::{Clock, System};
|
use moa_core::{ClockDuration, System};
|
||||||
|
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::frontend::{self, PixelsFrontend, LoadSystemFn};
|
use crate::frontend::{self, PixelsFrontend, LoadSystemFn};
|
||||||
|
@ -90,7 +90,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 = nanos as Clock;
|
let nanoseconds_per_frame = ClockDuration::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(nanoseconds_per_frame) {
|
if let Err(err) = handle.0.run_for(nanoseconds_per_frame) {
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{Error, System, ClockElapsed, Address, Addressable, Steppable, Transmutable, Signal, ObservableSignal, Observable, debug, warn};
|
use moa_core::{Error, System, ClockDuration, Address, Addressable, Steppable, Transmutable, Signal, ObservableSignal, Observable, debug, warn};
|
||||||
|
|
||||||
|
|
||||||
const REG_OUTPUT_B: Address = 0x00;
|
const REG_OUTPUT_B: Address = 0x00;
|
||||||
|
@ -99,9 +99,9 @@ impl Addressable for Mos6522 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Mos6522 {
|
impl Steppable for Mos6522 {
|
||||||
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, _system: &System) -> Result<ClockDuration, Error> {
|
||||||
|
|
||||||
Ok(16_600_000)
|
Ok(ClockDuration::from_micros(16_600))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Steppable, Addressable, Transmutable, debug};
|
use moa_core::{System, Error, ClockDuration, Frequency, Address, Steppable, Addressable, Transmutable, debug};
|
||||||
use moa_core::host::Tty;
|
use moa_core::host::Tty;
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,6 +147,8 @@ impl MC68681Port {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MC68681 {
|
pub struct MC68681 {
|
||||||
|
frequency: Frequency,
|
||||||
|
|
||||||
acr: u8,
|
acr: u8,
|
||||||
pub port_a: MC68681Port,
|
pub port_a: MC68681Port,
|
||||||
pub port_b: MC68681Port,
|
pub port_b: MC68681Port,
|
||||||
|
@ -169,6 +171,8 @@ pub struct MC68681 {
|
||||||
impl Default for MC68681 {
|
impl Default for MC68681 {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
MC68681 {
|
MC68681 {
|
||||||
|
frequency: Frequency::from_hz(3_686_400),
|
||||||
|
|
||||||
acr: 0,
|
acr: 0,
|
||||||
port_a: MC68681Port::default(),
|
port_a: MC68681Port::default(),
|
||||||
port_b: MC68681Port::default(),
|
port_b: MC68681Port::default(),
|
||||||
|
@ -201,7 +205,7 @@ impl MC68681 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for MC68681 {
|
impl Steppable for MC68681 {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, 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);
|
||||||
}
|
}
|
||||||
|
@ -237,7 +241,7 @@ impl Steppable for MC68681 {
|
||||||
self.set_interrupt_flag(ISR_CH_B_TX_READY, true);
|
self.set_interrupt_flag(ISR_CH_B_TX_READY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(1_000_000_000 / 3_686_400)
|
Ok(self.frequency.period_duration())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
use moa_core::{info, warn, debug};
|
use moa_core::{info, warn, debug};
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_core::host::{Host, Audio};
|
use moa_core::host::{Host, Audio};
|
||||||
use moa_core::host::audio::{SquareWave};
|
use moa_core::host::audio::{SquareWave};
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ impl NoiseGenerator {
|
||||||
|
|
||||||
|
|
||||||
pub struct Sn76489 {
|
pub struct Sn76489 {
|
||||||
clock_frequency: u32,
|
clock_frequency: Frequency,
|
||||||
first_byte: Option<u8>,
|
first_byte: Option<u8>,
|
||||||
source: Box<dyn Audio>,
|
source: Box<dyn Audio>,
|
||||||
tones: Vec<ToneGenerator>,
|
tones: Vec<ToneGenerator>,
|
||||||
|
@ -94,7 +94,7 @@ pub struct Sn76489 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sn76489 {
|
impl Sn76489 {
|
||||||
pub fn create<H: Host>(host: &mut H, clock_frequency: u32) -> Result<Self, Error> {
|
pub fn create<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
|
||||||
let source = host.create_audio_source()?;
|
let source = host.create_audio_source()?;
|
||||||
let sample_rate = source.samples_per_second();
|
let sample_rate = source.samples_per_second();
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ impl Sn76489 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Sn76489 {
|
impl Steppable for Sn76489 {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
let rate = self.source.samples_per_second();
|
let rate = self.source.samples_per_second();
|
||||||
let available = self.source.space_available();
|
let available = self.source.space_available();
|
||||||
let samples = if available < rate / 1000 { available } else { rate / 1000 };
|
let samples = if available < rate / 1000 { available } else { rate / 1000 };
|
||||||
|
@ -137,7 +137,7 @@ impl Steppable for Sn76489 {
|
||||||
self.source.flush();
|
self.source.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(1_000_000) // Every 1ms of simulated time
|
Ok(ClockDuration::from_millis(1)) // Every 1ms of simulated time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::num::NonZeroU8;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use moa_core::{debug, warn};
|
use moa_core::{debug, warn};
|
||||||
use moa_core::{System, Error, Clock, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_core::host::{Host, Audio};
|
use moa_core::host::{Host, Audio};
|
||||||
use moa_core::host::audio::{SineWave, db_to_gain};
|
use moa_core::host::audio::{SineWave, db_to_gain};
|
||||||
|
|
||||||
|
@ -120,6 +120,8 @@ const OPERATORS: usize = 4;
|
||||||
const MAX_ENVELOPE: u16 = 0xFFC;
|
const MAX_ENVELOPE: u16 = 0xFFC;
|
||||||
|
|
||||||
|
|
||||||
|
type EnvelopeClock = u64;
|
||||||
|
|
||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
enum EnvelopeState {
|
enum EnvelopeState {
|
||||||
|
@ -137,8 +139,8 @@ struct EnvelopeGenerator {
|
||||||
rates: [usize; 4],
|
rates: [usize; 4],
|
||||||
|
|
||||||
envelope_state: EnvelopeState,
|
envelope_state: EnvelopeState,
|
||||||
last_state_change: Clock,
|
last_state_change: ClockTime,
|
||||||
last_envelope_clock: Clock,
|
next_envelope_clock: EnvelopeClock,
|
||||||
envelope: u16,
|
envelope: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,8 +153,8 @@ impl EnvelopeGenerator {
|
||||||
rates: [0; 4],
|
rates: [0; 4],
|
||||||
|
|
||||||
envelope_state: EnvelopeState::Attack,
|
envelope_state: EnvelopeState::Attack,
|
||||||
last_state_change: 0,
|
last_state_change: ClockTime::START,
|
||||||
last_envelope_clock: 0,
|
next_envelope_clock: 0,
|
||||||
envelope: 0,
|
envelope: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,10 +171,10 @@ impl EnvelopeGenerator {
|
||||||
self.rates[etype as usize] = rate;
|
self.rates[etype as usize] = rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify_state_change(&mut self, state: bool, envelope_clock: Clock) {
|
fn notify_state_change(&mut self, state: bool, envelope_clock: EnvelopeClock) {
|
||||||
self.last_state_change = envelope_clock;
|
//self.last_state_change = envelope_clock;
|
||||||
if state {
|
if state {
|
||||||
self.last_envelope_clock = envelope_clock;
|
self.next_envelope_clock = envelope_clock;
|
||||||
self.envelope_state = EnvelopeState::Attack;
|
self.envelope_state = EnvelopeState::Attack;
|
||||||
self.envelope = 0;
|
self.envelope = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -180,14 +182,14 @@ impl EnvelopeGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_envelope(&mut self, envelope_clock: Clock) {
|
fn update_envelope(&mut self, envelope_clock: EnvelopeClock) {
|
||||||
for clock in (self.last_envelope_clock + 1)..=envelope_clock {
|
for clock in self.next_envelope_clock..=envelope_clock {
|
||||||
self.do_cycle(clock);
|
self.do_cycle(clock);
|
||||||
}
|
}
|
||||||
self.last_envelope_clock = envelope_clock;
|
self.next_envelope_clock = envelope_clock + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_cycle(&mut self, envelope_clock: Clock) {
|
fn do_cycle(&mut self, envelope_clock: EnvelopeClock) {
|
||||||
if self.envelope_state == EnvelopeState::Decay && self.envelope >= self.sustain_level {
|
if self.envelope_state == EnvelopeState::Decay && self.envelope >= self.sustain_level {
|
||||||
self.envelope_state = EnvelopeState::Sustain;
|
self.envelope_state = EnvelopeState::Sustain;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +226,7 @@ impl EnvelopeGenerator {
|
||||||
self.envelope
|
self.envelope
|
||||||
};
|
};
|
||||||
let attenuation_10bit = (envelope + self.total_level).min(MAX_ENVELOPE);
|
let attenuation_10bit = (envelope + self.total_level).min(MAX_ENVELOPE);
|
||||||
envelope as f32 * 0.09375
|
attenuation_10bit as f32 * 0.09375
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,11 +287,11 @@ impl Operator {
|
||||||
self.envelope.set_rate(etype, rate)
|
self.envelope.set_rate(etype, rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify_state_change(&mut self, state: bool, envelope_clock: Clock) {
|
fn notify_state_change(&mut self, state: bool, envelope_clock: EnvelopeClock) {
|
||||||
self.envelope.notify_state_change(state, envelope_clock);
|
self.envelope.notify_state_change(state, envelope_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sample(&mut self, modulator: f32, envelope_clock: Clock) -> f32 {
|
fn get_sample(&mut self, modulator: f32, envelope_clock: EnvelopeClock) -> f32 {
|
||||||
self.wave.set_frequency((self.frequency * self.multiplier) + modulator);
|
self.wave.set_frequency((self.frequency * self.multiplier) + modulator);
|
||||||
let sample = self.wave.next().unwrap();
|
let sample = self.wave.next().unwrap();
|
||||||
|
|
||||||
|
@ -300,9 +302,6 @@ impl Operator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rate_to_gain(rate: usize, envelope_clock: Clock) -> f32 {
|
|
||||||
envelope_clock as f32 * rate as f32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Channel {
|
struct Channel {
|
||||||
|
@ -339,12 +338,11 @@ impl Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sample(&mut self, envelope_clock: Clock) -> f32 {
|
fn get_sample(&mut self, envelope_clock: EnvelopeClock) -> f32 {
|
||||||
if self.on_state != self.next_on_state {
|
if self.on_state != self.next_on_state {
|
||||||
self.on_state = self.next_on_state;
|
self.on_state = self.next_on_state;
|
||||||
for (i, operator) in self.operators.iter_mut().enumerate() {
|
for (i, operator) in self.operators.iter_mut().enumerate() {
|
||||||
operator.notify_state_change(((self.on_state >> i) & 0x01) != 0, envelope_clock);
|
operator.notify_state_change(((self.on_state >> i) & 0x01) != 0, envelope_clock);
|
||||||
println!("notified at {}", envelope_clock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +353,7 @@ println!("notified at {}", envelope_clock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_algorithm_sample(&mut self, envelope_clock: Clock) -> f32 {
|
fn get_algorithm_sample(&mut self, envelope_clock: EnvelopeClock) -> f32 {
|
||||||
match self.algorithm {
|
match self.algorithm {
|
||||||
OperatorAlgorithm::A0 => {
|
OperatorAlgorithm::A0 => {
|
||||||
let modulator0 = self.operators[0].get_sample(0.0, envelope_clock);
|
let modulator0 = self.operators[0].get_sample(0.0, envelope_clock);
|
||||||
|
@ -441,8 +439,8 @@ pub struct Ym2612 {
|
||||||
selected_reg_0: Option<NonZeroU8>,
|
selected_reg_0: Option<NonZeroU8>,
|
||||||
selected_reg_1: Option<NonZeroU8>,
|
selected_reg_1: Option<NonZeroU8>,
|
||||||
|
|
||||||
clock_frequency: u32,
|
clock_frequency: Frequency,
|
||||||
envelope_clock_period: ClockElapsed,
|
envelope_clock_period: ClockDuration,
|
||||||
channels: Vec<Channel>,
|
channels: Vec<Channel>,
|
||||||
channel_frequencies: [(u8, u16); CHANNELS],
|
channel_frequencies: [(u8, u16); CHANNELS],
|
||||||
dac: Dac,
|
dac: Dac,
|
||||||
|
@ -461,7 +459,7 @@ pub struct Ym2612 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ym2612 {
|
impl Ym2612 {
|
||||||
pub fn create<H: Host>(host: &mut H, clock_frequency: u32) -> Result<Self, Error> {
|
pub fn create<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
|
||||||
let source = host.create_audio_source()?;
|
let source = host.create_audio_source()?;
|
||||||
let sample_rate = source.samples_per_second();
|
let sample_rate = source.samples_per_second();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -470,7 +468,7 @@ impl Ym2612 {
|
||||||
selected_reg_1: None,
|
selected_reg_1: None,
|
||||||
|
|
||||||
clock_frequency,
|
clock_frequency,
|
||||||
envelope_clock_period: 351 * 1_000_000_000 / clock_frequency as ClockElapsed, //3 * 144 * 1_000_000_000 / clock_frequency as ClockElapsed,
|
envelope_clock_period: clock_frequency.period_duration() * 351, //3 * 144 * 1_000_000_000 / clock_frequency as ClockDuration,
|
||||||
channels: (0..CHANNELS).map(|i| Channel::new(format!("ch {}", i), sample_rate)).collect(),
|
channels: (0..CHANNELS).map(|i| Channel::new(format!("ch {}", i), sample_rate)).collect(),
|
||||||
channel_frequencies: [(0, 0); CHANNELS],
|
channel_frequencies: [(0, 0); CHANNELS],
|
||||||
|
|
||||||
|
@ -492,16 +490,16 @@ impl Ym2612 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Ym2612 {
|
impl Steppable for Ym2612 {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
let rate = self.source.samples_per_second();
|
let rate = self.source.samples_per_second();
|
||||||
let available = self.source.space_available();
|
let available = self.source.space_available();
|
||||||
let samples = if available < rate / 1000 { available } else { rate / 1000 };
|
let samples = if available < rate / 1000 { available } else { rate / 1000 };
|
||||||
let nanos_per_sample = 1_000_000_000 / rate;
|
let sample_duration = ClockDuration::from_secs(1) / rate as u64;
|
||||||
|
|
||||||
//if self.source.space_available() >= samples {
|
//if self.source.space_available() >= samples {
|
||||||
let mut buffer = vec![0.0; samples];
|
let mut buffer = vec![0.0; samples];
|
||||||
for (i, buffered_sample) in buffer.iter_mut().enumerate().take(samples) {
|
for (i, buffered_sample) in buffer.iter_mut().enumerate().take(samples) {
|
||||||
let envelope_clock = (system.clock + (i * nanos_per_sample) as Clock) / self.envelope_clock_period;
|
let envelope_clock = (system.clock.as_duration() + (sample_duration * i as u64)) / self.envelope_clock_period;
|
||||||
let mut sample = 0.0;
|
let mut sample = 0.0;
|
||||||
|
|
||||||
for ch in 0..(CHANNELS - 1) {
|
for ch in 0..(CHANNELS - 1) {
|
||||||
|
@ -519,7 +517,7 @@ impl Steppable for Ym2612 {
|
||||||
self.source.write_samples(system.clock, &buffer);
|
self.source.write_samples(system.clock, &buffer);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Ok(1_000_000) // Every 1ms of simulated time
|
Ok(ClockDuration::from_millis(1)) // Every 1ms of simulated time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,7 +552,6 @@ impl Ym2612 {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
println!("ch {} is {}", ch, if data >> 4 != 0 { "on" } else { "off" });
|
|
||||||
self.channels[ch].next_on_state = data >> 4;
|
self.channels[ch].next_on_state = data >> 4;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, warn, debug};
|
use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable, warn, debug};
|
||||||
|
|
||||||
const DEV_NAME: &str = "z8530";
|
const DEV_NAME: &str = "z8530";
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ impl Addressable for Z8530 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Z8530 {
|
impl Steppable for Z8530 {
|
||||||
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, _system: &System) -> Result<ClockDuration, Error> {
|
||||||
|
|
||||||
Ok(1_000_000_000)
|
Ok(ClockDuration::from_secs(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, Debuggable, MemoryBlock, BusPort, wrap_transmutable};
|
use moa_core::{System, Error, Frequency, Debuggable, MemoryBlock, BusPort, wrap_transmutable};
|
||||||
use moa_core::host::Host;
|
use moa_core::host::Host;
|
||||||
|
|
||||||
use moa_m68k::{M68k, M68kType};
|
use moa_m68k::{M68k, M68kType};
|
||||||
|
@ -27,7 +27,7 @@ pub fn build_computie<H: Host>(host: &H) -> Result<System, Error> {
|
||||||
system.add_addressable_device(0x00700000, wrap_transmutable(serial))?;
|
system.add_addressable_device(0x00700000, wrap_transmutable(serial))?;
|
||||||
|
|
||||||
|
|
||||||
let mut cpu = M68k::new(M68kType::MC68010, 10_000_000, BusPort::new(0, 24, 16, system.bus.clone()));
|
let mut cpu = M68k::new(M68kType::MC68010, Frequency::from_hz(10_000_000), BusPort::new(0, 24, 16, system.bus.clone()));
|
||||||
|
|
||||||
//cpu.enable_tracing();
|
//cpu.enable_tracing();
|
||||||
//cpu.add_breakpoint(0x10781a);
|
//cpu.add_breakpoint(0x10781a);
|
||||||
|
@ -65,7 +65,7 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
|
||||||
system.add_addressable_device(0x00700000, wrap_transmutable(serial))?;
|
system.add_addressable_device(0x00700000, wrap_transmutable(serial))?;
|
||||||
|
|
||||||
|
|
||||||
let cpu = M68k::new(M68kType::MC68030, 10_000_000, BusPort::new(0, 32, 32, system.bus.clone()));
|
let cpu = M68k::new(M68kType::MC68030, Frequency::from_hz(10_000_000), BusPort::new(0, 32, 32, system.bus.clone()));
|
||||||
|
|
||||||
//cpu.enable_tracing();
|
//cpu.enable_tracing();
|
||||||
//cpu.add_breakpoint(0x10781a);
|
//cpu.add_breakpoint(0x10781a);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
use moa_core::{warn, info};
|
use moa_core::{warn, info};
|
||||||
use moa_core::{System, Error, Clock, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_core::host::{Host, ControllerUpdater, HostData, ControllerDevice, ControllerEvent};
|
use moa_core::host::{Host, ControllerUpdater, HostData, ControllerDevice, ControllerEvent};
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ pub struct GenesisControllers {
|
||||||
port_2: GenesisControllerPort,
|
port_2: GenesisControllerPort,
|
||||||
expansion: GenesisControllerPort,
|
expansion: GenesisControllerPort,
|
||||||
interrupt: HostData<bool>,
|
interrupt: HostData<bool>,
|
||||||
reset_timer: Clock,
|
reset_timer: ClockDuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GenesisControllers {
|
impl Default for GenesisControllers {
|
||||||
|
@ -127,7 +127,7 @@ impl Default for GenesisControllers {
|
||||||
port_2: GenesisControllerPort::default(),
|
port_2: GenesisControllerPort::default(),
|
||||||
expansion: GenesisControllerPort::default(),
|
expansion: GenesisControllerPort::default(),
|
||||||
interrupt: HostData::new(false),
|
interrupt: HostData::new(false),
|
||||||
reset_timer: 0,
|
reset_timer: ClockDuration::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ impl Addressable for GenesisControllers {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||||
self.reset_timer = 0;
|
self.reset_timer = ClockDuration::ZERO;
|
||||||
|
|
||||||
info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||||
match addr {
|
match addr {
|
||||||
|
@ -200,11 +200,11 @@ impl Addressable for GenesisControllers {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for GenesisControllers {
|
impl Steppable for GenesisControllers {
|
||||||
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, _system: &System) -> Result<ClockDuration, Error> {
|
||||||
let duration = 100_000; // Update every 100us
|
let duration = ClockDuration::from_micros(100); // Update every 100us
|
||||||
|
|
||||||
self.reset_timer += duration;
|
self.reset_timer += duration;
|
||||||
if self.reset_timer >= 1_500_000 {
|
if self.reset_timer >= ClockDuration::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,6 +1,6 @@
|
||||||
|
|
||||||
use moa_core::{debug, warn, error};
|
use moa_core::{debug, warn, error};
|
||||||
use moa_core::{System, Error, EdgeSignal, Clock, ClockElapsed, Address, Addressable, Steppable, Inspectable, Transmutable, TransmutableBox, read_beu16, dump_slice};
|
use moa_core::{System, Error, EdgeSignal, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Inspectable, Transmutable, TransmutableBox, read_beu16, dump_slice};
|
||||||
use moa_core::host::{Host, BlitableSurface, HostData};
|
use moa_core::host::{Host, BlitableSurface, HostData};
|
||||||
use moa_core::host::gfx::{Pixel, PixelEncoding, Frame, FrameQueue};
|
use moa_core::host::gfx::{Pixel, PixelEncoding, Frame, FrameQueue};
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ struct Ym7101State {
|
||||||
sprites: Vec<Sprite>,
|
sprites: Vec<Sprite>,
|
||||||
sprites_by_line: Vec<Vec<usize>>,
|
sprites_by_line: Vec<Vec<usize>>,
|
||||||
|
|
||||||
last_clock: Clock,
|
last_clock: ClockTime,
|
||||||
p_clock: u32,
|
p_clock: u32,
|
||||||
h_clock: u32,
|
h_clock: u32,
|
||||||
v_clock: u32,
|
v_clock: u32,
|
||||||
|
@ -349,7 +349,7 @@ impl Default for Ym7101State {
|
||||||
sprites: vec![],
|
sprites: vec![],
|
||||||
sprites_by_line: vec![],
|
sprites_by_line: vec![],
|
||||||
|
|
||||||
last_clock: 0,
|
last_clock: ClockTime::START,
|
||||||
p_clock: 0,
|
p_clock: 0,
|
||||||
h_clock: 0,
|
h_clock: 0,
|
||||||
v_clock: 0,
|
v_clock: 0,
|
||||||
|
@ -609,8 +609,8 @@ impl Sprite {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Ym7101 {
|
impl Steppable for Ym7101 {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
let diff = (system.clock - self.state.last_clock) 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;
|
||||||
|
|
||||||
if self.state.external_int_enabled() && self.external_interrupt.get() {
|
if self.state.external_int_enabled() && self.external_interrupt.get() {
|
||||||
|
@ -674,7 +674,7 @@ impl Steppable for Ym7101 {
|
||||||
self.state.status = (self.state.status & !STATUS_DMA_BUSY) | (if self.state.memory.transfer_dma_busy { STATUS_DMA_BUSY } else { 0 });
|
self.state.status = (self.state.status & !STATUS_DMA_BUSY) | (if self.state.memory.transfer_dma_busy { STATUS_DMA_BUSY } else { 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((1_000_000_000 / 13_423_294) * 4)
|
Ok(Frequency::from_hz(13_423_294).period_duration() * 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use moa_core::{System, Error, Signal, MemoryBlock, Bus, BusPort, Address, Addressable, Debuggable, wrap_transmutable};
|
use moa_core::{System, Error, Frequency, Signal, MemoryBlock, Bus, BusPort, Address, Addressable, Debuggable, wrap_transmutable};
|
||||||
use moa_core::host::Host;
|
use moa_core::host::Host;
|
||||||
|
|
||||||
use moa_m68k::{M68k, M68kType};
|
use moa_m68k::{M68k, M68kType};
|
||||||
|
@ -70,8 +70,8 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
||||||
// Build the Coprocessor's Bus
|
// Build the Coprocessor's Bus
|
||||||
let bank_register = Signal::new(0);
|
let bank_register = Signal::new(0);
|
||||||
let coproc_ram = wrap_transmutable(MemoryBlock::new(vec![0; 0x00002000]));
|
let coproc_ram = wrap_transmutable(MemoryBlock::new(vec![0; 0x00002000]));
|
||||||
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host, 7_670_454)?);
|
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host, Frequency::from_hz(7_670_454))?);
|
||||||
let coproc_sn_sound = wrap_transmutable(Sn76489::create(host, 3_579_545)?);
|
let coproc_sn_sound = wrap_transmutable(Sn76489::create(host, Frequency::from_hz(3_579_545))?);
|
||||||
let coproc_register = wrap_transmutable(CoprocessorBankRegister::new(bank_register.clone()));
|
let coproc_register = wrap_transmutable(CoprocessorBankRegister::new(bank_register.clone()));
|
||||||
let coproc_area = wrap_transmutable(CoprocessorBankArea::new(bank_register, system.bus.clone()));
|
let coproc_area = wrap_transmutable(CoprocessorBankArea::new(bank_register, system.bus.clone()));
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
||||||
coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone());
|
coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone());
|
||||||
coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone());
|
coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone());
|
||||||
coproc_bus.borrow_mut().insert(0x8000, coproc_area);
|
coproc_bus.borrow_mut().insert(0x8000, coproc_area);
|
||||||
let mut coproc = Z80::new(Z80Type::Z80, 3_579_545, BusPort::new(0, 16, 8, coproc_bus));
|
let mut coproc = Z80::new(Z80Type::Z80, Frequency::from_hz(3_579_545), BusPort::new(0, 16, 8, coproc_bus));
|
||||||
coproc.set_debugging(true);
|
coproc.set_debugging(true);
|
||||||
let mut reset = coproc.reset.clone();
|
let mut reset = coproc.reset.clone();
|
||||||
let mut bus_request = coproc.bus_request.clone();
|
let mut bus_request = coproc.bus_request.clone();
|
||||||
|
@ -109,7 +109,7 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
||||||
system.break_signal = Some(vdp.frame_complete.clone());
|
system.break_signal = Some(vdp.frame_complete.clone());
|
||||||
system.add_peripheral("vdp", 0x00c00000, wrap_transmutable(vdp)).unwrap();
|
system.add_peripheral("vdp", 0x00c00000, wrap_transmutable(vdp)).unwrap();
|
||||||
|
|
||||||
let cpu = M68k::new(M68kType::MC68000, 7_670_454, BusPort::new(0, 24, 16, system.bus.clone()));
|
let cpu = M68k::new(M68kType::MC68000, Frequency::from_hz(7_670_454), BusPort::new(0, 24, 16, system.bus.clone()));
|
||||||
system.add_interruptable_device("cpu", wrap_transmutable(cpu)).unwrap();
|
system.add_interruptable_device("cpu", wrap_transmutable(cpu)).unwrap();
|
||||||
|
|
||||||
Ok(system)
|
Ok(system)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, info, warn};
|
use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable, info, warn};
|
||||||
|
|
||||||
|
|
||||||
//const CA0: u8 = 0x01;
|
//const CA0: u8 = 0x01;
|
||||||
|
@ -94,9 +94,9 @@ impl Addressable for IWM {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for IWM {
|
impl Steppable for IWM {
|
||||||
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, _system: &System) -> Result<ClockDuration, Error> {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
Ok(1_000_000_000)
|
Ok(ClockDuration::from_secs(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use moa_core::{System, Bus, Error, Observable, Clock, ClockElapsed, Address, Addressable, AddressRepeater, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
|
use moa_core::{System, Bus, Error, Observable, ClockTime, ClockDuration, Address, Addressable, AddressRepeater, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
|
||||||
|
|
||||||
use moa_peripherals_mos::Mos6522;
|
use moa_peripherals_mos::Mos6522;
|
||||||
use moa_peripherals_zilog::Z8530;
|
use moa_peripherals_zilog::Z8530;
|
||||||
|
@ -18,7 +18,7 @@ pub struct Mainboard {
|
||||||
iwm: IWM,
|
iwm: IWM,
|
||||||
via: Mos6522,
|
via: Mos6522,
|
||||||
phase_read: PhaseRead,
|
phase_read: PhaseRead,
|
||||||
last_sec: Clock,
|
last_sec: ClockTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mainboard {
|
impl Mainboard {
|
||||||
|
@ -38,7 +38,7 @@ impl Mainboard {
|
||||||
iwm,
|
iwm,
|
||||||
via,
|
via,
|
||||||
phase_read,
|
phase_read,
|
||||||
last_sec: 0,
|
last_sec: ClockTime::START,
|
||||||
};
|
};
|
||||||
|
|
||||||
mainboard.via.port_a.set_observer(move |port| {
|
mainboard.via.port_a.set_observer(move |port| {
|
||||||
|
@ -110,12 +110,12 @@ impl Addressable for Mainboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Mainboard {
|
impl Steppable for Mainboard {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, 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 + 1_000_000_000 > system.clock {
|
if self.last_sec + ClockDuration::from_secs(1) > system.clock {
|
||||||
self.last_sec += 1_000_000_000;
|
self.last_sec += ClockDuration::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)?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_core::host::gfx::{Frame, FrameQueue};
|
use moa_core::host::gfx::{Frame, FrameQueue, Pixel};
|
||||||
use moa_core::host::{Host, BlitableSurface};
|
use moa_core::host::{Host, BlitableSurface};
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl BitIter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for BitIter {
|
impl Iterator for BitIter {
|
||||||
type Item = u32;
|
type Item = Pixel;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.bit < 0 {
|
if self.bit < 0 {
|
||||||
|
@ -48,16 +48,16 @@ impl Iterator for BitIter {
|
||||||
self.bit -= 1;
|
self.bit -= 1;
|
||||||
|
|
||||||
if bit {
|
if bit {
|
||||||
Some(0xC0C0C0)
|
Some(Pixel::Rgb(0xC0, 0xC0, 0xC0))
|
||||||
} else {
|
} else {
|
||||||
Some(0)
|
Some(Pixel::Rgb(0, 0, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for MacVideo {
|
impl Steppable for MacVideo {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, 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_queue.encoding());
|
let mut frame = Frame::new(SCRN_SIZE.0, SCRN_SIZE.1, self.frame_queue.encoding());
|
||||||
for y in 0..SCRN_SIZE.1 {
|
for y in 0..SCRN_SIZE.1 {
|
||||||
|
@ -68,7 +68,7 @@ impl Steppable for MacVideo {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.frame_queue.add(system.clock, frame);
|
self.frame_queue.add(system.clock, frame);
|
||||||
Ok(16_600_000)
|
Ok(ClockDuration::from_micros(16_600))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, MemoryBlock, BusPort, Debuggable, wrap_transmutable};
|
use moa_core::{System, Error, Frequency, MemoryBlock, BusPort, Debuggable, wrap_transmutable};
|
||||||
use moa_core::host::Host;
|
use moa_core::host::Host;
|
||||||
|
|
||||||
use moa_m68k::{M68k, M68kType};
|
use moa_m68k::{M68k, M68kType};
|
||||||
|
@ -69,7 +69,7 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
|
||||||
system.add_addressable_device(0x00000000, wrap_transmutable(mainboard))?;
|
system.add_addressable_device(0x00000000, wrap_transmutable(mainboard))?;
|
||||||
|
|
||||||
|
|
||||||
let mut cpu = M68k::new(M68kType::MC68000, 7_833_600, BusPort::new(0, 24, 16, system.bus.clone()));
|
let mut cpu = M68k::new(M68kType::MC68000, Frequency::from_hz(7_833_600), BusPort::new(0, 24, 16, system.bus.clone()));
|
||||||
|
|
||||||
//cpu.enable_tracing();
|
//cpu.enable_tracing();
|
||||||
//system.enable_debugging();
|
//system.enable_debugging();
|
||||||
|
|
|
@ -650,6 +650,7 @@ const CHARACTERS: [[u8; 8]; 64] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
use moa_core::host::gfx::Pixel;
|
||||||
|
|
||||||
pub struct CharacterGenerator {
|
pub struct CharacterGenerator {
|
||||||
pub row: i8,
|
pub row: i8,
|
||||||
|
@ -668,7 +669,7 @@ impl CharacterGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for CharacterGenerator {
|
impl Iterator for CharacterGenerator {
|
||||||
type Item = u32;
|
type Item = Pixel;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.row >= 8 {
|
if self.row >= 8 {
|
||||||
|
@ -683,9 +684,9 @@ impl Iterator for CharacterGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
if bit {
|
if bit {
|
||||||
Some(0xC0C0C0)
|
Some(Pixel::Rgb(0xC0, 0xC0, 0xC0))
|
||||||
} else {
|
} else {
|
||||||
Some(0)
|
Some(Pixel::Rgb(0, 0, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, debug, warn};
|
use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable, debug, warn};
|
||||||
use moa_core::host::gfx::{Frame, FrameQueue};
|
use moa_core::host::gfx::{Frame, FrameQueue};
|
||||||
use moa_core::host::{Host, BlitableSurface, KeyboardUpdater, KeyEvent};
|
use moa_core::host::{Host, BlitableSurface, KeyboardUpdater, KeyEvent};
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ impl Model1Peripherals {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
frame_queue,
|
frame_queue,
|
||||||
keyboard_mem,
|
keyboard_mem,
|
||||||
video_mem: [0; 1024],
|
video_mem: [0x20; 1024],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,18 +44,18 @@ impl KeyboardUpdater for Model1KeyboardUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for Model1Peripherals {
|
impl Steppable for Model1Peripherals {
|
||||||
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
|
||||||
let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1, self.frame_queue.encoding());
|
let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1, self.frame_queue.encoding());
|
||||||
for y in 0..16 {
|
for y in 0..16 {
|
||||||
for x in 0..64 {
|
for x in 0..64 {
|
||||||
let ch = self.video_mem[x + (y * 64)];
|
let ch = self.video_mem[x + (y * 64)];
|
||||||
let iter = CharacterGenerator::new((ch - 0x20) % 64);
|
let iter = CharacterGenerator::new(ch.saturating_sub(0x20) % 64);
|
||||||
frame.blit((x * 6) as u32, (y * 8) as u32, iter, 6, 8);
|
frame.blit((x * 6) as u32, (y * 8) as u32, iter, 6, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.frame_queue.add(system.clock, frame);
|
self.frame_queue.add(system.clock, frame);
|
||||||
|
|
||||||
Ok(16_630_000)
|
Ok(ClockDuration::from_micros(16_630))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, MemoryBlock, BusPort, wrap_transmutable};
|
use moa_core::{System, Error, Frequency, MemoryBlock, BusPort, wrap_transmutable};
|
||||||
use moa_core::host::Host;
|
use moa_core::host::Host;
|
||||||
|
|
||||||
use moa_z80::{Z80, Z80Type};
|
use moa_z80::{Z80, Z80Type};
|
||||||
|
@ -10,7 +10,7 @@ use crate::peripherals::model1::Model1Peripherals;
|
||||||
pub struct Trs80Options {
|
pub struct Trs80Options {
|
||||||
pub rom: String,
|
pub rom: String,
|
||||||
pub memory: u16,
|
pub memory: u16,
|
||||||
pub frequency: u32,
|
pub frequency: Frequency,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Trs80Options {
|
impl Default for Trs80Options {
|
||||||
|
@ -18,7 +18,7 @@ impl Default for Trs80Options {
|
||||||
Self {
|
Self {
|
||||||
rom: "binaries/trs80/level2.rom".to_string(),
|
rom: "binaries/trs80/level2.rom".to_string(),
|
||||||
memory: 0xC000,
|
memory: 0xC000,
|
||||||
frequency: 1_774_000,
|
frequency: Frequency::from_hz(1_774_000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
todo.txt
2
todo.txt
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
* the audio needs to not run if nothing is using it or there's constant buffer underruns
|
||||||
|
|
||||||
* add a Duration-like clock type that records femto seconds
|
* add a Duration-like clock type that records femto seconds
|
||||||
* move parser to utils/ of some kind, or lib/
|
* move parser to utils/ of some kind, or lib/
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user