Added controller support

This commit is contained in:
Lauchmelder 2022-03-01 18:13:47 +01:00
parent e5cc1565fd
commit f4e6198a99
No known key found for this signature in database
GPG key ID: C2403C69D78F011D
15 changed files with 310 additions and 6 deletions

View file

@ -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)

View file

@ -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;
};

View file

@ -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
View 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
View 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;
};

View file

@ -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;
}

View 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};
};

View 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;
}

View 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:
};

View 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();
}

View 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;
};

View file

@ -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
View 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
View 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;
};

View file

@ -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()