mirror of
https://github.com/transistorfet/moa.git
synced 2024-12-12 05:29:35 +00:00
Rewrite system device storage
This commit is contained in:
parent
6e7e315808
commit
64571e45b8
@ -1,6 +1,8 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::cell::{RefCell, RefMut, BorrowMutError};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::cell::RefCell;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use femtos::{Duration, Instant};
|
||||
|
||||
use crate::{Error, System};
|
||||
@ -172,6 +174,13 @@ pub trait Inspectable {
|
||||
}
|
||||
|
||||
|
||||
pub type DeviceId = u32;
|
||||
pub trait Resource: Any + 'static + Transmutable {}
|
||||
|
||||
pub type Device = Rc<RefCell<dyn Resource>>;
|
||||
|
||||
impl<T> Resource for T where T: Transmutable + 'static {}
|
||||
|
||||
pub trait Transmutable {
|
||||
#[inline]
|
||||
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
|
||||
@ -199,54 +208,76 @@ pub trait Transmutable {
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from deno_core
|
||||
fn is<T: Resource>(field: &dyn Resource) -> bool {
|
||||
field.type_id() == TypeId::of::<T>()
|
||||
}
|
||||
|
||||
// Taken from deno_core
|
||||
pub fn downcast_rc_refc<'a, T: Resource>(field: &'a Device) -> Option<&'a Rc<RefCell<T>>> {
|
||||
if is::<T>(field.borrow().deref()) {
|
||||
let ptr = field as *const Rc<RefCell<_>> as *const Rc<RefCell<T>>;
|
||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||
Some(unsafe { &*ptr })
|
||||
} else {
|
||||
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)))
|
||||
}
|
||||
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct DeviceId(usize);
|
||||
|
||||
impl DeviceId {
|
||||
pub fn new() -> Self {
|
||||
pub fn get_next_id() -> u32 {
|
||||
let next = NEXT_ID.load(Ordering::Acquire);
|
||||
NEXT_ID.store(next + 1, Ordering::Release);
|
||||
Self(next)
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
impl Default for DeviceId {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
// #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
// pub struct DeviceId(usize);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Device(DeviceId, TransmutableBox);
|
||||
// impl DeviceId {
|
||||
// pub fn new() -> Self {
|
||||
// let next = NEXT_ID.load(Ordering::Acquire);
|
||||
// NEXT_ID.store(next + 1, Ordering::Release);
|
||||
// Self(next)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Device {
|
||||
pub fn new<T>(value: T) -> Self
|
||||
where
|
||||
T: Transmutable + 'static,
|
||||
{
|
||||
Self(DeviceId::new(), wrap_transmutable(value))
|
||||
}
|
||||
// impl Default for DeviceId {
|
||||
// fn default() -> Self {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn id(&self) -> DeviceId {
|
||||
self.0
|
||||
}
|
||||
// #[derive(Clone)]
|
||||
// pub struct Device(DeviceId, TransmutableBox);
|
||||
|
||||
pub fn borrow_mut(&self) -> RefMut<'_, Box<dyn Transmutable>> {
|
||||
self.1.borrow_mut()
|
||||
}
|
||||
// impl Device {
|
||||
// pub fn new<T>(value: T) -> Self
|
||||
// where
|
||||
// T: Transmutable + 'static,
|
||||
// {
|
||||
// Self(DeviceId::new(), wrap_transmutable(value))
|
||||
// }
|
||||
|
||||
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, Box<dyn Transmutable>>, BorrowMutError> {
|
||||
self.1.try_borrow_mut()
|
||||
}
|
||||
}
|
||||
// pub fn id(&self) -> DeviceId {
|
||||
// self.0
|
||||
// }
|
||||
|
||||
// pub fn borrow_mut(&self) -> RefMut<'_, Box<dyn Transmutable>> {
|
||||
// self.1.borrow_mut()
|
||||
// }
|
||||
|
||||
// pub fn try_borrow_mut(&self) -> Result<RefMut<'_, Box<dyn Transmutable>>, BorrowMutError> {
|
||||
// self.1.try_borrow_mut()
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
|
@ -7,7 +7,7 @@ mod memory;
|
||||
mod system;
|
||||
|
||||
pub use crate::devices::{
|
||||
Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device,
|
||||
Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Resource
|
||||
};
|
||||
pub use crate::devices::{
|
||||
read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable,
|
||||
@ -15,6 +15,6 @@ pub use crate::devices::{
|
||||
pub use crate::error::Error;
|
||||
pub use crate::interrupts::InterruptController;
|
||||
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
|
||||
pub use crate::system::System;
|
||||
pub use crate::system::{System, DeviceSettings};
|
||||
|
||||
pub use emulator_hal::bus::{BusAccess};
|
||||
pub use emulator_hal::bus::BusAccess;
|
||||
|
@ -1,29 +1,33 @@
|
||||
use std::rc::Rc;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use femtos::{Instant, Duration};
|
||||
|
||||
use crate::{Bus, Error, InterruptController, Address, Device};
|
||||
use crate::devices::{downcast_rc_refc, get_next_id, Device, DeviceId, Resource};
|
||||
use crate::{Address, Bus, Error, InterruptController};
|
||||
|
||||
|
||||
pub struct System {
|
||||
pub clock: Instant,
|
||||
pub devices: HashMap<String, Device>,
|
||||
pub devices: BTreeMap<DeviceId, Device>,
|
||||
pub event_queue: Vec<NextStep>,
|
||||
pub id_to_name: HashMap<DeviceId, String>,
|
||||
|
||||
pub debuggables: Vec<Device>,
|
||||
pub debuggables: Vec<DeviceId>,
|
||||
|
||||
pub bus: Rc<RefCell<Bus>>,
|
||||
pub buses: HashMap<String, Rc<RefCell<Bus>>>,
|
||||
pub interrupt_controller: RefCell<InterruptController>,
|
||||
}
|
||||
|
||||
|
||||
impl Default for System {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
clock: Instant::START,
|
||||
devices: HashMap::new(),
|
||||
devices: BTreeMap::new(),
|
||||
event_queue: vec![],
|
||||
id_to_name: HashMap::new(),
|
||||
|
||||
debuggables: Vec::new(),
|
||||
|
||||
@ -34,6 +38,24 @@ impl Default for System {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceSettings {
|
||||
pub name: Option<String>,
|
||||
pub address: Option<Address>,
|
||||
pub debuggable: bool,
|
||||
pub queue: bool,
|
||||
}
|
||||
|
||||
impl Default for DeviceSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: None,
|
||||
address: None,
|
||||
debuggable: false,
|
||||
queue: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn get_bus(&self) -> RefMut<'_, Bus> {
|
||||
self.bus.borrow_mut()
|
||||
@ -43,43 +65,104 @@ impl System {
|
||||
self.interrupt_controller.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn get_device(&self, name: &str) -> Result<Device, Error> {
|
||||
pub fn get_device<T: Resource>(&self, device: DeviceId) -> Result<Rc<RefCell<T>>, Error> {
|
||||
self.devices
|
||||
.get(name)
|
||||
.get(&device)
|
||||
.and_then(|rc| downcast_rc_refc::<T>(rc))
|
||||
.cloned()
|
||||
.ok_or_else(|| Error::new(format!("system: no device named {}", name)))
|
||||
.ok_or_else(|| Error::new(format!("system: bad device id {}", device)))
|
||||
}
|
||||
|
||||
pub fn add_device(&mut self, name: &str, device: Device) -> Result<(), Error> {
|
||||
self.try_add_debuggable(device.clone());
|
||||
self.try_queue_device(device.clone());
|
||||
self.devices.insert(name.to_string(), device);
|
||||
Ok(())
|
||||
pub fn get_device_by_name<T: Resource>(&self, name: &str) -> Result<Rc<RefCell<T>>, Error> {
|
||||
let id = self.id_to_name.iter().find_map(|(key, &ref val)| if val == name { Some(key)} else { None });
|
||||
if let Some(id) = id {
|
||||
self.get_device(*id)
|
||||
} else {
|
||||
Err(Error::new(format!("system: could not find device {}", name)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_addressable_device(&mut self, addr: Address, device: Device) -> Result<(), Error> {
|
||||
self.add_peripheral(&format!("mem{:x}", addr), addr, device)
|
||||
pub fn get_dyn_device(&self, device: DeviceId) -> Result<Device, Error> {
|
||||
self.devices
|
||||
.get(&device)
|
||||
.cloned()
|
||||
.ok_or_else(|| Error::new(format!("system: bad device id {}", device)))
|
||||
}
|
||||
|
||||
pub fn add_peripheral(&mut self, name: &str, addr: Address, device: Device) -> Result<(), Error> {
|
||||
pub fn get_dyn_device_by_name(&self, name: &str) -> Result<Device, Error> {
|
||||
let id = self.id_to_name.iter().find_map(|(key, &ref val)| if val == name { Some(key)} else { None });
|
||||
if let Some(id) = id {
|
||||
self.get_dyn_device(*id)
|
||||
} else {
|
||||
Err(Error::new(format!("system: could not find device {}", name)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_device<T: Resource>(&mut self, device: T, settings: DeviceSettings) -> Result<DeviceId, Error> {
|
||||
self.add_device_rc_ref(Rc::new(RefCell::new(device)), settings)
|
||||
}
|
||||
|
||||
pub fn add_device_rc_ref<T: Resource>(&mut self, device: Rc<RefCell<T>>, settings: DeviceSettings) -> Result<DeviceId, Error> {
|
||||
let device = device as Device;
|
||||
self.add_device_rc_dyn(device, settings)
|
||||
}
|
||||
|
||||
pub fn add_device_rc_dyn(&mut self, device: Device, settings: DeviceSettings) -> Result<DeviceId, Error> {
|
||||
let id = get_next_id();
|
||||
self.id_to_name.insert(id, settings.name.unwrap_or_default());
|
||||
|
||||
self.devices.insert(id, device.clone());
|
||||
|
||||
if settings.debuggable && device.borrow_mut().as_debuggable().is_some() {
|
||||
self.debuggables.push(id);
|
||||
}
|
||||
if settings.queue && device.borrow_mut().as_steppable().is_some() {
|
||||
self.queue_device(NextStep::new(id));
|
||||
}
|
||||
if let Some(addr) = settings.address {
|
||||
self.bus.borrow_mut().insert(addr, device.clone());
|
||||
self.try_add_debuggable(device.clone());
|
||||
self.try_queue_device(device.clone());
|
||||
self.devices.insert(name.to_string(), device);
|
||||
Ok(())
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn add_interruptable_device(&mut self, name: &str, device: Device) -> Result<(), Error> {
|
||||
self.try_add_debuggable(device.clone());
|
||||
self.try_queue_device(device.clone());
|
||||
self.devices.insert(name.to_string(), device);
|
||||
Ok(())
|
||||
pub fn add_named_device<T: Resource>(&mut self, name: &str, device: T) -> Result<DeviceId, Error> {
|
||||
self.add_device(device, DeviceSettings {
|
||||
name: Some(name.to_owned()),
|
||||
queue: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_addressable_device<T: Resource>(&mut self, addr: Address, device: T) -> Result<DeviceId, Error> {
|
||||
self.add_device(device, DeviceSettings {
|
||||
name: Some(format!("mem{:x}", addr)),
|
||||
address: Some(addr),
|
||||
queue: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_peripheral<T: Resource>(&mut self, name: &str, addr: Address, device: T) -> Result<DeviceId, Error> {
|
||||
self.add_device(device, DeviceSettings {
|
||||
name: Some(name.to_owned()),
|
||||
address: Some(addr),
|
||||
queue: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_interruptable_device<T: Resource>(&mut self, name: &str, device: T) -> Result<DeviceId, Error> {
|
||||
self.add_device(device, DeviceSettings {
|
||||
name: Some(name.to_owned()),
|
||||
queue: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn process_one_event(&mut self) -> Result<(), Error> {
|
||||
let mut event_device = self.event_queue.pop().unwrap();
|
||||
self.clock = event_device.next_clock;
|
||||
let result = match event_device.device.borrow_mut().as_steppable().unwrap().step(self) {
|
||||
let result = match self.get_dyn_device(event_device.device).unwrap().borrow_mut().as_steppable().unwrap().step(self) {
|
||||
Ok(diff) => {
|
||||
event_device.next_clock = self.clock.checked_add(diff).unwrap();
|
||||
Ok(())
|
||||
@ -107,11 +190,11 @@ impl System {
|
||||
}
|
||||
|
||||
/// Step through the simulation until the next event is for the given device
|
||||
pub fn step_until_device(&mut self, device: Device) -> Result<(), Error> {
|
||||
pub fn step_until_device(&mut self, device: DeviceId) -> Result<(), Error> {
|
||||
loop {
|
||||
self.step()?;
|
||||
|
||||
if self.get_next_event_device().id() == device.id() {
|
||||
if self.get_next_event_device() == device {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -123,7 +206,7 @@ impl System {
|
||||
loop {
|
||||
self.step()?;
|
||||
|
||||
if self.get_next_event_device().borrow_mut().as_debuggable().is_some() {
|
||||
if self.get_dyn_device(self.get_next_event_device()).unwrap().borrow_mut().as_debuggable().is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -161,31 +244,19 @@ impl System {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_next_event_device(&self) -> Device {
|
||||
self.event_queue[self.event_queue.len() - 1].device.clone()
|
||||
pub fn get_next_event_device(&self) -> DeviceId {
|
||||
self.event_queue[self.event_queue.len() - 1].device
|
||||
}
|
||||
|
||||
pub fn get_next_debuggable_device(&self) -> Option<Device> {
|
||||
pub fn get_next_debuggable_device(&self) -> Option<DeviceId> {
|
||||
for event in self.event_queue.iter().rev() {
|
||||
if event.device.borrow_mut().as_debuggable().is_some() {
|
||||
return Some(event.device.clone());
|
||||
if self.get_dyn_device(event.device).unwrap().borrow_mut().as_debuggable().is_some() {
|
||||
return Some(event.device);
|
||||
}
|
||||
}
|
||||
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_steppable().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 {
|
||||
@ -200,11 +271,11 @@ impl System {
|
||||
|
||||
pub struct NextStep {
|
||||
pub next_clock: Instant,
|
||||
pub device: Device,
|
||||
pub device: DeviceId,
|
||||
}
|
||||
|
||||
impl NextStep {
|
||||
pub fn new(device: Device) -> Self {
|
||||
pub fn new(device: DeviceId) -> Self {
|
||||
Self {
|
||||
next_clock: Instant::START,
|
||||
device,
|
||||
|
@ -27,7 +27,7 @@ impl Debugger {
|
||||
pub fn print_step(&mut self, system: &mut System) -> Result<(), Error> {
|
||||
println!("@ {} ns", system.clock.as_duration().as_nanos());
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().print_current_step(system)?;
|
||||
system.get_dyn_device(device).unwrap().borrow_mut().as_debuggable().unwrap().print_current_step(system)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -65,13 +65,13 @@ impl Debugger {
|
||||
let (name, addr) = parse_address(args[1])?;
|
||||
match name {
|
||||
Some(name) => {
|
||||
let target = system.get_device(name)?;
|
||||
let target = system.get_dyn_device_by_name(name)?;
|
||||
target.borrow_mut().as_debuggable().unwrap().add_breakpoint(addr);
|
||||
println!("Breakpoint set for devices {:?} at {:08x}", name, addr);
|
||||
},
|
||||
None => {
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().add_breakpoint(addr);
|
||||
system.get_dyn_device(device).unwrap().borrow_mut().as_debuggable().unwrap().add_breakpoint(addr);
|
||||
println!("Breakpoint set for {:08x}", addr);
|
||||
}
|
||||
},
|
||||
@ -85,13 +85,13 @@ impl Debugger {
|
||||
let (name, addr) = parse_address(args[1])?;
|
||||
match name {
|
||||
Some(name) => {
|
||||
let target = system.get_device(name)?;
|
||||
let target = system.get_dyn_device_by_name(name)?;
|
||||
target.borrow_mut().as_debuggable().unwrap().remove_breakpoint(addr);
|
||||
println!("Breakpoint removed for devices {:?} at {:08x}", name, addr);
|
||||
},
|
||||
None => {
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device.borrow_mut().as_debuggable().unwrap().remove_breakpoint(addr);
|
||||
system.get_dyn_device(device).unwrap().borrow_mut().as_debuggable().unwrap().remove_breakpoint(addr);
|
||||
println!("Breakpoint removed for {:08x}", addr);
|
||||
}
|
||||
},
|
||||
@ -132,13 +132,13 @@ impl Debugger {
|
||||
if args.len() < 2 {
|
||||
println!("Usage: inspect <device_name> [<device specific arguments>]");
|
||||
} else {
|
||||
let device = system.get_device(args[1])?;
|
||||
let subargs = if args.len() > 2 { &args[2..] } else { &[""] };
|
||||
device
|
||||
.borrow_mut()
|
||||
.as_inspectable()
|
||||
.ok_or_else(|| Error::new("That device is not inspectable"))?
|
||||
.inspect(system, subargs)?;
|
||||
// let device = system.get_dyn_device(args[1])?;
|
||||
// let subargs = if args.len() > 2 { &args[2..] } else { &[""] };
|
||||
// device
|
||||
// .borrow_mut()
|
||||
// .as_inspectable()
|
||||
// .ok_or_else(|| Error::new("That device is not inspectable"))?
|
||||
// .inspect(system, subargs)?;
|
||||
}
|
||||
},
|
||||
"dis" | "disassemble" => {
|
||||
@ -155,7 +155,7 @@ impl Debugger {
|
||||
};
|
||||
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
device
|
||||
system.get_dyn_device(device).unwrap()
|
||||
.borrow_mut()
|
||||
.as_debuggable()
|
||||
.unwrap()
|
||||
@ -202,7 +202,7 @@ impl Debugger {
|
||||
//},
|
||||
_ => {
|
||||
if let Some(device) = system.get_next_debuggable_device() {
|
||||
if device.borrow_mut().as_debuggable().unwrap().run_command(system, &args)? {
|
||||
if system.get_dyn_device(device).unwrap().borrow_mut().as_debuggable().unwrap().run_command(system, &args)? {
|
||||
println!("Error: unknown command {}", args[0]);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user