summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/nes/ppu.cc97
-rw-r--r--lib/nes/ppu.hh167
2 files changed, 138 insertions, 126 deletions
diff --git a/lib/nes/ppu.cc b/lib/nes/ppu.cc
index 0f2e0d6..7ac2155 100644
--- a/lib/nes/ppu.cc
+++ b/lib/nes/ppu.cc
@@ -71,7 +71,7 @@ namespace nes
case 1:
{
// Load the tile pattern for the background from the name table.
- fetch_cycle_.tile = read8(address{ 0x2000 } + internal_.v.get_tile_address());
+ fetch_cycle_.tile = tile{ read8(address{ 0x2000 } + internal_.v.get_tile_address()) };
break;
}
case 3:
@@ -79,27 +79,33 @@ namespace nes
// Load the tile palette for the background from the attribute table.
auto const addr =
address{ 0x23C0 } +
- (internal_.v.get_name_table() * 0x400u) +
+ (static_cast<unsigned>(internal_.v.get_name_table()) * 0x400u) +
((internal_.v.get_coarse_y() & 0b11100u) << 1) +
((internal_.v.get_coarse_x() & 0b11100u) >> 2);
auto const shift =
((internal_.v.get_coarse_y() & 0b00010u) << 1) |
((internal_.v.get_coarse_x() & 0b00010u) << 0);
- fetch_cycle_.palette = static_cast<std::uint8_t>((read8(addr) >> shift) & 0b11);
+ fetch_cycle_.palette = static_cast<palette>((read8(addr) >> shift) & 0b11);
break;
}
case 5:
{
// Load bitplane 0 for the background tile's pattern.
fetch_cycle_.bitplane_0 = get_tile_bitplane(
- control_.get_background_pattern_table(), fetch_cycle_.tile, internal_.v.get_fine_y(), 0);
+ control_.get_background_pattern_table(),
+ fetch_cycle_.tile,
+ internal_.v.get_fine_y(),
+ bitplane::_0);
break;
}
case 7:
{
// Load bitplane 1 for the background tile's pattern.
fetch_cycle_.bitplane_1 = get_tile_bitplane(
- control_.get_background_pattern_table(), fetch_cycle_.tile, internal_.v.get_fine_y(), 1);
+ control_.get_background_pattern_table(),
+ fetch_cycle_.tile,
+ internal_.v.get_fine_y(),
+ bitplane::_1);
break;
}
case 0:
@@ -177,7 +183,7 @@ namespace nes
if (mask_.get_enable_background())
{
background_color = current_background_.colors[internal_.x + x % tile_size];
- background_color.set_foreground(false);
+ background_color.set_role(role::background);
}
auto foreground = evaluated_sprite{};
@@ -190,17 +196,17 @@ namespace nes
auto const offset = static_cast<int>(x) - static_cast<int>(s.x);
if (offset < 0 || static_cast<unsigned>(offset) >= tile_size) { continue; }
auto const color = s.pattern.colors[offset];
- if (color.get_color() == 0) { continue; }
+ if (color.get_color() == palette_color::_0) { continue; }
foreground = s;
foreground_color = color;
- foreground_color.set_foreground(true);
+ foreground_color.set_role(role::foreground);
break;
}
}
- auto has_background = background_color.get_color() != 0;
- auto has_foreground = foreground_color.get_color() != 0;
+ auto has_background = background_color.get_color() != palette_color::_0;
+ auto has_foreground = foreground_color.get_color() != palette_color::_0;
if (x < tile_size && !mask_.get_show_background_start()) { has_background = false; }
if (x < tile_size && !mask_.get_show_sprites_start()) { has_foreground = false; }
@@ -256,25 +262,29 @@ namespace nes
auto ppu::fetch_sprite_pattern(sprite const s, unsigned row) -> tile_row
{
if (s.get_flip_vertical()) { row = get_sprite_height() - 1 - row; }
- unsigned tile, pattern_table;
- if (control_.get_large_sprites())
+ tile tile;
+ pattern_table pattern_table;
+ switch (control_.get_sprite_size())
{
- tile = s.get_top_tile();
- if (row >= tile_size)
- {
- tile += 1;
- row -= tile_size;
- }
- pattern_table = s.get_pattern_table();
- }
- else
- {
- tile = s.tile_index;
- pattern_table = control_.get_sprite_pattern_table();
+ case sprite_size::single_height:
+ tile = s.get_small_tile();
+ pattern_table = control_.get_sprite_pattern_table();
+ break;
+ case sprite_size::double_height:
+ tile = s.get_large_top_tile();
+ if (row >= tile_size)
+ {
+ tile = static_cast<ppu::tile>(1);
+ row -= tile_size;
+ }
+ pattern_table = s.get_large_pattern_table();
+ break;
+ default:
+ return tile_row{};
}
- auto const bitplane_0 = get_tile_bitplane(pattern_table, tile, row, 0);
- auto const bitplane_1 = get_tile_bitplane(pattern_table, tile, row, 1);
+ auto const bitplane_0 = get_tile_bitplane(pattern_table, tile, row, bitplane::_0);
+ auto const bitplane_1 = get_tile_bitplane(pattern_table, tile, row, bitplane::_1);
auto res = get_tile_row(s.get_palette(), bitplane_0, bitplane_1);
if (s.get_flip_horizontal())
@@ -501,7 +511,15 @@ namespace nes
auto ppu::increment_vram() -> void
{
- internal_.v.value += control_.get_vram_row_increment() ? 32 : 1;
+ switch (control_.get_vram_increment())
+ {
+ case vram_increment::forward:
+ internal_.v.value += 1;
+ break;
+ case vram_increment::downward:
+ internal_.v.value += 32;
+ break;
+ }
}
auto ppu::increment_x() -> void
@@ -558,7 +576,15 @@ namespace nes
auto ppu::get_sprite_height() const -> unsigned
{
- return tile_size * (control_.get_large_sprites() ? 2 : 1);
+ switch (control_.get_sprite_size())
+ {
+ case sprite_size::single_height:
+ return tile_size;
+ case sprite_size::double_height:
+ return tile_size * 2;
+ }
+
+ return tile_size;
}
auto ppu::get_sprite(unsigned const i) const -> sprite
@@ -567,13 +593,14 @@ namespace nes
}
auto ppu::get_tile_bitplane(
- unsigned const pattern_table, unsigned const tile, unsigned const row, unsigned const bitplane) -> std::uint8_t
+ pattern_table const pattern_table, tile const tile, unsigned const row, bitplane const bitplane) -> std::uint8_t
{
- auto const addr = static_cast<std::uint16_t>(0x1000 * pattern_table + 0x10 * tile + row);
- return read8(address{ addr } + (bitplane * 8u));
+ auto const addr = static_cast<std::uint16_t>(
+ 0x1000 * static_cast<unsigned>(pattern_table) + 0x10 * static_cast<unsigned>(tile) + row);
+ return read8(address{ addr } + (static_cast<unsigned>(bitplane) * 8u));
}
- auto ppu::get_tile_row(unsigned const palette, std::uint8_t bitplane_0, std::uint8_t bitplane_1) const -> tile_row
+ auto ppu::get_tile_row(palette const palette, std::uint8_t bitplane_0, std::uint8_t bitplane_1) const -> tile_row
{
auto res = tile_row{};
for (auto i = unsigned{ 0 }; i < tile_size; ++i)
@@ -583,7 +610,7 @@ namespace nes
bitplane_0 <<= 1;
bitplane_1 <<= 1;
res.colors[i].set_palette(palette);
- res.colors[i].set_color((bit_1 << 1) | (bit_0 << 0));
+ res.colors[i].set_color(palette_color{ (bit_1 << 1) | (bit_0 << 0) });
}
return res;
}
@@ -592,10 +619,10 @@ namespace nes
{
// See https://www.nesdev.org/wiki/PPU_palettes
index.value = index.value % 0x20;
- if (index.get_color() == 0)
+ if (index.get_color() == palette_color::_0)
{
// The first color is mirrored between background and foreground palettes.
- index.set_foreground(false);
+ index.set_role(role::background);
}
return palette_buffer_[index.value];
}
diff --git a/lib/nes/ppu.hh b/lib/nes/ppu.hh
index 0301ec4..9fc5e39 100644
--- a/lib/nes/ppu.hh
+++ b/lib/nes/ppu.hh
@@ -21,19 +21,38 @@ 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 color : std::uint8_t
- {
- };
+ enum class bitplane : unsigned { _0, _1 };
+ enum class palette : unsigned { _0, _1, _2, _3 };
+ enum class palette_color : unsigned { _0, _1, _2, _3 };
+ enum class name_table : unsigned { _0, _1, _2, _3 };
+ enum class role : unsigned { background, foreground };
+ enum class pattern_table : unsigned { _0, _1 };
+ enum class vram_increment : unsigned { forward, downward };
+ enum class sprite_size : unsigned { single_height, double_height };
+ enum class tile : std::uint8_t {};
+ enum class color : std::uint8_t {};
+
+#define BITFIELD_VALIDATE(mask, offset_from_right) \
+ static_assert((mask & (1 << offset_from_right)) != 0); \
+ static_assert(offset_from_right == 0 || (mask & (1 << (offset_from_right - 1))) == 0);
+#define BITFIELD_VALUE(name, base, mask, offset_from_right) \
+ BITFIELD_VALIDATE(mask, offset_from_right) \
+ auto get_##name() const -> unsigned { return (base & mask) >> offset_from_right; } \
+ auto set_##name(unsigned const v) -> void { base = (base & ~mask) | ((v << offset_from_right) & mask); }
+#define BITFIELD_ENUM(name, type, base, mask, offset_from_right) \
+ BITFIELD_VALIDATE(mask, offset_from_right) \
+ auto get_##name() const -> type { return static_cast<type>((base & mask) >> offset_from_right); } \
+ auto set_##name(type const v) -> void { base = (base & ~mask) | ((static_cast<unsigned>(v) << offset_from_right) & mask); }
+#define BITFIELD_FLAG(name, base, mask, offset_from_right) \
+ BITFIELD_VALIDATE(mask, offset_from_right) \
+ auto get_##name() const -> bool { return base & mask; } \
+ auto set_##name(unsigned const v) -> void { base = (base & ~mask) | (v ? mask : 0); }
struct color_index
{
- 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_foreground(bool const v) -> void { value = (value & ~0b00010000) | (v ? 0b00010000 : 0); }
+ BITFIELD_ENUM(color, palette_color, value, 0b00000011, 0)
+ BITFIELD_ENUM(palette, palette, value, 0b00001100, 2)
+ BITFIELD_ENUM(role, role, value, 0b00010000, 4)
std::uint8_t value{};
@@ -47,23 +66,16 @@ 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); }
+ // Tile index (and pattern table) for sprites
+ BITFIELD_ENUM(large_pattern_table, pattern_table, tile_index, 0b00000001, 0)
+ BITFIELD_ENUM(large_top_tile, tile, tile_index, 0b11111110, 1)
+ BITFIELD_ENUM(small_tile, tile, tile_index, 0b11111111, 0)
// 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); }
+ BITFIELD_ENUM(palette, palette, attributes, 0b00000011, 0)
+ BITFIELD_FLAG(behind_background, attributes, 0b00100000, 5)
+ BITFIELD_FLAG(flip_horizontal, attributes, 0b01000000, 6)
+ BITFIELD_FLAG(flip_vertical, attributes, 0b10000000, 7)
std::uint8_t y{};
std::uint8_t tile_index{};
@@ -114,13 +126,9 @@ namespace nes
std::uint8_t latch_{}; // The last read/written IO register value, returned when reading write-only registers.
struct
{
- auto get_sprite_overflow() const -> bool { return value & 0b00100000; }
- auto get_sprite_zero_hit() const -> bool { return value & 0b01000000; }
- auto get_vblank() const -> bool { return value & 0b10000000; }
-
- auto set_sprite_overflow(bool const v) -> void { value = (value & ~0b00100000) | (v ? 0b00100000 : 0); }
- auto set_sprite_zero_hit(bool const v) -> void { value = (value & ~0b01000000) | (v ? 0b01000000 : 0); }
- auto set_vblank(bool const v) -> void { value = (value & ~0b10000000) | (v ? 0b10000000 : 0); }
+ BITFIELD_FLAG(sprite_overflow, value, 0b00100000, 5)
+ BITFIELD_FLAG(sprite_zero_hit, value, 0b01000000, 6)
+ BITFIELD_FLAG(vblank, value, 0b10000000, 7)
// Set the remaining (unused) bits based on the given value.
auto set_remaining(std::uint8_t const v) -> void { value = (value & ~0b00011111) | (v & 0b00011111); }
@@ -130,57 +138,40 @@ namespace nes
struct
{
// Base nametable address.
- auto get_base_name_table() const -> unsigned { return (value & 0b00000011u) >> 0; }
- // Increment VRAM addresses downward (+ 32) instead of moving to the right (+ 1) on each read/write.
- auto get_vram_row_increment() const -> bool { return value & 0b00000100; }
+ BITFIELD_ENUM(base_name_table, name_table, value, 0b00000011, 0)
+ // How VRAM addresses should be incremented each read/write.
+ BITFIELD_ENUM(vram_increment, vram_increment, value, 0b00000100, 2)
// Sprite pattern table address for 8x8 sprites.
- auto get_sprite_pattern_table() const -> unsigned { return (value & 0b00001000u) >> 3; }
+ BITFIELD_ENUM(sprite_pattern_table, pattern_table, value, 0b00001000, 3)
// Background pattern table address.
- auto get_background_pattern_table() const -> unsigned { return (value & 0b00010000u) >> 4; }
- // Whether to use double-height (8x16) instead of 8x8 sprites.
- auto get_large_sprites() const -> bool { return value & 0b00100000; }
+ BITFIELD_ENUM(background_pattern_table, pattern_table, value, 0b00010000, 4)
+ // The size of the sprites used (i.e. single-height or double-height).
+ BITFIELD_ENUM(sprite_size, sprite_size, value, 0b00100000, 5)
// PPU master/slave select.
- auto get_enable_ext_pin() const -> bool { return value & 0b01000000; }
+ BITFIELD_FLAG(enable_ext_pin, value, 0b01000000, 6)
// Enable/disable vblank NMI.
- auto get_vblank_nmi() const -> bool { return value & 0b10000000; }
-
- auto set_base_name_table(unsigned const v) -> void { value = (value & ~0b00000011) | ((v << 0) & 0b00000011); }
- auto set_vram_row_increment(bool const v) -> void { value = (value & ~0b00000100) | (v ? 0b00000100 : 0); }
- auto set_sprite_pattern_table(unsigned const v) -> void { value = (value & ~0b00001000) | ((v << 3) & 0b00001000); }
- auto set_background_pattern_table(unsigned const v) -> void { value = (value & ~0b00010000) | ((v << 4) & 0b00010000); }
- auto set_large_sprites(bool const v) -> void { value = (value & ~0b00100000) | (v ? 0b00100000 : 0); }
- auto set_enable_ext_pin(bool const v) -> void { value = (value & ~0b01000000) | (v ? 0b01000000 : 0); }
- auto set_vblank_nmi(bool const v) -> void { value = (value & ~0b10000000) | (v ? 0b10000000 : 0); }
+ BITFIELD_FLAG(vblank_nmi, value, 0b10000000, 7)
std::uint8_t value{ 0b00000000 };
} control_{};
struct
{
// Enable/disable grayscale.
- auto get_grayscale() const -> bool { return value & 0b00000001; }
+ BITFIELD_FLAG(grayscale, value, 0b00000001, 0)
// Show background in leftmost 8 pixels of screen.
- auto get_show_background_start() const -> bool { return value & 0b00000010; }
+ BITFIELD_FLAG(show_background_start, value, 0b00000010, 1)
// Show sprites in leftmost 8 pixels of screen.
- auto get_show_sprites_start() const -> bool { return value & 0b00000100; }
+ BITFIELD_FLAG(show_sprites_start, value, 0b00000100, 2)
// Enable background rendering.
- auto get_enable_background() const -> bool { return value & 0b00001000; }
+ BITFIELD_FLAG(enable_background, value, 0b00001000, 3)
// Enable sprite rendering.
- auto get_enable_sprites() const -> bool { return value & 0b00010000; }
+ BITFIELD_FLAG(enable_sprites, value, 0b00010000, 4)
// Emphasize red.
- auto get_emphasize_red() const -> bool { return value & 0b00100000; }
+ BITFIELD_FLAG(emphasize_red, value, 0b00100000, 5)
// Emphasize green.
- auto get_emphasize_green() const -> bool { return value & 0b01000000; }
+ BITFIELD_FLAG(emphasize_green, value, 0b01000000, 6)
// Emphasize blue.
- auto get_emphasize_blue() const -> bool { return value & 0b10000000; }
-
- auto set_grayscale(bool const v) -> void { value = (value & ~0b00000001) | (v ? 0b00000001 : 0); }
- auto set_show_background_start(bool const v) -> void { value = (value & ~0b00000010) | (v ? 0b00000010 : 0); }
- auto set_show_sprites_start(bool const v) -> void { value = (value & ~0b00000100) | (v ? 0b00000100 : 0); }
- auto set_enable_background(bool const v) -> void { value = (value & ~0b00001000) | (v ? 0b00001000 : 0); }
- auto set_enable_sprites(bool const v) -> void { value = (value & ~0b00010000) | (v ? 0b00010000 : 0); }
- auto set_emphasize_red(bool const v) -> void { value = (value & ~0b00100000) | (v ? 0b00100000 : 0); }
- auto set_emphasize_green(bool const v) -> void { value = (value & ~0b01000000) | (v ? 0b01000000 : 0); }
- auto set_emphasize_blue(bool const v) -> void { value = (value & ~0b10000000) | (v ? 0b10000000 : 0); }
+ BITFIELD_FLAG(emphasize_blue, value, 0b10000000, 7)
std::uint8_t value{ 0b00000000 };
} mask_{};
@@ -190,28 +181,17 @@ namespace nes
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_name_table() const -> unsigned { return (value & 0b0000110000000000) >> 10; }
- auto get_horizontal_name_table() const -> unsigned { return (value & 0b0000010000000000) >> 10; }
- auto get_vertical_name_table() const -> unsigned { return (value & 0b0000100000000000) >> 11; }
- auto get_tile_address() const -> unsigned { return (value & 0b0000111111111111) >> 0; }
- 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_name_table(unsigned const v) -> void { value = (value & ~0b0000110000000000) | ((v << 10) & 0b0000110000000000); }
- 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_tile_address(unsigned const v) -> void { value = (value & ~0b0000111111111111) | ((v << 0) & 0b0000111111111111); }
- auto set_fine_y(unsigned const v) -> void { value = (value & ~0b0111000000000000) | ((v << 12) & 0b0111000000000000); }
+ BITFIELD_VALUE(coarse_x, value, 0b0000000000011111, 0)
+ BITFIELD_VALUE(coarse_y, value, 0b0000001111100000, 5)
+ BITFIELD_ENUM(name_table, name_table, value, 0b0000110000000000, 10)
+ BITFIELD_VALUE(horizontal_name_table, value, 0b0000010000000000, 10)
+ BITFIELD_VALUE(vertical_name_table, value, 0b0000100000000000, 11)
+ BITFIELD_VALUE(tile_address, value, 0b0000111111111111, 0)
+ BITFIELD_VALUE(fine_y, value, 0b0111000000000000, 12)
// 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); }
+ BITFIELD_VALUE(address_low, value, 0b0000000011111111, 0)
+ BITFIELD_VALUE(address_high, value, 0b0111111100000000, 8)
std::uint16_t value{};
} v{}, t{}; // Current and temporary VRAM address and scroll position (15 bits)
@@ -227,14 +207,19 @@ namespace nes
tile_row next_background_{};
struct
{
- std::uint8_t tile{ 0 };
- std::uint8_t palette{ 0 };
+ tile tile{ 0 };
+ palette palette{ 0 };
std::uint8_t bitplane_0{ 0 };
std::uint8_t bitplane_1{ 0 };
} fetch_cycle_{}; // Data populated during the fetch cycle.
evaluated_sprite sprites_[8]{}; // Evaluated sprites.
unsigned sprite_count_{ 0 }; // Number of evaluated sprites in sprites_.
+#undef BITFIELD_VALIDATE
+#undef BITFIELD_VALUE
+#undef BITFIELD_ENUM
+#undef BITFIELD_FLAG
+
public:
explicit ppu(cpu&, cartridge&, display&);
@@ -282,9 +267,9 @@ namespace nes
auto copy_x() -> void;
auto copy_y() -> void;
auto get_sprite_height() const -> unsigned;
- auto get_sprite(unsigned) const -> sprite;
- auto get_tile_bitplane(unsigned pattern_table, unsigned tile, unsigned row, unsigned bitplane) -> std::uint8_t;
- auto get_tile_row(unsigned palette, std::uint8_t bitplane_0, std::uint8_t bitplane_1) const -> tile_row;
+ auto get_sprite(unsigned i) const -> sprite;
+ auto get_tile_bitplane(pattern_table, tile, unsigned row, bitplane) -> std::uint8_t;
+ auto get_tile_row(palette, std::uint8_t bitplane_0, std::uint8_t bitplane_1) const -> tile_row;
auto ref_color(color_index) -> color&;
auto resolve_color(color) const -> rgb;
};