diff options
Diffstat (limited to 'lib/nes/ref/ppu.hh')
| -rw-r--r-- | lib/nes/ref/ppu.hh | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/lib/nes/ref/ppu.hh b/lib/nes/ref/ppu.hh new file mode 100644 index 0000000..400ebdb --- /dev/null +++ b/lib/nes/ref/ppu.hh @@ -0,0 +1,212 @@ +#pragma once + +#include "nes/util/cycle-count.hh" + +namespace nes +{ + class address; + class display; + class rgb; + struct snapshot; +} // namespace nes + +namespace nes::ref +{ + class cpu; + class mapper; + + 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::ref
\ No newline at end of file |
