Фантазии о Вселенной и мой личный сайт
2.8 Обработчики аппаратных прерываний

2.8 Обработчики аппаратных прерываний

Обработка аппаратных прерываний значительно отличается в различных ОС, поэтому имеет смысл давать лишь общие рекомендации. Более серьёзно к этому вопросу мы подойдём после того, как изучим мультизадачность и виртуальную память, а пока при реализации обработчиков аппаратных прерываний придерживайтесь следующего:

  1. Не используйте в IDT шлюзы ловушек, а только прерываний, т.к. при переходе через шлюз прерывания процессор автоматически запрещает маскируемые прерывания (сбрасывая флаг IF в EFLAGS), но не делает этого для шлюза ловушки.
  2. В начале обработки прерывания посылайте в контроллер 8259A команду конца прерывания (EOI). Контроллер состоит из двух контроллеров master и slave. Master обслуживает первые 8 IRQ, slave - вторые и для них посылка EOI будет выглядеть так:

    для master (IRQ 0..7)

    mov al,20h
    out 20h, al
    

    для slave (IRQ 8..15)

    mov al, 20h
    out 0a0h, al
    
  3. Постарайтесь сделать обработку прерывания как можно быстрее, т.к. процессор не допустит генерации нового прерывания, пока не будет завершён обработчик.
  4. При перенаправлении прерываний процедура "redirect_IRQ" запрещает контроллеру генерацию аппаратных прерываний. Значения в портах 21h и A1h содержат флаги маскировки прерываний для master- и slave-контроллера соответственно.
Прерывания master
Бит IRQ Устройство
0 0 Таймер
1 1 Клавиатура
2 2 Каскад (подключён ко второму контроллеру)
3 3 COM 2/4
4 4 COM 1/3
5 5 LPT 2
6 6 Контроллер дисковода FDC (Floppy Drive Controller)
7 7 LPT 1
Прерывания slave
Бит IRQ Устройство
0 8 Часы реального времени RTC (Real Time Clock)
1 9 Редирект с IRQ 2
2 10 Резерв (т.е. не имеет устройства по умолчанию)
3 11 Резерв (т.е. не имеет устройства по умолчанию)
4 12 Резерв (т.е. не имеет устройства по умолчанию)
5 13 Исключение сопроцессора
6 14 Контроллер винчестера HDC (Hard Drive Controller)
7 15 Резерв (т.е. не имеет устройства по умолчанию)

Для того, чтобы разрешить какое-либо прерывание, нужно сбросить соответствующий бит, а для запрещения - установить.

Например, для разрешения прерывания таймера нужно выполнить следующее:

in      al,  21h    ; Читаем маску master-а
and     al,  0feh   ; FEh = 11111110b - сбрасываем 0-й бит.
out     21h, al     ; Записываем маску в контроллер. Таймер разрешён.

Как правило, операционная система защищённого режима подразумевает возврат в режим реальных адресов и выход в ту ОС, из которой её запускали (например, в MS-DOS). В таком случае необходимо предусмотреть правильное маскирование прерываний IRQ перед возвратом в такую ОС, так как обычно не все прерывания разрешены.

Начиная со следующего примера в начале будет использоваться процедура, сохраняющая маску прерываний IRQ:

in      al, 0a1h
mov     ah, al
in      al, 21h
mov     R_Mode_IRQ_Mask, ax
ret

Для корректного возврата в режим реальных адресов нужно изменить одну команду в процедуре перенаправления векторов IRQ для R-Mode.

Теперь, казалось бы, наш пример должен правильно работать, но MS-DOS приготовил один неприятный "подводный камень". Дело в том, что при повторном запуске нашего примера, при условии, что в нём выполняются какие-либо процессы, длительностью более, чем примерно 2 секунды, контроллер клавиатуры генерирует символ. Если не обработать его должным образом, то клавиатура будет заблокирована, поэтому во всех наших примерах предлагается следующее:

  1. Обязательно размаскировывать прерывание клавиатуры (IRQ 1).
  2. Обязательно разрешать прерывания на время выполнения части программы, работающей в защищённом режиме.
  3. Установить обработчик IRQ клавиатуры или хотя бы следующую заглушку
    push    ax
    in      al,60h  ; AL содержит скан-код клавиатуры, но в
                    ;  этом примере он не сохраняется -
                    ;  обработчик IRQ 1 работает как заглушка.
    in      al, 61h
    mov     ah, al
    ; ---
    mov     al, 20h
    out     20h, al
    pop     ax
    iret
    

Как видите, установка обработчика IRQ клавиатуры свелась к простой замене определяющего его макроса "IRQ_1_handler".

А теперь вашему вниманию предлагается демонстрация обработки прерываний по таймеру. В приведенном ниже примере внесены следующие изменения (по сравнению с предыдущим и с учётом всего, сказанного выше):

  1. Введена переменная "timer_count", в которой накапливаются "тики" таймера и ещё одна переменная - "timer_sec" - счётчик секунд. После каждого 18-го "тика" счётчик секунд увеличивается на 1. В качестве часов данный пример не совсем годится, т.к. за одну секунду таймер выдаёт около 18.2 "тиков" (если его дополнительно не программировать), а данный пример предназначен в качестве иллюстрации обработки IRQ и поэтому подсчёт времени здесь упрощённый.
  2. Макрос "IRQ_0_handler" изменён - он считает "тики" таймера. Теперь это не заглушка, а Обработчик Прерывания.
  3. Перед тем, как в программе будут разрешены прерывания (командой STI), размаскировывается IRQ 0 (а так же и IRQ 1, для корректной обработки контроллера клавиатуры).
  4. В программе приводится простой алгоритм, в котором на экран выводится dd-число, которое в бесконечном цикле увеличивается на +1. При это постоянно проверяется содержимое переменной "timer_count" и:

    • сбрасывается в 0, как только она превышает 18
    • при этом увеличивается на 1 переменная "timer_sec"
    • как только она превысит значение 4, производится возврат в R-Mode.

Вот так теперь выглядит обработчик IRQ 0:

    push    ax
    mov     al,20h
    out     20h,al
    pop     ax
    inc     timer_count
    iret

Как видите, всё что он делает - это посылает контроллеру прерываний команду конца прерывания (EOI) и увеличивает значение "timer_count" на 1. И всё! Так просто!

На самом деле, когда вы будете писать свою ОС, то, скорее всего, добавите в обработчик IRQ 0 функции подсчёта времени, даты и ещё что-нибудь важное, но даже в таком виде он будет корректно работать.

А вот так в примере разрешены прерывания и реализован алгоритм подсчёта и вывода времени:

Код опущен

Осталось добавить, что этот пример, как и предыдущий, правильно реагирует на исключения - выводит его номер, параметры и возвращается в R-Mode, так что можете смело экспериментировать - компьютер не зависнет.

Процедуры смотреть здесь.