351 lines
15 KiB
Markdown
351 lines
15 KiB
Markdown
В PlayStation 1 (PS1) взаимодействие с контроллерами (геймпадами, мышками, световыми пистолетами и т.д.) осуществляется через **порты ввода-вывода (I/O)** и специализированные **контроллеры SIO (Serial Input/Output)**. Вот детальное объяснение процесса:
|
||
|
||
---
|
||
|
||
## **1. Аппаратная основа**
|
||
### **1.1. Порты контроллеров**
|
||
- PS1 имеет **два порта** для подключения устройств (Multitap позволяет расширить до 4–8 игроков).
|
||
- Каждый порт использует **протокол SPI** (Serial Peripheral Interface) для обмена данными.
|
||
|
||
### **1.2. Регистры управления**
|
||
Основные регистры для работы с контроллерами:
|
||
|
||
| Адрес | Назначение |
|
||
|----------------|---------------------------------------------------------------------------|
|
||
| **0x1F801040** | **SIO_DATA** — Данные для отправки/приёма. |
|
||
| **0x1F801044** | **SIO_STAT** — Статус операции (готовность, ошибки). |
|
||
| **0x1F801048** | **SIO_MODE** — Настройка режима (скорость, прерывания). |
|
||
| **0x1F801050** | **SIO_CTRL** — Управление передачей (старт/стоп). |
|
||
|
||
---
|
||
|
||
## **2. Протокол обмена данными**
|
||
### **2.1. Формат команд**
|
||
Контроллеры PS1 понимают **5-байтовые команды**:
|
||
1. **Стартовый байт** (`0x01`) — начало передачи.
|
||
2. **Команда** (например, `0x42` — запрос состояния кнопок).
|
||
3. **Данные** (зависит от типа устройства).
|
||
|
||
### **2.2. Пример запроса состояния кнопок**
|
||
```asm
|
||
; 1. Запись команды в SIO_DATA
|
||
li t0, 0x1F801040
|
||
li t1, 0x01000042 // Старт (0x01) + команда (0x42)
|
||
sw t1, 0(t0)
|
||
|
||
; 2. Ожидание готовности
|
||
wait_ready:
|
||
lw t2, 0x1F801044(SIO_STAT)
|
||
andi t2, t2, 0x0002 // Проверка бита "Готов к передаче"
|
||
beqz t2, wait_ready
|
||
|
||
; 3. Чтение ответа (5 байт)
|
||
lw t3, 0x1F801040(SIO_DATA) // Первые 4 байта
|
||
lw t4, 0x1F801040(SIO_DATA) // Последний байт
|
||
```
|
||
|
||
---
|
||
|
||
## **3. Типы контроллеров и их ответы**
|
||
### **3.1. Стандартный геймпад (Digital Pad)**
|
||
- **Команда**: `0x42`.
|
||
- **Ответ**: 5 байт:
|
||
```
|
||
[0x41][0x5A][Кнопки 1][Кнопки 2][Аналоговые данные (если есть)]
|
||
```
|
||
- **Распаковка кнопок**:
|
||
- Байт 3: `SELECT`, `L3`, `R3`, `START`, `UP`, `RIGHT`, `DOWN`, `LEFT`.
|
||
- Байт 4: `L2`, `R2`, `L1`, `R1`, `TRIANGLE`, `CIRCLE`, `CROSS`, `SQUARE`.
|
||
|
||
### **3.2. Аналоговый джойстик (DualShock)**
|
||
- Поддерживает **вибрацию** и **аналоговые стики**.
|
||
- **Команды**:
|
||
- `0x43` — запрос аналоговых данных.
|
||
- `0x4D` — управление моторчиками.
|
||
|
||
---
|
||
|
||
## **4. Как игры обрабатывают ввод?**
|
||
1. **Инициализация порта**:
|
||
```c
|
||
InitPad(1, 0, NULL, 0); // Порт 1, без буфера
|
||
```
|
||
2. **Чтение состояния**:
|
||
```c
|
||
struct PadButtonStatus pad;
|
||
ReadPadStatus(&pad, 0); // Чтение порта 1
|
||
```
|
||
3. **Проверка кнопок**:
|
||
```c
|
||
if (pad.buttons & PAD_CROSS) {
|
||
// Кнопка CROSS нажата
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## **5. Прерывания и синхронизация**
|
||
- **Прерывание IRQ7** используется для обработки ввода.
|
||
- Игры часто опрашивают контроллеры **во время VBlank**, чтобы избежать задержек.
|
||
|
||
---
|
||
|
||
## **6. Псевдокод полного цикла**
|
||
```c
|
||
while (1) {
|
||
VSync(0); // Синхронизация с кадром
|
||
PollPad(); // Опрос контроллера
|
||
|
||
if (PAD_PRESSED(PAD_UP)) {
|
||
player_y--;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## **7. Интересные нюансы**
|
||
- **Light Gun** (GunCon) требует **строгой синхронизации** с лучом ЭЛТ.
|
||
- **Multitap** усложняет протокол (нужно переключать устройства).
|
||
|
||
---
|
||
|
||
# Функция `ChangeClearPad` в библиотеке PsyQ для PlayStation 1
|
||
|
||
Функция `ChangeClearPad` относится к работе с контроллерами (геймпадами) PlayStation и используется для управления поведением буфера ввода.
|
||
|
||
## Назначение функции
|
||
|
||
`ChangeClearPad` изменяет режим очистки буфера данных контроллера. Она определяет, как система будет обрабатывать ввод с геймпада между кадрами:
|
||
|
||
1. **Автоматическая очистка буфера** (по умолчанию) - система автоматически очищает данные о предыдущем состоянии кнопок
|
||
2. **Ручное управление буфером** - позволяет программисту самому решать, когда очищать буфер ввода
|
||
|
||
## Синтаксис
|
||
|
||
```c
|
||
void ChangeClearPad(int mode);
|
||
```
|
||
|
||
где `mode` может принимать значения:
|
||
- `0` - ручной режим (очистка буфера только при вызове `ClearPad()`)
|
||
- `1` - автоматический режим (очистка при каждом VSync)
|
||
|
||
## Практическое использование
|
||
|
||
```c
|
||
#include <libpad.h>
|
||
|
||
int main() {
|
||
// Инициализация контроллера
|
||
PadInit(0);
|
||
|
||
// Переключение в ручной режим очистки буфера
|
||
ChangeClearPad(0);
|
||
|
||
while(1) {
|
||
// Чтение состояния контроллера
|
||
PadRead(0);
|
||
|
||
// В ручном режиме нужно самостоятельно очищать буфер
|
||
if(need_to_clear) {
|
||
ClearPad(0);
|
||
}
|
||
|
||
// Автоматическая очистка происходить не будет
|
||
}
|
||
}
|
||
```
|
||
|
||
## Типичные сценарии использования
|
||
|
||
1. **Фиксация коротких нажатий**:
|
||
```c
|
||
ChangeClearPad(0); // Ручной режим
|
||
if(PadRead(0) {
|
||
if((pad_status & PAD_CROSS) && !(old_pad_status & PAD_CROSS)) {
|
||
// Обнаружено нажатие Cross
|
||
}
|
||
old_pad_status = pad_status;
|
||
ClearPad(0); // Очищаем буфер вручную
|
||
}
|
||
```
|
||
|
||
2. **Комбинации кнопок** - когда нужно отслеживать состояние нескольких кнопок одновременно
|
||
|
||
3. **Специальные игровые механики** - где важно точное время нажатия/отпускания кнопок
|
||
|
||
## Важные нюансы
|
||
|
||
1. По умолчанию в PsyQ используется автоматический режим (`ChangeClearPad(1)`)
|
||
|
||
2. В ручном режиме вы должны самостоятельно вызывать `ClearPad()` для очистки буфера
|
||
|
||
3. Функция влияет на работу `PadRead()` - в автоматическом режиме она сама очищает предыдущее состояние
|
||
|
||
4. Для большинства игр достаточно стандартного автоматического режима
|
||
|
||
5. Функция работает с обоими портами контроллера (0 и 1)
|
||
|
||
-----
|
||
|
||
# Функция `PadChkVsync` в библиотеке PsyQ для PlayStation 1
|
||
|
||
Функция `PadChkVsync` используется для проверки состояния геймпада (контроллера) с синхронизацией по вертикальному гашению (VSync). Это важная функция для обработки ввода в играх на PS1.
|
||
|
||
## Основное назначение
|
||
|
||
`PadChkVsync` выполняет две ключевые задачи:
|
||
1. Проверяет состояние подключенных контроллеров
|
||
2. Синхронизирует чтение состояния с вертикальной синхронизацией (VSync)
|
||
|
||
## Синтаксис
|
||
|
||
```c
|
||
int PadChkVsync(void);
|
||
```
|
||
|
||
## Возвращаемое значение
|
||
|
||
Функция возвращает битовую маску, указывающую на состояние подключения контроллеров:
|
||
- Бит 0: Состояние порта 1 (0 - контроллер подключен, 1 - не подключен)
|
||
- Бит 1: Состояние порта 2
|
||
- Бит 2: Состояние мемори-карты в порту 1
|
||
- Бит 3: Состояние мемори-карты в порту 2
|
||
|
||
## Особенности работы
|
||
|
||
1. **Синхронизация с VSync** - чтение состояния происходит во время вертикального гашения, что предотвращает "разрывы" изображения
|
||
2. **Определение подключенных устройств** - позволяет определить, какие контроллеры/мемори-карты подключены
|
||
3. **Работа в паре с другими функциями Pad** - обычно используется вместе с `PadRead()`
|
||
|
||
## Пример использования
|
||
|
||
```c
|
||
#include <libpad.h>
|
||
|
||
int main() {
|
||
PadInit(0); // Инициализация системы контроллеров
|
||
|
||
while(1) {
|
||
// Проверка состояния подключения
|
||
int connected = PadChkVsync();
|
||
|
||
if (!(connected & 1)) { // Если контроллер в порту 1 подключен
|
||
u_long pad = PadRead(0); // Чтение состояния
|
||
|
||
if (pad & PAD_UP) {
|
||
// Обработка нажатия UP
|
||
}
|
||
// Другие проверки кнопок...
|
||
}
|
||
|
||
VSync(0); // Ожидание следующего VBlank
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
## Типичный сценарий использования
|
||
|
||
1. В начале кадра вызывается `PadChkVsync()` для проверки подключенных устройств
|
||
2. Если контроллер подключен, вызывается `PadRead()` для получения состояния кнопок
|
||
3. Состояние кнопок обрабатывается в игровой логике
|
||
4. Цикл повторяется после `VSync(0)`
|
||
|
||
## Важные нюансы
|
||
|
||
1. Функция не возвращает состояние кнопок - только информацию о подключении
|
||
2. Для фактического чтения кнопок нужно использовать `PadRead()`
|
||
3. Рекомендуется вызывать эту функцию один раз за кадр
|
||
4. В отличие от `PadRead()`, `PadChkVsync` не очищает буфер ввода
|
||
|
||
Функция `PadChkVsync` особенно полезна в играх, где важно точно определять момент подключения/отключения контроллеров во время gameplay, например, в multiplayer-играх с возможностью "hot-plug" контроллеров.
|
||
|
||
-------
|
||
|
||
|
||
|
||
Да, функция **`PadInitDirect`** действительно существует в библиотеке PsyQ и является важной частью системы обработки ввода для PlayStation 1. Вот подробное объяснение:
|
||
|
||
---
|
||
|
||
### 🔍 **Назначение PadInitDirect**
|
||
Функция **инициализирует систему обработки геймпадов** в режиме прямого доступа (Direct Access). Она:
|
||
|
||
1. **Настраивает аппаратные порты контроллеров** (до 2-х геймпадов)
|
||
2. **Подготавливает буферы** для чтения состояния кнопок
|
||
3. **Активирует низкоуровневый доступ** к данным геймпадов
|
||
4. **Возвращает статус** инициализации (`1` = успех, `0` = ошибка)
|
||
|
||
---
|
||
|
||
### 📜 **Синтаксис в PsyQ**
|
||
```c
|
||
#include <libpad.h>
|
||
|
||
int PadInitDirect(unsigned char *padBuf1, unsigned char *padBuf2);
|
||
```
|
||
- **`padBuf1`** - буфер для геймпада на порту 1 (размер ≥ 34 байт)
|
||
- **`padBuf2`** - буфер для геймпада на порту 2 (может быть `NULL`)
|
||
|
||
---
|
||
|
||
### 💻 **Пример использования**
|
||
```c
|
||
#define BUF_SIZE 34
|
||
|
||
int main() {
|
||
unsigned char pad1Buf[BUF_SIZE] __attribute__((aligned(64))); // Выравнивание для DMA
|
||
|
||
if(PadInitDirect(pad1Buf, NULL)) {
|
||
printf("Геймпад 1 инициализирован!\n");
|
||
} else {
|
||
printf("Ошибка инициализации!\n");
|
||
return 1;
|
||
}
|
||
|
||
// Далее можно использовать PadRead()
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 🔧 **Особенности работы**
|
||
1. **Требования к буферам**:
|
||
- Минимальный размер: **34 байта**
|
||
- Должны быть **64-байтно выровнены** (для DMA)
|
||
- Пример объявления:
|
||
```c
|
||
unsigned char padBuf[34] __attribute__((aligned(64)));
|
||
```
|
||
|
||
2. **Режимы работы**:
|
||
- Прямой доступ (`PadInitDirect`) - ручное управление
|
||
- Стандартный режим (`PadInit`) - автоматическое управление
|
||
|
||
3. **Совместимость**:
|
||
- Поддерживает все типы контроллеров PS1:
|
||
- Обычные геймпады (Digital)
|
||
- Аналоговые джойстики (DualShock)
|
||
- Мышь PlayStation
|
||
|
||
---
|
||
|
||
### ⚠️ **Важные нюансы**
|
||
1. **Перед использованием** необходимо вызвать `PadStartCom()` для начала обмена данными
|
||
2. **Для чтения** используется `PadRead()` после инициализации
|
||
3. **В мультиплеерных играх** нужно инициализировать оба порта:
|
||
```c
|
||
PadInitDirect(pad1Buf, pad2Buf);
|
||
```
|
||
|
||
---
|
||
|
||
### 📚 **Документация PsyQ**
|
||
В официальном **PsyQ SDK** функция описана в файлах:
|
||
- `libpad.h` - заголовочный файл
|
||
- `libpad.doc` - документация
|
||
- Примеры в `Samples/PAD` демонстрируют использование
|