Added controller support
This commit is contained in:
parent
e5cc1565fd
commit
f4e6198a99
25
src/Bus.cpp
25
src/Bus.cpp
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "controllers/StandardController.hpp"
|
||||||
|
|
||||||
Bus::Bus() :
|
Bus::Bus() :
|
||||||
cpu(this), ppu(this), cartridge(this)
|
cpu(this), ppu(this), cartridge(this)
|
||||||
{
|
{
|
||||||
|
@ -20,6 +22,8 @@ Bus::Bus() :
|
||||||
|
|
||||||
LOG_CORE_INFO("Powering up PPU");
|
LOG_CORE_INFO("Powering up PPU");
|
||||||
ppu.Powerup();
|
ppu.Powerup();
|
||||||
|
|
||||||
|
controllerPort.PlugInController<StandardController>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::Reboot()
|
void Bus::Reboot()
|
||||||
|
@ -36,6 +40,8 @@ void Bus::Reset()
|
||||||
|
|
||||||
uint8_t Bus::Tick()
|
uint8_t Bus::Tick()
|
||||||
{
|
{
|
||||||
|
controllerPort.Tick();
|
||||||
|
|
||||||
uint8_t result = cpu.Tick();
|
uint8_t result = cpu.Tick();
|
||||||
|
|
||||||
// 3 ppu ticks per cpu tick
|
// 3 ppu ticks per cpu tick
|
||||||
|
@ -93,6 +99,15 @@ Byte Bus::ReadCPU(Word addr)
|
||||||
{
|
{
|
||||||
return cartridge.ReadCPU(addr);
|
return cartridge.ReadCPU(addr);
|
||||||
}
|
}
|
||||||
|
else if (0x4000 <= addr && addr <= 0x4017)
|
||||||
|
{
|
||||||
|
switch (addr)
|
||||||
|
{
|
||||||
|
case 0x4016:
|
||||||
|
case 0x4017:
|
||||||
|
return controllerPort.Read(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
@ -130,6 +145,16 @@ void Bus::WriteCPU(Word addr, Byte val)
|
||||||
{
|
{
|
||||||
cartridge.WriteCPU(addr, val);
|
cartridge.WriteCPU(addr, val);
|
||||||
}
|
}
|
||||||
|
else if (0x4000 <= addr && addr <= 0x4017)
|
||||||
|
{
|
||||||
|
switch (addr)
|
||||||
|
{
|
||||||
|
case 0x4016:
|
||||||
|
case 0x4017:
|
||||||
|
controllerPort.Write(addr, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::WritePPU(Word addr, Byte val)
|
void Bus::WritePPU(Word addr, Byte val)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "CPU.hpp"
|
#include "CPU.hpp"
|
||||||
#include "PPU.hpp"
|
#include "PPU.hpp"
|
||||||
#include "Cartridge.hpp"
|
#include "Cartridge.hpp"
|
||||||
|
#include "ControllerPort.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The main bus for hardware to communicate.
|
* @brief The main bus for hardware to communicate.
|
||||||
|
@ -85,4 +86,5 @@ private:
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
PPU ppu;
|
PPU ppu;
|
||||||
Cartridge cartridge;
|
Cartridge cartridge;
|
||||||
|
ControllerPort controllerPort;
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ add_executable(nesemu
|
||||||
"debugger/PPUWatcher.cpp"
|
"debugger/PPUWatcher.cpp"
|
||||||
"debugger/Disassembler.cpp"
|
"debugger/Disassembler.cpp"
|
||||||
"debugger/MemoryViewer.cpp"
|
"debugger/MemoryViewer.cpp"
|
||||||
"debugger/NametableViewer.cpp" )
|
"debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp")
|
||||||
|
|
||||||
target_include_directories(nesemu PRIVATE
|
target_include_directories(nesemu PRIVATE
|
||||||
mappers
|
mappers
|
||||||
|
|
41
src/ControllerPort.cpp
Normal file
41
src/ControllerPort.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include "ControllerPort.hpp"
|
||||||
|
|
||||||
|
ControllerPort::ControllerPort() :
|
||||||
|
latch{0}
|
||||||
|
{
|
||||||
|
for (auto it = connectedDevices.begin(); it != connectedDevices.end(); it++)
|
||||||
|
{
|
||||||
|
*it = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerPort::~ControllerPort()
|
||||||
|
{
|
||||||
|
for (Controller* controller : connectedDevices)
|
||||||
|
{
|
||||||
|
if (controller)
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Byte ControllerPort::Write(Word addr, Byte val)
|
||||||
|
{
|
||||||
|
if (addr != 0x4016)
|
||||||
|
return 0x00;
|
||||||
|
|
||||||
|
latch.Raw = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
Byte ControllerPort::Read(Word addr)
|
||||||
|
{
|
||||||
|
return connectedDevices[addr & 1]->CLK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerPort::Tick()
|
||||||
|
{
|
||||||
|
for (Controller* controller : connectedDevices)
|
||||||
|
{
|
||||||
|
if (controller)
|
||||||
|
controller->OUT(latch);
|
||||||
|
}
|
||||||
|
}
|
35
src/ControllerPort.hpp
Normal file
35
src/ControllerPort.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <array>
|
||||||
|
#include <type_traits>
|
||||||
|
#include "Types.hpp"
|
||||||
|
|
||||||
|
#include "controllers/Controller.hpp"
|
||||||
|
|
||||||
|
class ControllerPort
|
||||||
|
{
|
||||||
|
friend class ControllerPortViewer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ControllerPort();
|
||||||
|
~ControllerPort();
|
||||||
|
|
||||||
|
Byte Write(Word addr, Byte val);
|
||||||
|
Byte Read(Word addr);
|
||||||
|
|
||||||
|
void Tick();
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename T,
|
||||||
|
std::enable_if_t<std::is_base_of<Controller, T>::value, bool> = true
|
||||||
|
>
|
||||||
|
void PlugInController(int port)
|
||||||
|
{
|
||||||
|
connectedDevices[port] = new T;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PortLatch latch;
|
||||||
|
std::array<Controller*, 2> connectedDevices;
|
||||||
|
};
|
|
@ -26,15 +26,14 @@ public:
|
||||||
|
|
||||||
if (header.Flag6.Mirroring == 0x0)
|
if (header.Flag6.Mirroring == 0x0)
|
||||||
{
|
{
|
||||||
|
// Shift Bit 11 into Bit 10
|
||||||
addr &= ~(1 << 10);
|
addr &= ~(1 << 10);
|
||||||
addr |= ((addr & (1 << 11)) >> 1);
|
addr |= ((addr & (1 << 11)) >> 1);
|
||||||
addr &= ~(1 << 11);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
addr &= ~(1 << 11);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unset bit 11
|
||||||
|
addr &= ~(1 << 11);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/controllers/Controller.hpp
Normal file
36
src/controllers/Controller.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Types.hpp"
|
||||||
|
|
||||||
|
union PortLatch
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
Byte Controller : 1;
|
||||||
|
Byte Expansion : 2;
|
||||||
|
Byte Padding : 5;
|
||||||
|
} Ports;
|
||||||
|
|
||||||
|
Byte Raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Controller
|
||||||
|
{
|
||||||
|
friend class ControllerPortViewer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Controller(int outPin) : outPin(outPin) {}
|
||||||
|
|
||||||
|
virtual void OUT(PortLatch latch) = 0;
|
||||||
|
|
||||||
|
inline Byte CLK()
|
||||||
|
{
|
||||||
|
Byte output = outRegister & 1;
|
||||||
|
outRegister >>= 1;
|
||||||
|
return 0xF ^ (output << outPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Byte outPin;
|
||||||
|
Byte outRegister{0};
|
||||||
|
};
|
30
src/controllers/StandardController.cpp
Normal file
30
src/controllers/StandardController.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "StandardController.hpp"
|
||||||
|
|
||||||
|
#include "Input.hpp"
|
||||||
|
|
||||||
|
StandardController::StandardController() :
|
||||||
|
Controller(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardController::OUT(PortLatch latch)
|
||||||
|
{
|
||||||
|
if (!latch.Ports.Controller)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StandardButtons pressed;
|
||||||
|
|
||||||
|
pressed.Buttons.A = Input::IsKeyDown(GLFW_KEY_S);
|
||||||
|
pressed.Buttons.B = Input::IsKeyDown(GLFW_KEY_A);
|
||||||
|
pressed.Buttons.Select = Input::IsKeyDown(GLFW_KEY_RIGHT_SHIFT);
|
||||||
|
pressed.Buttons.Start = Input::IsKeyDown(GLFW_KEY_ENTER);
|
||||||
|
pressed.Buttons.Up = Input::IsKeyDown(GLFW_KEY_UP);
|
||||||
|
pressed.Buttons.Down = Input::IsKeyDown(GLFW_KEY_DOWN);
|
||||||
|
pressed.Buttons.Left = Input::IsKeyDown(GLFW_KEY_LEFT);
|
||||||
|
pressed.Buttons.Right = Input::IsKeyDown(GLFW_KEY_RIGHT);
|
||||||
|
|
||||||
|
if (pressed.Raw != 0)
|
||||||
|
volatile int jdfi = 3;
|
||||||
|
|
||||||
|
outRegister = pressed.Raw;
|
||||||
|
}
|
31
src/controllers/StandardController.hpp
Normal file
31
src/controllers/StandardController.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Controller.hpp"
|
||||||
|
|
||||||
|
union StandardButtons
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
Byte A : 1;
|
||||||
|
Byte B : 1;
|
||||||
|
Byte Select : 1;
|
||||||
|
Byte Start : 1;
|
||||||
|
Byte Up : 1;
|
||||||
|
Byte Down : 1;
|
||||||
|
Byte Left : 1;
|
||||||
|
Byte Right : 1;
|
||||||
|
} Buttons;
|
||||||
|
|
||||||
|
Byte Raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StandardController :
|
||||||
|
public Controller
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StandardController();
|
||||||
|
|
||||||
|
virtual void OUT(PortLatch latch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
58
src/debugger/ControllerPortViewer.cpp
Normal file
58
src/debugger/ControllerPortViewer.cpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#include "ControllerPortViewer.hpp"
|
||||||
|
|
||||||
|
#include "../ControllerPort.hpp"
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
|
ControllerPortViewer::ControllerPortViewer(Debugger* parent, ControllerPort* controllerPort) :
|
||||||
|
DebugWindow("Controller Port Viewer", parent), controllerPort(controllerPort)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerPortViewer::OnRender()
|
||||||
|
{
|
||||||
|
if (!ImGui::Begin(title.c_str(), &isOpen))
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Latch : %02X", controllerPort->latch.Raw);
|
||||||
|
ImGui::Text("Controller port: %d", controllerPort->latch.Ports.Controller);
|
||||||
|
ImGui::Text("Expansion port : %d", controllerPort->latch.Ports.Expansion);
|
||||||
|
|
||||||
|
int counter = 1;
|
||||||
|
for (Controller* controller : controllerPort->connectedDevices)
|
||||||
|
{
|
||||||
|
if (controller == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string controllerName = "Controller " + std::to_string(counter);
|
||||||
|
if (ImGui::CollapsingHeader(controllerName.c_str(), ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
|
{
|
||||||
|
ImGui::Text("Output Line: D%d", controller->outPin);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::InputScalar("Shift Register", ImGuiDataType_U8, &controller->outRegister, (const void*)0, (const void*)0, "%02X", ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
|
|
||||||
|
if (ImGui::BeginTable(controllerName.c_str(), 8))
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("%d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("%d", (controller->outRegister >> i) & 0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
17
src/debugger/ControllerPortViewer.hpp
Normal file
17
src/debugger/ControllerPortViewer.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DebugWindow.hpp"
|
||||||
|
|
||||||
|
class ControllerPort;
|
||||||
|
|
||||||
|
class ControllerPortViewer :
|
||||||
|
public DebugWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ControllerPortViewer(Debugger* parent, ControllerPort* controllerPort);
|
||||||
|
|
||||||
|
virtual void OnRender() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ControllerPort* controllerPort;
|
||||||
|
};
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Disassembler.hpp"
|
#include "Disassembler.hpp"
|
||||||
#include "MemoryViewer.hpp"
|
#include "MemoryViewer.hpp"
|
||||||
#include "NametableViewer.hpp"
|
#include "NametableViewer.hpp"
|
||||||
|
#include "ControllerPortViewer.hpp"
|
||||||
|
|
||||||
Debugger::Debugger(Bus* bus) :
|
Debugger::Debugger(Bus* bus) :
|
||||||
bus(bus)
|
bus(bus)
|
||||||
|
@ -20,6 +21,7 @@ Debugger::Debugger(Bus* bus) :
|
||||||
windows.push_back(disassembler);
|
windows.push_back(disassembler);
|
||||||
windows.push_back(new MemoryViewer(this, bus));
|
windows.push_back(new MemoryViewer(this, bus));
|
||||||
windows.push_back(new NametableViewer(this, bus));
|
windows.push_back(new NametableViewer(this, bus));
|
||||||
|
windows.push_back(new ControllerPortViewer(this, &bus->controllerPort));
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugger::~Debugger()
|
Debugger::~Debugger()
|
||||||
|
|
9
src/gfx/Input.cpp
Normal file
9
src/gfx/Input.cpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include "Window.hpp"
|
||||||
|
#include "Input.hpp"
|
||||||
|
|
||||||
|
Window* Input::window = nullptr;
|
||||||
|
|
||||||
|
bool Input::IsKeyDown(int key)
|
||||||
|
{
|
||||||
|
return (glfwGetKey(window->GetNativeWindow(), key) == GLFW_PRESS);
|
||||||
|
}
|
16
src/gfx/Input.hpp
Normal file
16
src/gfx/Input.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
class Input
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline void SetWindow(Window* source) { window = source; }
|
||||||
|
static bool IsKeyDown(int key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Window* window;
|
||||||
|
};
|
|
@ -5,6 +5,8 @@
|
||||||
#include <imgui/backends/imgui_impl_opengl3.h>
|
#include <imgui/backends/imgui_impl_opengl3.h>
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
|
#include "Input.hpp"
|
||||||
|
|
||||||
Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
||||||
handle(nullptr)
|
handle(nullptr)
|
||||||
{
|
{
|
||||||
|
@ -17,6 +19,7 @@ Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwMakeContextCurrent(handle);
|
glfwMakeContextCurrent(handle);
|
||||||
|
Input::SetWindow(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::~Window()
|
Window::~Window()
|
||||||
|
|
Loading…
Reference in a new issue