summaryrefslogtreecommitdiff
path: root/lib/nes/ppu.hh
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-01-23 22:02:11 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-01-23 22:02:11 +0100
commitb1d1573702f21e3104606d6ade5f5d39ec86a33a (patch)
treed703be130a5d6f9ff2626313d3380ed34de44dd7 /lib/nes/ppu.hh
parent6512dd599d13035f6a90b2009344e02c488f5aae (diff)
restructure project
Diffstat (limited to 'lib/nes/ppu.hh')
-rw-r--r--lib/nes/ppu.hh208
1 files changed, 208 insertions, 0 deletions
diff --git a/lib/nes/ppu.hh b/lib/nes/ppu.hh
new file mode 100644
index 0000000..d7d9baa
--- /dev/null
+++ b/lib/nes/ppu.hh
@@ -0,0 +1,208 @@
+#pragma once
+
+#include "nes/util/cycle-count.hh"
+
+namespace nes
+{
+ class address;
+ class cpu;
+ class mapper;
+ class display;
+ class rgb;
+ struct snapshot;
+
+ class ppu
+ {
+ static constexpr auto vram_size = std::size_t{ 0x800 };
+ static constexpr auto oam_size = std::size_t{ 0x100 };
+ static constexpr auto palette_buffer_size = std::size_t{ 0x20 };
+ static constexpr auto status_mask = std::uint8_t{ 0b11100000 };
+ // Writes to some registers are ignored until this clock cycle.
+ static constexpr auto boot_up_cycles = cycle_count::from_ppu(29658);
+
+ enum class vram_increment : unsigned
+ {
+ add_1_across = 0,
+ add_32_down = 1,
+ };
+
+ enum class sprite_size : unsigned
+ {
+ _8x8 = 0,
+ _8x16 = 1,
+ };
+
+ enum class role : unsigned
+ {
+ background = 0,
+ sprite = 1,
+ };
+
+ enum class color : std::uint8_t
+ {
+ };
+
+ union color_index
+ {
+ struct __attribute__((packed))
+ {
+ unsigned color : 2;
+ unsigned palette : 2;
+ role role : 1;
+ };
+ std::uint8_t value;
+
+ explicit color_index(std::uint8_t const value)
+ : value{ value }
+ {
+ }
+ };
+
+ struct sprite
+ {
+ std::uint32_t pattern{ 0 };
+ std::uint8_t position{ 0 };
+ bool is_in_front{ false };
+ bool is_sprite_zero{ false };
+ };
+
+ cycle_count current_cycles_;
+ cpu& cpu_;
+ mapper& mapper_;
+ display& display_;
+ std::uint8_t vram_[vram_size]{};
+ std::uint8_t oam_[oam_size]{};
+ color palette_buffer_[palette_buffer_size]
+ {
+ color{ 0x09 }, color{ 0x01 }, color{ 0x00 }, color{ 0x01 },
+ color{ 0x00 }, color{ 0x02 }, color{ 0x02 }, color{ 0x0D },
+ color{ 0x08 }, color{ 0x10 }, color{ 0x08 }, color{ 0x24 },
+ color{ 0x00 }, color{ 0x00 }, color{ 0x04 }, color{ 0x2C },
+ color{ 0x09 }, color{ 0x01 }, color{ 0x34 }, color{ 0x03 },
+ color{ 0x00 }, color{ 0x04 }, color{ 0x00 }, color{ 0x14 },
+ color{ 0x08 }, color{ 0x3A }, color{ 0x00 }, color{ 0x02 },
+ color{ 0x00 }, color{ 0x20 }, color{ 0x2C }, color{ 0x08 },
+ };
+ std::uint8_t latch_{}; // The last read/written IO register value, returned when reading write-only registers.
+ union
+ {
+ struct __attribute__((packed))
+ {
+ bool : 1;
+ bool : 1;
+ bool : 1;
+ bool : 1;
+ bool : 1;
+ bool sprite_overflow : 1;
+ bool sprite_zero_hit : 1;
+ bool vblank : 1;
+ };
+ std::uint8_t value{ 0b00000000 };
+ } status_{};
+ union
+ {
+ struct __attribute__((packed))
+ {
+ std::uint8_t base_name_table : 2; // Base nametable address.
+ vram_increment vram_increment : 1; // VRAM increment per CPU read/write of PPUDATA.
+ std::uint8_t sprite_pattern_table : 1; // Sprite pattern table address for 8x8 sprites.
+ std::uint8_t background_pattern_table : 1; // Background pattern table address.
+ sprite_size sprite_size : 1; // Sprite size.
+ bool enable_ext_pin : 1; // PPU master/slave select.
+ bool vblank_nmi : 1; // Enable/disable Vblank NMI
+ };
+ std::uint8_t value{ 0b00000000 };
+ } control_{};
+ union
+ {
+ struct __attribute__((packed))
+ {
+ bool grayscale : 1; // Enable/disable grayscale.
+ bool show_background_start : 1; // Show background in leftmost 8 pixels of screen.
+ bool show_sprites_start : 1; // Show sprites in leftmost 8 pixels of screen.
+ bool enable_background : 1; // Enable background rendering.
+ bool enable_sprites : 1; // Enable sprite rendering.
+ bool emphasize_red : 1; // Emphasize red.
+ bool emphasize_green : 1; // Emphasize green.
+ bool emphasize_blue : 1; // Emphasize blue.
+ };
+ std::uint8_t value{ 0b00000000 };
+ } mask_{};
+ struct
+ {
+ // See https://www.nesdev.org/wiki/PPU_scrolling#PPU_internal_registers
+ std::uint16_t v{}; // Current VRAM address (15 bits)
+ std::uint16_t t{}; // Temporary VRAM address (15 bits)
+ std::uint8_t x{}; // Fine X scroll (3 bits)
+ bool w{}; // Second write toggle (1 bit)
+ } internal_{};
+ std::uint8_t oamaddr_{}; // Address accessed by IO registers
+ std::uint8_t ppudata_read_buffer_{}; // Delays PPUDATA reads by one.
+ unsigned scanline_{ 240 };
+ unsigned scanline_cycle_{ 340 };
+ bool even_frame_{ true };
+ std::uint64_t tile_data_{ 0 }; // Data for the current tile (higher 32 bits) and the next tile (lower 32 bits).
+ std::uint8_t name_table_byte_{ 0 };
+ std::uint8_t attribute_table_byte_{ 0 };
+ std::uint8_t low_tile_byte_{ 0 };
+ std::uint8_t high_tile_byte_{ 0 };
+ sprite sprites_[8]{}; // Evaluated sprites.
+ unsigned sprite_count_{ 0 }; // Number of evaluated sprites in sprites_.
+
+ public:
+ explicit ppu(cpu&, mapper&, display&);
+
+ ppu(ppu const&) = delete;
+ ppu(ppu&&) = delete;
+ auto operator=(ppu const&) -> ppu& = delete;
+ auto operator=(ppu&&) -> ppu& = delete;
+
+ auto get_cycles() const -> cycle_count { return current_cycles_; }
+ auto build_snapshot(snapshot&) -> void;
+ auto step() -> void;
+
+ // Memory access
+
+ auto read8(address) -> std::uint8_t;
+ auto write8(address, std::uint8_t) -> void;
+
+ // IO registers
+
+ auto read_latch() -> std::uint8_t;
+ auto read_ppustatus() -> std::uint8_t;
+ auto read_oamdata() -> std::uint8_t;
+ auto read_ppudata() -> std::uint8_t;
+
+ auto write_latch(std::uint8_t) -> void;
+ auto write_ppuctrl(std::uint8_t) -> void;
+ auto write_ppuscroll(std::uint8_t) -> void;
+ auto write_ppumask(std::uint8_t) -> void;
+ auto write_ppuaddr(std::uint8_t) -> void;
+ auto write_ppudata(std::uint8_t) -> void;
+ auto write_oamaddr(std::uint8_t) -> void;
+ auto write_oamdata(std::uint8_t) -> void;
+ auto write_oamdma(std::uint8_t) -> void;
+
+ private:
+ auto render_pixel() -> void;
+ auto fetch_name_table_byte() -> void;
+ auto fetch_attribute_table_byte() -> void;
+ auto fetch_low_tile_byte() -> void;
+ auto fetch_high_tile_byte() -> void;
+ auto store_tile_data() -> void;
+ auto increment_x() -> void;
+ auto increment_y() -> void;
+ auto copy_x() -> void;
+ auto copy_y() -> void;
+ auto evaluate_sprites() -> void;
+ auto fetch_sprite_pattern(unsigned i, unsigned row) -> std::uint32_t;
+
+ // Helpers
+
+ auto nmi_change() -> void;
+ auto sprite_height() -> unsigned;
+ auto increment_vram() -> void;
+ auto get_color(color_index) -> color&;
+ auto resolve_color(color) -> rgb;
+ };
+} // namespace nes \ No newline at end of file