Add MMU support and start work on it

This commit is contained in:
pjht 2023-01-24 10:20:07 -06:00
parent 40ea7eae82
commit ade0b99b12
Signed by: pjht
GPG Key ID: E911DEB42C25F8E1
6 changed files with 257 additions and 16 deletions

View File

@ -15,20 +15,30 @@ use crate::{
};
pub trait DMAHandler: Debug {
fn handle<'a>(&mut self, backplane: &'a Backplane, card_accessor: DMACardAccessorBuilder<'a>);
fn handle<'a>(&mut self, backplane: &'a Backplane, card_accessor: CardAccessorBuilder<'a>);
}
pub trait MMUHandler: Debug {
fn translate_address<'a>(
&mut self,
backplane: &'a Backplane,
card_accessor: CardAccessorBuilder<'a>,
address: u32,
write: bool,
) -> NullableResult<u32, BusError>;
}
#[derive(Copy, Clone, Debug)]
#[allow(dead_code)]
pub struct DMACardAccessorBuilder<'a> {
pub struct CardAccessorBuilder<'a> {
backplane: &'a Backplane,
card_no: usize,
}
impl<'a> DMACardAccessorBuilder<'a> {
impl<'a> CardAccessorBuilder<'a> {
#[allow(dead_code)]
pub fn build<T: Card>(self) -> DMACardAccessor<'a, T> {
DMACardAccessor {
pub fn build<T: Card>(self) -> CardAccessor<'a, T> {
CardAccessor {
backplane: self.backplane,
card_no: self.card_no,
card_type: PhantomData,
@ -38,13 +48,13 @@ impl<'a> DMACardAccessorBuilder<'a> {
#[derive(Copy, Clone, Debug)]
#[allow(dead_code)]
pub struct DMACardAccessor<'a, T> {
pub struct CardAccessor<'a, T> {
backplane: &'a Backplane,
card_no: usize,
card_type: PhantomData<T>,
}
impl<T: Card> DMACardAccessor<'_, T> {
impl<T: Card> CardAccessor<'_, T> {
#[allow(dead_code)]
pub fn get(&self) -> MappedMutexGuard<T> {
MutexGuard::map(self.backplane.cards.lock(), |cards| {
@ -57,6 +67,7 @@ impl<T: Card> DMACardAccessor<'_, T> {
pub struct Backplane {
cards: Mutex<Vec<Box<dyn Card>>>,
dma_handlers: Mutex<Vec<(usize, Box<dyn DMAHandler>)>>,
mmu: Mutex<Option<(usize, Box<dyn MMUHandler>)>>,
}
impl Display for Backplane {
@ -81,17 +92,26 @@ impl Backplane {
}
let mut cards = Vec::new();
let mut dma_handlers = Vec::new();
let mut mmu = None;
for item in card_configs.into_iter().map(|cfg| cfg.into_card()) {
let (card, dma_handler) = item?;
cards.push(card);
let (mut card, dma_handler) = item?;
if let Some(dma_handler) = dma_handler {
dma_handlers.push((cards.len() - 1, dma_handler));
dma_handlers.push((cards.len(), dma_handler));
}
if let Some(mmu_ret) = card.try_get_mmu() {
if mmu.is_some() {
panic!("Can't have two MMU cards!");
} else {
mmu = Some((cards.len(), mmu_ret));
}
}
cards.push(card);
}
Ok(Self {
cards: Mutex::new(cards),
dma_handlers: Mutex::new(dma_handlers),
mmu: Mutex::new(mmu),
})
}
@ -115,6 +135,8 @@ impl Backplane {
|card| card.read_word(address),
|card| card.read_word_io(address as u8),
0,
false,
false,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
@ -128,6 +150,8 @@ impl Backplane {
|card| card.read_byte(address),
|card| card.read_byte_io(address as u8),
0,
false,
false,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
@ -141,6 +165,8 @@ impl Backplane {
|card| card.write_word(address, data),
|card| card.write_word_io(address as u8, data),
(),
false,
true,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
@ -154,6 +180,68 @@ impl Backplane {
|card| card.write_byte(address, data),
|card| card.write_byte_io(address as u8, data),
(),
false,
true,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(())
}
pub fn read_word_phys(&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,
true,
false,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(data)
}
pub fn read_byte_phys(&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,
true,
false,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(data)
}
pub fn write_word_phys(&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),
(),
true,
true,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
}
Ok(())
}
pub fn write_byte_phys(&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),
(),
true,
true,
)?;
if !self.dma_handlers.is_locked() {
self.handle_dma()
@ -167,12 +255,33 @@ impl Backplane {
mut mem_func: M,
mut io_func: I,
io_default: T,
bypass_mmu: bool,
write: bool,
) -> Result<T, BusError>
where
M: FnMut(&mut Box<dyn Card>) -> NullableResult<T, BusError>,
I: FnMut(&mut Box<dyn Card>) -> NullableResult<T, BusError>,
T: Copy,
{
let address = if bypass_mmu {
address
} else if let Some((card_no, ref mut mmu)) = *self.mmu.lock() {
match mmu.translate_address(
self,
CardAccessorBuilder {
backplane: self,
card_no,
},
address,
write,
) {
NullableResult::Ok(address) => address,
NullableResult::Err(e) => return Err(e),
NullableResult::Null => return Err(BusError),
}
} else {
address
};
match address {
(0..=0x00fe_ffff) | (0x0100_0000..=0xffff_ffff) => self
.cards
@ -195,7 +304,7 @@ impl Backplane {
for handler in self.dma_handlers.lock().iter_mut() {
handler.1.handle(
self,
DMACardAccessorBuilder {
CardAccessorBuilder {
backplane: self,
card_no: handler.0,
},

View File

@ -1,6 +1,9 @@
#![allow(clippy::transmute_ptr_to_ref)]
use crate::{backplane::DMAHandler, m68k::BusError};
use crate::{
backplane::{DMAHandler, MMUHandler},
m68k::BusError,
};
use anyhow::anyhow;
use mopa::mopafy;
use nullable_result::NullableResult;
@ -116,7 +119,12 @@ pub trait Card: Debug + Display + mopa::Any {
fn cmd(&mut self, _cmd: &[&str]) -> anyhow::Result<()> {
Ok(())
}
fn reset(&mut self) {}
fn try_get_mmu(&mut self) -> Option<Box<dyn MMUHandler>> {
None
}
}
mopafy!(Card);

View File

@ -6,6 +6,7 @@ mod disas;
mod instruction;
mod location;
mod m68k;
mod mmu;
mod peek;
mod ram;
mod rom;

125
src/mmu.rs Normal file
View File

@ -0,0 +1,125 @@
use std::fmt::Display;
use nullable_result::NullableResult;
use crate::{
backplane::{Backplane, CardAccessorBuilder, DMAHandler, MMUHandler},
card::{u16_get_be_byte, Card},
m68k::BusError,
register,
};
const ID: u16 = 5;
#[derive(Debug, Copy, Clone)]
struct PagingEntry {
frame: u32,
present: bool,
writable: bool,
#[allow(unused)]
user: bool,
}
impl From<u32> for PagingEntry {
fn from(entry: u32) -> Self {
Self {
frame: entry >> 12,
present: entry & 0x1 == 1,
writable: entry & 0x2 == 2,
user: entry & 0x4 == 4,
}
}
}
#[derive(Debug)]
pub struct MmuCard {
enabled: bool,
cache: [Option<PagingEntry>; 4096],
map_frames: [Option<u32>; 4],
}
impl Card for MmuCard {
fn new(_data: toml::Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)> {
Ok((
Self {
enabled: false,
cache: [None; 4096],
map_frames: [None; 4],
},
None,
))
}
fn try_get_mmu(&mut self) -> Option<Box<dyn MMUHandler>> {
Some(Box::new(Mmu))
}
fn read_byte_io(&mut self, address: u8) -> NullableResult<u8, BusError> {
match address {
(0xFE..=0xFF) => NullableResult::Ok(u16_get_be_byte(ID, address - 0xFE)),
_ => NullableResult::Null,
}
}
fn cmd(&mut self, cmd: &[&str]) -> anyhow::Result<()> {
if cmd[0] == "enable" && cmd.len() >= 2 {
self.enabled = cmd[1].parse()?;
}
Ok(())
}
fn reset(&mut self) {
self.enabled = false;
}
}
impl Display for MmuCard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"MMU card, {}",
if self.enabled { "enabled" } else { "disabled" },
))
}
}
#[derive(Debug)]
pub struct Mmu;
impl MMUHandler for Mmu {
fn translate_address<'a>(
&mut self,
backplane: &'a Backplane,
card_accessor: CardAccessorBuilder<'a>,
address: u32,
write: bool,
) -> NullableResult<u32, BusError> {
let card_accessor = card_accessor.build::<MmuCard>();
if card_accessor.get().enabled {
let page = address >> 12;
let offset = address & 0xFFF;
let entry = if let Some(entry) = card_accessor.get().cache[page as usize] {
entry
} else {
let map_frame = card_accessor.get().map_frames[(page >> 10) as usize]?;
let entry_address = (map_frame << 12) | ((page & 0x3FF) << 2);
let entry_hi = backplane.read_word_phys(entry_address)?;
let entry_lo = backplane.read_word_phys(entry_address + 2)?;
let entry = PagingEntry::from((entry_hi as u32) << 16 | entry_lo as u32);
card_accessor.get().cache[page as usize] = Some(entry);
entry
};
if entry.present {
if write && !entry.writable {
return NullableResult::Err(BusError);
}
NullableResult::Ok((entry.frame << 12) | offset)
} else {
NullableResult::Null
}
} else {
NullableResult::Ok(address)
}
}
}
register!(MmuCard, "mmu");

View File

@ -26,8 +26,6 @@ pub struct Ram {
enabled: bool,
}
impl Ram {}
impl Card for Ram {
fn new(data: Value) -> anyhow::Result<(Self, Option<Box<dyn DMAHandler>>)> {
let size = data.try_into::<Config>()?.size;

View File

@ -1,5 +1,5 @@
use crate::{
backplane::{Backplane, DMACardAccessorBuilder, DMAHandler},
backplane::{Backplane, CardAccessorBuilder, DMAHandler},
card::{u16_get_be_byte, u32_get_be_byte, u32_set_be_byte, Card},
m68k::BusError,
register,
@ -176,7 +176,7 @@ register!(Storage, "storage");
struct Dma;
impl DMAHandler for Dma {
fn handle<'a>(&mut self, backplane: &'a Backplane, card_accessor: DMACardAccessorBuilder<'a>) {
fn handle<'a>(&mut self, backplane: &'a Backplane, card_accessor: CardAccessorBuilder<'a>) {
let card_accessor = card_accessor.build::<Storage>();
if card_accessor.get().transfer {
let mut address = card_accessor.get().start_addresss;