From 6512dd599d13035f6a90b2009344e02c488f5aae Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Thu, 23 Jan 2025 19:37:32 +0100 Subject: initial working version --- src/nes/cpu.cc | 20 ++--- src/nes/cpu.hh | 6 +- src/nes/mapper.cc | 21 +++-- src/nes/mapper.hh | 3 + src/nes/nes.cc | 32 ++++++- src/nes/nes.hh | 2 + src/nes/ppu.cc | 208 +++++++++++++++++++++++++------------------- src/nes/ppu.hh | 19 ++-- src/nes/util/address.hh | 2 +- src/nes/util/cycle_count.hh | 2 + src/status.hh | 42 +++++++++ 11 files changed, 233 insertions(+), 124 deletions(-) diff --git a/src/nes/cpu.cc b/src/nes/cpu.cc index b488a08..9060e72 100644 --- a/src/nes/cpu.cc +++ b/src/nes/cpu.cc @@ -45,14 +45,6 @@ namespace nes nmi_pending_ = true; } - auto cpu::step_to(cycle_count const target) -> void - { - while (current_cycles_ < target) - { - step(); - } - } - auto cpu::step() -> void { // See https://www.nesdev.org/wiki/CPU_unofficial_opcodes @@ -400,8 +392,10 @@ namespace nes auto cpu::run_anc() -> void { // ANC: AND #i + copy N to C - // TODO - std::abort(); + auto operand = fetch_operand(); + eval_and(operand.read()); + registers_.c = registers_.n; + current_cycles_ += operand.get_cycles(); } template @@ -539,8 +533,10 @@ namespace nes auto cpu::run_alr() -> void { // ALR: AND #i + LSR A - // TODO - std::abort(); + auto operand = fetch_operand(); + eval_and(operand.read()); + registers_.a = eval_lsr(registers_.a); + current_cycles_ += operand.get_cycles(); } auto cpu::run_pha() -> void diff --git a/src/nes/cpu.hh b/src/nes/cpu.hh index 0614f76..fc3ef5b 100644 --- a/src/nes/cpu.hh +++ b/src/nes/cpu.hh @@ -134,9 +134,11 @@ namespace nes auto snapshot(test::status&) -> void; + auto get_cycles() const -> cycle_count { return current_cycles_; } auto stall_cycles(cycle_count) -> void; auto trigger_nmi() -> void; - auto step_to(cycle_count) -> void; + auto step() -> void; + auto is_nmi_pending() const -> bool { return nmi_pending_; } // XXX: Debugging // Memory access @@ -146,8 +148,6 @@ namespace nes auto write16(address, std::uint16_t) -> void; private: - auto step() -> void; - // Instructions #define DEFINE_SIMPLE_INSTRUCTION(name) \ diff --git a/src/nes/mapper.cc b/src/nes/mapper.cc index aa9618e..5392d87 100644 --- a/src/nes/mapper.cc +++ b/src/nes/mapper.cc @@ -16,6 +16,11 @@ namespace nes { } + auto mapper::snapshot(test::status& snapshot) const -> void + { + snapshot.chrrom = std::vector(cartridge_.get_chr_rom(), cartridge_.get_chr_rom() + cartridge_.get_chr_rom_length()); + } + auto mapper::select(cartridge& cartridge) -> mapper { switch (cartridge.get_mapper_number()) @@ -115,17 +120,17 @@ namespace nes { case name_table_arrangement::horizontal: // $2000 = $2400, $2800 = $2c00 - if (rel <= 0x23FF) { return 0x000 + (rel % 0x400); } - if (rel <= 0x27FF) { return 0x000 + (rel % 0x400); } - if (rel <= 0x2BFF) { return 0x400 + (rel % 0x400); } - if (rel <= 0x2FFF) { return 0x400 + (rel % 0x400); } + if (rel <= 0x3FF) { return 0x000 + (rel % 0x400); } + if (rel <= 0x7FF) { return 0x000 + (rel % 0x400); } + if (rel <= 0xBFF) { return 0x400 + (rel % 0x400); } + if (rel <= 0xFFF) { return 0x400 + (rel % 0x400); } break; case name_table_arrangement::vertical: // $2000 = $2800, $2400 = $2c00 - if (rel <= 0x23FF) { return 0x000 + (rel % 0x400); } - if (rel <= 0x27FF) { return 0x400 + (rel % 0x400); } - if (rel <= 0x2BFF) { return 0x000 + (rel % 0x400); } - if (rel <= 0x2FFF) { return 0x400 + (rel % 0x400); } + if (rel <= 0x3FF) { return 0x000 + (rel % 0x400); } + if (rel <= 0x7FF) { return 0x400 + (rel % 0x400); } + if (rel <= 0xBFF) { return 0x000 + (rel % 0x400); } + if (rel <= 0xFFF) { return 0x400 + (rel % 0x400); } break; } diff --git a/src/nes/mapper.hh b/src/nes/mapper.hh index f2873a6..12ad804 100644 --- a/src/nes/mapper.hh +++ b/src/nes/mapper.hh @@ -1,6 +1,7 @@ #pragma once #include "nes/util/address.hh" +#include "status.hh" namespace nes { @@ -27,6 +28,8 @@ namespace nes auto operator=(mapper const&) -> mapper& = delete; auto operator=(mapper&&) -> mapper& = delete; + auto snapshot(test::status&) const -> void; + auto read_cpu(address const addr) -> std::uint8_t { return (this->*read_cpu_)(addr); diff --git a/src/nes/nes.cc b/src/nes/nes.cc index 3ed7a22..7ac1887 100644 --- a/src/nes/nes.cc +++ b/src/nes/nes.cc @@ -32,8 +32,34 @@ namespace nes auto nes::step(cycle_count const delta) -> void { current_cycles_ += delta; - cpu_.step_to(current_cycles_); - ppu_.step_to(current_cycles_); + while (cpu_.get_cycles() < current_cycles_) + { + cpu_.step(); + while (ppu_.get_cycles() < cpu_.get_cycles()) + { + ppu_.step(); + } + } + } + + auto nes::step_to_nmi() -> void + { + while (cpu_.is_nmi_pending()) + { + cpu_.step(); + while (ppu_.get_cycles() < cpu_.get_cycles()) + { + ppu_.step(); + } + } + while (!cpu_.is_nmi_pending()) + { + cpu_.step(); + while (ppu_.get_cycles() < cpu_.get_cycles()) + { + ppu_.step(); + } + } } auto nes::snapshot() -> test::status @@ -41,6 +67,8 @@ namespace nes auto res = test::status{}; res.sram = std::vector(cartridge_.get_ram(), cartridge_.get_ram() + cartridge_.get_ram_length()); cpu_.snapshot(res); + ppu_.snapshot(res); + mapper_.snapshot(res); return res; } } // namespace nes \ No newline at end of file diff --git a/src/nes/nes.hh b/src/nes/nes.hh index 16e645a..76c29ab 100644 --- a/src/nes/nes.hh +++ b/src/nes/nes.hh @@ -36,9 +36,11 @@ namespace nes auto get_controller_1() -> controller& { return controller_1_; } auto get_controller_2() const -> controller const& { return controller_2_; } auto get_controller_2() -> controller& { return controller_2_; } + auto ref_operations() -> std::vector& { return ppu_.memory_operations; } auto step(std::chrono::microseconds delta) -> void; auto step(cycle_count delta) -> void; + auto step_to_nmi() -> void; // XXX: Debugging auto snapshot() -> test::status; }; diff --git a/src/nes/ppu.cc b/src/nes/ppu.cc index 29a5643..87c7f8d 100644 --- a/src/nes/ppu.cc +++ b/src/nes/ppu.cc @@ -4,6 +4,9 @@ #include "nes/mapper.hh" #include "nes/util/address.hh" #include "nes/util/rgb.hh" +#include + +// #define ENABLE_TRACES namespace nes { @@ -14,12 +17,10 @@ namespace nes { } - auto ppu::step_to(cycle_count const cycle) -> void + auto ppu::snapshot(test::status& snapshot) -> void { - while (current_cycles_ < cycle) - { - step(); - } + snapshot.vram = std::vector(std::begin(vram_), std::end(vram_)); + snapshot.oam = std::vector(std::begin(oam_), std::end(oam_)); } auto ppu::step() -> void @@ -27,15 +28,6 @@ namespace nes // See https://www.nesdev.org/wiki/PPU_rendering // Based on: https://github.com/fogleman/nes/blob/master/nes/ppu.go - if (nmi_delay_ > 0) - { - nmi_delay_ -= 1; - if (nmi_delay_ == 0 && control_.vblank_nmi && status_.vblank) - { - cpu_.trigger_nmi(); - } - } - auto const enable_rendering = mask_.enable_background || mask_.enable_sprites; current_cycles_ += cycle_count::from_ppu(1); @@ -160,24 +152,27 @@ namespace nes if (mask_.enable_background) { auto const tile_data = static_cast(tile_data_ >> 32); - background_color = color_index{ (tile_data >> ((7 - internal_.x) * 4)) & 0xF }; + background_color = color_index{ static_cast((tile_data >> ((7 - internal_.x) * 4)) & 0xF) }; background_color.role = role::background; } auto foreground = sprite{}; auto foreground_color = color_index{ 0 }; - for (auto i = unsigned{ 0 }; i < sprite_count_; ++i) + if (mask_.enable_sprites) { - auto const s = sprites_[i]; - auto const offset = static_cast(x) - static_cast(s.position); - if (offset < 0 || offset > 7) { continue; } - auto const color = color_index{ (s.pattern >> ((7 - offset) * 4)) & 0xF }; - if (color.color == 0) { continue; } - - foreground = s; - foreground_color = color; - foreground_color.role = role::sprite; - break; + for (auto i = unsigned{ 0 }; i < sprite_count_; ++i) + { + auto const s = sprites_[i]; + 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; } + + foreground = s; + foreground_color = color; + foreground_color.role = role::sprite; + break; + } } auto has_background = background_color.color != 0; @@ -341,7 +336,7 @@ namespace nes sprites_[sprite_count_].position = x; // TODO: Check // sprites_[sprite_count_].is_in_front = a & 0b00100000; - sprites_[sprite_count_].is_in_front = (a >> 5) & 1; + sprites_[sprite_count_].is_in_front = ((a >> 5) & 1) == 0; sprites_[sprite_count_].is_sprite_zero = i == 0; sprite_count_ += 1; } @@ -425,7 +420,7 @@ namespace nes if (addr <= address{ 0x3EFF }) { return mapper_.read_ppu(addr, vram_); } if (addr <= address{ 0x3FFF }) { - auto const index = color_index{ addr.get_absolute() }; + auto const index = color_index{ static_cast(addr.get_absolute() % 0x20) }; auto const color = get_color(index); return static_cast(color); } @@ -443,7 +438,7 @@ namespace nes } if (addr <= address{ 0x3FFF }) { - auto const index = color_index{ addr.get_absolute() }; + auto const index = color_index{ static_cast(addr.get_absolute() % 0x20) }; get_color(index) = color{ value }; return; } @@ -466,12 +461,15 @@ namespace nes { auto const base = latch_ & ~status_mask; auto const res = base | (status_.value & status_mask); +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::read_ppustatus, res, current_cycles_); +#endif status_.vblank = false; internal_.w = false; nmi_change(); - //write_latch(res); + write_latch(res); return res; } @@ -482,6 +480,9 @@ namespace nes { res &= 0xE3; } +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::read_oamdata, res, current_cycles_); +#endif write_latch(res); return res; } @@ -490,8 +491,9 @@ namespace nes { auto const addr = address{ internal_.v }; auto res = read8(addr); + write_latch(ppudata_read_buffer_); - if (addr % 0x4000 <= address{ 0x3F00 }) + if (addr % 0x4000 <= address{ 0x3EFF }) { std::swap(res, ppudata_read_buffer_); } @@ -501,7 +503,9 @@ namespace nes ppudata_read_buffer_ = read8(addr - 0x1000); } - write_latch(res); +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::read_ppudata, res, current_cycles_); +#endif increment_vram(); return res; } @@ -513,20 +517,26 @@ namespace nes auto ppu::write_ppuctrl(std::uint8_t const value) -> void { - write_latch(value); if (current_cycles_ > boot_up_cycles) { + write_latch(value); control_.value = value; internal_.t = (internal_.t & 0xF3FF) | ((value & 0x03) << 10); +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_ppuctrl, value, current_cycles_); +#endif nmi_change(); } } auto ppu::write_ppuscroll(std::uint8_t const value) -> void { - write_latch(value); if (current_cycles_ > boot_up_cycles) { + write_latch(value); +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_ppuscroll, value, current_cycles_); +#endif if (!internal_.w) { // First write -> x value. @@ -546,18 +556,24 @@ namespace nes auto ppu::write_ppumask(std::uint8_t const value) -> void { - write_latch(value); if (current_cycles_ > boot_up_cycles) { + write_latch(value); +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_ppumask, value, current_cycles_); +#endif mask_.value = value; } } auto ppu::write_ppuaddr(std::uint8_t const value) -> void { - write_latch(value); if (current_cycles_ > boot_up_cycles) { + write_latch(value); +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_ppuaddr, value, current_cycles_); +#endif if (!internal_.w) { // First write. @@ -576,6 +592,9 @@ namespace nes auto ppu::write_ppudata(std::uint8_t const value) -> void { +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_ppudata, value, current_cycles_); +#endif write_latch(value); write8(address{ internal_.v }, value); increment_vram(); @@ -583,12 +602,18 @@ namespace nes auto ppu::write_oamaddr(std::uint8_t const value) -> void { +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_oamaddr, value, current_cycles_); +#endif write_latch(value); oamaddr_ = value; } auto ppu::write_oamdata(std::uint8_t const value) -> void { +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_oamdata, value, current_cycles_); +#endif write_latch(value); oam_[oamaddr_] = value; oamaddr_ += 1; @@ -597,13 +622,18 @@ namespace nes auto ppu::write_oamdma(std::uint8_t const value) -> void { // TODO: Do this in CPU::step instead in case there's an NMI? - for (auto i = std::uint8_t{ 0x00 }; i < 0xff; ++i) +#ifdef ENABLE_TRACES + memory_operations.emplace_back(test::memory_operation::type::write_oamdma, value, current_cycles_); +#endif + auto addr = address{ value, 0x00 }; + for (auto i = unsigned{ 0 }; i < 256; ++i) { - oam_[oamaddr_] = cpu_.read8(address{ value, i }); + oam_[oamaddr_] = cpu_.read8(addr); oamaddr_ += 1; + addr = addr + 1; } - auto const stalled_cycles = cycle_count::from_cpu((current_cycles_.to_ppu() % 2 == 0) ? 513 : 514); + auto const stalled_cycles = cycle_count::from_cpu((cpu_.get_cycles().to_cpu() % 2 == 0) ? 513 : 514); cpu_.stall_cycles(stalled_cycles); } @@ -614,12 +644,10 @@ namespace nes auto ppu::nmi_change() -> void { auto const should_trigger = control_.vblank_nmi && status_.vblank; - if (!nmi_requested_ && should_trigger) + if (should_trigger) { - // TODO: HACK - nmi_delay_ = 15; + cpu_.trigger_nmi(); } - nmi_requested_ = should_trigger; } auto ppu::sprite_height() -> unsigned @@ -666,66 +694,66 @@ namespace nes switch (index & 0x3F) { case 0x00: return rgb::from_hex(0x666666); - case 0x01: return rgb::from_hex(0x882A00); - case 0x02: return rgb::from_hex(0xA71214); - case 0x03: return rgb::from_hex(0xA4003B); - case 0x04: return rgb::from_hex(0x7E005C); - case 0x05: return rgb::from_hex(0x40006E); - case 0x06: return rgb::from_hex(0x00066C); - case 0x07: return rgb::from_hex(0x001D56); - case 0x08: return rgb::from_hex(0x003533); - case 0x09: return rgb::from_hex(0x00480B); + case 0x01: return rgb::from_hex(0x002A88); + case 0x02: return rgb::from_hex(0x1412A7); + case 0x03: return rgb::from_hex(0x3B00A4); + case 0x04: return rgb::from_hex(0x5C007E); + case 0x05: return rgb::from_hex(0x6E0040); + case 0x06: return rgb::from_hex(0x6C0600); + case 0x07: return rgb::from_hex(0x561D00); + case 0x08: return rgb::from_hex(0x333500); + case 0x09: return rgb::from_hex(0x0B4800); case 0x0A: return rgb::from_hex(0x005200); - case 0x0B: return rgb::from_hex(0x084F00); - case 0x0C: return rgb::from_hex(0x4D4000); + case 0x0B: return rgb::from_hex(0x004F08); + case 0x0C: return rgb::from_hex(0x00404D); case 0x0D: return rgb::from_hex(0x000000); case 0x0E: return rgb::from_hex(0x000000); case 0x0F: return rgb::from_hex(0x000000); case 0x10: return rgb::from_hex(0xADADAD); - case 0x11: return rgb::from_hex(0xD95F15); - case 0x12: return rgb::from_hex(0xFF4042); - case 0x13: return rgb::from_hex(0xFE2775); - case 0x14: return rgb::from_hex(0xCC1AA0); - case 0x15: return rgb::from_hex(0x7B1EB7); - case 0x16: return rgb::from_hex(0x2031B5); - case 0x17: return rgb::from_hex(0x004E99); - case 0x18: return rgb::from_hex(0x006D6B); - case 0x19: return rgb::from_hex(0x008738); - case 0x1A: return rgb::from_hex(0x00930C); - case 0x1B: return rgb::from_hex(0x328F00); - case 0x1C: return rgb::from_hex(0x8D7C00); + case 0x11: return rgb::from_hex(0x155FD9); + case 0x12: return rgb::from_hex(0x4240FF); + case 0x13: return rgb::from_hex(0x7527FE); + case 0x14: return rgb::from_hex(0xA01ACC); + case 0x15: return rgb::from_hex(0xB71E7B); + case 0x16: return rgb::from_hex(0xB53120); + case 0x17: return rgb::from_hex(0x994E00); + case 0x18: return rgb::from_hex(0x6B6D00); + case 0x19: return rgb::from_hex(0x388700); + case 0x1A: return rgb::from_hex(0x0C9300); + case 0x1B: return rgb::from_hex(0x008F32); + case 0x1C: return rgb::from_hex(0x007C8D); case 0x1D: return rgb::from_hex(0x000000); case 0x1E: return rgb::from_hex(0x000000); case 0x1F: return rgb::from_hex(0x000000); case 0x20: return rgb::from_hex(0xFFFEFF); - case 0x21: return rgb::from_hex(0xFFB064); - case 0x22: return rgb::from_hex(0xFF9092); - case 0x23: return rgb::from_hex(0xFF76C6); - case 0x24: return rgb::from_hex(0xFF6AF3); - case 0x25: return rgb::from_hex(0xCC6EFE); - case 0x26: return rgb::from_hex(0x7081FE); - case 0x27: return rgb::from_hex(0x229EEA); - case 0x28: return rgb::from_hex(0x00BEBC); - case 0x29: return rgb::from_hex(0x00D888); - case 0x2A: return rgb::from_hex(0x30E45C); - case 0x2B: return rgb::from_hex(0x82E045); - case 0x2C: return rgb::from_hex(0xDECD48); + case 0x21: return rgb::from_hex(0x64B0FF); + case 0x22: return rgb::from_hex(0x9290FF); + case 0x23: return rgb::from_hex(0xC676FF); + case 0x24: return rgb::from_hex(0xF36AFF); + case 0x25: return rgb::from_hex(0xFE6ECC); + case 0x26: return rgb::from_hex(0xFE8170); + case 0x27: return rgb::from_hex(0xEA9E22); + case 0x28: return rgb::from_hex(0xBCBE00); + case 0x29: return rgb::from_hex(0x88D800); + case 0x2A: return rgb::from_hex(0x5CE430); + case 0x2B: return rgb::from_hex(0x45E082); + case 0x2C: return rgb::from_hex(0x48CDDE); case 0x2D: return rgb::from_hex(0x4F4F4F); case 0x2E: return rgb::from_hex(0x000000); case 0x2F: return rgb::from_hex(0x000000); case 0x30: return rgb::from_hex(0xFFFEFF); - case 0x31: return rgb::from_hex(0xFFDFC0); - case 0x32: return rgb::from_hex(0xFFD2D3); - case 0x33: return rgb::from_hex(0xFFC8E8); - case 0x34: return rgb::from_hex(0xFFC2FB); - case 0x35: return rgb::from_hex(0xEAC4FE); - case 0x36: return rgb::from_hex(0xC5CCFE); - case 0x37: return rgb::from_hex(0xA5D8F7); - case 0x38: return rgb::from_hex(0x94E5E4); - case 0x39: return rgb::from_hex(0x96EFCF); - case 0x3A: return rgb::from_hex(0xABF4BD); - case 0x3B: return rgb::from_hex(0xCCF3B3); - case 0x3C: return rgb::from_hex(0xF2EBB5); + case 0x31: return rgb::from_hex(0xC0DFFF); + case 0x32: return rgb::from_hex(0xD3D2FF); + case 0x33: return rgb::from_hex(0xE8C8FF); + case 0x34: return rgb::from_hex(0xFBC2FF); + case 0x35: return rgb::from_hex(0xFEC4EA); + case 0x36: return rgb::from_hex(0xFECCC5); + case 0x37: return rgb::from_hex(0xF7D8A5); + case 0x38: return rgb::from_hex(0xE4E594); + case 0x39: return rgb::from_hex(0xCFEF96); + case 0x3A: return rgb::from_hex(0xBDF4AB); + case 0x3B: return rgb::from_hex(0xB3F3CC); + case 0x3C: return rgb::from_hex(0xB5EBF2); case 0x3D: return rgb::from_hex(0xB8B8B8); case 0x3E: return rgb::from_hex(0x000000); case 0x3F: return rgb::from_hex(0x000000); diff --git a/src/nes/ppu.hh b/src/nes/ppu.hh index 9bd6139..fff51de 100644 --- a/src/nes/ppu.hh +++ b/src/nes/ppu.hh @@ -1,6 +1,7 @@ #pragma once #include "nes/util/cycle_count.hh" +#include "status.hh" namespace nes { @@ -19,19 +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 vram_increment + enum class vram_increment : unsigned { add_1_across = 0, add_32_down = 1, }; - enum class sprite_size + enum class sprite_size : unsigned { _8x8 = 0, _8x16 = 1, }; - enum class role + enum class role : unsigned { background = 0, sprite = 1, @@ -49,9 +50,9 @@ namespace nes unsigned palette : 2; role role : 1; }; - unsigned value; + std::uint8_t value; - explicit color_index(unsigned const value) + explicit color_index(std::uint8_t const value) : value{ value } { } @@ -147,7 +148,6 @@ namespace nes std::uint8_t high_tile_byte_{ 0 }; sprite sprites_[8]{}; // Evaluated sprites. unsigned sprite_count_{ 0 }; // Number of evaluated sprites in sprites_. - unsigned nmi_delay_{ 0 }; // Delay until an NMI is requested if conditions still met. bool nmi_requested_{ false }; public: @@ -158,7 +158,11 @@ namespace nes auto operator=(ppu const&) -> ppu& = delete; auto operator=(ppu&&) -> ppu& = delete; - auto step_to(cycle_count) -> void; + std::vector memory_operations; + + auto get_cycles() const -> cycle_count { return current_cycles_; } + auto snapshot(test::status&) -> void; + auto step() -> void; // Memory access @@ -183,7 +187,6 @@ namespace nes auto write_oamdma(std::uint8_t) -> void; private: - auto step() -> void; auto render_pixel() -> void; auto fetch_name_table_byte() -> void; auto fetch_attribute_table_byte() -> void; diff --git a/src/nes/util/address.hh b/src/nes/util/address.hh index 85a00ab..7987d07 100644 --- a/src/nes/util/address.hh +++ b/src/nes/util/address.hh @@ -66,7 +66,7 @@ namespace nes return address{ static_cast(a.get_absolute() - b) }; } - constexpr auto operator%(address const a, std::uint16_t b) -> address + constexpr auto operator%(address const a, std::uint16_t const b) -> address { return address{ static_cast(a.get_absolute() % b) }; } diff --git a/src/nes/util/cycle_count.hh b/src/nes/util/cycle_count.hh index f8039c2..8afd40a 100644 --- a/src/nes/util/cycle_count.hh +++ b/src/nes/util/cycle_count.hh @@ -11,6 +11,7 @@ namespace nes class cycle_count { friend constexpr auto operator+(cycle_count, cycle_count) -> cycle_count; + friend constexpr auto operator-(cycle_count, cycle_count) -> cycle_count; friend constexpr auto operator*(std::uint64_t, cycle_count) -> cycle_count; friend constexpr auto operator*(cycle_count, std::uint64_t) -> cycle_count; friend constexpr auto operator/(cycle_count, cycle_count) -> std::uint64_t; @@ -79,6 +80,7 @@ namespace nes constexpr auto operator<=(cycle_count const a, cycle_count const b) -> bool { return a.get_units() <= b.get_units(); } constexpr auto operator>=(cycle_count const a, cycle_count const b) -> bool { return a.get_units() >= b.get_units(); } constexpr auto operator+(cycle_count const a, cycle_count const b) -> cycle_count { return cycle_count{ a.get_units() + b.get_units() }; } + constexpr auto operator-(cycle_count const a, cycle_count const b) -> cycle_count { return cycle_count{ a.get_units() - b.get_units() }; } constexpr auto operator*(std::uint64_t const a, cycle_count const b) -> cycle_count { return cycle_count{ a * b.get_units() }; } constexpr auto operator*(cycle_count const a, std::uint64_t const b) -> cycle_count { return cycle_count{ a.get_units() * b }; } constexpr auto operator/(cycle_count const a, cycle_count const b) -> std::uint64_t { return a.get_units() / b.get_units(); } diff --git a/src/status.hh b/src/status.hh index 705ae6e..b56be58 100644 --- a/src/status.hh +++ b/src/status.hh @@ -14,6 +14,9 @@ namespace test nes::cycle_count cpu_cycle; std::vector ram; std::vector sram; + std::vector vram; + std::vector oam; + std::vector chrrom; struct { std::uint16_t pc{}; @@ -52,4 +55,43 @@ namespace test return true; } }; + + struct memory_operation + { + enum class type + { + read_ppustatus, + read_oamdata, + read_ppudata, + write_ppuctrl, + write_ppuscroll, + write_ppumask, + write_ppuaddr, + write_ppudata, + write_oamaddr, + write_oamdata, + write_oamdma, + }; + + explicit constexpr memory_operation(type const t, std::uint8_t const v, nes::cycle_count cycles) + : t{ t } + , v{ v } + , cycles{ cycles } + { + } + + type t{}; + std::uint8_t v{}; + nes::cycle_count cycles; + }; + + constexpr auto operator==(memory_operation const a, memory_operation const b) -> bool + { + return a.t == b.t && a.v == b.v; + } + + constexpr auto operator!=(memory_operation const a, memory_operation const b) -> bool + { + return a.t != b.t || a.v != b.v; + } } // namespace test \ No newline at end of file -- cgit v1.3.1