I. Un contrôleur clavier▲
Relu par ClaudeLELOUP.
Il n'y a pas à dire, le mode protégé, c'est violent. Au chapitre précédent, on a retrouvé les interruptions. Lesquelles malheureusement sont vides. Retrouver l'entrée clavier, ce ne serait pas mal.
I-1. Que se passe-t-il lors de l'appui sur une touche ?▲
Lors de l'appui sur une touche, le PIC déclenche l'interruption à laquelle est branchée l'IRQ1. Doit s'ensuivre une discussion protocolaire avec le processeur afin de transmettre au processeur les connaissances du PIC.
I-2. Comment ça se passe ?▲
Pour faire ça convenablement, nous allons écrire l'interruption déclenchée par l'IRQ1. Pour écrire une interruption, on ne se soucie pas du contexte, il est sauvegardé par l'instruction INT. On va commencer par interdire les interruptions : cli. Ensuite, on demande au PIC s'il est disponible pour un échange fructueux :
.attendBuffer:
in
al
, 0x64
; lire statut
test
byte
al
,1
; bus de sortie prêt ?
jz
.attendBuffer ; non => boucler
Ce qui signifie qu'on attend sagement qu'il y ait un octet avec le bit de poids faible à 1 sur le port 0x64, signifiant qu'il y a une entrée clavier à lire. Une fois qu'on est sûr qu'il y a une touche à traiter, on peut magnanimement en prendre connaissance :
in
al
, 0x60
;Lit la touche pressée
cmp
al
, 224
;224 est un code spécial, ça veut dire qu'il y a autre chose derrière, mais pour l'instant on s'en fiche
je
.attendBuffer ;On va donc lire la suite
Le caractère se trouve sur un autre port, le port 0x60. Ensuite, le traitement commence, et pour cela, il nous faut un peu d'histoire. Le circuit qui gère le clavier n'est pas tout jeune. Il est standard, éprouvé, et vient de gens dont la principale motivation n'était pas le rendu typographique. De base, le clavier envoyait un octet qui donnait à la fois la touche concernée et son état : pressée ou relâchée. Cela fait deux codes par touche, sur un octet. On peut donc avoir 128 touches différentes. Il faut aussi savoir qu'au niveau où nous sommes, on parle de touche et non de caractère : la touche 23 a été relâchée, que celle-ci soit un 'A', un 'é' ou F4. Il a été remarqué que ce 128 pouvait être une limite un peu basse. Le code de touche a été étendu - voilà pourquoi on peut encore entendre qu'un ordinateur actuel possède un clavier étendu - sur deux octets. Pour marquer l'utilisation de code étendu, le premier octet prend des valeurs spéciales, dont 224, celle testée ici. Les codes clavier étendu ont été si bien faits que quand on ne prend pas en compte le mode étendu, le code signifie quand même quelque chose, caractéristique que nous mettons à profit ici en "oubliant" le code 224.
Pour la suite, j'ai un peu joué quand même. Après tout, on a bien le droit de se faire plaisir. Je me suis dit que j'allais stocker une carte de toutes les touches appuyées. Comme ça, si vous voulez savoir à n'importe quel moment si une touche est appuyée, un coup d'œil sur cette carte et vous saurez si Shift ou Ctrl sont pressées. Mais si vous voulez faire "A + 3 + ,", c'est aussi possible. Pour ce faire, on a besoin de stocker 128 bits, un bit (pressée : oui ou non) par touche gérée par ce qu'il faut bien appeler un gestionnaire de clavier. 128 bits, c'est 16 octets: touchesActives: dd 0, 0, 0, 0. La prochaine étape consiste donc à mettre le bit de la touche traitée à la valeur convenable. Première étape, trouver l'octet concerné dans la table :
mov
bl
, al
shr
bl
, 3
; On divise le code par 8 pour savoir dans quel octet il se situe
push
eax
; On sauve le caractère dans la pile
mov
cl
, 8
div
cl
; On le divise par 8
mov
cl
, ah
; Pour une division par 8 bits, le reste est dans AH
pop
eax
mov
edx
, 1
shl
dl
, cl
add
ebx
, touchesActives ;C'est l'adresse de l'octet contenant ce qui nous intéresse
cmp
al
, 128
;Les touches appuyées ont un code inférieur à 128, les touches relâchées, c'est le même code qu'appuyées plus 128
jb
.stockeActif
sub
al
, 128
sub
ebx
, 128
/
8
not
dl
and
byte
[ebx
], dl
jmp
.suite
.stockeActif:
or
byte
[ebx
], dl
; Un OU met le bit à 1, un NOT AND le mettra à 0
mov
[derniereTouche], al
À noter dans ce morceau de code : l'utilisation de la position du reste d'une division en fonction de la taille des opérandes, chose à laquelle il faut toujours faire attention. SHL et sa copine SHR ne peuvent décaler que d'un nombre constant ou du contenu du registre C. La différence de traitement entre touche pressée/touche relâchée est faite en un seul endroit, ce qui donne un code un peu contre-intuitif (pourquoi cette soustraction de 128/8 sur ebx ?). Notez l'utilisation du AND pour mettre des bits à 0 et du OR pour les mettre à 1. On termine, en cas de touche pressée, par la stocker en mémoire, afin que d'autres sous-programmes puissent y accéder. C'est grossier, vu qu'on stocke aussi les modificateurs, et je suis quasiment certain qu'un bon gestionnaire de clavier utilise un buffer circulaire.
Il reste à accuser réception, remettre les interruptions en place et quitter (ici, en appelant l'interruption PIC qui ne fait rien) :
.suite:
in
al
, 0x61
or
al
, 130
out
0x61
, al
;Dit à l'ordi qu'une touche a été pressée
and
al
, 127
out
0x61
, al
;Reinitialisation terminée
sti
jmp
interruptionPICMaitreParDefaut
I-3. Convertir en caractères▲
Car, suite à l'étape précédente, nous avons bien la dernière touche tapée, mais nous n'avons aucune idée du caractère auquel elle correspond. Nous allons avoir besoin d'une table de conversion qui fera la correspondance entre numéro de touche et caractère sérigraphié sur la touche. Voici cette table :
traductionASCII
:
db
'.'
,'.'
,'1'
,'2'
,'3'
,'4'
,'5'
,'6'
,'7'
,'8'
,'9'
,'0'
,'.'
,'+'
,'.'
,'.'
,'a'
,'z'
,'e'
,'r'
,'t'
,'y'
,'u'
,'i'
,'o'
,'p'
,'^'
,'$'
,13
,'.'
,'q'
,'s'
,'d'
,'f'
,'g'
,'h'
,'j'
,'k'
,'l'
,'m'
,'%'
,'.'
,'.'
,'*'
,'w'
,'x'
,'c'
,'v'
,'b'
,'n'
,','
,';'
,':'
,'!'
;54 caractères gérés
La position du caractère dans cette table est son numéro de touche.
I-4. Lecture au clavier▲
Vous vous souvenez de la routine de lecture de caractères ? Oui, nous avons progressé, depuis, mais nous ne l'avons plus depuis le passage en mode protégé. Il faut la refaire.
L'idée est de commencer par vider le similitampon que nous avons dans la routine d'interruption. Ensuite, nous lirons le contenu de ce tampon. L'octet ainsi récupéré nous donne l'index à lire dans le tableau de conversion, et nous avons ainsi notre caractère.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; litChaine ;;
;;------------------------------------------------------------------;;
;; Ecrit dans la chaîne pointée par EDI les caractères écrits au ;;
;; clavier, jusqu'Ã un retour chariot. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
litChaine:
push
eax
push
ebx
xor
eax
, eax
mov
byte
[derniereTouche], 0
mov
esi
, edi
.attend_clavier:
cmp
byte
[derniereTouche], 0
jz
.attend_clavier
mov
al
, [derniereTouche]
mov
byte
[derniereTouche], 0
mov
ebx
, traductionASCII
add
ebx
, eax
mov
al
, [ebx
]
stosb
cmp
al
, 13
je
.fin_attend_clavier
mov
al
, 0
stosb
call
afficheChaine
dec
edi
dec
esi
jmp
.attend_clavier
.fin_attend_clavier:
pop
ebx
pop
eax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Fin litChaine ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I-5. Du code▲
L'ensemble du code se trouve ici : Paquet