Инструменты пользователя

Инструменты сайта


interrupt

Это старая версия документа!


Прерывания

Прерывание (interrupt) - это технология, которая позволяет программе реагировать на внешние события. При наступлении события процессор временно прекращает выполнять свою текущую работу и переходит в специальную функцию, называемую обработчиком прерывания. Когда обработка завершается, процессор возвращается к выполнению программы.

Прерывания могут быть как программными, так и аппаратными. На PlayStation программные прерывания часто называются исключениями (exception). Обработку прерываний, по большей части, берет на себя BIOS (хотя и делает это неэффективно - см. на сайте problemkaputt).

Управлением прерываниями занимается сопроцессор COP0. Пользовательский код может взаимодействовать с ним при помощи специальных регистров:

  • SR (cop0r12) - статусный регистр; позволяет произвольно включать и выключать обработку прерываний, а также маскировать те или иные линии.
  • Cause (cop0r13) - регистр причины прерывания. Предоставляет битовые флаги линий прерываний и код, идентифицирующий тип последнего прерывания. Регистр имеет 8 линий: 2 для исключений (биты 8-9) и 6 для аппаратных прерываний (биты 10-15). PlayStation использует только одну линию аппаратных прерываний (бит 10), все остальные всегда нулевые.
  • EPC (cop0r14) - содержит адрес инструкции, которая вызвала исключение. Если произошло аппаратное прерывание, то EPC содержит адрес следующей инструкции, которая должна выполниться после обработки.
  • BadVaddr (cop0r8) - используется при исключениях шины. Содержит адрес, обращение по которому вызвало исключение.

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

  1. COP0 сохраняет значение программного счетчика (PC) в регистр EPC. Если текущая инструкция - это слот задержки ветвления, то будет записано значение PC-4, т.е. адрес предыдущей инструкции. В регистре Cause устанавливается бит, означающий, что прерывание сработало в слоте задержки ветвления.
  2. Код прерывания записывается в регистр Cause.
  3. Отключается обработка дальнейших прерываний до тех пор, пока не завершится обработка текущего. Включается режим ядра (на PlayStation он обычно и так всегда включен). Предыдущее состояние сохраняется в регистр SR.
  4. Если сработало исключение шины, проблемный адрес записывается в BadVaddr.
  5. CPU переходит по вектору 0xBFC00180, если в SR установлен бит Boot Exception Vectors (BEV), либо по вектору 0x80000080 в противном случае. BEV устанавливается при включении, но BIOS его зануляет. По адресу 0x80000080, который находится в рабочем пространстве BIOS, начинается базовый код обработки прерывания.

Перед загрузкой игры BIOS отключает обработку прерываний, поэтому, чтобы работать с периферийными устройствами (геймпад, CD-ROM и пр.), нужно либо вручную установить биты 2 и 10 статусного регистра COP0, либо проинициализировать геймпад функцией BIOS InitPad.

BIOS создает четыре очереди обработчиков прерываний:

  • 0 - Системные исключения - syscall, деление на ноль, переполнение памяти;
  • 1 - Прерывания от системных таймеров и VBlank;
  • 2 - Зарезервировано;
  • 3 - Обработчики аппаратных прерываний периферии.

Перед вызовом очереди BIOS сохраняет все регистры общего назначения (GPR) в специальный буфер, так что самостоятельно это делать не нужно.

Установка пользовательских обработчиков

Обработчики прерываний добавляются функцией BIOS SysEnqIntRP и убираются функцией SysDeqIntRP. Обе функции принимают вторым аргументом указатель на следующую структуру:

struct IntRP
{
    struct IntRP* next; // следующий элемент IntRP в связном списке, либо null, если это последний элемент
    void* func2; // указатель на функцию, которая вызывается второй, если func1 вернула ненулевое значение
    void* func1; // указатель на функцию, которая вызывается первой
    int flag; // зарезервированное значение, должно быть равно нулю
};

Обработчик вызывается следующим образом:

  • func2(func1()), если присутствуют обе функции;
  • func1(), если присутствует первая функция, но отсутствует вторая;
  • В иных случаях ничего не вызывается.

Чтобы безопасно поместить элемент в очередь, необходимо обернуть этот вызов в критическую секцию, временно отключив аппаратные прерывания. Это предотвратит сбой в работе консоли, который мог бы произойти, если бы во время изменения указателей очереди возникло другое прерывание.

BIOS по цепочке вызывает все зарегистрированные обработчики прерываний. Каждый обработчик должен проверить нужный бит регистра I_STAT и подтвердить его, записав в него ноль. Например:

int irqHandler1()
{
    if ((*I_STAT & IRQ0) && (*I_MASK & IRQ0))
    {
        // Reset VBlank bit
        *I_STAT = ~IRQ0;
    }
    return 0;
}

Функции BIOS, относящиеся к обработке прерываний

  • С(02h) - SysEnqIntRP(priorityIndex, handler) - добавляет обработчик прерывания в очередь.
  • С(03h) - SysDeqIntRP(priorityIndex, handler) - убирает обработчик прерывания из очереди.
  • int EnterCriticalSection(), вызов через syscall с номером 0x01 в регистре $a0 - отключает обработку прерываний, зануляя биты 2 и 10 регистра SR (cop0r12). Возвращает 1, если оба бита были установлены, и 0 в противном случае. Эту функцию нужно вызывать в редких случаях, когда нужно выполнить код, который не должен прерываться.
  • void ExitCriticalSection(), вызов через syscall с номером 0x01 в регистре $a0 - включает обработку прерываний, устанавливая биты 2 и 10 регистра SR (cop0r12).
interrupt.1792947425.txt.gz · Последнее изменение: — gecko

Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: CC0 1.0 Universal
CC0 1.0 Universal Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki