Własny bufor klawiatury
cepa
Pisząc różnego rodzaju gry lub programy często może zajść potrzeba szybkiego sprawdzania
stanu klawiszy w klawiaturze. BIOS standardowo udostępnia niewielki bufor klawiatury jednak
jest on mało wygodny w obsłudze bo nie mamy pełnej kontroli nad wszystkimi klawiszami.
Co zrobić kiedy chcemy sprawdzać stan wszystkich 128 możliwych klawiszy ??
Jedynym rozwiązaniem jest napisanie własnego bufora klawiatury.
Aby to zrobić musimy przechwycić przerwanie sprzętowe 09H generowane przez klawiature.
W DOS'ie mamy dostępne dwie funkcje:
- 35H - pobiera adres procedury przerwania z tablicy przerwań
- 25H - ustawia adres procedury przerwania w tablicy przerwań
Ponieważ po zakończeniu programu ustawiona przez program procedura przerwania nadal by
funkcjonowała (co mogło by spowodować zawieszenie komputera) program musimy napisać w
taki sposób:
- pobierz adres starej procedury przerwania 09H
- ustaw nowy adres procedury przerwania klawiatury (09H)
- przywróć stary adres procedury przerwania
W praktyce wygląda to tak: - odczytaj z tablicy przerwań adres procedury przerwania 09H za pomocą funkcji 35H
- podepnij nową procedure przerwania 09H za pomocą funkcji 25H
- przywróc starą procedure za pomocą funkcji 25H
Komunikacja komputer<->klawierka odbywa się za pośrednictwem portu 60H. Kto bawił się tym
portem wie, że klawiatura wysyła dane zarówno przy naciśnięciu jak i przy zwolnieniu
klawisza. Jednocześnie można zauważyć, że np: gdy naciśniemy klawisz ESC klawiatura wyśle
bajt o wartości 1, natomiast gdy go puścimy wyśle 129 - oznacza to, że aby rozróżnić czy
jeden ze 128 klawiszy jest naciśnięty wystarczy sprawdzić czy wysłany przez klawiature bajt
ma wartość powyżej (puszczony) czy poniżej (naciśnięty) 127. Dzięki takiemu rozwiązaniu
możemy stworzyć tablicę o rozmiarze 128 bajtów gdzie będzie wpisywana wartość 0 lub 1 -
puszczony lub naciśnięty. Jako offset do odpowiedniej komurki - odpowiedniego klawisza
można użyć bajtu, który jest zwracany przez klawiaturę. Jeżeli bajt ten ma wartosć powyżej
127 to należy od niego odjąć 128 i jednocześnie ustawic w danej komurce bufora wartość 0,
jeżeli ma wartość poniżej 127 do odrazu ustawiamy wartość 1 w danej komurce bufora.
To byłoby na tyle teori, oto przykładowy kod dla kompilatora NASM:
;-------
CPU 186
BITS 16
ORG 100H
JMP main
keyboard_int: ; procedura obslugi przerwania sprzetowego 09H
STI
PUSH AX
IN AL,60H ; odczytaj kod ostatniego klawisza
MOV [CS:lastkey],AL ; wrzuc go do zmiennej lastkey
POP AX
PUSH AX
PUSH CX
MOV AX,0
MOV AL,[CS:lastkey]
CMP AL,128 ; jezeli kod ostatniego klawisza jest ponizej 128
JNAE check_keys_1 ; to dany klawisz jest nacisnienty, jezeli ponad
SUB AL,128 ; 128 to klawisz jest puszczony
MOV CL,0
JMP check_keys_2
check_keys_1:
MOV CL,1
check_keys_2:
MOV BX,key ; laduj poczatek bufora klawiatury do BX
ADD BX,AX ; dodaj do adresu offset dla danego klawisza
MOV [CS:BX],CL ; czyli jego kod i ustaw tam 1 jezeli klawisz
POP CX ; jest nacisnienty lub 0 jak nie
POP AX
PUSH AX
MOV AL,20H
OUT 20H,AL ; wyslij sygnal EOI (koniec procedury przerwania)
POP AX
CLI
IRET ; powrot
install_keyboard: ; kopiuje adres starej procedury przerwania 09H
MOV AX,3509H ; do zmiennej old_keyboard_int i ustawia nowa
INT 21H ; procedure przerwania 09H ktora steruje buforem
MOV [CS:old_keyboard_int],BX
MOV [CS:old_keyboard_int + 2],ES
MOV AX,2509H
MOV DX,keyboard_int
PUSH DS
PUSH CS
POP DS
INT 21H
POP DS
RET
remove_keyboard: ; przywraca starą procedure obsługi przerwania 09H
MOV AX,2509H
LDS DX,[CS:old_keyboard_int]
INT 21H
RET
wait_for_any_key: ; procedura czeka na wcisniencie dowolnego klawisza
MOV BYTE [lastkey],0
MOV DL,[lastkey]
wait_for_any_key_1:
CMP DL,[lastkey]
JE wait_for_any_key
RET
main:
CALL install_keyboard ; przejmij przerwanie klawiatury
main_loop:
CMP BYTE [key + 1],1 ; sprawdz czy wcisniento klawisz ESC
JNE main_loop
exit:
CALL remove_keyboard ; przywroc poprzednie ustawienia
MOV AX,4C00H
INT 21H
old_keyboard_int DD 0 ; wskaznik do starej procedury obslugi przerwania 09H
lastkey DB 0 ; ostatnio wcisnienty klawisz
key TIMES 128 DB 0 ; tablica 128 klawiszy (bufor klawiatury)
;------
Na koniec dodam jeszcze kody klawiszy:
; Numery klawiszy (definicje dla NASM'a)
;------
%define KEY_ESC 1
%define KEY_F1 59
%define KEY_F2 60
%define KEY_F3 61
%define KEY_F4 62
%define KEY_F5 63
%define KEY_F6 64
%define KEY_F7 65
%define KEY_F8 66
%define KEY_F9 67
%define KEY_F10 68
%define KEY_F11 87
%define KEY_F12 88
%define KEY_SCROLL_LOCK 70
%define KEY_SPECIAL1 41 ; '~'
%define KEY_1 2
%define KEY_2 3
%define KEY_3 4
%define KEY_4 5
%define KEY_5 6
%define KEY_6 7
%define KEY_7 8
%define KEY_8 9
%define KEY_9 10
%define KEY_10 11
%define KEY_SPECIAL2 12 ; '-'
%define KEY_SPECIAL3 13 ; '='
%define KEY_SPECIAL4 43 ; '|'
%define KEY_BACKSPACE 14
%define KEY_TAB 15
%define KEY_Q 16
%define KEY_W 17
%define KEY_E 18
%define KEY_R 19
%define KEY_T 20
%define KEY_Y 21
%define KEY_U 22
%define KEY_I 23
%define KEY_O 24
%define KEY_P 25
%define KEY_SPECIAL5 26 ; '['
%define KEY_SPECIAL6 27 ; ']'
%define KEY_ENTER 28
%define KEY_CAPS_LOCK 58
%define KEY_A 30
%define KEY_S 31
%define KEY_D 32
%define KEY_F 33
%define KEY_G 34
%define KEY_H 35
%define KEY_J 36
%define KEY_K 37
%define KEY_L 38
%define KEY_SPECIAL7 39 ; ';'
%define KEY_SPECIAL8 40 ; '"'
%define KEY_LSHIFT 42
%define KEY_Z 44
%define KEY_X 45
%define KEY_C 46
%define KEY_V 47
%define KEY_B 48
%define KEY_N 49
%define KEY_M 50
%define KEY_SPECIAL9 51 ; '<'
%define KEY_SPECIAL10 52 ; '>'
%define KEY_SPECIAL11 53 ; '?'
%define KEY_RSHIFT 54
%define KEY_CTRL 29
%define KEY_ALT 56
%define KEY_SPACE 57
%define KEY_INSERT 82
%define KEY_HOME 71
%define KEY_PAGE_UP 73
%define KEY_DELETE 83
%define KEY_END 79
%define KEY_PAGE_DOWN 81
%define KEY_UP 72
%define KEY_DOWN 80
%define KEY_RIGHT 77
%define KEY_LEFT 75
%define KEY_NUM_LOCK 69
%define KEY_SPECIAL12 53 ; NUM '/'
%define KEY_SPECIAL13 55 ; NUM '*'
%define KEY_SPECIAL14 74 ; NUM '-'
%define KEY_SPECIAL15 78 ; NUM '+'
%define KEY_NUM0 82
%define KEY_NUM1 79
%define KEY_NUM2 80
%define KEY_NUM3 81
%define KEY_NUM4 75
%define KEY_NUM5 76
%define KEY_NUM6 77
%define KEY_NUM7 71
%define KEY_NUM8 72
%define KEY_NUM9 73
;-------
Klawiszy 128 nie obsłużysz za Chiny ludowe, bo tylu nie ma =) poza tym klawiatura wysyła czasami sekwencje, takie jak po naciśnięciu przycisków ACPI czy WINDOWS MULTIMEDIA, czy też kombinacji niektórych, które bodajże zaczynają się od 0xE0.
Sprawdzanie, czy kod jest > 128 czy nie i potem wstawianie 0 lub 1 jest w sumie ok, ale ja bym zaproponował takie cosik:
in al,60h
cbw ;instrukcja dość wolna (3CL) i nieparowalna - można ją zamienić
and al,7fh
xor bx,bx
lea si,key
mov bl,al
mov [si+bx],ah
i mamy 0 jak wyciśnięty i 0ffh jak wciśnięty
swietny art !!