Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PS1 Gamepad.md
2025-04-20 00:25:47 +05:00

15 KiB
Raw Blame History

В PlayStation 1 (PS1) взаимодействие с контроллерами (геймпадами, мышками, световыми пистолетами и т.д.) осуществляется через порты ввода-вывода (I/O) и специализированные контроллеры SIO (Serial Input/Output). Вот детальное объяснение процесса:


1. Аппаратная основа

1.1. Порты контроллеров

  • PS1 имеет два порта для подключения устройств (Multitap позволяет расширить до 48 игроков).
  • Каждый порт использует протокол 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. Пример запроса состояния кнопок

; 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. Инициализация порта:
    InitPad(1, 0, NULL, 0);  // Порт 1, без буфера
    
  2. Чтение состояния:
    struct PadButtonStatus pad;
    ReadPadStatus(&pad, 0);  // Чтение порта 1
    
  3. Проверка кнопок:
    if (pad.buttons & PAD_CROSS) {
        // Кнопка CROSS нажата
    }
    

5. Прерывания и синхронизация

  • Прерывание IRQ7 используется для обработки ввода.
  • Игры часто опрашивают контроллеры во время VBlank, чтобы избежать задержек.

6. Псевдокод полного цикла

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. Ручное управление буфером - позволяет программисту самому решать, когда очищать буфер ввода

Синтаксис

void ChangeClearPad(int mode);

где mode может принимать значения:

  • 0 - ручной режим (очистка буфера только при вызове ClearPad())
  • 1 - автоматический режим (очистка при каждом VSync)

Практическое использование

#include <libpad.h>

int main() {
    // Инициализация контроллера
    PadInit(0);
    
    // Переключение в ручной режим очистки буфера
    ChangeClearPad(0);
    
    while(1) {
        // Чтение состояния контроллера
        PadRead(0);
        
        // В ручном режиме нужно самостоятельно очищать буфер
        if(need_to_clear) {
            ClearPad(0);
        }
        
        // Автоматическая очистка происходить не будет
    }
}

Типичные сценарии использования

  1. Фиксация коротких нажатий:

    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)

Синтаксис

int PadChkVsync(void);

Возвращаемое значение

Функция возвращает битовую маску, указывающую на состояние подключения контроллеров:

  • Бит 0: Состояние порта 1 (0 - контроллер подключен, 1 - не подключен)
  • Бит 1: Состояние порта 2
  • Бит 2: Состояние мемори-карты в порту 1
  • Бит 3: Состояние мемори-карты в порту 2

Особенности работы

  1. Синхронизация с VSync - чтение состояния происходит во время вертикального гашения, что предотвращает "разрывы" изображения
  2. Определение подключенных устройств - позволяет определить, какие контроллеры/мемори-карты подключены
  3. Работа в паре с другими функциями Pad - обычно используется вместе с PadRead()

Пример использования

#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

#include <libpad.h>

int PadInitDirect(unsigned char *padBuf1, unsigned char *padBuf2);
  • padBuf1 - буфер для геймпада на порту 1 (размер ≥ 34 байт)
  • padBuf2 - буфер для геймпада на порту 2 (может быть NULL)

💻 Пример использования

#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)
    • Пример объявления:
      unsigned char padBuf[34] __attribute__((aligned(64)));
      
  2. Режимы работы:

    • Прямой доступ (PadInitDirect) - ручное управление
    • Стандартный режим (PadInit) - автоматическое управление
  3. Совместимость:

    • Поддерживает все типы контроллеров PS1:
      • Обычные геймпады (Digital)
      • Аналоговые джойстики (DualShock)
      • Мышь PlayStation

⚠️ Важные нюансы

  1. Перед использованием необходимо вызвать PadStartCom() для начала обмена данными
  2. Для чтения используется PadRead() после инициализации
  3. В мультиплеерных играх нужно инициализировать оба порта:
    PadInitDirect(pad1Buf, pad2Buf);
    

📚 Документация PsyQ

В официальном PsyQ SDK функция описана в файлах:

  • libpad.h - заголовочный файл
  • libpad.doc - документация
  • Примеры в Samples/PAD демонстрируют использование