srctree

Johan parent d8a79098 0ecd69df fba7f601
Merge pull request #358 from johnor/typed-register

Typed registers
.clang-tidy added: 157, removed: 42, total 115
@@ -48,6 +48,10 @@ CheckOptions:
value: lower_case
- key: readability-identifier-naming.MemberCase
value: lower_case
- key: readability-identifier-naming.ProtectedMemberCase
value: lower_case
- key: readability-identifier-naming.ProtectedMemberSuffix
value: _
- key: readability-identifier-naming.PrivateMemberCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberSuffix
 
core/include/nes/core/ppu_registers.h added: 157, removed: 42, total 115
@@ -1,9 +1,58 @@
#pragma once
 
#include <cstddef>
#include <cstdint>
 
namespace n_e_s::core {
 
template <class TypeT>
class Register {
public:
constexpr Register() = default;
constexpr explicit Register(const TypeT value) : value_{value} {};
 
[[nodiscard]] constexpr bool operator==(const Register &) const = default;
 
[[nodiscard]] constexpr TypeT value() const {
return value_;
}
 
constexpr void set_bit(uint16_t bit) {
value_ |= (1u << bit);
}
 
[[nodiscard]] constexpr bool is_set(uint16_t bit) const {
return (static_cast<TypeT>(value_ >> bit) & 1u) != 0u;
}
 
Register &operator>>=(const std::size_t count) {
value_ >>= count;
return *this;
}
 
Register &operator<<=(const std::size_t count) {
value_ <<= count;
return *this;
}
 
protected:
TypeT value_{0};
};
 
template <class TypeT>
inline Register<TypeT> operator>>(Register<TypeT> reg,
const std::size_t count) {
reg >>= count;
return reg;
}
 
template <class TypeT>
inline Register<TypeT> operator<<(Register<TypeT> reg,
const std::size_t count) {
reg <<= count;
return reg;
}
 
// Vram register in PPU.
// From http://wiki.nesdev.com/w/index.php/PPU_scrolling
// The 15 bit registers t and v are composed this way:
@@ -12,17 +61,9 @@ namespace n_e_s::core {
// ||| || +++++-------- coarse Y scroll
// ||| ++-------------- nametable select
// +++----------------- fine Y scroll
class PpuVram {
class PpuVram : public Register<uint16_t> {
public:
constexpr PpuVram() noexcept = default;
constexpr explicit PpuVram(const uint16_t value) : value_(value) {}
 
[[nodiscard]] constexpr bool operator==(const PpuVram &) const = default;
[[nodiscard]] constexpr bool operator!=(const PpuVram &) const = default;
 
[[nodiscard]] constexpr uint16_t value() const {
return value_;
}
using Register<uint16_t>::Register;
 
constexpr void set_fine_scroll_y(uint8_t fine_scroll_y) {
fine_scroll_y = fine_scroll_y & 7u;
@@ -87,16 +128,40 @@ public:
}
}
}
};
 
private:
uint16_t value_{0u};
// Mask bits
// 0: Greyscale (0: normal color, 1: produce a greyscale display)
// 1: Show background in leftmost 8 pixels of screen if set.
// 2: Show sprites in leftmost 8 pixels of screen if set.
// 3: Show background if set.
// 4: Show sprites if set.
// 5: Emphasize red (green on PAL/Dendy)
// 6: Emphasize green (red on PAL/Dendy)
// 7: Emphasize blue
class PpuMask : public Register<uint8_t> {
public:
using Register<uint8_t>::Register;
 
[[nodiscard]] constexpr bool render_background() const {
return is_set(3u);
}
 
[[nodiscard]] constexpr bool render_background_left() const {
return is_set(1u);
}
 
[[nodiscard]] constexpr bool is_rendering_enabled() const {
return is_set(3u) || is_set(4u);
}
};
 
struct PpuRegisters {
uint16_t scanline;
uint16_t cycle;
uint8_t ctrl;
uint8_t mask;
 
PpuMask mask;
uint8_t status;
uint8_t oamaddr;
uint8_t fine_x_scroll;
@@ -115,10 +180,6 @@ struct PpuRegisters {
uint8_t attribute_table_latch;
uint16_t attribute_table_shifter_low;
uint16_t attribute_table_shifter_hi;
 
[[nodiscard]] constexpr bool is_rendering_enabled() const {
return (mask & (1u << 3u)) || (mask & (1u << 4u));
}
};
 
} // namespace n_e_s::core
 
core/src/ppu.cpp added: 157, removed: 42, total 115
@@ -81,7 +81,7 @@ void Ppu::write_byte(uint16_t addr, uint8_t byte) {
registers_->ctrl = byte;
registers_->temp_vram_addr.set_nametable(byte);
} else if (addr == kPpuMask) {
registers_->mask = byte;
registers_->mask = PpuMask(byte);
} else if (addr == kOamAddr) {
registers_->oamaddr = byte;
} else if (addr == kOamData) {
@@ -189,7 +189,7 @@ bool Ppu::is_vblank_scanline() const {
}
 
bool Ppu::is_rendering_active() const {
return registers_->is_rendering_enabled() &&
return registers_->mask.is_rendering_enabled() &&
(is_pre_render_scanline() || is_visible_scanline());
}
 
@@ -253,7 +253,7 @@ void Ppu::shift_registers() {
}
 
void Ppu::increase_scroll_counters() {
if (!registers_->is_rendering_enabled()) {
if (!registers_->mask.is_rendering_enabled()) {
return;
}
 
 
core/test/src/ippu_helpers.cpp added: 157, removed: 42, total 115
@@ -25,7 +25,7 @@ void PrintTo(const PpuRegisters &r, std::ostream *os) {
r.scanline,
r.ctrl,
r.fine_x_scroll,
r.mask,
r.mask.value(),
r.oamaddr,
r.status,
r.vram_addr.value(),
 
core/test/src/test_ppu.cpp added: 157, removed: 42, total 115
@@ -220,7 +220,7 @@ TEST_F(PpuTest, write_to_ctrl_register) {
}
 
TEST_F(PpuTest, write_to_mask_register) {
expected.mask = 0x33;
expected.mask = PpuMask(0x33);
 
ppu->write_byte(0x2001, 0x33);
 
@@ -236,7 +236,7 @@ TEST_F(PpuTest, write_to_oamaddr_register) {
}
 
TEST_F(PpuTest, ignore_oamdata_write_background_enabled) {
registers.mask = 0b00001000;
registers.mask = PpuMask(0b00001000);
registers.oamaddr = 0x02;
expected.mask = registers.mask;
expected.oamaddr = registers.oamaddr;
@@ -247,7 +247,7 @@ TEST_F(PpuTest, ignore_oamdata_write_background_enabled) {
}
 
TEST_F(PpuTest, ignore_oamdata_write_sprite_enabled) {
registers.mask = 0b00010000;
registers.mask = PpuMask(0b00001000);
registers.oamaddr = 0x02;
expected.mask = registers.mask;
expected.oamaddr = registers.oamaddr;
@@ -258,7 +258,7 @@ TEST_F(PpuTest, ignore_oamdata_write_sprite_enabled) {
}
 
TEST_F(PpuTest, ignore_oamdata_during_pre_render_scanline) {
registers.mask = 0b00011000;
registers.mask = PpuMask(0b00011000);
registers.oamaddr = 0x02;
registers.scanline = 261;
expected.mask = registers.mask;
@@ -443,7 +443,8 @@ TEST_F(PpuTest, increment_vram_addr_by_32_after_reading) {
 
TEST_F(PpuTest, visible_two_sub_cycles) {
registers.scanline = expected.scanline = 0;
registers.mask = expected.mask = 0b000'1000; // Enable background rendering
registers.mask = expected.mask =
PpuMask(0b000'1000); // Enable background rendering
 
expected.cycle = 17;
// Vram should be increased at cycle 8 and 16
@@ -492,7 +493,8 @@ TEST_F(PpuTest, visible_two_sub_cycles) {
 
TEST_F(PpuTest, visible_scanline) {
registers.scanline = 0u; // Start at visible scanline
registers.mask = expected.mask = 0b000'1000; // Enable background rendering
registers.mask = expected.mask =
PpuMask(0b000'1000); // Enable background rendering
 
expected.cycle = 257;
expected.scanline = 0u;
@@ -583,7 +585,8 @@ TEST_F(PpuTest, visible_scanline) {
 
TEST_F(PpuTest, pre_render_two_sub_cycles) {
registers.scanline = expected.scanline = 261; // Start at pre-render
registers.mask = expected.mask = 0b000'1000; // Enable background rendering
registers.mask = expected.mask =
PpuMask(0b000'1000); // Enable background rendering
 
expected.cycle = 17;
// Vram should be increased at cycle 8 and 16
@@ -634,7 +637,8 @@ TEST_F(PpuTest, pre_render_two_sub_cycles) {
 
TEST_F(PpuTest, pre_render_scanline) {
registers.scanline = 261u; // Start at pre-render
registers.mask = expected.mask = 0b000'1000; // Enable background rendering
registers.mask = expected.mask =
PpuMask(0b000'1000); // Enable background rendering
 
expected.cycle = 257;
expected.scanline = 261u;
 
core/test/src/test_ppu_registers.cpp added: 157, removed: 42, total 115
@@ -6,15 +6,61 @@ using namespace n_e_s::core;
 
namespace {
 
TEST(PpuRegisters, is_rendering_enabled_returns_true_for_bit_three_and_four) {
PpuRegisters r{};
EXPECT_FALSE(r.is_rendering_enabled());
TEST(RegisterUint8, set_and_get_bit) {
Register<uint8_t> reg;
reg.set_bit(0);
EXPECT_TRUE(reg.is_set(0));
EXPECT_EQ(0b0000'0001, reg.value());
 
r.mask = 0b0000'1000;
EXPECT_TRUE(r.is_rendering_enabled());
reg.set_bit(5);
EXPECT_TRUE(reg.is_set(5));
EXPECT_EQ(0b0010'0001, reg.value());
}
 
r.mask = 0b0001'0000;
EXPECT_TRUE(r.is_rendering_enabled());
TEST(RegisterUint8, shift_left) {
Register<uint8_t> reg;
reg.set_bit(0);
reg <<= 1u;
EXPECT_EQ(0b0000'0010, reg.value());
 
reg = reg << 1u;
EXPECT_EQ(0b0000'0100, reg.value());
}
 
TEST(RegisterUint8, shift_right) {
Register<uint8_t> reg(0b1000'0000);
reg >>= 1u;
EXPECT_EQ(0b0100'0000, reg.value());
 
reg = reg >> 1u;
EXPECT_EQ(0b0010'0000, reg.value());
}
 
TEST(PpuMask, render_background_returns_true_for_bit_three) {
PpuMask mask{};
EXPECT_FALSE(mask.render_background());
 
mask = PpuMask(0b0000'1000);
EXPECT_TRUE(mask.render_background());
}
 
TEST(PpuMask, render_background_left_returns_true_for_bit_one) {
PpuMask mask{};
EXPECT_FALSE(mask.render_background_left());
 
mask = PpuMask(0b0000'0010);
EXPECT_TRUE(mask.render_background_left());
}
 
TEST(PpuMask, is_rendering_enabled_returns_true_for_bit_three_and_four) {
PpuMask mask{};
EXPECT_FALSE(mask.is_rendering_enabled());
 
mask = PpuMask(0b0000'1000);
EXPECT_TRUE(mask.is_rendering_enabled());
 
mask = PpuMask(0b0001'0000);
EXPECT_TRUE(mask.is_rendering_enabled());
}
 
TEST(PpuVram, construction) {