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

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


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, относящиеся к обработке прерываний

  • С(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.1792946907.txt.gz · Последнее изменение: — gecko

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