summaryrefslogtreecommitdiff
path: root/src/nes
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-01-23 22:02:11 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-01-23 22:02:11 +0100
commitb1d1573702f21e3104606d6ade5f5d39ec86a33a (patch)
treed703be130a5d6f9ff2626313d3380ed34de44dd7 /src/nes
parent6512dd599d13035f6a90b2009344e02c488f5aae (diff)
restructure project
Diffstat (limited to 'src/nes')
-rw-r--r--src/nes/CMakeLists.txt19
-rw-r--r--src/nes/cartridge.cc70
-rw-r--r--src/nes/cartridge.hh41
-rw-r--r--src/nes/controller.cc18
-rw-r--r--src/nes/controller.hh30
-rw-r--r--src/nes/cpu.cc1359
-rw-r--r--src/nes/cpu.hh297
-rw-r--r--src/nes/display.cc16
-rw-r--r--src/nes/display.hh33
-rw-r--r--src/nes/mapper.cc139
-rw-r--r--src/nes/mapper.hh67
-rw-r--r--src/nes/nes.cc74
-rw-r--r--src/nes/nes.hh47
-rw-r--r--src/nes/ppu.cc763
-rw-r--r--src/nes/ppu.hh211
-rw-r--r--src/nes/util/CMakeLists.txt7
-rw-r--r--src/nes/util/address.hh73
-rw-r--r--src/nes/util/button-mask.hh43
-rw-r--r--src/nes/util/cycle_count.hh87
-rw-r--r--src/nes/util/rgb.hh33
20 files changed, 0 insertions, 3427 deletions
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/nes/cartridge.cc b/src/nes/cartridge.cc
deleted file mode 100644
index 861dc13..0000000
--- a/src/nes/cartridge.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "nes/cartridge.hh"
-#include <fstream>
-
-namespace nes
-{
- namespace
- {
- constexpr auto prg_rom_bank_size = std::size_t{ 16 * 1024 };
- constexpr auto chr_rom_bank_size = std::size_t{ 8 * 1024 };
- constexpr auto ram_bank_size = std::size_t{ 8 * 1024 };
- } // namespace
-
- auto cartridge::from_data(void const* data, std::size_t const length) -> cartridge
- {
- // See: https://www.nesdev.org/wiki/INES
-
- auto const bytes = static_cast<std::uint8_t const*>(data);
- auto res = cartridge{};
- res.is_valid_ = true;
-
- // Parse header.
- if (length < 16) { return cartridge{}; }
- if (bytes[0] != 'N' || bytes[1] != 'E' || bytes[2] != 'S' || bytes[3] != 0x1A) { return cartridge{}; }
- auto const prg_rom_banks = bytes[4];
- auto const chr_rom_banks = bytes[5];
- auto const control_1 = bytes[6];
- auto const control_2 = bytes[7];
- auto const ram_banks = (bytes[8] != 0) ? bytes[8] : 1;
- auto const has_trainer = (control_1 & 0b00000100) != 0;
-
- res.mapper_number_ =
- (control_1 & 0b11110000) >> 4 |
- (control_2 & 0b11110000) >> 0;
- res.name_table_arrangement_ = (control_1 & 0b00000001) != 0
- ? name_table_arrangement::vertical
- : name_table_arrangement::horizontal;
-
- // Load program data.
- auto offset = std::size_t{ 16 };
- if (has_trainer) { offset += 512; }
-
- auto const prg_rom_size = prg_rom_banks * prg_rom_bank_size;
- if (length < offset + prg_rom_size) { return cartridge{}; }
- res.prg_rom_ = std::vector(&bytes[offset], &bytes[offset + prg_rom_size]);
-
- offset += prg_rom_size;
-
- auto const chr_rom_size = chr_rom_banks * chr_rom_bank_size;
- if (length < offset + chr_rom_size) { return cartridge{}; }
- res.chr_rom_ = std::vector(&bytes[offset], &bytes[offset + chr_rom_size]);
-
- res.ram_ = std::vector(ram_banks * ram_bank_size, std::uint8_t{ 0 });
-
- return res;
- }
-
- auto cartridge::from_file(std::string_view const path) -> cartridge
- {
- auto stream = std::ifstream{ path, std::ios::binary };
- if (!stream.good()) { return cartridge{}; }
-
- stream.seekg(0, std::ios::end);
- auto const length = stream.tellg();
- stream.seekg(0, std::ios::beg);
- auto data = std::vector<std::uint8_t>(length);
- stream.read(reinterpret_cast<char*>(data.data()), data.size());
-
- return from_data(data.data(), data.size());
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/cartridge.hh b/src/nes/cartridge.hh
deleted file mode 100644
index 3ce2a16..0000000
--- a/src/nes/cartridge.hh
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <vector>
-#include <string_view>
-
-namespace nes
-{
- enum class name_table_arrangement
- {
- horizontal,
- vertical,
- };
-
- class cartridge
- {
- bool is_valid_{ false };
- std::vector<std::uint8_t> prg_rom_;
- std::vector<std::uint8_t> chr_rom_;
- std::vector<std::uint8_t> ram_;
- std::uint8_t mapper_number_{ 0 };
- name_table_arrangement name_table_arrangement_{};
-
- public:
- explicit cartridge() = default;
-
- static auto from_data(void const* data, std::size_t length) -> cartridge;
- static auto from_file(std::string_view path) -> cartridge;
-
- auto is_valid() const -> bool { return is_valid_; }
- auto get_prg_rom() const -> std::uint8_t const* { return prg_rom_.data(); }
- auto get_prg_rom_length() const -> std::size_t { return prg_rom_.size(); }
- auto get_chr_rom() const -> std::uint8_t const* { return chr_rom_.data(); }
- auto get_chr_rom_length() const -> std::size_t { return chr_rom_.size(); }
- auto get_ram() const -> std::uint8_t const* { return ram_.data(); }
- auto get_ram_mut() -> std::uint8_t* { return ram_.data(); }
- auto get_ram_length() const -> std::size_t { return ram_.size(); }
- auto get_mapper_number() const -> std::uint8_t { return mapper_number_; }
- auto get_name_table_arrangement() const -> name_table_arrangement { return name_table_arrangement_; }
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/controller.cc b/src/nes/controller.cc
deleted file mode 100644
index 87842e2..0000000
--- a/src/nes/controller.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "nes/controller.hh"
-
-namespace nes
-{
- auto controller::read() -> std::uint8_t
- {
- auto const res = index_ < 8 ? (pressed_.get_raw_value() >> index_) & 1 : 0;
- index_ += 1;
- if (strobing_) { index_ = 0; }
- return res;
- }
-
- auto controller::write(std::uint8_t const value) -> void
- {
- strobing_ = value & 1;
- if (strobing_) { index_ = 0; }
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/controller.hh b/src/nes/controller.hh
deleted file mode 100644
index fa16859..0000000
--- a/src/nes/controller.hh
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include "nes/util/button-mask.hh"
-
-namespace nes
-{
- class controller
- {
- button_mask pressed_;
- unsigned index_{ 0 };
- bool strobing_{ false };
-
- public:
- explicit controller() = default;
-
- controller(controller const&) = delete;
- controller(controller&&) = delete;
- auto operator=(controller const&) -> controller& = delete;
- auto operator=(controller&&) -> controller& = delete;
-
- auto get_pressed() const -> button_mask { return pressed_;}
- auto get_pressed() -> button_mask& { return pressed_; }
- auto set_pressed(button_mask const value) -> void { pressed_ = value; }
-
- // IO register
-
- auto read() -> std::uint8_t;
- auto write(std::uint8_t) -> void;
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/cpu.cc b/src/nes/cpu.cc
deleted file mode 100644
index 9060e72..0000000
--- a/src/nes/cpu.cc
+++ /dev/null
@@ -1,1359 +0,0 @@
-#include "nes/cpu.hh"
-#include "nes/ppu.hh"
-#include "nes/controller.hh"
-#include "nes/mapper.hh"
-
-#define ENABLE_PPU
-
-namespace nes
-{
- cpu::cpu(ppu& ppu, mapper& mapper, controller& controller_1, controller& controller_2)
- : ppu_{ ppu }
- , mapper_{ mapper }
- , controller_1_{ controller_1 }
- , controller_2_{ controller_2 }
- {
- registers_.pc = read16(address{ 0xFFFC });
- }
-
- auto cpu::snapshot(test::status& snapshot) -> void
- {
- snapshot.cpu_cycle = current_cycles_;
- snapshot.ram = std::vector(std::begin(ram_), std::end(ram_));
- snapshot.registers.pc = registers_.pc;
- snapshot.registers.sp = registers_.sp;
- 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;
- }
-
- auto cpu::stall_cycles(cycle_count const count) -> void
- {
- current_cycles_ += count;
- }
-
- auto cpu::trigger_nmi() -> void
- {
- nmi_pending_ = true;
- }
-
- auto cpu::step() -> void
- {
- // See https://www.nesdev.org/wiki/CPU_unofficial_opcodes
-
- if (nmi_pending_)
- {
-#ifdef ENABLE_PPU
- execute_interrupt(address{ 0xFFFA });
-#endif
- nmi_pending_ = false;
- }
-
- auto const opcode = advance_pc8();
- switch (opcode)
- {
- case 0x00: run_brk<addressing_mode::immediate>(); break;
- case 0x01: run_ora<addressing_mode::indexed_indirect>(); break;
- case 0x02: run_stp(); break;
- case 0x03: run_slo<addressing_mode::indexed_indirect>(); break;
- case 0x04: run_nop<addressing_mode::zero_page>(); break;
- case 0x05: run_ora<addressing_mode::zero_page>(); break;
- case 0x06: run_asl<addressing_mode::zero_page>(); break;
- case 0x07: run_slo<addressing_mode::zero_page>(); break;
- case 0x08: run_php(); break;
- case 0x09: run_ora<addressing_mode::immediate>(); break;
- case 0x0A: run_asl<addressing_mode::accumulator>(); break;
- case 0x0B: run_anc<addressing_mode::immediate>(); break;
- case 0x0C: run_nop<addressing_mode::absolute>(); break;
- case 0x0D: run_ora<addressing_mode::absolute>(); break;
- case 0x0E: run_asl<addressing_mode::absolute>(); break;
- case 0x0F: run_slo<addressing_mode::absolute>(); break;
- case 0x10: run_bpl<addressing_mode::relative>(); break;
- case 0x11: run_ora<addressing_mode::indirect_indexed>(); break;
- case 0x12: run_stp(); break;
- case 0x13: run_slo<addressing_mode::indirect_indexed>(); break;
- case 0x14: run_nop<addressing_mode::zero_page_indexed_x>(); break;
- case 0x15: run_ora<addressing_mode::zero_page_indexed_x>(); break;
- case 0x16: run_asl<addressing_mode::zero_page_indexed_x>(); break;
- case 0x17: run_slo<addressing_mode::zero_page_indexed_x>(); break;
- case 0x18: run_clc(); break;
- case 0x19: run_ora<addressing_mode::absolute_indexed_y>(); break;
- case 0x1A: run_nop(); break;
- case 0x1B: run_slo<addressing_mode::absolute_indexed_y>(); break;
- case 0x1C: run_nop<addressing_mode::absolute_indexed_x>(); break;
- case 0x1D: run_ora<addressing_mode::absolute_indexed_x>(); break;
- case 0x1E: run_asl<addressing_mode::absolute_indexed_x>(); break;
- case 0x1F: run_slo<addressing_mode::absolute_indexed_x>(); break;
- case 0x20: run_jsr<addressing_mode::absolute>(); break;
- case 0x21: run_and<addressing_mode::indexed_indirect>(); break;
- case 0x22: run_stp(); break;
- case 0x23: run_rla<addressing_mode::indexed_indirect>(); break;
- case 0x24: run_bit<addressing_mode::zero_page>(); break;
- case 0x25: run_and<addressing_mode::zero_page>(); break;
- case 0x26: run_rol<addressing_mode::zero_page>(); break;
- case 0x27: run_rla<addressing_mode::zero_page>(); break;
- case 0x28: run_plp(); break;
- case 0x29: run_and<addressing_mode::immediate>(); break;
- case 0x2A: run_rol<addressing_mode::accumulator>(); break;
- case 0x2B: run_anc<addressing_mode::immediate>(); break;
- case 0x2C: run_bit<addressing_mode::absolute>(); break;
- case 0x2D: run_and<addressing_mode::absolute>(); break;
- case 0x2E: run_rol<addressing_mode::absolute>(); break;
- case 0x2F: run_rla<addressing_mode::absolute>(); break;
- case 0x30: run_bmi<addressing_mode::relative>(); break;
- case 0x31: run_and<addressing_mode::indirect_indexed>(); break;
- case 0x32: run_stp(); break;
- case 0x33: run_rla<addressing_mode::indirect_indexed>(); break;
- case 0x34: run_nop<addressing_mode::zero_page_indexed_x>(); break;
- case 0x35: run_and<addressing_mode::zero_page_indexed_x>(); break;
- case 0x36: run_rol<addressing_mode::zero_page_indexed_x>(); break;
- case 0x37: run_rla<addressing_mode::zero_page_indexed_x>(); break;
- case 0x38: run_sec(); break;
- case 0x39: run_and<addressing_mode::absolute_indexed_y>(); break;
- case 0x3A: run_nop(); break;
- case 0x3B: run_rla<addressing_mode::absolute_indexed_y>(); break;
- case 0x3C: run_nop<addressing_mode::absolute_indexed_x>(); break;
- case 0x3D: run_and<addressing_mode::absolute_indexed_x>(); break;
- case 0x3E: run_rol<addressing_mode::absolute_indexed_x>(); break;
- case 0x3F: run_rla<addressing_mode::absolute_indexed_x>(); break;
- case 0x40: run_rti(); break;
- case 0x41: run_eor<addressing_mode::indexed_indirect>(); break;
- case 0x42: run_stp(); break;
- case 0x43: run_sre<addressing_mode::indexed_indirect>(); break;
- case 0x44: run_nop<addressing_mode::zero_page>(); break;
- case 0x45: run_eor<addressing_mode::zero_page>(); break;
- case 0x46: run_lsr<addressing_mode::zero_page>(); break;
- case 0x47: run_sre<addressing_mode::zero_page>(); break;
- case 0x48: run_pha(); break;
- case 0x49: run_eor<addressing_mode::immediate>(); break;
- case 0x4A: run_lsr<addressing_mode::accumulator>(); break;
- case 0x4B: run_alr<addressing_mode::immediate>(); break;
- case 0x4C: run_jmp<addressing_mode::absolute>(); break;
- case 0x4D: run_eor<addressing_mode::absolute>(); break;
- case 0x4E: run_lsr<addressing_mode::absolute>(); break;
- case 0x4F: run_sre<addressing_mode::absolute>(); break;
- case 0x50: run_bvc<addressing_mode::relative>(); break;
- case 0x51: run_eor<addressing_mode::indirect_indexed>(); break;
- case 0x52: run_stp(); break;
- case 0x53: run_sre<addressing_mode::indirect_indexed>(); break;
- case 0x54: run_nop<addressing_mode::zero_page_indexed_x>(); break;
- case 0x55: run_eor<addressing_mode::zero_page_indexed_x>(); break;
- case 0x56: run_lsr<addressing_mode::zero_page_indexed_x>(); break;
- case 0x57: run_sre<addressing_mode::zero_page_indexed_x>(); break;
- case 0x58: run_cli(); break;
- case 0x59: run_eor<addressing_mode::absolute_indexed_y>(); break;
- case 0x5A: run_nop(); break;
- case 0x5B: run_sre<addressing_mode::absolute_indexed_y>(); break;
- case 0x5C: run_nop<addressing_mode::absolute_indexed_x>(); break;
- case 0x5D: run_eor<addressing_mode::absolute_indexed_x>(); break;
- case 0x5E: run_lsr<addressing_mode::absolute_indexed_x>(); break;
- case 0x5F: run_sre<addressing_mode::absolute_indexed_x>(); break;
- case 0x60: run_rts(); break;
- case 0x61: run_adc<addressing_mode::indexed_indirect>(); break;
- case 0x62: run_stp(); break;
- case 0x63: run_rra<addressing_mode::indexed_indirect>(); break;
- case 0x64: run_nop<addressing_mode::zero_page>(); break;
- case 0x65: run_adc<addressing_mode::zero_page>(); break;
- case 0x66: run_ror<addressing_mode::zero_page>(); break;
- case 0x67: run_rra<addressing_mode::zero_page>(); break;
- case 0x68: run_pla(); break;
- case 0x69: run_adc<addressing_mode::immediate>(); break;
- case 0x6A: run_ror<addressing_mode::accumulator>(); break;
- case 0x6B: run_arr<addressing_mode::immediate>(); break;
- case 0x6C: run_jmp<addressing_mode::indirect>(); break;
- case 0x6D: run_adc<addressing_mode::absolute>(); break;
- case 0x6E: run_ror<addressing_mode::absolute>(); break;
- case 0x6F: run_rra<addressing_mode::absolute>(); break;
- case 0x70: run_bvs<addressing_mode::relative>(); break;
- case 0x71: run_adc<addressing_mode::indirect_indexed>(); break;
- case 0x72: run_stp(); break;
- case 0x73: run_rra<addressing_mode::indirect_indexed>(); break;
- case 0x74: run_nop<addressing_mode::zero_page_indexed_x>(); break;
- case 0x75: run_adc<addressing_mode::zero_page_indexed_x>(); break;
- case 0x76: run_ror<addressing_mode::zero_page_indexed_x>(); break;
- case 0x77: run_rra<addressing_mode::zero_page_indexed_x>(); break;
- case 0x78: run_sei(); break;
- case 0x79: run_adc<addressing_mode::absolute_indexed_y>(); break;
- case 0x7A: run_nop(); break;
- case 0x7B: run_rra<addressing_mode::absolute_indexed_y>(); break;
- case 0x7C: run_nop<addressing_mode::absolute_indexed_x>(); break;
- case 0x7D: run_adc<addressing_mode::absolute_indexed_x>(); break;
- case 0x7E: run_ror<addressing_mode::absolute_indexed_x>(); break;
- case 0x7F: run_rra<addressing_mode::absolute_indexed_x>(); break;
- case 0x80: run_nop<addressing_mode::immediate>(); break;
- case 0x81: run_sta<addressing_mode::indexed_indirect>(); break;
- case 0x82: run_nop<addressing_mode::immediate>(); break;
- case 0x83: run_sax<addressing_mode::indexed_indirect>(); break;
- case 0x84: run_sty<addressing_mode::zero_page>(); break;
- case 0x85: run_sta<addressing_mode::zero_page>(); break;
- case 0x86: run_stx<addressing_mode::zero_page>(); break;
- case 0x87: run_sax<addressing_mode::zero_page>(); break;
- case 0x88: run_dey(); break;
- case 0x89: run_nop<addressing_mode::immediate>(); break;
- case 0x8A: run_txa(); break;
- case 0x8B: run_xaa<addressing_mode::immediate>(); break;
- case 0x8C: run_sty<addressing_mode::absolute>(); break;
- case 0x8D: run_sta<addressing_mode::absolute>(); break;
- case 0x8E: run_stx<addressing_mode::absolute>(); break;
- case 0x8F: run_sax<addressing_mode::absolute>(); break;
- case 0x90: run_bcc<addressing_mode::relative>(); break;
- case 0x91: run_sta<addressing_mode::indirect_indexed>(); break;
- case 0x92: run_stp(); break;
- case 0x93: run_ahx<addressing_mode::indirect_indexed>(); break;
- case 0x94: run_sty<addressing_mode::zero_page_indexed_x>(); break;
- case 0x95: run_sta<addressing_mode::zero_page_indexed_x>(); break;
- case 0x96: run_stx<addressing_mode::zero_page_indexed_y>(); break;
- case 0x97: run_sax<addressing_mode::zero_page_indexed_y>(); break;
- case 0x98: run_tya(); break;
- case 0x99: run_sta<addressing_mode::absolute_indexed_y>(); break;
- case 0x9A: run_txs(); break;
- case 0x9B: run_tas<addressing_mode::absolute_indexed_y>(); break;
- case 0x9C: run_shy<addressing_mode::absolute_indexed_x>(); break;
- case 0x9D: run_sta<addressing_mode::absolute_indexed_x>(); break;
- case 0x9E: run_shx<addressing_mode::absolute_indexed_y>(); break;
- case 0x9F: run_ahx<addressing_mode::absolute_indexed_y>(); break;
- case 0xA0: run_ldy<addressing_mode::immediate>(); break;
- case 0xA1: run_lda<addressing_mode::indexed_indirect>(); break;
- case 0xA2: run_ldx<addressing_mode::immediate>(); break;
- case 0xA3: run_lax<addressing_mode::indexed_indirect>(); break;
- case 0xA4: run_ldy<addressing_mode::zero_page>(); break;
- case 0xA5: run_lda<addressing_mode::zero_page>(); break;
- case 0xA6: run_ldx<addressing_mode::zero_page>(); break;
- case 0xA7: run_lax<addressing_mode::zero_page>(); break;
- case 0xA8: run_tay(); break;
- case 0xA9: run_lda<addressing_mode::immediate>(); break;
- case 0xAA: run_tax(); break;
- case 0xAB: run_lax<addressing_mode::immediate>(); break;
- case 0xAC: run_ldy<addressing_mode::absolute>(); break;
- case 0xAD: run_lda<addressing_mode::absolute>(); break;
- case 0xAE: run_ldx<addressing_mode::absolute>(); break;
- case 0xAF: run_lax<addressing_mode::absolute>(); break;
- case 0xB0: run_bcs<addressing_mode::relative>(); break;
- case 0xB1: run_lda<addressing_mode::indirect_indexed>(); break;
- case 0xB2: run_stp(); break;
- case 0xB3: run_lax<addressing_mode::indirect_indexed>(); break;
- case 0xB4: run_ldy<addressing_mode::zero_page_indexed_x>(); break;
- case 0xB5: run_lda<addressing_mode::zero_page_indexed_x>(); break;
- case 0xB6: run_ldx<addressing_mode::zero_page_indexed_y>(); break;
- case 0xB7: run_lax<addressing_mode::zero_page_indexed_y>(); break;
- case 0xB8: run_clv(); break;
- case 0xB9: run_lda<addressing_mode::absolute_indexed_y>(); break;
- case 0xBA: run_tsx(); break;
- case 0xBB: run_las<addressing_mode::absolute_indexed_y>(); break;
- case 0xBC: run_ldy<addressing_mode::absolute_indexed_x>(); break;
- case 0xBD: run_lda<addressing_mode::absolute_indexed_x>(); break;
- case 0xBE: run_ldx<addressing_mode::absolute_indexed_y>(); break;
- case 0xBF: run_lax<addressing_mode::absolute_indexed_y>(); break;
- case 0xC0: run_cpy<addressing_mode::immediate>(); break;
- case 0xC1: run_cmp<addressing_mode::indexed_indirect>(); break;
- case 0xC2: run_nop<addressing_mode::immediate>(); break;
- case 0xC3: run_dcp<addressing_mode::indexed_indirect>(); break;
- case 0xC4: run_cpy<addressing_mode::zero_page>(); break;
- case 0xC5: run_cmp<addressing_mode::zero_page>(); break;
- case 0xC6: run_dec<addressing_mode::zero_page>(); break;
- case 0xC7: run_dcp<addressing_mode::zero_page>(); break;
- case 0xC8: run_iny(); break;
- case 0xC9: run_cmp<addressing_mode::immediate>(); break;
- case 0xCA: run_dex(); break;
- case 0xCB: run_axs<addressing_mode::immediate>(); break;
- case 0xCC: run_cpy<addressing_mode::absolute>(); break;
- case 0xCD: run_cmp<addressing_mode::absolute>(); break;
- case 0xCE: run_dec<addressing_mode::absolute>(); break;
- case 0xCF: run_dcp<addressing_mode::absolute>(); break;
- case 0xD0: run_bne<addressing_mode::relative>(); break;
- case 0xD1: run_cmp<addressing_mode::indirect_indexed>(); break;
- case 0xD2: run_stp(); break;
- case 0xD3: run_dcp<addressing_mode::indirect_indexed>(); break;
- case 0xD4: run_nop<addressing_mode::zero_page_indexed_x>(); break;
- case 0xD5: run_cmp<addressing_mode::zero_page_indexed_x>(); break;
- case 0xD6: run_dec<addressing_mode::zero_page_indexed_x>(); break;
- case 0xD7: run_dcp<addressing_mode::zero_page_indexed_x>(); break;
- case 0xD8: run_cld(); break;
- case 0xD9: run_cmp<addressing_mode::absolute_indexed_y>(); break;
- case 0xDA: run_nop(); break;
- case 0xDB: run_dcp<addressing_mode::absolute_indexed_y>(); break;
- case 0xDC: run_nop<addressing_mode::absolute_indexed_x>(); break;
- case 0xDD: run_cmp<addressing_mode::absolute_indexed_x>(); break;
- case 0xDE: run_dec<addressing_mode::absolute_indexed_x>(); break;
- case 0xDF: run_dcp<addressing_mode::absolute_indexed_x>(); break;
- case 0xE0: run_cpx<addressing_mode::immediate>(); break;
- case 0xE1: run_sbc<addressing_mode::indexed_indirect>(); break;
- case 0xE2: run_nop<addressing_mode::immediate>(); break;
- case 0xE3: run_isc<addressing_mode::indexed_indirect>(); break;
- case 0xE4: run_cpx<addressing_mode::zero_page>(); break;
- case 0xE5: run_sbc<addressing_mode::zero_page>(); break;
- case 0xE6: run_inc<addressing_mode::zero_page>(); break;
- case 0xE7: run_isc<addressing_mode::zero_page>(); break;
- case 0xE8: run_inx(); break;
- case 0xE9: run_sbc<addressing_mode::immediate>(); break;
- case 0xEA: run_nop(); break;
- case 0xEB: run_sbc<addressing_mode::immediate>(); break;
- case 0xEC: run_cpx<addressing_mode::absolute>(); break;
- case 0xED: run_sbc<addressing_mode::absolute>(); break;
- case 0xEE: run_inc<addressing_mode::absolute>(); break;
- case 0xEF: run_isc<addressing_mode::absolute>(); break;
- case 0xF0: run_beq<addressing_mode::relative>(); break;
- case 0xF1: run_sbc<addressing_mode::indirect_indexed>(); break;
- case 0xF2: run_stp(); break;
- case 0xF3: run_isc<addressing_mode::indirect_indexed>(); break;
- case 0xF4: run_nop<addressing_mode::zero_page_indexed_x>(); break;
- case 0xF5: run_sbc<addressing_mode::zero_page_indexed_x>(); break;
- case 0xF6: run_inc<addressing_mode::zero_page_indexed_x>(); break;
- case 0xF7: run_isc<addressing_mode::zero_page_indexed_x>(); break;
- case 0xF8: run_sed(); break;
- case 0xF9: run_sbc<addressing_mode::absolute_indexed_y>(); break;
- case 0xFA: run_nop(); break;
- case 0xFB: run_isc<addressing_mode::absolute_indexed_y>(); break;
- case 0xFC: run_nop<addressing_mode::absolute_indexed_x>(); break;
- case 0xFD: run_sbc<addressing_mode::absolute_indexed_x>(); break;
- case 0xFE: run_inc<addressing_mode::absolute_indexed_x>(); break;
- case 0xFF: run_isc<addressing_mode::absolute_indexed_x>(); break;
- default: std::abort();
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Instructions
- // -----------------------------------------------------------------------------------------------------------------
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_brk() -> void
- {
- // BRK: Break
- fetch_operand<Mode>();
- execute_interrupt(address{ 0xFFFE });
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_ora() -> void
- {
- // ORA: Logical Inclusive OR
- auto operand = fetch_operand<Mode>();
- eval_ora(operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- auto cpu::run_stp() -> void
- {
- // TODO: Proper STP handling
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_slo() -> void
- {
- // SLO: ASL + ORA
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- auto const tmp = eval_asl(operand.read());
- operand.write(tmp);
- eval_ora(tmp);
- current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_nop() -> void
- {
- // NOP: NOP
- auto operand = fetch_operand<Mode>();
- current_cycles_ += operand.get_cycles();
- }
-
- auto cpu::run_nop() -> void
- {
- // NOP: NOP
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_php() -> void
- {
- // PHP: Push Processor Status
- eval_php();
- current_cycles_ += cycle_count::from_cpu(3);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_asl() -> void
- {
- // ASL: Arithmetic Shift Left
- auto operand = fetch_operand<Mode>();
- operand.write(eval_asl(operand.read()));
- current_cycles_ += shift_cycle_count<Mode>();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_anc() -> void
- {
- // ANC: AND #i + copy N to C
- auto operand = fetch_operand<Mode>();
- eval_and(operand.read());
- registers_.c = registers_.n;
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bpl() -> void
- {
- // BPL: Break If Positive
- branch<Mode>(!registers_.n);
- }
-
- auto cpu::run_clc() -> void
- {
- // CLC: Clear Carry Flag
- registers_.c = false;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_jsr() -> void
- {
- // JSR: Jump to Subroutine
- static_assert(Mode == addressing_mode::absolute, "JSR only supports absolute addressing.");
-
- push_stack16(registers_.pc + 1);
-
- auto operand = fetch_operand<Mode>();
- registers_.pc = operand.get_address().get_absolute();
- current_cycles_ += cycle_count::from_cpu(6);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_and() -> void
- {
- // AND: Logical AND
- auto operand = fetch_operand<Mode>();
- eval_and(operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_rla() -> void
- {
- // RLA: ROL + AND
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- auto const tmp = eval_rol(operand.read());
- operand.write(tmp);
- eval_and(tmp);
- current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bit() -> void
- {
- // BIT: Bit Test
- 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;
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_rol() -> void
- {
- // ROR: Rotate Left
- auto operand = fetch_operand<Mode>();
- auto const result = eval_rol(operand.read());
- operand.write(result);
- current_cycles_ += shift_cycle_count<Mode>();
- }
-
- auto cpu::run_plp() -> void
- {
- // PLP: Pull Processor Status
- eval_plp();
- current_cycles_ += cycle_count::from_cpu(4);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bmi() -> void
- {
- branch<Mode>(registers_.n);
- }
-
- auto cpu::run_sec() -> void
- {
- // SEC: Set Carry Flag
- registers_.c = true;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_rti() -> void
- {
- // RTI: Return From Interrupt
- eval_plp();
- registers_.pc = pop_stack16();
- current_cycles_ += cycle_count::from_cpu(6);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_eor() -> void
- {
- // EOR: Exclusive OR
- auto operand = fetch_operand<Mode>();
- eval_eor(operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_sre() -> void
- {
- // SRE: LSR + EOR
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- auto const tmp = eval_lsr(operand.read());
- operand.write(tmp);
- eval_eor(tmp);
- current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_lsr() -> void
- {
- // LSR: Logical Shift Right
- auto operand = fetch_operand<Mode>();
- 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;
- update_zn(new_val);
- current_cycles_ += shift_cycle_count<Mode>();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_alr() -> void
- {
- // ALR: AND #i + LSR A
- auto operand = fetch_operand<Mode>();
- eval_and(operand.read());
- registers_.a = eval_lsr(registers_.a);
- current_cycles_ += operand.get_cycles();
- }
-
- auto cpu::run_pha() -> void
- {
- // PHA: Push Accumulator
- push_stack8(registers_.a);
- current_cycles_ += cycle_count::from_cpu(3);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_jmp() -> void
- {
- // JMP: Jump
- auto operand = fetch_operand<Mode>();
- registers_.pc = operand.get_address().get_absolute();
- current_cycles_ += jump_cycle_count<Mode>();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bvc() -> void
- {
- // BVC: Branch If Overflow Clear
- branch<Mode>(!registers_.v);
- }
-
- auto cpu::run_cli() -> void
- {
- // CLI: Clear Interrupt Disable
- registers_.i = false;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_rts() -> void
- {
- // RTS: Return from Subroutine
- auto const addr = address{ pop_stack16() } + 1;
- registers_.pc = addr.get_absolute();
- current_cycles_ += cycle_count::from_cpu(6);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_adc() -> void
- {
- // ADC: Add With Carry
- auto operand = fetch_operand<Mode>();
- eval_adc(operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_rra() -> void
- {
- // RRA: ROR + ADC
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- auto const tmp = eval_ror(operand.read());
- operand.write(tmp);
- eval_adc(tmp);
- current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_ror() -> void
- {
- // ROR: Rotate Right
- auto operand = fetch_operand<Mode>();
- auto const result = eval_ror(operand.read());
- operand.write(result);
- current_cycles_ += shift_cycle_count<Mode>();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_arr() -> void
- {
- // ARR: AND #i + ROR A but with different flags
- // TODO
- std::abort();
- }
-
- auto cpu::run_pla() -> void
- {
- // PLA: Pull Accumulator
- registers_.a = pop_stack8();
- update_zn(registers_.a);
- current_cycles_ += cycle_count::from_cpu(4);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bvs() -> void
- {
- // BVS: Branch If Overflow Set
- branch<Mode>(registers_.v);
- }
-
- auto cpu::run_sei() -> void
- {
- // SEI: Set Interrupt Disable
- registers_.i = true;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_sta() -> void
- {
- // STA: Store Accumulator
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- operand.write(registers_.a);
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_ahx() -> void
- {
- // AHX: ???
- // TODO
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_sax() -> void
- {
- // SAX: AND A X
- auto operand = fetch_operand<Mode>();
- auto const result = static_cast<std::uint8_t>(registers_.a & registers_.x);
- operand.write(result);
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_sty() -> void
- {
- // STX: Store Y
- auto operand = fetch_operand<Mode>();
- operand.write(registers_.y);
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_stx() -> void
- {
- // STX: Store X
- auto operand = fetch_operand<Mode>();
- operand.write(registers_.x);
- current_cycles_ += operand.get_cycles();
- }
-
- auto cpu::run_dey() -> void
- {
- // DEY: Decrement Y Register
- registers_.y -= 1;
- update_zn(registers_.y);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_txa() -> void
- {
- // TXA: Transfer X to Accumulator
- registers_.a = registers_.x;
- update_zn(registers_.a);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_xaa() -> void
- {
- // XAA: Depends on analog behavior.
- run_stp();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bcc() -> void
- {
- // BCC: Branch If Carry Clear
- branch<Mode>(!registers_.c);
- }
-
- auto cpu::run_tya() -> void
- {
- // TYA: Transfer Y to Accumulator
- registers_.a = registers_.y;
- update_zn(registers_.a);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_txs() -> void
- {
- // TXS: Transfer X to Stack Pointer
- registers_.sp = registers_.x;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_tas() -> void
- {
- // TAS: ???
- // TODO
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_shy() -> void
- {
- // SHY: ???
- // TODO
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_shx() -> void
- {
- // SHY: ???
- // TODO
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_ldy() -> void
- {
- // LDY: Load Y Register
- auto operand = fetch_operand<Mode>();
- registers_.y = operand.read();
- update_zn(registers_.y);
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_lda() -> void
- {
- // LDA: Load Accumulator
- auto operand = fetch_operand<Mode>();
- registers_.a = operand.read();
- update_zn(registers_.a);
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_ldx() -> void
- {
- // LDX: Load X Register
- auto operand = fetch_operand<Mode>();
- registers_.x = operand.read();
- update_zn(registers_.x);
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_lax() -> void
- {
- // LAX: LDA + TAX
- auto operand = fetch_operand<Mode>();
- registers_.x = registers_.a = operand.read();
- update_zn(registers_.a);
- current_cycles_ += operand.get_cycles();
- }
-
- auto cpu::run_tay() -> void
- {
- // TAY: Transfer Accumulator to Y
- registers_.y = registers_.a;
- update_zn(registers_.y);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_tax() -> void
- {
- // TAX: Transfer Accumulator to X
- registers_.x = registers_.a;
- update_zn(registers_.x);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bcs() -> void
- {
- // BCS: Branch If Carry Set
- branch<Mode>(registers_.c);
- }
-
- auto cpu::run_clv() -> void
- {
- // CLV: Clear Overflow Flag
- registers_.v = false;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_tsx() -> void
- {
- // TSX: Transfer Stack Pointer to X
- registers_.x = registers_.sp;
- update_zn(registers_.x);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_las() -> void
- {
- // LAS: ???
- // TODO
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_cpy() -> void
- {
- // CPY: Compare Y Register
- auto operand = fetch_operand<Mode>();
- eval_cmp(registers_.y, operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_axs() -> void
- {
- // AXS: Set X to ((A AND X) - #value without borrow), updates NZC
- // TODO
- std::abort();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_cmp() -> void
- {
- // CMP: Compare
- auto operand = fetch_operand<Mode>();
- eval_cmp(registers_.a, operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_dcp() -> void
- {
- // DCP: DEC + CMP
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- auto const tmp = static_cast<std::uint8_t>(operand.read() - 1);
- operand.write(tmp);
- eval_cmp(registers_.a, tmp);
- current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_dec() -> void
- {
- // DEC: Decrement Memory
- auto operand = fetch_operand<Mode>();
- auto const old_val = read8(operand.get_address());
- auto const new_val = static_cast<std::uint8_t>(old_val - 1);
- write8(operand.get_address(), new_val);
- update_zn(new_val);
- current_cycles_ += inc_dec_cycle_count<Mode>();
- }
-
- auto cpu::run_iny() -> void
- {
- // INY: Increment Y
- registers_.y += 1;
- update_zn(registers_.y);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- auto cpu::run_dex() -> void
- {
- // DEX: Decrement X Register
- registers_.x -= 1;
- update_zn(registers_.x);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_bne() -> void
- {
- // BNE: Branch If Not Equal
- branch<Mode>(!registers_.z);
- }
-
- auto cpu::run_cld() -> void
- {
- // CLD: Clear Decimal Mode
- registers_.d = false;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_cpx() -> void
- {
- // CPX: Compare X Register
- auto operand = fetch_operand<Mode>();
- eval_cmp(registers_.x, operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_sbc() -> void
- {
- // SBC: Subtract With Carry
- auto operand = fetch_operand<Mode>();
- eval_adc(~operand.read());
- current_cycles_ += operand.get_cycles();
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_isc() -> void
- {
- // ISC: INC + SBC
- auto operand = fetch_operand<Mode>(force_page_crossing::yes);
- auto const tmp = static_cast<std::uint8_t>(operand.read() + 1);
- operand.write(tmp);
- eval_adc(~tmp);
- current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_inc() -> void
- {
- // INC: Increment Memory
- auto operand = fetch_operand<Mode>();
- auto const old_val = read8(operand.get_address());
- auto const new_val = static_cast<std::uint8_t>(old_val + 1);
- write8(operand.get_address(), new_val);
- update_zn(new_val);
- current_cycles_ += inc_dec_cycle_count<Mode>();
- }
-
- auto cpu::run_inx() -> void
- {
- // INX: Increment X
- registers_.x += 1;
- update_zn(registers_.x);
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::run_beq() -> void
- {
- // BEQ: Branch If Equal
- branch<Mode>(registers_.z);
- }
-
- auto cpu::run_sed() -> void
- {
- // SED: Set Decimal Flag
- registers_.d = true;
- current_cycles_ += cycle_count::from_cpu(2);
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Helpers
- // -----------------------------------------------------------------------------------------------------------------
-
- auto cpu::advance_pc8() -> std::uint8_t
- {
- auto const val = read8(address{ registers_.pc });
- registers_.pc += 1;
- return val;
- }
-
- auto cpu::advance_pc16() -> std::uint16_t
- {
- auto const val = read16(address{ registers_.pc });
- registers_.pc += 2;
- return val;
- }
-
- auto cpu::push_stack8(std::uint8_t const value) -> void
- {
- write8(stack_offset + registers_.sp, value);
- registers_.sp -= 1;
- }
-
- auto cpu::push_stack16(std::uint16_t const value) -> void
- {
- auto const low = static_cast<std::uint8_t>(value >> 0 & 0xFF);
- auto const high = static_cast<std::uint8_t>(value >> 8 & 0xFF);
- push_stack8(high);
- push_stack8(low);
- }
-
- auto cpu::pop_stack8() -> std::uint8_t
- {
- registers_.sp += 1;
- return read8(stack_offset + registers_.sp);
- }
-
- auto cpu::pop_stack16() -> std::uint16_t
- {
- auto const low = pop_stack8();
- auto const high = pop_stack8();
- return static_cast<std::uint16_t>((high << 8) | (low << 0));
- }
-
- auto cpu::update_zn(std::uint8_t const value) -> void
- {
- registers_.z = value == 0;
- registers_.n = (value & 0x80) != 0;
- }
-
- template<cpu::addressing_mode Mode>
- auto cpu::branch(bool const condition) -> void
- {
- static_assert(Mode == addressing_mode::relative, "Branch should only be used with relative operands.");
-
- auto operand = fetch_operand<Mode>();
- if (condition)
- {
- registers_.pc = operand.get_address().get_absolute();
- current_cycles_ += operand.get_cycles();
- }
- else
- {
- current_cycles_ += cycle_count::from_cpu(2);
- }
- }
-
- auto cpu::execute_interrupt(address const vector) -> void
- {
- push_stack16(registers_.pc);
- eval_php();
- registers_.pc = read16(vector);
- registers_.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;
- 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;
- update_zn(res);
- return res;
- }
-
- 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;
- update_zn(new_val);
- return new_val;
- }
-
- 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;
- update_zn(new_val);
- return new_val;
- }
-
- 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 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;
- update_zn(registers_.a);
- }
-
- auto cpu::eval_and(std::uint8_t const arg) -> void
- {
- registers_.a &= arg;
- update_zn(registers_.a);
- }
-
- auto cpu::eval_ora(std::uint8_t const arg) -> void
- {
- registers_.a |= arg;
- update_zn(registers_.a);
- }
-
- auto cpu::eval_eor(std::uint8_t const arg) -> void
- {
- registers_.a ^= arg;
- update_zn(registers_.a);
- }
-
- auto cpu::eval_cmp(std::uint8_t const a, std::uint8_t const b) -> void
- {
- update_zn(a - b);
- registers_.c = a >= b;
- }
-
- auto cpu::eval_plp() -> void
- {
- // Ignore bits 4 and 5
- registers_.p =
- (pop_stack8() & 0b11001111) |
- (registers_.p & 0b00110000);
- }
-
- auto cpu::eval_php() -> void
- {
- // Always set bits 4 and 5.
- push_stack8(registers_.p | 0b00110000);
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Operands
- // -----------------------------------------------------------------------------------------------------------------
-
- //
- // See: https://www.nesdev.org/wiki/CPU_addressing_modes
- //
-
- template<cpu::addressing_mode Mode>
- auto cpu::fetch_operand(force_page_crossing const force_page_crossing) -> operand<Mode>
- {
- auto addr = address{};
- auto cycles = cycle_count{};
-
- switch (Mode)
- {
- case addressing_mode::immediate: static_assert(Mode != addressing_mode::immediate);
- case addressing_mode::accumulator: static_assert(Mode != addressing_mode::accumulator);
- case addressing_mode::zero_page:
- {
- addr = address{ advance_pc8() };
- cycles = cycle_count::from_cpu(3);
- break;
- }
- case addressing_mode::absolute:
- {
- addr = address{ advance_pc16() };
- cycles = cycle_count::from_cpu(4);
- break;
- }
- case addressing_mode::relative:
- {
- auto const offset = static_cast<std::int8_t>(advance_pc8());
- auto const base = address{ registers_.pc };
- addr = address{ static_cast<std::uint16_t>(registers_.pc + offset) };
- auto const page_crossing =
- base.get_page() != addr.get_page() || force_page_crossing == force_page_crossing::yes;
- cycles = cycle_count::from_cpu(page_crossing ? 4 : 3);
- break;
- }
- case addressing_mode::indirect:
- {
- auto const arg = address{ advance_pc16() };
- // Account for buggy indirect read: https://www.nesdev.org/wiki/Errata
- addr = arg.get_offset() == 0xFF
- ? address{ read8(address{ arg.get_page(), 0x00 }), read8(arg) }
- : address{ read16(arg) };
- cycles = cycle_count::from_cpu(6);
- break;
- }
- case addressing_mode::zero_page_indexed_x:
- {
- // PEEK((arg + X) % 256)
- addr = address{ 0x00, static_cast<std::uint8_t>(advance_pc8() + registers_.x) };
- cycles = cycle_count::from_cpu(4);
- break;
- }
- case addressing_mode::zero_page_indexed_y:
- {
- // PEEK((arg + Y) % 256)
- addr = address{ 0x00, static_cast<std::uint8_t>(advance_pc8() + registers_.y) };
- cycles = cycle_count::from_cpu(4);
- break;
- }
- case addressing_mode::absolute_indexed_x:
- {
- // PEEK(arg + X)
- auto const arg = address{ advance_pc16() };
- addr = arg + registers_.x;
- auto const page_crossing =
- addr.get_page() != arg.get_page() || force_page_crossing == force_page_crossing::yes;
- cycles = cycle_count::from_cpu(page_crossing ? 5 : 4);
- break;
- }
- case addressing_mode::absolute_indexed_y:
- {
- // PEEK(arg + Y)
- auto const arg = address{ advance_pc16() };
- addr = arg + registers_.y;
- auto const page_crossing =
- addr.get_page() != arg.get_page() || force_page_crossing == force_page_crossing::yes;
- cycles = cycle_count::from_cpu(page_crossing ? 5 : 4);
- break;
- }
- case addressing_mode::indexed_indirect:
- {
- // PEEK(PEEK((arg + X) % 256) + PEEK((arg + X + 1) % 256) * 256)
- auto const arg = advance_pc8();
- auto const page = read8(address{ 0x00, static_cast<std::uint8_t>(arg + registers_.x + 1) });
- auto const offset = read8(address{ 0x00, static_cast<std::uint8_t>(arg + registers_.x) });
- addr = address{ page, offset };
- cycles = cycle_count::from_cpu(6);
- break;
- }
- case addressing_mode::indirect_indexed:
- {
- // PEEK(PEEK(arg) + PEEK((arg + 1) % 256) * 256 + Y)
- auto const arg = advance_pc8();
- auto const page = read8(address{ 0x00, static_cast<std::uint8_t>(arg + 1) });
- auto const offset = read8(address{ 0x00, static_cast<std::uint8_t>(arg) });
- auto const base = address{ page, offset };
- addr = base + registers_.y;
- auto const page_crossing =
- addr.get_page() != base.get_page() || force_page_crossing == force_page_crossing::yes;
- cycles = cycle_count::from_cpu(page_crossing ? 6 : 5);
- break;
- }
- }
-
- return operand<Mode>{ *this, addr, cycles };
- }
-
- template<>
- auto cpu::fetch_operand<cpu::addressing_mode::accumulator>(force_page_crossing)
- -> operand<addressing_mode::accumulator>
- {
- return operand<addressing_mode::accumulator>{ *this };
- }
-
- template<>
- auto cpu::fetch_operand<cpu::addressing_mode::immediate>(force_page_crossing)
- -> operand<addressing_mode::immediate>
- {
- return operand<addressing_mode::immediate>{ *this, advance_pc8() };
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Memory Access
- // -----------------------------------------------------------------------------------------------------------------
-
- //
- // See: https://www.nesdev.org/wiki/CPU_memory_map
- //
-
- auto cpu::read8(address const addr) -> std::uint8_t
- {
- if (addr <= address{ 0x1FFF }) { return ram_[addr.get_absolute() % ram_size]; }
- if (addr <= address{ 0x3FFF })
- {
-#ifdef ENABLE_PPU
- switch (addr.get_absolute() % 8)
- {
- case 0: return ppu_.read_latch();
- case 1: return ppu_.read_latch();
- case 2: return ppu_.read_ppustatus();
- case 3: return ppu_.read_latch();
- case 4: return ppu_.read_oamdata();
- case 5: return ppu_.read_latch();
- case 6: return ppu_.read_latch();
- case 7: return ppu_.read_ppudata();
- default: return 0x0;
- }
-#else
- return 0x0;
-#endif
- }
- if (addr <= address{ 0x4013 }) { return 0x0; } // TODO: APU registers
-#ifdef ENABLE_PPU
- if (addr == address{ 0x4014 }) { return ppu_.read_latch(); }
-#else
- if (addr == address{ 0x4014 }) { return 0x0; }
-#endif
- if (addr == address{ 0x4015 }) { return 0x0; } // TODO: APU registers
- if (addr == address{ 0x4016 }) { return controller_1_.read(); }
- if (addr == address{ 0x4017 }) { return controller_2_.read(); }
- if (addr <= address{ 0x401F }) { return 0x0; }
- return mapper_.read_cpu(addr);
- }
-
- auto cpu::read16(address const addr) -> std::uint16_t
- {
- auto const low = read8(addr + 0);
- auto const high = read8(addr + 1);
- return static_cast<std::uint16_t>((high << 8) | (low << 0));
- }
-
- auto cpu::write8(address const addr, std::uint8_t const value) -> void
- {
- if (addr <= address{ 0x1FFF }) { ram_[addr.get_absolute() % ram_size] = value; return; }
- if (addr <= address{ 0x3FFF })
- {
-#ifdef ENABLE_PPU
- switch (addr.get_absolute() % 8)
- {
- case 0: ppu_.write_ppuctrl(value); return;
- case 1: ppu_.write_ppumask(value); return;
- case 2: ppu_.write_latch(value); return;
- case 3: ppu_.write_oamaddr(value); return;
- case 4: ppu_.write_oamdata(value); return;
- case 5: ppu_.write_ppuscroll(value); return;
- case 6: ppu_.write_ppuaddr(value); return;
- case 7: ppu_.write_ppudata(value); return;
- default: return;
- }
-#else
- return;
-#endif
- }
- if (addr <= address{ 0x4013 }) { return; } // TODO: APU registers
-#ifdef ENABLE_PPU
- if (addr == address{ 0x4014 }) { ppu_.write_oamdma(value); return; }
-#else
- if (addr == address{ 0x4014 }) { return; }
-#endif
- if (addr == address{ 0x4015 }) { return; } // TODO: APU registers
- if (addr == address{ 0x4016 }) { controller_1_.write(value); return; }
- if (addr == address{ 0x4017 }) { controller_2_.write(value); return; }
- if (addr <= address{ 0x401F }) { return; }
- mapper_.write_cpu(addr, value);
- }
-
- auto cpu::write16(address const addr, std::uint16_t const value) -> void
- {
- auto const low = static_cast<std::uint8_t>(value >> 0 & 0xFF);
- auto const high = static_cast<std::uint8_t>(value >> 8 & 0xFF);
- write8(addr + 0, low);
- write8(addr + 1, high);
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/cpu.hh b/src/nes/cpu.hh
deleted file mode 100644
index fc3ef5b..0000000
--- a/src/nes/cpu.hh
+++ /dev/null
@@ -1,297 +0,0 @@
-#pragma once
-
-#include "nes/util/cycle_count.hh"
-#include "nes/util/address.hh"
-#include "status.hh"
-
-namespace nes
-{
- class ppu;
- class controller;
- class mapper;
-
- class cpu
- {
- static constexpr auto ram_size = std::size_t{ 0x800 };
- static constexpr auto stack_offset = address{ 0x100 };
-
- cycle_count current_cycles_;
- ppu& ppu_;
- mapper& mapper_;
- controller& controller_1_;
- controller& controller_2_;
- std::uint8_t ram_[ram_size]{};
- bool nmi_pending_{ false };
-
- // Registers
- struct
- {
- std::uint16_t pc{ 0 };
- std::uint8_t sp{ 0xFD };
- std::uint8_t a{ 0 };
- std::uint8_t x{ 0 };
- std::uint8_t y{ 0 };
- union
- {
- 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
- };
- } registers_{};
-
- enum class addressing_mode
- {
- accumulator, // A
- immediate, // #v
- zero_page, // d
- absolute, // a
- relative, // *+d
- indirect, // (a)
- zero_page_indexed_x, // d,x
- zero_page_indexed_y, // d,y
- absolute_indexed_x, // a,x
- absolute_indexed_y, // a,y
- indexed_indirect, // (d,x)
- indirect_indexed, // (d),y
- };
-
- enum class force_page_crossing
- {
- no,
- yes,
- };
-
- template<addressing_mode>
- class operand
- {
- cpu& cpu_;
- address address_;
- cycle_count cycles_;
-
- public:
- explicit operand(cpu& cpu, address const address, cycle_count const cycles)
- : cpu_{ cpu }
- , address_{ address }
- , cycles_{ cycles }
- {
- }
-
- auto read() -> std::uint8_t { return cpu_.read8(address_); }
- auto write(std::uint8_t const value) -> void { cpu_.write8(address_, value); }
- auto get_address() -> address { return address_; }
- auto get_cycles() -> cycle_count { return cycles_; }
- };
-
- template<>
- class operand<addressing_mode::accumulator>
- {
- cpu& cpu_;
-
- public:
- explicit operand(cpu& cpu)
- : cpu_{ cpu }
- {
- }
-
- auto read() -> std::uint8_t { return cpu_.registers_.a; }
- auto write(std::uint8_t const value) -> void { cpu_.registers_.a = value; }
- auto get_cycles() -> cycle_count { return cycle_count::from_cpu(2); }
- };
-
- 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 }
- {
- }
-
- auto read() -> std::uint8_t { return value_; }
- auto get_cycles() -> cycle_count { return cycle_count::from_cpu(2); }
- };
-
- public:
- explicit cpu(ppu&, mapper&, controller& controller_1, controller& controller_2);
-
- cpu(cpu const&) = delete;
- cpu(cpu&&) = delete;
- 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 stall_cycles(cycle_count) -> void;
- auto trigger_nmi() -> void;
- auto step() -> void;
- auto is_nmi_pending() const -> bool { return nmi_pending_; } // XXX: Debugging
-
- // Memory access
-
- auto read8(address) -> std::uint8_t;
- auto read16(address) -> std::uint16_t;
- auto write8(address, std::uint8_t) -> void;
- auto write16(address, std::uint16_t) -> void;
-
- private:
- // Instructions
-
-#define DEFINE_SIMPLE_INSTRUCTION(name) \
- auto run_##name() -> void;
-#define DEFINE_OPERAND_INSTRUCTION(name) \
- template<addressing_mode Mode> auto run_##name() -> void;
-
- DEFINE_OPERAND_INSTRUCTION(brk)
- DEFINE_OPERAND_INSTRUCTION(ora)
- DEFINE_SIMPLE_INSTRUCTION(stp)
- DEFINE_OPERAND_INSTRUCTION(slo)
- DEFINE_OPERAND_INSTRUCTION(nop)
- DEFINE_SIMPLE_INSTRUCTION(nop)
- DEFINE_SIMPLE_INSTRUCTION(php)
- DEFINE_OPERAND_INSTRUCTION(asl)
- DEFINE_OPERAND_INSTRUCTION(anc)
- DEFINE_OPERAND_INSTRUCTION(bpl)
- DEFINE_SIMPLE_INSTRUCTION(clc)
- DEFINE_OPERAND_INSTRUCTION(jsr)
- DEFINE_OPERAND_INSTRUCTION(and)
- DEFINE_OPERAND_INSTRUCTION(rla)
- DEFINE_OPERAND_INSTRUCTION(bit)
- DEFINE_OPERAND_INSTRUCTION(rol)
- DEFINE_SIMPLE_INSTRUCTION(plp)
- DEFINE_OPERAND_INSTRUCTION(bmi)
- DEFINE_SIMPLE_INSTRUCTION(sec)
- DEFINE_SIMPLE_INSTRUCTION(rti)
- DEFINE_OPERAND_INSTRUCTION(eor)
- DEFINE_OPERAND_INSTRUCTION(sre)
- DEFINE_OPERAND_INSTRUCTION(lsr)
- DEFINE_OPERAND_INSTRUCTION(alr)
- DEFINE_SIMPLE_INSTRUCTION(pha)
- DEFINE_OPERAND_INSTRUCTION(jmp)
- DEFINE_OPERAND_INSTRUCTION(bvc)
- DEFINE_SIMPLE_INSTRUCTION(cli)
- DEFINE_SIMPLE_INSTRUCTION(rts)
- DEFINE_OPERAND_INSTRUCTION(adc)
- DEFINE_OPERAND_INSTRUCTION(rra)
- DEFINE_OPERAND_INSTRUCTION(ror)
- DEFINE_OPERAND_INSTRUCTION(arr)
- DEFINE_SIMPLE_INSTRUCTION(pla)
- DEFINE_OPERAND_INSTRUCTION(bvs)
- DEFINE_SIMPLE_INSTRUCTION(sei)
- DEFINE_OPERAND_INSTRUCTION(sta)
- DEFINE_OPERAND_INSTRUCTION(ahx)
- DEFINE_OPERAND_INSTRUCTION(sax)
- DEFINE_OPERAND_INSTRUCTION(sty)
- DEFINE_OPERAND_INSTRUCTION(stx)
- DEFINE_SIMPLE_INSTRUCTION(dey)
- DEFINE_SIMPLE_INSTRUCTION(txa)
- DEFINE_OPERAND_INSTRUCTION(xaa)
- DEFINE_OPERAND_INSTRUCTION(bcc)
- DEFINE_SIMPLE_INSTRUCTION(tya)
- DEFINE_SIMPLE_INSTRUCTION(txs)
- DEFINE_OPERAND_INSTRUCTION(tas)
- DEFINE_OPERAND_INSTRUCTION(shy)
- DEFINE_OPERAND_INSTRUCTION(shx)
- DEFINE_OPERAND_INSTRUCTION(ldy)
- DEFINE_OPERAND_INSTRUCTION(lda)
- DEFINE_OPERAND_INSTRUCTION(ldx)
- DEFINE_OPERAND_INSTRUCTION(lax)
- DEFINE_SIMPLE_INSTRUCTION(tay)
- DEFINE_SIMPLE_INSTRUCTION(tax)
- DEFINE_OPERAND_INSTRUCTION(bcs)
- DEFINE_SIMPLE_INSTRUCTION(clv)
- DEFINE_SIMPLE_INSTRUCTION(tsx)
- DEFINE_OPERAND_INSTRUCTION(las)
- DEFINE_OPERAND_INSTRUCTION(cpy)
- DEFINE_OPERAND_INSTRUCTION(axs)
- DEFINE_OPERAND_INSTRUCTION(cmp)
- DEFINE_OPERAND_INSTRUCTION(dcp)
- DEFINE_OPERAND_INSTRUCTION(dec)
- DEFINE_SIMPLE_INSTRUCTION(iny)
- DEFINE_SIMPLE_INSTRUCTION(dex)
- DEFINE_OPERAND_INSTRUCTION(bne)
- DEFINE_SIMPLE_INSTRUCTION(cld)
- DEFINE_OPERAND_INSTRUCTION(cpx)
- DEFINE_OPERAND_INSTRUCTION(sbc)
- DEFINE_OPERAND_INSTRUCTION(isc)
- DEFINE_OPERAND_INSTRUCTION(inc)
- DEFINE_SIMPLE_INSTRUCTION(inx)
- DEFINE_OPERAND_INSTRUCTION(beq)
- DEFINE_SIMPLE_INSTRUCTION(sed)
-
-#undef DEFINE_SIMPLE_INSTRUCTION
-#undef DEFINE_OPERAND_INSTRUCTION
-
- // Helpers
-
- auto advance_pc8() -> std::uint8_t;
- auto advance_pc16() -> std::uint16_t;
- auto push_stack8(std::uint8_t) -> void;
- auto push_stack16(std::uint16_t) -> void;
- auto pop_stack8() -> std::uint8_t;
- auto pop_stack16() -> std::uint16_t;
- auto update_zn(std::uint8_t value) -> void;
- template<addressing_mode Mode>
- auto branch(bool condition) -> void;
- auto execute_interrupt(address) -> void;
-
- auto eval_ror(std::uint8_t arg) -> std::uint8_t;
- auto eval_rol(std::uint8_t arg) -> std::uint8_t;
- auto eval_asl(std::uint8_t arg) -> std::uint8_t;
- auto eval_lsr(std::uint8_t arg) -> std::uint8_t;
- auto eval_adc(std::uint8_t arg) -> void;
- auto eval_and(std::uint8_t arg) -> void;
- auto eval_ora(std::uint8_t arg) -> void;
- auto eval_eor(std::uint8_t arg) -> void;
- auto eval_cmp(std::uint8_t a, std::uint8_t b) -> void;
- auto eval_plp() -> void;
- auto eval_php() -> void;
-
- // Operands
-
-#define DEFINE_CYCLE_COUNT(name) \
- template<addressing_mode Mode> static auto name##_cycle_count() -> cycle_count;
-#define DEFINE_CYCLE_COUNT_INSTANCE(name, mode, value) \
- template<> auto name##_cycle_count<addressing_mode::mode>() -> cycle_count { return cycle_count::from_cpu(value); }
-
- DEFINE_CYCLE_COUNT(jump)
- DEFINE_CYCLE_COUNT_INSTANCE(jump, absolute, 3)
- DEFINE_CYCLE_COUNT_INSTANCE(jump, indirect, 5)
-
- DEFINE_CYCLE_COUNT(shift)
- DEFINE_CYCLE_COUNT_INSTANCE(shift, accumulator, 2)
- DEFINE_CYCLE_COUNT_INSTANCE(shift, zero_page, 5)
- DEFINE_CYCLE_COUNT_INSTANCE(shift, zero_page_indexed_x, 6)
- DEFINE_CYCLE_COUNT_INSTANCE(shift, absolute, 6)
- DEFINE_CYCLE_COUNT_INSTANCE(shift, absolute_indexed_x, 7)
-
- DEFINE_CYCLE_COUNT(inc_dec)
- DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, zero_page, 5)
- DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, zero_page_indexed_x, 6)
- DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, absolute, 6)
- DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, absolute_indexed_x, 7)
-
-#undef DEFINE_CYCLE_COUNT
-#undef DEFINE_CYCLE_COUNT_INSTANCE
-
- template<addressing_mode Mode>
- auto fetch_operand(force_page_crossing = force_page_crossing::no) -> operand<Mode>;
- template<>
- auto fetch_operand<addressing_mode::accumulator>(force_page_crossing) -> operand<addressing_mode::accumulator>;
- template<>
- auto fetch_operand<addressing_mode::immediate>(force_page_crossing) -> operand<addressing_mode::immediate>;
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/display.cc b/src/nes/display.cc
deleted file mode 100644
index 4ff1d5f..0000000
--- a/src/nes/display.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "nes/display.hh"
-#include "nes/util/rgb.hh"
-
-namespace nes
-{
- auto display::clear(rgb const color) -> void
- {
- for (auto y = unsigned{ 0 }; y < height; ++y)
- {
- for (auto x = unsigned{ 0 }; x < width; ++x)
- {
- set(x, y, color);
- }
- }
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/display.hh b/src/nes/display.hh
deleted file mode 100644
index 14ccff1..0000000
--- a/src/nes/display.hh
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-namespace nes
-{
- class rgb;
-
- /// Abstraction of the display logic.
- class display
- {
- public:
- static constexpr auto width = unsigned{ 256 };
- static constexpr auto height = unsigned{ 240 };
-
- virtual ~display() = default;
-
- display(display const&) = delete;
- display(display&&) = delete;
- auto operator=(display const&) -> display& = delete;
- auto operator=(display&&) -> display& = delete;
-
- /// Switch between front and back buffers.
- virtual auto switch_buffers() -> void = 0;
- /// Update the pixel at the given position in the back buffer.
- virtual auto set(unsigned x, unsigned y, rgb value) -> void = 0;
- /// Load the pixel value at the given position in the back buffer.
- virtual auto get(unsigned x, unsigned y) const -> rgb = 0;
- /// Clear the entire back buffer with a color.
- virtual auto clear(rgb color) -> void;
-
- protected:
- explicit display() = default;
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/mapper.cc b/src/nes/mapper.cc
deleted file mode 100644
index 5392d87..0000000
--- a/src/nes/mapper.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "nes/mapper.hh"
-#include "nes/cartridge.hh"
-#include <iostream>
-
-namespace nes
-{
- mapper::mapper(
- cartridge& cartridge,
- read_cpu_func const read_cpu, write_cpu_func const write_cpu,
- read_ppu_func const read_ppu, write_ppu_func const write_ppu)
- : cartridge_{ cartridge }
- , read_cpu_{ read_cpu }
- , write_cpu_{ write_cpu }
- , read_ppu_{ read_ppu }
- , write_ppu_{ write_ppu }
- {
- }
-
- 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())
- {
- case 0x00:
- return mapper{
- cartridge,
- &mapper::nrom_read_cpu, &mapper::nrom_write_cpu,
- &mapper::nrom_read_ppu, &mapper::nrom_write_ppu };
- default:
- std::cerr << "Mapper not implemented: " << std::hex << cartridge.get_mapper_number() << std::endl;
- std::abort();
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // NROM
- // -----------------------------------------------------------------------------------------------------------------
-
- auto mapper::nrom_read_cpu(address const addr) -> std::uint8_t
- {
- if (addr <= address{ 0x5FFF })
- {
- return 0x0;
- }
- if (addr <= address{ 0x7FFF })
- {
- return cartridge_.get_ram()[addr.get_absolute() - 0x6000];
- }
- if (addr <= address{ 0xBFFF })
- {
- auto const rel = addr.get_absolute() - 0x8000;
- if (rel >= cartridge_.get_prg_rom_length()) { return 0x0; }
-
- return cartridge_.get_prg_rom()[rel];
- }
- if (addr <= address{ 0xFFFF })
- {
- auto rel = addr.get_absolute() - 0xC000;
- if (cartridge_.get_prg_rom_length() > 0x4000) { rel += 0x4000; }
- if (rel >= cartridge_.get_prg_rom_length()) { return 0x0; }
-
- return cartridge_.get_prg_rom()[rel];
- }
-
- return 0x0;
- }
-
- auto mapper::nrom_write_cpu(address const addr, std::uint8_t const value) -> void
- {
- if (addr <= address{ 0x5FFF })
- {
- return;
- }
- if (addr <= address{ 0x7FFF })
- {
- cartridge_.get_ram_mut()[addr.get_absolute() - 0x6000] = value;
- return;
- }
- }
-
- auto mapper::nrom_read_ppu(address const addr, std::uint8_t const* vram) -> std::uint8_t
- {
- if (addr <= address{ 0x1FFF })
- {
- auto const rel = addr.get_absolute();
- if (rel >= cartridge_.get_chr_rom_length()) { return 0x0; }
-
- return cartridge_.get_chr_rom()[rel];
- }
- if (addr <= address{ 0x3EFF })
- {
- return vram[nrom_mirrored_vram_address(addr)];
- }
-
- return 0x0;
- }
-
- auto mapper::nrom_write_ppu(address const addr, std::uint8_t* vram, std::uint8_t const value) -> void
- {
- if (addr <= address{ 0x1FFF })
- {
- return;
- }
- if (addr <= address{ 0x3EFF })
- {
- vram[nrom_mirrored_vram_address(addr)] = value;
- return;
- }
- }
-
- auto mapper::nrom_mirrored_vram_address(address const addr) const -> unsigned
- {
- auto const rel = addr.get_absolute() % 0x1000;
-
- switch (cartridge_.get_name_table_arrangement())
- {
- case name_table_arrangement::horizontal:
- // $2000 = $2400, $2800 = $2c00
- 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 <= 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;
- }
-
- return 0;
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/mapper.hh b/src/nes/mapper.hh
deleted file mode 100644
index 12ad804..0000000
--- a/src/nes/mapper.hh
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-
-#include "nes/util/address.hh"
-#include "status.hh"
-
-namespace nes
-{
- class cartridge;
-
- class mapper
- {
- using read_cpu_func = std::uint8_t(mapper::*)(address);
- using write_cpu_func = void(mapper::*)(address, std::uint8_t);
- using read_ppu_func = std::uint8_t(mapper::*)(address, std::uint8_t const* vram);
- using write_ppu_func = void(mapper::*)(address, std::uint8_t* vram, std::uint8_t);
-
- cartridge& cartridge_;
- read_cpu_func read_cpu_;
- write_cpu_func write_cpu_;
- read_ppu_func read_ppu_;
- write_ppu_func write_ppu_;
-
- public:
- static auto select(cartridge&) -> mapper;
-
- mapper(mapper const&) = delete;
- mapper(mapper&&) = delete;
- 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);
- }
-
- auto write_cpu(address const addr, std::uint8_t const value) -> void
- {
- (this->*write_cpu_)(addr, value);
- }
-
- auto read_ppu(address const addr, std::uint8_t const* vram) -> std::uint8_t
- {
- return (this->*read_ppu_)(addr, vram);
- }
-
- auto write_ppu(address const addr, std::uint8_t* vram, std::uint8_t const value) -> void
- {
- (this->*write_ppu_)(addr, vram, value);
- }
-
- private:
- explicit mapper(
- cartridge& cartridge,
- read_cpu_func read_cpu, write_cpu_func write_cpu,
- read_ppu_func read_ppu, write_ppu_func write_ppu);
-
- // Implementations
-
- auto nrom_read_cpu(address) -> std::uint8_t;
- auto nrom_write_cpu(address, std::uint8_t) -> void;
- auto nrom_read_ppu(address, std::uint8_t const* vram) -> std::uint8_t;
- auto nrom_write_ppu(address, std::uint8_t* vram, std::uint8_t) -> void;
- auto nrom_mirrored_vram_address(address) const -> unsigned;
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/nes.cc b/src/nes/nes.cc
deleted file mode 100644
index 7ac1887..0000000
--- a/src/nes/nes.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "nes/nes.hh"
-#include "nes/mapper.hh"
-#include "nes/display.hh"
-#include "nes/util/rgb.hh"
-#include <iostream>
-
-namespace nes
-{
- nes::nes(cartridge cartridge, display& display)
- : cartridge_{ std::move(cartridge) }
- , display_{ display }
- , mapper_{ mapper::select(cartridge_) }
- , ppu_{ cpu_, mapper_, display_ }
- , cpu_{ ppu_, mapper_, controller_1_, controller_2_ }
- {
- if (!cartridge_.is_valid())
- {
- std::cerr << "Cartridge not valid!" << std::endl;
- std::abort();
- }
-
- display_.clear(rgb{});
- display_.switch_buffers();
- display_.clear(rgb{});
- }
-
- auto nes::step(std::chrono::microseconds const delta) -> void
- {
- step(cycle_count::from_duration(delta));
- }
-
- auto nes::step(cycle_count const delta) -> void
- {
- current_cycles_ += delta;
- 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
- {
- 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
deleted file mode 100644
index 76c29ab..0000000
--- a/src/nes/nes.hh
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#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;
-
- /// The main console abstraction.
- class nes
- {
- cartridge cartridge_;
- display& display_;
- controller controller_1_;
- controller controller_2_;
- mapper mapper_;
- ppu ppu_;
- cpu cpu_;
- cycle_count current_cycles_;
-
- public:
- explicit nes(cartridge, display&);
-
- nes(nes const&) = delete;
- nes(nes&&) = delete;
- auto operator=(nes const&) -> nes& = delete;
- auto operator=(nes&&) -> nes& = delete;
-
- auto get_controller_1() const -> controller const& { return controller_1_; }
- 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;
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/ppu.cc b/src/nes/ppu.cc
deleted file mode 100644
index 87c7f8d..0000000
--- a/src/nes/ppu.cc
+++ /dev/null
@@ -1,763 +0,0 @@
-#include "nes/ppu.hh"
-#include "nes/display.hh"
-#include "nes/cpu.hh"
-#include "nes/mapper.hh"
-#include "nes/util/address.hh"
-#include "nes/util/rgb.hh"
-#include <iostream>
-
-// #define ENABLE_TRACES
-
-namespace nes
-{
- ppu::ppu(cpu& cpu, mapper& mapper, display& display)
- : cpu_{ cpu }
- , mapper_{ mapper }
- , display_{ display }
- {
- }
-
- auto ppu::snapshot(test::status& snapshot) -> void
- {
- snapshot.vram = std::vector(std::begin(vram_), std::end(vram_));
- snapshot.oam = std::vector(std::begin(oam_), std::end(oam_));
- }
-
- auto ppu::step() -> void
- {
- // 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;
-
- current_cycles_ += cycle_count::from_ppu(1);
- scanline_cycle_ += 1;
- if (enable_rendering && !even_frame_ && scanline_ == 261 && scanline_cycle_ == 340)
- {
- current_cycles_ += cycle_count::from_ppu(1);
- scanline_cycle_ += 1;
- }
- if (scanline_cycle_ == 341)
- {
- scanline_cycle_ = 0;
- scanline_ += 1;
-
- if (scanline_ == 262)
- {
- scanline_ = 0;
- even_frame_ = !even_frame_;
- }
- }
-
- auto const pre_line = scanline_ == 261;
- auto const visible_line = scanline_ < 240;
- auto const render_line = pre_line || visible_line;
- auto const pre_fetch_cycle = scanline_cycle_ >= 321 && scanline_cycle_ <= 336;
- auto const visible_cycle = scanline_cycle_ >= 1 && scanline_cycle_ <= 256;
- auto const fetch_cycle = pre_fetch_cycle || visible_cycle;
-
- // Background Logic
- if (enable_rendering)
- {
- if (visible_line && visible_cycle)
- {
- render_pixel();
- }
-
- if (render_line && fetch_cycle)
- {
- tile_data_ <<= 4;
- switch (scanline_cycle_ % 8)
- {
- case 1:
- fetch_name_table_byte();
- break;
- case 3:
- fetch_attribute_table_byte();
- break;
- case 5:
- fetch_low_tile_byte();
- break;
- case 7:
- fetch_high_tile_byte();
- break;
- case 0:
- store_tile_data();
- break;
- default:
- break;
- }
- }
-
- if (pre_line && scanline_cycle_ >= 280 && scanline_cycle_ <= 304)
- {
- copy_y();
- }
-
- if (render_line)
- {
- if (fetch_cycle && scanline_cycle_ % 8 == 0)
- {
- increment_x();
- }
- if (scanline_cycle_ == 256)
- {
- increment_y();
- }
- if (scanline_cycle_ == 257)
- {
- copy_x();
- }
- }
- }
-
- // Sprite Logic
- if (enable_rendering)
- {
- if (scanline_cycle_ == 257)
- {
- if (visible_line)
- {
- evaluate_sprites();
- }
- else
- {
- sprite_count_ = 0;
- }
- }
- }
-
- // Vblank Logic
- if (scanline_ == 241 && scanline_cycle_ == 1)
- {
- display_.switch_buffers();
- status_.vblank = true;
- nmi_change();
- }
- if (pre_line && scanline_cycle_ == 1)
- {
- status_.vblank = false;
- nmi_change();
- status_.sprite_zero_hit = false;
- status_.sprite_overflow = false;
- }
- }
-
- auto ppu::render_pixel() -> void
- {
- auto const x = scanline_cycle_ - 1;
- auto const y = scanline_;
-
- auto background_color = color_index{ 0 };
- if (mask_.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;
- }
-
- auto foreground = sprite{};
- auto foreground_color = color_index{ 0 };
- if (mask_.enable_sprites)
- {
- for (auto i = unsigned{ 0 }; i < sprite_count_; ++i)
- {
- auto const s = sprites_[i];
- 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; }
-
- foreground = s;
- foreground_color = color;
- foreground_color.role = role::sprite;
- break;
- }
- }
-
- auto has_background = background_color.color != 0;
- auto has_foreground = foreground_color.color != 0;
-
- if (x < 8 && !mask_.show_background_start) { has_background = false; }
- if (x < 8 && !mask_.show_sprites_start) { has_foreground = false; }
-
- auto color = color_index{ 0 };
- if (!has_background && has_foreground)
- {
- color = foreground_color;
- }
- if (has_background && !has_foreground)
- {
- color = background_color;
- }
- if (has_background && has_foreground)
- {
- if (foreground.is_sprite_zero && x < 255)
- {
- status_.sprite_zero_hit = true;
- }
-
- color = foreground.is_in_front ? foreground_color : background_color;
- }
-
- display_.set(x, y, resolve_color(get_color(color)));
- }
-
- auto ppu::fetch_name_table_byte() -> void
- {
- auto const addr = static_cast<std::uint16_t>(0x2000 | (internal_.v & 0x0FFF));
- name_table_byte_ = read8(address{ addr });
- }
-
- auto ppu::fetch_attribute_table_byte() -> void
- {
- auto const v = internal_.v;
- auto const addr = static_cast<std::uint16_t>(0x23C0 | (v & 0x0C00) | ((v >> 4) & 0x38) | ((v >> 2) & 0x07));
- auto const shift = ((v >> 4) & 4) | (v & 2);
- attribute_table_byte_ = static_cast<std::uint8_t>(((read8(address{ addr }) >> shift) & 3) << 2);
- }
-
- auto ppu::fetch_low_tile_byte() -> void
- {
- auto const fine_y = (internal_.v >> 12) & 7;
- auto const table = control_.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 });
- }
-
- auto ppu::fetch_high_tile_byte() -> void
- {
- auto const fine_y = (internal_.v >> 12) & 7;
- auto const table = control_.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);
- }
-
- auto ppu::store_tile_data() -> void
- {
- auto data = std::uint32_t{ 0 };
- for (auto i = unsigned{ 0 }; i < 8; ++i)
- {
- auto const p1 = (low_tile_byte_ & 0x80) >> 7;
- auto const p2 = (high_tile_byte_ & 0x80) >> 6;
- low_tile_byte_ <<= 1;
- high_tile_byte_ <<= 1;
- data <<= 4;
- data |= attribute_table_byte_ | p1 | p2;
- }
- tile_data_ |= data;
- }
-
- auto ppu::increment_x() -> void
- {
- // If coarse X == 31
- if ((internal_.v & 0x1F) == 0x1F)
- {
- // Coarse X = 0
- // TODO: Check
- // internal_.v &= ~0x1F;
- internal_.v &= 0xFFE0;
- // Switch horizontal nametable
- internal_.v ^= 0x400;
- }
- else
- {
- // Increment coarse X
- internal_.v += 1;
- }
- }
-
- auto ppu::increment_y() -> void
- {
- // If fine Y == 7
- if ((internal_.v & 0x7000) == 0x7000)
- {
- // Fine Y = 0
- internal_.v &= 0x8FFF;
- // Let y = coarse Y
- auto y = (internal_.v & 0x03E0) >> 5;
- if (y == 29)
- {
- // Coarse Y = 0
- y = 0;
- // Switch vertical nametable
- internal_.v ^= 0x0800;
- }
- else if (y == 31)
- {
- // Coarse Y = 0, nametable not switched
- y = 0;
- }
- else
- {
- // Increment coarse Y
- y += 1;
- }
- // Put coarse Y back into v
- // TODO: Check
- // internal_.v &= ~0x03E0;
- internal_.v &= 0xFC1F;
- internal_.v |= y << 5;
- }
- else
- {
- // Increment fine Y
- internal_.v += 0x1000;
- }
- }
-
- auto ppu::copy_x() -> void
- {
- internal_.v = (internal_.v & 0xFBE0) | (internal_.t & 0x041F);
- }
-
- auto ppu::copy_y() -> void
- {
- internal_.v = (internal_.v & 0x841F) | (internal_.t & 0x7BE0);
- }
-
- auto ppu::evaluate_sprites() -> void
- {
- auto const height = sprite_height();
- sprite_count_ = 0;
- for (auto i = unsigned{ 0 }; i < 64; ++i)
- {
- auto const y = oam_[i * 4 + 0];
- auto const a = oam_[i * 4 + 2];
- auto const x = oam_[i * 4 + 3];
- auto const row = static_cast<int>(scanline_) - static_cast<int>(y);
- if (row < 0 || row >= height) { continue; }
-
- if (sprite_count_ < 8)
- {
- sprites_[sprite_count_].pattern = fetch_sprite_pattern(i, row);
- sprites_[sprite_count_].position = x;
- // TODO: Check
- // sprites_[sprite_count_].is_in_front = a & 0b00100000;
- sprites_[sprite_count_].is_in_front = ((a >> 5) & 1) == 0;
- sprites_[sprite_count_].is_sprite_zero = i == 0;
- sprite_count_ += 1;
- }
- else
- {
- status_.sprite_overflow = true;
- break;
- }
- }
- }
-
- auto ppu::fetch_sprite_pattern(unsigned const i, unsigned row) -> std::uint32_t
- {
- auto tile = oam_[i * 4 + 1];
- auto const attributes = oam_[i * 4 + 2];
- auto const flip_vertical = (attributes & 0x80) == 0x80;
- if (flip_vertical) { row = sprite_height() - 1 - row; }
- auto addr = address{};
- switch (control_.sprite_size)
- {
- case sprite_size::_8x8:
- {
- auto const table = control_.sprite_pattern_table;
- addr = address{ static_cast<std::uint16_t>(0x1000 * table + 16 * tile + row) };
- break;
- }
- case sprite_size::_8x16:
- {
- auto const table = tile & 1;
- tile &= 0xFE;
- if (row > 7)
- {
- tile += 1;
- row -= 8;
- }
- addr = address{ static_cast<std::uint16_t>(0x1000 * table + 16 * tile + row) };
- break;
- }
- }
-
- auto const a = (attributes & 3) << 2;
- auto low_tile_byte = read8(addr);
- auto high_tile_byte = read8(addr + 8);
- auto data = std::uint32_t{ 0 };
- for (auto j = unsigned{ 0 }; j < 8; ++j)
- {
- auto const flip_horizontal = (attributes & 0x40) == 0x40;
- std::uint8_t p1, p2;
- if (flip_horizontal)
- {
- p1 = (low_tile_byte & 1) << 0;
- p2 = (high_tile_byte & 1) << 1;
- low_tile_byte >>= 1;
- high_tile_byte >>= 1;
- }
- else
- {
- p1 = (low_tile_byte & 0x80) >> 7;
- p2 = (high_tile_byte & 0x80) >> 6;
- low_tile_byte <<= 1;
- high_tile_byte <<= 1;
- }
- data <<= 4;
- data |= a | p1 | p2;
- }
-
- return data;
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Memory Access
- // -----------------------------------------------------------------------------------------------------------------
-
- //
- // See: https://www.nesdev.org/wiki/PPU_memory_map
- //
-
- auto ppu::read8(address addr) -> std::uint8_t
- {
- addr = addr % 0x4000; // PPU only has 16 KiB addresses.
- if (addr <= address{ 0x3EFF }) { return mapper_.read_ppu(addr, vram_); }
- if (addr <= address{ 0x3FFF })
- {
- auto const index = color_index{ static_cast<std::uint8_t>(addr.get_absolute() % 0x20) };
- auto const color = get_color(index);
- return static_cast<std::uint8_t>(color);
- }
-
- return 0x0;
- }
-
- auto ppu::write8(address addr, std::uint8_t const value) -> void
- {
- addr = addr % 0x4000; // PPU only has 16 KiB addresses.
- if (addr <= address{ 0x3EFF })
- {
- mapper_.write_ppu(addr, vram_, value);
- return;
- }
- if (addr <= address{ 0x3FFF })
- {
- auto const index = color_index{ static_cast<std::uint8_t>(addr.get_absolute() % 0x20) };
- get_color(index) = color{ value };
- return;
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // IO Registers
- // -----------------------------------------------------------------------------------------------------------------
-
- //
- // See: https://www.nesdev.org/wiki/PPU_registers
- //
-
- auto ppu::read_latch() -> std::uint8_t
- {
- return latch_;
- }
-
- auto ppu::read_ppustatus() -> std::uint8_t
- {
- 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);
- return res;
- }
-
- auto ppu::read_oamdata() -> std::uint8_t
- {
- auto res = oam_[oamaddr_];
- if ((oamaddr_ & 0x3) == 0x2)
- {
- res &= 0xE3;
- }
-#ifdef ENABLE_TRACES
- memory_operations.emplace_back(test::memory_operation::type::read_oamdata, res, current_cycles_);
-#endif
- write_latch(res);
- return res;
- }
-
- auto ppu::read_ppudata() -> std::uint8_t
- {
- auto const addr = address{ internal_.v };
- auto res = read8(addr);
- write_latch(ppudata_read_buffer_);
-
- if (addr % 0x4000 <= address{ 0x3EFF })
- {
- std::swap(res, ppudata_read_buffer_);
- }
- else
- {
- // For palette buffers: buffer the mirrored nametable instead.
- ppudata_read_buffer_ = read8(addr - 0x1000);
- }
-
-#ifdef ENABLE_TRACES
- memory_operations.emplace_back(test::memory_operation::type::read_ppudata, res, current_cycles_);
-#endif
- increment_vram();
- return res;
- }
-
- auto ppu::write_latch(std::uint8_t const value) -> void
- {
- latch_ = value;
- }
-
- auto ppu::write_ppuctrl(std::uint8_t const value) -> void
- {
- 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
- {
- 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.
- internal_.t = (internal_.t & 0b1111111111100000) | (value >> 3);
- internal_.x = value & 0b00000111;
- }
- else
- {
- // Second write -> y value.
- internal_.t = (internal_.t & 0b1000111111111111) | ((value & 0b00000111) << 12);
- internal_.t = (internal_.t & 0b1111110000011111) | ((value & 0b11111000) << 2);
- }
-
- internal_.w = !internal_.w;
- }
- }
-
- auto ppu::write_ppumask(std::uint8_t const value) -> void
- {
- 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
- {
- 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.
- internal_.t = (internal_.t & 0b1000000011111111) | ((value & 0b00111111) << 8);
- }
- else
- {
- // Second write.
- internal_.t = (internal_.t & 0b1111111100000000) | ((value & 0b11111111) << 0);
- internal_.v = internal_.t;
- }
-
- internal_.w = !internal_.w;
- }
- }
-
- 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();
- }
-
- 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;
- }
-
- 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
- auto addr = address{ value, 0x00 };
- for (auto i = unsigned{ 0 }; i < 256; ++i)
- {
- oam_[oamaddr_] = cpu_.read8(addr);
- oamaddr_ += 1;
- addr = addr + 1;
- }
-
- auto const stalled_cycles = cycle_count::from_cpu((cpu_.get_cycles().to_cpu() % 2 == 0) ? 513 : 514);
- cpu_.stall_cycles(stalled_cycles);
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Helpers
- // -----------------------------------------------------------------------------------------------------------------
-
- auto ppu::nmi_change() -> void
- {
- auto const should_trigger = control_.vblank_nmi && status_.vblank;
- if (should_trigger)
- {
- cpu_.trigger_nmi();
- }
- }
-
- auto ppu::sprite_height() -> unsigned
- {
- switch (control_.sprite_size)
- {
- case sprite_size::_8x8:
- return 8;
- case sprite_size::_8x16:
- return 16;
- }
-
- return 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;
- }
- }
-
- 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)
- {
- // The first color is mirrored between background and foreground palettes.
- index.role = role::background;
- }
- return palette_buffer_[index.value];
- }
-
- auto ppu::resolve_color(color const color) -> rgb
- {
- auto const index = static_cast<std::uint8_t>(color);
- switch (index & 0x3F)
- {
- case 0x00: return rgb::from_hex(0x666666);
- 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(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(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(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(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);
- default: return rgb::from_hex(0x000000);
- }
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/ppu.hh b/src/nes/ppu.hh
deleted file mode 100644
index fff51de..0000000
--- a/src/nes/ppu.hh
+++ /dev/null
@@ -1,211 +0,0 @@
-#pragma once
-
-#include "nes/util/cycle_count.hh"
-#include "status.hh"
-
-namespace nes
-{
- class address;
- class cpu;
- class mapper;
- class display;
- class rgb;
-
- class ppu
- {
- static constexpr auto vram_size = std::size_t{ 0x800 };
- static constexpr auto oam_size = std::size_t{ 0x100 };
- static constexpr auto palette_buffer_size = std::size_t{ 0x20 };
- static constexpr auto status_mask = std::uint8_t{ 0b11100000 };
- // 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,
- sprite = 1,
- };
-
- enum class color : std::uint8_t
- {
- };
-
- union color_index
- {
- struct __attribute__((packed))
- {
- unsigned color : 2;
- unsigned palette : 2;
- role role : 1;
- };
- std::uint8_t value;
-
- explicit color_index(std::uint8_t const value)
- : value{ value }
- {
- }
- };
-
- struct sprite
- {
- std::uint32_t pattern{ 0 };
- std::uint8_t position{ 0 };
- bool is_in_front{ false };
- bool is_sprite_zero{ false };
- };
-
- cycle_count current_cycles_;
- cpu& cpu_;
- mapper& mapper_;
- display& display_;
- std::uint8_t vram_[vram_size]{};
- std::uint8_t oam_[oam_size]{};
- color palette_buffer_[palette_buffer_size]
- {
- color{ 0x09 }, color{ 0x01 }, color{ 0x00 }, color{ 0x01 },
- color{ 0x00 }, color{ 0x02 }, color{ 0x02 }, color{ 0x0D },
- color{ 0x08 }, color{ 0x10 }, color{ 0x08 }, color{ 0x24 },
- color{ 0x00 }, color{ 0x00 }, color{ 0x04 }, color{ 0x2C },
- color{ 0x09 }, color{ 0x01 }, color{ 0x34 }, color{ 0x03 },
- color{ 0x00 }, color{ 0x04 }, color{ 0x00 }, color{ 0x14 },
- color{ 0x08 }, color{ 0x3A }, color{ 0x00 }, color{ 0x02 },
- 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 __attribute__((packed))
- {
- bool : 1;
- bool : 1;
- bool : 1;
- bool : 1;
- bool : 1;
- bool sprite_overflow : 1;
- bool sprite_zero_hit : 1;
- bool vblank : 1;
- };
- std::uint8_t value{ 0b00000000 };
- } status_{};
- union
- {
- 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
- };
- std::uint8_t value{ 0b00000000 };
- } control_{};
- union
- {
- 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.
- };
- std::uint8_t value{ 0b00000000 };
- } mask_{};
- struct
- {
- // See https://www.nesdev.org/wiki/PPU_scrolling#PPU_internal_registers
- std::uint16_t v{}; // Current VRAM address (15 bits)
- std::uint16_t t{}; // Temporary VRAM address (15 bits)
- std::uint8_t x{}; // Fine X scroll (3 bits)
- bool w{}; // Second write toggle (1 bit)
- } internal_{};
- std::uint8_t oamaddr_{}; // Address accessed by IO registers
- std::uint8_t ppudata_read_buffer_{}; // Delays PPUDATA reads by one.
- unsigned scanline_{ 240 };
- unsigned scanline_cycle_{ 340 };
- bool even_frame_{ true };
- std::uint64_t tile_data_{ 0 }; // Data for the current tile (higher 32 bits) and the next tile (lower 32 bits).
- std::uint8_t name_table_byte_{ 0 };
- std::uint8_t attribute_table_byte_{ 0 };
- std::uint8_t low_tile_byte_{ 0 };
- 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&);
-
- ppu(ppu const&) = delete;
- ppu(ppu&&) = delete;
- 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 step() -> void;
-
- // Memory access
-
- auto read8(address) -> std::uint8_t;
- auto write8(address, std::uint8_t) -> void;
-
- // IO registers
-
- auto read_latch() -> std::uint8_t;
- auto read_ppustatus() -> std::uint8_t;
- auto read_oamdata() -> std::uint8_t;
- auto read_ppudata() -> std::uint8_t;
-
- auto write_latch(std::uint8_t) -> void;
- auto write_ppuctrl(std::uint8_t) -> void;
- auto write_ppuscroll(std::uint8_t) -> void;
- auto write_ppumask(std::uint8_t) -> void;
- auto write_ppuaddr(std::uint8_t) -> void;
- auto write_ppudata(std::uint8_t) -> void;
- auto write_oamaddr(std::uint8_t) -> void;
- auto write_oamdata(std::uint8_t) -> void;
- auto write_oamdma(std::uint8_t) -> void;
-
- private:
- auto render_pixel() -> void;
- auto fetch_name_table_byte() -> void;
- auto fetch_attribute_table_byte() -> void;
- auto fetch_low_tile_byte() -> void;
- auto fetch_high_tile_byte() -> void;
- auto store_tile_data() -> void;
- auto increment_x() -> void;
- auto increment_y() -> void;
- auto copy_x() -> void;
- auto copy_y() -> void;
- auto evaluate_sprites() -> void;
- auto fetch_sprite_pattern(unsigned i, unsigned row) -> std::uint32_t;
-
- // Helpers
-
- auto nmi_change() -> void;
- auto sprite_height() -> unsigned;
- auto increment_vram() -> void;
- auto get_color(color_index) -> color&;
- auto resolve_color(color) -> rgb;
- };
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/util/CMakeLists.txt b/src/nes/util/CMakeLists.txt
deleted file mode 100644
index adaa80c..0000000
--- a/src/nes/util/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-target_sources(
- nes
- PRIVATE
- address.hh
- button-mask.hh
- cycle_count.hh
- rgb.hh) \ No newline at end of file
diff --git a/src/nes/util/address.hh b/src/nes/util/address.hh
deleted file mode 100644
index 7987d07..0000000
--- a/src/nes/util/address.hh
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace nes
-{
- /// An address on the NES.
- class address
- {
- std::uint16_t absolute_{ 0 };
-
- public:
- /// Create a zero address.
- constexpr explicit address() = default;
-
- /// Create an address from its absolute value.
- constexpr explicit address(std::uint16_t const absolute)
- : absolute_{ absolute }
- {
- }
-
- /// Create an address from its page and offset combination.
- constexpr explicit address(std::uint8_t const page, std::uint8_t const offset)
- : address{ static_cast<std::uint16_t>((page << 8) | (offset << 0)) }
- {
- }
-
- /// Get the absolute address value.
- constexpr auto get_absolute() const -> std::uint16_t
- {
- return absolute_;
- }
-
- /// Get the address page (the first 8 bits).
- constexpr auto get_page() const -> std::uint8_t
- {
- return static_cast<std::uint8_t>((get_absolute() & 0xFF00) >> 8);
- }
-
- /// Get the offset within the page (the last 8 bits).
- constexpr auto get_offset() const -> std::uint8_t
- {
- return static_cast<std::uint8_t>((get_absolute() & 0x00FF) >> 0);
- }
- };
-
- constexpr auto operator==(address const a, address const b) -> bool { return a.get_absolute() == b.get_absolute(); }
- constexpr auto operator!=(address const a, address const b) -> bool { return a.get_absolute() != b.get_absolute(); }
- constexpr auto operator<(address const a, address const b) -> bool { return a.get_absolute() < b.get_absolute(); }
- constexpr auto operator>(address const a, address const b) -> bool { return a.get_absolute() > b.get_absolute(); }
- constexpr auto operator<=(address const a, address const b) -> bool { return a.get_absolute() <= b.get_absolute(); }
- constexpr auto operator>=(address const a, address const b) -> bool { return a.get_absolute() >= b.get_absolute(); }
-
- constexpr auto operator+(address const a, std::uint16_t const b) -> address
- {
- return address{ static_cast<std::uint16_t>(a.get_absolute() + b) };
- }
-
- constexpr auto operator+(std::uint16_t const a, address const b) -> address
- {
- return address{ static_cast<std::uint16_t>(a + b.get_absolute()) };
- }
-
- constexpr auto operator-(address const a, std::uint16_t const b) -> address
- {
- return address{ static_cast<std::uint16_t>(a.get_absolute() - b) };
- }
-
- constexpr auto operator%(address const a, std::uint16_t const b) -> address
- {
- return address{ static_cast<std::uint16_t>(a.get_absolute() % b) };
- }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/util/button-mask.hh b/src/nes/util/button-mask.hh
deleted file mode 100644
index b010b9e..0000000
--- a/src/nes/util/button-mask.hh
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace nes
-{
- class button_mask
- {
- std::uint8_t raw_value_{ 0 };
-
- public:
- constexpr explicit button_mask() = default;
-
- static constexpr auto from_raw_value(std::uint8_t const raw_value) -> button_mask
- {
- return button_mask{ raw_value };
- }
-
- constexpr auto add(button_mask const other) -> void { raw_value_ |= other.raw_value_; }
- constexpr auto remove(button_mask const other) -> void { raw_value_ ^= other.raw_value_; }
- constexpr auto contains(button_mask const other) const -> bool { return (raw_value_ & other.raw_value_) == other.raw_value_; }
- constexpr auto is_empty() const -> bool { return raw_value_ == 0; }
- constexpr auto get_raw_value() const -> std::uint8_t { return raw_value_; }
-
- private:
- constexpr explicit button_mask(std::uint8_t const raw_value)
- : raw_value_{ raw_value }
- {
- }
- };
-
- namespace buttons
- {
- static constexpr auto a = button_mask::from_raw_value(1 << 0);
- static constexpr auto b = button_mask::from_raw_value(1 << 1);
- static constexpr auto select = button_mask::from_raw_value(1 << 2);
- static constexpr auto start = button_mask::from_raw_value(1 << 3);
- static constexpr auto up = button_mask::from_raw_value(1 << 4);
- static constexpr auto down = button_mask::from_raw_value(1 << 5);
- static constexpr auto left = button_mask::from_raw_value(1 << 6);
- static constexpr auto right = button_mask::from_raw_value(1 << 7);
- } // namespace buttons
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/util/cycle_count.hh b/src/nes/util/cycle_count.hh
deleted file mode 100644
index 8afd40a..0000000
--- a/src/nes/util/cycle_count.hh
+++ /dev/null
@@ -1,87 +0,0 @@
-#pragma once
-
-#include <chrono>
-
-namespace nes
-{
- /// A cycle count that can be used for multiple components.
- ///
- /// Each component can have a different clock frequency and this structure offers conversions between different
- /// cycle counts.
- 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;
-
- std::uint64_t units_{ 0 }; // The most fine-granular cycle count (here: PPU cycles).
-
- static constexpr auto ppu_cycle() -> cycle_count { return cycle_count{ 1 }; }
- static constexpr auto cpu_cycle() -> cycle_count { return cycle_count{ 3 }; }
- static constexpr auto second() -> cycle_count { return 1789773 * cpu_cycle(); }
-
- public:
- constexpr explicit cycle_count() = default;
-
- constexpr auto operator+=(cycle_count const other) -> cycle_count& { return *this = *this + other; }
- constexpr auto operator*=(std::uint64_t const other) -> cycle_count& { return *this = *this * other; }
-
- static constexpr auto from_units(std::uint64_t const units) -> cycle_count
- {
- return cycle_count{ units };
- }
-
- static constexpr auto from_duration(std::chrono::microseconds const duration) -> cycle_count
- {
- return from_units(duration.count() * second().get_units() / (1000 * 1000));
- }
-
- static constexpr auto from_cpu(std::uint64_t const cpu_cycles) -> cycle_count
- {
- return cpu_cycles * cpu_cycle();
- }
-
- static constexpr auto from_ppu(std::uint64_t const ppu_cycles) -> cycle_count
- {
- return ppu_cycles * ppu_cycle();
- }
-
- /// Value in implementation-defined units.
- constexpr auto get_units() const -> std::uint64_t { return units_; }
-
- constexpr auto to_duration() const -> std::chrono::microseconds
- {
- return std::chrono::microseconds{ get_units() * 1000 * 1000 / second().get_units() };
- }
-
- constexpr auto to_cpu() const -> std::uint64_t
- {
- return *this / cpu_cycle();
- }
-
- constexpr auto to_ppu() const -> std::uint64_t
- {
- return *this / ppu_cycle();
- }
-
- private:
- constexpr explicit cycle_count(std::uint64_t const units)
- : units_{ 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) -> 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) -> 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) -> 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(); }
-} // namespace nes \ No newline at end of file
diff --git a/src/nes/util/rgb.hh b/src/nes/util/rgb.hh
deleted file mode 100644
index d62c70c..0000000
--- a/src/nes/util/rgb.hh
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace nes
-{
- /// An RGB value.
- class rgb
- {
- public:
- explicit rgb() = default;
-
- explicit rgb(std::uint8_t const r, std::uint8_t const g, std::uint8_t const b)
- : r{ r }
- , g{ g }
- , b{ b }
- {
- }
-
- static auto from_hex(std::uint32_t const hex) -> rgb
- {
- auto res = rgb{};
- res.r = (hex & 0x00FF0000) >> 16;
- res.g = (hex & 0x0000FF00) >> 8;
- res.b = (hex & 0x000000FF) >> 0;
- return res;
- }
-
- std::uint8_t r{ 0 };
- std::uint8_t g{ 0 };
- std::uint8_t b{ 0 };
- };
-} // namespace nes \ No newline at end of file