devices6502 0.1.0

Helper library for cpu6502 implementing memory devices
Documentation
/// Trait for memory-mapped devices in a 6502 system.
///
/// The `Device` trait defines a common interface for all memory-mapped devices, such as RAM, ROM,
/// and composite devices. It provides methods for reading and writing memory, initializing device
/// data, and querying address space properties. All device types in this crate implement this trait.
pub trait Device {
    /// Creates a new device instance initialized with the provided data.
    ///
    /// # Arguments
    ///
    /// * `data` - A slice containing the initial data for the device.
    fn with_data(data: &[u8]) -> Self
    where
        Self: Sized;

    /// Initializes the device with the provided data.
    ///
    /// # Arguments
    ///
    /// * `data` - A slice containing the data to copy into the device.
    fn init_data(&mut self, data: &[u8]);

    /// Copies the current contents of the device into the provided destination buffer.
    ///
    /// # Arguments
    ///
    /// * `destination` - The buffer to copy the device contents to.
    fn cache_current_read_data(&self, destination: &mut [u8]) {
        debug_assert_eq!(destination.len(), self.addr_space_size_dyn() as usize);

        for i in 0u32..self.addr_space_size_dyn() {
            destination[i as usize] = self.read(i as u16);
        }
    }

    /// Reads a byte from the specified address.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address to read from.
    ///
    /// # Returns
    ///
    /// The byte read from the device.
    fn read(&self, addr: u16) -> u8;

    /// Writes a byte to the specified address.
    ///
    /// # Arguments
    ///
    /// * `data` - The byte to write.
    /// * `addr` - The address to write to.
    fn write(&mut self, data: u8, addr: u16);

    /// Returns the total size of the device's address space in bytes (static method).
    ///
    /// # Returns
    ///
    /// The size of the address space in bytes.
    fn addr_space_size() -> u32
    where
        Self: Sized;

    /// Returns the number of address bits needed to address this device (static method).
    ///
    /// # Returns
    ///
    /// The number of address bits.
    fn addr_bits_count() -> u8
    where
        Self: Sized,
    {
        let addr_space_size = Self::addr_space_size();
        addr_space_size.ilog2() as u8
    }

    /// Returns the total size of the device's address space in bytes (dynamic method).
    ///
    /// # Returns
    ///
    /// The size of the address space in bytes.
    fn addr_space_size_dyn(&self) -> u32;

    /// Returns the number of address bits needed to address this device (dynamic method).
    ///
    /// # Returns
    ///
    /// The number of address bits.
    fn addr_bits_count_dyn(&self) -> u8 {
        let addr_space_size = self.addr_space_size_dyn();
        addr_space_size.ilog2() as u8
    }

    /// Wraps an address to fit within the device's address space.
    ///
    /// This is useful for devices whose address space is smaller than 16 bits.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address to wrap.
    ///
    /// # Returns
    ///
    /// The wrapped address.
    fn wrap_addr(addr: u16) -> u16
    where
        Self: Sized,
    {
        if Self::addr_bits_count() < 16 {
            let mask = !(0xFFFFu16 << Self::addr_bits_count());
            addr & mask
        } else {
            addr
        }
    }

    /// Returns `true` if the device is currently connected.
    ///
    /// This can be used for devices that may be hot-swapped or disconnected at runtime.
    fn is_connected(&self) -> bool {
        true
    }
}

#[allow(missing_docs)]
pub mod size_const {
    //! Common memory size constants for device configuration.
    //!
    //! The `size_const` module provides convenient constants for common memory sizes,
    //! such as 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K, and 256K. These can be used as generic
    //! parameters for device types like `Ram` and `Rom` to specify their size.
    //!
    //! # Examples
    //! ```
    //! use devices6502::{Ram, size_const::SIZE_16K};
    //!
    //! let ram = Ram::<SIZE_16K>::new();
    //! ```

    pub const SIZE_256K: usize = 256 * 1024;
    pub const SIZE_128K: usize = 128 * 1024;
    pub const SIZE_64K: usize = 64 * 1024;
    pub const SIZE_32K: usize = 32 * 1024;
    pub const SIZE_16K: usize = 16 * 1024;
    pub const SIZE_8K: usize = 8 * 1024;
    pub const SIZE_4K: usize = 4 * 1024;
    pub const SIZE_2K: usize = 2 * 1024;
    pub const SIZE_1K: usize = 1024;
}