From b5409f3590de76750db9fa8b72ef9376168c1d3c Mon Sep 17 00:00:00 2001 From: pjht Date: Sun, 20 Nov 2022 19:15:58 -0600 Subject: [PATCH] Add support for DMA cards --- Cargo.lock | 8 ++++ Cargo.toml | 2 + dma_design | 3 ++ src/backplane.rs | 108 ++++++++++++++++++++++++++++++++++++++--------- src/card.rs | 32 ++++++++++---- src/ram.rs | 16 ++++--- src/rom.rs | 20 +++++---- src/storage.rs | 20 +++++---- src/term.rs | 6 +-- 9 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 dma_design diff --git a/Cargo.lock b/Cargo.lock index cf3816e..293883f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 8f89c70..8415e5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/dma_design b/dma_design new file mode 100644 index 0000000..6505bee --- /dev/null +++ b/dma_design @@ -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. diff --git a/src/backplane.rs b/src/backplane.rs index 86712c8..5c48c91 100644 --- a/src/backplane.rs +++ b/src/backplane.rs @@ -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(&self) -> Option> { + MutexGuard::try_map(self.backplane.cards.lock(), |cards| { + cards[self.card_no].downcast_mut::() + }) + .ok() + } +} + #[derive(Debug)] pub struct Backplane { - cards: Vec>, + cards: Mutex>>, + dma_handlers: Mutex)>>, } 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>) -> anyhow::Result { - if cards.len() > 255 { + pub fn new(card_configs: Vec>) -> anyhow::Result { + 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::, _>>()?, + 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 { - self.mem_helper( + pub fn read_word(&self, address: u32) -> Result { + 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 { - self.mem_helper( + pub fn read_byte(&self, address: u32) -> Result { + 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( - &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, + }, + ) + } + } } diff --git a/src/card.rs b/src/card.rs index a35e12d..da0da81 100644 --- a/src/card.rs +++ b/src/card.rs @@ -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> { + #[allow(clippy::type_complexity)] + pub fn into_card(self) -> anyhow::Result<(Box, Option>)> { inventory::iter::() .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>, + #[allow(clippy::type_complexity)] + new: fn(data: Value) -> anyhow::Result<(Box, Option>)>, } impl Type { @@ -35,22 +40,31 @@ impl Type { } } - fn new_card(&self, data: Value) -> anyhow::Result> { + #[allow(clippy::type_complexity)] + fn new_card( + &self, + data: Value, + ) -> anyhow::Result<(Box, Option>)> { (self.new)(data) } } inventory::collect!(Type); -pub trait Card: Debug + Display { - fn new(data: Value) -> anyhow::Result +pub trait Card: Debug + Display + mopa::Any { + fn new(data: Value) -> anyhow::Result<(Self, Option>)> where Self: Sized; - fn new_dyn(data: Value) -> anyhow::Result> + #[allow(clippy::type_complexity)] + fn new_dyn( + data: Value, + ) -> anyhow::Result<(Box, Option>)> 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(); diff --git a/src/ram.rs b/src/ram.rs index 4d03060..feef26c 100644 --- a/src/ram.rs +++ b/src/ram.rs @@ -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 { + fn new(data: Value) -> anyhow::Result<(Self, Option>)> { let size = data.try_into::()?.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 { if !self.enabled { diff --git a/src/rom.rs b/src/rom.rs index 5d8877a..e0fc968 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -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 { + fn new(data: Value) -> anyhow::Result<(Self, Option>)> { let file_name = data.try_into::()?.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 { diff --git a/src/storage.rs b/src/storage.rs index e1b46eb..c9b6d23 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -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 { + fn new(data: Value) -> anyhow::Result<(Self, Option>)> { let file_name = data.try_into::()?.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 { diff --git a/src/term.rs b/src/term.rs index 79a8965..df2b6ae 100644 --- a/src/term.rs +++ b/src/term.rs @@ -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 + fn new(_data: Value) -> anyhow::Result<(Self, Option>)> where Self: Sized, { - Ok(Self) + Ok((Self, None)) } fn read_byte_io(&mut self, address: u8) -> NullableResult {