From 235254015afdba7ed206e4c08cc5baa68c16777b Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Sat, 25 Jan 2025 16:40:30 +0100 Subject: don't rely on order for bitfields --- lib/nes/cpu.cc | 88 ++++++++++++++++++++-------------------- lib/nes/cpu.hh | 33 ++++++++------- lib/nes/ppu.cc | 99 ++++++++++++++++++--------------------------- lib/nes/ppu.hh | 126 ++++++++++++++++++++++++++++++++------------------------- 4 files changed, 173 insertions(+), 173 deletions(-) (limited to 'lib') 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(); 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(!registers_.n); + branch(!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(); 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 auto cpu::run_bmi() -> void { - branch(registers_.n); + branch(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(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(); } @@ -558,13 +558,13 @@ namespace nes auto cpu::run_bvc() -> void { // BVC: Branch If Overflow Clear - branch(!registers_.v); + branch(!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(registers_.v); + branch(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(!registers_.c); + branch(!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(registers_.c); + branch(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(!registers_.z); + branch(!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(registers_.z); + branch(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 @@ -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((registers_.c << 7) | (arg >> 1)); - registers_.c = (arg & 0x1) != 0; + auto const res = static_cast(((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((arg << 1) | (registers_.c ? 1 : 0)); - registers_.c = (arg & 0x80) != 0; + auto const res = static_cast((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(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(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(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(tile_data_ >> 32); background_color = color_index{ static_cast((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(x) - static_cast(s.position); if (offset < 0 || offset > 7) { continue; } auto const color = color_index{ static_cast((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(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(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(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(0x1000u * table + 16u * tile + row) }; - break; + tile += 1; + row -= 8; } + addr = address{ static_cast(0x1000u * table + 16u * tile + row) }; + } + else + { + auto const table = control_.get_sprite_pattern_table(); + addr = address{ static_cast(0x1000u * table + 16u * tile + row) }; } auto const a = (attributes & 3u) << 2; @@ -461,7 +456,7 @@ namespace nes auto const res = static_cast(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((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(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 -- cgit v1.3.1