devices6502 0.1.0

Helper library for cpu6502 implementing memory devices
Documentation
//! Read Only Memory (ROM) device implementation.
//!
//! This module provides a generic ROM implementation that can be configured with different sizes.
//! The ROM size must be:
//! - Greater than 0 bytes
//! - A power of 2
//! - Addressable with at most 16 bits (≤ 64KB)
//!
//! # Examples
//! ```
//! use devices6502::{Rom, Device, SIZE_8K};
//!
//! // Create an 8KB ROM with initial data
//! let data = vec![0xFF; SIZE_8K];
//! let mut rom = Rom::<SIZE_8K>::with_data(&data);
//!
//! // Read data (writes are ignored)
//! assert_eq!(rom.read(0x00), 0xFF);
//! rom.write(0x42, 0x00); // This write operation will be ignored
//! assert_eq!(rom.read(0x00), 0xFF);
//! ```

use super::device::Device;
use super::ram::Ram;

/// A Read Only Memory device implementation.
///
/// `Rom<SIZE>` represents a memory device of `SIZE` bytes that supports only read operations.
/// Write operations are silently ignored. The ROM contents must be initialized during creation
/// using either `Rom::with_data()` or `Rom::default()`.
///
/// The `SIZE` parameter must be:
/// - Greater than 0
/// - A power of 2
/// - Less than or equal to 65536 (64KB)
///
/// # Type Parameters
///
/// * `SIZE` - The size of the ROM in bytes, must be a power of 2
#[derive(Default, Clone, Copy)]
pub struct Rom<const SIZE: usize> {
    internal: Ram<SIZE>,
}

impl<const SIZE: usize> Rom<SIZE> {
    /// Creates a new ROM device with all bytes initialized to zero.
    ///
    /// This is equivalent to calling `Rom::default()`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Returns a reference to the ROM's contents as a slice.
    ///
    /// This can be useful for debugging or memory dumps.
    pub fn as_slice(&self) -> &[u8] {
        self.internal.as_slice()
    }
}

impl<const SIZE: usize> Device for Rom<SIZE> {
    /// Creates a new ROM device initialized with the provided data.
    ///
    /// # Arguments
    ///
    /// * `data` - A slice containing the initial data. Must be exactly `SIZE` bytes long.
    fn with_data(data: &[u8]) -> Self {
        Self {
            internal: Ram::<SIZE>::with_data(data),
        }
    }

    /// Initializes the ROM with the provided data.
    ///
    /// # Arguments
    ///
    /// * `data` - A slice containing the data to copy. Must be exactly `SIZE` bytes long.
    fn init_data(&mut self, data: &[u8]) {
        self.internal.init_data(data);
    }

    /// Copies the current ROM contents to the provided destination buffer.
    ///
    /// # Arguments
    ///
    /// * `destination` - The buffer to copy the ROM contents to. Must be exactly `SIZE` bytes long.
    fn cache_current_read_data(&self, destination: &mut [u8]) {
        self.internal.cache_current_read_data(destination);
    }

    /// Reads a byte from the specified address.
    ///
    /// The address will be wrapped if it exceeds the ROM size.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address to read from
    fn read(&self, addr: u16) -> u8 {
        self.internal.read(addr)
    }

    /// Ignores write operations as this is a read-only device.
    ///
    /// # Arguments
    ///
    /// * `_data` - The byte to write (ignored)
    /// * `_addr` - The address to write to (ignored)
    fn write(&mut self, _data: u8, _addr: u16) {}

    /// Returns the total size of the ROM's address space in bytes.
    ///
    /// This is a static method that returns the same value as the underlying RAM's size.
    fn addr_space_size() -> u32 {
        Ram::<SIZE>::size()
    }

    /// Returns the number of address bits needed to address this ROM.
    ///
    /// This is a static method that returns the same value as the underlying RAM's bits count.
    fn addr_bits_count() -> u8 {
        Ram::<SIZE>::bits_count()
    }

    /// Returns the total size of the ROM's address space in bytes.
    ///
    /// This is a dynamic method that returns the same value as the underlying RAM's size.
    fn addr_space_size_dyn(&self) -> u32 {
        Ram::<SIZE>::size()
    }

    /// Returns the number of address bits needed to address this ROM.
    ///
    /// This is a dynamic method that returns the same value as the underlying RAM's bits count.
    fn addr_bits_count_dyn(&self) -> u8 {
        Ram::<SIZE>::bits_count()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn createrom_write_writesignored() {
        let mut rom = Rom::<16>::new();

        for i in 0u16..16 {
            rom.write(i as u8, i);
        }

        for i in 0u16..16 {
            assert_eq!(rom.internal.read(i), 0);
        }
    }
}