Zabawa diodami na klawiaturze

Aby uczynić swój program bardziej atrakcyjnym wzrokowo i pochwalić się swoimi umiejętnościami, można sprawić, aby diody na klawiaturze wskazujące stan Num Lock, Caps Lock, Scroll Lock zaczęły migotać w jakimś rytmie.
Teraz pokażę, jak to zrobić.

Najpierw, tradycyjnie, spojrzymy w spis portów dołączony do Listy Przerwań Ralfa Brown'a. Potrzebny nam będzie podstawowy port kontrolera klawiatury, port 60h:


(przeskocz port 60h)
	0060 RW KB controller data port or keyboard input buffer (ISA, EISA)
                should only be read from after status port bit0 = 1
                should only be written to if status port bit1 = 0

Jak widać, trzeba też znaleźć jakiś port statusu. Jest to port 64h:


(przeskocz port 64h)
	Bitfields for keyboard controller read status (ISA, EISA):
	Bit(s)  Description     (Table P0398)
  	 7      parity error on transmission from keyboard
 	 6      receive timeout
	 5      transmit timeout
	 4      keyboard interface inhibited by keyboard lock
	        or by password server mode
	 3      =1 data written to input register is command (PORT 0064h)
	        =0 data written to input register is data (PORT 0060h)
	 2      system flag status: 0=power up or reset  1=selftest OK
	 1      input buffer full (input 60/64 has data for 8042)
	        no write access allowed until bit clears
	 0      output buffer full (output 60 has data for system)
	        bit is cleared after read access

Tak więc, potrzebna nam będzie procedura sprawdzająca, czy można pisać do portu klawiatury. Spróbujmy ją napisać:


(przeskocz procedurę sprawdzającą zajętość portu)
	; wersja TASM
	czy_mozna_pisac		proc	near
		push eax
	sprawdzaj:
		in al,64h
		and al,2		; sprawdzamy bit nr. 1
		jnz sprawdzaj		; jeśli różny od zera, to
					; sprawdzaj do skutku

		pop eax
		ret
	czy_mozna_pisac		endp

Teraz wersja NASM/FASM:


(przeskocz wersję NASM/FASM tej procedury)
	; wersja NASM
	czy_mozna_pisac:
		push eax
	sprawdzaj:
		in al,64h
		and al,2		; sprawdzamy bit nr. 1
		jnz sprawdzaj		; jeśli różny od zera, to
					;sprawdzaj do skutku

		pop eax
		ret


Ta powinna wystarczyć.
Trzeba jeszcze znaleźć polecenie kontrolera klawiatury, które kontroluje stan diód. Jest to bajt EDh:


(przeskocz komendę ustawiania diód)
	EDh    	       set/reset mode indicators Caps Num Scrl
        	       bit 2 = CapsLk, bit 1 = NumLk, bit 0 = ScrlLk
               		all other bits must be zero.

Możemy już zacząć coś pisać:


(przeskocz pierwszy program)
	call czy_mozna_pisac
	MOV AL,0EDh
	OUT 60h,AL
	XOR AL,AL		;żadna dioda się nie pali
	OUT 60h,AL
	call czy_mozna_pisac
	MOV AL,0EDh
	OUT 60h,AL
	MOV AL,2		;Num Lock
	OUT 60h,AL
	call czy_mozna_pisac
	MOV AL,0EDh
	OUT 60h,AL
	MOV AL,1		;Scroll Lock
	OUT 60h,AL
	call czy_mozna_pisac
	MOV AL,0EDh
	OUT 60h,AL
	MOV AL,6		;Caps+Num
	OUT 60h,AL

To był tylko przykład. No więc uruchamiamy go i co? Bzyk! I już nasz program się zakończył. Może komuś udało się zaobserwować efekty (z wyjątkiem ostatniego, który jest trwały). To stawia 2 pytania:

  1. Jak sprawić, żeby trwało to dłużej?
  2. Jak powrócić do stanu pierwotnego, zgodnego z prawdą?

Odpowiedzią na pierwsze pytanie jest już użyta raz przeze mnie w innym artykule funkcja 86h przerwania 15h. Przypomnę: CX:DX = liczba mikrosekund przerwy, którą chcemy uzyskać.
Po dodaniu niezbędnych linijek program może wyglądać tak:


(przeskocz program z opóźnieniami)
	MOV AH,86h
	MOV CX,0Fh
	MOV DX,4240h

	call czy_mozna_pisac
	MOV AL,0EDh
	OUT 60h,AL
	XOR AL,AL		;żadna dioda się nie pali
	OUT 60h,AL
	INT 15h
	;MOV AH,86h
	;INT 15h
	call czy_mozna_pisac
	MOV AL,0EDh
	OUT 60h,AL
	MOV AL,2		;Num Lock
	OUT 60h,AL
	MOV AH,86h
	INT 15h
	;MOV AH,86h
	;INT 15h

i tak dalej...
Jeśli zauważycie, że to nic nie daje, to odkomentujcie drugie wywołania przerwania. Rejestr AH musi być przed każdym wywołaniem przywracany, gdyż przerwanie go modyfikuje.

A co z drugim pytaniem?
Z pomocą tym razem przychodzi spis przerwań. Patrzymy:


(przeskocz opis funkcji 2 przerwania 16h)
	INT 16 - KEYBOARD - GET SHIFT FLAGS
	        AH = 02h
	Return: AL = shift flags (see #00582)
	        AH destroyed by many BIOSes

	Bitfields for keyboard shift flags:
	Bit(s)  Description     (Table 00582)
	 7      Insert active
	 6      CapsLock active
	 5      NumLock active
	 4      ScrollLock active
	 3      Alt key pressed
	 2      Ctrl key pressed
	 1      left shift key pressed
	 0      right shift key pressed

Nasz programik będzie więc wyglądał mniej-więcej tak:


(przeskocz program z opóźnieniami i z przywracaniem stanu)
		MOV AH,2
		INT 16h
		MOV BH,AL		; zachowujemy stary stan klawiatury

		MOV AH,86h
		MOV CX,0Fh
		MOV DX,4240h


		call czy_mozna_pisac
		MOV AL,0EDh
		OUT 60h,AL
		XOR AL,AL		;żadna dioda się nie pali
		OUT 60h,AL
		INT 15h
		;MOV AH,86h
		;INT 15h
		...
		...

		XOR AL,AL
		TEST BH,01000000b	; czy Caps był włączony?
		JZ nie_caps
		OR AL,4			; tak, ustaw bit 2
	nie_caps:
		TEST BH,00100000b	; czy Num?
		JZ nie_num
		OR AL,2
	nie_num:
		TEST BH,00010000b	; czy Scroll?
		JZ koniec
		OR AL,1
	koniec:
		MOV BL,AL
		MOV AL,0EDh
		OUT 60h,AL
		MOV AL,BL
		OUT 60h,AL
		...

Dalsze eksperymenty pozostawiam czytelnikom. Pamiętajcie, że istnieje aż 8 różnych kombinacji stanów diód i można przecież robić różne odstępy czasowe między zmianą stanu.

Miłej zabawy.



Spis treści off-line (Alt+1)
Spis treści on-line (Alt+2)
Ułatwienia dla niepełnosprawnych (Alt+0)