diff --git a/src/pse-sdl/main.cpp b/src/pse-sdl/main.cpp index 054836bee..31c54f51d 100644 --- a/src/pse-sdl/main.cpp +++ b/src/pse-sdl/main.cpp @@ -79,7 +79,8 @@ static int Run(int argc, char* argv[]) int main(int argc, char* argv[]) { // 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 g_pLog->SetFilterLevel(LOGLEVEL_INFO); diff --git a/src/pse-sdl/sdl_interface.cpp b/src/pse-sdl/sdl_interface.cpp index c846b7f1d..360d0762c 100644 --- a/src/pse-sdl/sdl_interface.cpp +++ b/src/pse-sdl/sdl_interface.cpp @@ -5,6 +5,7 @@ #include "imgui.h" #include "imgui_impl_opengl3.h" #include "imgui_impl_sdl.h" +#include "pse/digital_controller.h" #include "pse/system.h" #include #include @@ -159,6 +160,8 @@ std::unique_ptr SDLInterface::Create() if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() || !intf->CreateGLResources()) return nullptr; + intf->m_controller = DigitalController::Create(); + return intf; } @@ -210,6 +213,73 @@ bool SDLInterface::HandleSDLEvent(const SDL_Event* event) } 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: m_running = false; break; @@ -452,6 +522,8 @@ void SDLInterface::DoSaveState(u32 index) void SDLInterface::Run() { + m_system->SetPadDevice(0, m_controller); + while (m_running) { for (;;) diff --git a/src/pse-sdl/sdl_interface.h b/src/pse-sdl/sdl_interface.h index d310e5a0e..cd6f6b240 100644 --- a/src/pse-sdl/sdl_interface.h +++ b/src/pse-sdl/sdl_interface.h @@ -8,8 +8,10 @@ #include #include #include +#include class System; +class DigitalController; class SDLInterface : public HostInterface { @@ -72,4 +74,6 @@ private: std::deque m_osd_messages; std::mutex m_osd_messages_lock; + + std::shared_ptr m_controller; }; diff --git a/src/pse/bus.cpp b/src/pse/bus.cpp index df32d98fb..c14d99ed7 100644 --- a/src/pse/bus.cpp +++ b/src/pse/bus.cpp @@ -9,6 +9,7 @@ #include "dma.h" #include "gpu.h" #include "interrupt_controller.h" +#include "pad.h" #include Log_SetChannel(Bus); @@ -24,7 +25,7 @@ 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()) return false; @@ -34,6 +35,7 @@ bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_co m_interrupt_controller = interrupt_controller; m_gpu = gpu; m_cdrom = cdrom; + m_pad = pad; 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); } +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) { // TODO: Splitting of half/word reads. diff --git a/src/pse/bus.h b/src/pse/bus.h index 22c5d541d..94dc46c2b 100644 --- a/src/pse/bus.h +++ b/src/pse/bus.h @@ -15,6 +15,7 @@ class DMA; class InterruptController; class GPU; class CDROM; +class Pad; class System; class Bus @@ -23,7 +24,7 @@ public: 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(); bool DoState(StateWrapper& sw); @@ -40,6 +41,9 @@ public: void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF)); 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_SIZE = 0x08; static constexpr u32 INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1; @@ -75,6 +79,9 @@ private: bool ReadExpansionRegion2(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 DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value); @@ -95,6 +102,7 @@ private: InterruptController* m_interrupt_controller = nullptr; GPU* m_gpu = nullptr; CDROM* m_cdrom = nullptr; + Pad* m_pad = nullptr; std::array m_ram{}; // 2MB RAM std::array m_bios{}; // 512K BIOS ROM diff --git a/src/pse/bus.inl b/src/pse/bus.inl index bbbbd187f..cdcea12c0 100644 --- a/src/pse/bus.inl +++ b/src/pse/bus.inl @@ -80,6 +80,15 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddres { return DoRAMAccess(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) { return DoInvalidAccess(type, size, cpu_address, bus_address, value); diff --git a/src/pse/digital_controller.cpp b/src/pse/digital_controller.cpp new file mode 100644 index 000000000..d2a550031 --- /dev/null +++ b/src/pse/digital_controller.cpp @@ -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(button)); + else + m_button_state |= u16(1) << static_cast(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::Create() +{ + return std::make_shared(); +} diff --git a/src/pse/digital_controller.h b/src/pse/digital_controller.h new file mode 100644 index 000000000..989d2fb97 --- /dev/null +++ b/src/pse/digital_controller.h @@ -0,0 +1,45 @@ +#pragma once +#include "common/fifo_queue.h" +#include "pad_device.h" +#include + +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 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 m_transfer_fifo; +}; diff --git a/src/pse/pad.cpp b/src/pse/pad.cpp new file mode 100644 index 000000000..1075bc594 --- /dev/null +++ b/src/pse/pad.cpp @@ -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& 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(); +} diff --git a/src/pse/pad.h b/src/pse/pad.h new file mode 100644 index 000000000..62a3ab3f1 --- /dev/null +++ b/src/pse/pad.h @@ -0,0 +1,86 @@ +#pragma once +#include "common/bitfield.h" +#include "common/fifo_queue.h" +#include "types.h" +#include +#include + +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 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 TXEN; + BitField SELECT; + BitField RXEN; + BitField ACK; + BitField RESET; + BitField RXIMODE; + BitField TXINTEN; + BitField RXINTEN; + BitField ACKINTEN; + BitField SLOT; + }; + + union JOY_STAT + { + u32 bits; + + BitField TXRDY; + BitField RXFIFONEMPTY; + BitField TXDONE; + BitField ACKINPUTLEVEL; + BitField ACKINPUT; + BitField INTR; + BitField TMR; + }; + + union JOY_MODE + { + u16 bits; + + BitField reload_factor; + BitField character_length; + BitField parity_enable; + BitField parity_type; + BitField 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 m_RX_FIFO; + InlineFIFOQueue m_TX_FIFO; + + std::array, NUM_SLOTS> m_devices; +}; diff --git a/src/pse/pad_device.cpp b/src/pse/pad_device.cpp new file mode 100644 index 000000000..5b3c67f46 --- /dev/null +++ b/src/pse/pad_device.cpp @@ -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; +} diff --git a/src/pse/pad_device.h b/src/pse/pad_device.h new file mode 100644 index 000000000..268766439 --- /dev/null +++ b/src/pse/pad_device.h @@ -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); +}; + diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index 39a9efca8..61ae25e0a 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -39,6 +39,7 @@ + @@ -46,6 +47,8 @@ + + @@ -53,6 +56,7 @@ + @@ -62,6 +66,8 @@ + + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index 4b66926de..3ab922a7f 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -13,6 +13,9 @@ + + + @@ -31,6 +34,9 @@ + + + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 224dcf241..9c3573db6 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -7,6 +7,8 @@ #include "dma.h" #include "gpu.h" #include "interrupt_controller.h" +#include "pad.h" +#include "pad_device.h" 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(); m_gpu = GPU::CreateHardwareOpenGLRenderer(); m_cdrom = std::make_unique(); + m_pad = std::make_unique(); } System::~System() = default; @@ -26,8 +29,11 @@ bool System::Initialize() if (!m_cpu->Initialize(m_bus.get())) 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; + } if (!m_dma->Initialize(m_bus.get(), m_gpu.get())) return false; @@ -41,6 +47,9 @@ bool System::Initialize() if (!m_cdrom->Initialize(m_dma.get(), m_interrupt_controller.get())) return false; + if (!m_pad->Initialize(m_interrupt_controller.get())) + return false; + return true; } @@ -64,6 +73,9 @@ bool System::DoState(StateWrapper& sw) if (!sw.DoMarker("CDROM") || !m_cdrom->DoState(sw)) return false; + if (!sw.DoMarker("Pad") || !m_pad->DoState(sw)) + return false; + return !sw.HasError(); } @@ -77,6 +89,7 @@ void System::Reset() m_interrupt_controller->Reset(); m_gpu->Reset(); m_cdrom->Reset(); + m_pad->Reset(); m_frame_number = 1; } @@ -195,3 +208,8 @@ void System::SetSliceTicks(TickCount downcount) { m_cpu->SetSliceTicks(downcount); } + +void System::SetPadDevice(u32 slot, std::shared_ptr dev) +{ + m_pad->SetDevice(slot, std::move(dev)); +} diff --git a/src/pse/system.h b/src/pse/system.h index 3c1aefea0..81884965a 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include class ByteStream; class StateWrapper; @@ -16,6 +17,8 @@ class DMA; class InterruptController; class GPU; class CDROM; +class Pad; +class PadDevice; class System { @@ -40,6 +43,8 @@ public: void SetSliceTicks(TickCount downcount); + void SetPadDevice(u32 slot, std::shared_ptr dev); + private: bool DoState(StateWrapper& sw); @@ -50,5 +55,6 @@ private: std::unique_ptr m_interrupt_controller; std::unique_ptr m_gpu; std::unique_ptr m_cdrom; + std::unique_ptr m_pad; u32 m_frame_number = 1; };