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ć.
Do manipulowania urządzeniem znakowym (klawiaturą) posłużymy się funkcją systemową sys_ioctl (numer 54). Najpierw jednak trzeba się dowiedzieć, jakich komend możemy używać. W tym celu udajemy się do pliku /usr/include/linux/kd.h lub do manuala, jeśli mamy w nim stronę ioctl_list, a w nim mamy (po angielsku):
#define KDGETLED 0x4B31 /*zwróć bieżący stan diód */ #define KDSETLED 0x4B32 /*ustaw stan diód (światełek, nie flag)*/ #define LED_SCR 0x01 /*dioda scroll lock */ #define LED_NUM 0x02 /*dioda num lock */ #define LED_CAP 0x04 /*dioda caps lock */
Jedną z tych dwóch pierwszych wartości
wpiszemy do ECX.
Tymczasem w EBX musimy mieć deskryptor otwartego
pliku /dev/console (do zapisu) lub wartość 1, która oznacza standardowe urządzenie
wyjścia - STDOUT.
W EDX podajemy ostatni parametr: wartość 0-7 jeśli ustawiamy stan diód lub wskaźnik (adres)
na zmienną typu DWORD, która otrzyma bieżąca wartość stanu diód (także 0-7).
Widać więc, co musi zrobić nasz program: zachować bieżący stan diód, dowolnie je pozmieniać
(i zadbać by efekt był widoczny - robić pauzy), po czym przywrócić poprzedni stan.
Oto, jak taki program mógłby wyglądać (używanie załączników z mojej biblioteki nie jest
konieczne - w kodzie mówię, jak i co zamienić).
; Program manipuluje diodami klawiatury
;
; Autor: Bogdan D.
; Kontakt: bogdandr (at) op (dot) pl
;
; nasm -f elf klaw.asm
; ld -s -o klaw klaw.o
section .text
; nie musicie z tego korzystać:
%include "bibl/incl/linuxbsd/nasm/n_system.inc"
%include "bibl/incl/linuxbsd/nasm/n_const.inc"
%define KDGETLED 0x4b31
%define KDSETLED 0x4b32
global _start
_start:
; 1. otwieramy /dev/console, w trybie tylko do zapisu lub
; korzystamy ze STDOUT
mov eax, sys_open ; sys_open = 5 (otwieramy plik)
mov ebx, konsola ; adres nazwy pliku
mov ecx, O_WRONLY ; O_WRONLY = 01
mov edx, 777q ; RWX dla wszystkich
int 80h
cmp eax, 0
jge .ok ; jak nie ma błędu, to jedziemy dalej
; w przypadku błędu korzystamy ze STDOUT
mov eax, stdout ; stdout = 1
; 2. pobieramy aktualny stan diód
.ok:
mov ebx, eax ; EBX = deskryptor pliku
mov eax, sys_ioctl ; sys_ioctl = 54 - manipulacja urządzeniem
mov ecx, KDGETLED ; pobierz stan diód
mov edx, stare_diody ; adres DWORDa, który otrzyma
; aktualny stan diód
int 80h
mov eax, sys_ioctl ; sys_ioctl = 54
mov ecx, KDSETLED ; ustawiamy stan diód
mov edx, 7 ; wszystkie włączone
int 80h
mov cx, 7
mov dx, 0a120h ; opóźnienie pół sekundy
call pauza
; przywracamy poprzedni stan diód
mov eax, sys_ioctl
mov ecx, KDSETLED ; ustawiamy stan diód
mov edx, [stare_diody] ; EDX = poprzedni stan diód
int 80h
cmp ebx, stdout ; czy otworzyliśmy konsolę, czy STDOUT?
jle .koniec ; nie zamykamy STDOUT
mov eax, sys_close ; zamykamy otwarty plik konsoli
int 80h
.koniec:
wyjscie ; czyli
; mov eax, 1
; xor ebx, ebx
; int 80h
pauza: ; procedura pauzująca przez CX:DX milisekund
push ebx
push ecx
push edx
mov ax, cx
shl eax, 16
mov ebx, 1000000
mov ax, dx ; EAX = CX:DX
xor edx, edx
div ebx ; CX:DX dzielimy przez milion
mov [t1 + timespec.tv_sec], eax ; EAX = liczba sekund
mov ebx, 1000
mov eax, edx ; EAX = pozostała liczba mikrosekund
mul ebx
mov [t1 + timespec.tv_nsec], eax ; EAX = liczba nanosekund
mov eax, sys_nanosleep ; funkcja numer 162
mov ebx, t1
mov ecx, t2
int 80h
pop edx
pop ecx
pop ebx
ret
section .data
stare_diody dd 0
konsola db "/dev/console",0
; Struktura timespec jest zdefiniowana w pliku n_system.inc
;struc timespec
; .tv_sec: resd 1
; .tv_nsec: resd 1
;endstruc
t1 istruc timespec
t2 istruc timespec
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.