Add support for DMA cards

This commit is contained in:
pjht 2022-11-20 19:15:58 -06:00
parent a9d79025cf
commit b5409f3590
9 changed files with 161 additions and 54 deletions

8
Cargo.lock generated
View File

@ -460,7 +460,9 @@ dependencies = [
"indexmap",
"inventory",
"itertools",
"mopa",
"nullable-result",
"parking_lot",
"parse_int",
"reedline-repl-rs",
"serde",
@ -486,6 +488,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "mopa"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915"
[[package]]
name = "nu-ansi-term"
version = "0.45.1"

View File

@ -16,7 +16,9 @@ human-repr = { version = "1.0.1", features = ["iec", "space"] }
indexmap = "1.9.1"
inventory = "0.3.1"
itertools = "0.10.5"
mopa = "0.2.2"
nullable-result = { version = "0.7.0", features=["try_trait"] }
parking_lot = "0.12.1"
parse_int = "0.6.0"
reedline-repl-rs = "1.0.2"
serde = { version = "1.0.144", features = ["derive"] }

3
dma_design Normal file
View File

@ -0,0 +1,3 @@
Card new function can optionally return a DMA handler struct, backplane stores it in an array.
After every IO/memory access, backplane loops through and calls each DMA handler struct with a mutable reference to itself and a card accessor struct.
The card accessor struct takes a reference (mut/immut) and returns a reference (mut/immut respectively) to the main DMA card for that handler struct.

View File

@ -1,17 +1,43 @@
use std::fmt::Debug;
use std::fmt::Display;
use anyhow::anyhow;
use itertools::Itertools;
use nullable_result::{GeneralIterExt, NullableResult};
use parking_lot::MappedMutexGuard;
use parking_lot::Mutex;
use parking_lot::MutexGuard;
use crate::{
card::{self, Card},
m68k::BusError,
};
pub trait DMAHandler: Debug {
fn handle<'a>(&mut self, backplane: &'a Backplane, card_accessor: DMACardAccessor<'a>);
}
#[derive(Copy, Clone, Debug)]
#[allow(dead_code)]
pub struct DMACardAccessor<'a> {
backplane: &'a Backplane,
card_no: usize,
}
impl DMACardAccessor<'_> {
#[allow(dead_code)]
pub fn get<T: Card>(&self) -> Option<MappedMutexGuard<T>> {
MutexGuard::try_map(self.backplane.cards.lock(), |cards| {
cards[self.card_no].downcast_mut::<T>()
})
.ok()
}
}
#[derive(Debug)]
pub struct Backplane {
cards: Vec<Box<dyn Card>>,
cards: Mutex<Vec<Box<dyn Card>>>,
dma_handlers: Mutex<Vec<(usize, Box<dyn DMAHandler>)>>,
}
impl Display for Backplane {
@ -19,6 +45,7 @@ impl Display for Backplane {
f.write_fmt(format_args!(
"{}",
self.cards
.lock()
.iter()
.enumerate()
.format_with("\n", |(i, card), g| {
@ -29,69 +56,94 @@ impl Display for Backplane {
}
impl Backplane {
pub fn new(cards: Vec<card::Config<'_>>) -> anyhow::Result<Self> {
if cards.len() > 255 {
pub fn new(card_configs: Vec<card::Config<'_>>) -> anyhow::Result<Self> {
if card_configs.len() > 255 {
return Err(anyhow!("A maximum of 255 cards are allowed"));
}
let mut cards = Vec::new();
let mut dma_handlers = Vec::new();
for item in card_configs.into_iter().map(|cfg| cfg.into_card()) {
let (card, dma_handler) = item?;
cards.push(card);
if let Some(dma_handler) = dma_handler {
dma_handlers.push((cards.len() - 1, dma_handler));
}
}
Ok(Self {
cards: cards
.into_iter()
.map(|cfg| cfg.into_card())
.collect::<Result<Vec<_>, _>>()?,
cards: Mutex::new(cards),
dma_handlers: Mutex::new(dma_handlers),
})
}
pub fn reset(&mut self) {
for card in &mut self.cards {
pub fn reset(&self) {
for card in self.cards.lock().iter_mut() {
card.reset();
}
}
pub fn card_cmd(&mut self, card_num: u8, cmd: &[&str]) -> anyhow::Result<()> {
pub fn card_cmd(&self, card_num: u8, cmd: &[&str]) -> anyhow::Result<()> {
self.cards
.lock()
.get_mut(card_num as usize)
.ok_or_else(|| anyhow!("Card {} does not exist", card_num))?
.cmd(cmd)
}
pub fn read_word(&mut self, address: u32) -> Result<u16, BusError> {
self.mem_helper(
pub fn read_word(&self, address: u32) -> Result<u16, BusError> {
let data = self.mem_helper(
address,
|card| card.read_word(address),
|card| card.read_word_io(address as u8),
0,
)
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(data)
}
pub fn read_byte(&mut self, address: u32) -> Result<u8, BusError> {
self.mem_helper(
pub fn read_byte(&self, address: u32) -> Result<u8, BusError> {
let data = self.mem_helper(
address,
|card| card.read_byte(address),
|card| card.read_byte_io(address as u8),
0,
)
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(data)
}
pub fn write_word(&mut self, address: u32, data: u16) -> Result<(), BusError> {
pub fn write_word(&self, address: u32, data: u16) -> Result<(), BusError> {
self.mem_helper(
address,
|card| card.write_word(address, data),
|card| card.write_word_io(address as u8, data),
(),
)
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(())
}
pub fn write_byte(&mut self, address: u32, data: u8) -> Result<(), BusError> {
pub fn write_byte(&self, address: u32, data: u8) -> Result<(), BusError> {
self.mem_helper(
address,
|card| card.write_byte(address, data),
|card| card.write_byte_io(address as u8, data),
(),
)
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(())
}
fn mem_helper<T, M, I>(
&mut self,
&self,
address: u32,
mut mem_func: M,
mut io_func: I,
@ -105,16 +157,30 @@ impl Backplane {
match address {
(0..=0x00fe_ffff) | (0x0100_0000..=0xffff_ffff) => self
.cards
.lock()
.iter_mut()
.try_find_map(&mut mem_func)
.result(BusError),
(0x00ff_0000..=0x00ff_00ff) => Ok(io_default),
(0x00ff_0100..=0x00ff_ffff) => self
.cards
.lock()
.get_mut(((address >> 8) as u8 - 1) as usize)
.map_or(Ok(io_default), |card| {
io_func(card).optional_result().unwrap_or(Ok(io_default))
}),
}
}
fn handle_dma(&self) {
for handler in self.dma_handlers.lock().iter_mut() {
handler.1.handle(
self,
DMACardAccessor {
backplane: self,
card_no: handler.0,
},
)
}
}
}

View File

@ -1,5 +1,8 @@
use crate::m68k::BusError;
#![allow(clippy::transmute_ptr_to_ref)]
use crate::{backplane::DMAHandler, m68k::BusError};
use anyhow::anyhow;
use mopa::mopafy;
use nullable_result::NullableResult;
use serde::Deserialize;
use std::fmt::{Debug, Display};
@ -14,7 +17,8 @@ pub struct Config<'a> {
}
impl Config<'_> {
pub fn into_card(self) -> anyhow::Result<Box<dyn Card>> {
#[allow(clippy::type_complexity)]
pub fn into_card(self) -> anyhow::Result<(Box<dyn Card>, Option<Box<dyn DMAHandler>>)> {
inventory::iter::<Type>()
.find(|card_type| card_type.name == self.typ)
.ok_or_else(|| anyhow!("Invalid card type {}", self.typ))?
@ -24,7 +28,8 @@ impl Config<'_> {
pub struct Type {
name: &'static str,
new: fn(data: Value) -> anyhow::Result<Box<dyn Card>>,
#[allow(clippy::type_complexity)]
new: fn(data: Value) -> anyhow::Result<(Box<dyn Card>, Option<Box<dyn DMAHandler>>)>,
}
impl Type {
@ -35,22 +40,31 @@ impl Type {
}
}
fn new_card(&self, data: Value) -> anyhow::Result<Box<dyn Card>> {
#[allow(clippy::type_complexity)]
fn new_card(
&self,
data: Value,
) -> anyhow::Result<(Box<dyn Card>, Option<Box<dyn DMAHandler>>)> {
(self.new)(data)
}
}
inventory::collect!(Type);
pub trait Card: Debug + Display {
fn new(data: Value) -> anyhow::Result<Self>
pub trait Card: Debug + Display + mopa::Any {
fn new(data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler + 'static>>)>
where
Self: Sized;
fn new_dyn(data: Value) -> anyhow::Result<Box<dyn Card>>
#[allow(clippy::type_complexity)]
fn new_dyn(
data: Value,
) -> anyhow::Result<(Box<dyn Card>, Option<Box<dyn DMAHandler + 'static>>)>
where
Self: Sized + 'static,
{
Ok(Box::new(Self::new(data)?))
let (card, handler) = Self::new(data)?;
let card = Box::new(card);
Ok((card, handler))
}
fn display(&self) -> String {
String::new()
@ -105,6 +119,8 @@ pub trait Card: Debug + Display {
fn reset(&mut self) {}
}
mopafy!(Card);
#[allow(dead_code)]
pub const fn u64_set_be_byte(val: u64, idx: u8, byte: u8) -> u64 {
let mut bytes = val.to_be_bytes();

View File

@ -6,6 +6,7 @@ use serde::Deserialize;
use toml::Value;
use crate::{
backplane::DMAHandler,
card::{u32_get_be_byte, Card},
m68k::BusError,
register,
@ -26,13 +27,16 @@ pub struct Ram {
impl Ram {}
impl Card for Ram {
fn new(data: Value) -> anyhow::Result<Self> {
fn new(data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)> {
let size = data.try_into::<Config>()?.size;
Ok(Self {
data: vec![0; size as usize],
start: 0,
enabled: false,
})
Ok((
Self {
data: vec![0; size as usize],
start: 0,
enabled: false,
},
None,
))
}
fn read_byte(&mut self, address: u32) -> NullableResult<u8, BusError> {
if !self.enabled {

View File

@ -7,6 +7,7 @@ use serde::Deserialize;
use toml::Value;
use crate::{
backplane::DMAHandler,
card::{u16_get_be_byte, u16_set_be_byte, Card},
m68k::BusError,
register,
@ -29,7 +30,7 @@ pub struct Rom {
impl Rom {}
impl Card for Rom {
fn new(data: Value) -> anyhow::Result<Self> {
fn new(data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)> {
let file_name = data.try_into::<Config>()?.image;
let mut data = Vec::new();
if let Some(file_name) = file_name.as_ref() {
@ -38,13 +39,16 @@ impl Card for Rom {
.read_to_end(&mut data)
.map_err(|e| anyhow!("Failed to read ROM image file {} ({})", file_name, e))?;
};
Ok(Self {
data,
enabled: true,
ram: [0; 32 * 1024],
file_name,
start: 0,
})
Ok((
Self {
data,
enabled: true,
ram: [0; 32 * 1024],
file_name,
start: 0,
},
None,
))
}
fn read_byte(&mut self, address: u32) -> NullableResult<u8, BusError> {

View File

@ -1,4 +1,5 @@
use crate::{
backplane::DMAHandler,
card::{u16_get_be_byte, u32_get_be_byte, u32_set_be_byte, Card},
m68k::BusError,
register,
@ -46,7 +47,7 @@ pub struct Storage {
// 0xA-0xC: Data
impl Card for Storage {
fn new(data: Value) -> anyhow::Result<Self> {
fn new(data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)> {
let file_name = data.try_into::<Config>()?.image;
let file = file_name
.as_ref()
@ -56,13 +57,16 @@ impl Card for Storage {
})
.transpose()?;
Ok(Self {
file: file.zip(file_name),
read_data: VecDeque::new(),
sector: 0,
count: 0,
status: Status::empty(),
})
Ok((
Self {
file: file.zip(file_name),
read_data: VecDeque::new(),
sector: 0,
count: 0,
status: Status::empty(),
},
None,
))
}
fn read_byte_io(&mut self, address: u8) -> NullableResult<u8, BusError> {

View File

@ -3,7 +3,7 @@ use std::fmt::Display;
use nullable_result::NullableResult;
use toml::Value;
use crate::{card::Card, m68k::BusError, register};
use crate::{backplane::DMAHandler, card::Card, m68k::BusError, register};
#[derive(Debug)]
pub struct Term;
@ -15,11 +15,11 @@ impl Display for Term {
}
impl Card for Term {
fn new(_data: Value) -> anyhow::Result<Self>
fn new(_data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)>
where
Self: Sized,
{
Ok(Self)
Ok((Self, None))
}
fn read_byte_io(&mut self, address: u8) -> NullableResult<u8, BusError> {