summaryrefslogtreecommitdiff
path: root/lib/nes/ref/cpu.hh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nes/ref/cpu.hh')
-rw-r--r--lib/nes/ref/cpu.hh298
1 files changed, 298 insertions, 0 deletions
diff --git a/lib/nes/ref/cpu.hh b/lib/nes/ref/cpu.hh
new file mode 100644
index 0000000..05efe56
--- /dev/null
+++ b/lib/nes/ref/cpu.hh
@@ -0,0 +1,298 @@
+#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