### **Прерывания (Interrupts) на PlayStation 1: Зачем они нужны и как работают?** Прерывания в PS1 — это механизм, позволяющий процессору **мгновенно реагировать** на критичные события (например, завершение рендеринга кадра или чтение с диска), не требуя постоянного опроса (polling). Они играют ключевую роль в синхронизации и эффективности системы. --- ## **1. Основные типы прерываний** В PS1 есть несколько аппаратных прерываний, каждое обслуживает свою задачу: | Прерывание | Источник | Задача | |------------|---------------------------|-----------------------------------------------------------------------| | **IRQ0** | VBlank (вертикальная синхронизация) | Оповещает о завершении рендеринга кадра. Используется для обновления графики. | | **IRQ1** | GPU | Генерируется при ошибках GPU (например, переполнение буфера команд). | | **IRQ2** | CD-ROM | Сигнализирует о завершении чтения данных с диска. | | **IRQ3** | DMA | Уведомляет о завершении передачи данных (например, в VRAM). | | **IRQ4** | Timer (таймер) | Используется для точного измерения времени. | | **IRQ5** | Контроллеры (геймпады) | Обработка ввода игрока. | | **IRQ6** | SPU (звук) | Прерывание от звукового процессора (например, окончание воспроизведения сэмпла). | --- ## **2. Зачем они нужны?** ### **(1) Синхронизация графики (VBlank)** Без прерывания **IRQ0** (VBlank) игра не могла бы: - Плавно обновлять кадры (без разрывов изображения). - Безопасно изменять VRAM (иначе артефакты). **Пример кода:** ```mips wait_vblank: li $t0, 0x1F801070 # I_STAT (регистр статуса прерываний) lw $t1, 0($t0) andi $t1, 0x1 # Проверяем бит IRQ0 (VBlank) beqz $t1, wait_vblank nop jr $ra ``` --- ### **(2) Асинхронная работа с CD-ROM** Прерывание **IRQ2** позволяет: - Читать данные с диска **без блокировки основного цикла игры**. - Загружать уровни/аудио **в фоне**. **Пример:** ```mips cd_read_done: # IRQ2 вызвано — данные готовы la $t0, cd_buffer sw $t0, 0x1F8010A0 # DMA CD-ROM jr $ra ``` --- ### **(3) Обработка ввода (геймпады)** Прерывание **IRQ5** сообщает: - Когда игрок нажал кнопку (без задержек). - Поддерживает **вибрацию** (если есть DualShock). **Пример:** ```mips read_pad: li $t0, 0x1F801040 # SIO_DATA lw $t1, 0($t0) # Чтение кнопок andi $t1, 0xFF // Фильтруем биты jr $ra ``` --- ## **3. Как работают прерывания?** 1. **Аппаратное событие** (например, завершение VBlank) устанавливает флаг в **I_STAT (0x1F801070)**. 2. Процессор **приостанавливает текущий код** и переходит к **обработчику прерывания** (адрес задаётся в **$k0**). 3. Обработчик: - Сохраняет регистры в стек. - Выполняет нужные действия (например, обновляет графику). - Очищает флаг прерывания. - Восстанавливает регистры и возвращает управление. **Пример обработчика на ассемблере:** ```mips irq_handler: addiu $sp, -32 # Резервируем стек sw $ra, 0($sp) # Сохраняем $ra sw $a0, 4($sp) # Сохраняем аргументы # Проверяем источник прерывания li $t0, 0x1F801070 lw $t1, 0($t0) andi $t2, $t1, 0x1 # IRQ0 (VBlank)? bnez $t2, handle_vblank nop # Выход lw $a0, 4($sp) lw $ra, 0($sp) addiu $sp, 32 eret # Возврат из прерывания handle_vblank: # Обновляем кадр jal update_graphics nop j irq_handler # Возврат ``` --- ## **4. Почему без прерываний — плохо?** - **Графика**: Без VBlank появится **tearing** (разрывы кадров). - **Звук**: SPU будет работать с задержками. - **Ввод**: Кнопки станут «лагающими». - **CD-ROM**: Игра зависнет на время загрузки. --- ## **5. Итог** Прерывания в PS1 — это **«нервная система» консоли**, которая: 1. **Синхронизирует** графику, звук, ввод и загрузку данных. 2. **Избегает busy-waiting** (постоянного опроса), экономя ресурсы CPU. 3. **Гарантирует точность** (например, 60 FPS через VBlank). Если ты **пишешь на ассемблере**, прерывания придётся обрабатывать вручную. В **C** этим частично занимается PsyQ SDK. P.S. В твоём переводе Persona 2 прерывания **уже работают** — иначе игра бы просто не запустилась! 😉 Но если хочешь **добавить свои обработчики** (например, для кастомной загрузки), дай знать — помогу с кодом. ------------------ ### **Как работают прерывания на PlayStation 1: детальный разбор** Прерывания (IRQ) в PS1 — это механизм, позволяющий процессору R3000A мгновенно реагировать на аппаратные события. Вот их полный цикл работы: #### **1. Инициализация прерываний** Перед использованием нужно: - Задать адрес обработчика в **$k0** (регистр ядра) - Разрешить нужные IRQ в регистре **I_MASK (0x1F801074)** ```mips setup_irq: la $k0, irq_handler # Устанавливаем обработчик li $t0, 0x1F801074 li $t1, 0x0001 # Разрешаем только IRQ0 (VBlank) sw $t1, 0($t0) jr $ra ``` #### **2. Аппаратный триггер** Когда происходит событие (например, VBlank): 1. CD-ROM контроллер/GPU устанавливает бит в **I_STAT (0x1F801070)** 2. Процессор проверяет: - Разрешено ли это прерывание в **I_MASK** - Не находится ли он уже в обработчике (бит **IEc** в регистре **SR**) #### **3. Переход в обработчик** Если условия выполнены: 1. Процессор: - Сохраняет **PC** и **SR** в специальных регистрах (**EPC**, **ErrorEPC**) - Переходит в режим ядра (бит **KUc** = 0) - Прыгает по адресу в **$k0** #### **4. Обработка прерывания** Типичный обработчик: ```mips irq_handler: # 1. Сохраняем контекст addiu $sp, -120 # Резервируем место для 30 регистров sw $at, 0($sp) sw $v0, 4($sp) # ... сохраняем все используемые регистры # 2. Определяем источник li $t0, 0x1F801070 lw $t1, 0($t0) # Читаем I_STAT # 3. Обработка VBlank andi $t2, $t1, 0x1 beqz $t2, not_vblank nop jal handle_vblank nop not_vblank: # 4. Сбрасываем флаг прерывания li $t3, 0xFFFF00FF # Маска для очистки and $t1, $t1, $t3 sw $t1, 0($t0) # Пишем обратно в I_STAT # 5. Восстанавливаем контекст lw $at, 0($sp) lw $v0, 4($sp) # ... addiu $sp, 120 # 6. Возврат eret # Возврат с восстановлением SR и PC ``` #### **5. Возврат из прерывания** Инструкция **eret**: - Восстанавливает **PC** из **EPC** - Восстанавливает **SR** (включая бит **IEc**) - Возвращает процессор в пользовательский режим #### **6. Особенности PS1** 1. **Приоритеты**: Нет аппаратного приоритета - порядок обработки определяется программно 2. **Вложенность**: Новые прерывания блокируются, пока **IEc = 0** 3. **Задержки**: Минимальная задержка между прерываниями - 5 тактов #### **7. Реальный пример из PsyQ** Обработчик VBlank в библиотеке **libgs**: 1. Сохраняет матрицу преобразования 2. Обновляет буферы GPU 3. Вызывает пользовательский callback #### **8. Оптимизации** - **Короткие обработчики**: Не более 100 тактов - **Минимальное сохранение**: Только используемые регистры - **Аппаратное ускорение**: Для DMA используйте **D_ICR (0x1F8010F4)** Для отладки в no$psx используйте точки останова на записи в **I_STAT**!