Прерывания в защищённом режиме:

Глава 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:


    store_R_Mode_IRQ_Mask proc near ; Сохраняет значение маски IRQ в переменную R_Mode_IRQ_Mask для корректного ; восстановления IRQ при возврате в R-Mode. in al,0a1h mov ah,al in al,21h mov R_Mode_IRQ_Mask,ax ret endp

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


    init_R_Mode_redirect_IRQ macro R_Mode_redirect_IRQ proc near mov bx,7008h mov dx,R_Mode_IRQ_Mask ; Вот эту команду мы используем, ; вместо MOV DX,0. call redirect_IRQ cmp APIC_presence,1 jne rmrirq_1 call enable_APIC rmrirq_1: ret endp endm

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


    IRQ_1_handler macro push ax in al,60h ; AL содержит скан-код клавиатуры, но в ; этом примере он не сохраняется - ; обработчик IRQ 1 работает как заглушка. in al,61H mov ah,al or al,80h out 61H,al xchg ah,al out 61H,al mov al,20h out 20h,al pop ax iret endm

            Как видите, установка обработчика 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:


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

            Как видите, всё что он делает - это посылает контроллеру прерываний команду конца прерывания (EOI) и увеличивает значение "timer_count" на 1. И всё! Так просто!
            На самом деле, когда вы будете писать свою ОС, то, скорее всего, добавите в обработчик IRQ 0 функции подсчёта времени, даты и ещё что-нибудь важное, но даже в таком виде он будет корректно работать.

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


    ; Демонстрация работы прерывания по таймеру in al,21h and al,11111100b ; Размаскируем прерывания таймера ; и клавиатуры. out 21h,al mov al,0 mov timer_count,al ; Сбрасываем наши счётчики mov timer_sec,al xor eax,eax ; Число в EAX будет выводится на экран ; в бесконечном цикле. sti ; Разрешаем аппаратные прерывания. Теперь наш ; бесконечный цикл будет "рваться" таймером и ; клавиатурой и остаётся только следить за ; счётчиками. timer_demo_start: mov dx,1400h ; В 20-ю строку, нулевую позицию ... call put_dd_num ; ... будет выводится dd-число. inc eax ; А здесь оно увеличивается. cmp timer_count,18 jb timer_demo_start ; Выводим число, пока timer_count < 18 mov timer_count,0 ; timer_count достиг 18. ; Сбрасываем его. push ax mov al,timer_sec inc al mov timer_sec,al mov dx,1410h call put_db_num ; Выводим число секунд. pop ax cmp timer_sec,4 jbe timer_demo_start ; Продолжаем цикл, если timer_sec < 4 jmp Return_to_R_Mode

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

            Исходный текст примера вы можете скачать здесь: examp_6.asm, pmode_6.lib и examp_6.com в архиве examp_6.zip (9594 байт).

      Оглавление Вопросы? Замечания? Пишите: sasm@narod.ru

      Copyright © Александр Семенко.
    TopList

    Hosted by uCoz