diff options
Diffstat (limited to 'lib/nes')
| -rw-r--r-- | lib/nes/CMakeLists.txt | 20 | ||||
| -rw-r--r-- | lib/nes/common/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | lib/nes/common/debug.hh (renamed from lib/nes/util/debug.hh) | 0 | ||||
| -rw-r--r-- | lib/nes/common/display.hh (renamed from lib/nes/util/display.hh) | 2 | ||||
| -rw-r--r-- | lib/nes/common/rgb.hh (renamed from lib/nes/util/rgb.hh) | 0 | ||||
| -rw-r--r-- | lib/nes/ref/cartridge.cc | 70 | ||||
| -rw-r--r-- | lib/nes/ref/cartridge.hh | 41 | ||||
| -rw-r--r-- | lib/nes/ref/controller.cc | 18 | ||||
| -rw-r--r-- | lib/nes/ref/controller.hh | 30 | ||||
| -rw-r--r-- | lib/nes/ref/cpu.cc | 1358 | ||||
| -rw-r--r-- | lib/nes/ref/cpu.hh | 298 | ||||
| -rw-r--r-- | lib/nes/ref/mapper.cc | 134 | ||||
| -rw-r--r-- | lib/nes/ref/mapper.hh | 64 | ||||
| -rw-r--r-- | lib/nes/ref/nes.cc | 74 | ||||
| -rw-r--r-- | lib/nes/ref/nes.hh | 49 | ||||
| -rw-r--r-- | lib/nes/ref/ppu.cc | 737 | ||||
| -rw-r--r-- | lib/nes/ref/ppu.hh | 212 | ||||
| -rw-r--r-- | lib/nes/sys/CMakeLists.txt (renamed from lib/nes/ref/CMakeLists.txt) | 10 | ||||
| -rw-r--r-- | lib/nes/sys/cartridge.cc (renamed from lib/nes/cartridge.cc) | 4 | ||||
| -rw-r--r-- | lib/nes/sys/cartridge.hh (renamed from lib/nes/cartridge.hh) | 4 | ||||
| -rw-r--r-- | lib/nes/sys/controller.cc (renamed from lib/nes/controller.cc) | 2 | ||||
| -rw-r--r-- | lib/nes/sys/controller.hh (renamed from lib/nes/controller.hh) | 2 | ||||
| -rw-r--r-- | lib/nes/sys/cpu.cc (renamed from lib/nes/cpu.cc) | 10 | ||||
| -rw-r--r-- | lib/nes/sys/cpu.hh (renamed from lib/nes/cpu.hh) | 6 | ||||
| -rw-r--r-- | lib/nes/sys/mapper.cc (renamed from lib/nes/mapper.cc) | 4 | ||||
| -rw-r--r-- | lib/nes/sys/mapper.hh (renamed from lib/nes/mapper.hh) | 4 | ||||
| -rw-r--r-- | lib/nes/sys/nes.cc (renamed from lib/nes/nes.cc) | 8 | ||||
| -rw-r--r-- | lib/nes/sys/nes.hh (renamed from lib/nes/nes.hh) | 10 | ||||
| -rw-r--r-- | lib/nes/sys/ppu.cc (renamed from lib/nes/ppu.cc) | 16 | ||||
| -rw-r--r-- | lib/nes/sys/ppu.hh (renamed from lib/nes/ppu.hh) | 2 | ||||
| -rw-r--r-- | lib/nes/sys/types/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | lib/nes/sys/types/address.hh (renamed from lib/nes/util/address.hh) | 3 | ||||
| -rw-r--r-- | lib/nes/sys/types/button-mask.hh (renamed from lib/nes/util/button-mask.hh) | 1 | ||||
| -rw-r--r-- | lib/nes/sys/types/cycle-count.hh (renamed from lib/nes/util/cycle-count.hh) | 0 | ||||
| -rw-r--r-- | lib/nes/sys/types/snapshot.hh (renamed from lib/nes/util/snapshot.hh) | 2 | ||||
| -rw-r--r-- | lib/nes/sys/types/status.hh (renamed from lib/nes/util/status.hh) | 0 | ||||
| -rw-r--r-- | lib/nes/util/CMakeLists.txt | 11 |
37 files changed, 63 insertions, 3157 deletions
diff --git a/lib/nes/CMakeLists.txt b/lib/nes/CMakeLists.txt index 3fb7a73..78c09fe 100644 --- a/lib/nes/CMakeLists.txt +++ b/lib/nes/CMakeLists.txt @@ -1,18 +1,2 @@ -target_sources( - nes - PRIVATE - nes.hh - nes.cc - cartridge.hh - cartridge.cc - controller.hh - controller.cc - cpu.hh - cpu.cc - ppu.hh - ppu.cc - mapper.hh - mapper.cc) - -add_subdirectory(util) -add_subdirectory(ref)
\ No newline at end of file +add_subdirectory(common) +add_subdirectory(sys)
\ No newline at end of file diff --git a/lib/nes/common/CMakeLists.txt b/lib/nes/common/CMakeLists.txt new file mode 100644 index 0000000..69ae435 --- /dev/null +++ b/lib/nes/common/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources( + nes + PRIVATE + debug.hh + rgb.hh + display.hh)
\ No newline at end of file diff --git a/lib/nes/util/debug.hh b/lib/nes/common/debug.hh index 409d55a..409d55a 100644 --- a/lib/nes/util/debug.hh +++ b/lib/nes/common/debug.hh diff --git a/lib/nes/util/display.hh b/lib/nes/common/display.hh index 8b6e0e5..ca78a85 100644 --- a/lib/nes/util/display.hh +++ b/lib/nes/common/display.hh @@ -1,6 +1,6 @@ #pragma once -#include "nes/util/rgb.hh" +#include "nes/common/rgb.hh" namespace nes { diff --git a/lib/nes/util/rgb.hh b/lib/nes/common/rgb.hh index d62c70c..d62c70c 100644 --- a/lib/nes/util/rgb.hh +++ b/lib/nes/common/rgb.hh diff --git a/lib/nes/ref/cartridge.cc b/lib/nes/ref/cartridge.cc deleted file mode 100644 index 7f8257e..0000000 --- a/lib/nes/ref/cartridge.cc +++ /dev/null @@ -1,70 +0,0 @@ -#include "nes/ref/cartridge.hh" -#include <fstream> - -namespace nes::ref -{ - 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/cartridge.hh b/lib/nes/ref/cartridge.hh deleted file mode 100644 index ce94757..0000000 --- a/lib/nes/ref/cartridge.hh +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include <cstddef> -#include <vector> -#include <string_view> - -namespace nes::ref -{ - 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/controller.cc b/lib/nes/ref/controller.cc deleted file mode 100644 index 70d6ba2..0000000 --- a/lib/nes/ref/controller.cc +++ /dev/null @@ -1,18 +0,0 @@ -#include "nes/ref/controller.hh" - -namespace nes::ref -{ - 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 static_cast<std::uint8_t>(res); - } - - auto controller::write(std::uint8_t const value) -> void - { - strobing_ = value & 1; - if (strobing_) { index_ = 0; } - } -} // namespace nes::ref
\ No newline at end of file diff --git a/lib/nes/ref/controller.hh b/lib/nes/ref/controller.hh deleted file mode 100644 index 231653c..0000000 --- a/lib/nes/ref/controller.hh +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "nes/util/button-mask.hh" - -namespace nes::ref -{ - 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/cpu.cc b/lib/nes/ref/cpu.cc deleted file mode 100644 index 79b0c56..0000000 --- a/lib/nes/ref/cpu.cc +++ /dev/null @@ -1,1358 +0,0 @@ -#include "nes/ref/cpu.hh" -#include "nes/ref/ppu.hh" -#include "nes/ref/controller.hh" -#include "nes/ref/mapper.hh" -#include "nes/util/snapshot.hh" - -namespace nes::ref -{ - 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::build_snapshot(snapshot& 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 - -#ifdef NES_ENABLE_PPU - if (nmi_pending_) - { - execute_interrupt(address{ 0xFFFA }); - nmi_pending_ = false; - } -#endif - - 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>{ 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 NES_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 NES_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 NES_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 NES_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::ref
\ No newline at end of file diff --git a/lib/nes/ref/cpu.hh b/lib/nes/ref/cpu.hh deleted file mode 100644 index 05efe56..0000000 --- a/lib/nes/ref/cpu.hh +++ /dev/null @@ -1,298 +0,0 @@ -#pragma once - -#include "nes/util/cycle-count.hh" -#include "nes/util/address.hh" - -namespace nes -{ - struct snapshot; -} // namespace nes - -namespace nes::ref -{ - 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> - { - std::uint8_t value_{ 0 }; - - public: - explicit operand(std::uint8_t const value) - : 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 get_cycles() const -> cycle_count { return current_cycles_; } - auto build_snapshot(snapshot&) -> void; - auto stall_cycles(cycle_count) -> void; - auto trigger_nmi() -> void; - auto step() -> void; - 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/mapper.cc b/lib/nes/ref/mapper.cc deleted file mode 100644 index 7e4cffe..0000000 --- a/lib/nes/ref/mapper.cc +++ /dev/null @@ -1,134 +0,0 @@ -#include "nes/ref/mapper.hh" -#include "nes/ref/cartridge.hh" -#include <iostream> - -namespace nes::ref -{ - 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::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::ref
\ No newline at end of file diff --git a/lib/nes/ref/mapper.hh b/lib/nes/ref/mapper.hh deleted file mode 100644 index 671e1d4..0000000 --- a/lib/nes/ref/mapper.hh +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "nes/util/address.hh" - -namespace nes::ref -{ - 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 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/nes.cc b/lib/nes/ref/nes.cc deleted file mode 100644 index e34ca9a..0000000 --- a/lib/nes/ref/nes.cc +++ /dev/null @@ -1,74 +0,0 @@ -#include "nes/ref/nes.hh" -#include "nes/ref/mapper.hh" -#include "nes/util/display.hh" -#include "nes/util/rgb.hh" -#include "nes/util/snapshot.hh" -#include <iostream> - -namespace nes::ref -{ - 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::get_snapshot() -> snapshot - { - auto res = snapshot{}; - res.sram = std::vector(cartridge_.get_ram(), cartridge_.get_ram() + cartridge_.get_ram_length()); - cpu_.build_snapshot(res); - ppu_.build_snapshot(res); - return res; - } -} // namespace nes::ref
\ No newline at end of file diff --git a/lib/nes/ref/nes.hh b/lib/nes/ref/nes.hh deleted file mode 100644 index 3df8646..0000000 --- a/lib/nes/ref/nes.hh +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "nes/util/cycle-count.hh" -#include "nes/ref/cartridge.hh" -#include "nes/ref/controller.hh" -#include "nes/ref/cpu.hh" -#include "nes/ref/ppu.hh" -#include "nes/ref/mapper.hh" - -namespace nes -{ - class display; - struct snapshot; -} // namespace nes - -namespace nes::ref -{ - /// 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 step(std::chrono::microseconds delta) -> void; - auto step(cycle_count delta) -> void; - auto step_to_nmi() -> void; // XXX: Debugging - - auto get_snapshot() -> snapshot; - }; -} // namespace nes::ref
\ No newline at end of file diff --git a/lib/nes/ref/ppu.cc b/lib/nes/ref/ppu.cc deleted file mode 100644 index 0856093..0000000 --- a/lib/nes/ref/ppu.cc +++ /dev/null @@ -1,737 +0,0 @@ -#include "nes/ref/ppu.hh" -#include "nes/ref/cpu.hh" -#include "nes/ref/mapper.hh" -#include "nes/util/address.hh" -#include "nes/util/display.hh" -#include "nes/util/rgb.hh" -#include "nes/util/snapshot.hh" -#include "nes/util/debug.hh" - -namespace nes::ref -{ - ppu::ppu(cpu& cpu, mapper& mapper, display& display) - : cpu_{ cpu } - , mapper_{ mapper } - , display_{ display } - { - } - - auto ppu::build_snapshot(snapshot& snapshot) -> void - { - snapshot.vram = std::vector(std::begin(vram_), std::end(vram_)); - snapshot.oam = std::vector(std::begin(oam_), std::end(oam_)); - } - - 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; - 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); - NES_DEBUG_LOG(ppu, "PPUSTATUS -> {:#2x}", res); - - status_.vblank = false; - internal_.w = false; - - write_latch(res); - return res; - } - - auto ppu::read_oamdata() -> std::uint8_t - { - auto res = oam_[oamaddr_]; - if ((oamaddr_ & 0x3) == 0x2) - { - res &= 0xE3; - } - NES_DEBUG_LOG(ppu, "OAMDATA -> {:#2x}", res); - 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); - } - - NES_DEBUG_LOG(ppu, "PPUDATA -> {:#2x}", res); - 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 - { - write_latch(value); - if (current_cycles_ > boot_up_cycles) - { - NES_DEBUG_LOG(ppu, "PPUCTRL <- {:#2x}", value); - control_.value = value; - internal_.t = (internal_.t & 0xF3FF) | ((value & 0x03) << 10); - nmi_change(); - } - } - - auto ppu::write_ppuscroll(std::uint8_t const value) -> void - { - write_latch(value); - if (current_cycles_ > boot_up_cycles) - { - NES_DEBUG_LOG(ppu, "PPUSCROLL <- {:#2x}", value); - 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 - { - write_latch(value); - if (current_cycles_ > boot_up_cycles) - { - NES_DEBUG_LOG(ppu, "PPUMASK <- {:#2x}", value); - mask_.value = value; - } - } - - auto ppu::write_ppuaddr(std::uint8_t const value) -> void - { - write_latch(value); - if (current_cycles_ > boot_up_cycles) - { - NES_DEBUG_LOG(ppu, "PPUADDR <- {:#2x}", value); - 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 - { - NES_DEBUG_LOG(ppu, "PPUDATA <- {:#2x} (address: {:#4x})", value, internal_.v); - write_latch(value); - write8(address{ internal_.v }, value); - increment_vram(); - } - - auto ppu::write_oamaddr(std::uint8_t const value) -> void - { - NES_DEBUG_LOG(ppu, "OAMADDR <- {:#2x}", value); - write_latch(value); - oamaddr_ = value; - } - - auto ppu::write_oamdata(std::uint8_t const value) -> void - { - NES_DEBUG_LOG(ppu, "OAMDATA <- {:#2x}", value); - 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? - NES_DEBUG_LOG(ppu, "OAMDMA <- {:#2x}", value); - 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 - { - if (control_.vblank_nmi && status_.vblank) - { - 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/ppu.hh b/lib/nes/ref/ppu.hh deleted file mode 100644 index 400ebdb..0000000 --- a/lib/nes/ref/ppu.hh +++ /dev/null @@ -1,212 +0,0 @@ -#pragma once - -#include "nes/util/cycle-count.hh" - -namespace nes -{ - class address; - class display; - class rgb; - struct snapshot; -} // namespace nes - -namespace nes::ref -{ - class cpu; - class mapper; - - 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_. - - public: - explicit ppu(cpu&, mapper&, display&); - - ppu(ppu const&) = delete; - ppu(ppu&&) = delete; - auto operator=(ppu const&) -> ppu& = delete; - auto operator=(ppu&&) -> ppu& = delete; - - auto get_cycles() const -> cycle_count { return current_cycles_; } - auto build_snapshot(snapshot&) -> 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::ref
\ No newline at end of file diff --git a/lib/nes/ref/CMakeLists.txt b/lib/nes/sys/CMakeLists.txt index ce97573..60f77d2 100644 --- a/lib/nes/ref/CMakeLists.txt +++ b/lib/nes/sys/CMakeLists.txt @@ -1,8 +1,6 @@ target_sources( - nes_ref + nes PRIVATE - nes.hh - nes.cc cartridge.hh cartridge.cc controller.hh @@ -12,4 +10,8 @@ target_sources( ppu.hh ppu.cc mapper.hh - mapper.cc)
\ No newline at end of file + mapper.cc + nes.hh + nes.cc) + +add_subdirectory(types)
\ No newline at end of file diff --git a/lib/nes/cartridge.cc b/lib/nes/sys/cartridge.cc index bd8e7d3..d60ea8b 100644 --- a/lib/nes/cartridge.cc +++ b/lib/nes/sys/cartridge.cc @@ -1,5 +1,5 @@ -#include "nes/cartridge.hh" -#include "nes/util/status.hh" +#include "nes/sys/cartridge.hh" +#include "nes/sys/types/status.hh" #include <fstream> namespace nes diff --git a/lib/nes/cartridge.hh b/lib/nes/sys/cartridge.hh index fb75946..a8b61a2 100644 --- a/lib/nes/cartridge.hh +++ b/lib/nes/sys/cartridge.hh @@ -1,7 +1,7 @@ #pragma once -#include "nes/util/status.hh" -#include "nes/mapper.hh" +#include "nes/sys/types/status.hh" +#include "nes/sys/mapper.hh" #include <cstddef> #include <vector> #include <string_view> diff --git a/lib/nes/controller.cc b/lib/nes/sys/controller.cc index d9d6946..910ef57 100644 --- a/lib/nes/controller.cc +++ b/lib/nes/sys/controller.cc @@ -1,4 +1,4 @@ -#include "nes/controller.hh" +#include "nes/sys/controller.hh" namespace nes { diff --git a/lib/nes/controller.hh b/lib/nes/sys/controller.hh index eb87545..441e236 100644 --- a/lib/nes/controller.hh +++ b/lib/nes/sys/controller.hh @@ -1,6 +1,6 @@ #pragma once -#include "nes/util/button-mask.hh" +#include "nes/sys/types/button-mask.hh" namespace nes { diff --git a/lib/nes/cpu.cc b/lib/nes/sys/cpu.cc index cf8943f..84fbe78 100644 --- a/lib/nes/cpu.cc +++ b/lib/nes/sys/cpu.cc @@ -1,8 +1,8 @@ -#include "nes/cpu.hh" -#include "nes/ppu.hh" -#include "nes/controller.hh" -#include "nes/cartridge.hh" -#include "nes/util/snapshot.hh" +#include "nes/sys/cpu.hh" +#include "nes/sys/ppu.hh" +#include "nes/sys/controller.hh" +#include "nes/sys/cartridge.hh" +#include "nes/sys/types/snapshot.hh" namespace nes { diff --git a/lib/nes/cpu.hh b/lib/nes/sys/cpu.hh index d5bc032..d0901c0 100644 --- a/lib/nes/cpu.hh +++ b/lib/nes/sys/cpu.hh @@ -1,8 +1,8 @@ #pragma once -#include "nes/util/cycle-count.hh" -#include "nes/util/address.hh" -#include "nes/util/status.hh" +#include "nes/sys/types/cycle-count.hh" +#include "nes/sys/types/address.hh" +#include "nes/sys/types/status.hh" namespace nes { diff --git a/lib/nes/mapper.cc b/lib/nes/sys/mapper.cc index 08af1ae..4951966 100644 --- a/lib/nes/mapper.cc +++ b/lib/nes/sys/mapper.cc @@ -1,5 +1,5 @@ -#include "nes/mapper.hh" -#include "nes/cartridge.hh" +#include "nes/sys/mapper.hh" +#include "nes/sys/cartridge.hh" namespace nes { diff --git a/lib/nes/mapper.hh b/lib/nes/sys/mapper.hh index 7e41b23..e196b25 100644 --- a/lib/nes/mapper.hh +++ b/lib/nes/sys/mapper.hh @@ -1,7 +1,7 @@ #pragma once -#include "nes/util/address.hh" -#include "nes/util/status.hh" +#include "nes/sys/types/address.hh" +#include "nes/sys/types/status.hh" namespace nes { diff --git a/lib/nes/nes.cc b/lib/nes/sys/nes.cc index 9a35bfa..f922b7d 100644 --- a/lib/nes/nes.cc +++ b/lib/nes/sys/nes.cc @@ -1,7 +1,7 @@ -#include "nes/nes.hh" -#include "nes/util/display.hh" -#include "nes/util/rgb.hh" -#include "nes/util/snapshot.hh" +#include "nes/sys/nes.hh" +#include "nes/sys/types/snapshot.hh" +#include "nes/common/display.hh" +#include "nes/common/rgb.hh" namespace nes { diff --git a/lib/nes/nes.hh b/lib/nes/sys/nes.hh index dab5ded..b421a5f 100644 --- a/lib/nes/nes.hh +++ b/lib/nes/sys/nes.hh @@ -1,10 +1,10 @@ #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/sys/types/cycle-count.hh" +#include "nes/sys/cartridge.hh" +#include "nes/sys/controller.hh" +#include "nes/sys/cpu.hh" +#include "nes/sys/ppu.hh" namespace nes { diff --git a/lib/nes/ppu.cc b/lib/nes/sys/ppu.cc index 815224f..2a5f01c 100644 --- a/lib/nes/ppu.cc +++ b/lib/nes/sys/ppu.cc @@ -1,11 +1,11 @@ -#include "nes/ppu.hh" -#include "nes/cpu.hh" -#include "nes/cartridge.hh" -#include "nes/util/address.hh" -#include "nes/util/display.hh" -#include "nes/util/rgb.hh" -#include "nes/util/snapshot.hh" -#include "nes/util/debug.hh" +#include "nes/sys/ppu.hh" +#include "nes/sys/cpu.hh" +#include "nes/sys/cartridge.hh" +#include "nes/sys/types/address.hh" +#include "nes/sys/types/snapshot.hh" +#include "nes/common/display.hh" +#include "nes/common/rgb.hh" +#include "nes/common/debug.hh" namespace nes { diff --git a/lib/nes/ppu.hh b/lib/nes/sys/ppu.hh index afc295c..b1b8842 100644 --- a/lib/nes/ppu.hh +++ b/lib/nes/sys/ppu.hh @@ -1,6 +1,6 @@ #pragma once -#include "nes/util/cycle-count.hh" +#include "nes/sys/types/cycle-count.hh" namespace nes { diff --git a/lib/nes/sys/types/CMakeLists.txt b/lib/nes/sys/types/CMakeLists.txt new file mode 100644 index 0000000..f51b0ae --- /dev/null +++ b/lib/nes/sys/types/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources( + nes + PRIVATE + address.hh + button-mask.hh + cycle-count.hh + snapshot.hh + status.hh)
\ No newline at end of file diff --git a/lib/nes/util/address.hh b/lib/nes/sys/types/address.hh index 2cce2f0..c2e1352 100644 --- a/lib/nes/util/address.hh +++ b/lib/nes/sys/types/address.hh @@ -4,7 +4,8 @@ namespace nes { - /// An address on the NES. + /// An address on the NES (for simplicity, we share the same type for CPU and PPU, even though the PPU only uses + /// 14 bit addresses). class address { std::uint16_t absolute_{ 0 }; diff --git a/lib/nes/util/button-mask.hh b/lib/nes/sys/types/button-mask.hh index b010b9e..16b0dad 100644 --- a/lib/nes/util/button-mask.hh +++ b/lib/nes/sys/types/button-mask.hh @@ -4,6 +4,7 @@ namespace nes { + /// Buttons pressed on a controller. class button_mask { std::uint8_t raw_value_{ 0 }; diff --git a/lib/nes/util/cycle-count.hh b/lib/nes/sys/types/cycle-count.hh index f1a4a7e..f1a4a7e 100644 --- a/lib/nes/util/cycle-count.hh +++ b/lib/nes/sys/types/cycle-count.hh diff --git a/lib/nes/util/snapshot.hh b/lib/nes/sys/types/snapshot.hh index de23a46..683e862 100644 --- a/lib/nes/util/snapshot.hh +++ b/lib/nes/sys/types/snapshot.hh @@ -1,6 +1,6 @@ #pragma once -#include "nes/util/cycle-count.hh" +#include "nes/sys/types/cycle-count.hh" #include <vector> #include <cstdint> diff --git a/lib/nes/util/status.hh b/lib/nes/sys/types/status.hh index 64f5969..64f5969 100644 --- a/lib/nes/util/status.hh +++ b/lib/nes/sys/types/status.hh diff --git a/lib/nes/util/CMakeLists.txt b/lib/nes/util/CMakeLists.txt deleted file mode 100644 index 6f5c112..0000000 --- a/lib/nes/util/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -target_sources( - nes - PRIVATE - address.hh - button-mask.hh - cycle-count.hh - debug.hh - display.hh - rgb.hh - snapshot.hh - status.hh)
\ No newline at end of file |
