diff --git a/src/backplane.rs b/src/backplane.rs index d5f0415..70d1662 100644 --- a/src/backplane.rs +++ b/src/backplane.rs @@ -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; } #[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(self) -> DMACardAccessor<'a, T> { - DMACardAccessor { + pub fn build(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, } -impl DMACardAccessor<'_, T> { +impl CardAccessor<'_, T> { #[allow(dead_code)] pub fn get(&self) -> MappedMutexGuard { MutexGuard::map(self.backplane.cards.lock(), |cards| { @@ -57,6 +67,7 @@ impl DMACardAccessor<'_, T> { pub struct Backplane { cards: Mutex>>, dma_handlers: Mutex)>>, + mmu: Mutex)>>, } 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 { + 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 { + 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 where M: FnMut(&mut Box) -> NullableResult, I: FnMut(&mut Box) -> NullableResult, 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, }, diff --git a/src/card.rs b/src/card.rs index da0da81..da97cee 100644 --- a/src/card.rs +++ b/src/card.rs @@ -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> { + None + } } mopafy!(Card); diff --git a/src/main.rs b/src/main.rs index 985c4ab..ef3f535 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod disas; mod instruction; mod location; mod m68k; +mod mmu; mod peek; mod ram; mod rom; diff --git a/src/mmu.rs b/src/mmu.rs new file mode 100644 index 0000000..47a0018 --- /dev/null +++ b/src/mmu.rs @@ -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 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; 4096], + map_frames: [Option; 4], +} + +impl Card for MmuCard { + fn new(_data: toml::Value) -> anyhow::Result<(Self, Option>)> { + Ok(( + Self { + enabled: false, + cache: [None; 4096], + map_frames: [None; 4], + }, + None, + )) + } + + fn try_get_mmu(&mut self) -> Option> { + Some(Box::new(Mmu)) + } + + fn read_byte_io(&mut self, address: u8) -> NullableResult { + 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 { + let card_accessor = card_accessor.build::(); + 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"); diff --git a/src/ram.rs b/src/ram.rs index afd1475..ffe8dd9 100644 --- a/src/ram.rs +++ b/src/ram.rs @@ -26,8 +26,6 @@ pub struct Ram { enabled: bool, } -impl Ram {} - impl Card for Ram { fn new(data: Value) -> anyhow::Result<(Self, Option>)> { let size = data.try_into::()?.size; diff --git a/src/storage.rs b/src/storage.rs index 4a4dd7a..9193ba8 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -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::(); if card_accessor.get().transfer { let mut address = card_accessor.get().start_addresss;