diff --git a/Cargo.lock b/Cargo.lock index daff098..ede0eb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -804,13 +804,12 @@ dependencies = [ "log", "moa-common", "moa-core", - "moa-debugger", "moa-host", "moa-m68k", "moa-peripherals-generic", "moa-peripherals-motorola", + "moa-system", "moa-systems-computie", - "moa-systems-genesis", "simple_logger", ] @@ -884,9 +883,11 @@ dependencies = [ name = "moa-peripherals-generic" version = "0.1.0" dependencies = [ + "emulator-hal", "femtos", "log", "moa-core", + "moa-system", ] [[package]] @@ -903,10 +904,12 @@ dependencies = [ name = "moa-peripherals-motorola" version = "0.1.0" dependencies = [ + "emulator-hal", "femtos", "log", "moa-core", "moa-host", + "moa-system", ] [[package]] @@ -937,6 +940,17 @@ dependencies = [ "femtos", ] +[[package]] +name = "moa-system" +version = "0.1.0" +dependencies = [ + "emulator-hal", + "femtos", + "log", + "moa-host", + "thiserror", +] + [[package]] name = "moa-systems-computie" version = "0.1.0" @@ -948,6 +962,7 @@ dependencies = [ "moa-m68k", "moa-peripherals-generic", "moa-peripherals-motorola", + "moa-system", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9146329..3768d35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "emulator/core", + "emulator/libraries/system", "emulator/frontends/common", "emulator/frontends/console", "emulator/frontends/minifb", diff --git a/emulator/core/src/devices.rs b/emulator/core/src/devices.rs index 3f46c4c..d57c8dd 100644 --- a/emulator/core/src/devices.rs +++ b/emulator/core/src/devices.rs @@ -101,6 +101,31 @@ pub trait Addressable { } } +impl Addressable for &mut T +where + T: Addressable + ?Sized, +{ + #[inline] + fn size(&self) -> usize { + T::size(self) + } + + #[inline] + fn read( + &mut self, + now: Instant, + addr: Address, + data: &mut [u8], + ) -> Result<(), Error> { + T::read(self, now, addr, data) + } + + #[inline] + fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + T::write(self, now, addr, data) + } +} + #[inline] pub fn read_beu16(data: &[u8]) -> u16 { (data[0] as u16) << 8 | (data[1] as u16) @@ -220,6 +245,7 @@ pub fn wrap_transmutable(value: T) -> TransmutableBox Rc::new(RefCell::new(Box::new(value))) } + static NEXT_ID: AtomicUsize = AtomicUsize::new(1); #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] diff --git a/emulator/frontends/console/Cargo.toml b/emulator/frontends/console/Cargo.toml index 3ff2b3b..97953d2 100644 --- a/emulator/frontends/console/Cargo.toml +++ b/emulator/frontends/console/Cargo.toml @@ -11,11 +11,12 @@ simple_logger = "4" femtos = "0.1" moa-core = { path = "../../core" } +moa-system = { path = "../../libraries/system" } moa-host = { path = "../../libraries/host" } moa-common = { path = "../common", features = ["tty"] } -moa-debugger = { path = "../../libraries/debugger" } -moa-systems-genesis = { path = "../../systems/genesis" } +#moa-debugger = { path = "../../libraries/debugger" } +#moa-systems-genesis = { path = "../../systems/genesis" } moa-systems-computie = { path = "../../systems/computie" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] } moa-peripherals-generic = { path = "../../peripherals/generic" } diff --git a/emulator/frontends/console/src/lib.rs b/emulator/frontends/console/src/lib.rs index c0b1971..558e7c2 100644 --- a/emulator/frontends/console/src/lib.rs +++ b/emulator/frontends/console/src/lib.rs @@ -2,8 +2,8 @@ use clap::{Command, Arg, ArgAction, ArgMatches}; use std::io::{self, Write}; use femtos::Duration; -use moa_core::{Error, System}; -use moa_debugger::{Debugger, DebugControl}; +use moa_system::{Error, System}; +//use moa_debugger::{Debugger, DebugControl}; use moa_host::{Host, HostError, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender}; pub struct ConsoleFrontend; @@ -75,9 +75,10 @@ impl ConsoleFrontend { .unwrap(); // Run the main loop - let mut debugger = Debugger::default(); + //let mut debugger = Debugger::default(); let mut run_debugger = matches.get_flag("debugger"); loop { + /* if run_debugger { run_debugger = false; @@ -99,6 +100,7 @@ impl ConsoleFrontend { } } } + */ match system.run_for_duration(Duration::MAX - system.clock.as_duration()) { Ok(()) => {}, diff --git a/emulator/libraries/system/Cargo.toml b/emulator/libraries/system/Cargo.toml new file mode 100644 index 0000000..41b9294 --- /dev/null +++ b/emulator/libraries/system/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "moa-system" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4" +femtos = "0.1" +thiserror = "1.0" +moa-host = { path = "../host" } +emulator-hal = { path = "../emulator-hal/emulator-hal", features = ["femtos"] } + +[features] +default = ["std"] +std = [] diff --git a/emulator/libraries/system/src/devices.rs b/emulator/libraries/system/src/devices.rs new file mode 100644 index 0000000..9dfe811 --- /dev/null +++ b/emulator/libraries/system/src/devices.rs @@ -0,0 +1,374 @@ +use std::fmt; +use std::rc::Rc; +use std::cell::{RefCell, RefMut, BorrowMutError}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use femtos::{Duration, Instant}; + +use crate::{Error, System}; + + +/// A universal memory address used by the Addressable trait +pub type Address = u64; + +/* +/// A device that can change state over time. The `step()` method will be called +/// by the containing `System` when the system clock advances. If an error occurs +/// 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; + fn on_error(&mut self, _system: &System) {} +} + +/// A device that can receive an interrupt. The `interrupt_state_change()` method +/// will be called whenever an interrupt signal changes goes high or low. +pub trait Interruptable { + //fn interrupt_state_change(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error>; +} + +/// A device that can be addressed to read data from or write data to the device. +pub trait Addressable { + fn size(&self) -> usize; + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error>; + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error>; + + #[inline] + fn read_u8(&mut self, clock: Instant, addr: Address) -> Result { + let mut data = [0; 1]; + self.read(clock, addr, &mut data)?; + Ok(data[0]) + } + + #[inline] + fn read_beu16(&mut self, clock: Instant, addr: Address) -> Result { + let mut data = [0; 2]; + self.read(clock, addr, &mut data)?; + Ok(read_beu16(&data)) + } + + #[inline] + fn read_leu16(&mut self, clock: Instant, addr: Address) -> Result { + let mut data = [0; 2]; + self.read(clock, addr, &mut data)?; + Ok(read_leu16(&data)) + } + + #[inline] + fn read_beu32(&mut self, clock: Instant, addr: Address) -> Result { + let mut data = [0; 4]; + self.read(clock, addr, &mut data)?; + Ok(read_beu32(&data)) + } + + #[inline] + fn read_leu32(&mut self, clock: Instant, addr: Address) -> Result { + let mut data = [0; 4]; + self.read(clock, addr, &mut data)?; + Ok(read_leu32(&data)) + } + + #[inline] + fn write_u8(&mut self, clock: Instant, addr: Address, value: u8) -> Result<(), Error> { + let data = [value]; + self.write(clock, addr, &data) + } + + #[inline] + fn write_beu16(&mut self, clock: Instant, addr: Address, value: u16) -> Result<(), Error> { + let mut data = [0; 2]; + write_beu16(&mut data, value); + self.write(clock, addr, &data) + } + + #[inline] + fn write_leu16(&mut self, clock: Instant, addr: Address, value: u16) -> Result<(), Error> { + let mut data = [0; 2]; + write_leu16(&mut data, value); + self.write(clock, addr, &data) + } + + #[inline] + fn write_beu32(&mut self, clock: Instant, addr: Address, value: u32) -> Result<(), Error> { + let mut data = [0; 4]; + write_beu32(&mut data, value); + self.write(clock, addr, &data) + } + + #[inline] + fn write_leu32(&mut self, clock: Instant, addr: Address, value: u32) -> Result<(), Error> { + let mut data = [0; 4]; + write_leu32(&mut data, value); + self.write(clock, addr, &data) + } +} + +impl Addressable for &mut T +where + T: Addressable + ?Sized, +{ + #[inline] + fn size(&self) -> usize { + T::size(self) + } + + #[inline] + fn read( + &mut self, + now: Instant, + addr: Address, + data: &mut [u8], + ) -> Result<(), Error> { + T::read(self, now, addr, data) + } + + #[inline] + fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + T::write(self, now, addr, data) + } +} + +#[inline] +pub fn read_beu16(data: &[u8]) -> u16 { + (data[0] as u16) << 8 | (data[1] as u16) +} + +#[inline] +pub fn read_leu16(data: &[u8]) -> u16 { + (data[1] as u16) << 8 | (data[0] as u16) +} + +#[inline] +pub fn read_beu32(data: &[u8]) -> u32 { + (data[0] as u32) << 24 | (data[1] as u32) << 16 | (data[2] as u32) << 8 | (data[3] as u32) +} + +#[inline] +pub fn read_leu32(data: &[u8]) -> u32 { + (data[3] as u32) << 24 | (data[2] as u32) << 16 | (data[1] as u32) << 8 | (data[0] as u32) +} + + + +#[inline] +pub fn write_beu16(data: &mut [u8], value: u16) -> &mut [u8] { + data[0] = (value >> 8) as u8; + data[1] = value as u8; + data +} + +#[inline] +pub fn write_leu16(data: &mut [u8], value: u16) -> &mut [u8] { + data[0] = value as u8; + data[1] = (value >> 8) as u8; + data +} + +#[inline] +pub fn write_beu32(data: &mut [u8], value: u32) -> &mut [u8] { + data[0] = (value >> 24) as u8; + data[1] = (value >> 16) as u8; + data[2] = (value >> 8) as u8; + data[3] = value as u8; + data +} + +#[inline] +pub fn write_leu32(data: &mut [u8], value: u32) -> &mut [u8] { + data[0] = value as u8; + data[1] = (value >> 8) as u8; + data[2] = (value >> 16) as u8; + data[3] = (value >> 24) as u8; + data +} + + +/// A device (cpu) that can debugged using the built-in debugger +pub trait Debuggable { + fn add_breakpoint(&mut self, addr: Address); + fn remove_breakpoint(&mut self, addr: Address); + + fn print_current_step(&mut self, system: &System) -> Result<(), Error>; + fn print_disassembly(&mut self, system: &System, addr: Address, count: usize); + fn run_command(&mut self, system: &System, args: &[&str]) -> Result; +} + +/// A device (peripheral) that can inspected using the built-in debugger +pub trait Inspectable { + fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>; +} + + +pub trait Transmutable { + #[inline] + fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { + None + } + + #[inline] + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + None + } + + #[inline] + fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> { + None + } + + #[inline] + fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> { + None + } + + #[inline] + fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> { + None + } +} + +pub type TransmutableBox = Rc>>; + +pub fn wrap_transmutable(value: T) -> TransmutableBox { + Rc::new(RefCell::new(Box::new(value))) +} + +*/ + + +use emulator_hal::{BusAccess, Step, Inspect, Debug}; + +pub type MoaBus = dyn BusAccess; +pub type MoaStep = dyn Step; +//pub type MoaInspect<'a> = dyn Inspect; +//pub type MoaDebug<'a> = dyn Debug; + +pub trait DeviceInterface { + #[inline] + fn as_bus_access(&mut self) -> Option<&mut MoaBus> { + None + } + + #[inline] + fn as_step(&mut self) -> Option<&mut MoaStep> { + None + } + + /* + #[inline] + fn as_inspect(&mut self) -> Option<&mut MoaInspect> { + None + } + + #[inline] + fn as_debug(&mut self) -> Option<&mut MoaDebug> { + None + } + */ +} + + + + + + +static NEXT_ID: AtomicUsize = AtomicUsize::new(1); + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct DeviceId(usize); + +impl DeviceId { + pub fn new() -> Self { + let next = NEXT_ID.load(Ordering::Acquire); + NEXT_ID.store(next + 1, Ordering::Release); + Self(next) + } +} + +impl Default for DeviceId { + fn default() -> Self { + Self::new() + } +} + +pub type BoxedInterface = Rc>>; + +#[derive(Clone)] +pub struct Device(DeviceId, BoxedInterface); + +impl Device { + pub fn new(value: T) -> Self + where + T: DeviceInterface + 'static, + { + Self(DeviceId::new(), Rc::new(RefCell::new(Box::new(value)))) + } + + pub fn id(&self) -> DeviceId { + self.0 + } + + pub fn borrow_mut(&self) -> RefMut<'_, Box> { + self.1.borrow_mut() + } + + pub fn try_borrow_mut(&self) -> Result>, BorrowMutError> { + self.1.try_borrow_mut() + } +} + +impl fmt::Debug for Device { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{:?}", self.0) + } +} + + +/* +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct DeviceId(usize); + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Interrupt { + Number(usize), +} + +pub enum InterruptPriority { + NonMaskable, + Number(usize), +} + +struct InterruptPort { + id: usize, + controller: TransmutableBox, +} + +impl InterruptPort { + fn check_pending(&self) -> Option { + self.controller.borrow_mut().as_interrupt_controller().check_pending(self.id) + } + + fn acknowledge(&self, interrupt: Interrupt) -> Result<(), Error> { + self.controller.borrow_mut().as_interrupt_controller().acknowledge(self.id, interrupt) + } +} + +//pub trait InterruptPort { +// fn check_pending(&mut self, id: DeviceId) -> Option; +// fn acknowledge(&mut self, id: DeviceId, interrupt: Interrupt) -> Result<(), Error>; +//} + +//pub trait Interrupter { +// fn trigger(&mut self, id: DeviceId, interrupt: Interrupt) -> Result<(), Error>; +//} + +struct Interrupter { + input_id: usize, + interrupt: Interrupt, + controller: Rc>, +} + +pub trait InterruptController { + fn connect(&mut self, priority: InterruptPriority) -> Result; + fn check_pending(&mut self, id: usize) -> Option; + fn acknowledge(&mut self, id: usize, interrupt: Interrupt) -> Result<(), Error>; +} +*/ diff --git a/emulator/libraries/system/src/error.rs b/emulator/libraries/system/src/error.rs new file mode 100644 index 0000000..dd3a06f --- /dev/null +++ b/emulator/libraries/system/src/error.rs @@ -0,0 +1,101 @@ +use core::fmt; +use core::convert::Infallible; +use moa_host::HostError; +use emulator_hal::Error as EmuError; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum EmulatorErrorKind { + Misc, + MemoryAlignment, +} + +#[derive(Clone, Debug)] +pub enum Error { + Assertion(String), + Breakpoint(String), + Emulator(EmulatorErrorKind, String), + Processor(u32), + Other(String), +} + +impl Default for Error { + fn default() -> Self { + Self::Other("default".to_string()) + } +} + +impl From for Error { + fn from(err: Infallible) -> Self { + Error::new("infallible".to_string()) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +/* +impl From for Error +where + E: EmuError, +{ + fn from(err: E) -> Self { + Error::new(format!("{}", err)) + } +} +*/ + +impl Error { + pub fn new(msg: S) -> Error + where + S: Into, + { + Error::Emulator(EmulatorErrorKind::Misc, msg.into()) + } + + pub fn emulator(kind: EmulatorErrorKind, msg: S) -> Error + where + S: Into, + { + Error::Emulator(kind, msg.into()) + } + + pub fn processor(native: u32) -> Error { + Error::Processor(native) + } + + pub fn breakpoint(msg: S) -> Error + where + S: Into, + { + Error::Breakpoint(msg.into()) + } + + pub fn assertion(msg: S) -> Error + where + S: Into, + { + Error::Assertion(msg.into()) + } + + pub fn msg(&self) -> &str { + match self { + Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => msg.as_str(), + Error::Processor(_) => "native exception", + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => write!(f, "{}", msg), + Error::Processor(_) => write!(f, "native exception"), + } + } +} + +impl From> for Error { + fn from(_err: HostError) -> Self { + Self::Other("other".to_string()) + } +} diff --git a/emulator/libraries/system/src/interrupts.rs b/emulator/libraries/system/src/interrupts.rs new file mode 100644 index 0000000..73b90b0 --- /dev/null +++ b/emulator/libraries/system/src/interrupts.rs @@ -0,0 +1,44 @@ +use crate::error::Error; + + +pub struct InterruptController { + interrupts: Vec<(bool, u8)>, + highest: u8, +} + +impl Default for InterruptController { + fn default() -> InterruptController { + InterruptController { + interrupts: vec![(false, 0); 7], + highest: 0, + } + } +} + +impl InterruptController { + pub fn set(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error> { + self.interrupts[priority as usize].0 = state; + self.interrupts[priority as usize].1 = number; + if state && priority > self.highest { + self.highest = priority; + } + Ok(()) + } + + pub fn check(&mut self) -> (bool, u8, u8) { + if self.highest > 0 { + (true, self.highest, self.interrupts[self.highest as usize].1) + } else { + (false, 0, 0) + } + } + + pub fn acknowledge(&mut self, priority: u8) -> Result { + let acknowledge = self.interrupts[priority as usize].1; + self.interrupts[priority as usize].0 = false; + while self.highest > 0 && !self.interrupts[self.highest as usize].0 { + self.highest -= 1; + } + Ok(acknowledge) + } +} diff --git a/emulator/libraries/system/src/lib.rs b/emulator/libraries/system/src/lib.rs new file mode 100644 index 0000000..910cc11 --- /dev/null +++ b/emulator/libraries/system/src/lib.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod error; + +mod devices; +mod interrupts; +mod memory; +mod system; + +pub use crate::devices::{ + Address, Device, + DeviceInterface, MoaBus, MoaStep, +}; +pub use crate::error::Error; +pub use crate::interrupts::InterruptController; +pub use crate::memory::{MemoryBlock, Bus, dump_slice, dump_memory}; +pub use crate::system::System; + +pub use emulator_hal; +pub use emulator_hal::BusAdapter; diff --git a/emulator/libraries/system/src/memory.rs b/emulator/libraries/system/src/memory.rs new file mode 100644 index 0000000..22435ab --- /dev/null +++ b/emulator/libraries/system/src/memory.rs @@ -0,0 +1,461 @@ +use std::fs; +use std::cmp; +use std::rc::Rc; +use std::cell::RefCell; +use std::fmt::Write; +use femtos::Instant; +use emulator_hal::{BusAccess, Error as BusError}; + +use crate::error::Error; +use crate::devices::{Address, Device, DeviceInterface, MoaBus}; + +impl BusError for Error {} + +/// A contiguous block of `Addressable` memory, backed by a `Vec` +pub struct MemoryBlock { + read_only: bool, + contents: Vec, +} + +impl MemoryBlock { + pub fn new(contents: Vec) -> MemoryBlock { + MemoryBlock { + read_only: false, + contents, + } + } + + pub fn load(filename: &str) -> Result { + match fs::read(filename) { + Ok(contents) => Ok(MemoryBlock::new(contents)), + Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))), + } + } + + pub fn load_at(&mut self, addr: Address, filename: &str) -> Result<(), Error> { + match fs::read(filename) { + Ok(contents) => { + self.contents[(addr as usize)..(addr as usize) + contents.len()].copy_from_slice(&contents); + Ok(()) + }, + Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))), + } + } + + pub fn size(&self) -> usize { + self.contents.len() + } + + pub fn read_only(&mut self) { + self.read_only = true; + } + + pub fn resize(&mut self, new_size: usize) { + self.contents.resize(new_size, 0); + } +} + +impl BusAccess
for MemoryBlock { + type Instant = Instant; + // TODO this is temporary + type Error = Error; + + #[inline] + fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result { + data.copy_from_slice(&self.contents[(addr as usize)..(addr as usize) + data.len()]); + Ok(data.len()) + } + + #[inline] + fn write(&mut self, _clock: Self::Instant, addr: Address, data: &[u8]) -> Result { + if self.read_only { + return Err(Error::breakpoint(format!( + "Attempt to write to read-only memory at {:x} with data {:?}", + addr, data + ))); + } + + self.contents[(addr as usize)..(addr as usize) + data.len()].copy_from_slice(data); + Ok(data.len()) + } +} + +impl DeviceInterface for MemoryBlock { + fn as_bus_access(&mut self) -> Option<&mut MoaBus> { + Some(self) + } +} + +/* +/// An address adapter that repeats the address space of the subdevice over the given range +pub struct AddressRepeater { + subdevice: Device, + range: Address, +} + +impl AddressRepeater { + pub fn new(subdevice: Device, range: Address) -> Self { + Self { + subdevice, + range, + } + } +} + +impl Addressable for AddressRepeater { + fn size(&self) -> usize { + self.range as usize + } + + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { + let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address; + self.subdevice + .borrow_mut() + .as_addressable() + .unwrap() + .read(clock, addr % size, data) + } + + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address; + self.subdevice + .borrow_mut() + .as_addressable() + .unwrap() + .write(clock, addr % size, data) + } +} + +impl Transmutable for AddressRepeater { + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + Some(self) + } +} + + +/// An address adapter that uses a closure to translate the address before accessing the subdevice +pub struct AddressTranslator { + subdevice: Device, + size: usize, + func: Box Address>, +} + +impl AddressTranslator { + pub fn new(subdevice: Device, size: usize, func: F) -> Self + where + F: Fn(Address) -> Address + 'static, + { + Self { + subdevice, + size, + func: Box::new(func), + } + } +} + +impl Addressable for AddressTranslator { + fn size(&self) -> usize { + self.size + } + + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { + self.subdevice + .borrow_mut() + .as_addressable() + .unwrap() + .read(clock, (self.func)(addr), data) + } + + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + self.subdevice + .borrow_mut() + .as_addressable() + .unwrap() + .write(clock, (self.func)(addr), data) + } +} + +impl Transmutable for AddressTranslator { + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + Some(self) + } +} +*/ + +#[derive(Clone)] +pub struct Block { + pub base: Address, + pub size: usize, + pub dev: Device, +} + +/// A bus-like collection of `Addressable` `Device`s mapped to different address ranges +/// +/// This is the fundamental means of connecting devices together to a CPU implementation. +#[derive(Clone, Default)] +pub struct Bus { + blocks: Vec, + ignore_unmapped: bool, + watchers: Vec
, + watcher_modified: bool, +} + +impl Bus { + pub fn set_ignore_unmapped(&mut self, ignore_unmapped: bool) { + self.ignore_unmapped = ignore_unmapped; + } + + pub fn clear_all_bus_devices(&mut self) { + self.blocks.clear(); + } + + pub fn size(&self) -> usize { + let block = &self.blocks[self.blocks.len() - 1]; + (block.base as usize) + block.size + } + + pub fn insert(&mut self, base: Address, dev: Device, size: usize) { + let block = Block { + base, + size, + dev, + }; + let i = self + .blocks + .iter() + .position(|cur| cur.base > block.base) + .unwrap_or(self.blocks.len()); + self.blocks.insert(i, block); + } + + pub fn get_device_at(&self, addr: Address, count: usize) -> Result<(Device, Address), Error> { + for block in &self.blocks { + if addr >= block.base && addr < (block.base + block.size as Address) { + let relative_addr = addr - block.base; + if relative_addr as usize + count <= block.size { + return Ok((block.dev.clone(), relative_addr)); + } else { + return Err(Error::new(format!("Error reading address {:#010x}", addr))); + } + } + } + Err(Error::new(format!("No segment found at {:#010x}", addr))) + } + + pub fn dump_memory(&mut self, clock: Instant, mut addr: Address, mut count: Address) { + while count > 0 { + let mut line = format!("{:#010x}: ", addr); + + let to = if count < 16 { count / 2 } else { 8 }; + for _ in 0..to { + let word = self.read_beu16(clock, addr); + if word.is_err() { + println!("{}", line); + return; + } + write!(line, "{:#06x} ", word.unwrap()).unwrap(); + addr += 2; + count -= 2; + } + println!("{}", line); + } + } + + pub fn add_watcher(&mut self, addr: Address) { + self.watchers.push(addr); + } + + pub fn remove_watcher(&mut self, addr: Address) { + self.watchers.push(addr); + if let Some(index) = self.watchers.iter().position(|a| *a == addr) { + self.watchers.remove(index); + } + } + + pub fn check_and_reset_watcher_modified(&mut self) -> bool { + let result = self.watcher_modified; + self.watcher_modified = false; + result + } +} + +impl BusAccess
for Bus { + type Instant = Instant; + type Error = Error; + + #[inline] + fn read(&mut self, clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result { + let (dev, relative_addr) = match self.get_device_at(addr, data.len()) { + Ok(result) => result, + Err(err) if self.ignore_unmapped => { + log::info!("{:?}", err); + return Ok(0); + }, + Err(err) => return Err(err), + }; + let result = dev.borrow_mut().as_bus_access().unwrap().read(clock, relative_addr, data); + result + } + + #[inline] + fn write(&mut self, clock: Self::Instant, addr: Address, data: &[u8]) -> Result { + if self.watchers.iter().any(|a| *a == addr) { + println!("watch: writing to address {:#06x} with {:?}", addr, data); + self.watcher_modified = true; + } + + let (dev, relative_addr) = match self.get_device_at(addr, data.len()) { + Ok(result) => result, + Err(err) if self.ignore_unmapped => { + log::info!("{:?}", err); + return Ok(0); + }, + Err(err) => return Err(err), + }; + let result = dev.borrow_mut().as_bus_access().unwrap().write(clock, relative_addr, data); + result + } +} + +/* +/// An adapter for limiting the access requests of a device (eg. CPU) on a `Bus` to the address +/// and data widths of the device +#[derive(Clone)] +pub struct BusPort { + offset: Address, + address_mask: Address, + data_width: u8, + subdevice: Rc>, +} + +impl BusPort { + pub fn new(offset: Address, address_bits: u8, data_bits: u8, bus: Rc>) -> Self { + Self { + offset, + address_mask: (1 << address_bits) - 1, + data_width: data_bits / 8, + subdevice: bus, + } + } + + pub fn dump_memory(&mut self, clock: Instant, addr: Address, count: Address) { + self.subdevice + .borrow_mut() + .dump_memory(clock, self.offset + (addr & self.address_mask), count) + } + + #[inline] + pub fn address_mask(&self) -> Address { + self.address_mask + } + + #[inline] + pub fn data_width(&self) -> u8 { + self.data_width + } +} + +impl Addressable for BusPort { + fn size(&self) -> usize { + self.subdevice.borrow().size() + } + + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { + let addr = self.offset + (addr & self.address_mask); + let mut subdevice = self.subdevice.borrow_mut(); + for i in (0..data.len()).step_by(self.data_width as usize) { + let addr_index = (addr + i as Address) & self.address_mask; + let end = cmp::min(i + self.data_width as usize, data.len()); + Addressable::read(&mut *subdevice, clock, addr_index, &mut data[i..end])?; + } + Ok(()) + } + + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + let addr = self.offset + (addr & self.address_mask); + let mut subdevice = self.subdevice.borrow_mut(); + for i in (0..data.len()).step_by(self.data_width as usize) { + let addr_index = (addr + i as Address) & self.address_mask; + let end = cmp::min(i + self.data_width as usize, data.len()); + Addressable::write(&mut *subdevice, clock, addr_index, &data[i..end])?; + } + Ok(()) + } +} +*/ + +pub fn dump_slice(data: &[u8], mut count: usize) { + let mut addr = 0; + while count > 0 { + let mut line = format!("{:#010x}: ", addr); + + let to = if count < 16 { count / 2 } else { 8 }; + for _ in 0..to { + let word = u16::from_be_bytes(data[addr..addr + 2].try_into().unwrap()); + write!(line, "{:#06x} ", word).unwrap(); + addr += 2; + count -= 2; + } + println!("{}", line); + } +} + +pub fn dump_memory(bus: &mut Bus, clock: Instant, addr: Address, count: Address) +where + Bus: BusAccess, + Address: From + Into + Copy, + Instant: Copy, +{ + let mut addr = addr.into(); + let mut count = count.into(); + while count > 0 { + let mut line = format!("{:#010x}: ", addr); + + let to = if count < 16 { count / 2 } else { 8 }; + for _ in 0..to { + let word = bus.read_beu16(clock, Address::from(addr)); + if word.is_err() { + println!("{}", line); + return; + } + write!(line, "{:#06x} ", word.unwrap()).unwrap(); + addr += 2; + count -= 2; + } + println!("{}", line); + } +} + +/* +impl BusError for Error {} + +impl BusAccess for &mut dyn Addressable { + type Instant = Instant; + type Error = Error; + + fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result { + (*self).read(now, addr, data)?; + Ok(data.len()) + } + + fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result { + (*self).write(now, addr, data)?; + Ok(data.len()) + } +} + +impl BusAccess for Bus { + type Instant = Instant; + type Error = Error; + + fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result { + Addressable::read(self, now, addr, data)?; + Ok(data.len()) + } + + fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result { + Addressable::write(self, now, addr, data)?; + Ok(data.len()) + } +} +*/ diff --git a/emulator/libraries/system/src/system.rs b/emulator/libraries/system/src/system.rs new file mode 100644 index 0000000..fc170da --- /dev/null +++ b/emulator/libraries/system/src/system.rs @@ -0,0 +1,240 @@ +use std::rc::Rc; +use std::cell::{RefCell, RefMut}; +use std::collections::HashMap; +use femtos::{Instant, Duration}; + +use crate::{Bus, Error, InterruptController, Address, Device}; + + +pub struct System { + pub clock: Instant, + pub devices: HashMap, + pub event_queue: Vec, + + pub debuggables: Vec, + + pub bus: Rc>, + pub interrupt_controller: RefCell, +} + +impl Default for System { + fn default() -> Self { + Self { + clock: Instant::START, + devices: HashMap::new(), + event_queue: vec![], + + debuggables: Vec::new(), + + bus: Rc::new(RefCell::new(Bus::default())), + interrupt_controller: RefCell::new(InterruptController::default()), + } + } +} + +impl System { + pub fn get_bus(&self) -> RefMut<'_, Bus> { + self.bus.borrow_mut() + } + + pub fn get_interrupt_controller(&self) -> RefMut<'_, InterruptController> { + self.interrupt_controller.borrow_mut() + } + + pub fn get_device(&self, name: &str) -> Result { + self.devices + .get(name) + .cloned() + .ok_or_else(|| Error::new(format!("system: no device named {}", name))) + } + + pub fn add_device(&mut self, name: &str, device: Device) -> Result<(), Error> { + log::debug!("adding device {:?}", device); + //self.try_add_debuggable(device.clone()); + self.try_queue_device(device.clone()); + self.devices.insert(name.to_string(), device); + Ok(()) + } + + pub fn add_addressable_device(&mut self, addr: Address, len: usize, device: Device) -> Result<(), Error> { + self.add_peripheral(&format!("mem{:x}", addr), addr, len, device) + } + + pub fn add_peripheral(&mut self, name: &str, addr: Address, len: usize, device: Device) -> Result<(), Error> { + self.bus.borrow_mut().insert(addr, device.clone(), len); + //self.try_add_debuggable(device.clone()); + self.add_device(name, device)?; + Ok(()) + } + + pub fn add_interruptable_device(&mut self, name: &str, device: Device) -> Result<(), Error> { + //self.try_add_debuggable(device.clone()); + self.add_device(name, device)?; + Ok(()) + } + + fn process_one_event(&mut self) -> Result<(), Error> { + let mut event_device = self.event_queue.pop().unwrap(); + self.clock = event_device.next_clock; + //println!("{:?}", event_device.device); + let result = match event_device.device.borrow_mut().as_step().unwrap().step(self.clock, &mut *self.bus.borrow_mut()) { + Ok(next) => { + event_device.next_clock = next; + Ok(()) + }, + Err(err) => Err(err), + }; + self.queue_device(event_device); + result + } + + /// Step the simulation one event exactly + pub fn step(&mut self) -> Result<(), Error> { + match self.process_one_event() { + Ok(()) => {}, + Err(err @ Error::Breakpoint(_)) => { + return Err(err); + }, + Err(err) => { + self.exit_error(); + log::error!("{:?}", err); + return Err(err); + }, + } + Ok(()) + } + + /// Step through the simulation until the next event is for the given device + pub fn step_until_device(&mut self, device: Device) -> Result<(), Error> { + loop { + self.step()?; + + if self.get_next_event_device().id() == device.id() { + break; + } + } + Ok(()) + } + + /* + /// Step through the simulation until the next event scheduled is for a debuggable device + pub fn step_until_debuggable(&mut self) -> Result<(), Error> { + loop { + self.step()?; + + if self.get_next_event_device().borrow_mut().as_debuggable().is_some() { + break; + } + } + Ok(()) + } + */ + + /// Run the simulation until the given simulation clock time has been reached + pub fn run_until_clock(&mut self, clock: Instant) -> Result<(), Error> { + while self.clock < clock { + self.step()?; + } + Ok(()) + } + + /// Run the simulation for `elapsed` amount of simulation time + pub fn run_for_duration(&mut self, elapsed: Duration) -> Result<(), Error> { + let target = self.clock + elapsed; + + while self.clock < target { + self.step()?; + } + Ok(()) + } + + /// Run the simulation forever, or until there is an error + pub fn run_forever(&mut self) -> Result<(), Error> { + self.run_until_clock(Instant::FOREVER) + } + + pub fn exit_error(&mut self) { + for (_, dev) in self.devices.iter() { + if let Some(dev) = dev.borrow_mut().as_step() { + // TODO no on_error anymore + //dev.on_error(self); + } + } + } + + pub fn get_next_event_device(&self) -> Device { + self.event_queue[self.event_queue.len() - 1].device.clone() + } + + /* + pub fn get_next_debuggable_device(&self) -> Option { + for event in self.event_queue.iter().rev() { + if event.device.borrow_mut().as_debuggable().is_some() { + return Some(event.device.clone()); + } + } + None + } + + fn try_add_debuggable(&mut self, device: Device) { + if device.borrow_mut().as_debuggable().is_some() { + self.debuggables.push(device); + } + } + */ + + fn try_queue_device(&mut self, device: Device) { + if device.borrow_mut().as_step().is_some() { + self.queue_device(NextStep::new(device)); + } + } + + fn queue_device(&mut self, device_step: NextStep) { + for (i, event) in self.event_queue.iter().enumerate().rev() { + if event.next_clock > device_step.next_clock { + self.event_queue.insert(i + 1, device_step); + return; + } + } + self.event_queue.insert(0, device_step); + } +} + + +pub struct NextStep { + pub next_clock: Instant, + pub device: Device, +} + +impl NextStep { + pub fn new(device: Device) -> Self { + Self { + next_clock: Instant::START, + device, + } + } +} + +/* +use emulator_hal::bus::{BusType, BusAccess}; + +impl BusType for System { + type Address = u64; + type Error = Error; + type Instant = Instant; +} + +impl BusAccess for System { + fn read(&mut self, _now: Instant, addr: u64, data: &mut [u8]) -> Result { + let addr = addr as usize; + data.copy_from_slice(&self.0[addr..addr + data.len()]); + Ok(data.len()) + } + + fn write(&mut self, _now: Instant, addr: u64, data: &[u8]) -> Result { + let addr = addr as usize; + self.0[addr..addr + data.len()].copy_from_slice(data); + Ok(data.len()) + } +} +*/ diff --git a/emulator/peripherals/generic/Cargo.toml b/emulator/peripherals/generic/Cargo.toml index c2c76f1..f6b0344 100644 --- a/emulator/peripherals/generic/Cargo.toml +++ b/emulator/peripherals/generic/Cargo.toml @@ -6,4 +6,6 @@ edition = "2021" [dependencies] log = "0.4" femtos = "0.1" +emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] } +moa-system = { path = "../../libraries/system" } moa-core = { path = "../../core" } diff --git a/emulator/peripherals/generic/src/ata.rs b/emulator/peripherals/generic/src/ata.rs index a7e2953..d12ea28 100644 --- a/emulator/peripherals/generic/src/ata.rs +++ b/emulator/peripherals/generic/src/ata.rs @@ -1,30 +1,36 @@ use std::fs; +use std::io; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::convert::Infallible; use femtos::Instant; +use emulator_hal::{BusAccess, BusAdapter, Step, FromAddress, IntoAddress, Error as EmuError}; -use moa_core::{Error, Address, Addressable, Transmutable}; +//use moa_core::{Error, Bus, MoaBus, Address, Addressable, Transmutable, DeviceInterface}; +use moa_system::{Error as MoaError, MoaBus, DeviceInterface}; #[rustfmt::skip] mod reg { - use super::Address; - pub(super) const DATA_WORD: Address = 0x20; - pub(super) const DATA_BYTE: Address = 0x21; - pub(super) const FEATURE: Address = 0x23; - pub(super) const ERROR: Address = 0x23; - pub(super) const SECTOR_COUNT: Address = 0x25; - pub(super) const SECTOR_NUM: Address = 0x27; - pub(super) const CYL_LOW: Address = 0x29; - pub(super) const CYL_HIGH: Address = 0x2B; - pub(super) const DRIVE_HEAD: Address = 0x2D; - pub(super) const STATUS: Address = 0x2F; - pub(super) const COMMAND: Address = 0x2F; + use super::DeviceAddress; + pub(super) const DATA_WORD: u8 = 0x20; + pub(super) const DATA_BYTE: u8 = 0x21; + pub(super) const FEATURE: u8 = 0x23; + pub(super) const ERROR: u8 = 0x23; + pub(super) const SECTOR_COUNT: u8 = 0x25; + pub(super) const SECTOR_NUM: u8 = 0x27; + pub(super) const CYL_LOW: u8 = 0x29; + pub(super) const CYL_HIGH: u8 = 0x2B; + pub(super) const DRIVE_HEAD: u8 = 0x2D; + pub(super) const STATUS: u8 = 0x2F; + pub(super) const COMMAND: u8 = 0x2F; } #[rustfmt::skip] mod cmd { - pub(super) const READ_SECTORS: u8 = 0x20; - pub(super) const WRITE_SECTORS: u8 = 0x30; - pub(super) const IDENTIFY: u8 = 0xEC; - pub(super) const SET_FEATURE: u8 = 0xEF; + pub(super) const READ_SECTORS: u8 = 0x20; + pub(super) const WRITE_SECTORS: u8 = 0x30; + pub(super) const IDENTIFY: u8 = 0xEC; + pub(super) const SET_FEATURE: u8 = 0xEF; } #[allow(dead_code)] @@ -38,32 +44,47 @@ const ATA_SECTOR_SIZE: u32 = 512; const DEV_NAME: &str = "ata"; +pub struct DeviceAddress(u8); + #[derive(Default)] -pub struct AtaDevice { +pub struct AtaDevice +where + Error: Default, +{ selected_sector: u32, selected_count: u32, last_error: u8, contents: Vec, + error: PhantomData, } -impl AtaDevice { - pub fn load(&mut self, filename: &str) -> Result<(), Error> { - match fs::read(filename) { - Ok(contents) => { - self.contents = contents; - Ok(()) - }, - Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))), - } +impl AtaDevice +where + Error: Default, +{ + pub fn load(&mut self, filename: &str) -> Result<(), io::Error> { + let contents = fs::read(filename)?; + self.contents = contents; + Ok(()) } -} -impl Addressable for AtaDevice { - fn size(&self) -> usize { + pub fn address_space(&self) -> usize { 0x30 } +} + +impl BusAccess
for AtaDevice +where + Error: EmuError + Default, + Address: IntoAddress + Copy, +{ + type Instant = Instant; + type Error = Error; + + #[inline] + fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result { + let addr = addr.into_address().0; - fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { reg::DATA_WORD => { self.selected_count -= 2; @@ -95,10 +116,13 @@ impl Addressable for AtaDevice { }, } - Ok(()) + Ok(1) } - fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + #[inline] + fn write(&mut self, _clock: Self::Instant, addr: Address, data: &[u8]) -> Result { + let addr = addr.into_address().0; + log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { reg::DRIVE_HEAD => { @@ -139,12 +163,76 @@ impl Addressable for AtaDevice { log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } + Ok(1) + } +} + +impl FromAddress for DeviceAddress { + fn from_address(address: u64) -> Self { + Self(address as u8) + } +} + +impl DeviceInterface for AtaDevice { + fn as_bus_access(&mut self) -> Option<&mut MoaBus> { + Some(self) + } +} + +/* +pub struct MoaAtaDevice(BusAdapter); + +impl Default for MoaAtaDevice { + fn default() -> Self { + MoaAtaDevice(BusAdapter::new(AtaDevice::default(), |addr| addr as u8, |err| Error::new(format!("{:?}", err)))) + } +} + +impl DeviceInterface for MoaAtaDevice { + fn as_bus_access(&mut self) -> Option<&mut MoaBus> { + Some(&mut self.0) + } +} + +impl Deref for MoaAtaDevice { + type Target = AtaDevice; + + fn deref(&self) -> &Self::Target { + &self.0.bus + } +} + +impl DerefMut for MoaAtaDevice { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0.bus + } +} +*/ + +//// OLD INTERFACE + +/* +impl Addressable for AtaDevice { + fn size(&self) -> usize { + self.address_space() + } + + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { + >::read(self, clock, addr as u8, data) + .map_err(|err| Error::new(format!("{:?}", err)))?; + Ok(()) + } + + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + >::write(self, clock, addr as u8, data) + .map_err(|err| Error::new(format!("{:?}", err)))?; Ok(()) } } -impl Transmutable for AtaDevice { +impl Transmutable for AtaDevice { fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { Some(self) } } +*/ diff --git a/emulator/peripherals/motorola/Cargo.toml b/emulator/peripherals/motorola/Cargo.toml index 02c37cc..71fc43a 100644 --- a/emulator/peripherals/motorola/Cargo.toml +++ b/emulator/peripherals/motorola/Cargo.toml @@ -6,5 +6,8 @@ edition = "2021" [dependencies] log = "0.4" femtos = "0.1" +emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] } + +moa-system = { path = "../../libraries/system" } moa-core = { path = "../../core" } moa-host = { path = "../../libraries/host" } diff --git a/emulator/peripherals/motorola/src/lib.rs b/emulator/peripherals/motorola/src/lib.rs index ea6da46..bb66aea 100644 --- a/emulator/peripherals/motorola/src/lib.rs +++ b/emulator/peripherals/motorola/src/lib.rs @@ -1,2 +1,2 @@ mod mc68681; -pub use crate::mc68681::MC68681; +pub use crate::mc68681::{MC68681, MoaMC68681}; diff --git a/emulator/peripherals/motorola/src/mc68681.rs b/emulator/peripherals/motorola/src/mc68681.rs index ec2f300..3b5e720 100644 --- a/emulator/peripherals/motorola/src/mc68681.rs +++ b/emulator/peripherals/motorola/src/mc68681.rs @@ -1,39 +1,46 @@ +use core::marker::PhantomData; +use core::convert::Infallible; +use core::ops::{Deref, DerefMut}; use femtos::{Instant, Duration, Frequency}; +use emulator_hal::{BusAccess, BusAdapter, Step, Instant as EmuInstant, Error as EmuError}; -use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable}; +use moa_core::{System, Bus, Address, Steppable, Addressable, Transmutable}; use moa_host::Tty; +use moa_system::{DeviceInterface, Error, MoaBus, MoaStep}; -const REG_MR1A_MR2A: Address = 0x01; -const REG_SRA_RD: Address = 0x03; -const REG_CSRA_WR: Address = 0x03; -const REG_CRA_WR: Address = 0x05; -const REG_TBA_WR: Address = 0x07; -const REG_RBA_RD: Address = 0x07; +type DeviceAddress = u64; -const REG_MR1B_MR2B: Address = 0x11; -const REG_SRB_RD: Address = 0x13; -const REG_CSRB_WR: Address = 0x13; -const REG_CRB_WR: Address = 0x15; -const REG_TBB_WR: Address = 0x17; -const REG_RBB_RD: Address = 0x17; +const REG_MR1A_MR2A: DeviceAddress = 0x01; +const REG_SRA_RD: DeviceAddress = 0x03; +const REG_CSRA_WR: DeviceAddress = 0x03; +const REG_CRA_WR: DeviceAddress = 0x05; +const REG_TBA_WR: DeviceAddress = 0x07; +const REG_RBA_RD: DeviceAddress = 0x07; -const REG_ACR_WR: Address = 0x09; +const REG_MR1B_MR2B: DeviceAddress = 0x11; +const REG_SRB_RD: DeviceAddress = 0x13; +const REG_CSRB_WR: DeviceAddress = 0x13; +const REG_CRB_WR: DeviceAddress = 0x15; +const REG_TBB_WR: DeviceAddress = 0x17; +const REG_RBB_RD: DeviceAddress = 0x17; -const REG_CTUR_WR: Address = 0x0D; -const REG_CTLR_WR: Address = 0x0F; -const REG_START_RD: Address = 0x1D; -const REG_STOP_RD: Address = 0x1F; +const REG_ACR_WR: DeviceAddress = 0x09; -const REG_IPCR_RD: Address = 0x09; -const REG_OPCR_WR: Address = 0x1B; -const REG_INPUT_RD: Address = 0x1B; -const REG_OUT_SET: Address = 0x1D; -const REG_OUT_RESET: Address = 0x1F; +const REG_CTUR_WR: DeviceAddress = 0x0D; +const REG_CTLR_WR: DeviceAddress = 0x0F; +const REG_START_RD: DeviceAddress = 0x1D; +const REG_STOP_RD: DeviceAddress = 0x1F; -const REG_ISR_RD: Address = 0x0B; -const REG_IMR_WR: Address = 0x0B; -const REG_IVR_WR: Address = 0x19; +const REG_IPCR_RD: DeviceAddress = 0x09; +const REG_OPCR_WR: DeviceAddress = 0x1B; +const REG_INPUT_RD: DeviceAddress = 0x1B; +const REG_OUT_SET: DeviceAddress = 0x1D; +const REG_OUT_RESET: DeviceAddress = 0x1F; + +const REG_ISR_RD: DeviceAddress = 0x0B; +const REG_IMR_WR: DeviceAddress = 0x0B; +const REG_IVR_WR: DeviceAddress = 0x19; // Status Register Bits (SRA/SRB) @@ -80,11 +87,11 @@ pub struct MC68681Port { } impl MC68681Port { - pub fn connect(&mut self, pty: Box) -> Result { + pub fn connect(&mut self, pty: Box) -> String { let name = pty.device_name(); println!("{}: opening pts {}", DEV_NAME, name); self.tty = Some(pty); - Ok(name) + name } pub fn send_byte(&mut self, data: u8) { @@ -114,17 +121,17 @@ impl MC68681Port { } } - pub fn check_rx(&mut self) -> Result { + pub fn check_rx(&mut self) -> bool { if self.rx_enabled && (self.status & SR_RX_READY) == 0 && self.tty.is_some() { let tty = self.tty.as_mut().unwrap(); let result = tty.read(); if let Some(input) = result { self.input = input; self.set_rx_status(true); - return Ok(true); + return true; } } - Ok(false) + false } pub fn check_tx(&mut self) -> bool { @@ -155,7 +162,7 @@ impl MC68681Port { } } -pub struct MC68681 { +pub struct MC68681 { frequency: Frequency, acr: u8, @@ -175,11 +182,15 @@ pub struct MC68681 { input_state: u8, output_conf: u8, output_state: u8, + + address: PhantomData
, + instant: PhantomData, + error: PhantomData, } -impl Default for MC68681 { +impl Default for MC68681 { fn default() -> Self { - MC68681 { + Self { frequency: Frequency::from_hz(3_686_400), acr: 0, @@ -199,29 +210,51 @@ impl Default for MC68681 { input_state: 0, output_conf: 0, output_state: 0, + + address: PhantomData, + instant: PhantomData, + error: PhantomData, } } } -impl MC68681 { - fn set_interrupt_flag(&mut self, flag: u8, value: bool) { +impl MC68681 { + pub fn address_space(&self) -> usize { + 0x30 + } + + pub fn set_interrupt_flag(&mut self, flag: u8, value: bool) { self.int_status = (self.int_status & !flag) | (if value { flag } else { 0 }); } - fn check_interrupt_state(&mut self, system: &System) -> Result<(), Error> { - system - .get_interrupt_controller() - .set((self.int_status & self.int_mask) != 0, 4, self.int_vector) + pub fn get_interrupt_flag(&mut self) -> (bool, u8, u8) { + ((self.int_status & self.int_mask) != 0, 4, self.int_vector) } } -impl Steppable for MC68681 { - fn step(&mut self, system: &System) -> Result { - if self.port_a.check_rx()? { +impl Step for MC68681 +where + Address: Into + Copy, + Instant: EmuInstant, + Bus: BusAccess + ?Sized, +{ + type Instant = Instant; + type Error = Error; + + fn is_running(&mut self) -> bool { + true + } + + fn reset(&mut self, _now: Self::Instant, _bus: &mut Bus) -> Result<(), Self::Error> { + Ok(()) + } + + fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result { + if self.port_a.check_rx() { self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, true); } - if self.port_b.check_rx()? { + if self.port_b.check_rx() { self.set_interrupt_flag(ISR_CH_B_RX_READY_FULL, true); } @@ -242,7 +275,8 @@ impl Steppable for MC68681 { } } - self.check_interrupt_state(system)?; + // TODO this has been added to the Steppable impl, but isn't handled by Step + //self.check_interrupt_state(system)?; if self.port_a.check_tx() { self.set_interrupt_flag(ISR_CH_A_TX_READY, true); @@ -252,16 +286,23 @@ impl Steppable for MC68681 { self.set_interrupt_flag(ISR_CH_B_TX_READY, true); } - Ok(self.frequency.period_duration()) + Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64)) } } -impl Addressable for MC68681 { - fn size(&self) -> usize { - 0x30 - } +impl BusAccess
for MC68681 +where + Address: Into + Copy, + Instant: EmuInstant, + Error: EmuError, +{ + type Instant = Instant; + type Error = Error; + + #[inline] + fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result { + let addr = addr.into(); - fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { REG_SRA_RD => data[0] = self.port_a.status, REG_RBA_RD => { @@ -306,10 +347,13 @@ impl Addressable for MC68681 { log::debug!("{}: read from {:0x} of {:0x}", DEV_NAME, addr, data[0]); } - Ok(()) + Ok(1) } - fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + #[inline] + fn write(&mut self, _clock: Self::Instant, addr: Address, data: &[u8]) -> Result { + let addr = addr.into(); + log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); match addr { REG_MR1A_MR2A | REG_MR1B_MR2B | REG_CSRA_WR | REG_CSRB_WR => { @@ -361,6 +405,71 @@ impl Addressable for MC68681 { }, _ => {}, } + Ok(1) + } +} + +pub struct MoaMC68681(MC68681); + +impl Default for MoaMC68681 { + fn default() -> Self { + MoaMC68681(MC68681::default()) + } +} + +impl DeviceInterface for MC68681 { + fn as_bus_access(&mut self) -> Option<&mut MoaBus> { + Some(self) + } + + fn as_step(&mut self) -> Option<&mut MoaStep> { + Some(self) + } +} + +impl Deref for MoaMC68681 { + type Target = MC68681; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for MoaMC68681 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +//// OLD INTERFACE +/* +impl Steppable for MC68681 { + fn step(&mut self, system: &System) -> Result { + let duration = >::step(self, system.clock, &mut *system.bus.borrow_mut()) + .map(|next| next.duration_since(system.clock)); + + let flags = self.get_interrupt_flag(); + system + .get_interrupt_controller() + .set(flags.0, flags.1, flags.2)?; + duration + } +} + +impl Addressable for MC68681 { + fn size(&self) -> usize { + self.address_space() + } + + fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { + >::read(self, clock, addr as u8, data) + .map_err(|err| Error::new(format!("{:?}", err)))?; + Ok(()) + } + + fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { + >::write(self, clock, addr as u8, data) + .map_err(|err| Error::new(format!("{:?}", err)))?; Ok(()) } } @@ -374,3 +483,4 @@ impl Transmutable for MC68681 { Some(self) } } +*/ diff --git a/emulator/systems/computie/Cargo.toml b/emulator/systems/computie/Cargo.toml index aca9774..1947d13 100644 --- a/emulator/systems/computie/Cargo.toml +++ b/emulator/systems/computie/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" log = "0.4" femtos = "0.1" moa-core = { path = "../../core" } +moa-system = { path = "../../libraries/system" } moa-host = { path = "../../libraries/host" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] } moa-peripherals-generic = { path = "../../peripherals/generic" } diff --git a/emulator/systems/computie/src/system.rs b/emulator/systems/computie/src/system.rs index 680696b..263d88c 100644 --- a/emulator/systems/computie/src/system.rs +++ b/emulator/systems/computie/src/system.rs @@ -1,6 +1,7 @@ -use femtos::Frequency; +use femtos::{Instant, Frequency}; -use moa_core::{System, Error, Debuggable, MemoryBlock, Device}; +//use moa_core::{System, Error, Debuggable, MemoryBlock, Device}; +use moa_system::{System, Error, MoaBus, MoaStep, MemoryBlock, Device, DeviceInterface, BusAdapter}; use moa_host::Host; use moa_m68k::{M68k, M68kType}; @@ -23,32 +24,86 @@ impl Default for ComputieOptions { } } +struct MoaM68k(M68k); + +use moa_system::emulator_hal::{Step, BusAccess}; + +impl Step for MoaM68k +where + Bus: BusAccess + ?Sized, +{ + type Instant = Instant; + type Error = Error; + + fn is_running(&mut self) -> bool { + true + } + + fn reset(&mut self, _now: Self::Instant, _bus: &mut Bus) -> Result<(), Self::Error> { + Ok(()) + } + + fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result { + self.0.step(now, &mut BusAdapter::new(bus, |addr| addr as u64, |err| Error::new(format!("{:?}", err)))) + .map_err(|err| Error::new(format!("{:?}", err))) + } +} + +impl MoaM68k { + pub fn new(cpu: M68k) -> Self { + Self(cpu) + } +} + +impl DeviceInterface for MoaM68k { + fn as_step(&mut self) -> Option<&mut MoaStep> { + Some(self) + } +} + +use core::ops::{Deref, DerefMut}; +impl Deref for MoaM68k { + type Target = M68k; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for MoaM68k { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + pub fn build_computie(host: &H, options: ComputieOptions) -> Result { let mut system = System::default(); let mut rom = MemoryBlock::new(vec![0; 0x10000]); rom.load_at(0x0000, &options.rom)?; - system.add_addressable_device(0x00000000, Device::new(rom))?; + system.add_addressable_device(0x00000000, rom.size(), Device::new(rom))?; let mut ram = MemoryBlock::new(vec![0; options.ram]); - ram.load_at(0, "binaries/computie/kernel.bin")?; - system.add_addressable_device(0x00100000, Device::new(ram))?; + ram.load_at(0, "binaries/computie/kernel.bin") + .map_err(|err| Error::new(format!("{}", err)))?; + system.add_addressable_device(0x00100000, ram.size(), Device::new(ram))?; let mut ata = AtaDevice::default(); - ata.load("binaries/computie/disk-with-partition-table.img")?; - system.add_addressable_device(0x00600000, Device::new(ata))?; + ata.load("binaries/computie/disk-with-partition-table.img") + .map_err(|err| Error::new(format!("{}", err)))?; + system.add_addressable_device(0x00600000, ata.address_space(), Device::new(ata))?; let mut serial = MC68681::default(); - launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?); - launch_slip_connection(serial.port_b.connect(host.add_pty()?)?); - system.add_addressable_device(0x00700000, Device::new(serial))?; + launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)); + launch_slip_connection(serial.port_b.connect(host.add_pty()?)); + system.add_addressable_device(0x00700000, serial.address_space(), Device::new(serial))?; let mut cpu = M68k::from_type(M68kType::MC68010, options.frequency); - cpu.add_breakpoint(0); + //cpu.add_breakpoint(0); - system.add_interruptable_device("cpu", Device::new(cpu))?; + system.add_interruptable_device("cpu", Device::new(MoaM68k::new(cpu)))?; Ok(system) } @@ -57,25 +112,27 @@ pub fn build_computie_k30(host: &H) -> Result { let mut system = System::default(); let monitor = MemoryBlock::load("binaries/computie/monitor-68030.bin")?; - system.add_addressable_device(0x00000000, Device::new(monitor))?; + system.add_addressable_device(0x00000000, monitor.size(), Device::new(monitor))?; let mut ram = MemoryBlock::new(vec![0; 0x00100000]); - ram.load_at(0, "binaries/computie/kernel-68030.bin")?; - system.add_addressable_device(0x00100000, Device::new(ram))?; + ram.load_at(0, "binaries/computie/kernel-68030.bin") + .map_err(|err| Error::new(format!("{}", err)))?; + system.add_addressable_device(0x00100000, ram.size(), Device::new(ram))?; let mut ata = AtaDevice::default(); - ata.load("binaries/computie/disk-with-partition-table.img")?; - system.add_addressable_device(0x00600000, Device::new(ata))?; + ata.load("binaries/computie/disk-with-partition-table.img") + .map_err(|err| Error::new(format!("{}", err)))?; + system.add_addressable_device(0x00600000, ata.address_space(), Device::new(ata))?; let mut serial = MC68681::default(); - launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?); + launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)); //launch_slip_connection(serial.port_b.connect(host.add_pty()?)?); - system.add_addressable_device(0x00700000, Device::new(serial))?; + system.add_addressable_device(0x00700000, serial.address_space(), Device::new(serial))?; let cpu = M68k::from_type(M68kType::MC68030, Frequency::from_hz(10_000_000)); - system.add_interruptable_device("cpu", Device::new(cpu))?; + system.add_interruptable_device("cpu", Device::new(MoaM68k::new(cpu)))?; Ok(system) }