125 lines
4.1 KiB
Rust
125 lines
4.1 KiB
Rust
use serde::{Deserialize, Serialize};
|
||
|
||
use crate::{Emulator, RandomAccessMemory, processor::interrupts::Interrupt};
|
||
|
||
pub mod display;
|
||
|
||
pub struct MappedDevice {
|
||
pub base: u32,
|
||
pub device: Box<dyn IoDevice>,
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||
pub struct IoAccess(u8);
|
||
|
||
impl IoAccess {
|
||
pub const READ: Self = Self(0b01);
|
||
pub const WRITE: Self = Self(0b10);
|
||
|
||
/// Convenience alias for Read + Write.
|
||
pub const READWRITE: Self = Self(Self::READ.0 | Self::WRITE.0);
|
||
pub const NONE: Self = Self(0);
|
||
|
||
/// Bitwise OR – returns a new `IoAccess` that contains all flags from both operands.
|
||
#[inline]
|
||
pub fn or(self, other: Self) -> Self {
|
||
Self(self.0 | other.0)
|
||
}
|
||
|
||
/// Check if a flag is present.
|
||
#[inline]
|
||
pub fn contains(&self, flag: Self) -> bool {
|
||
self.0 & flag.0 != 0
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum DeviceId {
|
||
Display,
|
||
Serial,
|
||
Random,
|
||
Timer,
|
||
}
|
||
|
||
/// Trait representing an input/output device that can be mapped into a memory space.
|
||
///
|
||
/// - Default methods return dummy data.
|
||
pub trait IoDevice: Send {
|
||
/// Return the size (in bytes) of the device's addressable region.
|
||
fn size(&self) -> u32;
|
||
|
||
/// Return the access permissions for the device.
|
||
fn access(&self) -> IoAccess;
|
||
|
||
/// Return the DeviceId
|
||
fn id(&self) -> DeviceId;
|
||
|
||
#[inline]
|
||
/// Read a single byte from the specified address.
|
||
///
|
||
/// By default, write‑only devices return `None`. All other devices
|
||
/// return `Some(0)` as placeholder data. Implementations should override
|
||
/// this method to provide actual read logic.
|
||
fn read_byte(&self, offset: u32) -> Result<u8, Interrupt> {
|
||
if self.access().contains(IoAccess::READ) {
|
||
Ok(0)
|
||
} else {
|
||
Err(Interrupt::ReadFromWriteOnly)
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
/// Write a single byte to the specified address.
|
||
///
|
||
/// By default, read‑only devices return `false` indicating failure.
|
||
/// All other devices return `true`. Implementations should override
|
||
/// this method to perform real write operations and return whether
|
||
/// the operation succeeded.
|
||
fn write_byte(&mut self, offset: u32, val: u8) -> Result<(), Interrupt> {
|
||
if self.access().contains(IoAccess::WRITE) {
|
||
Ok(())
|
||
} else {
|
||
Err(Interrupt::WriteToReadOnly)
|
||
}
|
||
}
|
||
|
||
/// Read a 32‑bit word from the specified address in little‑endian order.
|
||
///
|
||
/// The default implementation composes four consecutive byte reads using
|
||
/// `read_byte`. If any byte read fails, the whole operation returns `None`.
|
||
/// Write‑only devices return `None` immediately. Override this method for
|
||
/// more efficient word reads.
|
||
#[inline]
|
||
fn read_word(&self, offset: u32) -> Result<u32, Interrupt> {
|
||
if self.access().contains(IoAccess::READ) {
|
||
// default: compose from bytes
|
||
let b0 = self.read_byte(offset)? as u32;
|
||
let b1 = self.read_byte(offset + 1)? as u32;
|
||
let b2 = self.read_byte(offset + 2)? as u32;
|
||
let b3 = self.read_byte(offset + 3)? as u32;
|
||
return Ok(b0 | b1 << 8 | b2 << 16 | b3 << 24);
|
||
}
|
||
|
||
Err(Interrupt::ReadFromWriteOnly)
|
||
}
|
||
|
||
/// Write a 32‑bit word to the specified address in little‑endian order.
|
||
///
|
||
/// The default implementation splits the value into bytes and writes each
|
||
/// using `write_byte`. Read‑only devices return `false`; otherwise returns
|
||
/// `true` after attempting all byte writes. Override for more efficient
|
||
/// word writes or to handle partial failures.
|
||
fn write_word(&mut self, offset: u32, val: u32) -> Result<(), Interrupt> {
|
||
if self.access().contains(IoAccess::WRITE) {
|
||
let bytes = val.to_le_bytes();
|
||
self.write_byte(offset, bytes[0]);
|
||
self.write_byte(offset + 1, bytes[1]);
|
||
self.write_byte(offset + 2, bytes[2]);
|
||
self.write_byte(offset + 3, bytes[3]);
|
||
return Ok(());
|
||
}
|
||
|
||
Err(Interrupt::WriteToReadOnly)
|
||
}
|
||
}
|