Какое прерывание имеет таймер драйвер мышь клавиатуры
Для работы с системными часами используется прерывание 8h. По мимо установки времени и прочих функций, оно вызывает прерывание 1Ch с периодичностью работы системного таймера (приблизительно, 55 раз в секунду). Обработчик данного прерывания по умолчанию не делает ничего, имея в своем теле только команду iret, фактически представляет собой "заглушку", таким образом предоставляя разработчикам возможность безболезненно реализовать обработчик прерываний таймера для своих нужд.
Обработчик прерывания работает следующим образом. Считается количество вызовов 1Ch. Если оно становится большим или равным некоторому значению, установленному в переменной loval_factor, изменяется значение флага flag, после чего счетчик local_counter обнуляется. В противном случае, значение local_counter увеличивается на 1. Сброс флага осуществляется самим приложением.
local_counter db 0 ; счетчик "тиков", то есть то количество раз, сколько было вызвано прерывание 1Ch
local_factor db 0 ; то количество вызовов прерывания, после которого необходимо изменить флаг
flag db 0 ; флаг достижения нужного количества вызовов прерывания, значит достигнута необходимая задержка
cli
push dx
push ax
xor ax,ax
mov al,[cs:local_counter] ; сравним значение счетчика с переменной local_factor
cmp al,[cs:local_factor]
jb exit1 ; если меньше, счетчик увеличиваем на 1
mov [cs:flag],0ffh ; иначе устанавливаем флаг
mov [cs:local_counter],0 ; и обнуляем счетчик
jmp exit2
exit1:
inc al
mov [cs:local_counter],al ; инкремент счетчика
exit2:
pop ax
pop dx
sti
iret
Само приложение должно работать следующим образом. Пока значение флага не изменилось, выполняется цикл проверки. Как только значение флага изменилось, следовательно достигнута некоторая задержка и можно работать. Значение самого флага должно быть обнулено.
main_loop:
cmp [flag],0ffh ; крутимся в цикле, пока обработчик прерывания не установит флаг
jne main_loop
mov [flag],0 ; сбросим флаг
.
jmp main_loop ; вернемся в цикл
Имеются другие варианты работы с таймером. Например, опрос двойного слова по адресу 0040h:006сh. Оно содержит количество вызовов прерывания 8h (и 1Ch соответственно), прошедших с полуночи. В полночь данный счетчик обнуляется.
Другой вариант - это программирование аппаратного таймера через порты ввода-вывода. Описание этой процедуры выходит за рамки данной статьи. Простейший способ состоит в том, чтобы опрашивать порт 43h и в случае изменения значения байта, прочитанного из этого порта, констатировать соответствующую задержку.
Существует также способ фиксирования времени на основе подсчета тактов ЦП. В "современных" процессорах, начиная с P-III присутствует ассемблерная инструкция, позволяющая получить количество тактов процессора со времени последнего рестарта - rdtsc. Ее опкод равен 0f31h. Количество тактов процессора возвращается в паре EDX:EAX. Для оценки реального времени, необходимо получить частоту работы процессора, которую можно получить с помощью команды cpuid. Ее краткое описание дано здесь, а также в стандарте Intel, который приведен в приложении.
итак, чево я добился, и как я вижу это дело:
- сохраняем стандартный 9-ый вектор перерываний
- заменяем ево на свой, в котором пишем обработчик
- перед выходом из программы восстанавливаем стандартный
в своем обработчике:
- считываем скан-коды из 60 порта
- . тут ступор, нада как то обрабатывать все полученные скан-коды, преобразовывать в ASCII коды, кудато передавать, кудато записывать, штоб программы их использовали, не совсем понимаю если честно
- если нажата определенная комбинация клавиш - восстанавливаем стандартный обработчик и выходим
- пишем в 20 порт значение 20h, разрешая дальнейшие прерывания
вот тот кривой косой код который сваял на данный момент, жду подсказок критики по делу, и тд и тп
void interrupt (*OldInt09h) (. ); /* старый обработчик */
void interrupt NewInt09h (. ) /* новый обработчик */
Key=inportb(0x60); // считываем скан-код нажатой клавиши из 60-го порта
cout<<Key<<" "; //ето временно сделал чтоб посмотреть что я получаю
if (Key==01) // пока что выходим по нажатии Esc
< setvect(9,OldInt09h);
puts("\nВектор прерываний INT 09h восстановлен.");
puts("Нажмите любую клавишу чтобы выйти.\n");
>
outportb(0x20,0x20); //разрешаем следующие прерывания
>
int main ()
< clrscr();
OldInt09h=getvect(9);
puts("Вектор прерываний INT 09h сохранён.");
setvect(9,NewInt09h);
puts("Вектор прерываний INT 09h изменён.\n");
getch();
return 0;
>
ideal
model tiny
codeseg
org 100h
start:
mov ax,3509h
int 21h ; AL=vector to get ES:BX=return vector
mov [oldofs],bx
mov ax,es
mov [oldseg],ax
;-------------------------------------
mov ah,9
mov dx,offset mess
int 21h
;-------------------------------------
mov ax,2509h
mov dx,offset int9
int 21h ; AL=int num to set DS:DX=new handler
;---------- Настройка клавиатуры ---------------------------
mov al,0f3h
out 60h,al
call wait_kbd
mov al,01111111B ; Slower keyboard
out 60h,al
;-------------------------------------
jmp $ ; Main program ;-)
mov dx,[oldofs] ;
mov ax,[oldseg] ;
mov ds,ax ;
mov ax,2509h ;
int 21h ; AL=int num to set DS:DX=new handler
mov al,20h
out 20h,al ; EOI
out 0a0h,al ; secondary
;-------------------------------------
mov al,0f3h
out 60h,al
call wait_kbd
mov al,0
out 60h,al ; Faster keyboard
ret
;-------------------------------------
int9:
in al,60h
mov bl,al
in al,61h
mov ah,al
or al,80h
out 61h,al
xchg ah,al
out 61h,al
test bl,80h ; something pressed
jz obrab
cmp bl,81h ; escape depressed
jne zero_cl
inc cl
cmp cl,3 ; Depressed
je exit
jmp obrab
zero_cl:
mov cl,0
obrab:
;-------------------------------------
push ax
shr al,4
call hex
pop ax
and al,0fh
call hex
mov dl,' '
call print
send_eoi:
mov al,20h
out 20h,al ; EOI signal
out 0a0h,al ; secondary
iret
;--------------- Вывод байта на экран ---------------------------------------
hex:
add al,'0'
cmp al,'9'
jbe nol
add al,'A'-'9'-1
nol:
mov dl,al
print:
mov ah,6
int 21h
ret
;------------------- Ожидание готовности клавиатуры ------------------
wait_kbd:
push cx
xor cx,cx
wk:
in al,64h
test al,10b
loopnz wk
pop cx
ret
;-------------------------------------
mess db 13,10,'Keyboard scancode viewer version 1.',13,10
db 'Copyright (c) 2006, ExTRA Laboratories [Vov4ik].',13,10
db 'Press ESCAPE three times to exit.',13,10,'$'
oldseg dw ?
oldofs dw ?
end start
для меня эта работа важна только ради сдать (это долг за прошлый семестр) поэтому я не вижу смысла заморачиваться с ассемблером, так как нам целиком и полностью разрешено использовать си + асм вставки, ладно еслиб это была программа для работы тд, а так это одноразовый труд
я бы сделал так
адрес старого прерывания у нас есть
поэтому, в нашем новом прерывании обрабатываем нажатие , а затем вызываем старое и пучкай оно дальше трудится
порылся в литературе , нашел алгоритм работы сего девайса
Перепрограммирование прерывания клавиатуры.
Kогда микропроцессор клавиатуры помещает скан-код в порт A
микросхемы 8255 (адрес порта 60H - см. [1.1.1]), то при этом
вызывается прерывание 9. Задача этого прерывания - преобразовать
скан-код символа, основываясь на состоянии клавиш-переключателей,
и поместить его в буфер клавиатуры. (Если скан-код соответствует
клавише-переключателю, то в буфер клавиатуры не пишется ничего,
за исключением случая клавиши Ins, а вместо этого прерывание
изменяет байты статуса, расположенные в области данных BIOS
[3.1.7]). Прерывания "ввода с клавиатуры" DOS и BIOS на самом
деле всего лишь прерывания "ввода из буфера клавиатуры". Hа самом
деле они не распознают нажатия клавиш. Точнее, они читают интерп-
ретацию введенных клавиш, которую обеспечило прерывание 9. Заме-
тим, что PCjr использует специальную процедуру (INT 48H) для
преобразования ввода от его 62 клавиш к 83-клавишному протоколу,
используемому другими IBM PC. Результат этой процедуры передается
прерыванию 9, которое выполняет свою работу как обычно. Прерыва-
нием 49H PCjr обеспечивает специальные неклавишные скан-коды,
которые потенциально могут устанавливаться периферийными уст-
ройствами, использующими инфракрасную (беспроволочную) связь с
клавиатурой.
Требуется весьма необычное применение, чтобы имело смысл пе-
репрограммировать это прерывание, особенно учитывая, что MS DOS
позволяет Вам перепрограммировать любую клавишу клавиатуры
[3.2.6]. Если все же Вам придется перепрограммировать прерывание
9, то эта глава даст Вам основы для старта. Сначала надо прочи-
тать [1.2.3], чтобы понимать как программируются прерывания. В
прерывании клавиатуры можно выделить три основных шага:
1. Прочитать скан-код и послать клавиатуре подтвердающий сиг-
нал.
2. Преобразовать скан-код в номер кода или в установку оегист-
ра статуса клавиш-переключателей.
3. Поместить код клавиши в буфер клавиатуры.
В момент вызова прерывания скан-код будет находиться в порте
A. Поэтому сначала надо этот код прочитать и сохранить на стеке.
Затем используется порт B (адрес 61H), чтобы быстро послать сиг-
нал подтверждения микропроцессору клавиатуры. Hадо просто устано-
вить бит 7 в 1, а затем сразу изменить его назад в 0. Заметим,
что бит 6 порта B управляет сигналом часов клавиатуры. Он всегда
должен быть установлен в 1, иначе клавиатура будет выключена. Эти
адреса портов применимы и к AT, хотя он и не имеет микросхемы
интерфейса с периферией 8255.
Сначала скан-код анализируется на предмет того, была ли клави-
ша нажата (код нажатия) или отпущена (код освобождения). Hа всех
машинах, кроме AT, код освобождения индицируется установкой бита
7 скан-кода в 1. Для AT, у которого бит 7 всегда равен 0, код
освобождения состоит из двух байтов: сначала 0F0H, а затем
скан-код. Все коды освобождения отбрасываются, кроме случая кла-
виш-переключателей, для которых делаются соответствующие измене-
ния в байтах их статуса. С другой стороны, все коды нажатия обра-
батываются. При этом опять могут изменяться байты статуса кла-
виш-переключателей. В случае же символьных кодов, надо проверять
байты статуса, чтобы определить, например, что скан-код 30 соот-
ветствует нижнему или верхнему регистру буквы A.
После того как введенный символ идентифицирован, процедура
ввода с клавиатуры должна найти соответствующий ему код ASCII или
расширенный код. Приведенный пример слишком короток, чтобы рас-
смотреть все случаи. В общем случае скан-коды сопоставляются
элементам таблицы данных, которая анализируется инструкцией XLAT.
XLAT принимает в AL число от 0 до 255, а возвращает в AL 1-байт-
ное значение из 256-байтной таблицы, на которую указывает DS:BX.
Таблица может находиться в сегменте данных. Если в AL находился
скан-код 30, то туда будет помещен из таблицы байт номер 30 (31-й
байт, так как отсчет начинается с нуля). Этот байт в таблице
должен быть установлен равным 97, давая код ASCII для "a". Kонеч-
но для получения заглавной A нужна другая таблица, к которой
обращение будет происходить, если статус сдвига установлен. Или
заглавные буквы могут храниться в другой части той же таблицы, но
в этом случае к скан-коду надо будет добавлять смещение, опреде-
ляемое статусом клавиш-переключателей.
Hаконец, номера кодов должны быть помещены в буфер клавиатуры.
Процедура должна сначала проверить, имеется ли в буфере место для
следующего символа. В [3.1.1] показано, что этот буфер устроен
как циклическая очередь. Ячейка памяти 0040:001A содержит указа-
тель на голову буфера, а 0040:001C - указатель на хвост. Эти
словные указатели дают смещение в области данных BIOS (которая
начинается в сегменте 40H) и находятся в диапазоне от 30 до 60.
Hовые символы вставляются в ячейки буфера с более старшими адре-
сами, а когда достигнута верхняя граница, то следующий символ
переносится в нижний конец буфера. Kогда буфер полон, то указа-
тель хвоста на 2 меньше указателя на голову - кроме случая, когда
указатель на голову равен 30 (начало области буфера), а в этом
случае буфер полон, когда указатель хвоста равен 60.
Для вставки символа в буфер, надо поместить его в позицию, на
которую указывает хвост буфера и затем увеличить указатель хвоста
на 2; если указатель хвоста был равен 60, то надо изменить его
значение на 30. Вот и все. Схема прерывания клавиатуры показана
на рис. 3-4.
Прерывания от внешних устройств, или аппаратные прерывания — это то, что понимается под термином «прерывание». Внешние устройства (клавиатура, дисковод, таймер, звуковая карта и т.д.) подают сигнал, по которому процессор прерывает выполнение программы и передает управление на обработчик прерывания. Всего на персональных компьютерах используется 15 аппаратных прерываний, хотя теоретически возможности архитектуры позволяют довести их число до 64.
Рассмотрим их кратко в порядке убывания приоритетов (прерывание имеет более высокий приоритет, и это означает, что, пока не завершился его обработчик, прерывания с низкими приоритетами будут ждать своей очереди).
IRQ0 (INT 8) — прерывание системного таймера. Это прерывание вызывается 18,2 раза в секунду. Стандартный обработчик этого прерывания вызывает INT 1Ch при каждом вызове, так что, если программе необходимо только регулярно получать управление, а не перепрограммировать таймер, рекомендуется использовать прерывание 1Ch.
IRQ1 (INT 9) — прерывание клавиатуры. Это прерывание вызывается при каждом нажатии и отпускании клавиши на клавиатуре. Стандартный обработчик этого прерывания выполняет довольно много функций, начиная с перезагрузки по Ctrl-Alt-Del и заканчивая помещением кода клавиши в буфер клавиатуры BIOS.
IRQ2 — к этому входу на первом контроллере прерываний подключены аппаратные прерывания IRQ8 – IRQ15, но многие BIOS перенаправляют IRQ9 на INT 0Ah.
IRQ8 (INT 70h) — прерывание часов реального времени. Это прерывание вызывается часами реального времени при срабатывании будильника и если они установлены на генерацию периодического прерывания (в последнем случае IRQ8 вызывается 1024 раза в секунду).
IRQ9 (INT 0Ah или INT 71h) — прерывание обратного хода луча. Вызывается некоторыми видеоадаптерами при обратном ходе луча. Часто используется дополнительными устройствами (например, звуковыми картами, SCSI-адаптерами и т.д.).
IRQ10 (INT 72h) — используется дополнительными устройствами.
IRQ11 (INT 73h) — используется дополнительными устройствами.
IRQ12 (INT 74h) — мышь на системах PS используется дополнительными устройствами.
IRQ13 (INT 02h или INT 75h) — ошибка математического сопроцессора. По умолчанию это прерывание отключено как на FPU, так и на контроллере прерываний.
IRQ14 (INT 76h) — прерывание первого IDE-контроллера «операция завершена».
IRQ15 (INT 77h) — прерывание второго IDE-контроллера «операция завершена».
IRQ3 (INT 0Bh) — прерывание последовательного порта COM2 вызывается, если порт COM2 получил данные.
IRQ4 (INT 0Ch) — прерывание последовательного порта СОМ1 вызывается, если порт СОМ1 получил данные.
IRQ5 (INT 0Dh) — прерывание LPT2 используется дополнительными устройствами.
IRQ6 (INT 0Eh) — прерывание дисковода «операция завершена».
IRQ7 (INT 0Fh) — прерывание LPT1 используется дополнительными устройствами.
Самые полезные для программ аппаратные прерывания — прерывания системного таймера и клавиатуры. Так как их стандартные обработчики выполняют множество функций, от которых зависит работа системы, их нельзя заменять полностью, как мы делали это с обработчиком INT 5. Следует обязательно вызвать предыдущий обработчик, передав ему управление следующим образом (если его адрес сохранен в переменной old_handler, как в предыдущих примерах):
Эти две команды выполняют действие, аналогичное команде INT (сохранить флаги в стеке и передать управление подобно команде call), так что, когда обработчик завершится командой IRET, управление вернется в нашу программу. Так удобно вызывать предыдущий обработчик в начале собственного. Другой способ — простая команда jmp:
приводит к тому, что, когда старый обработчик выполнит команду IRET, управление сразу же перейдет к прерванной программе. Этот способ применяют, если нужно, чтобы сначала отработал новый обработчик, а потом он передал бы управление старому.
Посмотрим, как работает перехват прерывания от таймера на следующем примере:
Если в этом примере вместо ожидания нажатия на клавишу поместить какую-нибудь программу, работающую в текстовом режиме, например tinyshell из главы 1.3, она выполнится как обычно, но в правом верхнем углу будет постоянно показываться текущее время, то есть такая программа будет осуществлять два действия одновременно. Именно для этого и применяется механизм аппаратных прерываний — они позволяют процессору выполнять одну программу, в то время как отдельные программы следят за временем, считывают символы из клавиатуры и помещают их в буфер, получают и передают данные через последовательные и параллельные порты и даже обеспечивают многозадачность, переключая процессор между разными задачами по прерыванию системного таймера.
Разумеется, обработка прерываний не должна занимать много времени: если прерывание происходит достаточно часто (например, прерывание последовательного порта может происходить 28 800 раз в секунду), его обработчик обязательно должен выполняться за более короткое время. Если, например, обработчик прерывания таймера будет выполняться 1/32,4 секунды, то есть половину времени между прерываниями, вся система будет работать в два раза медленнее. А если еще одна программа с таким же долгим обработчиком перехватит это прерывание, система остановится совсем. Именно поэтому обработчики прерываний принято писать исключительно на ассемблере.
В этом разделе мы подробно рассмотрим одно из важнейших устройств персонального компьютера - клавиатуру. Практически ни одна программа не обходится без обращения к клавиатуре.
Программа может использовать клавиатуру по-разному. Она может задерживать свое выполнение до тех пор, пока пользователь не введет какое-нибудь число или пока не нажмет какую-нибудь клавишу. Выполняя некоторую работу, программа может периодически проверять, была ли нажата клавиша, изменяющая режим работы программы. Резидентные программы могут активизироваться, когда пользователь нажимает заранее определенную комбинацию. Можно использовать прерывание, вырабатываемое клавиатурой, например, для завершения работы программы.
Мы расскажем о работе с клавиатурой в среде MS-DOS на разных уровнях - от использования клавиатурных портов ввода/вывода до средств, предоставляемых стандартными библиотеками трансляторов C. Какой уровень вам следует выбрать, зависит от решаемой задачи. Единственное, что можно порекомендовать - это использовать по возможности средства высокого уровня. Если ваша программа обращается к клавиатуре через порты ввода/вывода, ее работоспособность может оказаться зависимой от типа клавиатуры и от модели компьютера.
Что же касается методики работы с клавиатурой в операционных системах Microsoft Windows, то она была описана в 11 томе «Библиотеки системного программиста», который называется «Операционная система Microsoft Windows для программиста».
Как работает клавиатура
Клавиатура выполнена, как правило, в виде отдельного устройства, подключаемого к компьютеру тонким кабелем. Малогабаритные блокнотные компьютеры содержат встроенную клавиатуру.
Что же находится внутри клавиатуры?
Оказывается, там есть компьютер! Только этот компьютер состоит из одной микросхемы и выполняет специализированные функции. Когда вы нажимаете на клавиши, он посылает номер нажатой клавиши в центральный компьютер.
Клавиатурная матрица
Если рассмотреть сильно упрощенную принципиальную схему клавиатуры, представленную на рисунке, можно заметить, что все клавиши находятся в узлах матрицы (рис. 2.1).
Рис.2.1. Упрощенная схема клавиатуры
Устанавливая по очереди на каждой из вертикальных линий уровень напряжения, соответствующий логическому нулю, клавиатурный компьютер опрашивает состояние горизонтальных линий. Если ни одна клавиша не нажата, уровень напряжения на всех горизонтальных линиях соответствует логической единице (так как все эти линии подключены к источнику питания +5 В через резисторы).
Если вы нажмете на какую-либо клавишу, то соответствующая вертикальная и горизонтальная линии окажутся замкнутыми. Когда на этой вертикальной линии процессор установит значение логического нуля, то уровень напряжения на горизонтальной линии также будет соответствовать логическому нулю.
Как только на одной из горизонтальных линий появится уровень логического нуля, клавиатурный процессор фиксирует нажатие на клавишу. Он посылает в центральный компьютер запрос на прерывание и номер клавиши в матрице. Аналогичные действия выполняются и тогда, когда вы отпускаете нажатую ранее клавишу.
Скан-код клавиши
Номер клавиши, посылаемый клавиатурным процессором, однозначно зависит от схемы клавиатурной матрицы, но не от обозначений, нанесенных на поверхность клавиш. Этот номер называется скан-кодом (Scan Code). Слово scan ("сканирование"), подчеркивает тот факт, что клавиатурный компьютер сканирует клавиатуру для поиска нажатой клавиши.
Код ASCII нажатой клавиши
Обычно программе нужен не порядковый номер нажатой клавиши, а код, соответствующий обозначению на этой клавише (код ASCII).
Код ASCII не связан напрямую со скан-кодом, так как одной и той же клавише могут соответствовать несколько значений кода ASCII в зависимости от состояния других клавиш. Например, клавиша с обозначением «1» используется еще и для ввода символа «!» (если она была нажата вместе с клавишей <Shift>).
Поэтому все преобразования скан-кода в код ASCII выполняются программно. Как правило, в операционной системе MS-DOS эти преобразования выполняют модули BIOS. Для использования символов кириллицы эти модули расширяются клавиатурными драйверами, как входящими в состав локализованных версий MS-DOS, так и созданными в виде отдельных программ.
Режим автоповтора
Если нажать на клавишу и не отпускать ее, клавиатура перейдет в режим автоповтора. При этом в компьютер автоматически через некоторый период времени, называемый периодом автоповтора, посылается код нажатой клавиши. Режим автоповтора облегчает ввод с клавиатуры большого количества одинаковых символов.
Следует отметить, что клавиатура содержит внутренний 16-байтовый буфер, через который она осуществляет обмен данными с компьютером.
Типы клавиатур
До недавнего времени существовали три различных типа клавиатуры. Это клавиатура для компьютеров IBM PC/XT, 84-клавишная клавиатура для IBM PC/AT и 101-клавишная (расширенная) клавиатура для IBM PC/AT. Некоторые клавиатуры имеют переключатель режима работы (XT/AT), расположенный на нижней крышке. Он должен быть установлен в правильное положение.
После того как операционная система Microsoft Windows получила широкое распространение, специально для нее был создан новый тип клавиатуры. К обычной клавиатуре типа IBM PC/AT были добавлены две кнопки, первая из которых дублирует вызов меню Start, выполняемый при помощи левой клавиши мыши, а вторая – вызов того же меню при помощи правой клавиши мыши.
Порты для работы с клавиатурой
Назначение портов, предназначенных для работы с клавиатурой, зависят от типа компьютера.
Компьютер IBM PC/XT
Для работы с клавиатурой типа IBM PC/XT используются порты с адресами 60h и 61h.
Порт 60h доступен только для чтения. После выполнения этой операции он содержит скан-код последней нажатой клавиши.
Порт 61h доступен как для чтения, так и для записи. Он управляет не только клавиатурой, но и другими устройствами компьютера, например, работой встроенного динамика. Если в старший бит порта 61h записать значение 1, клавиатура будет заблокирована, если 0 - разблокирована.
Так как порт 61h управляет не только клавиатурой, при изменении содержимого старшего бита необходимо сохранить состояние остальных битов этого порта. Для этого можно сначала выполнить чтение содержимого порта в регистр, изменить состояние старшего бита, затем выполнить запись нового значения в порт:
Современные компьютеры
Современные компьютеры позволяют управлять скоростными характеристиками клавиатуры, а также зажигать или гасить светодиоды Scroll Lock, Num Lock и Caps Lock, расположенные на лицевой панели клавиатуры.
Для расширенного управления клавиатурой применяется порт 60h в режиме записи. Этот порт служит для управления подчиненным процессором Intel 8042, ответственным за обмен данными с клавиатурным компьютером, или его аналогом, установленным на системной плате компьютера.
При использовании порта 60h на запись программа дополнительно получает следующие возможности:
· установка времени ожидания перед переходом клавиатуры в режим автоповтора;
· установка периода генерации скан-кода в режиме автоповтора;
· управление светодиодами, расположенными на лицевой панели клавиатуры.
Процессор 8042 обслуживает не только клавиатуру, но и другие системы компьютера. Через порт 64h, например, выполняется сброс (отключение) процессора 80286 для возврата из защищенного режима работы в реальный. Подробности об этом вы можете узнать из 6 тома «Библиотеки системного программиста», который называется «Защищенный режим процессоров Intel 80286/80386/80486».
Перед тем как посылать команду процессору 8042, необходимо убедиться в том, что его внутренняя очередь команд пуста. Это можно сделать, прочитав слово состояния 8042 из порта с адресом 64h. Бит с номером 1 должен быть равен нулю.
Приведем фрагмент программы, составленной на языке ассемблера, проверяющий состояние очереди команд процессора 8042:
После того, как программа дождется готовности процессора 8042, она может послать ему команду, записав ее в порт с адресом 60h:
Некоторые команды состоят более чем из одного байта. Второй и следующие байты таких команд необходимо записывать в порт 60h, предварительно убедившись в готовности процессора 8042 с помощью приведенной выше последовательности команд. В большинстве случаев можно также использовать простую временную задержку:
Мы приведем формат двух команд процессора 8042, имеющих отношение к работе с клавиатурой - команду установки задержки и периода автоповтора и команду управления светодиодами, расположенными на клавиатуре.
Для установки характеристик режима автоповтора в порт 60h необходимо записать код команды 0F3h, затем байт, определяющий характеристики режима. Ниже вы найдете описание полей байта режима автоповтора:
Период автоповтора, который определяет количество посылок скан-кода, генерируемых процессором клавиатуры в одну секунду. Можно использовать не только те значения, которые приведены ниже, но и промежуточные, например, 9 или 15h.
Задержка включения режима автоповтора, mc:
Зарезервировано и должно быть равно нулю
Первоначально при инициализации системы BIOS устанавливает период задержки для включения режима автоповтора равным 500 мс при периоде автоповтора, равным 10 повторам в секунду. Если это слишком медленно для вас, вы можете установить другие значения.
Для управления светодиодами, расположенными на лицевой панели клавиатуры, используйте команду 0EDh. Вслед за этой командой в порт 60h необходимо записать байт, имеющий следующий формат:
1 – включить светодиод Scroll Lock;
0 – выключить светдиод Scroll Lock
1 – включить светодиод Num Lock;
0 – выключить светодиод Num Lock
1 – включить светодиод Caps Lock;
0 – включить светодиод Caps Lock
Программа KBDLED
Приведем пример простейшей программы KBDLED, управляющей светодиодами, расположенными на лицевой панели клавиатуры (листинг 2.1). Такое управление может выполняться только при использовании порта 60h, так как BIOS не содержит никаких подходящих для этого функций. Наша программа после запуска включит все светодиоды и будет ожидать, пока вы не нажмете любую клавишу. Затем программа выключит светодиоды.
Заметим, что программа KBDLED может не работать на виртуальной машине DOS, создаваемой, например, в операционной системе Microsoft Windows NT.
Листинг 2.1. Файл kbdled\kbdled.c
Аппаратное прерывание клавиатуры
Клавиатура подключена к линии прерывания IRQ1. Этой линии соответствует прерывание INT 09h.
Клавиатурное прерывание обслуживается BIOS, однако драйверы клавиатуры и резидентные программы могут организовывать дополнительную обработку прерывания INT 09h. Для этого может быть использована цепочка обработчиков прерывания.
Стандартный обработчик прерывания INT 09h
Как работает стандартный обработчик клавиатурного прерывания, входящий в состав BIOS?
Этот обработчик выполняет следующие действия:
· читает из порта 60h скан-код нажатой клавиши;
· записывает вычисленное по скан-коду значение кода ASCII нажатой клавиши в специальный буфер клавиатуры, расположенный в области данных BIOS;
· устанавливает в единицу бит 7 порта 61h, разрешая дальнейшую работу клавиатуры;
· возвращает этот бит в исходное состояние;
· записывает в порт 20h значение 20h для правильного завершения обработки аппаратного прерывания.
Обработчик прерывания INT 09h не просто записывает значение кода ASCII в буфер клавиатуры, но также выполняет и другие действия. Например, он дополнительно отслеживает нажатие пользователем таких комбинаций клавиш, как <Ctrl+Alt+Delete>, обрабатывает специальные клавиши <PrintScreen> и <SysReq>. При вычислении кода ASCII нажатой клавиши учитывается состояние клавиш <Shift> и <CapsLock>.
Буфер клавиатуры
Буфер клавиатуры имеет длину 32 байта и расположен в компьютере IBM PC/XT по адресу 0000h:041Eh.
В компьютерах моделей IBM PC/AT и IBM PS/2 расположение клавиатурного буфера задается содержимым двух слов памяти с адресами 0000h:0480h (смещение адреса начала буфера) и 0000h:0482h (смещение конца буфера). Обычно эти ячейки памяти содержат значения, соответственно, 001Eh и 003Eh. Так как смещения заданы относительно сегментного адреса 0040h, то стандартное расположение клавиатурного буфера в IBM PC/AT и IBM PS/2 соответствует его расположению в IBM PC/XT.
Буфер клавиатуры организован циклически. Это означает, что при его переполнении самые старые значения будут потеряны. Две ячейки памяти, находящиеся в области данных BIOS с адресами 0000h:041Ah и 0000h:041Ch содержат, соответственно, указатели на начало и конец буфера. Если значения этих указателей равны друг другу, буфер пуст.
Заметим, что вы можете удалить все символы из буфера клавиатуры, установив оба указателя на начало буфера. Однако есть более предпочтительный способ с использованием прерывания BIOS INT 16h, функции которого мы опишем позже в этой главе.
Указателями на начало и конец клавиатурного буфера обычно управляют обработчики прерываний INT 09h и INT 16h. Программа извлекает из буфера коды нажатых клавиш, используя различные функции прерывания INT 16h.
При переполнении внутреннего буфера клавиатуры или буфера, расположенного в области данных BIOS программа-обработчик прерывания INT 09h генерирует звуковой сигнал.
В программах MS-DOS у вас едва ли появится необходимость непосредственного манипулирования содержимым буфера клавиатуры - вы можете использовать прерывание BIOS INT 16h для выполнения практически всех клавиатурных операций.
Переключающие клавиши
Помимо управления содержимым буфера клавиатуры, обработчик прерывания INT 09h отслеживает так называемые переключающие клавиши - <NumLock>, <ScrollLock>, <CapsLock>, <Insert>. Состояние этих клавиш записывается в область данных BIOS в два байта с адресами 0000h:0417h и 0000h:0418h.
Резидентный перехватчик аппаратного прерывания клавиатуры для ДОС.
Сегодня мы рассмотрим, как написать простейший перехватчик прерывания под ДОС. Зачем это надо в наше время? Ответ прост - для того, чтобы лучше разобраться как работает машина на низком уровне, общего развития и расширения кругозора, ну и некоторым для написания курсачей. Возможно этот материал будет полезен желающим попробовать написать свою ОС - почему бы и нет?
Интересно? Мы начинаем!
Термины и определения
Прерывание – реакция процессора на внешнее событие, при котором приостанавливается выполнения потока команд ЦП (центральный процессор) и управление передается специальному куску кода, называемому обработчиком прерывания.
Программное прерывание – прерывание, при котором обработчик вызывается не по внешнему событию, а напрямую из основной программы. Таким образом, можно реализовать набор часто используемых подпрограмм (так работает DOS ( int 21h )).
Перехват прерывания – подмена системного обработчика прерывания собственным, с целью модификации алгоритма работы обработчика прерывания, либо реализации собственного обработчика.
Вектор прерывания – адрес ячейки памяти в которой находится адрес точки входа в обработчик прерывания (аналог указателей в Си).
Резидентная программа – программа, которая находится в памяти постоянно и управление ей передается по прерываниям, либо иным способом (можно напрямую сделать jmp, если известна точка входа в резидент (сегмент и смещение)). Резидент состоит из блока инициализации, в котором происходит инициализация векторов обрабатываемых прерываний и прочие процедуры, необходимые для установки резидентной программы, и соственно кода обработчика прерывания.
Постановка задачи
С терминами разобрались – поехали разбираться с тем, что нам предстоит сделать. А будем делать мы резидентный перехватчик аппаратного прерывания клавиатуры для ДОС. Алгоритмы обработчика могут быть разными – это и будет «домашним заданием» и полем для экспериментов. На них останавливаться не будем – нам важны сами принципы написания такого драйвера. Об этом и продолжим.
Что нужно иметь ввиду: т.к. речь идет про ДОС, нужно понимать, что это однозадачная система (что нам на руку), т.е. никакой другой процесс (поток выполнения) не может поменять значения регистров, которые мы будем использовать в нашей программе, а это очень круто! Сразу естественный вопрос: если система однозадачная – каким образом обеспечить выполнение нашей программы в фоне (именно так «обязаны» работать драйверы)? Ответ прост – пишем резидент! Для начала определимся с планом действий, т.е. что нам нужно сделать – это определит структуру нашей программы.
- Запомнить системный (досовский) вектор прерывания клавиатуры;
- Установить этот вектор на свой обработчик;
- Реализовать обработчик (сам алгоритм);
- Остаться работать в фоне и запускать обработчик по возникновению прерывания от клавиатуры.
Структура программы на ассемблере
Уточню сразу: все нижеизложенное будет касаться ассемблеров TASM и MASM (сам использую TASM). У других ассемблеров синтаксис отличается и надо читать документацию. Для начала рассмотрим шапку программы. Выглядит она следующим образом:
Что это такое? Для начала, разберемся с «типами данных». Тут использованы два типа: db – байт и dw – слово. Как несложно догадаться, в слове у нас 16 бит, а в байте 8. Первая запись, с текстовой строкой – массив байт. Т.е. по адресу, по которому он будет находиться, будет шутливое предложение про кошку, затем два байта – 10 и 13 (ASCII коды символов «перенос строки» и «возврат каретки») и знак доллара. Знак доллара нам нужен для функции 09h программного прерывания ДОС ( int 21h ) – это метка конца текстовой строки. Во второй строке резервируется слово в памяти для хранения сегмента (обычное число). Ну и третья строка – обычная переменная, с инициализированым значением – типа счетчик. С основами разобрались. Теперь рассмотрим функции программного прерывания ДОС ( int 21h ), которые нам пригодятся для реализации задуманного. Поехали (тут должна быть картинка с Гагариным).
INT 21h функция 09h
Вывод текстовой строки.
ВХОД: AH – 09h DX – смещение в сегменте данных (в нашем случае совпадает с сегментом кода) текстовой строки, завершающейся символом $ . Пример:
Все, теперь открываем пиво (чай, молоко, кумыс – по вкусу) – мы написали свой первый «хеллоуворлд». Откладываем кумыс в сторону, идем дальше.
INT 21h функция 02h
Функция выводит один символ на экран. Можно использовать для вывода служебных кодов ASCII.
ВХОД: AH – 02h DL – выводимый символ
INT 21h функция 4Сh
Тут мы просто выходим в ДОС и сообщаем ДОСу код завершения (применяется в BAT-файлах). Аналогично с return в Си.
ВХОД: AH – 4Ch AL – код завершения программы
INT 21h функция 35h
После вызова этой функции в регистрах ES и BX окажутся, соответственно, сегмент и смещение обработчика прерывания. Это нам пригодится для корректной передачи управления в ДОС после выполнения нашего алгоритма.
ВХОД: AH – 35h AL – номер прерывания ВЫХОД: ES – сегмент обработчика прерывания BX – смещение обработчика прерывания
INT 21h функция 25h
Эта функция устанавливает новый обработчик прерывания. Пользоваться надо очень осторожно и обдуманно, дабы не отстрелить себе очередную ногу.
ВХОД: AH – 25h AL – номер прерывания DS – сегмент обработчика прерывания DX – смещение обработчика прерывания
INT 27h
Завершиться, но остаться резидентным
ВХОД: DX – адрес первого байта за резидентным участком программы ВНИМАНИЕ, АХТУНГ, АЛЯРМ! Не 21, а 27! Да-да, именно 27. Эта функция завершает программу, оставляя резидентную часть (обработчик прерывания) в памяти. Т.е. она, по сути, информирует ДОС, что эта область памяти занята и писать туда ничего нельзя, дабы не затереть обработчик.
Теперь перейдем к коду
Напишем скелет нашего резидента.
С первыми тремя строками ясно – это шапка программы.
В пятой строке управление передается на код с меткой Init . Важно отметить, что в коде обработчик прерывания в коде располагается раньше модуля инициализации резидента – это обусловлено работой программного прерывания int 27h , в противном случае ДОС затрет обработчик, что приведёт к отстрелам ног пулеметными очередями.
С 10-й по 14-ю строку – собственно сам обработчик. Мы просто выводим на экран букву А (см. выше) и перепрыгиваем на системный обработчик прерывания 09h , чтоб не нарушать работу ДОСа и других программ, вызываемых из него.
Кульбит с адресом системного обработчика прерывания 09h (22-23) обусловлен устройством памяти у Intel . Дело в том, что Intel использует организацию памяти «младшим-вперед» - это значит, что физически первым передается младший байт ( little-endian ). Т.к. мы передаем указатель в виде СЕГМЕНТ:СМЕЩЕНИЕ , то нам надо это учитывать и передавать адрес в том виде, как это принято.
Подробнее c порядком байтов можно познакомиться тут - Википедия, да-да, именно там
Ну вот и все – мы написали наш первый резидент под ДОС. Можно вновь доставать кумыс и насладиться им в полной мере – мы это заслужили.
Реализацию алгоритма отдаю на откуп читателю, намекнув лишь, что при возникновении прерывания скан-код нажатой клавиши будет находится в порту по адресу 60h (читать – in ax, 60h ).
Так же, при нажатии и отпускании клавиши возвращается 2 сканкода, т.е. прерывание будет вызвано дважды. Наш резидент, описанный в примере выведет две буквы А вместо одной.
Читайте также: