Клавиатура
Контроллеру клавиатуры соответствуют порты с номерами от 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
Содержание раздела