From 9442aa38315a936b7043bca697c3f3577df53b65 Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Sat, 25 Jan 2025 23:45:09 +0100 Subject: wip refactoring ppu --- lib/nes/ppu.cc | 159 +++++++++++++++++++++++++-------------------------------- lib/nes/ppu.hh | 88 ++++++++++++++++++++++++------- 2 files changed, 138 insertions(+), 109 deletions(-) (limited to 'lib') diff --git a/lib/nes/ppu.cc b/lib/nes/ppu.cc index 7e3428c..e37a303 100644 --- a/lib/nes/ppu.cc +++ b/lib/nes/ppu.cc @@ -149,26 +149,28 @@ namespace nes auto background_color = color_index{ 0 }; if (mask_.get_enable_background()) { + // TODO auto const tile_data = static_cast(tile_data_ >> 32); background_color = color_index{ static_cast((tile_data >> ((7 - internal_.x) * 4)) & 0xF) }; - background_color.set_role(role::background); + background_color.set_foreground(false); } - auto foreground = sprite{}; + auto foreground = evaluated_sprite{}; auto foreground_color = color_index{ 0 }; if (mask_.get_enable_sprites()) { for (auto i = unsigned{ 0 }; i < sprite_count_; ++i) { auto const s = sprites_[i]; - auto const offset = static_cast(x) - static_cast(s.position); + auto const offset = static_cast(x) - static_cast(s.x); if (offset < 0 || offset > 7) { continue; } + // TODO auto const color = color_index{ static_cast((s.pattern >> ((7 - offset) * 4)) & 0xF) }; if (color.get_color() == 0) { continue; } foreground = s; foreground_color = color; - foreground_color.set_role(role::sprite); + foreground_color.set_foreground(true); break; } } @@ -198,18 +200,20 @@ namespace nes color = foreground.is_in_front ? foreground_color : background_color; } - display_.set(x, y, resolve_color(get_color(color))); + display_.set(x, y, resolve_color(ref_color(color))); } auto ppu::fetch_name_table_byte() -> void { - auto const addr = static_cast(0x2000 | (internal_.v & 0x0FFF)); + // TODO + auto const addr = static_cast(0x2000 | (internal_.v.value & 0x0FFF)); name_table_byte_ = read8(address{ addr }); } auto ppu::fetch_attribute_table_byte() -> void { - auto const v = internal_.v; + // TODO + auto const v = internal_.v.value; auto const addr = static_cast(0x23C0 | (v & 0x0C00) | ((v >> 4) & 0x38) | ((v >> 2) & 0x07)); auto const shift = ((v >> 4) & 4) | (v & 2); attribute_table_byte_ = static_cast(((read8(address{ addr }) >> shift) & 3) << 2); @@ -217,24 +221,19 @@ namespace nes auto ppu::fetch_low_tile_byte() -> void { - auto const fine_y = (internal_.v >> 12) & 7u; - auto const table = control_.get_background_pattern_table(); - auto const tile = name_table_byte_; - auto const addr = static_cast(0x1000 * table + 16 * tile + fine_y); + auto const addr = static_cast(0x1000 * control_.get_background_pattern_table() + 16 * name_table_byte_ + internal_.v.get_fine_y()); low_tile_byte_ = read8(address{ addr }); } auto ppu::fetch_high_tile_byte() -> void { - auto const fine_y = (internal_.v >> 12) & 7u; - auto const table = control_.get_background_pattern_table(); - auto const tile = name_table_byte_; - auto const addr = static_cast(0x1000 * table + 16 * tile + fine_y); + auto const addr = static_cast(0x1000 * control_.get_background_pattern_table() + 16 * name_table_byte_ + internal_.v.get_fine_y()); high_tile_byte_ = read8(address{ addr } + 8); } auto ppu::store_tile_data() -> void { + // TODO auto data = std::uint32_t{ 0 }; for (auto i = unsigned{ 0 }; i < 8; ++i) { @@ -250,70 +249,53 @@ namespace nes auto ppu::increment_x() -> void { - // If coarse X == 31 - if ((internal_.v & 0x1F) == 0x1F) + if (internal_.v.get_coarse_x() == 0x1F) { - // Coarse X = 0 - // TODO: Check - // internal_.v &= ~0x1F; - internal_.v &= 0xFFE0; - // Switch horizontal nametable - internal_.v ^= 0x400; + internal_.v.set_coarse_x(0); + internal_.v.set_horizontal_name_table(1 ^ internal_.v.get_horizontal_name_table()); } else { - // Increment coarse X - internal_.v += 1; + internal_.v.set_coarse_x(internal_.v.get_coarse_x() + 1); } } auto ppu::increment_y() -> void { - // If fine Y == 7 - if ((internal_.v & 0x7000) == 0x7000) + if (internal_.v.get_fine_y() == 7) { - // Fine Y = 0 - internal_.v &= 0x8FFF; - // Let y = coarse Y - auto y = (internal_.v & 0x03E0) >> 5; - if (y == 29) + internal_.v.set_fine_y(0); + if (internal_.v.get_coarse_y() == 29) { - // Coarse Y = 0 - y = 0; - // Switch vertical nametable - internal_.v ^= 0x0800; + internal_.v.set_coarse_y(0); + internal_.v.set_vertical_name_table(1 ^ internal_.v.get_vertical_name_table()); } - else if (y == 31) + else if (internal_.v.get_coarse_y() == 31) { - // Coarse Y = 0, nametable not switched - y = 0; + internal_.v.set_coarse_y(0); + // Nametable not switched } else { - // Increment coarse Y - y += 1; + internal_.v.set_coarse_y(internal_.v.get_coarse_y() + 1); } - // Put coarse Y back into v - // TODO: Check - // internal_.v &= ~0x03E0; - internal_.v &= 0xFC1F; - internal_.v |= y << 5; } else { - // Increment fine Y - internal_.v += 0x1000; + internal_.v.set_fine_y(internal_.v.get_fine_y() + 1); } } auto ppu::copy_x() -> void { - internal_.v = (internal_.v & 0xFBE0) | (internal_.t & 0x041F); + // TODO + internal_.v.value = (internal_.v.value & 0xFBE0) | (internal_.t.value & 0x041F); } auto ppu::copy_y() -> void { - internal_.v = (internal_.v & 0x841F) | (internal_.t & 0x7BE0); + // TODO + internal_.v.value = (internal_.v.value & 0x841F) | (internal_.t.value & 0x7BE0); } auto ppu::evaluate_sprites() -> void @@ -322,19 +304,15 @@ namespace nes sprite_count_ = 0; for (auto i = unsigned{ 0 }; i < 64; ++i) { - auto const y = oam_[i * 4 + 0]; - auto const a = oam_[i * 4 + 2]; - auto const x = oam_[i * 4 + 3]; - auto const row = static_cast(scanline_) - static_cast(y); + auto const s = get_sprite(i); + auto const row = static_cast(scanline_) - static_cast(s.y); if (row < 0 || static_cast(row) >= height) { continue; } if (sprite_count_ < 8) { - sprites_[sprite_count_].pattern = fetch_sprite_pattern(i, static_cast(row)); - sprites_[sprite_count_].position = x; - // TODO: Check - // sprites_[sprite_count_].is_in_front = a & 0b00100000; - sprites_[sprite_count_].is_in_front = ((a >> 5) & 1) == 0; + sprites_[sprite_count_].pattern = fetch_sprite_pattern(s, static_cast(row)); + sprites_[sprite_count_].x = s.x; + sprites_[sprite_count_].is_in_front = !s.get_behind_background(); sprites_[sprite_count_].is_sprite_zero = i == 0; sprite_count_ += 1; } @@ -346,39 +324,34 @@ namespace nes } } - auto ppu::fetch_sprite_pattern(unsigned const i, unsigned row) -> std::uint32_t + auto ppu::fetch_sprite_pattern(sprite const s, unsigned row) -> std::uint32_t { - auto tile = oam_[i * 4 + 1]; - auto const attributes = oam_[i * 4 + 2]; - auto const flip_vertical = (attributes & 0x80) == 0x80; - if (flip_vertical) { row = sprite_height() - 1 - row; } - auto addr = address{}; + if (s.get_flip_vertical()) { row = sprite_height() - 1 - row; } + address addr; if (control_.get_large_sprites()) { - auto const table = tile & 1u; - tile &= 0xFE; - if (row > 7) + auto tile = s.get_top_tile(); + if (row >= 8) { tile += 1; row -= 8; } - addr = address{ static_cast(0x1000u * table + 16u * tile + row) }; + addr = address{ static_cast(0x1000u * s.get_pattern_table() + 16u * tile + row) }; } else { - auto const table = control_.get_sprite_pattern_table(); - addr = address{ static_cast(0x1000u * table + 16u * tile + row) }; + addr = address{ static_cast(0x1000u * control_.get_sprite_pattern_table() + 16u * s.tile_index + row) }; } - auto const a = (attributes & 3u) << 2; + auto const a = s.get_palette() << 2; auto low_tile_byte = read8(addr); auto high_tile_byte = read8(addr + 8); auto data = std::uint32_t{ 0 }; for (auto j = unsigned{ 0 }; j < 8; ++j) { - auto const flip_horizontal = (attributes & 0x40) == 0x40; + // TODO std::uint8_t p1, p2; - if (flip_horizontal) + if (s.get_flip_horizontal()) { p1 = static_cast((low_tile_byte & 1) << 0); p2 = static_cast((high_tile_byte & 1) << 1); @@ -414,7 +387,7 @@ namespace nes if (addr <= address{ 0x3FFF }) { auto const index = color_index{ static_cast(addr.get_absolute() % 0x20) }; - auto const color = get_color(index); + auto const color = ref_color(index); return static_cast(color); } @@ -432,7 +405,7 @@ namespace nes if (addr <= address{ 0x3FFF }) { auto const index = color_index{ static_cast(addr.get_absolute() % 0x20) }; - get_color(index) = color{ value }; + ref_color(index) = color{ value }; return; } } @@ -477,7 +450,7 @@ namespace nes auto ppu::read_ppudata() -> std::uint8_t { - auto const addr = address{ internal_.v }; + auto const addr = address{ internal_.v.value }; auto res = read8(addr); write_latch(ppudata_read_buffer_); @@ -508,7 +481,8 @@ namespace nes { NES_DEBUG_LOG(ppu, "PPUCTRL <- {:#2x}", value); control_.value = value; - internal_.t = static_cast((internal_.t & 0xF3FF) | ((value & 0x03) << 10)); + // TODO: https://www.nesdev.org/wiki/PPU_registers#PPUCTRL + internal_.t.value = static_cast((internal_.t.value & 0xF3FF) | ((value & 0x03) << 10)); if (control_.get_vblank_nmi() && status_.get_vblank()) { cpu_.trigger_nmi(); } } } @@ -522,14 +496,14 @@ namespace nes if (!internal_.w) { // First write -> x value. - internal_.t = static_cast((internal_.t & 0b1111111111100000) | (value >> 3)); - internal_.x = value & 0b00000111; + internal_.t.set_coarse_x((value & 0b11111000) >> 3); + internal_.x = (value & 0b00000111) >> 0; } else { // Second write -> y value. - internal_.t = static_cast((internal_.t & 0b1000111111111111) | ((value & 0b00000111) << 12)); - internal_.t = static_cast((internal_.t & 0b1111110000011111) | ((value & 0b11111000) << 2)); + internal_.t.set_coarse_y((value & 0b11111000) >> 3); + internal_.t.set_fine_y((value & 0b00000111) >> 0); } internal_.w = !internal_.w; @@ -555,12 +529,12 @@ namespace nes if (!internal_.w) { // First write. - internal_.t = static_cast((internal_.t & 0b1000000011111111) | ((value & 0b00111111) << 8)); + internal_.t.set_address_high(value & 0b00111111); } else { // Second write. - internal_.t = static_cast((internal_.t & 0b1111111100000000) | ((value & 0b11111111) << 0)); + internal_.t.set_address_low(value); internal_.v = internal_.t; } @@ -572,7 +546,7 @@ namespace nes { NES_DEBUG_LOG(ppu, "PPUDATA <- {:#2x} (address: {:#4x})", value, internal_.v); write_latch(value); - write8(address{ internal_.v }, value); + write8(address{ internal_.v.value }, value); increment_vram(); } @@ -610,29 +584,34 @@ namespace nes // Helpers // ----------------------------------------------------------------------------------------------------------------- - auto ppu::sprite_height() -> unsigned + auto ppu::sprite_height() const -> unsigned { return control_.get_large_sprites() ? 16 : 8; } auto ppu::increment_vram() -> void { - internal_.v += control_.get_vram_row_increment() ? 32 : 1; + internal_.v.value += control_.get_vram_row_increment() ? 32 : 1; } - auto ppu::get_color(color_index index) -> color& + auto ppu::get_sprite(unsigned const i) const -> sprite + { + return sprite{ oam_[i * 4 + 0], oam_[i * 4 + 1], oam_[i * 4 + 2], oam_[i * 4 + 3] }; + } + + auto ppu::ref_color(color_index index) -> color& { // See https://www.nesdev.org/wiki/PPU_palettes index.value = index.value % 0x20; if (index.get_color() == 0) { // The first color is mirrored between background and foreground palettes. - index.set_role(role::background); + index.set_foreground(false); } return palette_buffer_[index.value]; } - auto ppu::resolve_color(color const color) -> rgb + auto ppu::resolve_color(color const color) const -> rgb { auto const index = static_cast(color); switch (index & 0x3F) diff --git a/lib/nes/ppu.hh b/lib/nes/ppu.hh index d58bfd4..4f15d37 100644 --- a/lib/nes/ppu.hh +++ b/lib/nes/ppu.hh @@ -20,26 +20,19 @@ namespace nes // Writes to some registers are ignored until this clock cycle. static constexpr auto boot_up_cycles = cycle_count::from_ppu(29658); - enum class role : unsigned - { - background = 0, - sprite = 1, - }; - enum class color : std::uint8_t { }; struct color_index { - auto get_color() const -> unsigned { return (value & 0b00000011u) >> 0; } - auto get_palette() const -> unsigned { return (value & 0b00001100u) >> 2; } - auto get_role() const -> role { return role{ (value & 0b00010000u) >> 4 }; } + auto get_color() const -> unsigned { return (value & 0b00000011) >> 0; } + auto get_palette() const -> unsigned { return (value & 0b00001100) >> 2; } + auto get_foreground() const -> bool { return value & 0b00010000; } auto set_color(unsigned const v) -> void { value = (value & ~0b00000011) | ((v << 0) & 0b00000011); } auto set_palette(unsigned const v) -> void { value = (value & ~0b00001100) | ((v << 2) & 0b00001100); } - auto set_role(unsigned const v) -> void { value = (value & ~0b00010000) | ((v << 4) & 0b00010000); } - auto set_role(role const v) -> void { set_role(static_cast(v)); } + auto set_foreground(bool const v) -> void { value = (value & ~0b00010000) | (v ? 0b00010000 : 0); } std::uint8_t value{}; @@ -50,9 +43,43 @@ namespace nes }; struct sprite + { + // Tile index and pattern table for large sprites + auto get_pattern_table() const -> unsigned { return (tile_index & 0b00000001) >> 0; } + auto get_top_tile() const -> unsigned { return (tile_index & 0b11111110) >> 1; } + + auto set_pattern_table(unsigned const v) -> void { tile_index = (tile_index & ~0b00000001) | ((v << 0) & 0b00000001); } + auto set_top_tile(unsigned const v) -> void { tile_index = (tile_index & ~0b11111110) | ((v << 1) & 0b11111110); } + + // Attributes + auto get_palette() const -> unsigned { return (attributes & 0b00000011) >> 0; } + auto get_behind_background() const -> bool { return attributes & 0b00100000; } + auto get_flip_horizontal() const -> bool { return attributes & 0b01000000; } + auto get_flip_vertical() const -> bool { return attributes & 0b10000000; } + + auto set_palette(unsigned const v) -> void { attributes = (attributes & ~0b00000011) | ((v << 0) & 0b00000011); } + auto set_behind_background(bool const v) -> void { attributes = (attributes & ~0b00100000) | (v ? 0b00100000 : 0); } + auto set_flip_horizontal(bool const v) -> void { attributes = (attributes & ~0b01000000) | (v ? 0b01000000 : 0); } + auto set_flip_vertical(bool const v) -> void { attributes = (attributes & ~0b10000000) | (v ? 0b10000000 : 0); } + + std::uint8_t y{}; + std::uint8_t tile_index{}; + std::uint8_t attributes{}; + std::uint8_t x{}; + + explicit sprite(std::uint8_t const y, std::uint8_t const tile_index, std::uint8_t const attributes, std::uint8_t const x) + : y{ y } + , tile_index{ tile_index } + , attributes{ attributes } + , x{ x } + { + } + }; + + struct evaluated_sprite { std::uint32_t pattern{ 0 }; - std::uint8_t position{ 0 }; + std::uint8_t x{ 0 }; bool is_in_front{ false }; bool is_sprite_zero{ false }; }; @@ -147,8 +174,30 @@ namespace nes 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) + struct + { + // Scroll position + auto get_coarse_x() const -> unsigned { return (value & 0b0000000000011111) >> 0; } + auto get_coarse_y() const -> unsigned { return (value & 0b0000001111100000) >> 5; } + auto get_horizontal_name_table() const -> unsigned { return (value & 0b0000010000000000) >> 10; } + auto get_vertical_name_table() const -> unsigned { return (value & 0b0000100000000000) >> 11; } + auto get_fine_y() const -> unsigned { return (value & 0b0111000000000000) >> 12; } + + auto set_coarse_x(unsigned const v) -> void { value = (value & ~0b0000000000011111) | ((v << 0) & 0b0000000000011111); } + auto set_coarse_y(unsigned const v) -> void { value = (value & ~0b0000001111100000) | ((v << 5) & 0b0000001111100000); } + auto set_horizontal_name_table(unsigned const v) -> void { value = (value & ~0b0000010000000000) | ((v << 10) & 0b0000010000000000); } + auto set_vertical_name_table(unsigned const v) -> void { value = (value & ~0b0000100000000000) | ((v << 11) & 0b0000100000000000); } + auto set_fine_y(unsigned const v) -> void { value = (value & ~0b0111000000000000) | ((v << 12) & 0b0111000000000000); } + + // Address + auto get_address_low() const -> unsigned { return (value & 0b0000000011111111) >> 0; } + auto get_address_high() const -> unsigned { return (value & 0b0111111100000000) >> 8; } + + auto set_address_low(unsigned const v) -> void { value = (value & ~0b0000000011111111) | ((v << 0) & 0b0000000011111111); } + auto set_address_high(unsigned const v) -> void { value = (value & ~0b0111111100000000) | ((v << 8) & 0b0111111100000000); } + + std::uint16_t value{}; + } v{}, t{}; // Current and temporary VRAM address and scroll position (15 bits) std::uint8_t x{}; // Fine X scroll (3 bits) bool w{}; // Second write toggle (1 bit) } internal_{}; @@ -162,7 +211,7 @@ namespace nes 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. + evaluated_sprite sprites_[8]{}; // Evaluated sprites. unsigned sprite_count_{ 0 }; // Number of evaluated sprites in sprites_. public: @@ -211,13 +260,14 @@ namespace nes auto copy_x() -> void; auto copy_y() -> void; auto evaluate_sprites() -> void; - auto fetch_sprite_pattern(unsigned i, unsigned row) -> std::uint32_t; + auto fetch_sprite_pattern(sprite, unsigned row) -> std::uint32_t; // Helpers - auto sprite_height() -> unsigned; + auto sprite_height() const -> unsigned; auto increment_vram() -> void; - auto get_color(color_index) -> color&; - auto resolve_color(color) -> rgb; + auto get_sprite(unsigned) const -> sprite; + auto ref_color(color_index) -> color&; + auto resolve_color(color) const -> rgb; }; } // namespace nes \ No newline at end of file -- cgit v1.3.1