summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--lib/nes/cpu.cc88
-rw-r--r--lib/nes/cpu.hh33
-rw-r--r--lib/nes/ppu.cc99
-rw-r--r--lib/nes/ppu.hh126
5 files changed, 174 insertions, 177 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 095dbc2..e0d9a86 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,10 +25,7 @@ target_compile_options(nes_options
-Wsign-conversion
-Wdouble-promotion
-Wformat=2
- -Wswitch-enum
- # extensions used
- -Wno-nested-anon-types
- -Wno-gnu-anonymous-struct)
+ -Wswitch-enum)
if(APPLE)
# Use automatic reference counting for Objective-C code.
target_compile_options(nes_options INTERFACE "-fobjc-arc")
diff --git a/lib/nes/cpu.cc b/lib/nes/cpu.cc
index 51507b0..ee7416d 100644
--- a/lib/nes/cpu.cc
+++ b/lib/nes/cpu.cc
@@ -24,14 +24,14 @@ namespace nes
snapshot.registers.a = registers_.a;
snapshot.registers.x = registers_.x;
snapshot.registers.y = registers_.y;
- snapshot.registers.p = registers_.p;
- snapshot.registers.c = registers_.c;
- snapshot.registers.z = registers_.z;
- snapshot.registers.i = registers_.i;
- snapshot.registers.d = registers_.d;
- snapshot.registers.b = registers_.b;
- snapshot.registers.v = registers_.v;
- snapshot.registers.n = registers_.n;
+ snapshot.registers.p = registers_.p.value;
+ snapshot.registers.c = registers_.p.get_c();
+ snapshot.registers.z = registers_.p.get_z();
+ snapshot.registers.i = registers_.p.get_i();
+ snapshot.registers.d = registers_.p.get_d();
+ snapshot.registers.b = registers_.p.get_b();
+ snapshot.registers.v = registers_.p.get_v();
+ snapshot.registers.n = registers_.p.get_n();
}
auto cpu::stall_cycles(cycle_count const count) -> void
@@ -393,7 +393,7 @@ namespace nes
// ANC: AND #i + copy N to C
auto operand = fetch_operand<Mode>();
eval_and(operand.read());
- registers_.c = registers_.n;
+ registers_.p.set_c(registers_.p.get_n());
current_cycles_ += operand.get_cycles();
}
@@ -401,13 +401,13 @@ namespace nes
auto cpu::run_bpl() -> void
{
// BPL: Break If Positive
- branch<Mode>(!registers_.n);
+ branch<Mode>(!registers_.p.get_n());
}
auto cpu::run_clc() -> void
{
// CLC: Clear Carry Flag
- registers_.c = false;
+ registers_.p.set_c(false);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -451,9 +451,9 @@ namespace nes
auto operand = fetch_operand<Mode>();
auto const arg = operand.read();
auto const res = (registers_.a & arg) != 0;
- registers_.z = res == 0;
- registers_.v = (arg & 0x40) != 0;
- registers_.n = (arg & 0x80) != 0;
+ registers_.p.set_z(res == 0);
+ registers_.p.set_v((arg & 0x40) != 0);
+ registers_.p.set_n((arg & 0x80) != 0);
current_cycles_ += operand.get_cycles();
}
@@ -477,13 +477,13 @@ namespace nes
template<cpu::addressing_mode Mode>
auto cpu::run_bmi() -> void
{
- branch<Mode>(registers_.n);
+ branch<Mode>(registers_.p.get_n());
}
auto cpu::run_sec() -> void
{
// SEC: Set Carry Flag
- registers_.c = true;
+ registers_.p.set_c(true);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -523,7 +523,7 @@ namespace nes
auto const old_val = operand.read();
auto const new_val = static_cast<std::uint8_t>(old_val >> 1);
operand.write(new_val);
- registers_.c = (old_val & 0x1) != 0;
+ registers_.p.set_c((old_val & 0x1) != 0);
update_zn(new_val);
current_cycles_ += shift_cycle_count<Mode>();
}
@@ -558,13 +558,13 @@ namespace nes
auto cpu::run_bvc() -> void
{
// BVC: Branch If Overflow Clear
- branch<Mode>(!registers_.v);
+ branch<Mode>(!registers_.p.get_v());
}
auto cpu::run_cli() -> void
{
// CLI: Clear Interrupt Disable
- registers_.i = false;
+ registers_.p.set_i(false);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -626,13 +626,13 @@ namespace nes
auto cpu::run_bvs() -> void
{
// BVS: Branch If Overflow Set
- branch<Mode>(registers_.v);
+ branch<Mode>(registers_.p.get_v());
}
auto cpu::run_sei() -> void
{
// SEI: Set Interrupt Disable
- registers_.i = true;
+ registers_.p.set_i(true);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -708,7 +708,7 @@ namespace nes
auto cpu::run_bcc() -> void
{
// BCC: Branch If Carry Clear
- branch<Mode>(!registers_.c);
+ branch<Mode>(!registers_.p.get_c());
}
auto cpu::run_tya() -> void
@@ -810,13 +810,13 @@ namespace nes
auto cpu::run_bcs() -> void
{
// BCS: Branch If Carry Set
- branch<Mode>(registers_.c);
+ branch<Mode>(registers_.p.get_c());
}
auto cpu::run_clv() -> void
{
// CLV: Clear Overflow Flag
- registers_.v = false;
+ registers_.p.set_v(false);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -905,13 +905,13 @@ namespace nes
auto cpu::run_bne() -> void
{
// BNE: Branch If Not Equal
- branch<Mode>(!registers_.z);
+ branch<Mode>(!registers_.p.get_z());
}
auto cpu::run_cld() -> void
{
// CLD: Clear Decimal Mode
- registers_.d = false;
+ registers_.p.set_d(false);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -968,13 +968,13 @@ namespace nes
auto cpu::run_beq() -> void
{
// BEQ: Branch If Equal
- branch<Mode>(registers_.z);
+ branch<Mode>(registers_.p.get_z());
}
auto cpu::run_sed() -> void
{
// SED: Set Decimal Flag
- registers_.d = true;
+ registers_.p.set_d(true);
current_cycles_ += cycle_count::from_cpu(2);
}
@@ -1025,8 +1025,8 @@ namespace nes
auto cpu::update_zn(std::uint8_t const value) -> void
{
- registers_.z = value == 0;
- registers_.n = (value & 0x80) != 0;
+ registers_.p.set_z(value == 0);
+ registers_.p.set_n((value & 0x80) != 0);
}
template<cpu::addressing_mode Mode>
@@ -1051,22 +1051,22 @@ namespace nes
push_stack16(registers_.pc);
eval_php();
registers_.pc = read16(vector);
- registers_.i = true;
+ registers_.p.set_i(true);
current_cycles_ += cycle_count::from_cpu(7);
}
auto cpu::eval_ror(std::uint8_t const arg) -> std::uint8_t
{
- auto const res = static_cast<std::uint8_t>((registers_.c << 7) | (arg >> 1));
- registers_.c = (arg & 0x1) != 0;
+ auto const res = static_cast<std::uint8_t>(((registers_.p.get_c() ? 1 :0) << 7) | (arg >> 1));
+ registers_.p.set_c((arg & 0x1) != 0);
update_zn(res);
return res;
}
auto cpu::eval_rol(std::uint8_t const arg) -> std::uint8_t
{
- auto const res = static_cast<std::uint8_t>((arg << 1) | (registers_.c ? 1 : 0));
- registers_.c = (arg & 0x80) != 0;
+ auto const res = static_cast<std::uint8_t>((arg << 1) | (registers_.p.get_c() ? 1 : 0));
+ registers_.p.set_c((arg & 0x80) != 0);
update_zn(res);
return res;
}
@@ -1074,7 +1074,7 @@ namespace nes
auto cpu::eval_asl(std::uint8_t const arg) -> std::uint8_t
{
auto const new_val = static_cast<std::uint8_t>(arg << 1);
- registers_.c = (arg & 0x80) != 0;
+ registers_.p.set_c((arg & 0x80) != 0);
update_zn(new_val);
return new_val;
}
@@ -1082,7 +1082,7 @@ namespace nes
auto cpu::eval_lsr(std::uint8_t const arg) -> std::uint8_t
{
auto const new_val = static_cast<std::uint8_t>(arg >> 1);
- registers_.c = (arg & 0x01) != 0;
+ registers_.p.set_c((arg & 0x01) != 0);
update_zn(new_val);
return new_val;
}
@@ -1090,12 +1090,12 @@ namespace nes
auto cpu::eval_adc(std::uint8_t const arg) -> void
{
auto const old_val = registers_.a;
- auto const tmp = old_val + arg + (registers_.c ? 1 : 0);
+ auto const tmp = old_val + arg + (registers_.p.get_c() ? 1 : 0);
auto const new_val = static_cast<std::uint8_t>(tmp);
registers_.a = new_val;
- registers_.c = tmp > 0xFF;
- registers_.v = ((old_val ^ arg) & 0x80) == 0 && ((old_val ^ new_val) & 0x80) != 0;
+ registers_.p.set_c(tmp > 0xFF);
+ registers_.p.set_v(((old_val ^ arg) & 0x80) == 0 && ((old_val ^ new_val) & 0x80) != 0);
update_zn(registers_.a);
}
@@ -1120,21 +1120,21 @@ namespace nes
auto cpu::eval_cmp(std::uint8_t const a, std::uint8_t const b) -> void
{
update_zn(a - b);
- registers_.c = a >= b;
+ registers_.p.set_c(a >= b);
}
auto cpu::eval_plp() -> void
{
// Ignore bits 4 and 5
- registers_.p =
+ registers_.p.value =
(pop_stack8() & 0b11001111) |
- (registers_.p & 0b00110000);
+ (registers_.p.value & 0b00110000);
}
auto cpu::eval_php() -> void
{
// Always set bits 4 and 5.
- push_stack8(registers_.p | 0b00110000);
+ push_stack8(registers_.p.value | 0b00110000);
}
// -----------------------------------------------------------------------------------------------------------------
diff --git a/lib/nes/cpu.hh b/lib/nes/cpu.hh
index 56ec4b0..431cdf2 100644
--- a/lib/nes/cpu.hh
+++ b/lib/nes/cpu.hh
@@ -31,21 +31,26 @@ namespace nes
std::uint8_t a{ 0 };
std::uint8_t x{ 0 };
std::uint8_t y{ 0 };
- union
+ struct
{
- struct
- {
- bool c : 1; // carry
- bool z : 1; // zero
- bool i : 1; // interrupt inhibit
- bool d : 1; // decimal
- bool b : 1; // break
- bool : 1; // (unused, always true)
- bool v : 1; // overflow
- bool n : 1; // negative
- };
- std::uint8_t p{ 0b00100100 }; // initially, set i = true
- };
+ auto get_c() const -> bool { return value & 0b00000001; } // carry
+ auto get_z() const -> bool { return value & 0b00000010; } // zero
+ auto get_i() const -> bool { return value & 0b00000100; } // interrupt inhibit
+ auto get_d() const -> bool { return value & 0b00001000; } // decimal
+ auto get_b() const -> bool { return value & 0b00010000; } // break
+ auto get_v() const -> bool { return value & 0b01000000; } // overflow
+ auto get_n() const -> bool { return value & 0b10000000; } // negative
+
+ auto set_c(bool const v) -> void { value = (value & ~0b00000001) | (v ? 0b00000001 : 0); }
+ auto set_z(bool const v) -> void { value = (value & ~0b00000010) | (v ? 0b00000010 : 0); }
+ auto set_i(bool const v) -> void { value = (value & ~0b00000100) | (v ? 0b00000100 : 0); }
+ auto set_d(bool const v) -> void { value = (value & ~0b00001000) | (v ? 0b00001000 : 0); }
+ auto set_b(bool const v) -> void { value = (value & ~0b00010000) | (v ? 0b00010000 : 0); }
+ auto set_v(bool const v) -> void { value = (value & ~0b01000000) | (v ? 0b01000000 : 0); }
+ auto set_n(bool const v) -> void { value = (value & ~0b10000000) | (v ? 0b10000000 : 0); }
+
+ std::uint8_t value{ 0b00100100 };
+ } p{};
} registers_{};
enum class addressing_mode
diff --git a/lib/nes/ppu.cc b/lib/nes/ppu.cc
index 0f56c96..7e3428c 100644
--- a/lib/nes/ppu.cc
+++ b/lib/nes/ppu.cc
@@ -27,7 +27,7 @@ namespace nes
// See https://www.nesdev.org/wiki/PPU_rendering
// Based on: https://github.com/fogleman/nes/blob/master/nes/ppu.go
- auto const enable_rendering = mask_.enable_background || mask_.enable_sprites;
+ auto const enable_rendering = mask_.get_enable_background() || mask_.get_enable_sprites();
current_cycles_ += cycle_count::from_ppu(1);
scanline_cycle_ += 1;
@@ -130,14 +130,14 @@ namespace nes
if (scanline_ == 241 && scanline_cycle_ == 1)
{
display_.switch_buffers();
- status_.vblank = true;
- if (control_.vblank_nmi) { cpu_.trigger_nmi(); }
+ status_.set_vblank(true);
+ if (control_.get_vblank_nmi()) { cpu_.trigger_nmi(); }
}
if (pre_line && scanline_cycle_ == 1)
{
- status_.vblank = false;
- status_.sprite_zero_hit = false;
- status_.sprite_overflow = false;
+ status_.set_vblank(false);
+ status_.set_sprite_zero_hit(false);
+ status_.set_sprite_overflow(false);
}
}
@@ -147,16 +147,16 @@ namespace nes
auto const y = scanline_;
auto background_color = color_index{ 0 };
- if (mask_.enable_background)
+ if (mask_.get_enable_background())
{
auto const tile_data = static_cast<std::uint32_t>(tile_data_ >> 32);
background_color = color_index{ static_cast<std::uint8_t>((tile_data >> ((7 - internal_.x) * 4)) & 0xF) };
- background_color.role = role::background;
+ background_color.set_role(role::background);
}
auto foreground = sprite{};
auto foreground_color = color_index{ 0 };
- if (mask_.enable_sprites)
+ if (mask_.get_enable_sprites())
{
for (auto i = unsigned{ 0 }; i < sprite_count_; ++i)
{
@@ -164,20 +164,20 @@ namespace nes
auto const offset = static_cast<int>(x) - static_cast<int>(s.position);
if (offset < 0 || offset > 7) { continue; }
auto const color = color_index{ static_cast<std::uint8_t>((s.pattern >> ((7 - offset) * 4)) & 0xF) };
- if (color.color == 0) { continue; }
+ if (color.get_color() == 0) { continue; }
foreground = s;
foreground_color = color;
- foreground_color.role = role::sprite;
+ foreground_color.set_role(role::sprite);
break;
}
}
- auto has_background = background_color.color != 0;
- auto has_foreground = foreground_color.color != 0;
+ auto has_background = background_color.get_color() != 0;
+ auto has_foreground = foreground_color.get_color() != 0;
- if (x < 8 && !mask_.show_background_start) { has_background = false; }
- if (x < 8 && !mask_.show_sprites_start) { has_foreground = false; }
+ if (x < 8 && !mask_.get_show_background_start()) { has_background = false; }
+ if (x < 8 && !mask_.get_show_sprites_start()) { has_foreground = false; }
auto color = color_index{ 0 };
if (!has_background && has_foreground)
@@ -192,7 +192,7 @@ namespace nes
{
if (foreground.is_sprite_zero && x < 255)
{
- status_.sprite_zero_hit = true;
+ status_.set_sprite_zero_hit(true);
}
color = foreground.is_in_front ? foreground_color : background_color;
@@ -217,8 +217,8 @@ namespace nes
auto ppu::fetch_low_tile_byte() -> void
{
- auto const fine_y = (internal_.v >> 12) & 7;
- auto const table = control_.background_pattern_table;
+ 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<std::uint16_t>(0x1000 * table + 16 * tile + fine_y);
low_tile_byte_ = read8(address{ addr });
@@ -226,8 +226,8 @@ namespace nes
auto ppu::fetch_high_tile_byte() -> void
{
- auto const fine_y = (internal_.v >> 12) & 7;
- auto const table = control_.background_pattern_table;
+ 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<std::uint16_t>(0x1000 * table + 16 * tile + fine_y);
high_tile_byte_ = read8(address{ addr } + 8);
@@ -340,7 +340,7 @@ namespace nes
}
else
{
- status_.sprite_overflow = true;
+ status_.set_sprite_overflow(true);
break;
}
}
@@ -353,26 +353,21 @@ namespace nes
auto const flip_vertical = (attributes & 0x80) == 0x80;
if (flip_vertical) { row = sprite_height() - 1 - row; }
auto addr = address{};
- switch (control_.sprite_size)
+ if (control_.get_large_sprites())
{
- case sprite_size::_8x8:
+ auto const table = tile & 1u;
+ tile &= 0xFE;
+ if (row > 7)
{
- auto const table = control_.sprite_pattern_table;
- addr = address{ static_cast<std::uint16_t>(0x1000u * table + 16u * tile + row) };
- break;
- }
- case sprite_size::_8x16:
- {
- auto const table = tile & 1u;
- tile &= 0xFE;
- if (row > 7)
- {
- tile += 1;
- row -= 8;
- }
- addr = address{ static_cast<std::uint16_t>(0x1000u * table + 16u * tile + row) };
- break;
+ tile += 1;
+ row -= 8;
}
+ addr = address{ static_cast<std::uint16_t>(0x1000u * table + 16u * tile + row) };
+ }
+ else
+ {
+ auto const table = control_.get_sprite_pattern_table();
+ addr = address{ static_cast<std::uint16_t>(0x1000u * table + 16u * tile + row) };
}
auto const a = (attributes & 3u) << 2;
@@ -461,7 +456,7 @@ namespace nes
auto const res = static_cast<std::uint8_t>(base | (status_.value & status_mask));
NES_DEBUG_LOG(ppu, "PPUSTATUS -> {:#2x}", res);
- status_.vblank = false;
+ status_.set_vblank(false);
internal_.w = false;
write_latch(res);
@@ -514,7 +509,7 @@ namespace nes
NES_DEBUG_LOG(ppu, "PPUCTRL <- {:#2x}", value);
control_.value = value;
internal_.t = static_cast<std::uint16_t>((internal_.t & 0xF3FF) | ((value & 0x03) << 10));
- if (control_.vblank_nmi && status_.vblank) { cpu_.trigger_nmi(); }
+ if (control_.get_vblank_nmi() && status_.get_vblank()) { cpu_.trigger_nmi(); }
}
}
@@ -617,38 +612,22 @@ namespace nes
auto ppu::sprite_height() -> unsigned
{
- switch (control_.sprite_size)
- {
- case sprite_size::_8x8:
- return 8;
- case sprite_size::_8x16:
- return 16;
- }
-
- return 8;
+ return control_.get_large_sprites() ? 16 : 8;
}
auto ppu::increment_vram() -> void
{
- switch (control_.vram_increment)
- {
- case vram_increment::add_1_across:
- internal_.v += 1;
- break;
- case vram_increment::add_32_down:
- internal_.v += 32;
- break;
- }
+ internal_.v += control_.get_vram_row_increment() ? 32 : 1;
}
auto ppu::get_color(color_index index) -> color&
{
// See https://www.nesdev.org/wiki/PPU_palettes
index.value = index.value % 0x20;
- if (index.color == 0)
+ if (index.get_color() == 0)
{
// The first color is mirrored between background and foreground palettes.
- index.role = role::background;
+ index.set_role(role::background);
}
return palette_buffer_[index.value];
}
diff --git a/lib/nes/ppu.hh b/lib/nes/ppu.hh
index 4961964..d58bfd4 100644
--- a/lib/nes/ppu.hh
+++ b/lib/nes/ppu.hh
@@ -20,18 +20,6 @@ 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 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,
@@ -42,15 +30,18 @@ namespace nes
{
};
- union color_index
+ struct color_index
{
- struct __attribute__((packed))
- {
- unsigned color : 2;
- unsigned palette : 2;
- role role : 1;
- };
- std::uint8_t value;
+ 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 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<unsigned>(v)); }
+
+ std::uint8_t value{};
explicit color_index(std::uint8_t const value)
: value{ value }
@@ -84,48 +75,73 @@ namespace nes
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
{
- 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;
- };
+ 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); }
+
std::uint8_t value{ 0b00000000 };
} status_{};
- union
+ struct
{
- 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
- };
+ // 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; }
+ // Sprite pattern table address for 8x8 sprites.
+ auto get_sprite_pattern_table() const -> unsigned { return (value & 0b00001000u) >> 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; }
+ // PPU master/slave select.
+ auto get_enable_ext_pin() const -> bool { return value & 0b01000000; }
+ // 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); }
+
std::uint8_t value{ 0b00000000 };
} control_{};
- union
+ struct
{
- 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.
- };
+ // Enable/disable grayscale.
+ auto get_grayscale() const -> bool { return value & 0b00000001; }
+ // Show background in leftmost 8 pixels of screen.
+ auto get_show_background_start() const -> bool { return value & 0b00000010; }
+ // Show sprites in leftmost 8 pixels of screen.
+ auto get_show_sprites_start() const -> bool { return value & 0b00000100; }
+ // Enable background rendering.
+ auto get_enable_background() const -> bool { return value & 0b00001000; }
+ // Enable sprite rendering.
+ auto get_enable_sprites() const -> bool { return value & 0b00010000; }
+ // Emphasize red.
+ auto get_emphasize_red() const -> bool { return value & 0b00100000; }
+ // Emphasize green.
+ auto get_emphasize_green() const -> bool { return value & 0b01000000; }
+ // 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); }
+
std::uint8_t value{ 0b00000000 };
} mask_{};
struct