5.8.2. Прерывания от внешних устройств

Прерывания от внешних устройств, или аппаратные прерывания, - это то, что понимается под термином «прерывание». Внешние устройства (клавиатура, диско­вод, таймер, звуковая карта и т. д.) подают сигнал, по которому процессор преры­вает выполнение программы и передает управление на обработчик прерывания. Всего на персональных компьютерах используется 15 аппаратных прерываний, хотя теоретически возможности архитектуры позволяют довести их число до 64.

Рассмотрим их кратко в порядке убывания приоритетов («прерывание имеет более высокий приоритет» означает, что, пока не завершился его обработчик, пре­рывания с низкими приоритетами будут ждать своей очереди):

□ IRQO (INT 8) - прерывание системного таймера, вызывается 18,2 раза в се­кунду. Стандартный обработчик этого прерывания вызывает INT tCh при каждом вызове, так что, если программе необходимо только регулярно полу­чать управление, а не перепрограммировать таймер, рекомендуется исполь­зовать прерывание 1 Ch;

□ IRQ1 (INT 9) - прерывание клавиатуры, вызывается при каждом нажатии и отпускании клавиши на клавиатуре. Стандартный обработчик этого преры­вания выполняет довольно много функций, начиная с перезагрузки по Ctrl­' Alt-Del и заканчивая помещением кода клавиши в буфер клавиатуры BIOS;

□ IRQ2 - к этому входу на первом контроллере прерываний подключены ап­паратные прерывания IRQ8 - IRQ15, но многие BIOS перенаправляют IRQ9 на INT OAh;

□ IRQ8 (INT 70h) - прерывание часов реального времени, вызывается часами реального времени при срабатывании будильника и если они установлены на генерацию периодического прерывания (в последнем случае IRQ$ вызы­вается 1024 раза в секунду);

(INT INT        - прерывание обратного хода луча, вызывается

некоторыми видеоадаптерами при обратном ходе луча. Часто используется дополнительными устройствами (например, звуковыми картами, SCSI-адап-терами и т. д.);

□ IRQ10 (INT 72h) - используется дополнительными устройствами;

□ IRQU (INT 73h) - используется дополнительными устройствами; '■

□ IRQ12 (INT 74h) - мышь на системах PS, используется дополнительными устройствами;

□ IRQ13 (INT 02h или INT 75h) - ошибка математического сопроцессора. По

умолчанию это прерывание отключено как на FPU, так и на контроллере прерываний;

□ IRQ14 (INT 76h) - прерывание первого IDE-контроллера «операция завер­шена»;

□ IRQ15 (INT 77h) - прерывание второго IDE-контроллера «операция завер­шена»;

□ IRQ3 (INT OBh) - прерывание последовательного порта COM2, вызывает­ся, если порт COM2 получил данные;

□ IRQ4 (INT OCh) - прерывание последовательного порта СОМ1, вызывается, если порт получил данные;

□ IRQ5 (INT ODh) - прерывание LPT2, используется дополнительными уст­ройствами;

□ IRQ6 (INT OEh) - прерывание дисковода «операция завершена»;

□ IRQ7 (INT OFh) - прерывание LPT1, используется дополнительными уст­ройствами.

Самые полезные для программ аппаратные прерывания - прерывания систем­ного таймера и клавиатуры. Так как стандартные обработчики этих прерываний выполняют множество функций, от которых зависит работа системы, их нельзя заменять полностью, как мы поступали с обработчиком INT 5. Необходимо вы­звать предыдущий обработчик, передав ему управление следующим образом

(если его адрес сохранен в переменной old_handler - см. примеры ранее):

pushf

call old_handler

Данные команды выполняют действие, аналогичное команде INT (сохранить флаги в стеке и передать управление подобно команде call), поэтому, когда обра­ботчик завершится командой IRET, управление вернется в нашу программу. Так удобно вызывать предыдущий обработчик в начале собственного. Другой способ -простая команда jmp:

jmp cs:old_handler

приводит к тому, что по выполнении команды IRET старым обработчиком управле­ние сразу же перейдет к прерванной программе. Этот способ применяют, если нужно, чтобы сначала отработал новый обработчик, а потом он передал управление старому. На следующем примере посмотрим, как осуществляется перехват прерывания

от таймера:

; timer.asm

; Демонстрация перехвата прерывания системного таймера: вывод текущего времени ;  в левом углу экрана.

tiny

 

. code

 

 

 

org

 

start                proc near

 

;   Сохранить адрес предыдущего обработчика

прерывания

-

; AH = 35h, AL = номер прерывания.

int 21h

; Функция DOS: определить адрес обработчика

mov       word ptr old_intlCh,bx

прерывания

mov       word ptr old_int1Ch+2,es

:   (возвращается в ES:BX).

; Установитв наш обработчик.

 

mov

; АН = 25h, AL = номер прерывания.

mov       dx, offset intlCh handler

; DS:DX - адрес обработчика.

int 21h

; Установитв обработчик прерывания 1Сп.

; Здесь размещается собственно программа,

например вызов command.com.

mov       ah,1

 

int 21h

;  Ожидание нажатия на любую клавишу.

; Конец программы.

; Восстановитв предыдущий обработчик прерывания 1СЬ.

шоу        ах,251Сп ;  АН = 25й,  АИ = номер прерывания.

шоу        Сх^о^ рРг оЫ_М1СЬ+2


шш

mm

mov

ds, dx

mov

dx.word

int

21h '

ret

 

приемы

DS'.OX адрес обработчика.

old_int1Ch

start_position

start

dd dw

endp

Здесь хранится адрес предыдущего обработчика. Позиция на экране,   в которую выводится текущее время.

Обработчик для прерывания 1Сп.

Выводит текущее время в позицию ^^^^роз111оп на экране (только текстовом режиме).

intlChjiandler

proc far

 

 

pusha

Обработчик аппаратного прерывания

push

es                        должен сохранять

ВСЕ регистры.

push

ds

 

 

push

cs                        На входе

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

pop

ds значение

регистра

CS.

mov

ah,02h       ■        ; Функция

прерывания

int

Чтение времени из RTC.

jc

exit_handler        ; Если часы заняты

- в другой раз.

 

; AL = час

в BCD-формате.

call

bcd2asc

;

Преобразовать в ASCII.

mov

byte ptr output_line[2],ah

;

Поместить их в

mov

byte  ptr output_line[4],al

;

строку output_line.

mov

al.cl

;

CL = минута в BCD-формате.

call

bcd2asc

 

 

mov

byte ptr output_line[10],ah

 

 

mov

byte ptr output_line[12]',al

 

 

mov

al.dh

. ;

DH = секунда в BCD-формате.

call

bcd2asc

 

 

mov

byte ptr output_line[16],ah

 

 

mov

byte ptr output_line[18],al

 

 

mov

cx,output_line_l

;

Число байтов в строке - в С

push

0В80ОҐ)

 

 

pop

es

 

Адрес в видеопамяти -

mov

di,word ptr start_position

 

в

mov

si,offset output_line

 

Адрес строки в DS:SI.

eld

 

 

 

rep

movsb

 

Скопировать строку.

exit_handler:

 

 

 

pop

ds

 

Восстановить все регистры.

pop

es

 

 

popa

 

 

 

jmp

Передать

управление предыдущему

Процедура bcd2asc.

Преобразует старшую цифру упакованного BCD-числа из AL в ASGII-символ,

в

Перехват прерываний |

;  который будет помещен в АН, а младшую цифру - в ASCII-символ.в AL.

bcd2asc

proc near

 

 

mov

ah, al

 

 

and

al,OFh

; Оставить младшие

4 бита в AL.

shr

ah,4

; Сдвинуть старшие

4 бита в АН.

or

ax,3030h

; Преобразовать в

ASCII-символы.

ret

 

 

 

bcd2asc

endp

 

 

; Строка " OOh

00:00" с атрибутом IFh (белый

на синем) после каждого

символа.

output_line

db         '  ' ,1Fh,'0', IFh.'O',

1Fh,'h',1Fh

 

 

db         '  MFh.'O', 1 Fh, '0',

1Fh,':',1Fh

 

 

db         ' 0' , 1Fh, ' 0', IFh,' ',

1Fh

 

output_line_l

equ $-output_line

 

 

int1Ch_handler

endp

 

 

end

start

 

 

Если в этом примере вместо ожидания нажатия на клавишу поместить какую-нибудь программу, работающую в текстовом режиме, например tinyshell из разде­ла 4.10, она выполнится как обычно, но в правом верхнем углу будет постоянно показываться текущее время, то есть такая программа будет осуществлять два действия одновременно. Именно для этого и применяется механизм аппаратных прерываний - они позволяют процессору выполнять одну программу, в то время как ' отдельные программы следят за временем, считывают символы из клавиату­ры и помещают их в буфер, получают и передают данные через последовательные и параллельные порты и даже обеспечивают многозадачность, переключая про­цессор между разными задачами по прерыванию системного таймера.

Разумеется, обработка прерываний не должна занимать много времени: если прерывание происходит достаточно часто (например, прерывание последователь­ного порта может происходить 28 800 раз в секунду), его обработчик обязательно должен выполняться за более короткое время. Если, например, обработчик пре­рывания таймера будет выполняться 1/32,4 секунды, то есть половину времени между прерываниями, вся система станет работать в два раза медленнее. А если еще одна программа с таким же долгим обработчиком перехватит это прерыва­ние, система остановится совсем. Именно поэтому обработчики прерываний при­нято писать исключительно на ассемблере.