Added new clock types similar to Duration

This commit is contained in:
transistor 2023-04-23 15:46:47 -07:00
parent f298d1b341
commit 07a675fab5
34 changed files with 452 additions and 178 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ junk/
perf.data
perf.data.old
flamegraph*.svg
binaries/*/*.asm
binaries/*/*.bin

265
emulator/core/src/clock.rs Normal file
View 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)
}
}

View File

@ -22,7 +22,7 @@ impl Debugger {
pub fn run_debugger(&mut self, system: &System, target: TransmutableBox) -> Result<(), Error> {
let mut target = target.borrow_mut();
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)?;
if self.trace_only {

View File

@ -4,14 +4,9 @@ use std::cell::RefCell;
use crate::error::Error;
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
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
/// information that might be helpful for debugging.
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) { }
}
@ -207,3 +202,11 @@ pub fn wrap_transmutable<T: Transmutable + 'static>(value: T) -> TransmutableBox
Rc::new(RefCell::new(Box::new(value)))
}
#[derive(Clone)]
pub struct Device(TransmutableBox);
impl Device {
}

View File

@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};
use crate::host::traits::{BlitableSurface, ClockedQueue, WindowUpdater};
use crate::Clock;
use crate::ClockTime;
use crate::Error;
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 x in pos_x..(pos_x + width) {
match bitmap.next().unwrap() {
MASK_COLOUR => {}
Pixel::Mask => {}
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) {
let value = if value == MASK_COLOUR { 0 } else { value };
fn clear(&mut self, value: Pixel) {
let value = value.encode(self.encoding);
self.bitmap.iter_mut().for_each(|pixel| *pixel = value);
}
}
@ -132,11 +132,11 @@ impl FrameQueue {
*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);
}
pub fn latest(&self) -> Option<(Clock, Frame)> {
pub fn latest(&self) -> Option<(ClockTime, Frame)> {
self.queue.pop_latest()
}
}

View File

@ -2,7 +2,7 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, MutexGuard};
use crate::{Clock, Error};
use crate::{ClockTime, Error};
use crate::host::gfx::{PixelEncoding, Pixel, Frame};
use crate::host::keys::KeyEvent;
use crate::host::controllers::{ControllerDevice, ControllerEvent};
@ -62,7 +62,7 @@ pub trait MouseUpdater: Send {
pub trait Audio {
fn samples_per_second(&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);
}
@ -70,8 +70,8 @@ pub trait BlitableSurface {
fn set_size(&mut self, width: u32, height: u32);
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 blit<B: Iterator<Item=u32>>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32);
fn clear(&mut self, value: 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: Pixel);
}
@ -99,26 +99,26 @@ impl<T: Copy> HostData<T> {
}
#[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> {
pub fn push(&self, clock: Clock, data: T) {
pub fn push(&self, clock: ClockTime, data: T) {
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()
}
pub fn pop_latest(&self) -> Option<(Clock, T)> {
pub fn pop_latest(&self) -> Option<(ClockTime, T)> {
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));
}
pub fn peek_clock(&self) -> Option<Clock> {
pub fn peek_clock(&self) -> Option<ClockTime> {
self.0.lock().unwrap().front().map(|(clock, _)| *clock)
}
}
@ -135,7 +135,7 @@ impl Audio for DummyAudio {
4800
}
fn write_samples(&mut self, _clock: Clock, _buffer: &[f32]) {}
fn write_samples(&mut self, _clock: ClockTime, _buffer: &[f32]) {}
fn flush(&mut self) {}
}

View File

@ -2,6 +2,7 @@
#[macro_use]
mod error;
mod clock;
mod debugger;
mod devices;
mod interrupts;
@ -14,8 +15,9 @@ pub mod timers;
pub use log::{trace, debug, info, warn, error};
pub use crate::clock::{ClockTime, ClockDuration, Frequency};
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::error::{Error, ErrorType};
pub use crate::interrupts::InterruptController;

View File

@ -8,11 +8,12 @@ use crate::debugger::Debugger;
use crate::signals::EdgeSignal;
use crate::error::{Error, ErrorType};
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 clock: Clock,
pub clock: ClockTime,
pub devices: HashMap<String, TransmutableBox>,
pub event_queue: Vec<NextStep>,
@ -28,7 +29,7 @@ pub struct System {
impl Default for System {
fn default() -> Self {
Self {
clock: 0,
clock: ClockTime::START,
devices: HashMap::new(),
event_queue: vec![],
@ -125,7 +126,7 @@ impl System {
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;
while self.clock < target {
@ -147,7 +148,7 @@ impl System {
}
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) {
@ -188,14 +189,14 @@ impl System {
pub struct NextStep {
pub next_clock: Clock,
pub next_clock: ClockTime,
pub device: TransmutableBox,
}
impl NextStep {
pub fn new(device: TransmutableBox) -> Self {
Self {
next_clock: 0,
next_clock: ClockTime::START,
device,
}
}

View File

@ -1,6 +1,6 @@
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::instructions::{
@ -30,7 +30,7 @@ pub enum Used {
}
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)
}
@ -62,7 +62,7 @@ impl M68k {
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 {
Status::Init => self.init(),
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
//Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
self.exception(native as u8, false)?;
Ok(4)
Ok(self.frequency.period_duration() * 4)
},
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.pc = self.port.read_beu32(4)?;
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.decode_next()?;
self.execute_current()?;
@ -99,7 +99,7 @@ impl M68k {
self.check_pending_interrupts(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> {
@ -115,7 +115,7 @@ impl M68k {
let priority_mask = ((self.state.sr & Flags::IntMask as u16) >> 8) as u8;
if (pending_ipl > priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
debug!("{} interrupt: {} @ {} ns", DEV_NAME, pending_ipl, system.clock);
debug!("{} interrupt: {} @ {} ns", DEV_NAME, pending_ipl, system.clock.as_duration().as_nanos());
self.state.current_ipl = self.state.pending_ipl;
let ack_num = system.get_interrupt_controller().acknowledge(self.state.current_ipl as u8)?;
self.exception(ack_num, true)?;

View File

@ -1,5 +1,5 @@
use moa_core::{Address, BusPort};
use moa_core::{Address, BusPort, Frequency};
use moa_core::timers::CpuTimer;
use crate::instructions::Size;
@ -125,7 +125,7 @@ pub struct M68kState {
#[derive(Clone)]
pub struct M68k {
pub cputype: M68kType,
pub frequency: u32,
pub frequency: Frequency,
pub state: M68kState,
pub decoder: M68kDecoder,
pub timing: M68kInstructionTiming,
@ -155,7 +155,7 @@ impl Default for M68kState {
}
impl M68k {
pub fn new(cputype: M68kType, frequency: u32, port: BusPort) -> M68k {
pub fn new(cputype: M68kType, frequency: Frequency, port: BusPort) -> M68k {
M68k {
cputype,
frequency,

View File

@ -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::state::{Z80, Status, Flags, Register};
@ -19,19 +19,16 @@ enum RotateType {
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() {
//println!("RESET");
self.reset()?
} else if self.bus_request.get() {
//println!("BUS REQ");
4
} else {
//println!("RUNNING {:?}", self.decoder.instruction);
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) {

View File

@ -1,5 +1,5 @@
use moa_core::{Address, BusPort, Signal};
use moa_core::{Address, BusPort, Signal, Frequency};
use crate::decode::Z80Decoder;
use crate::debugger::Z80Debugger;
@ -106,7 +106,7 @@ impl Z80State {
pub struct Z80 {
pub cputype: Z80Type,
pub frequency: u32,
pub frequency: Frequency,
pub state: Z80State,
pub decoder: Z80Decoder,
pub debugger: Z80Debugger,
@ -116,7 +116,7 @@ pub struct Z80 {
}
impl Z80 {
pub fn new(cputype: Z80Type, frequency: u32, port: BusPort) -> Self {
pub fn new(cputype: Z80Type, frequency: Frequency, port: BusPort) -> Self {
Self {
cputype,
frequency,

View File

@ -2,7 +2,7 @@
use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
use moa_core::Clock;
use moa_core::{ClockTime, ClockDuration};
use moa_core::host::{Audio, ClockedQueue};
@ -51,7 +51,7 @@ impl AudioSource {
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![];
for sample in buffer.iter() {
// 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()
}
fn write_samples(&mut self, clock: Clock, buffer: &[f32]) {
fn write_samples(&mut self, clock: ClockTime, buffer: &[f32]) {
self.add_frame(clock, buffer);
self.flush();
}
@ -96,7 +96,7 @@ pub struct AudioMixer {
sample_rate: usize,
frame_size: usize,
sequence_num: usize,
clock: Clock,
clock: ClockTime,
sources: Vec<ClockedQueue<AudioFrame>>,
buffer_underrun: bool,
output: Arc<Mutex<AudioOutput>>,
@ -108,7 +108,7 @@ impl AudioMixer {
sample_rate,
frame_size: 1280,
sequence_num: 0,
clock: 0,
clock: ClockTime::START,
sources: vec![],
buffer_underrun: false,
output: AudioOutput::new(),
@ -132,8 +132,8 @@ impl AudioMixer {
self.sample_rate
}
pub fn nanos_per_sample(&self) -> Clock {
1_000_000_000 as Clock / self.sample_rate as Clock
pub fn sample_duration(&self) -> ClockDuration {
ClockDuration::from_secs(1) / self.sample_rate as u64
}
pub fn frame_size(&self) -> usize {
@ -157,12 +157,12 @@ impl AudioMixer {
pub fn assemble_frame(&mut self) {
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];
if self.buffer_underrun {
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 };
self.output.lock().unwrap().add_frame(empty_frame.clone());
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);
data[start..start + length].iter_mut()
@ -201,14 +201,14 @@ impl AudioMixer {
)
);
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);
source.unpop(adjusted_clock, AudioFrame { data: frame.data[length..].to_vec() });
}
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 });
}

View File

@ -3,9 +3,9 @@ use std::sync::mpsc;
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::{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>);
@ -37,7 +37,7 @@ impl 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() {
match event.key {
@ -57,10 +57,10 @@ impl Steppable for SynthControl {
}
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);
Ok(33_000_000)
Ok(ClockDuration::from_millis(1))
}
}
@ -104,11 +104,11 @@ fn main() {
let control = wrap_transmutable(SynthControl::new(queue.clone(), receiver));
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())?;
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)?;
host.add_window(Box::new(queue))?;

View File

@ -14,7 +14,7 @@ fn main() {
.get_matches();
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();
}

View File

@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
use minifb::{self, Key, MouseMode, MouseButton};
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::gfx::{PixelEncoding, Frame};
@ -257,22 +257,23 @@ impl MiniFrontend {
// Limit to max ~60 fps update rate
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 last_frame = Frame::new(size.0, size.1, PixelEncoding::ARGB);
while window.is_open() && !window.is_key_down(Key::Escape) {
let frame_time = update_timer.elapsed();
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() {
//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();
}
//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);
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);
if let Some(keys) = window.get_keys_pressed(minifb::KeyRepeat::No) {
for key in keys {

View File

@ -7,7 +7,7 @@ use winit::dpi::LogicalSize;
use winit::event_loop::EventLoop;
use winit::window::{Window, WindowBuilder};
use moa_core::{Clock, System};
use moa_core::{ClockDuration, System};
use crate::settings;
use crate::frontend::{self, PixelsFrontend, LoadSystemFn};
@ -90,7 +90,7 @@ pub fn load_system(handle: &mut HostHandle, load: LoadSystemFnHandle) -> SystemH
#[wasm_bindgen]
pub fn run_system_for(handle: &mut SystemHandle, nanos: u32) -> usize {
let run_timer = Instant::now();
let nanoseconds_per_frame = 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;
if let Err(err) = handle.0.run_for(nanoseconds_per_frame) {
log::error!("{:?}", err);

View File

@ -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;
@ -99,9 +99,9 @@ impl Addressable 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))
}
}

View File

@ -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;
@ -147,6 +147,8 @@ impl MC68681Port {
}
pub struct MC68681 {
frequency: Frequency,
acr: u8,
pub port_a: MC68681Port,
pub port_b: MC68681Port,
@ -169,6 +171,8 @@ pub struct MC68681 {
impl Default for MC68681 {
fn default() -> Self {
MC68681 {
frequency: Frequency::from_hz(3_686_400),
acr: 0,
port_a: MC68681Port::default(),
port_b: MC68681Port::default(),
@ -201,7 +205,7 @@ impl 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()? {
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);
}
Ok(1_000_000_000 / 3_686_400)
Ok(self.frequency.period_duration())
}
}

View File

@ -1,6 +1,6 @@
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::audio::{SquareWave};
@ -86,7 +86,7 @@ impl NoiseGenerator {
pub struct Sn76489 {
clock_frequency: u32,
clock_frequency: Frequency,
first_byte: Option<u8>,
source: Box<dyn Audio>,
tones: Vec<ToneGenerator>,
@ -94,7 +94,7 @@ pub struct 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 sample_rate = source.samples_per_second();
@ -109,7 +109,7 @@ impl 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 available = self.source.space_available();
let samples = if available < rate / 1000 { available } else { rate / 1000 };
@ -137,7 +137,7 @@ impl Steppable for Sn76489 {
self.source.flush();
}
Ok(1_000_000) // Every 1ms of simulated time
Ok(ClockDuration::from_millis(1)) // Every 1ms of simulated time
}
}

View File

@ -12,7 +12,7 @@ use std::num::NonZeroU8;
use std::collections::VecDeque;
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::audio::{SineWave, db_to_gain};
@ -120,6 +120,8 @@ const OPERATORS: usize = 4;
const MAX_ENVELOPE: u16 = 0xFFC;
type EnvelopeClock = u64;
#[repr(usize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EnvelopeState {
@ -137,8 +139,8 @@ struct EnvelopeGenerator {
rates: [usize; 4],
envelope_state: EnvelopeState,
last_state_change: Clock,
last_envelope_clock: Clock,
last_state_change: ClockTime,
next_envelope_clock: EnvelopeClock,
envelope: u16,
}
@ -151,8 +153,8 @@ impl EnvelopeGenerator {
rates: [0; 4],
envelope_state: EnvelopeState::Attack,
last_state_change: 0,
last_envelope_clock: 0,
last_state_change: ClockTime::START,
next_envelope_clock: 0,
envelope: 0,
}
}
@ -169,10 +171,10 @@ impl EnvelopeGenerator {
self.rates[etype as usize] = rate;
}
fn notify_state_change(&mut self, state: bool, envelope_clock: Clock) {
self.last_state_change = envelope_clock;
fn notify_state_change(&mut self, state: bool, envelope_clock: EnvelopeClock) {
//self.last_state_change = envelope_clock;
if state {
self.last_envelope_clock = envelope_clock;
self.next_envelope_clock = envelope_clock;
self.envelope_state = EnvelopeState::Attack;
self.envelope = 0;
} else {
@ -180,14 +182,14 @@ impl EnvelopeGenerator {
}
}
fn update_envelope(&mut self, envelope_clock: Clock) {
for clock in (self.last_envelope_clock + 1)..=envelope_clock {
fn update_envelope(&mut self, envelope_clock: EnvelopeClock) {
for clock in self.next_envelope_clock..=envelope_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 {
self.envelope_state = EnvelopeState::Sustain;
}
@ -224,7 +226,7 @@ impl EnvelopeGenerator {
self.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)
}
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);
}
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);
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)]
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 {
self.on_state = self.next_on_state;
for (i, operator) in self.operators.iter_mut().enumerate() {
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 {
OperatorAlgorithm::A0 => {
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_1: Option<NonZeroU8>,
clock_frequency: u32,
envelope_clock_period: ClockElapsed,
clock_frequency: Frequency,
envelope_clock_period: ClockDuration,
channels: Vec<Channel>,
channel_frequencies: [(u8, u16); CHANNELS],
dac: Dac,
@ -461,7 +459,7 @@ pub struct 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 sample_rate = source.samples_per_second();
Ok(Self {
@ -470,7 +468,7 @@ impl Ym2612 {
selected_reg_1: None,
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(),
channel_frequencies: [(0, 0); CHANNELS],
@ -492,16 +490,16 @@ impl 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 available = self.source.space_available();
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 {
let mut buffer = vec![0.0; 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;
for ch in 0..(CHANNELS - 1) {
@ -519,7 +517,7 @@ impl Steppable for Ym2612 {
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;
},
};
println!("ch {} is {}", ch, if data >> 4 != 0 { "on" } else { "off" });
self.channels[ch].next_on_state = data >> 4;
},

View File

@ -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";
@ -27,9 +27,9 @@ impl Addressable 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))
}
}

View File

@ -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_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))?;
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.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))?;
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.add_breakpoint(0x10781a);

View File

@ -1,6 +1,6 @@
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};
@ -117,7 +117,7 @@ pub struct GenesisControllers {
port_2: GenesisControllerPort,
expansion: GenesisControllerPort,
interrupt: HostData<bool>,
reset_timer: Clock,
reset_timer: ClockDuration,
}
impl Default for GenesisControllers {
@ -127,7 +127,7 @@ impl Default for GenesisControllers {
port_2: GenesisControllerPort::default(),
expansion: GenesisControllerPort::default(),
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> {
self.reset_timer = 0;
self.reset_timer = ClockDuration::ZERO;
info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
@ -200,11 +200,11 @@ impl Addressable for GenesisControllers {
}
impl Steppable for GenesisControllers {
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
let duration = 100_000; // Update every 100us
fn step(&mut self, _system: &System) -> Result<ClockDuration, Error> {
let duration = ClockDuration::from_micros(100); // Update every 100us
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_2.reset_count();
self.expansion.reset_count();

View File

@ -1,6 +1,6 @@
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::gfx::{Pixel, PixelEncoding, Frame, FrameQueue};
@ -314,7 +314,7 @@ struct Ym7101State {
sprites: Vec<Sprite>,
sprites_by_line: Vec<Vec<usize>>,
last_clock: Clock,
last_clock: ClockTime,
p_clock: u32,
h_clock: u32,
v_clock: u32,
@ -349,7 +349,7 @@ impl Default for Ym7101State {
sprites: vec![],
sprites_by_line: vec![],
last_clock: 0,
last_clock: ClockTime::START,
p_clock: 0,
h_clock: 0,
v_clock: 0,
@ -609,8 +609,8 @@ impl Sprite {
}
impl Steppable for Ym7101 {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let diff = (system.clock - self.state.last_clock) as u32;
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
let diff = system.clock.duration_since(self.state.last_clock).as_nanos() as u32;
self.state.last_clock = system.clock;
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 });
}
Ok((1_000_000_000 / 13_423_294) * 4)
Ok(Frequency::from_hz(13_423_294).period_duration() * 4)
}
}

View File

@ -3,7 +3,7 @@ use std::mem;
use std::rc::Rc;
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_m68k::{M68k, M68kType};
@ -70,8 +70,8 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
// Build the Coprocessor's Bus
let bank_register = Signal::new(0);
let coproc_ram = wrap_transmutable(MemoryBlock::new(vec![0; 0x00002000]));
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host, 7_670_454)?);
let coproc_sn_sound = wrap_transmutable(Sn76489::create(host, 3_579_545)?);
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host, Frequency::from_hz(7_670_454))?);
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_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(0x7f11, coproc_sn_sound.clone());
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);
let mut reset = coproc.reset.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.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();
Ok(system)

View File

@ -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;
@ -94,9 +94,9 @@ impl Addressable 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
Ok(1_000_000_000)
Ok(ClockDuration::from_secs(1))
}
}

View File

@ -2,7 +2,7 @@
use std::rc::Rc;
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_zilog::Z8530;
@ -18,7 +18,7 @@ pub struct Mainboard {
iwm: IWM,
via: Mos6522,
phase_read: PhaseRead,
last_sec: Clock,
last_sec: ClockTime,
}
impl Mainboard {
@ -38,7 +38,7 @@ impl Mainboard {
iwm,
via,
phase_read,
last_sec: 0,
last_sec: ClockTime::START,
};
mainboard.via.port_a.set_observer(move |port| {
@ -110,12 +110,12 @@ impl Addressable 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)?;
// 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 {
self.last_sec += 1_000_000_000;
if self.last_sec + ClockDuration::from_secs(1) > system.clock {
self.last_sec += ClockDuration::from_secs(1);
//let port_a = self.via.port_a.borrow_mut();
// TODO how will the ca1/ca2 cb1/cb2 pins work in the via
system.get_interrupt_controller().set(true, 1, 25)?;

View File

@ -1,6 +1,6 @@
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable};
use moa_core::host::gfx::{Frame, FrameQueue};
use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable};
use moa_core::host::gfx::{Frame, FrameQueue, Pixel};
use moa_core::host::{Host, BlitableSurface};
@ -38,7 +38,7 @@ impl BitIter {
}
impl Iterator for BitIter {
type Item = u32;
type Item = Pixel;
fn next(&mut self) -> Option<Self::Item> {
if self.bit < 0 {
@ -48,16 +48,16 @@ impl Iterator for BitIter {
self.bit -= 1;
if bit {
Some(0xC0C0C0)
Some(Pixel::Rgb(0xC0, 0xC0, 0xC0))
} else {
Some(0)
Some(Pixel::Rgb(0, 0, 0))
}
}
}
}
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 frame = Frame::new(SCRN_SIZE.0, SCRN_SIZE.1, self.frame_queue.encoding());
for y in 0..SCRN_SIZE.1 {
@ -68,7 +68,7 @@ impl Steppable for MacVideo {
}
self.frame_queue.add(system.clock, frame);
Ok(16_600_000)
Ok(ClockDuration::from_micros(16_600))
}
}

View File

@ -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_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))?;
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();
//system.enable_debugging();

View File

@ -650,6 +650,7 @@ const CHARACTERS: [[u8; 8]; 64] = [
];
use moa_core::host::gfx::Pixel;
pub struct CharacterGenerator {
pub row: i8,
@ -668,7 +669,7 @@ impl CharacterGenerator {
}
impl Iterator for CharacterGenerator {
type Item = u32;
type Item = Pixel;
fn next(&mut self) -> Option<Self::Item> {
if self.row >= 8 {
@ -683,9 +684,9 @@ impl Iterator for CharacterGenerator {
}
if bit {
Some(0xC0C0C0)
Some(Pixel::Rgb(0xC0, 0xC0, 0xC0))
} else {
Some(0)
Some(Pixel::Rgb(0, 0, 0))
}
}
}

View File

@ -1,7 +1,7 @@
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::{Host, BlitableSurface, KeyboardUpdater, KeyEvent};
@ -29,7 +29,7 @@ impl Model1Peripherals {
Ok(Self {
frame_queue,
keyboard_mem,
video_mem: [0; 1024],
video_mem: [0x20; 1024],
})
}
}
@ -44,18 +44,18 @@ impl KeyboardUpdater for Model1KeyboardUpdater {
}
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());
for y in 0..16 {
for x in 0..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);
}
}
self.frame_queue.add(system.clock, frame);
Ok(16_630_000)
Ok(ClockDuration::from_micros(16_630))
}
}

View File

@ -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_z80::{Z80, Z80Type};
@ -10,7 +10,7 @@ use crate::peripherals::model1::Model1Peripherals;
pub struct Trs80Options {
pub rom: String,
pub memory: u16,
pub frequency: u32,
pub frequency: Frequency,
}
impl Default for Trs80Options {
@ -18,7 +18,7 @@ impl Default for Trs80Options {
Self {
rom: "binaries/trs80/level2.rom".to_string(),
memory: 0xC000,
frequency: 1_774_000,
frequency: Frequency::from_hz(1_774_000),
}
}
}

View File

@ -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
* move parser to utils/ of some kind, or lib/