diff --git a/src/Bus.cpp b/src/Bus.cpp index 96a3b7e..1d7d94f 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -3,6 +3,8 @@ #include +#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(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) diff --git a/src/Bus.hpp b/src/Bus.hpp index 4cd0006..bbbeb8c 100644 --- a/src/Bus.hpp +++ b/src/Bus.hpp @@ -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; }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc1c269..3bc6206 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/ControllerPort.cpp b/src/ControllerPort.cpp new file mode 100644 index 0000000..f08f3b6 --- /dev/null +++ b/src/ControllerPort.cpp @@ -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); + } +} diff --git a/src/ControllerPort.hpp b/src/ControllerPort.hpp new file mode 100644 index 0000000..c9ccd01 --- /dev/null +++ b/src/ControllerPort.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#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::value, bool> = true + > + void PlugInController(int port) + { + connectedDevices[port] = new T; + } + +private: + PortLatch latch; + std::array connectedDevices; +}; \ No newline at end of file diff --git a/src/Mapper.hpp b/src/Mapper.hpp index 3c11679..c3b871d 100644 --- a/src/Mapper.hpp +++ b/src/Mapper.hpp @@ -26,15 +26,14 @@ public: if (header.Flag6.Mirroring == 0x0) { + // Shift Bit 11 into Bit 10 addr &= ~(1 << 10); addr |= ((addr & (1 << 11)) >> 1); - addr &= ~(1 << 11); - } - else - { - addr &= ~(1 << 11); } + // Unset bit 11 + addr &= ~(1 << 11); + return false; } diff --git a/src/controllers/Controller.hpp b/src/controllers/Controller.hpp new file mode 100644 index 0000000..be57dc1 --- /dev/null +++ b/src/controllers/Controller.hpp @@ -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}; +}; diff --git a/src/controllers/StandardController.cpp b/src/controllers/StandardController.cpp new file mode 100644 index 0000000..0fa58a3 --- /dev/null +++ b/src/controllers/StandardController.cpp @@ -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; +} diff --git a/src/controllers/StandardController.hpp b/src/controllers/StandardController.hpp new file mode 100644 index 0000000..e017dc3 --- /dev/null +++ b/src/controllers/StandardController.hpp @@ -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: +}; diff --git a/src/debugger/ControllerPortViewer.cpp b/src/debugger/ControllerPortViewer.cpp new file mode 100644 index 0000000..f18b213 --- /dev/null +++ b/src/debugger/ControllerPortViewer.cpp @@ -0,0 +1,58 @@ +#include "ControllerPortViewer.hpp" + +#include "../ControllerPort.hpp" +#include + +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(); +} diff --git a/src/debugger/ControllerPortViewer.hpp b/src/debugger/ControllerPortViewer.hpp new file mode 100644 index 0000000..c99c4ed --- /dev/null +++ b/src/debugger/ControllerPortViewer.hpp @@ -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; +}; diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index ca48b85..c7e087f 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -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() diff --git a/src/gfx/Input.cpp b/src/gfx/Input.cpp new file mode 100644 index 0000000..a0faf48 --- /dev/null +++ b/src/gfx/Input.cpp @@ -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); +} diff --git a/src/gfx/Input.hpp b/src/gfx/Input.hpp new file mode 100644 index 0000000..204e170 --- /dev/null +++ b/src/gfx/Input.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class Window; + +class Input +{ +public: + static inline void SetWindow(Window* source) { window = source; } + static bool IsKeyDown(int key); + +private: + static Window* window; +}; diff --git a/src/gfx/Window.cpp b/src/gfx/Window.cpp index 5ce1bc1..6154de0 100644 --- a/src/gfx/Window.cpp +++ b/src/gfx/Window.cpp @@ -5,6 +5,8 @@ #include #include +#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()