В 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 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 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 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` демонстрируют использование