pub struct Ppu { /* private fields */ }Expand description
PPU1 (5C77) + PPU2 (5C78) state.
Owns VRAM (32 K words), CGRAM (256 × 15-bit BGR), OAM (544 bytes), the full register file,
the dot/scanline timeline, and a 256×239 15-bit framebuffer. Advanced one dot at a time by
the master-clock scheduler via Ppu::tick_dot; the scheduler polls Ppu::nmi_pending,
Ppu::irq_pending, and Ppu::frame_ready to drive interrupts and presentation.
Implementations§
Source§impl Ppu
impl Ppu
Sourcepub fn new() -> Self
pub fn new() -> Self
Construct at power-on (NTSC). Storage starts zeroed; this is deterministic — the
determinism contract forbids the OS RNG (docs/adr/0004). Phase alignment, if any, is
driven by the scheduler’s seeded PRNG, not here.
Sourcepub fn with_region(region: Region) -> Self
pub fn with_region(region: Region) -> Self
Construct at power-on for a specific Region.
Sourcepub const fn set_region(&mut self, region: Region)
pub const fn set_region(&mut self, region: Region)
Set the video region (NTSC/PAL). Affects line count + the STAT78 region bit.
Sourcepub const fn visible_height(&self) -> u16
pub const fn visible_height(&self) -> u16
The total visible height for the current overscan setting (224 or 239).
Sourcepub const fn is_hires(&self) -> bool
pub const fn is_hires(&self) -> bool
Whether the PPU is currently configured for hi-res output: BGMODE 5/6, or pseudo-hires
(SETINI $2133 bit 3) in any mode — matching ares’ hires = pseudoHires || bgMode==5 || bgMode==6 (ref-proj/ares/ares/sfc/ppu/dac.cpp). This is the live per-scanline register
state; Ppu::frame_hires is the value latched from this at the start of the frame that’s
actually being composited into the framebuffer right now.
Sourcepub const fn frame_hires(&self) -> bool
pub const fn frame_hires(&self) -> bool
Whether the framebuffer currently being composited is hi-res (512-wide) — the value
Ppu::framebuffer’s length/stride is based on for this frame. See the frame_hires
field doc for why this is latched once per frame rather than read live per scanline.
Sourcepub const fn visible_width(&self) -> usize
pub const fn visible_width(&self) -> usize
The output framebuffer’s width for the frame currently being composited: SCREEN_WIDTH
normally, MAX_SCREEN_WIDTH when Ppu::frame_hires is set.
Sourcepub fn tick_dot(&mut self, bus: &mut impl VideoBus)
pub fn tick_dot(&mut self, bus: &mut impl VideoBus)
Advance the PPU by exactly one dot, the scheduler’s video quantum. Composites a full
visible scanline at RENDER_DOT (one dot before that line’s own per-line HDMA run can
observe/mutate the registers the composite reads — see docs/ppu.md §Mid-scanline/
HDMA-driven register timing); raises NMI at VBlank start and the HV-IRQ when the
programmed H/V is hit; calls VideoBus::notify_scanline at each line start and
VideoBus::notify_vblank when VBlank begins. Hot path: allocation-free.
Sourcepub const fn nmi_pending(&self) -> bool
pub const fn nmi_pending(&self) -> bool
Whether an NMI is pending (asserted at VBlank start). Poll; clear with Ppu::ack_nmi.
Sourcepub const fn ack_nmi(&mut self)
pub const fn ack_nmi(&mut self)
Acknowledge / clear the pending NMI (the CPU does this when it takes the vector).
Sourcepub const fn irq_pending(&self) -> bool
pub const fn irq_pending(&self) -> bool
Whether an HV-timer IRQ is pending. Poll; clear with Ppu::ack_irq.
Sourcepub const fn set_hv_irq(
&mut self,
enable_h: bool,
enable_v: bool,
h: u16,
v: u16,
)
pub const fn set_hv_irq( &mut self, enable_h: bool, enable_v: bool, h: u16, v: u16, )
Program the HV-IRQ comparator (the CPU writes $4200/$4207–$420A; the scheduler
forwards them here so the PPU owns the comparison against its own H/V phase).
Sourcepub const fn latch_hv_counters(&mut self)
pub const fn latch_hv_counters(&mut self)
Latch the current H/V dot counters into OPHCT/OPVCT ($213C/$213D) right now — the same
effect a CPU read of SLHV ($2137) has (regs.rs’s 0x2137 arm calls this too, so there
is one implementation of the latch itself). Real hardware also drives this from the WRIO
($4201) I/O port’s bit7 falling edge (the controller-port-2 IOBIT pin, wired straight to
this latch — a Super Scope’s light sensor toggles it to record the CRT beam position when
it “sees” it, rustysnes_core::controller); exposed as pub so the Bus (which owns WRIO,
not the PPU) can trigger the identical latch from that path without duplicating it.
Sourcepub const fn frame_ready(&self) -> bool
pub const fn frame_ready(&self) -> bool
Whether a finished frame is available. Cleared by Ppu::take_frame.
Sourcepub const fn bg_mode(&self) -> u8
pub const fn bg_mode(&self) -> u8
The current BGMODE ($2105) value (0..=7) — which of the 8 tile/priority layouts is
active. For the debugger overlay’s PPU panel (docs/frontend.md §Debugger overlay).
Sourcepub const fn display_brightness(&self) -> u8
pub const fn display_brightness(&self) -> u8
The current INIDISP ($2100) master brightness (0..=15). For the debugger overlay.
Sourcepub const fn frame_count(&self) -> u64
pub const fn frame_count(&self) -> u64
Completed-frame count since power-on (monotonic, wrapping).
Sourcepub fn framebuffer(&self) -> &[u16]
pub fn framebuffer(&self) -> &[u16]
The composited framebuffer: visible_width() * SCREEN_HEIGHT 15-bit BGR pixels, row-major
at the current frame’s stride (SCREEN_WIDTH normally, MAX_SCREEN_WIDTH for a
hi-res frame — see Ppu::frame_hires). Only the first Ppu::visible_height rows are
written each frame. A non-hires frame’s length here is exactly FRAMEBUFFER_LEN,
unchanged from before hi-res output existed.
Sourcepub fn take_frame(&mut self) -> &[u16]
pub fn take_frame(&mut self) -> &[u16]
Borrow the framebuffer and clear the frame_ready flag (one-shot presentation).
Sourcepub fn vram_word(&self, addr: u16) -> u16
pub fn vram_word(&self, addr: u16) -> u16
Read a VRAM word directly (test/diagnostic; not the register path).
Sourcepub fn vram(&self) -> &[u16]
pub fn vram(&self) -> &[u16]
The full 64 KiB VRAM as a flat word slice (32Ki x u16, native word addressing) — for a
host embedder that needs a raw memory-map pointer (e.g. a libretro core’s
RETRO_MEMORY_VIDEO_RAM).
Sourcepub fn vram_mut(&mut self) -> &mut [u16]
pub fn vram_mut(&mut self) -> &mut [u16]
The mutable counterpart to Self::vram — same host-embedder use case.
Sourcepub const fn cgram_word(&self, index: u8) -> u16
pub const fn cgram_word(&self, index: u8) -> u16
Read a CGRAM entry directly (test/diagnostic).
Sourcepub fn save_state(&self, w: &mut SaveWriter)
pub fn save_state(&self, w: &mut SaveWriter)
Write VRAM/CGRAM/OAM, the full register file, the write latches, the dot/scanline
timeline, the interrupt/frame poll state, and the composited framebuffer into a "PPU0"
section. There is no firmware/ROM byte here to exclude — the PPU carries no chip-ROM
dump (docs/adr/0003); region is written too since it’s set by cart detection, not
re-derivable from anything else stored here.
Sourcepub fn load_state(
&mut self,
r: &mut SaveReader<'_>,
) -> Result<(), SaveStateError>
pub fn load_state( &mut self, r: &mut SaveReader<'_>, ) -> Result<(), SaveStateError>
The inverse of Self::save_state.
§Errors
SaveStateError on truncated/corrupt input, a section with unconsumed trailing bytes,
or SaveStateError::Invalid if the encoded region discriminant doesn’t match one of
Region’s two variants (a semantic enum constraint, not a hardware register width).