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:
  1. pobierz adres starej procedury przerwania 09H
  2. ustaw nowy adres procedury przerwania klawiatury (09H)
  3. przywróć stary adres procedury przerwania
    W praktyce wygląda to tak:
  4. odczytaj z tablicy przerwań adres procedury przerwania 09H za pomocą funkcji 35H
  5. podepnij nową procedure przerwania 09H za pomocą funkcji 25H
  6. 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
;-------

3 komentarzy

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 !!