Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

psyqo Multi-tap driver and example update #1815

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
100 changes: 81 additions & 19 deletions src/mips/psyqo/advancedpad.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

MIT License

Copyright (c) 2024 PCSX-Redux authors
Copyright (c) 2025 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
Expand Down Expand Up @@ -42,7 +42,7 @@ namespace psyqo {

class AdvancedPad {
public:
enum Pad { Pad1a, Pad1b, Pad1c, Pad1d, Pad2a, Pad2b, Pad2c, Pad2d };
enum class Pad : unsigned { Pad1a, Pad1b, Pad1c, Pad1d, Pad2a, Pad2b, Pad2c, Pad2d };

enum Button {
Select = 0,
Expand All @@ -63,21 +63,6 @@ class AdvancedPad {
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
};

enum PadType : uint8_t {
Mouse = 0x12, // (two button mouse)
NegCon = 0x23, // (steering twist/wheel/paddle)
Expand Down Expand Up @@ -143,7 +128,7 @@ class AdvancedPad {
* @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; }
bool isPadConnected(Pad pad) const { return (m_padData[static_cast<unsigned>(pad)][0] & 0xff) == 0; }

/**
* @brief Returns the state of a button.
Expand All @@ -155,9 +140,86 @@ class AdvancedPad {
* @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; }
bool isButtonPressed(Pad pad, Button button) const {
return (m_padData[static_cast<unsigned>(pad)][1] & (1 << button)) == 0;
}

/**
* @brief Returns the state of an Analog Input.
*
* @details See the specific Analog Input functions for details.
* Indices greater than 3 will return 0.
* index 0: For analog pads: RightJoyX (00h=Left, 80h=Center, FFh=Right), mouse: X-axis
* index 1: For analog pads: RightJoyY (00h=Up, 80h=Center, FFh=Down), mouse: Y-axis
* index 2: For analog pads: LeftJoyX (00h=Left, 80h=Center, FFh=Right)
* index 3: For analog pads: LeftJoyY (00h=Up, 80h=Center, FFh=Down)
*
* @param pad The pad to query.
* @param index The index of the Analog Input(adc#).
* @return The state of the Analog Input as an unsigned 8-bit value(0-255).
*/
uint8_t getAdc(Pad pad, unsigned int index) const {
switch (index) {
case 0:
return m_padData[static_cast<unsigned>(pad)][2] & 0xff;
case 1:
return m_padData[static_cast<unsigned>(pad)][2] >> 8;
case 2:
return m_padData[static_cast<unsigned>(pad)][3] & 0xff;
case 3:
return m_padData[static_cast<unsigned>(pad)][3] >> 8;
default:
return 0;
}
}

/**
* @brief Returns raw pad data as an unsigned 16-bit value.
*
* @details A low level call which returns the halfword value for the requested index of the given pad index.
* It is recommended to use the higher level functions instead.
* index 0: pad type << 8 | connected(0 = connected, ffh = disconnected)
* index 1: button state
* index 2: analog input 1 << 8 | analog input 0
* index 3: analog input 3 << 8 | analog input 2
* The index is modulo 4, so it will wrap around if it is greater than 3.
*
*
* @param pad The pad to query.
* @param index The index of the halfword.
* @return The value of the halfword.
*/

uint16_t getHalfword(Pad pad, unsigned int index) const { return m_padData[static_cast<unsigned>(pad)][index % 4]; }

/**
* @brief Returns the type of the pad.
*
* @details Known pad types are defined in the PadType enum, returns 0xff if no pad is connected.
* PadType::Multitap is for internal use only, and should not be returned.
* Pad connection status should be checked with isPadConnected.
*
* @param pad The pad to query.
* @return The type of the pad.
*/
uint8_t getPadType(Pad pad) const { return m_padData[static_cast<unsigned>(pad)][0] >> 8; }

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

void busyLoop(unsigned delay) {
unsigned cycles = 0;
while (++cycles < delay) asm("");
Expand Down
162 changes: 112 additions & 50 deletions src/mips/psyqo/examples/multitap/multitap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

MIT License

Copyright (c) 2024 PCSX-Redux authors
Copyright (c) 2025 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
Expand Down Expand Up @@ -32,6 +32,7 @@
#include "psyqo/gpu.hh"
#include "psyqo/kernel.hh"
#include "psyqo/scene.hh"
#include "psyqo/xprintf.h"

namespace {

Expand All @@ -44,7 +45,7 @@
void createScene() override;

public:
psyqo::Font<1> m_font;
psyqo::Font<> m_font;
psyqo::AdvancedPad m_input;
};

Expand All @@ -57,9 +58,14 @@

// 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);
void print(int x, int y, bool enabled, const char *format, ...);
void printPadList(int column);
void printPadStatus(psyqo::AdvancedPad::Pad pad, int column);
void printPadType(psyqo::AdvancedPad::Pad pad, int column, const char *name);

const char *c_padNames[8] = {"1a", "1b", "1c", "1d", "2a", "2b", "2c", "2d"};
const psyqo::Color c_colorWhite = {{.r = 255, .g = 255, .b = 255}};
const psyqo::Color c_colorGray = {{.r = 48, .g = 48, .b = 48}};
};

MultitapTest multitapTest;
Expand Down Expand Up @@ -96,41 +102,108 @@
m_padIndex = psyqo::AdvancedPad::Pad::Pad1a;
}

void MultitapTestScene::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)}};
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;
multitapTest.m_font.print(multitapTest.gpu(), text, pos, c);
void MultitapTestScene::print(int x, int y, bool enabled, const char *format, ...) {
va_list args;
const psyqo::Vertex pos = {{.x = int16_t(x * 8), .y = int16_t(y * 16)}};
const psyqo::Color c = enabled ? c_colorWhite : c_colorGray;

va_start(args, format);
multitapTest.m_font.vprintf(multitapTest.gpu(), pos, c, format, args);
va_end(args);
}

void MultitapTestScene::printPadConnectionStatus(psyqo::AdvancedPad::Pad pad, int row, const char *name) {
auto &input = multitapTest.m_input;
print(8, row, input.isPadConnected(pad), name);
void MultitapTestScene::printPadList(int column) {
// Print pad names and selected pad indicator
for (int i = 0; i < 8; i++) {
const auto pad = static_cast<psyqo::AdvancedPad::Pad>(i);
const bool isConnected = multitapTest.m_input.isPadConnected(pad);
const char padIndicator = (m_padIndex == pad && isConnected) ? '>' : ' ';
print(column, i + 2, isConnected, "%cPad %s", padIndicator, c_padNames[i]);
}
}

void MultitapTestScene::printPadStatus(psyqo::AdvancedPad::Pad pad, int column, const char *name) {
auto &input = multitapTest.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");
void MultitapTestScene::printPadStatus(psyqo::AdvancedPad::Pad pad, int column) {
const auto &input = multitapTest.m_input;
print(column + 0, 2, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Start), "Start");
print(column + 0, 3, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Select), "Select");

print(column + 0, 5, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::L1), "L1");
print(column + 0, 6, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::R1), "R1");
print(column + 0, 7, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::L2), "L2");
print(column + 0, 8, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::R2), "R2");
print(column + 0, 9, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::L3), "L3");
print(column + 0, 10, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::R3), "R3");

print(column + 10, 2, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Up), "Up");
print(column + 10, 3, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Down), "Down");
print(column + 10, 4, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Left), "Left");
print(column + 10, 5, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Right), "Right");

print(column + 10, 7, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Cross), "Cross");
print(column + 10, 8, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Circle), "Circle");
print(column + 10, 9, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Square), "Square");
print(column + 10, 10, input.isButtonPressed(pad, psyqo::AdvancedPad::Button::Triangle), "Triangle");

const auto padType = input.getPadType(pad);

// The lower 4-bits of the pad type indicate the number of half-words of pad data
// The 1st half-word is for the digital switches
const auto halfWords = padType & 0xf;
const auto adcBytes = (halfWords - 1) * 2;

if (halfWords > 1 && padType != psyqo::AdvancedPad::PadType::None) {
print(column + 0, 11, false, "ADC[0-%d]", adcBytes - 1);

for (int i = 0; i < adcBytes && i < 4; i++) {
print(column + 10 + (i * 3), 11, true, "%02X ", input.getAdc(pad, i));
}
}
}

void MultitapTestScene::printPadType(psyqo::AdvancedPad::Pad pad, int column, const char *name) {
const auto padType = multitapTest.m_input.getPadType(pad);
const char *padTypeStr;

switch (padType) {
case psyqo::AdvancedPad::PadType::Mouse:
padTypeStr = "Mouse";
break;
case psyqo::AdvancedPad::PadType::NegCon:
padTypeStr = "NegCon";
break;
case psyqo::AdvancedPad::PadType::KonamiLightgun:
padTypeStr = "KonamiLightgun";
break;
case psyqo::AdvancedPad::PadType::DigitalPad:
padTypeStr = "DigitalPad";
break;
case psyqo::AdvancedPad::PadType::AnalogStick:
padTypeStr = "AnalogStick";
break;
case psyqo::AdvancedPad::PadType::NamcoLightgun:
padTypeStr = "NamcoLightgun";
break;
case psyqo::AdvancedPad::PadType::AnalogPad:
padTypeStr = "AnalogPad";
break;
case psyqo::AdvancedPad::PadType::Multitap:
padTypeStr = "Multitap";
break;
case psyqo::AdvancedPad::PadType::Jogcon:
padTypeStr = "Jogcon";
break;
case psyqo::AdvancedPad::PadType::ConfigMode:
padTypeStr = "ConfigMode";
break;
case psyqo::AdvancedPad::PadType::None:
padTypeStr = "None";
break;
default:
padTypeStr = "Unknown";
break;
}
print(column + 0, 13, true, name);
print(column + 10, 13, true, padTypeStr);

Check warning on line 206 in src/mips/psyqo/examples/multitap/multitap.cpp

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Method

MultitapTestScene::printPadType has a cyclomatic complexity of 13, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
}

void MultitapTestScene::start(Scene::StartReason reason) {
Expand All @@ -144,20 +217,9 @@
void MultitapTestScene::frame() {
multitapTest.gpu().clear();

if (multitapTest.m_input.isPadConnected(m_padIndex)) {
print(7, m_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<psyqo::AdvancedPad::Pad>(m_padIndex), 20, "Pad Status");
printPadList(1);
printPadStatus(static_cast<psyqo::AdvancedPad::Pad>(m_padIndex), 16);
printPadType(static_cast<psyqo::AdvancedPad::Pad>(m_padIndex), 16, "Type");
}

int main() { return multitapTest.run(); }
Loading
Loading