В 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" контроллеров.