mirror of
https://github.com/transistorfet/moa.git
synced 2024-10-03 11:59:55 +00:00
Rewrote core into libraries/system, using the emulator-hal interfaces
This is the proof-of-concept using computie, since it only needs one peripheral and the m68k cpu, which was already rewritten and tested
This commit is contained in:
parent
342bb8aa3d
commit
aa0336c6eb
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -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]]
|
||||
|
@ -2,6 +2,7 @@
|
||||
resolver = "2"
|
||||
members = [
|
||||
"emulator/core",
|
||||
"emulator/libraries/system",
|
||||
"emulator/frontends/common",
|
||||
"emulator/frontends/console",
|
||||
"emulator/frontends/minifb",
|
||||
|
@ -101,6 +101,31 @@ pub trait Addressable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> 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<T: Transmutable + 'static>(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)]
|
||||
|
@ -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" }
|
||||
|
@ -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(()) => {},
|
||||
|
15
emulator/libraries/system/Cargo.toml
Normal file
15
emulator/libraries/system/Cargo.toml
Normal file
@ -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 = []
|
374
emulator/libraries/system/src/devices.rs
Normal file
374
emulator/libraries/system/src/devices.rs
Normal file
@ -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<Duration, Error>;
|
||||
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<u8, Error> {
|
||||
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<u16, Error> {
|
||||
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<u16, Error> {
|
||||
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<u32, Error> {
|
||||
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<u32, Error> {
|
||||
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<T> 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<bool, Error>;
|
||||
}
|
||||
|
||||
/// 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<RefCell<Box<dyn Transmutable>>>;
|
||||
|
||||
pub fn wrap_transmutable<T: Transmutable + 'static>(value: T) -> TransmutableBox {
|
||||
Rc::new(RefCell::new(Box::new(value)))
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
use emulator_hal::{BusAccess, Step, Inspect, Debug};
|
||||
|
||||
pub type MoaBus = dyn BusAccess<u64, Instant = Instant, Error = Error>;
|
||||
pub type MoaStep = dyn Step<MoaBus, Instant = Instant, Error = Error>;
|
||||
//pub type MoaInspect<'a> = dyn Inspect<u64, &'a mut MoaBus, String>;
|
||||
//pub type MoaDebug<'a> = dyn Debug<u64, &'a mut MoaBus, String>;
|
||||
|
||||
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<RefCell<Box<dyn DeviceInterface>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Device(DeviceId, BoxedInterface);
|
||||
|
||||
impl Device {
|
||||
pub fn new<T>(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<dyn DeviceInterface>> {
|
||||
self.1.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, Box<dyn DeviceInterface>>, 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<Interrupt> {
|
||||
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<Interrupt>;
|
||||
// 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<RefCell<TransmutableBox>>,
|
||||
}
|
||||
|
||||
pub trait InterruptController {
|
||||
fn connect(&mut self, priority: InterruptPriority) -> Result<InterruptPort, Error>;
|
||||
fn check_pending(&mut self, id: usize) -> Option<Interrupt>;
|
||||
fn acknowledge(&mut self, id: usize, interrupt: Interrupt) -> Result<(), Error>;
|
||||
}
|
||||
*/
|
101
emulator/libraries/system/src/error.rs
Normal file
101
emulator/libraries/system/src/error.rs
Normal file
@ -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<Infallible> for Error {
|
||||
fn from(err: Infallible) -> Self {
|
||||
Error::new("infallible".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/*
|
||||
impl<E> From<E> for Error
|
||||
where
|
||||
E: EmuError,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
Error::new(format!("{}", err))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl Error {
|
||||
pub fn new<S>(msg: S) -> Error
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Error::Emulator(EmulatorErrorKind::Misc, msg.into())
|
||||
}
|
||||
|
||||
pub fn emulator<S>(kind: EmulatorErrorKind, msg: S) -> Error
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Error::Emulator(kind, msg.into())
|
||||
}
|
||||
|
||||
pub fn processor(native: u32) -> Error {
|
||||
Error::Processor(native)
|
||||
}
|
||||
|
||||
pub fn breakpoint<S>(msg: S) -> Error
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Error::Breakpoint(msg.into())
|
||||
}
|
||||
|
||||
pub fn assertion<S>(msg: S) -> Error
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
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<E> From<HostError<E>> for Error {
|
||||
fn from(_err: HostError<E>) -> Self {
|
||||
Self::Other("other".to_string())
|
||||
}
|
||||
}
|
44
emulator/libraries/system/src/interrupts.rs
Normal file
44
emulator/libraries/system/src/interrupts.rs
Normal file
@ -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<u8, Error> {
|
||||
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)
|
||||
}
|
||||
}
|
19
emulator/libraries/system/src/lib.rs
Normal file
19
emulator/libraries/system/src/lib.rs
Normal file
@ -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;
|
461
emulator/libraries/system/src/memory.rs
Normal file
461
emulator/libraries/system/src/memory.rs
Normal file
@ -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<u8>,
|
||||
}
|
||||
|
||||
impl MemoryBlock {
|
||||
pub fn new(contents: Vec<u8>) -> MemoryBlock {
|
||||
MemoryBlock {
|
||||
read_only: false,
|
||||
contents,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(filename: &str) -> Result<MemoryBlock, Error> {
|
||||
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<Address> 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<usize, Self::Error> {
|
||||
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<usize, Self::Error> {
|
||||
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<dyn Fn(Address) -> Address>,
|
||||
}
|
||||
|
||||
impl AddressTranslator {
|
||||
pub fn new<F>(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<Block>,
|
||||
ignore_unmapped: bool,
|
||||
watchers: Vec<Address>,
|
||||
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<Address> for Bus {
|
||||
type Instant = Instant;
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn read(&mut self, clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
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<usize, Self::Error> {
|
||||
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<RefCell<Bus>>,
|
||||
}
|
||||
|
||||
impl BusPort {
|
||||
pub fn new(offset: Address, address_bits: u8, data_bits: u8, bus: Rc<RefCell<Bus>>) -> 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, Address, Instant>(bus: &mut Bus, clock: Instant, addr: Address, count: Address)
|
||||
where
|
||||
Bus: BusAccess<Address, Instant = Instant>,
|
||||
Address: From<u64> + Into<u64> + 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<u64> for &mut dyn Addressable {
|
||||
type Instant = Instant;
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
(*self).read(now, addr, data)?;
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
|
||||
(*self).write(now, addr, data)?;
|
||||
Ok(data.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl BusAccess<u64> for Bus {
|
||||
type Instant = Instant;
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
Addressable::read(self, now, addr, data)?;
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
|
||||
Addressable::write(self, now, addr, data)?;
|
||||
Ok(data.len())
|
||||
}
|
||||
}
|
||||
*/
|
240
emulator/libraries/system/src/system.rs
Normal file
240
emulator/libraries/system/src/system.rs
Normal file
@ -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<String, Device>,
|
||||
pub event_queue: Vec<NextStep>,
|
||||
|
||||
pub debuggables: Vec<Device>,
|
||||
|
||||
pub bus: Rc<RefCell<Bus>>,
|
||||
pub interrupt_controller: RefCell<InterruptController>,
|
||||
}
|
||||
|
||||
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<Device, Error> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
< |