From 8808bf2142ea3847ed8111dc4286773ea40478bf Mon Sep 17 00:00:00 2001 From: John Baumann Date: Sat, 9 Mar 2024 20:04:55 -0600 Subject: [PATCH 1/5] Cherry-pick mulitap to redux repo --- src/mips/psyqo/advancedpad.hh | 193 +++++++++++ src/mips/psyqo/examples/multitap/Makefile | 12 + src/mips/psyqo/examples/multitap/multitap.cpp | 150 +++++++++ src/mips/psyqo/hardware/sio.hh | 67 ++++ src/mips/psyqo/src/advancedpad.cpp | 305 ++++++++++++++++++ src/mips/psyqo/src/hardware/sio.cpp | 33 ++ 6 files changed, 760 insertions(+) create mode 100644 src/mips/psyqo/advancedpad.hh create mode 100644 src/mips/psyqo/examples/multitap/Makefile create mode 100644 src/mips/psyqo/examples/multitap/multitap.cpp create mode 100644 src/mips/psyqo/hardware/sio.hh create mode 100644 src/mips/psyqo/src/advancedpad.cpp create mode 100644 src/mips/psyqo/src/hardware/sio.cpp diff --git a/src/mips/psyqo/advancedpad.hh b/src/mips/psyqo/advancedpad.hh new file mode 100644 index 000000000..fc95097f3 --- /dev/null +++ b/src/mips/psyqo/advancedpad.hh @@ -0,0 +1,193 @@ +/* + +MIT License + +Copyright (c) 2023 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#pragma once + +#include +#include + +#include "psyqo/application.hh" + +namespace psyqo { + +/** + * @brief An advanced class to access the pads. + * + * @details This class is meant to be used as a singleton, probably in + * the `Application` derived class. It does not use the BIOS' + * PAD interface. Instead, it uses the SIO interface directly, and + * can therefore support more device types including multitaps. Polling + * alternates between the two pads/ports on each frame. + */ + +class AdvancedPad { + public: + enum Pad { Pad1a, Pad1b, Pad1c, Pad1d, Pad2a, Pad2b, Pad2c, Pad2d }; + + enum Button { + 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, + }; + + enum Command : uint8_t { + PadSelect = 0x01, + ReadPad = 0x42, // 'B' Read Buttons AND analog inputs + // Config mode commands + ToggleConfigMode = 0x43, // 'C' Enter/Exit Configuration Mode + SetLED = 0x44, // 'D' Set LED State (analog mode on/off) + GetLED = 0x45, // 'E' Get LED State (and whatever values) + GetMotorInfo = 0x46, // 'F' Allegedly get info about a motor + GetMotorList = 0x47, // 'G' Allegedly get list of motors + GetMotorState = 0x48, // 'H' Allegedly get motor state + GetSupportedModes = 0x4C, // 'L' Allegedly get supported modes + ConfigRequestFormat = 0x4D, // 'M' Allegedly configure poll request format + ConfigResponseFormat = 0x4F, // 'O' Allegedly configure poll response format + + // Config mode commands + + }; + + enum PadType : uint8_t { + Mouse = 0x12, // (two button mouse) + NegCon = 0x23, // (steering twist/wheel/paddle) + KonamiLightgun = 0x31, // (IRQ10-type) + DigitalPad = 0x41, // (or analog pad/stick in digital mode; LED=Off) + AnalogStick = 0x53, // (or analog pad in "flight mode"; LED=Green) + NamcoLightgun = 0x63, // (Cinch-type) + AnalogPad = 0x73, // (in normal analog mode; LED=Red) + Multitap = 0x80, // (multiplayer adaptor) (when activated) + Jogcon = 0xe3, // (steering dial) + ConfigMode = 0xf3, // (when in config mode; see rumble command 43h) + None = 0xFF // (no controller connected, pins floating High-Z) + }; + + struct Event { + enum { PadConnected, PadDisconnected, ButtonPressed, ButtonReleased } type; + Pad pad; + Button button; + }; + + enum class TapReadMode { + Passthrough, + BufferedTransferAll, + }; + + /** + * @brief Initializes the pads. + * + * @details This will initialize the pads polling by calling the BIOS' + * interface. This means this method cannot be called from the `prepare` + * method of the `Application` class, but rather from the `start` method + * of the root `Scene` object. Also, there can be interference with + * the BIOS' memory card functions, so this method is explicit to be + * called in the right order. + */ + void initialize(); + + /** + * @brief Sets the event callback function. + * + * @details The event callback will be called for each pad-related event, + * such as pad connection / disconnection, or button press / release. + * The callback will only be called between frames. + * + * Scenes that are calling `setOnEvent` during their `start` method should + * call `setOnEvent` again in their `teardown` method with the `nullptr` + * value in order to unregister the event callback cleanly. + * + * Only one callback can be registered at a time, so setting a new + * callback will simply remove the previous one. + * + * Careful about what is called from the callback: pushing or popping scenes + * might call into `setOnEvent` as a result, and could end up corrupting + * memory as a result of the callback being deleted while being executed. + */ + void setOnEvent(eastl::function &&callback) { m_callback = eastl::move(callback); } + + /** + * @brief Returns the state of a pad. + * + * @details Returns the state of a pad. The state is a boolean value + * that is `true` if the pad is connected, and `false` otherwise. + * + * @param pad The pad to query. + * @return A boolean value indicating whether the pad is connected. + */ + bool isPadConnected(Pad pad) const { return (m_padData[pad][0] & 0xff) == 0; } + + /** + * @brief Returns the state of a button. + * + * @details Returns the state of a button. The state is a boolean value + * that is `true` if the button is pressed, and `false` otherwise. + * + * @param pad The pad to query. + * @param button The button to query. + * @return A boolean value indicating whether the button is pressed. + */ + bool isButtonPressed(Pad pad, Button button) const { return (m_padData[pad][1] & (1 << button)) == 0; } + + private: + void busyLoop(uint16_t delay) { + uint16_t cycles = 0; + while (++cycles < delay) { + __asm__ volatile(""); + } + }; + + void flushRxBuffer(); + constexpr uint8_t output_analog(uint8_t ticks); + constexpr uint8_t output_multitap(uint8_t ticks); + constexpr uint8_t output_default(uint8_t ticks); + void processChanges(Pad pad); + void readPad(); + uint8_t transceive(uint8_t data_out); + bool waitForAck(); // true if ack received, false if timeout + + // bool m_configMode[8] = {false, false, false, false, false, false, false, false}; + uint16_t m_padData[8][4]; + eastl::function m_callback; + bool m_connected[8] = {false, false}; + uint16_t m_buttons[8] = { + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + }; +}; + +} // namespace psyqo diff --git a/src/mips/psyqo/examples/multitap/Makefile b/src/mips/psyqo/examples/multitap/Makefile new file mode 100644 index 000000000..6ea9b0b9f --- /dev/null +++ b/src/mips/psyqo/examples/multitap/Makefile @@ -0,0 +1,12 @@ +TARGET = multitap +TYPE = ps-exe + +SRCS = \ +multitap.cpp \ + +ifeq ($(TEST),true) +CPPFLAGS = -Werror +endif +CXXFLAGS = -std=c++20 + +include ../../psyqo.mk diff --git a/src/mips/psyqo/examples/multitap/multitap.cpp b/src/mips/psyqo/examples/multitap/multitap.cpp new file mode 100644 index 000000000..42ef79b91 --- /dev/null +++ b/src/mips/psyqo/examples/multitap/multitap.cpp @@ -0,0 +1,150 @@ + +#include + +#include "common/syscalls/syscalls.h" +#include "psyqo/advancedpad.hh" +#include "psyqo/application.hh" +#include "psyqo/font.hh" +#include "psyqo/gpu.hh" +#include "psyqo/hardware/cpu.hh" +#include "psyqo/hardware/hwregs.hh" +#include "psyqo/hardware/sio.hh" +#include "psyqo/kernel.hh" +#include "psyqo/scene.hh" + +namespace { + +// Our application. The PadTest class will be created statically, and run from `main`. +class PadTest final : public psyqo::Application { + // We will need both methods to properly set up the application. + void prepare() override; + void createScene() override; + + public: + // We will store the font here. We're not going to use chained DMA to display + // anything, so we don't need multiple fragments. Only a single one will suffice. + psyqo::Font<1> m_font; + + // Our pad reader. + psyqo::AdvancedPad m_input; +}; + +// We only have a single since to display the status of the pads. +class PadTestScene final : public psyqo::Scene { + // Since there's only a single scene, we won't need to override the `start` + // or `teardown` methods. We will do all the initialization in the application. + int framecount = 1; + int padindex = 0; + + void frame() override; + + // Couple of small helpers. + void print(int x, int y, bool enabled, const char *text); + void printPadStatus(psyqo::AdvancedPad::Pad pad, int column, const char *name); + void printPadConnectionStatus(psyqo::AdvancedPad::Pad pad, int row, const char *name); +}; + +PadTest padTest; +PadTestScene padTestScene; + +} // namespace + +// The application's `prepare` method is the location to initialize and activate the GPU. +// We shouldn't do anything else that touches the hardware here however, because interrupts +// aren't initialized yet. We could also create more objects here, but we don't have any. +void PadTest::prepare() { + psyqo::GPU::Configuration config; + config.set(psyqo::GPU::Resolution::W320) + .set(psyqo::GPU::VideoMode::AUTO) + .set(psyqo::GPU::ColorMode::C15BITS) + .set(psyqo::GPU::Interlace::PROGRESSIVE); + gpu().initialize(config); +} + +// The `createScene` method will be called automatically to create the first scene. It will +// never exit, so there's no need to cater for the reentrancy case here, but technically, +// this method _can_ be called multiple times, if the last scene got popped out. This would +// be bad in this case because it'd mean we're initializing the pads, and uploading the system +// font multiple times. +void PadTest::createScene() { + // We don't have a specific font, so let's just use the built-in system one. + m_font.uploadSystemFont(gpu()); + // During `createScene`, interrupts are enabled, so it's okay to call `SimplePad::initialize`. + m_input.initialize(); + // And finally we call `pushScene` with the address of our one and only scene. This last + // call is mandatory for everything to function properly. + pushScene(&padTestScene); +} + +// Using the system font, our display is roughly 40 columns by 15 lines of text. Although the +// font system can draw text at arbitrary positions on the screen, it's a bit easier to consider +// a matrix of characters instead. +void PadTestScene::print(int x, int y, bool enabled, const char *text) { + y += 2; + psyqo::Vertex pos = {{.x = int16_t(x * 8), .y = int16_t(y * 16)}}; + // Doing these lazy initializations is great for readability and encapsulation, + // but due to the way C++ works, it'll call into the C++ guard functions every + // time the function gets called, which may be slower than it could if those + // were in fact globals. + static const auto WHITE = psyqo::Color{{.r = 255, .g = 255, .b = 255}}; + static const auto GRAY = psyqo::Color{{.r = 48, .g = 48, .b = 48}}; + psyqo::Color c = enabled ? WHITE : GRAY; + padTest.m_font.print(padTest.gpu(), text, pos, c); +} +void PadTestScene::printPadConnectionStatus(psyqo::AdvancedPad::Pad pad, int row, const char *name) { + auto &input = padTest.m_input; + print(8, row, input.isPadConnected(pad), name); +} + +void PadTestScene::printPadStatus(psyqo::AdvancedPad::Pad pad, int column, const char *name) { + auto &input = padTest.m_input; + print(column + 0, 0, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Start), "Start"); + print(column + 0, 1, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Select), "Select"); + + print(column + 0, 3, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::L1), "L1"); + print(column + 0, 4, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::R1), "R1"); + print(column + 0, 5, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::L2), "L2"); + print(column + 0, 6, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::R2), "R2"); + print(column + 0, 7, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::L3), "L3"); + print(column + 0, 8, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::R3), "R3"); + + print(column + 10, 0, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Up), "Up"); + print(column + 10, 1, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Down), "Down"); + print(column + 10, 2, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Left), "Left"); + print(column + 10, 3, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Right), "Right"); + + print(column + 10, 5, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Cross), "Cross"); + print(column + 10, 6, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Circle), "Circle"); + print(column + 10, 7, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Square), "Square"); + print(column + 10, 8, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Triangle), "Triangle"); +} + +// Our rendering function that'll be called periodically. +void PadTestScene::frame() { + padTest.gpu().clear(); + + if (framecount++ % (60 * 5) == 0 || + !padTest.m_input.isPadConnected(static_cast(padindex))) { + // Iterate through the pads, stop on next connected pad + while (!(padTest.m_input.isPadConnected(static_cast(++padindex))) && padindex < 8) + ; + if (padindex > 7) { + padindex = 0; + } + } + + print(7, padindex, true, ">"); + + printPadConnectionStatus(psyqo::AdvancedPad::Pad1a, 0, "Pad 1a"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad1b, 1, "Pad 1b"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad1c, 2, "Pad 1c"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad1d, 3, "Pad 1d"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad2a, 4, "Pad 2a"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad2b, 5, "Pad 2b"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad2c, 6, "Pad 2c"); + printPadConnectionStatus(psyqo::AdvancedPad::Pad2d, 7, "Pad 2d"); + + printPadStatus(static_cast(padindex), 20, "Pad Status"); +} + +int main() { return padTest.run(); } diff --git a/src/mips/psyqo/hardware/sio.hh b/src/mips/psyqo/hardware/sio.hh new file mode 100644 index 000000000..91fb51469 --- /dev/null +++ b/src/mips/psyqo/hardware/sio.hh @@ -0,0 +1,67 @@ +/* + +MIT License + +Copyright (c) 2023 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#pragma once + +#include + +#include "psyqo/hardware/hwregs.hh" + +namespace psyqo::Hardware::SIO { +enum Control : uint16_t { + CTRL_TXEN = (1 << 0), // Transmit Enable + CTRL_DTR = (1 << 1), // Data Terminal Ready, aka Select (output) + CTRL_RXE = (1 << 2), // Receive Enable + CTRL_SBRK = (1 << 3), // Send Break character + CTRL_ERRRES = (1 << 4), // Error Reset + CTRL_RTS = (1 << 5), // Request to Send (output) + CTRL_IR = (1 << 6), // Internal Reset, resets most SIO registers + CTRL_RXIRQMODE = (1 << 8), // Receive IRQ Mode (0..3 = IRQ when RX FIFO contains 1,2,4,8 bytes) + CTRL_TXIRQEN = (1 << 10), // Transmit IRQ Enable + CTRL_RXIRQEN = (1 << 11), // Receive IRQ Enable + CTRL_ACKIRQEN = (1 << 12), // Acknowledge IRQ Enable + CTRL_PORTSEL = (1 << 13), // Port Select +}; + +enum Status : uint32_t { + STAT_TXRDY = (1 << 0), // TX buffer is empty + STAT_RXRDY = (1 << 1), // RX buffer has data + STAT_TXEMPTY = (1 << 2), // No data in TX buffer + STAT_PE = (1 << 3), // Parity Error + STAT_OE = (1 << 4), // Overrun Error + STAT_FE = (1 << 5), // Framing Error + STAT_SYNDET = (1 << 6), // Sync Detect + STAT_ACK = (1 << 7), // ACK signal level (input) + STAT_CTS = (1 << 8), // Clear to Send (output), unused on SIO0 + STAT_IRQ = (1 << 9), // Interrupt Request +}; + +extern psyqo::Hardware::Register<0x0040, uint8_t, psyqo::Hardware::WriteQueue::Bypass> Data; +extern psyqo::Hardware::Register<0x0044, uint32_t, psyqo::Hardware::WriteQueue::Bypass> Stat; +extern psyqo::Hardware::Register<0x0048, uint16_t, psyqo::Hardware::WriteQueue::Bypass> Mode; +extern psyqo::Hardware::Register<0x004a, uint16_t, psyqo::Hardware::WriteQueue::Bypass> Ctrl; +extern psyqo::Hardware::Register<0x004e, uint16_t, psyqo::Hardware::WriteQueue::Bypass> Baud; +} // namespace psyqo::Hardware::SIO \ No newline at end of file diff --git a/src/mips/psyqo/src/advancedpad.cpp b/src/mips/psyqo/src/advancedpad.cpp new file mode 100644 index 000000000..48708c3f7 --- /dev/null +++ b/src/mips/psyqo/src/advancedpad.cpp @@ -0,0 +1,305 @@ +/* + +MIT License + +Copyright (c) 2023 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#include "psyqo/advancedpad.hh" + +#include "common/syscalls/syscalls.h" +#include "psyqo/hardware/cpu.hh" +#include "psyqo/hardware/sio.hh" +#include "psyqo/kernel.hh" + +using namespace psyqo::Hardware; + +void psyqo::AdvancedPad::initialize() { + // Stop the kernel from processing pad(and card) events + syscall_stopPad(); + + for (int i = 0; i < 8; i++) { + __builtin_memset(m_padData[i], 0xff, sizeof(m_padData[i])); + } + + // Init Pad + syscall_memset(m_padData[0], 0xff, sizeof(m_padData[0])); + + SIO::Ctrl = SIO::Control::CTRL_IR; + SIO::Baud = 0x88; // 250kHz + SIO::Mode = 0xd; // MUL1, 8bit, no parity, normal polarity + SIO::Ctrl = 0; + + using namespace timer_literals; + + Kernel::Internal::addOnFrame([this]() { + readPad(); + + if (!m_callback) return; + + processChanges(Pad1a); + processChanges(Pad1b); + processChanges(Pad1c); + processChanges(Pad1d); + processChanges(Pad2a); + processChanges(Pad2b); + processChanges(Pad2c); + processChanges(Pad2d); + }); +} + +inline void psyqo::AdvancedPad::flushRxBuffer() { + while (SIO::Stat & SIO::Status::STAT_RXRDY) { + SIO::Data; // throwaway read + } +} + +constexpr uint8_t psyqo::AdvancedPad::output_analog(uint8_t ticks) { + uint8_t data_out = 0xff; + + switch (ticks) {} + + return data_out; +} + +constexpr uint8_t psyqo::AdvancedPad::output_default(uint8_t ticks) { + uint8_t data_out = 0x00; + switch (ticks) { + case 0: + case 2: + data_out = 0x01; + break; + case 1: + data_out = 0x42; + break; + } + + return data_out; +} + +constexpr uint8_t psyqo::AdvancedPad::output_multitap(uint8_t ticks) { + uint8_t data_out = 0x00; + + switch (ticks) { + case 0: // Pad select + case 2: // Buffered transfer + data_out = 0x01; + break; + + case 1: // Initial read command + case 3: // Read pad A + case 11: // Read pad B + case 19: // Read pad C + case 27: // Read pad D + data_out = 0x42; + break; + } + + switch (ticks) { + case 2: // Burst speed for idhi + case 11: // Burst speed for Pads B-D + SIO::Baud = 0x22; // 1MHz + break; + + case 3: // Return to normal speed for Pad A + SIO::Baud = 0x88; // 250kHz + break; + } + + return data_out; +} + +void psyqo::AdvancedPad::processChanges(Pad pad) { + bool padConnected = isPadConnected(pad); + bool wasConnected = m_connected[pad]; + if (wasConnected && !padConnected) { + m_callback(Event{Event::PadDisconnected, pad}); + } else if (!wasConnected && padConnected) { + m_callback(Event{Event::PadConnected, pad}); + } + m_connected[pad] = padConnected; + + uint32_t mask = 1; + uint32_t padData = m_padData[pad][1]; + uint32_t buttons = m_buttons[pad]; + for (int i = 0; i < 16; i++, mask <<= 1) { + bool buttonPressed = (padData & mask) == 0; + bool wasButtonPressed = (buttons & mask) == 0; + if (buttonPressed && !wasButtonPressed) { + m_callback(Event{Event::ButtonPressed, pad, Button(i)}); + } else if (!buttonPressed && wasButtonPressed) { + m_callback(Event{Event::ButtonReleased, pad, Button(i)}); + } + } + m_buttons[pad] = padData; +} + +inline uint8_t psyqo::AdvancedPad::transceive(uint8_t data_out) { + SIO::Data = data_out; + + // Wait for transceive to complete and data to populate FIFO + while (!(SIO::Stat & SIO::Status::STAT_RXRDY)) + ; + + // Pull data from FIFO + return SIO::Data; +} + +void psyqo::AdvancedPad::readPad() { + uint8_t data_in, data_out; + uint8_t port_dev_type[2] = {PadType::None, PadType::None}; + + syscall_memset(m_padData, 0xff, sizeof(m_padData)); + + uint8_t *pad_data; + static constexpr unsigned pad_data_width = 8; + + for (uint16_t port = 0; port < 2; port++) { + // Select enable on current port + SIO::Ctrl = (port << 13) | SIO::Control::CTRL_DTR; + + // Set baud 250kHz + SIO::Baud = 0x88; + + flushRxBuffer(); + + // Enable transmit and IRQ on ACK + SIO::Ctrl |= (SIO::Control::CTRL_TXEN | SIO::Control::CTRL_ACKIRQEN); + + // Pads get finicky if we don't wait a bit here + busyLoop(100); + + pad_data = reinterpret_cast(&m_padData[port * 4][0]); + for (unsigned int ticks = 0, max_ticks = 5; ticks < max_ticks; ticks++) { + SIO::Ctrl |= SIO::Control::CTRL_ERRRES; // Clear error + CPU::IReg.clear(CPU::IRQ::Controller); // Clear IRQ + + if (port_dev_type[port] == PadType::Multitap) { + data_out = output_multitap(ticks); + } else { + data_out = output_default(ticks); + } + data_in = transceive(data_out); + + if (ticks == 2) { + if (data_in == 0x5a) { + // Set number of half-words to read + max_ticks = port_dev_type[port] & 0x0f; + if (!max_ticks) { + max_ticks = 0x10; + } + max_ticks = (max_ticks * 2) + ticks + 1; + + pad_data[0] = 0; + } else { + // Derp? Unknown device type or bad data, stop reading + max_ticks = ticks; + pad_data[0] = 0xff; + } + } + + if (port_dev_type[port] == PadType::Multitap) { + unsigned pad_index = ticks >= 11 ? (ticks - 3) / 8 : 0; + + switch (ticks) { + // Unreachable + // case 0: + // case 1: + // break; + + case 2: + // Discard data + break; + + case 4: // Pad A + case 12: // Pad B + case 20: // Pad C + case 28: // Pad D + // 0 = connected + pad_data[(pad_data_width * pad_index) + 0] = !(data_in == 0x5a); + break; + + case 3: // Pad A idlo + case 11: // Pad B idlo + case 19: // Pad C idlo + case 27: // Pad D idlo + pad_data[(pad_data_width * pad_index) + 1] = data_in; + break; + + default: + pad_data[(pad_data_width * pad_index) + ((ticks - 3) % 8)] = data_in; + } + } else { + switch (ticks) { + case 0: // Discard data + break; + + case 1: + port_dev_type[port] = data_in; + if (data_in != PadType::Multitap) { + pad_data[1] = data_in; + } + break; + + case 2: + pad_data[0] = !(data_in == 0x5a); + break; + + default: + pad_data[ticks - 1] = data_in; + } + } + + if (ticks < (max_ticks - 1)) { + if (!waitForAck()) { + // Timeout waiting for ACK + port_dev_type[port] = PadType::None; + for (int pad_index = 0; pad_index < 4; pad_index++) { + pad_data[(pad_data_width * pad_index)] = 0x01; + } + break; + } + + while (SIO::Stat & SIO::Status::STAT_ACK) + ; // Wait for ACK to return to high + } + } // tick loop + + // End transmission + SIO::Ctrl = 0; + } // port loop +} + +inline bool psyqo::AdvancedPad::waitForAck() { + int cyclesWaited = 0; + static constexpr int max_ack_wait = 0x137; // ~105us + + while (!(CPU::IReg & (static_cast(CPU::IRQ::Controller))) && ++cyclesWaited < max_ack_wait) + ; + + if (cyclesWaited >= max_ack_wait) { + // Timeout waiting for ACK + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/mips/psyqo/src/hardware/sio.cpp b/src/mips/psyqo/src/hardware/sio.cpp new file mode 100644 index 000000000..97f1b6935 --- /dev/null +++ b/src/mips/psyqo/src/hardware/sio.cpp @@ -0,0 +1,33 @@ +/* + +MIT License + +Copyright (c) 2023 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#include "psyqo/hardware/sio.hh" + +psyqo::Hardware::Register<0x0040, uint8_t, psyqo::Hardware::WriteQueue::Bypass> psyqo::Hardware::SIO::Data; +psyqo::Hardware::Register<0x0044, uint32_t, psyqo::Hardware::WriteQueue::Bypass> psyqo::Hardware::SIO::Stat; +psyqo::Hardware::Register<0x0048, uint16_t, psyqo::Hardware::WriteQueue::Bypass> psyqo::Hardware::SIO::Mode; +psyqo::Hardware::Register<0x004a, uint16_t, psyqo::Hardware::WriteQueue::Bypass> psyqo::Hardware::SIO::Ctrl; +psyqo::Hardware::Register<0x004e, uint16_t, psyqo::Hardware::WriteQueue::Bypass> psyqo::Hardware::SIO::Baud; \ No newline at end of file From c9ed28e6db694c5f8817b59cd8bdfc78b42927c7 Mon Sep 17 00:00:00 2001 From: John Baumann Date: Sun, 10 Mar 2024 10:06:18 -0500 Subject: [PATCH 2/5] Address most of the current review concerns --- src/mips/psyqo/advancedpad.hh | 22 ++++------- src/mips/psyqo/examples/multitap/multitap.cpp | 29 +++++++++++++- src/mips/psyqo/hardware/sio.hh | 2 +- src/mips/psyqo/src/advancedpad.cpp | 38 ++++++------------- src/mips/psyqo/src/hardware/sio.cpp | 2 +- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/mips/psyqo/advancedpad.hh b/src/mips/psyqo/advancedpad.hh index fc95097f3..ff25509eb 100644 --- a/src/mips/psyqo/advancedpad.hh +++ b/src/mips/psyqo/advancedpad.hh @@ -2,7 +2,7 @@ MIT License -Copyright (c) 2023 PCSX-Redux authors +Copyright (c) 2024 PCSX-Redux authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -39,8 +39,7 @@ namespace psyqo { * @details This class is meant to be used as a singleton, probably in * the `Application` derived class. It does not use the BIOS' * PAD interface. Instead, it uses the SIO interface directly, and - * can therefore support more device types including multitaps. Polling - * alternates between the two pads/ports on each frame. + * can therefore support more device types including multitaps. */ class AdvancedPad { @@ -79,9 +78,6 @@ class AdvancedPad { GetSupportedModes = 0x4C, // 'L' Allegedly get supported modes ConfigRequestFormat = 0x4D, // 'M' Allegedly configure poll request format ConfigResponseFormat = 0x4F, // 'O' Allegedly configure poll response format - - // Config mode commands - }; enum PadType : uint8_t { @@ -165,26 +161,24 @@ class AdvancedPad { bool isButtonPressed(Pad pad, Button button) const { return (m_padData[pad][1] & (1 << button)) == 0; } private: - void busyLoop(uint16_t delay) { - uint16_t cycles = 0; + void busyLoop(unsigned delay) { + unsigned cycles = 0; while (++cycles < delay) { - __asm__ volatile(""); + asm(""); } }; void flushRxBuffer(); - constexpr uint8_t output_analog(uint8_t ticks); - constexpr uint8_t output_multitap(uint8_t ticks); - constexpr uint8_t output_default(uint8_t ticks); + constexpr uint8_t outputDefault(unsigned ticks); + constexpr uint8_t outputMultitap(unsigned ticks); void processChanges(Pad pad); void readPad(); uint8_t transceive(uint8_t data_out); bool waitForAck(); // true if ack received, false if timeout - // bool m_configMode[8] = {false, false, false, false, false, false, false, false}; uint16_t m_padData[8][4]; eastl::function m_callback; - bool m_connected[8] = {false, false}; + bool m_connected[8] = {false, false, false, false, false, false}; uint16_t m_buttons[8] = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, }; diff --git a/src/mips/psyqo/examples/multitap/multitap.cpp b/src/mips/psyqo/examples/multitap/multitap.cpp index 42ef79b91..47ee09dcb 100644 --- a/src/mips/psyqo/examples/multitap/multitap.cpp +++ b/src/mips/psyqo/examples/multitap/multitap.cpp @@ -1,3 +1,28 @@ +/* + +MIT License + +Copyright (c) 2024 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ #include @@ -33,8 +58,8 @@ class PadTest final : public psyqo::Application { class PadTestScene final : public psyqo::Scene { // Since there's only a single scene, we won't need to override the `start` // or `teardown` methods. We will do all the initialization in the application. - int framecount = 1; - int padindex = 0; + unsigned framecount = 1; + unsigned padindex = 0; void frame() override; diff --git a/src/mips/psyqo/hardware/sio.hh b/src/mips/psyqo/hardware/sio.hh index 91fb51469..138f99659 100644 --- a/src/mips/psyqo/hardware/sio.hh +++ b/src/mips/psyqo/hardware/sio.hh @@ -2,7 +2,7 @@ MIT License -Copyright (c) 2023 PCSX-Redux authors +Copyright (c) 2024 PCSX-Redux authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/mips/psyqo/src/advancedpad.cpp b/src/mips/psyqo/src/advancedpad.cpp index 48708c3f7..2128b1e9d 100644 --- a/src/mips/psyqo/src/advancedpad.cpp +++ b/src/mips/psyqo/src/advancedpad.cpp @@ -2,7 +2,7 @@ MIT License -Copyright (c) 2023 PCSX-Redux authors +Copyright (c) 2024 PCSX-Redux authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,20 +37,14 @@ void psyqo::AdvancedPad::initialize() { // Stop the kernel from processing pad(and card) events syscall_stopPad(); - for (int i = 0; i < 8; i++) { - __builtin_memset(m_padData[i], 0xff, sizeof(m_padData[i])); - } - - // Init Pad - syscall_memset(m_padData[0], 0xff, sizeof(m_padData[0])); + // Init Pads + __builtin_memset(m_padData, 0xff, sizeof(m_padData)); SIO::Ctrl = SIO::Control::CTRL_IR; SIO::Baud = 0x88; // 250kHz SIO::Mode = 0xd; // MUL1, 8bit, no parity, normal polarity SIO::Ctrl = 0; - using namespace timer_literals; - Kernel::Internal::addOnFrame([this]() { readPad(); @@ -69,19 +63,11 @@ void psyqo::AdvancedPad::initialize() { inline void psyqo::AdvancedPad::flushRxBuffer() { while (SIO::Stat & SIO::Status::STAT_RXRDY) { - SIO::Data; // throwaway read + SIO::Data.throwAway(); // throwaway read } } -constexpr uint8_t psyqo::AdvancedPad::output_analog(uint8_t ticks) { - uint8_t data_out = 0xff; - - switch (ticks) {} - - return data_out; -} - -constexpr uint8_t psyqo::AdvancedPad::output_default(uint8_t ticks) { +constexpr uint8_t psyqo::AdvancedPad::outputDefault(unsigned ticks) { uint8_t data_out = 0x00; switch (ticks) { case 0: @@ -96,7 +82,7 @@ constexpr uint8_t psyqo::AdvancedPad::output_default(uint8_t ticks) { return data_out; } -constexpr uint8_t psyqo::AdvancedPad::output_multitap(uint8_t ticks) { +constexpr uint8_t psyqo::AdvancedPad::outputMultitap(unsigned ticks) { uint8_t data_out = 0x00; switch (ticks) { @@ -168,14 +154,14 @@ void psyqo::AdvancedPad::readPad() { uint8_t data_in, data_out; uint8_t port_dev_type[2] = {PadType::None, PadType::None}; - syscall_memset(m_padData, 0xff, sizeof(m_padData)); + __builtin_memset(m_padData, 0xff, sizeof(m_padData)); uint8_t *pad_data; static constexpr unsigned pad_data_width = 8; - for (uint16_t port = 0; port < 2; port++) { + for (unsigned port = 0; port < 2; port++) { // Select enable on current port - SIO::Ctrl = (port << 13) | SIO::Control::CTRL_DTR; + SIO::Ctrl = (static_cast(port) << 13) | SIO::Control::CTRL_DTR; // Set baud 250kHz SIO::Baud = 0x88; @@ -189,14 +175,14 @@ void psyqo::AdvancedPad::readPad() { busyLoop(100); pad_data = reinterpret_cast(&m_padData[port * 4][0]); - for (unsigned int ticks = 0, max_ticks = 5; ticks < max_ticks; ticks++) { + for (unsigned ticks = 0, max_ticks = 5; ticks < max_ticks; ticks++) { SIO::Ctrl |= SIO::Control::CTRL_ERRRES; // Clear error CPU::IReg.clear(CPU::IRQ::Controller); // Clear IRQ if (port_dev_type[port] == PadType::Multitap) { - data_out = output_multitap(ticks); + data_out = outputMultitap(ticks); } else { - data_out = output_default(ticks); + data_out = outputDefault(ticks); } data_in = transceive(data_out); diff --git a/src/mips/psyqo/src/hardware/sio.cpp b/src/mips/psyqo/src/hardware/sio.cpp index 97f1b6935..9c3165680 100644 --- a/src/mips/psyqo/src/hardware/sio.cpp +++ b/src/mips/psyqo/src/hardware/sio.cpp @@ -2,7 +2,7 @@ MIT License -Copyright (c) 2023 PCSX-Redux authors +Copyright (c) 2024 PCSX-Redux authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 7115bb967b7ac1a12b8bb8f61220812e703cd668 Mon Sep 17 00:00:00 2001 From: John Baumann Date: Sun, 10 Mar 2024 15:50:37 -0500 Subject: [PATCH 3/5] Tidy tap example, timer, and op overload --- src/mips/psyqo/advancedpad.hh | 63 ++++++++++++++++++- src/mips/psyqo/examples/multitap/multitap.cpp | 44 ++++++++----- 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/src/mips/psyqo/advancedpad.hh b/src/mips/psyqo/advancedpad.hh index ff25509eb..5fd505f6c 100644 --- a/src/mips/psyqo/advancedpad.hh +++ b/src/mips/psyqo/advancedpad.hh @@ -135,7 +135,7 @@ class AdvancedPad { * might call into `setOnEvent` as a result, and could end up corrupting * memory as a result of the callback being deleted while being executed. */ - void setOnEvent(eastl::function &&callback) { m_callback = eastl::move(callback); } + void setOnEvent(eastl::function&& callback) { m_callback = eastl::move(callback); } /** * @brief Returns the state of a pad. @@ -184,4 +184,65 @@ class AdvancedPad { }; }; +// prefix increment operator +inline psyqo::AdvancedPad::Pad& operator++(psyqo::AdvancedPad::Pad& pad) { + switch (pad) { + case psyqo::AdvancedPad::Pad::Pad1a: + return pad = psyqo::AdvancedPad::Pad::Pad1b; + case psyqo::AdvancedPad::Pad::Pad1b: + return pad = psyqo::AdvancedPad::Pad::Pad1c; + case psyqo::AdvancedPad::Pad::Pad1c: + return pad = psyqo::AdvancedPad::Pad::Pad1d; + case psyqo::AdvancedPad::Pad::Pad1d: + return pad = psyqo::AdvancedPad::Pad::Pad2a; + case psyqo::AdvancedPad::Pad::Pad2a: + return pad = psyqo::AdvancedPad::Pad::Pad2b; + case psyqo::AdvancedPad::Pad::Pad2b: + return pad = psyqo::AdvancedPad::Pad::Pad2c; + case psyqo::AdvancedPad::Pad::Pad2c: + return pad = psyqo::AdvancedPad::Pad::Pad2d; + case psyqo::AdvancedPad::Pad::Pad2d: + return pad = psyqo::AdvancedPad::Pad::Pad1a; + + default: + return pad = psyqo::AdvancedPad::Pad::Pad1a; + } +} + +// postfix increment operator +inline psyqo::AdvancedPad::Pad operator++(psyqo::AdvancedPad::Pad& pad, int) { + psyqo::AdvancedPad::Pad copy(pad); + switch (pad) { + case psyqo::AdvancedPad::Pad::Pad1a: + pad = psyqo::AdvancedPad::Pad::Pad1b; + break; + case psyqo::AdvancedPad::Pad::Pad1b: + pad = psyqo::AdvancedPad::Pad::Pad1c; + break; + case psyqo::AdvancedPad::Pad::Pad1c: + pad = psyqo::AdvancedPad::Pad::Pad1d; + break; + case psyqo::AdvancedPad::Pad::Pad1d: + pad = psyqo::AdvancedPad::Pad::Pad2a; + break; + case psyqo::AdvancedPad::Pad::Pad2a: + pad = psyqo::AdvancedPad::Pad::Pad2b; + break; + case psyqo::AdvancedPad::Pad::Pad2b: + pad = psyqo::AdvancedPad::Pad::Pad2c; + break; + case psyqo::AdvancedPad::Pad::Pad2c: + pad = psyqo::AdvancedPad::Pad::Pad2d; + break; + case psyqo::AdvancedPad::Pad::Pad2d: + pad = psyqo::AdvancedPad::Pad::Pad1a; + break; + + default: + pad = psyqo::AdvancedPad::Pad::Pad1a; + } + + return copy; +} + } // namespace psyqo diff --git a/src/mips/psyqo/examples/multitap/multitap.cpp b/src/mips/psyqo/examples/multitap/multitap.cpp index 47ee09dcb..f715d3cf3 100644 --- a/src/mips/psyqo/examples/multitap/multitap.cpp +++ b/src/mips/psyqo/examples/multitap/multitap.cpp @@ -56,14 +56,14 @@ class PadTest final : public psyqo::Application { // We only have a single since to display the status of the pads. class PadTestScene final : public psyqo::Scene { - // Since there's only a single scene, we won't need to override the `start` - // or `teardown` methods. We will do all the initialization in the application. - unsigned framecount = 1; - unsigned padindex = 0; + psyqo::AdvancedPad::Pad m_padIndex = psyqo::AdvancedPad::Pad::Pad1a; + uintptr_t m_timerId; void frame() override; + void start(Scene::StartReason reason) override; // Couple of small helpers. + void nextPad(); void print(int x, int y, bool enabled, const char *text); void printPadStatus(psyqo::AdvancedPad::Pad pad, int column, const char *name); void printPadConnectionStatus(psyqo::AdvancedPad::Pad pad, int row, const char *name); @@ -94,13 +94,25 @@ void PadTest::prepare() { void PadTest::createScene() { // We don't have a specific font, so let's just use the built-in system one. m_font.uploadSystemFont(gpu()); - // During `createScene`, interrupts are enabled, so it's okay to call `SimplePad::initialize`. + // During `createScene`, interrupts are enabled, so it's okay to call `AdvancedPad::initialize`. m_input.initialize(); // And finally we call `pushScene` with the address of our one and only scene. This last // call is mandatory for everything to function properly. pushScene(&padTestScene); } +// Cycle through the pads to find the next one connected +void PadTestScene::nextPad() { + for (unsigned i = 0; i < 8; i++) { + if (padTest.m_input.isPadConnected(++m_padIndex)) { + return; // Found a connected pad + } + } + + // No connected pad found, reset index to first pad + m_padIndex = psyqo::AdvancedPad::Pad::Pad1a; +} + // Using the system font, our display is roughly 40 columns by 15 lines of text. Although the // font system can draw text at arbitrary positions on the screen, it's a bit easier to consider // a matrix of characters instead. @@ -144,22 +156,22 @@ void PadTestScene::printPadStatus(psyqo::AdvancedPad::Pad pad, int column, const print(column + 10, 8, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Triangle), "Triangle"); } +void PadTestScene::start(Scene::StartReason reason) { + if (reason == Scene::StartReason::Create) { + // If we are getting created, create a 5 second periodic timer. + using namespace psyqo::timer_literals; + m_timerId = padTest.gpu().armPeriodicTimer(5_s, [this](auto) { nextPad(); }); + } +} + // Our rendering function that'll be called periodically. void PadTestScene::frame() { padTest.gpu().clear(); - if (framecount++ % (60 * 5) == 0 || - !padTest.m_input.isPadConnected(static_cast(padindex))) { - // Iterate through the pads, stop on next connected pad - while (!(padTest.m_input.isPadConnected(static_cast(++padindex))) && padindex < 8) - ; - if (padindex > 7) { - padindex = 0; - } + if (padTest.m_input.isPadConnected(m_padIndex)) { + print(7, m_padIndex, true, ">"); } - print(7, padindex, true, ">"); - printPadConnectionStatus(psyqo::AdvancedPad::Pad1a, 0, "Pad 1a"); printPadConnectionStatus(psyqo::AdvancedPad::Pad1b, 1, "Pad 1b"); printPadConnectionStatus(psyqo::AdvancedPad::Pad1c, 2, "Pad 1c"); @@ -169,7 +181,7 @@ void PadTestScene::frame() { printPadConnectionStatus(psyqo::AdvancedPad::Pad2c, 6, "Pad 2c"); printPadConnectionStatus(psyqo::AdvancedPad::Pad2d, 7, "Pad 2d"); - printPadStatus(static_cast(padindex), 20, "Pad Status"); + printPadStatus(static_cast(m_padIndex), 20, "Pad Status"); } int main() { return padTest.run(); } From 34cffd08ac0dae4da5bde62c8fdcd64046fe5d6e Mon Sep 17 00:00:00 2001 From: John Baumann Date: Mon, 11 Mar 2024 18:54:22 -0500 Subject: [PATCH 4/5] Simplify inline op overload --- src/mips/psyqo/advancedpad.hh | 63 ++++++++--------------------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/mips/psyqo/advancedpad.hh b/src/mips/psyqo/advancedpad.hh index 5fd505f6c..15e3e8352 100644 --- a/src/mips/psyqo/advancedpad.hh +++ b/src/mips/psyqo/advancedpad.hh @@ -186,62 +186,25 @@ class AdvancedPad { // prefix increment operator inline psyqo::AdvancedPad::Pad& operator++(psyqo::AdvancedPad::Pad& pad) { - switch (pad) { - case psyqo::AdvancedPad::Pad::Pad1a: - return pad = psyqo::AdvancedPad::Pad::Pad1b; - case psyqo::AdvancedPad::Pad::Pad1b: - return pad = psyqo::AdvancedPad::Pad::Pad1c; - case psyqo::AdvancedPad::Pad::Pad1c: - return pad = psyqo::AdvancedPad::Pad::Pad1d; - case psyqo::AdvancedPad::Pad::Pad1d: - return pad = psyqo::AdvancedPad::Pad::Pad2a; - case psyqo::AdvancedPad::Pad::Pad2a: - return pad = psyqo::AdvancedPad::Pad::Pad2b; - case psyqo::AdvancedPad::Pad::Pad2b: - return pad = psyqo::AdvancedPad::Pad::Pad2c; - case psyqo::AdvancedPad::Pad::Pad2c: - return pad = psyqo::AdvancedPad::Pad::Pad2d; - case psyqo::AdvancedPad::Pad::Pad2d: - return pad = psyqo::AdvancedPad::Pad::Pad1a; - - default: - return pad = psyqo::AdvancedPad::Pad::Pad1a; - } + return pad = static_cast((static_cast(pad) + 1) & 7); } // postfix increment operator inline psyqo::AdvancedPad::Pad operator++(psyqo::AdvancedPad::Pad& pad, int) { psyqo::AdvancedPad::Pad copy(pad); - switch (pad) { - case psyqo::AdvancedPad::Pad::Pad1a: - pad = psyqo::AdvancedPad::Pad::Pad1b; - break; - case psyqo::AdvancedPad::Pad::Pad1b: - pad = psyqo::AdvancedPad::Pad::Pad1c; - break; - case psyqo::AdvancedPad::Pad::Pad1c: - pad = psyqo::AdvancedPad::Pad::Pad1d; - break; - case psyqo::AdvancedPad::Pad::Pad1d: - pad = psyqo::AdvancedPad::Pad::Pad2a; - break; - case psyqo::AdvancedPad::Pad::Pad2a: - pad = psyqo::AdvancedPad::Pad::Pad2b; - break; - case psyqo::AdvancedPad::Pad::Pad2b: - pad = psyqo::AdvancedPad::Pad::Pad2c; - break; - case psyqo::AdvancedPad::Pad::Pad2c: - pad = psyqo::AdvancedPad::Pad::Pad2d; - break; - case psyqo::AdvancedPad::Pad::Pad2d: - pad = psyqo::AdvancedPad::Pad::Pad1a; - break; - - default: - pad = psyqo::AdvancedPad::Pad::Pad1a; - } + pad = static_cast((static_cast(pad) + 1) & 7); + return copy; +} +// prefix decrement operator +inline psyqo::AdvancedPad::Pad& operator--(psyqo::AdvancedPad::Pad& pad) { + return pad = static_cast((static_cast(pad) - 1) & 7); +} + +// postfix decrement operator +inline psyqo::AdvancedPad::Pad operator--(psyqo::AdvancedPad::Pad& pad, int) { + psyqo::AdvancedPad::Pad copy(pad); + pad = static_cast((static_cast(pad) - 1) & 7); return copy; } From 6b49b304a16359d9b43db179c49aa9f3255b6b9a Mon Sep 17 00:00:00 2001 From: John Baumann Date: Mon, 13 May 2024 20:28:33 -0500 Subject: [PATCH 5/5] Fix a few derps --- src/mips/psyqo/advancedpad.hh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/mips/psyqo/advancedpad.hh b/src/mips/psyqo/advancedpad.hh index 15e3e8352..f3c29c79d 100644 --- a/src/mips/psyqo/advancedpad.hh +++ b/src/mips/psyqo/advancedpad.hh @@ -108,12 +108,8 @@ class AdvancedPad { /** * @brief Initializes the pads. * - * @details This will initialize the pads polling by calling the BIOS' - * interface. This means this method cannot be called from the `prepare` - * method of the `Application` class, but rather from the `start` method - * of the root `Scene` object. Also, there can be interference with - * the BIOS' memory card functions, so this method is explicit to be - * called in the right order. + * @details This will stop the BIOS driver and start psyqo's driver. + * This method should be called once at the beginning of the program. */ void initialize(); @@ -178,7 +174,7 @@ class AdvancedPad { uint16_t m_padData[8][4]; eastl::function m_callback; - bool m_connected[8] = {false, false, false, false, false, false}; + bool m_connected[8] = {false, false, false, false, false, false, false, false}; uint16_t m_buttons[8] = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, };