| [Назад] [Далее] | ![]() |
Контроллеру клавиатуры соответствуют порты с номерами от 60h до 6Fh, хотя для всех стандартных операций достаточно портов 60h и 61h.
64h для чтения: регистр состояния клавиатуры, возвращает следующий байт:
бит 7: ошибка четности при передаче данных с клавиатуры
бит 6: тайм-аут при приеме
бит 5: тайм-аут при передаче
бит 4: клавиатура закрыта ключом
бит 3: данные, записанные в регистр ввода, — команда
бит 2: самотестирование закончено
бит 1: в буфере ввода есть данные (для контроллера клавиатуры)
бит 0: в буфере вывода есть данные (для компьютера)
При записи в этот порт он играет роль дополнительного регистра управления клавиатурой, но его команды сильно различаются для разных плат и разных BIOS, и мы не будем его подробно рассматривать.
61h для чтения и записи — регистр управления клавиатурой. Если в старший бит этого порта записать значение 1, клавиатура будет заблокирована, если 0 — разблокирована. Другие биты этого порта менять нельзя, так как они управляют другими устройствами (в частности динамиком). Чтобы изменить состояние клавиатуры, надо считать байт из порта, изменить бит 7 и снова записать в порт 61h этот байт.
60h для чтения — порт данных клавиатуры. При чтении из него можно получить скан-код последней нажатой клавиши (см. приложение 1) — именно так лучше всего реализовывать резидентные программы, перехватывающие прерывание IRQ1, так как по этому коду можно определять момент нажатия и отпускания любой клавиши, включая такие клавиши, как Shift/Ctrl/Alt или даже Pause (скан-код отпускания клавиши равен скан-коду нажатия плюс 80h):
int09h_handler:
in al,60h ; прочитать скан-код клавиши,
cmp al,hot_key ; если это наша "горячая"
; клавиша,
jne not_our_key ; перейти к нашему
; обработчику
[...] ; наши действия здесь
not_our_key:
jmp old_int09h ; вызов старого обработчика
Мы пока не можем завершить обработчик просто командой IRET, потому что, во-первых, обработчик аппаратного прерывания клавиатуры должен установить бит 7 порта 61h, а затем вернуть его в исходное состояние, например так:
in al,61h
push ax
or al,80h
out 61h,al
pop ax
out 61h,al
А во-вторых, он должен сообщить контроллеру прерываний, что обработка аппаратного прерывания закончилась командами
mov al,20h
out 20h,al
60h для записи — регистр управления клавиатурой. Байт, записанный в этот порт (если бит 1 в порту 64h равен 0), интерпретируется как команда. Некоторые команды состоят из более чем одного байта — тогда следует дождаться обнуления зтого бита еще раз перед тем, как посылать следующий байт. Перечислим наиболее стандартные команды.
Команда EDh 0?h — изменить состояние светодиодов клавиатуры. Второй байт этой команды определяет новое состояние:
бит 0 — состояние Scroll Lock (1 — включена, 0 — выключена)
бит 1 — состояние Num Lock
бит 2 — состояние Caps Lock
При этом состояние переключателей, которое хранит BIOS в байтах состояния клавиатуры, не изменяется, и при первой возможности обработчик прерывания клавиатуры BIOS восстановит состояние светодиодов.
Команда EEh — эхо-запрос. Клавиатура отвечает скан-кодом EEh.
Команда F3h ??h — Установить параметры режима автоповтора:
бит 7 второго байта команды — 0
биты 6 – 5 устанавливают паузу перед началом автоповтора:
00 = 250ms, 01 = 500ms, 10 = 750ms, 11 = 1000ms
биты 4 – 0 устанавливают скорость автоповтора (символов в секунду):
00000 = 30,0 01111 = 8,0
00010 = 24,0 10010 = 6,0
00100 = 20,0 10100 = 5,0
00111 = 16,0 10111 = 4,0
01000 = 15,0 11010 = 3,0
01010 = 12,0 11111 = 2,0
01100 = 10,0
Все промежуточные значения также имеют смысл и соответствуют промежуточным скоростям, например 00001 = 26,7.
Команда F4h — включить клавиатуру.
Команда F5h — выключить клавиатуру.
Команда F6h — установить параметры по умолчанию.
Команда FEh — послать последний скан-код еще раз.
Команда FFh — выполнить самотестирование.
Клавиатура отвечает на все команды, кроме EEh и FEh, скан-кодом FAh (подтверждение), который поглощается стандартным обработчиком BIOS, так что, если мы не замещаем полностью стандартный обработчик, о его обработке можно не беспокоиться.
В качестве примера работы с клавиатурой напрямую рассмотрим простую программу, выполняющую переключение светодиодов.
; mig.asm
; циклически переключает светодиоды клавиатуры
.model tiny
.code
org 100h ; СОМ-программа
start proc near
mov ah,2 ; функция 02 прерывания 1Ah
int 1Ah ; получить текущее время
mov ch,dh ; сохранить текущую секунду в СН
mov cl,0100b ; CL = состояние светодиодов клавиатуры
main_loop:
call change_LEDs ; установить светодиоды в соответствии с CL
shl cl,1 ; следующий светодиод,
test cl,1000b ; если единица вышла в бит 3,
jz continue
mov cl,0001b ; вернуть ее в бит 0,
continue:
mov ah,1 ; проверить, не была ли нажата клавиша,
int 16h
jnz exit_loop ; если да - выйти из программы
push cx
mov ah,2 ; функция 02 прерывания 1Ah
int 1Ah ; получить текущее время
pop сх
cmp ch,dh ; сравнить текущую секунду в DH с СН,
mov ch,dh ; скопировать ее в любом случае,
je continue ; если это была та же самая секунда - не
; переключать светодиоды,
jmp short main_loop ; иначе - переключить светодиоды
exit_loop:
mov ah,0 ; выход из цикла - была нажата клавиша,
int 16h ; считать ее
ret ; и завершить программу
start endp
; процедура change_LEDs
; устанавливает состояние светодиодов клавиатуры в соответствии с числом в CL
change_LEDs proc near
call wait_KBin ; ожидание возможности посылки команды
mov al,0EDh
out 60h,al ; команда клавиатуры EDh
call wait_KBin ; ожидание возможности посылки команды
mov al,cl
out 60h,al ; новое состояние светодиодов
ret
change_LEDs endp
; процедура wait_KBin
; ожидание возможности ввода команды для клавиатуры
wait_KBin proc near
in al,64h ; прочитать слово состояния
test al,0010b ; бит 1 равен 1?
jnz wait_KBin ; если нет - ждать,
ret ; если да - выйти
wait_KBin endp
end start