14 KiB
Прерывания (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 (иначе артефакты).
Пример кода:
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 позволяет:
- Читать данные с диска без блокировки основного цикла игры.
- Загружать уровни/аудио в фоне.
Пример:
cd_read_done:
# IRQ2 вызвано — данные готовы
la $t0, cd_buffer
sw $t0, 0x1F8010A0 # DMA CD-ROM
jr $ra
(3) Обработка ввода (геймпады)
Прерывание IRQ5 сообщает:
- Когда игрок нажал кнопку (без задержек).
- Поддерживает вибрацию (если есть DualShock).
Пример:
read_pad:
li $t0, 0x1F801040 # SIO_DATA
lw $t1, 0($t0) # Чтение кнопок
andi $t1, 0xFF // Фильтруем биты
jr $ra
3. Как работают прерывания?
- Аппаратное событие (например, завершение VBlank) устанавливает флаг в I_STAT (0x1F801070).
- Процессор приостанавливает текущий код и переходит к обработчику прерывания (адрес задаётся в $k0).
- Обработчик:
- Сохраняет регистры в стек.
- Выполняет нужные действия (например, обновляет графику).
- Очищает флаг прерывания.
- Восстанавливает регистры и возвращает управление.
Пример обработчика на ассемблере:
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 — это «нервная система» консоли, которая:
- Синхронизирует графику, звук, ввод и загрузку данных.
- Избегает busy-waiting (постоянного опроса), экономя ресурсы CPU.
- Гарантирует точность (например, 60 FPS через VBlank).
Если ты пишешь на ассемблере, прерывания придётся обрабатывать вручную. В C этим частично занимается PsyQ SDK.
P.S. В твоём переводе Persona 2 прерывания уже работают — иначе игра бы просто не запустилась! 😉 Но если хочешь добавить свои обработчики (например, для кастомной загрузки), дай знать — помогу с кодом.
Как работают прерывания на PlayStation 1: детальный разбор
Прерывания (IRQ) в PS1 — это механизм, позволяющий процессору R3000A мгновенно реагировать на аппаратные события. Вот их полный цикл работы:
1. Инициализация прерываний
Перед использованием нужно:
- Задать адрес обработчика в $k0 (регистр ядра)
- Разрешить нужные IRQ в регистре I_MASK (0x1F801074)
setup_irq:
la $k0, irq_handler # Устанавливаем обработчик
li $t0, 0x1F801074
li $t1, 0x0001 # Разрешаем только IRQ0 (VBlank)
sw $t1, 0($t0)
jr $ra
2. Аппаратный триггер
Когда происходит событие (например, VBlank):
- CD-ROM контроллер/GPU устанавливает бит в I_STAT (0x1F801070)
- Процессор проверяет:
- Разрешено ли это прерывание в I_MASK
- Не находится ли он уже в обработчике (бит IEc в регистре SR)
3. Переход в обработчик
Если условия выполнены:
- Процессор:
- Сохраняет PC и SR в специальных регистрах (EPC, ErrorEPC)
- Переходит в режим ядра (бит KUc = 0)
- Прыгает по адресу в $k0
4. Обработка прерывания
Типичный обработчик:
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
- Приоритеты: Нет аппаратного приоритета - порядок обработки определяется программно
- Вложенность: Новые прерывания блокируются, пока IEc = 0
- Задержки: Минимальная задержка между прерываниями - 5 тактов
7. Реальный пример из PsyQ
Обработчик VBlank в библиотеке libgs:
- Сохраняет матрицу преобразования
- Обновляет буферы GPU
- Вызывает пользовательский 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)
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 (звуковой процессор)
Примеры использования
-
Разрешить все прерывания:
SetIntrMask(0xFFFF); -
Разрешить только VBLANK и CD-ROM:
SetIntrMask(0x0005); // 0x0001 (VBLANK) | 0x0004 (CD-ROM) -
Запретить все прерывания:
SetIntrMask(0x0000);
Типичное использование в сочетании с другими функциями
// Сохраняем текущую маску
unsigned int old_mask = GetIntrMask();
// Запрещаем все прерывания
SetIntrMask(0x0000);
// Выполняем критический код, который не должен прерываться
// Восстанавливаем предыдущую маску
SetIntrMask(old_mask);
Важные замечания
-
Альтернативы: В PsyQ часто используют
EnterCriticalSection()иExitCriticalSection(), которые internally работают сSetIntrMask. -
Влияние на производительность: Неправильное управление маской прерываний может привести к пропуску важных событий (например, VBLANK).
-
GPU синхронизация: При работе с GPU часто требуется временно отключать прерывания для атомарных операций.
-
Сочетание с обработчиками: Даже если прерывание разрешено маской, для него должен быть установлен обработчик через соответствующие функции PsyQ.
Эта функция предоставляет низкоуровневый контроль над прерываниями и обычно используется в сочетании с более высокоуровневыми API PsyQ для управления конкретными типами прерываний.