PAD: Basic support for digital controllers
This commit is contained in:
parent
d84bffead1
commit
8cd75a4937
|
@ -79,7 +79,8 @@ static int Run(int argc, char* argv[])
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
// set log flags
|
// set log flags
|
||||||
g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
|
// g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
|
||||||
|
g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL DMA", LOGLEVEL_DEBUG);
|
||||||
|
|
||||||
#ifdef Y_BUILD_CONFIG_RELEASE
|
#ifdef Y_BUILD_CONFIG_RELEASE
|
||||||
g_pLog->SetFilterLevel(LOGLEVEL_INFO);
|
g_pLog->SetFilterLevel(LOGLEVEL_INFO);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
#include "imgui_impl_sdl.h"
|
#include "imgui_impl_sdl.h"
|
||||||
|
#include "pse/digital_controller.h"
|
||||||
#include "pse/system.h"
|
#include "pse/system.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <glad.h>
|
#include <glad.h>
|
||||||
|
@ -159,6 +160,8 @@ std::unique_ptr<SDLInterface> SDLInterface::Create()
|
||||||
if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() || !intf->CreateGLResources())
|
if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() || !intf->CreateGLResources())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
intf->m_controller = DigitalController::Create();
|
||||||
|
|
||||||
return intf;
|
return intf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +213,73 @@ bool SDLInterface::HandleSDLEvent(const SDL_Event* event)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
case SDL_KEYUP:
|
||||||
|
{
|
||||||
|
const bool pressed = (event->type == SDL_KEYDOWN);
|
||||||
|
switch (event->key.keysym.scancode)
|
||||||
|
{
|
||||||
|
case SDL_SCANCODE_KP_8:
|
||||||
|
case SDL_SCANCODE_I:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Triangle, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_KP_2:
|
||||||
|
case SDL_SCANCODE_K:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Cross, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_KP_4:
|
||||||
|
case SDL_SCANCODE_J:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Square, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_KP_6:
|
||||||
|
case SDL_SCANCODE_L:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Circle, pressed);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SDL_SCANCODE_W:
|
||||||
|
case SDL_SCANCODE_UP:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Up, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_S:
|
||||||
|
case SDL_SCANCODE_DOWN:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Down, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_A:
|
||||||
|
case SDL_SCANCODE_LEFT:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Left, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_D:
|
||||||
|
case SDL_SCANCODE_RIGHT:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Right, pressed);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SDL_SCANCODE_Q:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::L1, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_E:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::R1, pressed);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SDL_SCANCODE_1:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::L2, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_3:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::R3, pressed);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SDL_SCANCODE_RETURN:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Start, pressed);
|
||||||
|
return true;
|
||||||
|
case SDL_SCANCODE_BACKSPACE:
|
||||||
|
m_controller->SetButtonState(DigitalController::Button::Select, pressed);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
m_running = false;
|
m_running = false;
|
||||||
break;
|
break;
|
||||||
|
@ -452,6 +522,8 @@ void SDLInterface::DoSaveState(u32 index)
|
||||||
|
|
||||||
void SDLInterface::Run()
|
void SDLInterface::Run()
|
||||||
{
|
{
|
||||||
|
m_system->SetPadDevice(0, m_controller);
|
||||||
|
|
||||||
while (m_running)
|
while (m_running)
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class System;
|
class System;
|
||||||
|
class DigitalController;
|
||||||
|
|
||||||
class SDLInterface : public HostInterface
|
class SDLInterface : public HostInterface
|
||||||
{
|
{
|
||||||
|
@ -72,4 +74,6 @@ private:
|
||||||
|
|
||||||
std::deque<OSDMessage> m_osd_messages;
|
std::deque<OSDMessage> m_osd_messages;
|
||||||
std::mutex m_osd_messages_lock;
|
std::mutex m_osd_messages_lock;
|
||||||
|
|
||||||
|
std::shared_ptr<DigitalController> m_controller;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
|
#include "pad.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
Log_SetChannel(Bus);
|
Log_SetChannel(Bus);
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ Bus::Bus() = default;
|
||||||
|
|
||||||
Bus::~Bus() = default;
|
Bus::~Bus() = default;
|
||||||
|
|
||||||
bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom)
|
bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, Pad* pad)
|
||||||
{
|
{
|
||||||
if (!LoadBIOS())
|
if (!LoadBIOS())
|
||||||
return false;
|
return false;
|
||||||
|
@ -34,6 +35,7 @@ bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_co
|
||||||
m_interrupt_controller = interrupt_controller;
|
m_interrupt_controller = interrupt_controller;
|
||||||
m_gpu = gpu;
|
m_gpu = gpu;
|
||||||
m_cdrom = cdrom;
|
m_cdrom = cdrom;
|
||||||
|
m_pad = pad;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +218,18 @@ bool Bus::WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value)
|
||||||
return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
|
return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bus::DoReadPad(MemoryAccessSize size, u32 offset, u32& value)
|
||||||
|
{
|
||||||
|
value = m_pad->ReadRegister(offset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bus::DoWritePad(MemoryAccessSize size, u32 offset, u32 value)
|
||||||
|
{
|
||||||
|
m_pad->WriteRegister(offset, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Bus::DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value)
|
bool Bus::DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value)
|
||||||
{
|
{
|
||||||
// TODO: Splitting of half/word reads.
|
// TODO: Splitting of half/word reads.
|
||||||
|
|
|
@ -15,6 +15,7 @@ class DMA;
|
||||||
class InterruptController;
|
class InterruptController;
|
||||||
class GPU;
|
class GPU;
|
||||||
class CDROM;
|
class CDROM;
|
||||||
|
class Pad;
|
||||||
class System;
|
class System;
|
||||||
|
|
||||||
class Bus
|
class Bus
|
||||||
|
@ -23,7 +24,7 @@ public:
|
||||||
Bus();
|
Bus();
|
||||||
~Bus();
|
~Bus();
|
||||||
|
|
||||||
bool Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom);
|
bool Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, Pad* pad);
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
@ -40,6 +41,9 @@ public:
|
||||||
void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr u32 PAD_BASE = 0x1F801040;
|
||||||
|
static constexpr u32 PAD_SIZE = 0x10;
|
||||||
|
static constexpr u32 PAD_MASK = PAD_SIZE - 1;
|
||||||
static constexpr u32 INTERRUPT_CONTROLLER_BASE = 0x1F801070;
|
static constexpr u32 INTERRUPT_CONTROLLER_BASE = 0x1F801070;
|
||||||
static constexpr u32 INTERRUPT_CONTROLLER_SIZE = 0x08;
|
static constexpr u32 INTERRUPT_CONTROLLER_SIZE = 0x08;
|
||||||
static constexpr u32 INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1;
|
static constexpr u32 INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1;
|
||||||
|
@ -75,6 +79,9 @@ private:
|
||||||
bool ReadExpansionRegion2(MemoryAccessSize size, u32 offset, u32& value);
|
bool ReadExpansionRegion2(MemoryAccessSize size, u32 offset, u32& value);
|
||||||
bool WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value);
|
bool WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value);
|
||||||
|
|
||||||
|
bool DoReadPad(MemoryAccessSize size, u32 offset, u32& value);
|
||||||
|
bool DoWritePad(MemoryAccessSize size, u32 offset, u32 value);
|
||||||
|
|
||||||
bool DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value);
|
bool DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value);
|
||||||
bool DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value);
|
bool DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value);
|
||||||
|
|
||||||
|
@ -95,6 +102,7 @@ private:
|
||||||
InterruptController* m_interrupt_controller = nullptr;
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
GPU* m_gpu = nullptr;
|
GPU* m_gpu = nullptr;
|
||||||
CDROM* m_cdrom = nullptr;
|
CDROM* m_cdrom = nullptr;
|
||||||
|
Pad* m_pad = nullptr;
|
||||||
|
|
||||||
std::array<u8, 2097152> m_ram{}; // 2MB RAM
|
std::array<u8, 2097152> m_ram{}; // 2MB RAM
|
||||||
std::array<u8, 524288> m_bios{}; // 512K BIOS ROM
|
std::array<u8, 524288> m_bios{}; // 512K BIOS ROM
|
||||||
|
|
|
@ -80,6 +80,15 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddres
|
||||||
{
|
{
|
||||||
return DoRAMAccess<type, size>(bus_address, value);
|
return DoRAMAccess<type, size>(bus_address, value);
|
||||||
}
|
}
|
||||||
|
else if (bus_address < PAD_BASE)
|
||||||
|
{
|
||||||
|
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
|
||||||
|
}
|
||||||
|
else if (bus_address < (PAD_BASE + PAD_SIZE))
|
||||||
|
{
|
||||||
|
return (type == MemoryAccessType::Read) ? DoReadPad(size, bus_address & PAD_MASK, value) :
|
||||||
|
DoWritePad(size, bus_address & PAD_MASK, value);
|
||||||
|
}
|
||||||
else if (bus_address < INTERRUPT_CONTROLLER_BASE)
|
else if (bus_address < INTERRUPT_CONTROLLER_BASE)
|
||||||
{
|
{
|
||||||
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
|
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include "digital_controller.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
Log_SetChannel(DigitalController);
|
||||||
|
|
||||||
|
DigitalController::DigitalController() = default;
|
||||||
|
|
||||||
|
DigitalController::~DigitalController() = default;
|
||||||
|
|
||||||
|
void DigitalController::SetButtonState(Button button, bool pressed)
|
||||||
|
{
|
||||||
|
if (pressed)
|
||||||
|
m_button_state &= ~(u16(1) << static_cast<u8>(button));
|
||||||
|
else
|
||||||
|
m_button_state |= u16(1) << static_cast<u8>(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DigitalController::Transfer(const u8 data_in, u8* data_out)
|
||||||
|
{
|
||||||
|
bool ack;
|
||||||
|
|
||||||
|
switch (data_in)
|
||||||
|
{
|
||||||
|
case 0x01: // tests if the controller is present
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Access");
|
||||||
|
|
||||||
|
// response is hi-z
|
||||||
|
*data_out = data_in;
|
||||||
|
ack = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x42: // query state
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Query state");
|
||||||
|
QueryState();
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
|
|
||||||
|
default: // sending response
|
||||||
|
{
|
||||||
|
if (m_transfer_fifo.IsEmpty())
|
||||||
|
{
|
||||||
|
Log_WarningPrint("FIFO empty on read");
|
||||||
|
*data_out = 0xFF;
|
||||||
|
ack = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*data_out = m_transfer_fifo.Pop();
|
||||||
|
ack = !m_transfer_fifo.IsEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_DebugPrintf("Transfer, data_in=0x%02X, data_out=0x%02X, ack=%s", data_in, *data_out, ack ? "true" : "false");
|
||||||
|
return ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DigitalController::QueryState()
|
||||||
|
{
|
||||||
|
constexpr u16 ID = 0x5A41;
|
||||||
|
|
||||||
|
m_transfer_fifo.Clear();
|
||||||
|
|
||||||
|
m_transfer_fifo.Push(Truncate8(ID));
|
||||||
|
m_transfer_fifo.Push(Truncate8(ID >> 8));
|
||||||
|
|
||||||
|
m_transfer_fifo.Push(Truncate8(m_button_state)); // Digital switches low
|
||||||
|
m_transfer_fifo.Push(Truncate8(m_button_state >> 8)); // Digital switches high
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DigitalController> DigitalController::Create()
|
||||||
|
{
|
||||||
|
return std::make_shared<DigitalController>();
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
#include "common/fifo_queue.h"
|
||||||
|
#include "pad_device.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class DigitalController final : public PadDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Button : u8
|
||||||
|
{
|
||||||
|
Select = 0,
|
||||||
|
L3 = 1,
|
||||||
|
R3 = 2,
|
||||||
|
Start = 3,
|
||||||
|
Up = 4,
|
||||||
|
Right = 5,
|
||||||
|
Down = 6,
|
||||||
|
Left = 7,
|
||||||
|
L2 = 8,
|
||||||
|
R2 = 9,
|
||||||
|
L1 = 10,
|
||||||
|
R1 = 11,
|
||||||
|
Triangle = 12,
|
||||||
|
Circle = 13,
|
||||||
|
Cross = 14,
|
||||||
|
Square = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
DigitalController();
|
||||||
|
~DigitalController() override;
|
||||||
|
|
||||||
|
static std::shared_ptr<DigitalController> Create();
|
||||||
|
|
||||||
|
void SetButtonState(Button button, bool pressed);
|
||||||
|
|
||||||
|
bool Transfer(const u8 data_in, u8* data_out) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void QueryState();
|
||||||
|
|
||||||
|
// buttons are active low
|
||||||
|
u16 m_button_state = UINT16_C(0xFFFF);
|
||||||
|
|
||||||
|
InlineFIFOQueue<u8, 8> m_transfer_fifo;
|
||||||
|
};
|
|
@ -0,0 +1,172 @@
|
||||||
|
#include "pad.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "common/state_wrapper.h"
|
||||||
|
#include "interrupt_controller.h"
|
||||||
|
#include "pad_device.h"
|
||||||
|
Log_SetChannel(Pad);
|
||||||
|
|
||||||
|
Pad::Pad() = default;
|
||||||
|
|
||||||
|
Pad::~Pad() = default;
|
||||||
|
|
||||||
|
bool Pad::Initialize(InterruptController* interrupt_controller)
|
||||||
|
{
|
||||||
|
m_interrupt_controller = interrupt_controller;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pad::Reset()
|
||||||
|
{
|
||||||
|
SoftReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pad::DoState(StateWrapper& sw)
|
||||||
|
{
|
||||||
|
return !sw.HasError();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Pad::ReadRegister(u32 offset)
|
||||||
|
{
|
||||||
|
switch (offset)
|
||||||
|
{
|
||||||
|
case 0x00: // JOY_DATA
|
||||||
|
{
|
||||||
|
if (m_RX_FIFO.IsEmpty())
|
||||||
|
{
|
||||||
|
Log_WarningPrint("Read from RX fifo when empty");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u8 value = m_RX_FIFO.Pop();
|
||||||
|
UpdateJoyStat();
|
||||||
|
Log_DebugPrintf("JOY_DATA (R) -> 0x%02X", ZeroExtend32(value));
|
||||||
|
return ZeroExtend32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x04: // JOY_STAT
|
||||||
|
return m_JOY_STAT.bits;
|
||||||
|
|
||||||
|
case 0x08: // JOY_MODE
|
||||||
|
return ZeroExtend32(m_JOY_MODE.bits);
|
||||||
|
|
||||||
|
case 0x0A: // JOY_CTRL
|
||||||
|
return ZeroExtend32(m_JOY_CTRL.bits);
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log_ErrorPrintf("Unknown register read: 0x%X", offset);
|
||||||
|
return UINT32_C(0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pad::WriteRegister(u32 offset, u32 value)
|
||||||
|
{
|
||||||
|
switch (offset)
|
||||||
|
{
|
||||||
|
case 0x00: // JOY_DATA
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("JOY_DATA (W) <- 0x%02X", value);
|
||||||
|
if (m_TX_FIFO.IsFull())
|
||||||
|
{
|
||||||
|
Log_WarningPrint("TX FIFO overrun");
|
||||||
|
m_TX_FIFO.RemoveOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_TX_FIFO.Push(Truncate8(value));
|
||||||
|
|
||||||
|
if (m_JOY_CTRL.SELECT)
|
||||||
|
DoTransfer();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x0A: // JOY_CTRL
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("JOY_CTRL <- 0x%04X", value);
|
||||||
|
const bool old_select = m_JOY_CTRL.SELECT;
|
||||||
|
|
||||||
|
m_JOY_CTRL.bits = Truncate16(value);
|
||||||
|
if (m_JOY_CTRL.RESET)
|
||||||
|
SoftReset();
|
||||||
|
|
||||||
|
if (m_JOY_CTRL.ACK)
|
||||||
|
{
|
||||||
|
// reset stat bits
|
||||||
|
m_JOY_STAT.ACKINPUTLEVEL = false;
|
||||||
|
m_JOY_STAT.INTR = false;
|
||||||
|
m_JOY_CTRL.ACK = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!old_select && m_JOY_CTRL.SELECT && !m_TX_FIFO.IsEmpty())
|
||||||
|
DoTransfer();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x08: // JOY_MODE
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("JOY_MODE <- 0x%08X", value);
|
||||||
|
m_JOY_MODE.bits = Truncate16(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x0E:
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("JOY_BAUD <- 0x%08X", value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log_ErrorPrintf("Unknown register write: 0x%X <- 0x%08X", offset, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pad::SoftReset()
|
||||||
|
{
|
||||||
|
m_JOY_CTRL.bits = 0;
|
||||||
|
m_JOY_STAT.bits = 0;
|
||||||
|
m_JOY_MODE.bits = 0;
|
||||||
|
m_RX_FIFO.Clear();
|
||||||
|
m_TX_FIFO.Clear();
|
||||||
|
UpdateJoyStat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pad::UpdateJoyStat()
|
||||||
|
{
|
||||||
|
m_JOY_STAT.RXFIFONEMPTY = !m_RX_FIFO.IsEmpty();
|
||||||
|
m_JOY_STAT.TXDONE = m_TX_FIFO.IsEmpty();
|
||||||
|
m_JOY_STAT.TXRDY = !m_TX_FIFO.IsFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pad::DoTransfer()
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Transferring slot %d", m_JOY_CTRL.SLOT.GetValue());
|
||||||
|
|
||||||
|
const std::shared_ptr<PadDevice>& dev = m_devices[m_JOY_CTRL.SLOT];
|
||||||
|
if (!dev)
|
||||||
|
{
|
||||||
|
// no device present, don't set ACK and read hi-z
|
||||||
|
m_TX_FIFO.Clear();
|
||||||
|
m_RX_FIFO.Clear();
|
||||||
|
m_RX_FIFO.Push(0xFF);
|
||||||
|
UpdateJoyStat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!m_TX_FIFO.IsEmpty())
|
||||||
|
{
|
||||||
|
const u8 data_out = m_TX_FIFO.Pop();
|
||||||
|
u8 data_in;
|
||||||
|
m_JOY_STAT.ACKINPUTLEVEL |= dev->Transfer(data_out, &data_in);
|
||||||
|
m_RX_FIFO.Push(data_in);
|
||||||
|
m_JOY_CTRL.RXEN = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_JOY_STAT.ACKINPUTLEVEL && m_JOY_CTRL.ACKINTEN)
|
||||||
|
{
|
||||||
|
m_JOY_STAT.INTR = true;
|
||||||
|
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateJoyStat();
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
#pragma once
|
||||||
|
#include "common/bitfield.h"
|
||||||
|
#include "common/fifo_queue.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class StateWrapper;
|
||||||
|
|
||||||
|
class InterruptController;
|
||||||
|
class PadDevice;
|
||||||
|
|
||||||
|
class Pad
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pad();
|
||||||
|
~Pad();
|
||||||
|
|
||||||
|
bool Initialize(InterruptController* interrupt_controller);
|
||||||
|
void Reset();
|
||||||
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
PadDevice* GetDevice(u32 slot) { return m_devices[slot].get(); }
|
||||||
|
void SetDevice(u32 slot, std::shared_ptr<PadDevice> dev) { m_devices[slot] = dev; }
|
||||||
|
|
||||||
|
u32 ReadRegister(u32 offset);
|
||||||
|
void WriteRegister(u32 offset, u32 value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr u32 NUM_SLOTS = 2;
|
||||||
|
|
||||||
|
union JOY_CTRL
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
|
||||||
|
BitField<u16, bool, 0, 1> TXEN;
|
||||||
|
BitField<u16, bool, 1, 1> SELECT;
|
||||||
|
BitField<u16, bool, 2, 1> RXEN;
|
||||||
|
BitField<u16, bool, 4, 1> ACK;
|
||||||
|
BitField<u16, bool, 6, 1> RESET;
|
||||||
|
BitField<u16, u8, 8, 2> RXIMODE;
|
||||||
|
BitField<u16, bool, 10, 1> TXINTEN;
|
||||||
|
BitField<u16, bool, 11, 1> RXINTEN;
|
||||||
|
BitField<u16, bool, 12, 1> ACKINTEN;
|
||||||
|
BitField<u16, u8, 13, 1> SLOT;
|
||||||
|
};
|
||||||
|
|
||||||
|
union JOY_STAT
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
|
||||||
|
BitField<u32, bool, 0, 1> TXRDY;
|
||||||
|
BitField<u32, bool, 1, 1> RXFIFONEMPTY;
|
||||||
|
BitField<u32, bool, 2, 1> TXDONE;
|
||||||
|
BitField<u32, bool, 3, 1> ACKINPUTLEVEL;
|
||||||
|
BitField<u32, bool, 7, 1> ACKINPUT;
|
||||||
|
BitField<u32, bool, 9, 1> INTR;
|
||||||
|
BitField<u32, u32, 11, 21> TMR;
|
||||||
|
};
|
||||||
|
|
||||||
|
union JOY_MODE
|
||||||
|
{
|
||||||
|
u16 bits;
|
||||||
|
|
||||||
|
BitField<u16, u8, 0, 2> reload_factor;
|
||||||
|
BitField<u16, u8, 2, 2> character_length;
|
||||||
|
BitField<u16, bool, 4, 1> parity_enable;
|
||||||
|
BitField<u16, u8, 5, 1> parity_type;
|
||||||
|
BitField<u16, u8, 8, 1> clk_polarity;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SoftReset();
|
||||||
|
void UpdateJoyStat();
|
||||||
|
void DoTransfer();
|
||||||
|
|
||||||
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
|
|
||||||
|
JOY_CTRL m_JOY_CTRL = {};
|
||||||
|
JOY_STAT m_JOY_STAT = {};
|
||||||
|
JOY_MODE m_JOY_MODE = {};
|
||||||
|
|
||||||
|
InlineFIFOQueue<u8, 8> m_RX_FIFO;
|
||||||
|
InlineFIFOQueue<u8, 2> m_TX_FIFO;
|
||||||
|
|
||||||
|
std::array<std::shared_ptr<PadDevice>, NUM_SLOTS> m_devices;
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "pad_device.h"
|
||||||
|
|
||||||
|
PadDevice::PadDevice() = default;
|
||||||
|
|
||||||
|
PadDevice::~PadDevice() = default;
|
||||||
|
|
||||||
|
bool PadDevice::Transfer(const u8 data_in, u8* data_out)
|
||||||
|
{
|
||||||
|
*data_out = 0xFF;
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
class PadDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PadDevice();
|
||||||
|
virtual ~PadDevice();
|
||||||
|
|
||||||
|
// Returns the value of ACK, as well as filling out_data.
|
||||||
|
virtual bool Transfer(const u8 data_in, u8* data_out);
|
||||||
|
};
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
<ClCompile Include="cdrom.cpp" />
|
<ClCompile Include="cdrom.cpp" />
|
||||||
<ClCompile Include="cpu_core.cpp" />
|
<ClCompile Include="cpu_core.cpp" />
|
||||||
<ClCompile Include="cpu_disasm.cpp" />
|
<ClCompile Include="cpu_disasm.cpp" />
|
||||||
|
<ClCompile Include="digital_controller.cpp" />
|
||||||
<ClCompile Include="gte.cpp" />
|
<ClCompile Include="gte.cpp" />
|
||||||
<ClCompile Include="dma.cpp" />
|
<ClCompile Include="dma.cpp" />
|
||||||
<ClCompile Include="gpu.cpp" />
|
<ClCompile Include="gpu.cpp" />
|
||||||
|
@ -46,6 +47,8 @@
|
||||||
<ClCompile Include="gpu_hw_opengl.cpp" />
|
<ClCompile Include="gpu_hw_opengl.cpp" />
|
||||||
<ClCompile Include="host_interface.cpp" />
|
<ClCompile Include="host_interface.cpp" />
|
||||||
<ClCompile Include="interrupt_controller.cpp" />
|
<ClCompile Include="interrupt_controller.cpp" />
|
||||||
|
<ClCompile Include="pad.cpp" />
|
||||||
|
<ClCompile Include="pad_device.cpp" />
|
||||||
<ClCompile Include="system.cpp" />
|
<ClCompile Include="system.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -53,6 +56,7 @@
|
||||||
<ClInclude Include="cdrom.h" />
|
<ClInclude Include="cdrom.h" />
|
||||||
<ClInclude Include="cpu_core.h" />
|
<ClInclude Include="cpu_core.h" />
|
||||||
<ClInclude Include="cpu_disasm.h" />
|
<ClInclude Include="cpu_disasm.h" />
|
||||||
|
<ClInclude Include="digital_controller.h" />
|
||||||
<ClInclude Include="gte.h" />
|
<ClInclude Include="gte.h" />
|
||||||
<ClInclude Include="cpu_types.h" />
|
<ClInclude Include="cpu_types.h" />
|
||||||
<ClInclude Include="dma.h" />
|
<ClInclude Include="dma.h" />
|
||||||
|
@ -62,6 +66,8 @@
|
||||||
<ClInclude Include="gte_types.h" />
|
<ClInclude Include="gte_types.h" />
|
||||||
<ClInclude Include="host_interface.h" />
|
<ClInclude Include="host_interface.h" />
|
||||||
<ClInclude Include="interrupt_controller.h" />
|
<ClInclude Include="interrupt_controller.h" />
|
||||||
|
<ClInclude Include="pad.h" />
|
||||||
|
<ClInclude Include="pad_device.h" />
|
||||||
<ClInclude Include="save_state_version.h" />
|
<ClInclude Include="save_state_version.h" />
|
||||||
<ClInclude Include="system.h" />
|
<ClInclude Include="system.h" />
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
<ClCompile Include="interrupt_controller.cpp" />
|
<ClCompile Include="interrupt_controller.cpp" />
|
||||||
<ClCompile Include="cdrom.cpp" />
|
<ClCompile Include="cdrom.cpp" />
|
||||||
<ClCompile Include="gte.cpp" />
|
<ClCompile Include="gte.cpp" />
|
||||||
|
<ClCompile Include="pad.cpp" />
|
||||||
|
<ClCompile Include="pad_device.cpp" />
|
||||||
|
<ClCompile Include="digital_controller.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -31,6 +34,9 @@
|
||||||
<ClInclude Include="cdrom.h" />
|
<ClInclude Include="cdrom.h" />
|
||||||
<ClInclude Include="gte.h" />
|
<ClInclude Include="gte.h" />
|
||||||
<ClInclude Include="gte_types.h" />
|
<ClInclude Include="gte_types.h" />
|
||||||
|
<ClInclude Include="pad.h" />
|
||||||
|
<ClInclude Include="pad_device.h" />
|
||||||
|
<ClInclude Include="digital_controller.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpu_core.inl" />
|
<None Include="cpu_core.inl" />
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
|
#include "pad.h"
|
||||||
|
#include "pad_device.h"
|
||||||
|
|
||||||
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
{
|
{
|
||||||
|
@ -17,6 +19,7 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
// m_gpu = std::make_unique<GPU>();
|
// m_gpu = std::make_unique<GPU>();
|
||||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||||
m_cdrom = std::make_unique<CDROM>();
|
m_cdrom = std::make_unique<CDROM>();
|
||||||
|
m_pad = std::make_unique<Pad>();
|
||||||
}
|
}
|
||||||
|
|
||||||
System::~System() = default;
|
System::~System() = default;
|
||||||
|
@ -26,8 +29,11 @@ bool System::Initialize()
|
||||||
if (!m_cpu->Initialize(m_bus.get()))
|
if (!m_cpu->Initialize(m_bus.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get()))
|
if (!m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(),
|
||||||
|
m_pad.get()))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_dma->Initialize(m_bus.get(), m_gpu.get()))
|
if (!m_dma->Initialize(m_bus.get(), m_gpu.get()))
|
||||||
return false;
|
return false;
|
||||||
|
@ -41,6 +47,9 @@ bool System::Initialize()
|
||||||
if (!m_cdrom->Initialize(m_dma.get(), m_interrupt_controller.get()))
|
if (!m_cdrom->Initialize(m_dma.get(), m_interrupt_controller.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!m_pad->Initialize(m_interrupt_controller.get()))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +73,9 @@ bool System::DoState(StateWrapper& sw)
|
||||||
if (!sw.DoMarker("CDROM") || !m_cdrom->DoState(sw))
|
if (!sw.DoMarker("CDROM") || !m_cdrom->DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!sw.DoMarker("Pad") || !m_pad->DoState(sw))
|
||||||
|
return false;
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +89,7 @@ void System::Reset()
|
||||||
m_interrupt_controller->Reset();
|
m_interrupt_controller->Reset();
|
||||||
m_gpu->Reset();
|
m_gpu->Reset();
|
||||||
m_cdrom->Reset();
|
m_cdrom->Reset();
|
||||||
|
m_pad->Reset();
|
||||||
m_frame_number = 1;
|
m_frame_number = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,3 +208,8 @@ void System::SetSliceTicks(TickCount downcount)
|
||||||
{
|
{
|
||||||
m_cpu->SetSliceTicks(downcount);
|
m_cpu->SetSliceTicks(downcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev)
|
||||||
|
{
|
||||||
|
m_pad->SetDevice(slot, std::move(dev));
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class ByteStream;
|
class ByteStream;
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
@ -16,6 +17,8 @@ class DMA;
|
||||||
class InterruptController;
|
class InterruptController;
|
||||||
class GPU;
|
class GPU;
|
||||||
class CDROM;
|
class CDROM;
|
||||||
|
class Pad;
|
||||||
|
class PadDevice;
|
||||||
|
|
||||||
class System
|
class System
|
||||||
{
|
{
|
||||||
|
@ -40,6 +43,8 @@ public:
|
||||||
|
|
||||||
void SetSliceTicks(TickCount downcount);
|
void SetSliceTicks(TickCount downcount);
|
||||||
|
|
||||||
|
void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
@ -50,5 +55,6 @@ private:
|
||||||
std::unique_ptr<InterruptController> m_interrupt_controller;
|
std::unique_ptr<InterruptController> m_interrupt_controller;
|
||||||
std::unique_ptr<GPU> m_gpu;
|
std::unique_ptr<GPU> m_gpu;
|
||||||
std::unique_ptr<CDROM> m_cdrom;
|
std::unique_ptr<CDROM> m_cdrom;
|
||||||
|
std::unique_ptr<Pad> m_pad;
|
||||||
u32 m_frame_number = 1;
|
u32 m_frame_number = 1;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue