cge_nes 0.1.1

Cycle-accurate NES (Nintendo Entertainment System) emulator library: CPU, PPU, cartridge, input, and iNES ROM loading.
Documentation
//! Implementation of core PPU functionality including initialization and reset.
//!
//! This module provides the basic implementation for the PPU struct, including:
//! * Default implementation for initialization
//! * Constructor and reset functionality
//! * Access to rendered screen data

use super::{palette, Ppu};
use crate::ppu::palette::Color;
use crate::ppu::scanline_pos::ScanlinePos;
use crate::ppu::vram_read_buffer::VramReadBuffer;
use arrayvec::ArrayVec;

/// Default implementation for PPU initialization.
///
/// Creates a new PPU instance with:
/// * All registers zeroed
/// * Empty OAM and palette RAM
/// * No sprites or background tiles
/// * Frame rendering starting at the beginning
impl Default for Ppu {
    fn default() -> Self {
        Self {
            regs: Default::default(),
            oam: Default::default(),
            palette: palette::Ram::new(),
            read_buffer: Default::default(),
            scanline_pos: ScanlinePos::start_frame(),
            sprites_for_scanline: ArrayVec::new(),
            sprites_start_col: ArrayVec::new(),
            sprites_at_current_pixel: ArrayVec::new(),
            screen_colors: ArrayVec::new(),
            vram_read_buffer: VramReadBuffer::new(),
            cycle_accurate_sprites_enabled: false,
            bg_tile: None,
            #[cfg(test)]
            cycles_since_reset: 0,
            nmi_signal: false,
            sprite_addr: 0,
            sprites_for_next_scanline: Default::default(),

            sprite_zero_pos: u8::MAX,

            #[cfg(feature = "show_name_table_change")]
            last_event: Default::default(),
        }
    }
}

impl Ppu {
    /// Creates a new PPU instance with default values.
    ///
    /// Equivalent to `Ppu::default()` but more explicit.
    /// Initializes all PPU state to known values for starting emulation.
    pub fn new() -> Self {
        Self::default()
    }

    /// Resets the PPU to its initial power-on state.
    ///
    /// This clears all register values and internal state.
    /// In test builds, also resets the cycle counter.
    pub fn reset(&mut self) {
        self.regs.reset();
        #[cfg(test)]
        {
            self.cycles_since_reset = 0;
        }
    }

    /// Returns a slice containing the rendered screen colors.
    ///
    /// This provides access to the internal framebuffer containing the
    /// current frame's pixel data. The slice length will be equal to
    /// the number of pixels rendered so far in the current frame.
    ///
    /// # Returns
    /// A slice of `Color` values representing the screen contents
    pub fn screen_colors(&self) -> &[Color] {
        self.screen_colors.as_slice()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{FrameEvent, PpuCartMemorySpace};

    const CYCLES_PER_SCANLINE: usize = 341;
    const SCANLINES_PER_FRAME: usize = 262;

    struct MockCart;

    impl PpuCartMemorySpace for MockCart {
        fn read(&mut self, _addr: u16) -> u8 {
            0xFF
        }

        fn write(&mut self, _data: u8, _addr: u16) {}
    }

    #[test]
    fn ppu_cycle_count() {
        let mut ppu = Ppu::new();
        let mut cart = MockCart;

        while ppu.run_cycle(&mut cart) != FrameEvent::EndOfFrame {}
        while ppu.run_cycle(&mut cart) != FrameEvent::EndOfFrame {}

        assert_eq!(
            ppu.cycles_since_reset,
            CYCLES_PER_SCANLINE * SCANLINES_PER_FRAME * 2 - 1
        );
    }
}