Skip to main content

rustynes_ppu/
lib.rs

1//! Cycle-accurate Ricoh 2C02 PPU implementation.
2//!
3//! See `docs/ppu-2c02.md` for the implementation spec and
4//! `ref-docs/research-report.md` §PPU for the source material.
5//!
6//! Background rendering, sprite evaluation + rendering, sprite-zero hit,
7//! sprite overflow, and the open-bus latch are all implemented at PPU-dot
8//! resolution. PPUSTATUS / PPUDATA / PPUSCROLL / PPUADDR register quirks
9//! match the test-ROM corpus. The 2-PPU-clock PPUMASK pipeline delay
10//! between a mask write and the odd-frame dot-skip check is wired through.
11//!
12//! Region timing (NTSC vs PAL vs Dendy) is parameterized via
13//! [`PpuRegion`]; the structural difference between them is the post-
14//! render-to-pre-render scanline span (NTSC: 241..=260; PAL: 241..=310;
15//! Dendy: 241..=290).
16
17#![no_std]
18// The chip stack carries no `unsafe`; `forbid` makes that a compile-time
19// guarantee rather than an observation (core audit section 2.1). The FFI
20// and platform `unsafe` lives in the frontend, cheevos and mobile crates.
21#![forbid(unsafe_code)]
22#![warn(missing_docs)]
23// Truncating casts are the canonical encoding for the PPU's 8/16-bit register
24// arithmetic; we annotate this once at module level rather than per-line.
25#![allow(
26    clippy::cast_possible_truncation,
27    clippy::cast_lossless,
28    clippy::cast_possible_wrap
29)]
30
31extern crate alloc;
32
33mod bus;
34mod emphasis;
35#[cfg(feature = "ppu-fetch-trace")]
36pub mod fetch_trace;
37mod palette;
38mod palette_gen;
39mod ppu;
40#[cfg(feature = "debug-hooks")]
41pub mod provenance;
42mod raw_signal;
43mod registers;
44mod snapshot;
45#[cfg(feature = "ppu-state-trace")]
46pub mod state_trace;
47
48pub use bus::{BgSplitState, ExAttribute, PpuBus};
49pub use palette::{
50    NES_PALETTE, PpuPalette, build_rgba_lut, build_rgba_lut_from_base, nes_color_to_rgba,
51    palette_color_to_rgba,
52};
53pub use palette_gen::{NtscPaletteParams, generate_base_palette};
54pub use ppu::MASK_WRITE_DELAY;
55#[cfg(feature = "phi2-write-sweep")]
56pub use ppu::OAM2_GATE_LAG;
57#[cfg(feature = "debug-hooks")]
58pub use ppu::ProvBgAddrs;
59#[cfg(feature = "phi2-write-sweep")]
60pub use ppu::RENDER_GATE_LAG;
61#[cfg(feature = "phi2-write-sweep")]
62pub use ppu::SCROLL_GATE_LAG;
63#[cfg(feature = "phi2-write-sweep")]
64pub use ppu::SKIP_GATE_LAG;
65#[cfg(feature = "phi2-write-sweep")]
66pub use ppu::SPRITE_REARM_LAG;
67#[cfg(feature = "phi2-write-sweep")]
68pub use ppu::VBL_SET_DOT;
69pub use ppu::octal_trace;
70pub use ppu::read2007_diag;
71pub use ppu::{
72    FRAMEBUFFER_LEN, FRAMEBUFFER_PIXELS, PaletteInit, Ppu, PpuRegion, PpuRevision, SCREEN_HEIGHT,
73    SCREEN_WIDTH,
74};
75#[cfg(feature = "hd-pack")]
76pub use ppu::{HD_CHR_RAM, HD_TILE_NONE, HdSprite, HdTileSource};
77#[cfg(feature = "debug-hooks")]
78pub use provenance::{
79    CIRAM_LEN as ATTRIB_CIRAM_LEN, OAM_LEN as ATTRIB_OAM_LEN, PALETTE_LEN as ATTRIB_PALETTE_LEN,
80    PATTERN_ADDR_NONE, PixelLayer, PixelProvenance, PixelProvenanceFrame, ProvenanceStash,
81    SPRITE_SLOT_NONE, WriteAttrib, WriteAttribution,
82};
83pub use raw_signal::{
84    ATTENUATION, BLACK, LEVELS, PHASES, RAW_ENTRIES, WHITE, composite_voltage,
85    generate_raw_signal_lut, in_color_phase, normalize, signal_samples,
86};
87pub use snapshot::{PPU_SNAPSHOT_SLIM_FLAG, PPU_SNAPSHOT_VERSION, PpuSnapshotError};
88
89#[cfg(feature = "ppu-state-trace")]
90pub use state_trace::{
91    BINARY_MAGIC, HEADER_SIZE, PPU_TRACE_SCHEMA_VERSION, PpuStateRecord, PpuStateTrace,
92    PpuTraceConfig, RECORD_SIZE, fnv1a64,
93};
94
95/// Returns the crate version string.
96#[must_use]
97pub const fn version() -> &'static str {
98    env!("CARGO_PKG_VERSION")
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn version_is_non_empty() {
107        assert_ne!(version(), "");
108    }
109}