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:
transistor 2024-03-31 22:31:40 -07:00
parent 342bb8aa3d
commit aa0336c6eb
19 changed files with 1675 additions and 115 deletions

19
Cargo.lock generated
View File

@ -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]]

View File

@ -2,6 +2,7 @@
resolver = "2"
members = [
"emulator/core",
"emulator/libraries/system",
"emulator/frontends/common",
"emulator/frontends/console",
"emulator/frontends/minifb",

View File

@ -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)]

View File

@ -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" }

View File

@ -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(()) => {},

View 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 = []

View 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>;
}
*/

View 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())
}
}

View 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)
}
}

View 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;

View 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())
}
}
*/

View 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);
}
}
}
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<Device> {
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<usize, Self::Error> {
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<usize, Self::Error> {
let addr = addr as usize;
self.0[addr..addr + data.len()].copy_from_slice(data);
Ok(data.len())
}
}
*/

View File

@ -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" }

View File

@ -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<Error>
where
Error: Default,
{
selected_sector: u32,
selected_count: u32,
last_error: u8,
contents: Vec<u8>,
error: PhantomData<Error>,
}
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<Error> AtaDevice<Error>
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<Address, Error> BusAccess<Address> for AtaDevice<Error>
where
Error: EmuError + Default,
Address: IntoAddress<DeviceAddress> + Copy,
{
type Instant = Instant;
type Error = Error;
#[inline]
fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
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<usize, Self::Error> {
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<u64> for DeviceAddress {
fn from_address(address: u64) -> Self {
Self(address as u8)
}
}
impl DeviceInterface for AtaDevice<MoaError> {
fn as_bus_access(&mut self) -> Option<&mut MoaBus> {
Some(self)
}
}
/*
pub struct MoaAtaDevice(BusAdapter<u64, u8, AtaDevice, Error>);
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<u64> {
fn size(&self) -> usize {
self.address_space()
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
<Self as BusAccess<u8>>::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> {
<Self as BusAccess<u8>>::write(self, clock, addr as u8, data)
.map_err(|err| Error::new(format!("{:?}", err)))?;
Ok(())
}
}
impl Transmutable for AtaDevice {
impl Transmutable for AtaDevice<u64> {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
}
*/

View File

@ -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" }

View File

@ -1,2 +1,2 @@
mod mc68681;
pub use crate::mc68681::MC68681;
pub use crate::mc68681::{MC68681, MoaMC68681};

View File

@ -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<dyn Tty>) -> Result<String, Error> {
pub fn connect(&mut self, pty: Box<dyn Tty>) -> 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<bool, Error> {
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<Address, Instant, Error> {
frequency: Frequency,
acr: u8,
@ -175,11 +182,15 @@ pub struct MC68681 {
input_state: u8,
output_conf: u8,
output_state: u8,
address: PhantomData<Address>,
instant: PhantomData<Instant>,
error: PhantomData<Error>,
}
impl Default for MC68681 {
impl<Address, Instant, Error> Default for MC68681<Address, Instant, Error> {
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<Address, Instant, Error> MC68681<Address, Instant, Error> {
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<Duration, Error> {
if self.port_a.check_rx()? {
impl<Address, Instant, Error, Bus> Step<Bus> for MC68681<Address, Instant, Error>
where
Address: Into<DeviceAddress> + Copy,
Instant: EmuInstant,
Bus: BusAccess<Address, Instant = Instant> + ?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::Instant, Self::Error> {
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<Address, Instant, Error> BusAccess<Address> for MC68681<Address, Instant, Error>
where
Address: Into<DeviceAddress> + 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<usize, Self::Error> {
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<usize, Self::Error> {
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<u64, Instant, Error>);
impl Default for MoaMC68681 {
fn default() -> Self {
MoaMC68681(MC68681::default())
}
}
impl DeviceInterface for MC68681<u64, Instant, Error> {
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<u64, Instant, Error>;
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<Duration, Error> {
let duration = <Self as Step<u64, Bus>>::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> {
<Self as BusAccess<u8>>::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> {
<Self as BusAccess<u8>>::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)
}
}
*/

View File

@ -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" }

View File

@ -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<Instant>);
use moa_system::emulator_hal::{Step, BusAccess};
impl<Bus> Step<Bus> for MoaM68k
where
Bus: BusAccess<u64, Instant = Instant> + ?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::Instant, Self::Error> {
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<Instant>) -> 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<Instant>;
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<H: Host>(host: &H, options: ComputieOptions) -> Result<System, Error> {
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<H: Host>(host: &H) -> Result<System, Error> {
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)
}