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 "controllers/StandardController.hpp"
|
||||
|
||||
Bus::Bus() :
|
||||
cpu(this), ppu(this), cartridge(this)
|
||||
{
|
||||
|
@ -20,6 +22,8 @@ Bus::Bus() :
|
|||
|
||||
LOG_CORE_INFO("Powering up PPU");
|
||||
ppu.Powerup();
|
||||
|
||||
controllerPort.PlugInController<StandardController>(0);
|
||||
}
|
||||
|
||||
void Bus::Reboot()
|
||||
|
@ -36,6 +40,8 @@ void Bus::Reset()
|
|||
|
||||
uint8_t Bus::Tick()
|
||||
{
|
||||
controllerPort.Tick();
|
||||
|
||||
uint8_t result = cpu.Tick();
|
||||
|
||||
// 3 ppu ticks per cpu tick
|
||||
|
@ -93,6 +99,15 @@ Byte Bus::ReadCPU(Word addr)
|
|||
{
|
||||
return cartridge.ReadCPU(addr);
|
||||
}
|
||||
else if (0x4000 <= addr && addr <= 0x4017)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4016:
|
||||
case 0x4017:
|
||||
return controllerPort.Read(addr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
@ -130,6 +145,16 @@ void Bus::WriteCPU(Word addr, Byte 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)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "CPU.hpp"
|
||||
#include "PPU.hpp"
|
||||
#include "Cartridge.hpp"
|
||||
#include "ControllerPort.hpp"
|
||||
|
||||
/**
|
||||
* @brief The main bus for hardware to communicate.
|
||||
|
@ -85,4 +86,5 @@ private:
|
|||
CPU cpu;
|
||||
PPU ppu;
|
||||
Cartridge cartridge;
|
||||
ControllerPort controllerPort;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ add_executable(nesemu
|
|||
"debugger/PPUWatcher.cpp"
|
||||
"debugger/Disassembler.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
|
||||
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,14 +26,13 @@ public:
|
|||
|
||||
if (header.Flag6.Mirroring == 0x0)
|
||||
{
|
||||
// Shift Bit 11 into Bit 10
|
||||
addr &= ~(1 << 10);
|
||||
addr |= ((addr & (1 << 11)) >> 1);
|
||||
addr &= ~(1 << 11);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Unset bit 11
|
||||
addr &= ~(1 << 11);
|
||||
}
|
||||
|
||||
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 "MemoryViewer.hpp"
|
||||
#include "NametableViewer.hpp"
|
||||
#include "ControllerPortViewer.hpp"
|
||||
|
||||
Debugger::Debugger(Bus* bus) :
|
||||
bus(bus)
|
||||
|
@ -20,6 +21,7 @@ Debugger::Debugger(Bus* bus) :
|
|||
windows.push_back(disassembler);
|
||||
windows.push_back(new MemoryViewer(this, bus));
|
||||
windows.push_back(new NametableViewer(this, bus));
|
||||
windows.push_back(new ControllerPortViewer(this, &bus->controllerPort));
|
||||
}
|
||||
|
||||
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/imgui.h>
|
||||
|
||||
#include "Input.hpp"
|
||||
|
||||
Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
||||
handle(nullptr)
|
||||
{
|
||||
|
@ -17,6 +19,7 @@ Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
|||
}
|
||||
|
||||
glfwMakeContextCurrent(handle);
|
||||
Input::SetWindow(this);
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
|
|
Loading…
Reference in a new issue