<-Précédent Retour à l'accueil Contact : etienne"point"sauvage"at"gmail.com Suivant->
  1. Que se passe-t-il lors de l'appui sur une touche ?
  2. Comment ça se passe ?
  3. Convertir en caractères
  4. Lecture au clavier
  5. Du code

    Assembleur : un contrôleur clavier

    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 serait pas mal.

  1. Que se passe-t-il lors de l'appui sur une touche ?
  2. 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.

  3. Comment ça se passe ?
  4. 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.

    Pou 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'oeil 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
    	

    A 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
    	

  5. Convertir en caractères
  6. 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 caratères gérés
    La position du caractère dans cette table est son numéro de touche.

  7. Lecture au clavier.
  8. Vous vous souvenez de cette 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 simili-tampon 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													;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    	

  9. Du code
  10. L'ensemble du code, depuis le chapitre 8, se trouve ici :
    Tuto8