summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt50
-rw-r--r--app/CMakeLists.txt5
-rw-r--r--app/mac/CMakeLists.txt20
-rw-r--r--app/mac/main.mm (renamed from src/main.mm)0
-rw-r--r--app/mac/scene.hh (renamed from src/scene.hh)0
-rw-r--r--app/mac/scene.mm (renamed from src/scene.mm)0
-rw-r--r--app/test/CMakeLists.txt14
-rw-r--r--app/test/main.cc76
-rw-r--r--lib/CMakeLists.txt21
-rw-r--r--lib/nes/CMakeLists.txt20
-rw-r--r--lib/nes/cartridge.cc (renamed from src/nes/cartridge.cc)0
-rw-r--r--lib/nes/cartridge.hh (renamed from src/nes/cartridge.hh)0
-rw-r--r--lib/nes/controller.cc (renamed from src/nes/controller.cc)2
-rw-r--r--lib/nes/controller.hh (renamed from src/nes/controller.hh)0
-rw-r--r--lib/nes/cpu.cc (renamed from src/nes/cpu.cc)19
-rw-r--r--lib/nes/cpu.hh (renamed from src/nes/cpu.hh)13
-rw-r--r--lib/nes/display.cc (renamed from src/nes/display.cc)0
-rw-r--r--lib/nes/display.hh (renamed from src/nes/display.hh)0
-rw-r--r--lib/nes/mapper.cc (renamed from src/nes/mapper.cc)5
-rw-r--r--lib/nes/mapper.hh (renamed from src/nes/mapper.hh)3
-rw-r--r--lib/nes/nes.cc (renamed from src/nes/nes.cc)10
-rw-r--r--lib/nes/nes.hh (renamed from src/nes/nes.hh)7
-rw-r--r--lib/nes/ppu.cc (renamed from src/nes/ppu.cc)64
-rw-r--r--lib/nes/ppu.hh (renamed from src/nes/ppu.hh)9
-rw-r--r--lib/nes/ref/CMakeLists.txt4
-rw-r--r--lib/nes/ref/test.cc3
-rw-r--r--lib/nes/util/CMakeLists.txt (renamed from src/nes/util/CMakeLists.txt)6
-rw-r--r--lib/nes/util/address.hh (renamed from src/nes/util/address.hh)0
-rw-r--r--lib/nes/util/button-mask.hh (renamed from src/nes/util/button-mask.hh)0
-rw-r--r--lib/nes/util/cycle-count.hh (renamed from src/nes/util/cycle_count.hh)2
-rw-r--r--lib/nes/util/debug.hh15
-rw-r--r--lib/nes/util/rgb.hh (renamed from src/nes/util/rgb.hh)0
-rw-r--r--lib/nes/util/snapshot.hh63
-rw-r--r--src/CMakeLists.txt13
-rw-r--r--src/nes/CMakeLists.txt19
-rw-r--r--src/status.hh97
-rw-r--r--src/test/CMakeLists.txt0
-rw-r--r--src/test/main.cc3
38 files changed, 335 insertions, 228 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5bfbf5d..abd4f29 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,11 +4,49 @@ project(
LANGUAGES CXX OBJCXX OBJC
VERSION 1.0)
-set(CMAKE_CXX_STANDARD 17)
+option(NES_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer." OFF)
+option(NES_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer." OFF)
+option(NES_ENABLE_DEBUG_OUTPUT "Enable debug output." OFF)
+option(NES_ENABLE_PPU "Enable Picture Processing Unit." ON)
-add_executable(nes)
-add_subdirectory(src)
+add_library(nes_options INTERFACE)
+add_library(nes::options ALIAS nes_options)
+target_compile_features(nes_options INTERFACE cxx_std_17)
+target_compile_options(nes_options
+ INTERFACE
+ -Wall
+ -Wextra
+ -Wnon-virtual-dtor
+ -Wcast-align
+ -Wunused
+ -Woverloaded-virtual
+ -Wpedantic
+ -Wconversion
+ -Wsign-conversion
+ -Wdouble-promotion
+ -Wformat=2
+ -Wswitch-enum
+ -Wno-gnu-anonymous-struct)
+if(APPLE)
+ # Use automatic reference counting for Objective-C code.
+ target_compile_options(nes_options INTERFACE "-fobjc-arc")
+endif()
+if(NES_ENABLE_SANITIZER_ADDRESS AND NES_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR)
+ message(FATAL_ERROR "Address sanitizer and undefined behavior sanitizer cannot be enabled at the same time.")
+elseif(NES_ENABLE_SANITIZER_ADDRESS)
+ target_compile_options(nes_options INTERFACE -fsanitize=address)
+elseif(NES_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR)
+ target_compile_options(nes_options INTERFACE -fsanitize=undefined)
+endif()
-find_library(FRAMEWORK_COCOA Cocoa REQUIRED)
-find_library(FRAMEWORK_SPRITEKIT SpriteKit REQUIRED)
-target_link_libraries(nes PRIVATE ${FRAMEWORK_COCOA} ${FRAMEWORK_SPRITEKIT}) \ No newline at end of file
+if(NES_ENABLE_DEBUG_OUTPUT)
+ include(FetchContent)
+ FetchContent_Declare(
+ spdlog
+ GIT_REPOSITORY https://github.com/gabime/spdlog.git
+ GIT_TAG v1.15.0)
+ FetchContent_MakeAvailable(spdlog)
+endif()
+
+add_subdirectory(lib)
+add_subdirectory(app) \ No newline at end of file
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..8ff9434
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,5 @@
+if(APPLE)
+ add_subdirectory(mac)
+endif()
+
+add_subdirectory(test) \ No newline at end of file
diff --git a/app/mac/CMakeLists.txt b/app/mac/CMakeLists.txt
new file mode 100644
index 0000000..a4f25c9
--- /dev/null
+++ b/app/mac/CMakeLists.txt
@@ -0,0 +1,20 @@
+add_executable(nes_mac)
+
+find_library(FRAMEWORK_COCOA Cocoa REQUIRED)
+find_library(FRAMEWORK_SPRITEKIT SpriteKit REQUIRED)
+
+target_include_directories(nes_mac PRIVATE .)
+target_link_libraries(
+ nes_mac
+ PRIVATE
+ nes::options
+ nes::nes
+ ${FRAMEWORK_COCOA}
+ ${FRAMEWORK_SPRITEKIT})
+
+target_sources(
+ nes_mac
+ PRIVATE
+ main.mm
+ scene.hh
+ scene.mm) \ No newline at end of file
diff --git a/src/main.mm b/app/mac/main.mm
index 5ca7c5c..5ca7c5c 100644
--- a/src/main.mm
+++ b/app/mac/main.mm
diff --git a/src/scene.hh b/app/mac/scene.hh
index 1660e66..1660e66 100644
--- a/src/scene.hh
+++ b/app/mac/scene.hh
diff --git a/src/scene.mm b/app/mac/scene.mm
index f858185..f858185 100644
--- a/src/scene.mm
+++ b/app/mac/scene.mm
diff --git a/app/test/CMakeLists.txt b/app/test/CMakeLists.txt
new file mode 100644
index 0000000..b88acc2
--- /dev/null
+++ b/app/test/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_executable(nes_test)
+
+target_include_directories(nes_test PRIVATE .)
+target_link_libraries(
+ nes_test
+ PRIVATE
+ nes::options
+ nes::nes
+ nes::ref)
+
+target_sources(
+ nes_test
+ PRIVATE
+ main.cc) \ No newline at end of file
diff --git a/app/test/main.cc b/app/test/main.cc
new file mode 100644
index 0000000..3bb785f
--- /dev/null
+++ b/app/test/main.cc
@@ -0,0 +1,76 @@
+#include "nes/display.hh"
+#include "nes/nes.hh"
+#include "nes/util/rgb.hh"
+#include "nes/util/snapshot.hh"
+#include <iostream>
+
+namespace
+{
+ class dummy_display final : public nes::display
+ {
+ std::uint8_t buffer_a_[width * height * 3]{};
+ std::uint8_t buffer_b_[width * height * 3]{};
+ std::uint8_t* front_buffer_{ buffer_a_ };
+ std::uint8_t* back_buffer_{ buffer_b_ };
+
+ public:
+ explicit dummy_display() = default;
+
+ auto get(unsigned const x, unsigned const y) const -> nes::rgb override
+ {
+ auto const offset = ((y * width) + x) * 3;
+ return nes::rgb{ back_buffer_[offset + 0], back_buffer_[offset + 1], back_buffer_[offset + 2] };
+ }
+
+ auto set(unsigned const x, unsigned const y, nes::rgb const value) -> void override
+ {
+ auto const offset = ((y * width) + x) * 3;
+ back_buffer_[offset + 0] = value.r;
+ back_buffer_[offset + 1] = value.g;
+ back_buffer_[offset + 2] = value.b;
+ }
+
+ auto switch_buffers() -> void override
+ {
+ std::swap(front_buffer_, back_buffer_);
+ }
+ };
+} // namespace
+
+int main()
+{
+ auto const path = "/Users/hannes/Documents/temp/supermario.nes";
+
+ auto cartridge = nes::cartridge::from_file(path);
+ if (!cartridge.is_valid())
+ {
+ std::cerr << "Could not load cartridge!" << std::endl;
+ std::abort();
+ }
+
+ // Initialize the system to be tested.
+ auto display_a = std::make_unique<dummy_display>();
+ auto sys_a = nes::nes{ cartridge, *display_a };
+
+ // Initialize the (assumed to be working) reference implementation.
+ auto display_b = std::make_unique<dummy_display>();
+ auto sys_b = nes::nes{ cartridge, *display_b };
+
+ // Run.
+ while (true)
+ {
+ auto const status_a = sys_a.get_snapshot();
+ auto const status_b = sys_b.get_snapshot();
+
+ if (status_a != status_b)
+ {
+ std::cout << "Different snapshot!\n";
+ return 1;
+ }
+
+ std::cout << "Cycle: " << status_a.cpu_cycle.to_cpu() << ", " << status_b.cpu_cycle.to_cpu() << '\n';
+
+ sys_a.step_to_nmi();
+ sys_b.step_to_nmi();
+ }
+} \ No newline at end of file
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..e98002c
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,21 @@
+add_library(nes STATIC)
+add_library(nes::nes ALIAS nes)
+
+target_include_directories(nes PUBLIC .)
+target_link_libraries(nes PRIVATE nes::options)
+
+if(NES_ENABLE_PPU)
+ target_compile_definitions(nes PUBLIC NES_ENABLE_PPU)
+endif()
+if(NES_ENABLE_DEBUG_OUTPUT)
+ target_compile_definitions(nes PUBLIC NES_ENABLE_DEBUG_OUTPUT)
+ target_link_libraries(nes PUBLIC spdlog::spdlog)
+endif()
+
+add_library(nes_ref STATIC)
+add_library(nes::ref ALIAS nes_ref)
+
+target_link_libraries(nes_ref PRIVATE nes::options)
+target_link_libraries(nes_ref PUBLIC nes::nes)
+
+add_subdirectory(nes) \ No newline at end of file
diff --git a/lib/nes/CMakeLists.txt b/lib/nes/CMakeLists.txt
new file mode 100644
index 0000000..78b7d04
--- /dev/null
+++ b/lib/nes/CMakeLists.txt
@@ -0,0 +1,20 @@
+target_sources(
+ nes
+ PRIVATE
+ nes.hh
+ nes.cc
+ cartridge.hh
+ cartridge.cc
+ display.hh
+ display.cc
+ controller.hh
+ controller.cc
+ cpu.hh
+ cpu.cc
+ ppu.hh
+ ppu.cc
+ mapper.hh
+ mapper.cc)
+
+add_subdirectory(util)
+add_subdirectory(ref) \ No newline at end of file
diff --git a/src/nes/cartridge.cc b/lib/nes/cartridge.cc
index 861dc13..861dc13 100644
--- a/src/nes/cartridge.cc
+++ b/lib/nes/cartridge.cc
diff --git a/src/nes/cartridge.hh b/lib/nes/cartridge.hh
index 3ce2a16..3ce2a16 100644
--- a/src/nes/cartridge.hh
+++ b/lib/nes/cartridge.hh
diff --git a/src/nes/controller.cc b/lib/nes/controller.cc
index 87842e2..d9d6946 100644
--- a/src/nes/controller.cc
+++ b/lib/nes/controller.cc
@@ -7,7 +7,7 @@ namespace nes
auto const res = index_ < 8 ? (pressed_.get_raw_value() >> index_) & 1 : 0;
index_ += 1;
if (strobing_) { index_ = 0; }
- return res;
+ return static_cast<std::uint8_t>(res);
}
auto controller::write(std::uint8_t const value) -> void
diff --git a/src/nes/controller.hh b/lib/nes/controller.hh
index fa16859..fa16859 100644
--- a/src/nes/controller.hh
+++ b/lib/nes/controller.hh
diff --git a/src/nes/cpu.cc b/lib/nes/cpu.cc
index 9060e72..51507b0 100644
--- a/src/nes/cpu.cc
+++ b/lib/nes/cpu.cc
@@ -2,8 +2,7 @@
#include "nes/ppu.hh"
#include "nes/controller.hh"
#include "nes/mapper.hh"
-
-#define ENABLE_PPU
+#include "nes/util/snapshot.hh"
namespace nes
{
@@ -16,7 +15,7 @@ namespace nes
registers_.pc = read16(address{ 0xFFFC });
}
- auto cpu::snapshot(test::status& snapshot) -> void
+ auto cpu::build_snapshot(snapshot& snapshot) -> void
{
snapshot.cpu_cycle = current_cycles_;
snapshot.ram = std::vector(std::begin(ram_), std::end(ram_));
@@ -49,13 +48,13 @@ namespace nes
{
// See https://www.nesdev.org/wiki/CPU_unofficial_opcodes
+#ifdef NES_ENABLE_PPU
if (nmi_pending_)
{
-#ifdef ENABLE_PPU
execute_interrupt(address{ 0xFFFA });
-#endif
nmi_pending_ = false;
}
+#endif
auto const opcode = advance_pc8();
switch (opcode)
@@ -1261,7 +1260,7 @@ namespace nes
auto cpu::fetch_operand<cpu::addressing_mode::immediate>(force_page_crossing)
-> operand<addressing_mode::immediate>
{
- return operand<addressing_mode::immediate>{ *this, advance_pc8() };
+ return operand<addressing_mode::immediate>{ advance_pc8() };
}
// -----------------------------------------------------------------------------------------------------------------
@@ -1277,7 +1276,7 @@ namespace nes
if (addr <= address{ 0x1FFF }) { return ram_[addr.get_absolute() % ram_size]; }
if (addr <= address{ 0x3FFF })
{
-#ifdef ENABLE_PPU
+#ifdef NES_ENABLE_PPU
switch (addr.get_absolute() % 8)
{
case 0: return ppu_.read_latch();
@@ -1295,7 +1294,7 @@ namespace nes
#endif
}
if (addr <= address{ 0x4013 }) { return 0x0; } // TODO: APU registers
-#ifdef ENABLE_PPU
+#ifdef NES_ENABLE_PPU
if (addr == address{ 0x4014 }) { return ppu_.read_latch(); }
#else
if (addr == address{ 0x4014 }) { return 0x0; }
@@ -1319,7 +1318,7 @@ namespace nes
if (addr <= address{ 0x1FFF }) { ram_[addr.get_absolute() % ram_size] = value; return; }
if (addr <= address{ 0x3FFF })
{
-#ifdef ENABLE_PPU
+#ifdef NES_ENABLE_PPU
switch (addr.get_absolute() % 8)
{
case 0: ppu_.write_ppuctrl(value); return;
@@ -1337,7 +1336,7 @@ namespace nes
#endif
}
if (addr <= address{ 0x4013 }) { return; } // TODO: APU registers
-#ifdef ENABLE_PPU
+#ifdef NES_ENABLE_PPU
if (addr == address{ 0x4014 }) { ppu_.write_oamdma(value); return; }
#else
if (addr == address{ 0x4014 }) { return; }
diff --git a/src/nes/cpu.hh b/lib/nes/cpu.hh
index fc3ef5b..9511c09 100644
--- a/src/nes/cpu.hh
+++ b/lib/nes/cpu.hh
@@ -1,14 +1,14 @@
#pragma once
-#include "nes/util/cycle_count.hh"
+#include "nes/util/cycle-count.hh"
#include "nes/util/address.hh"
-#include "status.hh"
namespace nes
{
class ppu;
class controller;
class mapper;
+ struct snapshot;
class cpu
{
@@ -110,13 +110,11 @@ namespace nes
template<>
class operand<addressing_mode::immediate>
{
- cpu& cpu_;
std::uint8_t value_{ 0 };
public:
- explicit operand(cpu& cpu, std::uint8_t const value)
- : cpu_{ cpu }
- , value_{ value }
+ explicit operand(std::uint8_t const value)
+ : value_{ value }
{
}
@@ -132,9 +130,8 @@ namespace nes
auto operator=(cpu const&) -> cpu& = delete;
auto operator=(cpu&&) -> cpu& = delete;
- auto snapshot(test::status&) -> void;
-
auto get_cycles() const -> cycle_count { return current_cycles_; }
+ auto build_snapshot(snapshot&) -> void;
auto stall_cycles(cycle_count) -> void;
auto trigger_nmi() -> void;
auto step() -> void;
diff --git a/src/nes/display.cc b/lib/nes/display.cc
index 4ff1d5f..4ff1d5f 100644
--- a/src/nes/display.cc
+++ b/lib/nes/display.cc
diff --git a/src/nes/display.hh b/lib/nes/display.hh
index 14ccff1..14ccff1 100644
--- a/src/nes/display.hh
+++ b/lib/nes/display.hh
diff --git a/src/nes/mapper.cc b/lib/nes/mapper.cc
index 5392d87..9346be7 100644
--- a/src/nes/mapper.cc
+++ b/lib/nes/mapper.cc
@@ -16,11 +16,6 @@ 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())
diff --git a/src/nes/mapper.hh b/lib/nes/mapper.hh
index 12ad804..f2873a6 100644
--- a/src/nes/mapper.hh
+++ b/lib/nes/mapper.hh
@@ -1,7 +1,6 @@
#pragma once
#include "nes/util/address.hh"
-#include "status.hh"
namespace nes
{
@@ -28,8 +27,6 @@ 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/lib/nes/nes.cc
index 7ac1887..01e339d 100644
--- a/src/nes/nes.cc
+++ b/lib/nes/nes.cc
@@ -2,6 +2,7 @@
#include "nes/mapper.hh"
#include "nes/display.hh"
#include "nes/util/rgb.hh"
+#include "nes/util/snapshot.hh"
#include <iostream>
namespace nes
@@ -62,13 +63,12 @@ namespace nes
}
}
- auto nes::snapshot() -> test::status
+ auto nes::get_snapshot() -> snapshot
{
- auto res = test::status{};
+ auto res = snapshot{};
res.sram = std::vector(cartridge_.get_ram(), cartridge_.get_ram() + cartridge_.get_ram_length());
- cpu_.snapshot(res);
- ppu_.snapshot(res);
- mapper_.snapshot(res);
+ cpu_.build_snapshot(res);
+ ppu_.build_snapshot(res);
return res;
}
} // namespace nes \ No newline at end of file
diff --git a/src/nes/nes.hh b/lib/nes/nes.hh
index 76c29ab..fe57cd4 100644
--- a/src/nes/nes.hh
+++ b/lib/nes/nes.hh
@@ -1,16 +1,16 @@
#pragma once
-#include "nes/util/cycle_count.hh"
+#include "nes/util/cycle-count.hh"
#include "nes/cartridge.hh"
#include "nes/controller.hh"
#include "nes/cpu.hh"
#include "nes/ppu.hh"
#include "nes/mapper.hh"
-#include "status.hh"
namespace nes
{
class display;
+ struct snapshot;
/// The main console abstraction.
class nes
@@ -36,12 +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<test::memory_operation>& { 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;
+ auto get_snapshot() -> snapshot;
};
} // namespace nes \ No newline at end of file
diff --git a/src/nes/ppu.cc b/lib/nes/ppu.cc
index 87c7f8d..3170de9 100644
--- a/src/nes/ppu.cc
+++ b/lib/nes/ppu.cc
@@ -4,9 +4,8 @@
#include "nes/mapper.hh"
#include "nes/util/address.hh"
#include "nes/util/rgb.hh"
-#include <iostream>
-
-// #define ENABLE_TRACES
+#include "nes/util/snapshot.hh"
+#include "nes/util/debug.hh"
namespace nes
{
@@ -17,7 +16,7 @@ namespace nes
{
}
- auto ppu::snapshot(test::status& snapshot) -> void
+ auto ppu::build_snapshot(snapshot& snapshot) -> void
{
snapshot.vram = std::vector(std::begin(vram_), std::end(vram_));
snapshot.oam = std::vector(std::begin(oam_), std::end(oam_));
@@ -137,7 +136,6 @@ namespace nes
if (pre_line && scanline_cycle_ == 1)
{
status_.vblank = false;
- nmi_change();
status_.sprite_zero_hit = false;
status_.sprite_overflow = false;
}
@@ -461,13 +459,10 @@ 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
+ NES_DEBUG_LOG(ppu, "PPUSTATUS -> {:#2x}", res);
status_.vblank = false;
internal_.w = false;
- nmi_change();
write_latch(res);
return res;
@@ -480,9 +475,7 @@ namespace nes
{
res &= 0xE3;
}
-#ifdef ENABLE_TRACES
- memory_operations.emplace_back(test::memory_operation::type::read_oamdata, res, current_cycles_);
-#endif
+ NES_DEBUG_LOG(ppu, "OAMDATA -> {:#2x}", res);
write_latch(res);
return res;
}
@@ -503,9 +496,7 @@ namespace nes
ppudata_read_buffer_ = read8(addr - 0x1000);
}
-#ifdef ENABLE_TRACES
- memory_operations.emplace_back(test::memory_operation::type::read_ppudata, res, current_cycles_);
-#endif
+ NES_DEBUG_LOG(ppu, "PPUDATA -> {:#2x}", res);
increment_vram();
return res;
}
@@ -517,26 +508,22 @@ namespace nes
auto ppu::write_ppuctrl(std::uint8_t const value) -> void
{
+ write_latch(value);
if (current_cycles_ > boot_up_cycles)
{
- write_latch(value);
+ NES_DEBUG_LOG(ppu, "PPUCTRL <- {:#2x}", 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
+ NES_DEBUG_LOG(ppu, "PPUSCROLL <- {:#2x}", value);
if (!internal_.w)
{
// First write -> x value.
@@ -556,24 +543,20 @@ 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
+ NES_DEBUG_LOG(ppu, "PPUMASK <- {:#2x}", value);
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
+ NES_DEBUG_LOG(ppu, "PPUADDR <- {:#2x}", value);
if (!internal_.w)
{
// First write.
@@ -592,9 +575,7 @@ 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
+ NES_DEBUG_LOG(ppu, "PPUDATA <- {:#2x} (address: {:#4x})", value, internal_.v);
write_latch(value);
write8(address{ internal_.v }, value);
increment_vram();
@@ -602,18 +583,14 @@ 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
+ NES_DEBUG_LOG(ppu, "OAMADDR <- {:#2x}", value);
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
+ NES_DEBUG_LOG(ppu, "OAMDATA <- {:#2x}", value);
write_latch(value);
oam_[oamaddr_] = value;
oamaddr_ += 1;
@@ -622,9 +599,7 @@ 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?
-#ifdef ENABLE_TRACES
- memory_operations.emplace_back(test::memory_operation::type::write_oamdma, value, current_cycles_);
-#endif
+ NES_DEBUG_LOG(ppu, "OAMDMA <- {:#2x}", value);
auto addr = address{ value, 0x00 };
for (auto i = unsigned{ 0 }; i < 256; ++i)
{
@@ -643,8 +618,7 @@ namespace nes
auto ppu::nmi_change() -> void
{
- auto const should_trigger = control_.vblank_nmi && status_.vblank;
- if (should_trigger)
+ if (control_.vblank_nmi && status_.vblank)
{
cpu_.trigger_nmi();
}
diff --git a/src/nes/ppu.hh b/lib/nes/ppu.hh
index fff51de..d7d9baa 100644
--- a/src/nes/ppu.hh
+++ b/lib/nes/ppu.hh
@@ -1,7 +1,6 @@
#pragma once
-#include "nes/util/cycle_count.hh"
-#include "status.hh"
+#include "nes/util/cycle-count.hh"
namespace nes
{
@@ -10,6 +9,7 @@ namespace nes
class mapper;
class display;
class rgb;
+ struct snapshot;
class ppu
{
@@ -148,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_.
- bool nmi_requested_{ false };
public:
explicit ppu(cpu&, mapper&, display&);
@@ -158,10 +157,8 @@ namespace nes
auto operator=(ppu const&) -> ppu& = delete;
auto operator=(ppu&&) -> ppu& = delete;
- std::vector<test::memory_operation> memory_operations;
-
auto get_cycles() const -> cycle_count { return current_cycles_; }
- auto snapshot(test::status&) -> void;
+ auto build_snapshot(snapshot&) -> void;
auto step() -> void;
// Memory access
diff --git a/lib/nes/ref/CMakeLists.txt b/lib/nes/ref/CMakeLists.txt
new file mode 100644
index 0000000..ab8e74d
--- /dev/null
+++ b/lib/nes/ref/CMakeLists.txt
@@ -0,0 +1,4 @@
+target_sources(
+ nes_ref
+ PRIVATE
+ test.cc) \ No newline at end of file
diff --git a/lib/nes/ref/test.cc b/lib/nes/ref/test.cc
new file mode 100644
index 0000000..210ece0
--- /dev/null
+++ b/lib/nes/ref/test.cc
@@ -0,0 +1,3 @@
+//
+// Created by Hannes Schulze on 23.01.25.
+//
diff --git a/src/nes/util/CMakeLists.txt b/lib/nes/util/CMakeLists.txt
index adaa80c..0fdb0ba 100644
--- a/src/nes/util/CMakeLists.txt
+++ b/lib/nes/util/CMakeLists.txt
@@ -3,5 +3,7 @@ target_sources(
PRIVATE
address.hh
button-mask.hh
- cycle_count.hh
- rgb.hh) \ No newline at end of file
+ cycle-count.hh
+ debug.hh
+ rgb.hh
+ snapshot.hh) \ No newline at end of file
diff --git a/src/nes/util/address.hh b/lib/nes/util/address.hh
index 7987d07..7987d07 100644
--- a/src/nes/util/address.hh
+++ b/lib/nes/util/address.hh
diff --git a/src/nes/util/button-mask.hh b/lib/nes/util/button-mask.hh
index b010b9e..b010b9e 100644
--- a/src/nes/util/button-mask.hh
+++ b/lib/nes/util/button-mask.hh
diff --git a/src/nes/util/cycle_count.hh b/lib/nes/util/cycle-count.hh
index 8afd40a..f1a4a7e 100644
--- a/src/nes/util/cycle_count.hh
+++ b/lib/nes/util/cycle-count.hh
@@ -35,7 +35,7 @@ namespace nes
static constexpr auto from_duration(std::chrono::microseconds const duration) -> cycle_count
{
- return from_units(duration.count() * second().get_units() / (1000 * 1000));
+ return from_units(static_cast<std::uint64_t>(duration.count()) * second().get_units() / (1000 * 1000));
}
static constexpr auto from_cpu(std::uint64_t const cpu_cycles) -> cycle_count
diff --git a/lib/nes/util/debug.hh b/lib/nes/util/debug.hh
new file mode 100644
index 0000000..409d55a
--- /dev/null
+++ b/lib/nes/util/debug.hh
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifdef NES_ENABLE_DEBUG_OUTPUT
+
+#include <spdlog/spdlog.h>
+
+#define NES_DEBUG_LOG(name, ...) \
+ spdlog::info("[" #name "] " __VA_ARGS__)
+
+#else
+
+#define NES_DEBUG_LOG(name, fmt, ...) \
+ (void)0
+
+#endif \ No newline at end of file
diff --git a/src/nes/util/rgb.hh b/lib/nes/util/rgb.hh
index d62c70c..d62c70c 100644
--- a/src/nes/util/rgb.hh
+++ b/lib/nes/util/rgb.hh
diff --git a/lib/nes/util/snapshot.hh b/lib/nes/util/snapshot.hh
new file mode 100644
index 0000000..de23a46
--- /dev/null
+++ b/lib/nes/util/snapshot.hh
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "nes/util/cycle-count.hh"
+#include <vector>
+#include <cstdint>
+
+namespace nes
+{
+ /// System snapshot used for comparison with a reference implementation.
+ struct snapshot
+ {
+ explicit snapshot() = default;
+
+ cycle_count cpu_cycle;
+ cycle_count ppu_cycle;
+ std::vector<std::uint8_t> ram;
+ std::vector<std::uint8_t> sram;
+ std::vector<std::uint8_t> vram;
+ std::vector<std::uint8_t> oam;
+ struct
+ {
+ std::uint16_t pc{};
+ std::uint8_t sp{};
+ std::uint8_t a{};
+ std::uint8_t x{};
+ std::uint8_t y{};
+ std::uint8_t p{};
+ bool c{};
+ bool z{};
+ bool i{};
+ bool d{};
+ bool b{};
+ bool v{};
+ bool n{};
+ } registers{};
+ };
+
+ inline auto operator==(snapshot const& a, snapshot const& b) -> bool
+ {
+ if (a.cpu_cycle != b.cpu_cycle) { return false; }
+ if (a.ppu_cycle != b.ppu_cycle) { return false; }
+ if (a.ram != b.ram) { return false; }
+ if (a.sram != b.sram) { return false; }
+ if (a.vram != b.vram) { return false; }
+ if (a.oam != b.oam) { return false; }
+ if (a.registers.pc != b.registers.pc) { return false; }
+ if (a.registers.sp != b.registers.sp) { return false; }
+ if (a.registers.a != b.registers.a) { return false; }
+ if (a.registers.x != b.registers.x) { return false; }
+ if (a.registers.y != b.registers.y) { return false; }
+ if (a.registers.p != b.registers.p) { return false; }
+ if (a.registers.c != b.registers.c) { return false; }
+ if (a.registers.z != b.registers.z) { return false; }
+ if (a.registers.i != b.registers.i) { return false; }
+ if (a.registers.d != b.registers.d) { return false; }
+ if (a.registers.b != b.registers.b) { return false; }
+ if (a.registers.v != b.registers.v) { return false; }
+ if (a.registers.n != b.registers.n) { return false; }
+ return true;
+ }
+
+ inline auto operator!=(snapshot const& a, snapshot const& b) -> bool { return !(a == b); }
+} // namespace nes \ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644
index b997a1d..0000000
--- a/src/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-add_subdirectory(nes)
-#add_subdirectory(ref)
-#add_subdirectory(test)
-
-target_sources(
- nes
- PRIVATE
- main.mm
- status.hh
- scene.hh
- scene.mm)
-
-target_include_directories(nes PRIVATE .) \ No newline at end of file
diff --git a/src/nes/CMakeLists.txt b/src/nes/CMakeLists.txt
deleted file mode 100644
index fcdcb07..0000000
--- a/src/nes/CMakeLists.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-add_subdirectory(util)
-
-target_sources(
- nes
- PRIVATE
- nes.hh
- nes.cc
- cartridge.hh
- cartridge.cc
- display.hh
- display.cc
- controller.hh
- controller.cc
- cpu.hh
- cpu.cc
- ppu.hh
- ppu.cc
- mapper.hh
- mapper.cc) \ No newline at end of file
diff --git a/src/status.hh b/src/status.hh
deleted file mode 100644
index b56be58..0000000
--- a/src/status.hh
+++ /dev/null
@@ -1,97 +0,0 @@
-#pragma once
-
-#include "nes/util/cycle_count.hh"
-#include <vector>
-#include <cstdint>
-
-namespace test
-{
- /// System status used for comparison.
- struct status
- {
- explicit status() = default;
-
- nes::cycle_count cpu_cycle;
- std::vector<std::uint8_t> ram;
- std::vector<std::uint8_t> sram;
- std::vector<std::uint8_t> vram;
- std::vector<std::uint8_t> oam;
- std::vector<std::uint8_t> chrrom;
- struct
- {
- std::uint16_t pc{};
- std::uint8_t sp{};
- std::uint8_t a{};
- std::uint8_t x{};
- std::uint8_t y{};
- std::uint8_t p{};
- bool c{};
- bool z{};
- bool i{};
- bool d{};
- bool b{};
- bool v{};
- bool n{};
- } registers{};
-
- static auto equal(status const& a, status const& b) -> bool
- {
- if (a.cpu_cycle != b.cpu_cycle) { return false; }
- if (a.ram != b.ram) { return false; }
- if (a.sram != b.sram) { return false; }
- if (a.registers.pc != b.registers.pc) { return false; }
- if (a.registers.sp != b.registers.sp) { return false; }
- if (a.registers.a != b.registers.a) { return false; }
- if (a.registers.x != b.registers.x) { return false; }
- if (a.registers.y != b.registers.y) { return false; }
- if (a.registers.p != b.registers.p) { return false; }
- if (a.registers.c != b.registers.c) { return false; }
- if (a.registers.z != b.registers.z) { return false; }
- if (a.registers.i != b.registers.i) { return false; }
- if (a.registers.d != b.registers.d) { return false; }
- if (a.registers.b != b.registers.b) { return false; }
- if (a.registers.v != b.registers.v) { return false; }
- if (a.registers.n != b.registers.n) { return false; }
- 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
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
deleted file mode 100644
index e69de29..0000000
--- a/src/test/CMakeLists.txt
+++ /dev/null
diff --git a/src/test/main.cc b/src/test/main.cc
deleted file mode 100644
index 58e618d..0000000
--- a/src/test/main.cc
+++ /dev/null
@@ -1,3 +0,0 @@
-//
-// Created by Hannes Schulze on 17.01.25.
-//