devices6502 0.1.0

Helper library for cpu6502 implementing memory devices
Documentation
//! Memory mirroring device.
//!
//! This module provides the [`Mirror`] struct, which allows a device to be repeated (mirrored) multiple
//! times in the address space. This is useful for emulating hardware where a smaller device is mapped
//! multiple times into a larger address space.

use super::device::Device;

/// A device that mirrors another device N times in the address space.
///
/// The `Mirror<T, N>` struct wraps a device of type `T` and exposes it as if it were repeated
/// `N` times in the address space. All reads and writes are mapped to the underlying device,
/// effectively creating a mirrored memory region.
///
/// # Type Parameters
/// - `T`: The device type to be mirrored (must implement [`Device`] and [`Default`])
/// - `N`: The number of times to mirror the device (must be a power of 2, at least 2)
pub struct Mirror<T, const N: u32> {
    device: T,
}

impl<T, const N: u32> Default for Mirror<T, N>
where
    T: Device + Default,
{
    /// Creates a new mirrored device with the underlying device initialized to its default value.
    ///
    /// # Panics
    /// Panics if `N` is less than 2, not a power of 2, or if the total address space exceeds 16 bits.
    fn default() -> Self {
        assert!(N >= 2, "Mirror should have at least N=2");
        assert!(
            Self::addr_bits_count() <= 16,
            "Mirror should be addressable with 16 bit at most"
        );
        assert_eq!(
            N,
            2u32.pow(N.ilog2()),
            "Mirror should have an N that is power of 2"
        );
        Self {
            device: T::default(),
        }
    }
}

impl<T, const N: u32> Device for Mirror<T, N>
where
    T: Device + Default,
{
    /// Creates a new mirrored device initialized with the provided data.
    ///
    /// # Arguments
    /// * `data` - A slice containing the initial data for all mirrored regions.
    fn with_data(data: &[u8]) -> Self {
        let mut new_mirror = Self::new();
        new_mirror.init_data(data);
        new_mirror
    }

    /// Initializes the mirrored device with the provided data.
    ///
    /// # Arguments
    /// * `data` - A slice containing the data for all mirrored regions.
    ///
    /// # Panics
    /// Panics if the data is not correctly mirrored or has the wrong length.
    fn init_data(&mut self, data: &[u8]) {
        assert_eq!(
            data.len(),
            (T::addr_space_size() * N) as usize,
            "incorrect sized data for mirror device"
        );
        let first_instance_of_data = &data[0..(T::addr_space_size() as usize)];
        for i in 1..N {
            let range_start = (i * T::addr_space_size()) as usize;
            let range_end = T::addr_space_size() as usize + range_start;
            assert_eq!(
                first_instance_of_data,
                &data[range_start..range_end],
                "data not correctly mirrored"
            );
        }
        self.device.init_data(first_instance_of_data);
    }

    /// Copies the current contents of the mirrored device into the provided buffer.
    ///
    /// # Arguments
    /// * `destination` - The buffer to copy the mirrored contents to.
    fn cache_current_read_data(&self, destination: &mut [u8]) {
        let inner_size = self.device.addr_space_size_dyn() as usize;
        for i in 0..(N as usize) {
            let start = i * inner_size;
            let end = (i + 1) * inner_size;
            self.device
                .cache_current_read_data(&mut destination[start..end]);
        }
    }

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

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

    /// Returns the total size of the mirrored address space in bytes.
    fn addr_space_size() -> u32 {
        T::addr_space_size() * N
    }

    /// Returns the number of address bits needed to address the mirrored device.
    fn addr_bits_count() -> u8 {
        T::addr_bits_count() + (N.ilog2() as u8)
    }

    /// Returns the total size of the mirrored address space in bytes (dynamic version).
    fn addr_space_size_dyn(&self) -> u32 {
        T::addr_space_size() * N
    }

    /// Returns the number of address bits needed to address the mirrored device (dynamic version).
    fn addr_bits_count_dyn(&self) -> u8 {
        T::addr_bits_count() + (N.ilog2() as u8)
    }

    /// Returns `true` if the underlying device is connected.
    fn is_connected(&self) -> bool {
        self.device.is_connected()
    }
}

impl<T, const N: u32> Mirror<T, N>
where
    T: Device + Default,
{
    /// Creates a new mirrored device with the underlying device initialized to its default value.
    pub fn new() -> Self {
        Self::default()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::Ram;
    extern crate alloc;
    use alloc::vec;
    use alloc::vec::Vec;

    #[test]
    fn createmirror_writedifferentinstances_allinstancescorrect() {
        const N: u16 = 4;
        const SIZE: usize = 4;
        let mut mirror = Mirror::<Ram<SIZE>, { N as u32 }>::new();

        for i in 0..N {
            let addr = i + i * N;
            mirror.device.write(i as u8, addr);
        }

        for n in 0..N {
            for i in 0..N {
                let addr = n * N + i;
                assert_eq!(mirror.device.read(addr), i as u8);
            }
        }
    }

    #[test]
    #[should_panic(expected = "Mirror should have an N that is power of 2")]
    fn createmirror_notpowe2n_panics() {
        let _ = Mirror::<Ram<16>, 3>::new();
    }

    #[test]
    #[should_panic(expected = "Mirror should have at least N=2")]
    fn createmirror_0n_panics() {
        let _ = Mirror::<Ram<16>, 0>::new();
    }

    #[test]
    #[should_panic(expected = "Mirror should have at least N=2")]
    fn createmirror_1n_panics() {
        let _ = Mirror::<Ram<16>, 1>::new();
    }

    #[test]
    fn createmirror_cachereaddata_cachecorrect() {
        const N: u16 = 4;
        const SIZE: usize = 4;
        let mut mirror = Mirror::<Ram<SIZE>, { N as u32 }>::new();

        let mut data = vec![1u8, 2, 3, 4];

        for (i, data) in data.iter().enumerate() {
            mirror.write(*data, i as u16);
        }

        let data_clone = data.clone();
        data.extend_from_slice(&data_clone);
        data.extend_from_slice(&data_clone);
        data.extend_from_slice(&data_clone);

        let mut cache = Vec::with_capacity(16);
        cache.resize(16, 0xFE);

        mirror.cache_current_read_data(&mut cache);

        assert_eq!(cache, data);
    }
}