Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PS1 IRQ.md
2025-04-19 00:11:01 +05:00

298 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### **Прерывания (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**!
-------------
# Команда `SetIntrMask` в PS1 (PlayStation 1)
`SetIntrMask` — это низкоуровневая функция в SDK PlayStation 1 (как в официальном Sony SDK, так и в PsyQ), которая управляет маской прерываний процессора R3000.
## Основное назначение
Функция `SetIntrMask` устанавливает битовую маску, определяющую, какие прерывания будут обрабатываться процессором. Она позволяет включать/выключать определенные типы прерываний.
## Синтаксис (в PsyQ)
```c
void SetIntrMask(unsigned int mask);
```
## Битовая маска прерываний
Маска прерываний обычно определяется следующими флагами (конкретные значения могут отличаться в разных версиях SDK):
```
0x0001 - VBLANK (вертикальное гашение)
0x0002 - GPU
0x0004 - CD-ROM
0x0008 - DMA
0x0010 - TIMER0 (таймер 0)
0x0020 - TIMER1 (таймер 1)
0x0040 - TIMER2 (таймер 2)
0x0080 - Периферийные устройства (контроллеры, память карт)
0x0400 - SPU (звуковой процессор)
```
## Примеры использования
1. **Разрешить все прерывания**:
```c
SetIntrMask(0xFFFF);
```
2. **Разрешить только VBLANK и CD-ROM**:
```c
SetIntrMask(0x0005); // 0x0001 (VBLANK) | 0x0004 (CD-ROM)
```
3. **Запретить все прерывания**:
```c
SetIntrMask(0x0000);
```
## Типичное использование в сочетании с другими функциями
```c
// Сохраняем текущую маску
unsigned int old_mask = GetIntrMask();
// Запрещаем все прерывания
SetIntrMask(0x0000);
// Выполняем критический код, который не должен прерываться
// Восстанавливаем предыдущую маску
SetIntrMask(old_mask);
```
## Важные замечания
1. **Альтернативы**: В PsyQ часто используют `EnterCriticalSection()` и `ExitCriticalSection()`, которые internally работают с `SetIntrMask`.
2. **Влияние на производительность**: Неправильное управление маской прерываний может привести к пропуску важных событий (например, VBLANK).
3. **GPU синхронизация**: При работе с GPU часто требуется временно отключать прерывания для атомарных операций.
4. **Сочетание с обработчиками**: Даже если прерывание разрешено маской, для него должен быть установлен обработчик через соответствующие функции PsyQ.
Эта функция предоставляет низкоуровневый контроль над прерываниями и обычно используется в сочетании с более высокоуровневыми API PsyQ для управления конкретными типами прерываний.