cge_nes 0.1.2

Cycle-accurate NES (Nintendo Entertainment System) emulator library: CPU, PPU, cartridge, input, and iNES ROM loading.
Documentation
//! Buffered pipeline of VRAM read metadata and data for background rendering.
//!
//! The PPU fetches nametable, attribute, and pattern bytes in a fixed pipeline.
//! This helper struct records addresses and fetched bytes across a few steps so
//! that when a pixel is rendered, the correct values are available.
use ringbuffer::{ConstGenericRingBuffer, RingBuffer};

/// A single entry in the VRAM read pipeline.
///
/// Holds the addresses and the fetched data for one background tile position.
#[derive(Copy, Clone, Default)]
struct VramReadBufferEntry {
    /// (coarse_x, coarse_y) coordinates inside the nametable for this tile.
    coords_in_name_table: (u16, u16),
    /// Full 14-bit address used to fetch the tile index from the nametable.
    tile_addr: u16,
    /// Wether second bg table is selected.
    second_bg_table_selected: bool,
    /// Address used to fetch the attribute byte affecting this tile quadrant.
    attr_addr: u16,
    /// Address for the low background pattern byte (bitplane 0).
    bg_low_addr: u16,
    /// Address for the high background pattern byte (bitplane 1).
    bg_high_addr: u16,
    /// Tile index or nametable byte.
    tile_data: u8,
    /// Attribute table byte for palette selection.
    attr_data: u8,
    /// Low background pattern byte just read.
    bg_low_data: u8,
    /// High background pattern byte just read.
    bg_high_data: u8,
}

/// Fixed-size ring buffer storing a small number of VRAM pipeline entries.
pub struct VramReadBuffer {
    /// Backing ring buffer. We keep a few entries to cover the pipeline latency.
    buffer: ConstGenericRingBuffer<VramReadBufferEntry, 3>,
}

impl VramReadBuffer {
    /// Create a new buffer pre-filled with empty entries so back/front exist.
    pub fn new() -> Self {
        let mut ret = Self {
            buffer: ConstGenericRingBuffer::new(),
        };

        for _ in 0..ret.buffer.capacity() {
            ret.start_new_entry();
        }

        ret
    }

    /// Drop the oldest entry; used after a tile has been fully consumed.
    pub fn pop_front(&mut self) {
        self.buffer.dequeue();
    }

    /// Start a fresh entry at the back, initialized to defaults.
    pub fn start_new_entry(&mut self) {
        self.buffer.push(VramReadBufferEntry::default());
    }

    /// Record the nametable tile address for the current back entry.
    pub fn set_tile_addr(&mut self, addr: u16) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .tile_addr = addr;
    }

    pub fn set_second_bg_table_selected(&mut self, selected: bool) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .second_bg_table_selected = selected;
    }

    /// Record the coarse (x, y) coordinates inside the nametable for the tile.
    pub fn set_coords_in_name_table(&mut self, coords: (u16, u16)) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .coords_in_name_table = coords;
    }

    /// Record the attribute-table address for the current tile.
    pub fn set_attr_addr(&mut self, addr: u16) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .attr_addr = addr;
    }

    /// Record the background low pattern address for the current tile.
    pub fn set_bg_low_addr(&mut self, addr: u16) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_low_addr = addr;
    }

    /// Record the background high pattern address for the current tile.
    pub fn set_bg_high_addr(&mut self, addr: u16) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_high_addr = addr;
    }

    /// Store the nametable byte (tile index) after it is read.
    pub fn set_tile_data(&mut self, data: u8) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .tile_data = data;
    }

    /// Store the attribute byte for the tile after it is read.
    pub fn set_attr_data(&mut self, data: u8) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .attr_data = data;
    }

    /// Store the low pattern byte (bitplane 0) after it is read.
    pub fn set_bg_low_data(&mut self, data: u8) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_low_data = data;
    }

    /// Store the high pattern byte (bitplane 1) after it is read and advance pipeline.
    pub fn set_bg_high_data(&mut self, data: u8) {
        self.buffer
            .back_mut()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_high_data = data;
        self.start_new_entry();
    }

    /// Inspect the most-recently set nametable address.
    pub fn back_tile_addr(&self) -> u16 {
        self.buffer
            .back()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .tile_addr
    }

    pub fn back_bg_second_table_selected(&self) -> bool {
        self.buffer
            .back()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .second_bg_table_selected
    }

    /// Inspect the most-recently set attribute address.
    pub fn back_attr_addr(&self) -> u16 {
        self.buffer
            .back()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .attr_addr
    }

    /// Inspect the most-recently set low pattern address.
    pub fn back_bg_low_addr(&self) -> u16 {
        self.buffer
            .back()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_low_addr
    }

    /// Inspect the most-recently set high pattern address.
    pub fn back_bg_high_addr(&self) -> u16 {
        self.buffer
            .back()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_high_addr
    }

    /// Inspect the most-recently read nametable byte.
    pub fn back_tile_data(&self) -> u8 {
        self.buffer
            .back()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .tile_data
    }

    /// Peek the oldest entry's tile (coarse_x, coarse_y) coordinates.
    pub fn front_coords_in_name_table(&self) -> (u16, u16) {
        self.buffer
            .front()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .coords_in_name_table
    }

    /// Peek the oldest entry's attribute byte.
    pub fn front_attr_data(&self) -> u8 {
        self.buffer
            .front()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .attr_data
    }

    /// Peek the oldest entry's low pattern byte.
    pub fn front_bg_low_data(&self) -> u8 {
        self.buffer
            .front()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_low_data
    }

    /// Peek the oldest entry's high pattern byte.
    pub fn front_bg_high_data(&self) -> u8 {
        self.buffer
            .front()
            .expect("VramReadBuffer should always contain at least 2 elements")
            .bg_high_data
    }
}