srctree

Stefan Andersson parent ff1bdbf9 c7be1285
Add interface/logic for nes game controller(s)

core/CMakeLists.txt added: 539, removed: 22, total 517
@@ -5,6 +5,7 @@ add_library(${PROJECT_NAME}
include/nes/core/icpu.h
include/nes/core/imembank.h
include/nes/core/immu.h
include/nes/core/ines_controller.h
include/nes/core/imos6502.h
include/nes/core/ines_header.h
include/nes/core/invalid_address.h
@@ -12,6 +13,7 @@ add_library(${PROJECT_NAME}
include/nes/core/irom.h
include/nes/core/membank_factory.h
include/nes/core/mmu_factory.h
include/nes/core/nes_controller_factory.h
include/nes/core/opcode.h
include/nes/core/ppu_factory.h
include/nes/core/rom_factory.h
@@ -20,10 +22,13 @@ add_library(${PROJECT_NAME}
src/mapped_membank.h
src/membank.h
src/membank_base.h
src/membank_controller_io.h
src/membank_factory.cpp
src/mmu.cpp
src/mmu.h
src/mmu_factory.cpp
src/nes_controller.h
src/nes_controller_factory.cpp
src/mos6502.cpp
src/mos6502.h
src/opcode.cpp
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,15 @@
#pragma once
 
namespace n_e_s::core {
 
class INesController {
public:
enum class Button { A, B, Select, Start, Up, Down, Left, Right };
 
virtual ~INesController() = default;
 
virtual void set(Button button, bool state) = 0;
virtual bool get(Button button) = 0;
};
 
} // namespace n_e_s::core
No newline at end of file
 
core/include/nes/core/membank_factory.h added: 539, removed: 22, total 517
@@ -1,6 +1,7 @@
#pragma once
 
#include "nes/core/imembank.h"
#include "nes/core/ines_controller.h"
 
namespace n_e_s::core {
 
@@ -9,7 +10,10 @@ class IRom;
 
class MemBankFactory {
public:
[[nodiscard]] static MemBankList create_nes_mem_banks(IPpu *ppu, IRom *rom);
[[nodiscard]] static MemBankList create_nes_mem_banks(IPpu *ppu,
IRom *rom,
INesController *controller1,
INesController *controller2);
 
[[nodiscard]] static MemBankList create_nes_ppu_mem_banks(IRom *rom);
};
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,15 @@
#pragma once
 
#include "nes/core/ines_controller.h"
 
#include <memory>
 
namespace n_e_s::core {
 
class NesControllerFactory {
public:
[[nodiscard]] static std::unique_ptr<INesController>
create_nes_controller();
};
 
} // namespace n_e_s::core
No newline at end of file
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,111 @@
#pragma once
 
#include "nes/core/imembank.h"
#include "nes/core/ines_controller.h"
 
namespace n_e_s::core {
 
class MemBankControllerIO : public IMemBank {
public:
MemBankControllerIO(INesController *controller1,
INesController *controller2)
: controller1_(controller1), controller2_(controller2) {}
 
bool is_address_in_range(uint16_t addr) const override {
return (addr == controller1_addr_) || (addr == controller2_addr_);
}
 
uint8_t read_byte(uint16_t addr) const override {
// Source: https://wiki.nesdev.com/w/index.php/Standard_controller
// In the NES and Famicom, the top three (or five) bits are not
// driven, and so retain the bits of the previous byte on the bus.
// Usually this is the most significant byte of the address of the
// controller port—0x40. Certain games (such as Paperboy) rely on
// this behavior and require that reads from the controller ports
// return exactly $40 or $41 as appropriate.
 
INesController *controller{};
uint8_t read_cnt{0};
 
if (addr == controller1_addr_) {
controller = controller1_;
read_cnt = read_cnt1_++;
} else if (addr == controller2_addr_) {
controller = controller2_;
read_cnt = read_cnt2_++;
} else {
return 0x00;
}
 
uint8_t data = 0x40;
uint8_t btn = 0x00;
 
// Special case, if strobe (0x4016, bit 1) is high, always return button
// A for each controller
if ((data1_ & static_cast<uint8_t>(0x01)) == 0x01) {
btn = controller->get(INesController::Button::A) ? 0x01 : 0x00;
return static_cast<uint8_t>(data & static_cast<uint8_t>(0xFE)) |
btn;
}
 
switch (read_cnt) {
case 0:
btn = controller->get(INesController::Button::A) ? 0x01 : 0x00;
break;
case 1:
btn = controller->get(INesController::Button::B) ? 0x01 : 0x00;
break;
case 2:
btn = controller->get(INesController::Button::Select) ? 0x01 : 0x00;
break;
case 3:
btn = controller->get(INesController::Button::Start) ? 0x01 : 0x00;
break;
case 4:
btn = controller->get(INesController::Button::Up) ? 0x01 : 0x00;
break;
case 5:
btn = controller->get(INesController::Button::Down) ? 0x01 : 0x00;
break;
case 6:
btn = controller->get(INesController::Button::Left) ? 0x01 : 0x00;
break;
case 7:
btn = controller->get(INesController::Button::Right) ? 0x01 : 0x00;
break;
default:
btn = 0x01; // After 8th read, return 1 for official Nintendo
// controllers
}
data = static_cast<uint8_t>(data & static_cast<uint8_t>(0xFE)) | btn;
return data;
}
 
void write_byte(uint16_t addr, uint8_t data) override {
if (addr == controller1_addr_) {
if (((data1_ & static_cast<uint8_t>(0x01)) == 0x01) &&
((data & static_cast<uint8_t>(0x01)) == 0x00)) {
// Neg flank on bit 0 should latch controller button states -
// here just make sure counters is reset
read_cnt1_ = 0;
read_cnt2_ = 0;
}
data1_ = data;
} else if (addr == controller2_addr_) {
data2_ = data;
}
}
 
private:
INesController *controller1_;
INesController *controller2_;
mutable uint8_t read_cnt1_{0};
mutable uint8_t read_cnt2_{0};
uint8_t data1_{0};
uint8_t data2_{0};
 
const uint16_t controller1_addr_{0x4016};
const uint16_t controller2_addr_{0x4017};
}; // namespace n_e_s::core
 
} // namespace n_e_s::core
No newline at end of file
 
core/src/membank_factory.cpp added: 539, removed: 22, total 517
@@ -4,6 +4,7 @@
 
#include "mapped_membank.h"
#include "membank.h"
#include "membank_controller_io.h"
#include "nes/core/ippu.h"
#include "nes/core/irom.h"
 
@@ -54,7 +55,10 @@ auto create_ppu_writer(IPpu *ppu) {
 
} // namespace
 
MemBankList MemBankFactory::create_nes_mem_banks(IPpu *ppu, IRom *rom) {
MemBankList MemBankFactory::create_nes_mem_banks(IPpu *ppu,
IRom *rom,
INesController *controller1,
INesController *controller2) {
MemBankList mem_banks;
 
// Rom, make sure this is first so the mapper can decide if it wants to
@@ -69,7 +73,9 @@ MemBankList MemBankFactory::create_nes_mem_banks(IPpu *ppu, IRom *rom) {
create_ppu_reader(ppu), create_ppu_writer(ppu)));
 
// Io
mem_banks.push_back(std::make_unique<MemBank<0x4000, 0x4017, 0x18>>());
mem_banks.push_back(std::make_unique<MemBank<0x4000, 0x4015, 0x16>>());
mem_banks.push_back(
std::make_unique<MemBankControllerIO>(controller1, controller2));
 
// Io dev
mem_banks.push_back(std::make_unique<MemBank<0x4018, 0x401F, 0x8>>());
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,72 @@
#pragma once
 
#include "nes/core/ines_controller.h"
 
namespace n_e_s::core {
 
class NesController : public INesController {
public:
void set(Button button, bool state) override {
switch (button) {
case Button::A:
a_ = state;
break;
case Button::B:
b_ = state;
break;
case Button::Select:
select_ = state;
break;
case Button::Start:
start_ = state;
break;
case Button::Up:
up_ = state;
break;
case Button::Down:
down_ = state;
break;
case Button::Left:
left_ = state;
break;
case Button::Right:
right_ = state;
break;
}
}
 
bool get(Button button) override {
switch (button) {
case Button::A:
return a_;
case Button::B:
return b_;
case Button::Select:
return select_;
case Button::Start:
return start_;
case Button::Up:
return up_;
case Button::Down:
return down_;
case Button::Left:
return left_;
case Button::Right:
return right_;
default:
return false;
}
}
 
private:
bool a_{false};
bool b_{false};
bool select_{false};
bool start_{false};
bool up_{false};
bool down_{false};
bool left_{false};
bool right_{false};
};
 
} // namespace n_e_s::core
No newline at end of file
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,11 @@
#include "nes/core/nes_controller_factory.h"
 
#include "nes_controller.h"
 
namespace n_e_s::core {
 
std::unique_ptr<INesController> NesControllerFactory::create_nes_controller() {
return std::make_unique<NesController>();
}
 
} // namespace n_e_s::core
No newline at end of file
 
core/test/CMakeLists.txt added: 539, removed: 22, total 517
@@ -26,6 +26,7 @@ add_executable(${PROJECT_NAME}
src/test_ines_header.cpp
src/test_invalid_address.cpp
src/test_mmu.cpp
src/test_nes_controller.cpp
src/test_opcode.cpp
src/test_ppu.cpp
src/test_ppu_membank.cpp
 
core/test/src/test_mmu.cpp added: 539, removed: 22, total 517
@@ -4,6 +4,7 @@
#include "nes/core/mmu_factory.h"
 
#include "mock_irom.h"
#include "mock_nes_controller.h"
#include "nes/core/test/mock_membank.h"
#include "nes/core/test/mock_ppu.h"
 
@@ -23,8 +24,14 @@ class NesMmuTest : public ::testing::Test {
public:
MockPpu ppu{};
testing::NiceMock<MockIRom> rom{};
std::unique_ptr<IMmu> mmu{MmuFactory::create(
MemBankFactory::create_nes_mem_banks(&ppu, &rom))};
::testing::StrictMock<MockNesController> controller1{};
::testing::StrictMock<MockNesController> controller2{};
 
std::unique_ptr<IMmu> mmu{
MmuFactory::create(MemBankFactory::create_nes_mem_banks(&ppu,
&rom,
&controller1,
&controller2))};
};
 
class MmuTest : public ::testing::Test {
@@ -95,4 +102,147 @@ TEST_F(MmuTest, set_membanks) {
mmu->write_byte(1234u, 42u);
}
 
TEST_F(NesMmuTest, controller_1) {
// No button pressed, extra reads at end to make sure controller is not read
EXPECT_CALL(controller1, get(INesController::Button::A))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::B))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Select))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Start))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Up))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Down))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Left))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Right))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
 
// Latch controller button states by toggling strobe (0x4016, bit 0), now
// new controller read is possible (while stope is high, button A's state
// should be returned on read)
// is possible
mmu->write_byte(0x4016, 0x01);
EXPECT_CALL(controller1, get(INesController::Button::A))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::A))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4016));
mmu->write_byte(0x4016, 0x00);
 
// All buttons pressed, extra reads at end to make sure controller is not
// read
EXPECT_CALL(controller1, get(INesController::Button::A))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::B))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Select))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Start))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Up))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Down))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Left))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_CALL(controller1, get(INesController::Button::Right))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
EXPECT_EQ(0x41, mmu->read_byte(0x4016));
}
 
TEST_F(NesMmuTest, controller_2) {
// No button pressed, extra reads at end to make sure controller is not read
EXPECT_CALL(controller2, get(INesController::Button::A))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::B))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Select))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Start))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Up))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Down))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Left))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Right))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
 
// Latch controller button states by toggling strobe (0x4016, bit 0), now
// new controller read is possible (while stope is high, button A's state
// should be returned on read)
mmu->write_byte(0x4016, 0x01);
EXPECT_CALL(controller2, get(INesController::Button::A))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::A))
.WillOnce(testing::Return(false));
EXPECT_EQ(0x40, mmu->read_byte(0x4017));
mmu->write_byte(0x4016, 0x00);
 
// All buttons pressed, extra reads at end to make sure controller is not
// read
EXPECT_CALL(controller2, get(INesController::Button::A))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::B))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Select))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Start))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Up))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Down))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Left))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_CALL(controller2, get(INesController::Button::Right))
.WillOnce(testing::Return(true));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
EXPECT_EQ(0x41, mmu->read_byte(0x4017));
}
 
} // namespace
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,80 @@
#include "nes/core/ines_controller.h"
#include "nes/core/nes_controller_factory.h"
 
#include <gtest/gtest.h>
#include <memory>
 
using namespace n_e_s::core;
 
namespace {
 
class NesControllerTest : public ::testing::Test {
public:
std::unique_ptr<INesController> controller{
NesControllerFactory::create_nes_controller()};
 
void expect_buttons_pressed(const bool a,
const bool b,
const bool select,
const bool start,
const bool up,
const bool down,
const bool left,
const bool right) {
EXPECT_EQ(a, controller->get(INesController::Button::A));
EXPECT_EQ(b, controller->get(INesController::Button::B));
EXPECT_EQ(select, controller->get(INesController::Button::Select));
EXPECT_EQ(start, controller->get(INesController::Button::Start));
EXPECT_EQ(up, controller->get(INesController::Button::Up));
EXPECT_EQ(down, controller->get(INesController::Button::Down));
EXPECT_EQ(left, controller->get(INesController::Button::Left));
EXPECT_EQ(right, controller->get(INesController::Button::Right));
}
};
 
TEST_F(NesControllerTest, set_get_initial) {
expect_buttons_pressed(
false, false, false, false, false, false, false, false);
}
 
TEST_F(NesControllerTest, set_get_individual) {
controller->set(INesController::Button::A, true);
expect_buttons_pressed(
true, false, false, false, false, false, false, false);
controller->set(INesController::Button::B, true);
expect_buttons_pressed(
true, true, false, false, false, false, false, false);
controller->set(INesController::Button::Select, true);
expect_buttons_pressed(true, true, true, false, false, false, false, false);
controller->set(INesController::Button::Start, true);
expect_buttons_pressed(true, true, true, true, false, false, false, false);
controller->set(INesController::Button::Up, true);
expect_buttons_pressed(true, true, true, true, true, false, false, false);
controller->set(INesController::Button::Down, true);
expect_buttons_pressed(true, true, true, true, true, true, false, false);
controller->set(INesController::Button::Left, true);
expect_buttons_pressed(true, true, true, true, true, true, true, false);
controller->set(INesController::Button::Right, true);
expect_buttons_pressed(true, true, true, true, true, true, true, true);
controller->set(INesController::Button::A, false);
expect_buttons_pressed(false, true, true, true, true, true, true, true);
controller->set(INesController::Button::B, false);
expect_buttons_pressed(false, false, true, true, true, true, true, true);
controller->set(INesController::Button::Select, false);
expect_buttons_pressed(false, false, false, true, true, true, true, true);
controller->set(INesController::Button::Start, false);
expect_buttons_pressed(false, false, false, false, true, true, true, true);
controller->set(INesController::Button::Up, false);
expect_buttons_pressed(false, false, false, false, false, true, true, true);
controller->set(INesController::Button::Down, false);
expect_buttons_pressed(
false, false, false, false, false, false, true, true);
controller->set(INesController::Button::Left, false);
expect_buttons_pressed(
false, false, false, false, false, false, false, true);
controller->set(INesController::Button::Right, false);
expect_buttons_pressed(
false, false, false, false, false, false, false, false);
}
 
} // namespace
No newline at end of file
 
filename was Deleted added: 539, removed: 22, total 517
@@ -0,0 +1,15 @@
#pragma once
 
#include "nes/core/ines_controller.h"
 
#include <gmock/gmock.h>
 
namespace n_e_s::core::test {
 
class MockNesController : public INesController {
public:
MOCK_METHOD(void, set, (Button, bool), (override));
MOCK_METHOD(bool, get, (Button), (override));
};
 
} // namespace n_e_s::core::test
No newline at end of file
 
nes/include/nes/nes.h added: 539, removed: 22, total 517
@@ -13,6 +13,9 @@ struct PpuRegisters;
class IMmu;
 
class IRom;
 
class INesController;
 
} // namespace n_e_s::core
 
namespace n_e_s::nes {
@@ -45,6 +48,11 @@ public:
n_e_s::core::PpuRegisters &ppu_registers();
const n_e_s::core::PpuRegisters &ppu_registers() const;
 
n_e_s::core::INesController &controller1();
const n_e_s::core::INesController &controller1() const;
n_e_s::core::INesController &controller2();
const n_e_s::core::INesController &controller2() const;
 
uint64_t current_cycle() const;
 
private:
@@ -59,6 +67,9 @@ private:
 
std::unique_ptr<n_e_s::core::IRom> rom_;
 
std::unique_ptr<n_e_s::core::INesController> controller1_;
std::unique_ptr<n_e_s::core::INesController> controller2_;
 
uint64_t cycle_{};
};
 
 
nes/src/nes.cpp added: 539, removed: 22, total 517
@@ -5,6 +5,9 @@
#include "nes/core/imos6502.h"
#include "nes/core/ippu.h"
#include "nes/core/irom.h"
 
#include "nes/core/nes_controller_factory.h"
 
#include "nes/core/membank_factory.h"
#include "nes/core/mmu_factory.h"
#include "nes/core/ppu_factory.h"
@@ -46,7 +49,9 @@ Nes::Nes()
cpu_registers_(std::make_unique<n_e_s::core::CpuRegisters>()),
cpu_(CpuFactory::create_mos6502(cpu_registers_.get(),
mmu_.get(),
ppu_.get())) {
ppu_.get())),
controller1_(NesControllerFactory::create_nes_controller()),
controller2_(NesControllerFactory::create_nes_controller()) {
// P should be set to 0x34 according to the information here:
// https://wiki.nesdev.com/w/index.php/CPU_power_up_state
// However, nestest sets p to 0x24 instead. We do that for now as well.
@@ -81,8 +86,8 @@ void Nes::load_rom(std::istream &bytestream) {
MemBankFactory::create_nes_ppu_mem_banks(rom_.get())};
ppu_mmu_->set_mem_banks(std::move(ppu_membanks));
 
MemBankList cpu_membanks{
MemBankFactory::create_nes_mem_banks(ppu_.get(), rom_.get())};
MemBankList cpu_membanks{MemBankFactory::create_nes_mem_banks(
ppu_.get(), rom_.get(), controller1_.get(), controller2_.get())};
mmu_->set_mem_banks(std::move(cpu_membanks));
 
reset();
@@ -130,6 +135,22 @@ const n_e_s::core::PpuRegisters &Nes::ppu_registers() const {
return *ppu_registers_;
}
 
n_e_s::core::INesController &Nes::controller1() {
return *controller1_;
}
 
const n_e_s::core::INesController &Nes::controller1() const {
return *controller1_;
}
 
n_e_s::core::INesController &Nes::controller2() {
return *controller2_;
}
 
const n_e_s::core::INesController &Nes::controller2() const {
return *controller2_;
}
 
uint64_t Nes::current_cycle() const {
return cycle_;
}