mirror of
https://github.com/transistorfet/moa.git
synced 2024-09-27 16:57:32 +00:00
35e746f0af
This is the proof-of-concept using computie, since it only needs one peripheral and the m68k cpu, which was already rewritten and tested
375 lines
9.6 KiB
Rust
375 lines
9.6 KiB
Rust
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>;
|
|
}
|
|
*/
|