summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-01-26 23:19:56 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-01-26 23:19:56 +0100
commit430a246fe81d7eab6823bf6ca4562fd1e99f2a5d (patch)
tree75c5f4e8eeeb1e5f25c866e828440e5c493bed8b /lib
parentd1f4b65eafe9c41ecca7bbe8026ec0a693c1ecda (diff)
refactor mappers
Diffstat (limited to 'lib')
-rw-r--r--lib/nes/cartridge.cc37
-rw-r--r--lib/nes/cartridge.hh17
-rw-r--r--lib/nes/cpu.cc763
-rw-r--r--lib/nes/cpu.hh13
-rw-r--r--lib/nes/mapper.cc252
-rw-r--r--lib/nes/mapper.hh56
-rw-r--r--lib/nes/nes.cc20
-rw-r--r--lib/nes/nes.hh4
-rw-r--r--lib/nes/ppu.cc15
-rw-r--r--lib/nes/ppu.hh9
-rw-r--r--lib/nes/util/CMakeLists.txt3
-rw-r--r--lib/nes/util/status.hh36
12 files changed, 674 insertions, 551 deletions
diff --git a/lib/nes/cartridge.cc b/lib/nes/cartridge.cc
index d4ade23..6a60741 100644
--- a/lib/nes/cartridge.cc
+++ b/lib/nes/cartridge.cc
@@ -1,36 +1,38 @@
#include "nes/cartridge.hh"
+#include "nes/util/status.hh"
#include <fstream>
namespace nes
{
- namespace
+ cartridge::cartridge(status const status)
+ : status_{ status }
{
- 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;
+ auto res = cartridge{ status::success };
// Parse header.
- if (length < 16) { return cartridge{}; }
- if (bytes[0] != 'N' || bytes[1] != 'E' || bytes[2] != 'S' || bytes[3] != 0x1A) { return cartridge{}; }
+ if (length < 16) { return cartridge{ status::error_invalid_ines_data }; }
+ if (bytes[0] != 'N' || bytes[1] != 'E' || bytes[2] != 'S' || bytes[3] != 0x1A)
+ {
+ return cartridge{ status::error_invalid_ines_data };
+ }
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] : 1u;
auto const has_trainer = (control_1 & 0b00000100) != 0;
-
- res.mapper_number_ =
+ auto const mapper_number = static_cast<std::uint8_t>(
(control_1 & 0b11110000) >> 4 |
- (control_2 & 0b11110000) >> 0;
+ (control_2 & 0b11110000) >> 0);
+
+ res.mapper_ = &mapper::get(mapper_number);
res.name_table_arrangement_ = (control_1 & 0b00000001) != 0
? name_table_arrangement::vertical
: name_table_arrangement::horizontal;
@@ -40,24 +42,29 @@ namespace nes
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{}; }
+ if (length < offset + prg_rom_size) { return cartridge{ status::error_invalid_ines_data }; }
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{}; }
+ if (length < offset + chr_rom_size) { return cartridge{ status::error_invalid_ines_data }; }
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 });
+ if (auto const status = res.get_mapper().validate(res); status != status::success)
+ {
+ return cartridge{ status };
+ }
+
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{}; }
+ if (!stream.good()) { return cartridge{ status::error_system_error }; }
stream.seekg(0, std::ios::end);
auto const length = stream.tellg();
diff --git a/lib/nes/cartridge.hh b/lib/nes/cartridge.hh
index 3ce2a16..13c3594 100644
--- a/lib/nes/cartridge.hh
+++ b/lib/nes/cartridge.hh
@@ -1,5 +1,7 @@
#pragma once
+#include "nes/util/status.hh"
+#include "nes/mapper.hh"
#include <cstddef>
#include <vector>
#include <string_view>
@@ -14,12 +16,16 @@ namespace nes
class cartridge
{
- bool is_valid_{ false };
+ static constexpr auto prg_rom_bank_size = std::size_t{ 16 * 1024 };
+ static constexpr auto chr_rom_bank_size = std::size_t{ 8 * 1024 };
+ static constexpr auto ram_bank_size = std::size_t{ 8 * 1024 };
+
+ status status_{ status::error_uninitialized };
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_{};
+ mapper* mapper_{ &mapper::invalid() };
public:
explicit cartridge() = default;
@@ -27,7 +33,7 @@ namespace nes
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_status() const -> status { return status_; }
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(); }
@@ -35,7 +41,10 @@ namespace nes
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_mapper() const -> mapper& { return *mapper_; }
auto get_name_table_arrangement() const -> name_table_arrangement { return name_table_arrangement_; }
+
+ private:
+ explicit cartridge(status);
};
} // namespace nes \ No newline at end of file
diff --git a/lib/nes/cpu.cc b/lib/nes/cpu.cc
index ee7416d..cf8943f 100644
--- a/lib/nes/cpu.cc
+++ b/lib/nes/cpu.cc
@@ -1,14 +1,14 @@
#include "nes/cpu.hh"
#include "nes/ppu.hh"
#include "nes/controller.hh"
-#include "nes/mapper.hh"
+#include "nes/cartridge.hh"
#include "nes/util/snapshot.hh"
namespace nes
{
- cpu::cpu(ppu& ppu, mapper& mapper, controller& controller_1, controller& controller_2)
+ cpu::cpu(ppu& ppu, cartridge& cartridge, controller& controller_1, controller& controller_2)
: ppu_{ ppu }
- , mapper_{ mapper }
+ , cartridge_{ cartridge }
, controller_1_{ controller_1 }
, controller_2_{ controller_2 }
{
@@ -44,7 +44,7 @@ namespace nes
nmi_pending_ = true;
}
- auto cpu::step() -> void
+ auto cpu::step() -> status
{
// See https://www.nesdev.org/wiki/CPU_unofficial_opcodes
@@ -59,263 +59,263 @@ namespace nes
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();
+ case 0x00: return run_brk<addressing_mode::immediate>();
+ case 0x01: return run_ora<addressing_mode::indexed_indirect>();
+ case 0x02: return run_stp();
+ case 0x03: return run_slo<addressing_mode::indexed_indirect>();
+ case 0x04: return run_nop<addressing_mode::zero_page>();
+ case 0x05: return run_ora<addressing_mode::zero_page>();
+ case 0x06: return run_asl<addressing_mode::zero_page>();
+ case 0x07: return run_slo<addressing_mode::zero_page>();
+ case 0x08: return run_php();
+ case 0x09: return run_ora<addressing_mode::immediate>();
+ case 0x0A: return run_asl<addressing_mode::accumulator>();
+ case 0x0B: return run_anc<addressing_mode::immediate>();
+ case 0x0C: return run_nop<addressing_mode::absolute>();
+ case 0x0D: return run_ora<addressing_mode::absolute>();
+ case 0x0E: return run_asl<addressing_mode::absolute>();
+ case 0x0F: return run_slo<addressing_mode::absolute>();
+ case 0x10: return run_bpl<addressing_mode::relative>();
+ case 0x11: return run_ora<addressing_mode::indirect_indexed>();
+ case 0x12: return run_stp();
+ case 0x13: return run_slo<addressing_mode::indirect_indexed>();
+ case 0x14: return run_nop<addressing_mode::zero_page_indexed_x>();
+ case 0x15: return run_ora<addressing_mode::zero_page_indexed_x>();
+ case 0x16: return run_asl<addressing_mode::zero_page_indexed_x>();
+ case 0x17: return run_slo<addressing_mode::zero_page_indexed_x>();
+ case 0x18: return run_clc();
+ case 0x19: return run_ora<addressing_mode::absolute_indexed_y>();
+ case 0x1A: return run_nop();
+ case 0x1B: return run_slo<addressing_mode::absolute_indexed_y>();
+ case 0x1C: return run_nop<addressing_mode::absolute_indexed_x>();
+ case 0x1D: return run_ora<addressing_mode::absolute_indexed_x>();
+ case 0x1E: return run_asl<addressing_mode::absolute_indexed_x>();
+ case 0x1F: return run_slo<addressing_mode::absolute_indexed_x>();
+ case 0x20: return run_jsr<addressing_mode::absolute>();
+ case 0x21: return run_and<addressing_mode::indexed_indirect>();
+ case 0x22: return run_stp();
+ case 0x23: return run_rla<addressing_mode::indexed_indirect>();
+ case 0x24: return run_bit<addressing_mode::zero_page>();
+ case 0x25: return run_and<addressing_mode::zero_page>();
+ case 0x26: return run_rol<addressing_mode::zero_page>();
+ case 0x27: return run_rla<addressing_mode::zero_page>();
+ case 0x28: return run_plp();
+ case 0x29: return run_and<addressing_mode::immediate>();
+ case 0x2A: return run_rol<addressing_mode::accumulator>();
+ case 0x2B: return run_anc<addressing_mode::immediate>();
+ case 0x2C: return run_bit<addressing_mode::absolute>();
+ case 0x2D: return run_and<addressing_mode::absolute>();
+ case 0x2E: return run_rol<addressing_mode::absolute>();
+ case 0x2F: return run_rla<addressing_mode::absolute>();
+ case 0x30: return run_bmi<addressing_mode::relative>();
+ case 0x31: return run_and<addressing_mode::indirect_indexed>();
+ case 0x32: return run_stp();
+ case 0x33: return run_rla<addressing_mode::indirect_indexed>();
+ case 0x34: return run_nop<addressing_mode::zero_page_indexed_x>();
+ case 0x35: return run_and<addressing_mode::zero_page_indexed_x>();
+ case 0x36: return run_rol<addressing_mode::zero_page_indexed_x>();
+ case 0x37: return run_rla<addressing_mode::zero_page_indexed_x>();
+ case 0x38: return run_sec();
+ case 0x39: return run_and<addressing_mode::absolute_indexed_y>();
+ case 0x3A: return run_nop();
+ case 0x3B: return run_rla<addressing_mode::absolute_indexed_y>();
+ case 0x3C: return run_nop<addressing_mode::absolute_indexed_x>();
+ case 0x3D: return run_and<addressing_mode::absolute_indexed_x>();
+ case 0x3E: return run_rol<addressing_mode::absolute_indexed_x>();
+ case 0x3F: return run_rla<addressing_mode::absolute_indexed_x>();
+ case 0x40: return run_rti();
+ case 0x41: return run_eor<addressing_mode::indexed_indirect>();
+ case 0x42: return run_stp();
+ case 0x43: return run_sre<addressing_mode::indexed_indirect>();
+ case 0x44: return run_nop<addressing_mode::zero_page>();
+ case 0x45: return run_eor<addressing_mode::zero_page>();
+ case 0x46: return run_lsr<addressing_mode::zero_page>();
+ case 0x47: return run_sre<addressing_mode::zero_page>();
+ case 0x48: return run_pha();
+ case 0x49: return run_eor<addressing_mode::immediate>();
+ case 0x4A: return run_lsr<addressing_mode::accumulator>();
+ case 0x4B: return run_alr<addressing_mode::immediate>();
+ case 0x4C: return run_jmp<addressing_mode::absolute>();
+ case 0x4D: return run_eor<addressing_mode::absolute>();
+ case 0x4E: return run_lsr<addressing_mode::absolute>();
+ case 0x4F: return run_sre<addressing_mode::absolute>();
+ case 0x50: return run_bvc<addressing_mode::relative>();
+ case 0x51: return run_eor<addressing_mode::indirect_indexed>();
+ case 0x52: return run_stp();
+ case 0x53: return run_sre<addressing_mode::indirect_indexed>();
+ case 0x54: return run_nop<addressing_mode::zero_page_indexed_x>();
+ case 0x55: return run_eor<addressing_mode::zero_page_indexed_x>();
+ case 0x56: return run_lsr<addressing_mode::zero_page_indexed_x>();
+ case 0x57: return run_sre<addressing_mode::zero_page_indexed_x>();
+ case 0x58: return run_cli();
+ case 0x59: return run_eor<addressing_mode::absolute_indexed_y>();
+ case 0x5A: return run_nop();
+ case 0x5B: return run_sre<addressing_mode::absolute_indexed_y>();
+ case 0x5C: return run_nop<addressing_mode::absolute_indexed_x>();
+ case 0x5D: return run_eor<addressing_mode::absolute_indexed_x>();
+ case 0x5E: return run_lsr<addressing_mode::absolute_indexed_x>();
+ case 0x5F: return run_sre<addressing_mode::absolute_indexed_x>();
+ case 0x60: return run_rts();
+ case 0x61: return run_adc<addressing_mode::indexed_indirect>();
+ case 0x62: return run_stp();
+ case 0x63: return run_rra<addressing_mode::indexed_indirect>();
+ case 0x64: return run_nop<addressing_mode::zero_page>();
+ case 0x65: return run_adc<addressing_mode::zero_page>();
+ case 0x66: return run_ror<addressing_mode::zero_page>();
+ case 0x67: return run_rra<addressing_mode::zero_page>();
+ case 0x68: return run_pla();
+ case 0x69: return run_adc<addressing_mode::immediate>();
+ case 0x6A: return run_ror<addressing_mode::accumulator>();
+ case 0x6B: return run_arr<addressing_mode::immediate>();
+ case 0x6C: return run_jmp<addressing_mode::indirect>();
+ case 0x6D: return run_adc<addressing_mode::absolute>();
+ case 0x6E: return run_ror<addressing_mode::absolute>();
+ case 0x6F: return run_rra<addressing_mode::absolute>();
+ case 0x70: return run_bvs<addressing_mode::relative>();
+ case 0x71: return run_adc<addressing_mode::indirect_indexed>();
+ case 0x72: return run_stp();
+ case 0x73: return run_rra<addressing_mode::indirect_indexed>();
+ case 0x74: return run_nop<addressing_mode::zero_page_indexed_x>();
+ case 0x75: return run_adc<addressing_mode::zero_page_indexed_x>();
+ case 0x76: return run_ror<addressing_mode::zero_page_indexed_x>();
+ case 0x77: return run_rra<addressing_mode::zero_page_indexed_x>();
+ case 0x78: return run_sei();
+ case 0x79: return run_adc<addressing_mode::absolute_indexed_y>();
+ case 0x7A: return run_nop();
+ case 0x7B: return run_rra<addressing_mode::absolute_indexed_y>();
+ case 0x7C: return run_nop<addressing_mode::absolute_indexed_x>();
+ case 0x7D: return run_adc<addressing_mode::absolute_indexed_x>();
+ case 0x7E: return run_ror<addressing_mode::absolute_indexed_x>();
+ case 0x7F: return run_rra<addressing_mode::absolute_indexed_x>();
+ case 0x80: return run_nop<addressing_mode::immediate>();
+ case 0x81: return run_sta<addressing_mode::indexed_indirect>();
+ case 0x82: return run_nop<addressing_mode::immediate>();
+ case 0x83: return run_sax<addressing_mode::indexed_indirect>();
+ case 0x84: return run_sty<addressing_mode::zero_page>();
+ case 0x85: return run_sta<addressing_mode::zero_page>();
+ case 0x86: return run_stx<addressing_mode::zero_page>();
+ case 0x87: return run_sax<addressing_mode::zero_page>();
+ case 0x88: return run_dey();
+ case 0x89: return run_nop<addressing_mode::immediate>();
+ case 0x8A: return run_txa();
+ case 0x8B: return run_xaa<addressing_mode::immediate>();
+ case 0x8C: return run_sty<addressing_mode::absolute>();
+ case 0x8D: return run_sta<addressing_mode::absolute>();
+ case 0x8E: return run_stx<addressing_mode::absolute>();
+ case 0x8F: return run_sax<addressing_mode::absolute>();
+ case 0x90: return run_bcc<addressing_mode::relative>();
+ case 0x91: return run_sta<addressing_mode::indirect_indexed>();
+ case 0x92: return run_stp();
+ case 0x93: return run_ahx<addressing_mode::indirect_indexed>();
+ case 0x94: return run_sty<addressing_mode::zero_page_indexed_x>();
+ case 0x95: return run_sta<addressing_mode::zero_page_indexed_x>();
+ case 0x96: return run_stx<addressing_mode::zero_page_indexed_y>();
+ case 0x97: return run_sax<addressing_mode::zero_page_indexed_y>();
+ case 0x98: return run_tya();
+ case 0x99: return run_sta<addressing_mode::absolute_indexed_y>();
+ case 0x9A: return run_txs();
+ case 0x9B: return run_tas<addressing_mode::absolute_indexed_y>();
+ case 0x9C: return run_shy<addressing_mode::absolute_indexed_x>();
+ case 0x9D: return run_sta<addressing_mode::absolute_indexed_x>();
+ case 0x9E: return run_shx<addressing_mode::absolute_indexed_y>();
+ case 0x9F: return run_ahx<addressing_mode::absolute_indexed_y>();
+ case 0xA0: return run_ldy<addressing_mode::immediate>();
+ case 0xA1: return run_lda<addressing_mode::indexed_indirect>();
+ case 0xA2: return run_ldx<addressing_mode::immediate>();
+ case 0xA3: return run_lax<addressing_mode::indexed_indirect>();
+ case 0xA4: return run_ldy<addressing_mode::zero_page>();
+ case 0xA5: return run_lda<addressing_mode::zero_page>();
+ case 0xA6: return run_ldx<addressing_mode::zero_page>();
+ case 0xA7: return run_lax<addressing_mode::zero_page>();
+ case 0xA8: return run_tay();
+ case 0xA9: return run_lda<addressing_mode::immediate>();
+ case 0xAA: return run_tax();
+ case 0xAB: return run_lax<addressing_mode::immediate>();
+ case 0xAC: return run_ldy<addressing_mode::absolute>();
+ case 0xAD: return run_lda<addressing_mode::absolute>();
+ case 0xAE: return run_ldx<addressing_mode::absolute>();
+ case 0xAF: return run_lax<addressing_mode::absolute>();
+ case 0xB0: return run_bcs<addressing_mode::relative>();
+ case 0xB1: return run_lda<addressing_mode::indirect_indexed>();
+ case 0xB2: return run_stp();
+ case 0xB3: return run_lax<addressing_mode::indirect_indexed>();
+ case 0xB4: return run_ldy<addressing_mode::zero_page_indexed_x>();
+ case 0xB5: return run_lda<addressing_mode::zero_page_indexed_x>();
+ case 0xB6: return run_ldx<addressing_mode::zero_page_indexed_y>();
+ case 0xB7: return run_lax<addressing_mode::zero_page_indexed_y>();
+ case 0xB8: return run_clv();
+ case 0xB9: return run_lda<addressing_mode::absolute_indexed_y>();
+ case 0xBA: return run_tsx();
+ case 0xBB: return run_las<addressing_mode::absolute_indexed_y>();
+ case 0xBC: return run_ldy<addressing_mode::absolute_indexed_x>();
+ case 0xBD: return run_lda<addressing_mode::absolute_indexed_x>();
+ case 0xBE: return run_ldx<addressing_mode::absolute_indexed_y>();
+ case 0xBF: return run_lax<addressing_mode::absolute_indexed_y>();
+ case 0xC0: return run_cpy<addressing_mode::immediate>();
+ case 0xC1: return run_cmp<addressing_mode::indexed_indirect>();
+ case 0xC2: return run_nop<addressing_mode::immediate>();
+ case 0xC3: return run_dcp<addressing_mode::indexed_indirect>();
+ case 0xC4: return run_cpy<addressing_mode::zero_page>();
+ case 0xC5: return run_cmp<addressing_mode::zero_page>();
+ case 0xC6: return run_dec<addressing_mode::zero_page>();
+ case 0xC7: return run_dcp<addressing_mode::zero_page>();
+ case 0xC8: return run_iny();
+ case 0xC9: return run_cmp<addressing_mode::immediate>();
+ case 0xCA: return run_dex();
+ case 0xCB: return run_axs<addressing_mode::immediate>();
+ case 0xCC: return run_cpy<addressing_mode::absolute>();
+ case 0xCD: return run_cmp<addressing_mode::absolute>();
+ case 0xCE: return run_dec<addressing_mode::absolute>();
+ case 0xCF: return run_dcp<addressing_mode::absolute>();
+ case 0xD0: return run_bne<addressing_mode::relative>();
+ case 0xD1: return run_cmp<addressing_mode::indirect_indexed>();
+ case 0xD2: return run_stp();
+ case 0xD3: return run_dcp<addressing_mode::indirect_indexed>();
+ case 0xD4: return run_nop<addressing_mode::zero_page_indexed_x>();
+ case 0xD5: return run_cmp<addressing_mode::zero_page_indexed_x>();
+ case 0xD6: return run_dec<addressing_mode::zero_page_indexed_x>();
+ case 0xD7: return run_dcp<addressing_mode::zero_page_indexed_x>();
+ case 0xD8: return run_cld();
+ case 0xD9: return run_cmp<addressing_mode::absolute_indexed_y>();
+ case 0xDA: return run_nop();
+ case 0xDB: return run_dcp<addressing_mode::absolute_indexed_y>();
+ case 0xDC: return run_nop<addressing_mode::absolute_indexed_x>();
+ case 0xDD: return run_cmp<addressing_mode::absolute_indexed_x>();
+ case 0xDE: return run_dec<addressing_mode::absolute_indexed_x>();
+ case 0xDF: return run_dcp<addressing_mode::absolute_indexed_x>();
+ case 0xE0: return run_cpx<addressing_mode::immediate>();
+ case 0xE1: return run_sbc<addressing_mode::indexed_indirect>();
+ case 0xE2: return run_nop<addressing_mode::immediate>();
+ case 0xE3: return run_isc<addressing_mode::indexed_indirect>();
+ case 0xE4: return run_cpx<addressing_mode::zero_page>();
+ case 0xE5: return run_sbc<addressing_mode::zero_page>();
+ case 0xE6: return run_inc<addressing_mode::zero_page>();
+ case 0xE7: return run_isc<addressing_mode::zero_page>();
+ case 0xE8: return run_inx();
+ case 0xE9: return run_sbc<addressing_mode::immediate>();
+ case 0xEA: return run_nop();
+ case 0xEB: return run_sbc<addressing_mode::immediate>();
+ case 0xEC: return run_cpx<addressing_mode::absolute>();
+ case 0xED: return run_sbc<addressing_mode::absolute>();
+ case 0xEE: return run_inc<addressing_mode::absolute>();
+ case 0xEF: return run_isc<addressing_mode::absolute>();
+ case 0xF0: return run_beq<addressing_mode::relative>();
+ case 0xF1: return run_sbc<addressing_mode::indirect_indexed>();
+ case 0xF2: return run_stp();
+ case 0xF3: return run_isc<addressing_mode::indirect_indexed>();
+ case 0xF4: return run_nop<addressing_mode::zero_page_indexed_x>();
+ case 0xF5: return run_sbc<addressing_mode::zero_page_indexed_x>();
+ case 0xF6: return run_inc<addressing_mode::zero_page_indexed_x>();
+ case 0xF7: return run_isc<addressing_mode::zero_page_indexed_x>();
+ case 0xF8: return run_sed();
+ case 0xF9: return run_sbc<addressing_mode::absolute_indexed_y>();
+ case 0xFA: return run_nop();
+ case 0xFB: return run_isc<addressing_mode::absolute_indexed_y>();
+ case 0xFC: return run_nop<addressing_mode::absolute_indexed_x>();
+ case 0xFD: return run_sbc<addressing_mode::absolute_indexed_x>();
+ case 0xFE: return run_inc<addressing_mode::absolute_indexed_x>();
+ case 0xFF: return run_isc<addressing_mode::absolute_indexed_x>();
+ default: return status::error_unsupported_instruction;
}
}
@@ -324,30 +324,31 @@ namespace nes
// -----------------------------------------------------------------------------------------------------------------
template<cpu::addressing_mode Mode>
- auto cpu::run_brk() -> void
+ auto cpu::run_brk() -> status
{
// BRK: Break
fetch_operand<Mode>();
execute_interrupt(address{ 0xFFFE });
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_ora() -> void
+ auto cpu::run_ora() -> status
{
// ORA: Logical Inclusive OR
auto operand = fetch_operand<Mode>();
eval_ora(operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
- auto cpu::run_stp() -> void
+ auto cpu::run_stp() -> status
{
- // TODO: Proper STP handling
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_slo() -> void
+ auto cpu::run_slo() -> status
{
// SLO: ASL + ORA
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
@@ -355,64 +356,72 @@ namespace nes
operand.write(tmp);
eval_ora(tmp);
current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_nop() -> void
+ auto cpu::run_nop() -> status
{
// NOP: NOP
auto operand = fetch_operand<Mode>();
current_cycles_ += operand.get_cycles();
+ return status::success;
}
- auto cpu::run_nop() -> void
+ auto cpu::run_nop() -> status
{
// NOP: NOP
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_php() -> void
+ auto cpu::run_php() -> status
{
// PHP: Push Processor Status
eval_php();
current_cycles_ += cycle_count::from_cpu(3);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_asl() -> void
+ auto cpu::run_asl() -> status
{
// ASL: Arithmetic Shift Left
auto operand = fetch_operand<Mode>();
operand.write(eval_asl(operand.read()));
current_cycles_ += shift_cycle_count<Mode>();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_anc() -> void
+ auto cpu::run_anc() -> status
{
// ANC: AND #i + copy N to C
auto operand = fetch_operand<Mode>();
eval_and(operand.read());
registers_.p.set_c(registers_.p.get_n());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bpl() -> void
+ auto cpu::run_bpl() -> status
{
// BPL: Break If Positive
branch<Mode>(!registers_.p.get_n());
+ return status::success;
}
- auto cpu::run_clc() -> void
+ auto cpu::run_clc() -> status
{
// CLC: Clear Carry Flag
registers_.p.set_c(false);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_jsr() -> void
+ auto cpu::run_jsr() -> status
{
// JSR: Jump to Subroutine
static_assert(Mode == addressing_mode::absolute, "JSR only supports absolute addressing.");
@@ -422,19 +431,21 @@ namespace nes
auto operand = fetch_operand<Mode>();
registers_.pc = operand.get_address().get_absolute();
current_cycles_ += cycle_count::from_cpu(6);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_and() -> void
+ auto cpu::run_and() -> status
{
// AND: Logical AND
auto operand = fetch_operand<Mode>();
eval_and(operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_rla() -> void
+ auto cpu::run_rla() -> status
{
// RLA: ROL + AND
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
@@ -442,10 +453,11 @@ namespace nes
operand.write(tmp);
eval_and(tmp);
current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bit() -> void
+ auto cpu::run_bit() -> status
{
// BIT: Bit Test
auto operand = fetch_operand<Mode>();
@@ -455,57 +467,64 @@ namespace nes
registers_.p.set_v((arg & 0x40) != 0);
registers_.p.set_n((arg & 0x80) != 0);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_rol() -> void
+ auto cpu::run_rol() -> status
{
// ROR: Rotate Left
auto operand = fetch_operand<Mode>();
auto const result = eval_rol(operand.read());
operand.write(result);
current_cycles_ += shift_cycle_count<Mode>();
+ return status::success;
}
- auto cpu::run_plp() -> void
+ auto cpu::run_plp() -> status
{
// PLP: Pull Processor Status
eval_plp();
current_cycles_ += cycle_count::from_cpu(4);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bmi() -> void
+ auto cpu::run_bmi() -> status
{
branch<Mode>(registers_.p.get_n());
+ return status::success;
}
- auto cpu::run_sec() -> void
+ auto cpu::run_sec() -> status
{
// SEC: Set Carry Flag
registers_.p.set_c(true);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_rti() -> void
+ auto cpu::run_rti() -> status
{
// RTI: Return From Interrupt
eval_plp();
registers_.pc = pop_stack16();
current_cycles_ += cycle_count::from_cpu(6);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_eor() -> void
+ auto cpu::run_eor() -> status
{
// EOR: Exclusive OR
auto operand = fetch_operand<Mode>();
eval_eor(operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_sre() -> void
+ auto cpu::run_sre() -> status
{
// SRE: LSR + EOR
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
@@ -513,10 +532,11 @@ namespace nes
operand.write(tmp);
eval_eor(tmp);
current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_lsr() -> void
+ auto cpu::run_lsr() -> status
{
// LSR: Logical Shift Right
auto operand = fetch_operand<Mode>();
@@ -526,67 +546,75 @@ namespace nes
registers_.p.set_c((old_val & 0x1) != 0);
update_zn(new_val);
current_cycles_ += shift_cycle_count<Mode>();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_alr() -> void
+ auto cpu::run_alr() -> status
{
// 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();
+ return status::success;
}
- auto cpu::run_pha() -> void
+ auto cpu::run_pha() -> status
{
// PHA: Push Accumulator
push_stack8(registers_.a);
current_cycles_ += cycle_count::from_cpu(3);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_jmp() -> void
+ auto cpu::run_jmp() -> status
{
// JMP: Jump
auto operand = fetch_operand<Mode>();
registers_.pc = operand.get_address().get_absolute();
current_cycles_ += jump_cycle_count<Mode>();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bvc() -> void
+ auto cpu::run_bvc() -> status
{
// BVC: Branch If Overflow Clear
branch<Mode>(!registers_.p.get_v());
+ return status::success;
}
- auto cpu::run_cli() -> void
+ auto cpu::run_cli() -> status
{
// CLI: Clear Interrupt Disable
registers_.p.set_i(false);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_rts() -> void
+ auto cpu::run_rts() -> status
{
// RTS: Return from Subroutine
auto const addr = address{ pop_stack16() } + 1;
registers_.pc = addr.get_absolute();
current_cycles_ += cycle_count::from_cpu(6);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_adc() -> void
+ auto cpu::run_adc() -> status
{
// ADC: Add With Carry
auto operand = fetch_operand<Mode>();
eval_adc(operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_rra() -> void
+ auto cpu::run_rra() -> status
{
// RRA: ROR + ADC
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
@@ -594,276 +622,302 @@ namespace nes
operand.write(tmp);
eval_adc(tmp);
current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_ror() -> void
+ auto cpu::run_ror() -> status
{
// ROR: Rotate Right
auto operand = fetch_operand<Mode>();
auto const result = eval_ror(operand.read());
operand.write(result);
current_cycles_ += shift_cycle_count<Mode>();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_arr() -> void
+ auto cpu::run_arr() -> status
{
// ARR: AND #i + ROR A but with different flags
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
- auto cpu::run_pla() -> void
+ auto cpu::run_pla() -> status
{
// PLA: Pull Accumulator
registers_.a = pop_stack8();
update_zn(registers_.a);
current_cycles_ += cycle_count::from_cpu(4);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bvs() -> void
+ auto cpu::run_bvs() -> status
{
// BVS: Branch If Overflow Set
branch<Mode>(registers_.p.get_v());
+ return status::success;
}
- auto cpu::run_sei() -> void
+ auto cpu::run_sei() -> status
{
// SEI: Set Interrupt Disable
registers_.p.set_i(true);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_sta() -> void
+ auto cpu::run_sta() -> status
{
// STA: Store Accumulator
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
operand.write(registers_.a);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_ahx() -> void
+ auto cpu::run_ahx() -> status
{
// AHX: ???
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_sax() -> void
+ auto cpu::run_sax() -> status
{
// 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();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_sty() -> void
+ auto cpu::run_sty() -> status
{
// STX: Store Y
auto operand = fetch_operand<Mode>();
operand.write(registers_.y);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_stx() -> void
+ auto cpu::run_stx() -> status
{
// STX: Store X
auto operand = fetch_operand<Mode>();
operand.write(registers_.x);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
- auto cpu::run_dey() -> void
+ auto cpu::run_dey() -> status
{
// DEY: Decrement Y Register
registers_.y -= 1;
update_zn(registers_.y);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_txa() -> void
+ auto cpu::run_txa() -> status
{
// TXA: Transfer X to Accumulator
registers_.a = registers_.x;
update_zn(registers_.a);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_xaa() -> void
+ auto cpu::run_xaa() -> status
{
// XAA: Depends on analog behavior.
run_stp();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bcc() -> void
+ auto cpu::run_bcc() -> status
{
// BCC: Branch If Carry Clear
branch<Mode>(!registers_.p.get_c());
+ return status::success;
}
- auto cpu::run_tya() -> void
+ auto cpu::run_tya() -> status
{
// TYA: Transfer Y to Accumulator
registers_.a = registers_.y;
update_zn(registers_.a);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_txs() -> void
+ auto cpu::run_txs() -> status
{
// TXS: Transfer X to Stack Pointer
registers_.sp = registers_.x;
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_tas() -> void
+ auto cpu::run_tas() -> status
{
// TAS: ???
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_shy() -> void
+ auto cpu::run_shy() -> status
{
// SHY: ???
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_shx() -> void
+ auto cpu::run_shx() -> status
{
// SHY: ???
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_ldy() -> void
+ auto cpu::run_ldy() -> status
{
// LDY: Load Y Register
auto operand = fetch_operand<Mode>();
registers_.y = operand.read();
update_zn(registers_.y);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_lda() -> void
+ auto cpu::run_lda() -> status
{
// LDA: Load Accumulator
auto operand = fetch_operand<Mode>();
registers_.a = operand.read();
update_zn(registers_.a);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_ldx() -> void
+ auto cpu::run_ldx() -> status
{
// LDX: Load X Register
auto operand = fetch_operand<Mode>();
registers_.x = operand.read();
update_zn(registers_.x);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_lax() -> void
+ auto cpu::run_lax() -> status
{
// LAX: LDA + TAX
auto operand = fetch_operand<Mode>();
registers_.x = registers_.a = operand.read();
update_zn(registers_.a);
current_cycles_ += operand.get_cycles();
+ return status::success;
}
- auto cpu::run_tay() -> void
+ auto cpu::run_tay() -> status
{
// TAY: Transfer Accumulator to Y
registers_.y = registers_.a;
update_zn(registers_.y);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_tax() -> void
+ auto cpu::run_tax() -> status
{
// TAX: Transfer Accumulator to X
registers_.x = registers_.a;
update_zn(registers_.x);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bcs() -> void
+ auto cpu::run_bcs() -> status
{
// BCS: Branch If Carry Set
branch<Mode>(registers_.p.get_c());
+ return status::success;
}
- auto cpu::run_clv() -> void
+ auto cpu::run_clv() -> status
{
// CLV: Clear Overflow Flag
registers_.p.set_v(false);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_tsx() -> void
+ auto cpu::run_tsx() -> status
{
// TSX: Transfer Stack Pointer to X
registers_.x = registers_.sp;
update_zn(registers_.x);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_las() -> void
+ auto cpu::run_las() -> status
{
// LAS: ???
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_cpy() -> void
+ auto cpu::run_cpy() -> status
{
// CPY: Compare Y Register
auto operand = fetch_operand<Mode>();
eval_cmp(registers_.y, operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_axs() -> void
+ auto cpu::run_axs() -> status
{
// AXS: Set X to ((A AND X) - #value without borrow), updates NZC
// TODO
- std::abort();
+ return status::error_unsupported_instruction;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_cmp() -> void
+ auto cpu::run_cmp() -> status
{
// CMP: Compare
auto operand = fetch_operand<Mode>();
eval_cmp(registers_.a, operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_dcp() -> void
+ auto cpu::run_dcp() -> status
{
// DCP: DEC + CMP
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
@@ -871,10 +925,11 @@ namespace nes
operand.write(tmp);
eval_cmp(registers_.a, tmp);
current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_dec() -> void
+ auto cpu::run_dec() -> status
{
// DEC: Decrement Memory
auto operand = fetch_operand<Mode>();
@@ -883,58 +938,65 @@ namespace nes
write8(operand.get_address(), new_val);
update_zn(new_val);
current_cycles_ += inc_dec_cycle_count<Mode>();
+ return status::success;
}
- auto cpu::run_iny() -> void
+ auto cpu::run_iny() -> status
{
// INY: Increment Y
registers_.y += 1;
update_zn(registers_.y);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
- auto cpu::run_dex() -> void
+ auto cpu::run_dex() -> status
{
// DEX: Decrement X Register
registers_.x -= 1;
update_zn(registers_.x);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_bne() -> void
+ auto cpu::run_bne() -> status
{
// BNE: Branch If Not Equal
branch<Mode>(!registers_.p.get_z());
+ return status::success;
}
- auto cpu::run_cld() -> void
+ auto cpu::run_cld() -> status
{
// CLD: Clear Decimal Mode
registers_.p.set_d(false);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_cpx() -> void
+ auto cpu::run_cpx() -> status
{
// CPX: Compare X Register
auto operand = fetch_operand<Mode>();
eval_cmp(registers_.x, operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_sbc() -> void
+ auto cpu::run_sbc() -> status
{
// SBC: Subtract With Carry
auto operand = fetch_operand<Mode>();
eval_adc(~operand.read());
current_cycles_ += operand.get_cycles();
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_isc() -> void
+ auto cpu::run_isc() -> status
{
// ISC: INC + SBC
auto operand = fetch_operand<Mode>(force_page_crossing::yes);
@@ -942,10 +1004,11 @@ namespace nes
operand.write(tmp);
eval_adc(~tmp);
current_cycles_ += operand.get_cycles() + cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_inc() -> void
+ auto cpu::run_inc() -> status
{
// INC: Increment Memory
auto operand = fetch_operand<Mode>();
@@ -954,28 +1017,32 @@ namespace nes
write8(operand.get_address(), new_val);
update_zn(new_val);
current_cycles_ += inc_dec_cycle_count<Mode>();
+ return status::success;
}
- auto cpu::run_inx() -> void
+ auto cpu::run_inx() -> status
{
// INX: Increment X
registers_.x += 1;
update_zn(registers_.x);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
template<cpu::addressing_mode Mode>
- auto cpu::run_beq() -> void
+ auto cpu::run_beq() -> status
{
// BEQ: Branch If Equal
branch<Mode>(registers_.p.get_z());
+ return status::success;
}
- auto cpu::run_sed() -> void
+ auto cpu::run_sed() -> status
{
// SED: Set Decimal Flag
registers_.p.set_d(true);
current_cycles_ += cycle_count::from_cpu(2);
+ return status::success;
}
// -----------------------------------------------------------------------------------------------------------------
@@ -1303,7 +1370,7 @@ namespace nes
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);
+ return cartridge_.get_mapper().read_cpu(addr, cartridge_);
}
auto cpu::read16(address const addr) -> std::uint16_t
@@ -1345,7 +1412,7 @@ namespace nes
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);
+ cartridge_.get_mapper().write_cpu(addr, value, cartridge_);
}
auto cpu::write16(address const addr, std::uint16_t const value) -> void
diff --git a/lib/nes/cpu.hh b/lib/nes/cpu.hh
index 431cdf2..d5bc032 100644
--- a/lib/nes/cpu.hh
+++ b/lib/nes/cpu.hh
@@ -2,12 +2,13 @@
#include "nes/util/cycle-count.hh"
#include "nes/util/address.hh"
+#include "nes/util/status.hh"
namespace nes
{
class ppu;
class controller;
- class mapper;
+ class cartridge;
struct snapshot;
class cpu
@@ -17,7 +18,7 @@ namespace nes
cycle_count current_cycles_;
ppu& ppu_;
- mapper& mapper_;
+ cartridge& cartridge_;
controller& controller_1_;
controller& controller_2_;
std::uint8_t ram_[ram_size]{};
@@ -128,7 +129,7 @@ namespace nes
};
public:
- explicit cpu(ppu&, mapper&, controller& controller_1, controller& controller_2);
+ explicit cpu(ppu&, cartridge&, controller& controller_1, controller& controller_2);
cpu(cpu const&) = delete;
cpu(cpu&&) = delete;
@@ -139,7 +140,7 @@ namespace nes
auto build_snapshot(snapshot&) -> void;
auto stall_cycles(cycle_count) -> void;
auto trigger_nmi() -> void;
- auto step() -> void;
+ auto step() -> status;
auto is_nmi_pending() const -> bool { return nmi_pending_; } // XXX: Debugging
// Memory access
@@ -153,9 +154,9 @@ namespace nes
// Instructions
#define DEFINE_SIMPLE_INSTRUCTION(name) \
- auto run_##name() -> void;
+ auto run_##name() -> status;
#define DEFINE_OPERAND_INSTRUCTION(name) \
- template<addressing_mode Mode> auto run_##name() -> void;
+ template<addressing_mode Mode> auto run_##name() -> status;
DEFINE_OPERAND_INSTRUCTION(brk)
DEFINE_OPERAND_INSTRUCTION(ora)
diff --git a/lib/nes/mapper.cc b/lib/nes/mapper.cc
index ad9ec40..40d11f8 100644
--- a/lib/nes/mapper.cc
+++ b/lib/nes/mapper.cc
@@ -1,134 +1,172 @@
#include "nes/mapper.hh"
#include "nes/cartridge.hh"
-#include <iostream>
namespace nes
{
- mapper::mapper(
- cartridge& cartridge,
- read_cpu_func const read_cpu, write_cpu_func const write_cpu,
- read_ppu_func const read_ppu, write_ppu_func const write_ppu)
- : cartridge_{ cartridge }
- , read_cpu_{ read_cpu }
- , write_cpu_{ write_cpu }
- , read_ppu_{ read_ppu }
- , write_ppu_{ write_ppu }
+ namespace
{
- }
+ // -------------------------------------------------------------------------------------------------------------
+ // NROM
+ // -------------------------------------------------------------------------------------------------------------
- auto mapper::select(cartridge& cartridge) -> mapper
- {
- switch (cartridge.get_mapper_number())
+ class mapper_nrom final : public mapper
{
- 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();
- }
- }
+ public:
+ explicit mapper_nrom() = default;
- // -----------------------------------------------------------------------------------------------------------------
- // NROM
- // -----------------------------------------------------------------------------------------------------------------
+ auto validate(cartridge& cartridge) -> status override
+ {
+ if (cartridge.get_ram_length() != 0x2000)
+ {
+ return status::error_invalid_ines_data;
+ }
- 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() - 0x8000u;
- if (rel >= cartridge_.get_prg_rom_length()) { return 0x0; }
+ if (cartridge.get_prg_rom_length() != 0x4000 && cartridge.get_prg_rom_length() != 0x8000)
+ {
+ return status::error_invalid_ines_data;
+ }
- return cartridge_.get_prg_rom()[rel];
- }
- if (addr <= address{ 0xFFFF })
- {
- auto rel = addr.get_absolute() - 0xC000u;
- if (cartridge_.get_prg_rom_length() > 0x4000) { rel += 0x4000; }
- if (rel >= cartridge_.get_prg_rom_length()) { return 0x0; }
+ if (cartridge.get_chr_rom_length() != 0x2000)
+ {
+ return status::error_invalid_ines_data;
+ }
- return cartridge_.get_prg_rom()[rel];
- }
+ return status::success;
+ }
- return 0x0;
- }
+ auto read_cpu(address const addr, cartridge& cartridge) -> std::uint8_t override
+ {
+ 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() - 0x8000u;
+ return cartridge.get_prg_rom()[rel];
+ }
+ if (addr <= address{ 0xFFFF })
+ {
+ auto rel = addr.get_absolute() - 0xC000u;
+ if (cartridge.get_prg_rom_length() > 0x4000) { rel += 0x4000; }
+ return cartridge.get_prg_rom()[rel];
+ }
- 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;
- }
- }
+ return 0x0;
+ }
- 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; }
+ auto write_cpu(address const addr, std::uint8_t const value, cartridge& cartridge) -> void override
+ {
+ if (addr <= address{ 0x5FFF })
+ {
+ return;
+ }
+ if (addr <= address{ 0x7FFF })
+ {
+ cartridge.get_ram_mut()[addr.get_absolute() - 0x6000] = value;
+ return;
+ }
+ }
- return cartridge_.get_chr_rom()[rel];
- }
- if (addr <= address{ 0x3EFF })
+ auto read_ppu(address const addr, cartridge& cartridge, std::uint8_t const* vram) -> std::uint8_t override
+ {
+ if (addr <= address{ 0x1FFF })
+ {
+ auto const rel = addr.get_absolute();
+ return cartridge.get_chr_rom()[rel];
+ }
+ if (addr <= address{ 0x3EFF })
+ {
+ return vram[mirrored_vram_address(addr, cartridge)];
+ }
+
+ return 0x0;
+ }
+
+ auto write_ppu(address const addr, std::uint8_t const value, cartridge& cartridge, std::uint8_t* vram) -> void override
+ {
+ if (addr <= address{ 0x1FFF })
+ {
+ return;
+ }
+ if (addr <= address{ 0x3EFF })
+ {
+ vram[mirrored_vram_address(addr, cartridge)] = value;
+ return;
+ }
+ }
+
+ private:
+ static auto mirrored_vram_address(address const addr, cartridge const& cartridge) -> unsigned
+ {
+ auto const rel = addr.get_absolute() % 0x1000u;
+
+ switch (cartridge.get_name_table_arrangement())
+ {
+ case name_table_arrangement::horizontal:
+ // $2000 = $2400, $2800 = $2c00
+ if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0x7FF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0xBFF) { return 0x400u + (rel % 0x400u); }
+ if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
+ break;
+ case name_table_arrangement::vertical:
+ // $2000 = $2800, $2400 = $2c00
+ if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0x7FF) { return 0x400u + (rel % 0x400u); }
+ if (rel <= 0xBFF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
+ break;
+ }
+
+ return 0;
+ }
+ };
+
+ // -------------------------------------------------------------------------------------------------------------
+ // Invalid
+ // -------------------------------------------------------------------------------------------------------------
+
+ class mapper_invalid final : public mapper
{
- return vram[nrom_mirrored_vram_address(addr)];
- }
+ public:
+ explicit mapper_invalid() = default;
- return 0x0;
- }
+ auto validate(cartridge&) -> status override { return status::error_unsupported_mapper; }
+ auto read_cpu(address, cartridge&) -> std::uint8_t override { return 0; }
+ auto write_cpu(address, std::uint8_t, cartridge&) -> void override {}
+ auto read_ppu(address, cartridge&, std::uint8_t const*) -> std::uint8_t override { return 0; }
+ auto write_ppu(address, std::uint8_t, cartridge&, std::uint8_t*) -> void override {}
+ };
+ } // namespace
- auto mapper::nrom_write_ppu(address const addr, std::uint8_t* vram, std::uint8_t const value) -> void
+ // -----------------------------------------------------------------------------------------------------------------
+ // Selection
+ // -----------------------------------------------------------------------------------------------------------------
+
+ auto mapper::invalid() -> mapper&
{
- if (addr <= address{ 0x1FFF })
- {
- return;
- }
- if (addr <= address{ 0x3EFF })
- {
- vram[nrom_mirrored_vram_address(addr)] = value;
- return;
- }
+ static auto instance = mapper_invalid{};
+ return instance;
}
- auto mapper::nrom_mirrored_vram_address(address const addr) const -> unsigned
+ auto mapper::get(std::uint8_t const number) -> mapper&
{
- auto const rel = addr.get_absolute() % 0x1000u;
-
- switch (cartridge_.get_name_table_arrangement())
+ switch (number)
{
- case name_table_arrangement::horizontal:
- // $2000 = $2400, $2800 = $2c00
- if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0x7FF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0xBFF) { return 0x400u + (rel % 0x400u); }
- if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
- break;
- case name_table_arrangement::vertical:
- // $2000 = $2800, $2400 = $2c00
- if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0x7FF) { return 0x400u + (rel % 0x400u); }
- if (rel <= 0xBFF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
- break;
+ case 0x00:
+ {
+ static auto instance = mapper_nrom{};
+ return instance;
+ }
+ default:
+ {
+ return invalid();
+ }
}
-
- return 0;
}
} // namespace nes \ No newline at end of file
diff --git a/lib/nes/mapper.hh b/lib/nes/mapper.hh
index f2873a6..7e41b23 100644
--- a/lib/nes/mapper.hh
+++ b/lib/nes/mapper.hh
@@ -1,6 +1,7 @@
#pragma once
#include "nes/util/address.hh"
+#include "nes/util/status.hh"
namespace nes
{
@@ -8,57 +9,24 @@ namespace nes
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;
+ static auto invalid() -> mapper&;
+ static auto get(std::uint8_t number) -> mapper&;
+
+ virtual ~mapper() = default;
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
+ virtual auto validate(cartridge&) -> status = 0;
+ virtual auto read_cpu(address, cartridge&) -> std::uint8_t = 0;
+ virtual auto write_cpu(address, std::uint8_t, cartridge&) -> void = 0;
+ virtual auto read_ppu(address, cartridge&, std::uint8_t const* vram) -> std::uint8_t = 0;
+ virtual auto write_ppu(address, std::uint8_t, cartridge&, std::uint8_t* vram) -> void = 0;
- 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;
+ protected:
+ explicit mapper() = default;
};
} // namespace nes \ No newline at end of file
diff --git a/lib/nes/nes.cc b/lib/nes/nes.cc
index 498ecd7..9a35bfa 100644
--- a/lib/nes/nes.cc
+++ b/lib/nes/nes.cc
@@ -1,25 +1,17 @@
#include "nes/nes.hh"
-#include "nes/mapper.hh"
#include "nes/util/display.hh"
#include "nes/util/rgb.hh"
#include "nes/util/snapshot.hh"
-#include <iostream>
namespace nes
{
nes::nes(cartridge cartridge, display& display)
: cartridge_{ std::move(cartridge) }
, display_{ display }
- , mapper_{ mapper::select(cartridge_) }
- , ppu_{ cpu_, mapper_, display_ }
- , cpu_{ ppu_, mapper_, controller_1_, controller_2_ }
+ , ppu_{ cpu_, cartridge_, display_ }
+ , cpu_{ ppu_, cartridge_, controller_1_, controller_2_ }
+ , status_{ cartridge_.get_status() }
{
- if (!cartridge_.is_valid())
- {
- std::cerr << "Cartridge not valid!" << std::endl;
- std::abort();
- }
-
display_.clear(rgb{});
display_.switch_buffers();
display_.clear(rgb{});
@@ -32,7 +24,9 @@ namespace nes
auto nes::step() -> void
{
- cpu_.step();
+ if (get_status() != status::success) { return; }
+
+ status_ = cpu_.step();
while (ppu_.get_cycles() < cpu_.get_cycles())
{
ppu_.step();
@@ -42,7 +36,7 @@ namespace nes
auto nes::step(cycle_count const delta) -> void
{
current_cycles_ += delta;
- while (cpu_.get_cycles() < current_cycles_)
+ while (cpu_.get_cycles() < current_cycles_ && get_status() == status::success)
{
step();
}
diff --git a/lib/nes/nes.hh b/lib/nes/nes.hh
index 437075f..dab5ded 100644
--- a/lib/nes/nes.hh
+++ b/lib/nes/nes.hh
@@ -5,7 +5,6 @@
#include "nes/controller.hh"
#include "nes/cpu.hh"
#include "nes/ppu.hh"
-#include "nes/mapper.hh"
namespace nes
{
@@ -19,10 +18,10 @@ namespace nes
display& display_;
controller controller_1_;
controller controller_2_;
- mapper mapper_;
ppu ppu_;
cpu cpu_;
cycle_count current_cycles_;
+ status status_{ status::success };
public:
explicit nes(cartridge, display&);
@@ -32,6 +31,7 @@ namespace nes
auto operator=(nes const&) -> nes& = delete;
auto operator=(nes&&) -> nes& = delete;
+ auto get_status() const -> status { return status_; }
auto get_controller_1() const -> controller const& { return controller_1_; }
auto ref_controller_1() -> controller& { return controller_1_; }
auto get_controller_2() const -> controller const& { return controller_2_; }
diff --git a/lib/nes/ppu.cc b/lib/nes/ppu.cc
index 0772d80..0f2e0d6 100644
--- a/lib/nes/ppu.cc
+++ b/lib/nes/ppu.cc
@@ -1,6 +1,6 @@
#include "nes/ppu.hh"
#include "nes/cpu.hh"
-#include "nes/mapper.hh"
+#include "nes/cartridge.hh"
#include "nes/util/address.hh"
#include "nes/util/display.hh"
#include "nes/util/rgb.hh"
@@ -9,9 +9,9 @@
namespace nes
{
- ppu::ppu(cpu& cpu, mapper& mapper, display& display)
+ ppu::ppu(cpu& cpu, cartridge& cartridge, display& display)
: cpu_{ cpu }
- , mapper_{ mapper }
+ , cartridge_{ cartridge }
, display_{ display }
{
}
@@ -110,7 +110,8 @@ namespace nes
fetch_cycle_.palette, fetch_cycle_.bitplane_0, fetch_cycle_.bitplane_1);
break;
}
- default: break;
+ default:
+ break;
}
}
@@ -230,7 +231,7 @@ namespace nes
{
auto const height = get_sprite_height();
sprite_count_ = 0;
- for (auto i = unsigned{ 0 }; i < 64; ++i)
+ for (auto i = unsigned{ 0 }; i < sprite_max_count; ++i)
{
auto const s = get_sprite(i);
auto const row = static_cast<int>(scanline_) - static_cast<int>(s.y);
@@ -298,7 +299,7 @@ namespace nes
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{ 0x3EFF }) { return cartridge_.get_mapper().read_ppu(addr, cartridge_, vram_); }
if (addr <= address{ 0x3FFF })
{
auto const index = color_index{ static_cast<std::uint8_t>(addr.get_absolute() % 0x20) };
@@ -314,7 +315,7 @@ namespace nes
addr = addr % 0x4000; // PPU only has 16 KiB addresses.
if (addr <= address{ 0x3EFF })
{
- mapper_.write_ppu(addr, vram_, value);
+ cartridge_.get_mapper().write_ppu(addr, value, cartridge_, vram_);
return;
}
if (addr <= address{ 0x3FFF })
diff --git a/lib/nes/ppu.hh b/lib/nes/ppu.hh
index 8e521c5..0301ec4 100644
--- a/lib/nes/ppu.hh
+++ b/lib/nes/ppu.hh
@@ -6,15 +6,16 @@ namespace nes
{
class address;
class cpu;
- class mapper;
+ class cartridge;
class display;
class rgb;
struct snapshot;
class ppu
{
+ static constexpr auto sprite_max_count = unsigned{ 64 };
static constexpr auto vram_size = std::size_t{ 0x800 };
- static constexpr auto oam_size = std::size_t{ 0x100 };
+ static constexpr auto oam_size = std::size_t{ sprite_max_count * 4 };
static constexpr auto palette_buffer_size = std::size_t{ 0x20 };
static constexpr auto tile_size = unsigned{ 8 };
// Writes to some registers are ignored until this clock cycle.
@@ -95,7 +96,7 @@ namespace nes
cycle_count current_cycles_;
cpu& cpu_;
- mapper& mapper_;
+ cartridge& cartridge_;
display& display_;
std::uint8_t vram_[vram_size]{};
std::uint8_t oam_[oam_size]{};
@@ -235,7 +236,7 @@ namespace nes
unsigned sprite_count_{ 0 }; // Number of evaluated sprites in sprites_.
public:
- explicit ppu(cpu&, mapper&, display&);
+ explicit ppu(cpu&, cartridge&, display&);
ppu(ppu const&) = delete;
ppu(ppu&&) = delete;
diff --git a/lib/nes/util/CMakeLists.txt b/lib/nes/util/CMakeLists.txt
index fdbc939..6f5c112 100644
--- a/lib/nes/util/CMakeLists.txt
+++ b/lib/nes/util/CMakeLists.txt
@@ -7,4 +7,5 @@ target_sources(
debug.hh
display.hh
rgb.hh
- snapshot.hh) \ No newline at end of file
+ snapshot.hh
+ status.hh) \ No newline at end of file
diff --git a/lib/nes/util/status.hh b/lib/nes/util/status.hh
new file mode 100644
index 0000000..64f5969
--- /dev/null
+++ b/lib/nes/util/status.hh
@@ -0,0 +1,36 @@
+#pragma once
+
+namespace nes
+{
+ /// A status code.
+ enum class status
+ {
+ success,
+ error_uninitialized,
+ error_system_error,
+ error_invalid_ines_data,
+ error_unsupported_mapper,
+ error_unsupported_instruction,
+ };
+
+ constexpr auto to_string(status const status) -> char const*
+ {
+ switch (status)
+ {
+ case status::success:
+ return "Success";
+ case status::error_uninitialized:
+ return "Uninitialized";
+ case status::error_system_error:
+ return "System error";
+ case status::error_invalid_ines_data:
+ return "Invalid iNES data";
+ case status::error_unsupported_mapper:
+ return "Unsupported mapper";
+ case status::error_unsupported_instruction:
+ return "Unsupported instruction";
+ }
+
+ return "(invalid)";
+ }
+} // namespace nes \ No newline at end of file