Table des matières
Comme d'habitude, je ne détaillerai rien concernant la compilation/l'édition des liens encore cette fois-ci. Reportez-vous au premier billet pour plus d'informations.
Explications préalables
Avant de commencer ce nouvel exercice, je souhaite revenir sur deux ou trois trucs qui m'ont été demandés.
Pourquoi mov ax, 4C00h pour fermer le programme ?
Il y a deux questions ici.
- D'une part, on ne peut pas utiliser une autre instruction pour terminer le programme. L'interruption 20h se destine aux programmes COM et la fonction 00h de l'interruption 21h a un usage incertain sur les programmes EXE. Comme d'habitude, je ne l'invente pas
- D'autre part, pourquoi mettre le nombre 4C00 en hexadécimal dans le registre AX ? L'interruption 21h s'attend à avoir le "nom" de la fonction à appeler (4Ch) dans AH et la fonction 4Ch attend le code retour à retourner dans AH. On retourne 0 (donc 00h) quand on veut indiquer que l'exécution du programme s'est bien déroulée (équivalent à un exit(0) en C). Mov ax, 4C00h revient à faire mov ah, 4Ch mov al, 00h si vous préférez. Il en va de même pour la saisie d'une chaîne de caractères : mov ax, 0C0Ah permet d'appeler la fonction qui a la charge de vider le buffer d'entrée puis d'appeler la fonction de saisie voulue (ici on choisi la 0Ah).
Pourquoi j'utilise des accents dans mes chaînes de caractères sous GNU/Linux mais pas sous Windows ?
Car cela serait plus compliqué pour vous.
Sous GNU/Linux, les éditeurs ont souvent le jeu de caractères (charset dans la langue de Shakespeare) "UTF-8" d'actif par défaut (en tout cas, je n'en ai pas croisé un qui ne le soit pas). Le shell est aussi dans ce jeu de caractères. Il n'y a donc pas de problèmes pour l'affichage des caractères accentués.
Sous Windows, il est possible de faire afficher des caractères accentués dans l'invite de commande, contrairement à l'idée reçue très répandue mais il faut choisir le bon charset dans votre éditeur : le charset "OEM 850". Comme tous les éditeurs ne le propose pas (Notepad++ does), je propose du code sans accents afin de ne pas embrouiller ceux qui ne veulent pas s'embêter avec ça. Tester et approuver sous XP.
Tes codes sont pas optimisés, tu utilises n'importe quel registre pour n'importe quoi !
J'ai jamais prétendu le contraire 😛 .
Concernant l'optimisation, je ne suis pas convaincu par tous les conseils qu'on trouve sur le net et dans le doute, j'évite de les utiliser. Exemple : pour vider un registre, il est de coutume de faire un xor registre, registre (remplacez registre par le registre à vider). Je fais un mov registre,0. Si on regarde la liste des instructions que je vous ai donné dans le premier billet, on voit que sur le 8086 le mov registre, immédiat coûte plus de cycle d'horloge que le xor registre, registre (4 contre 3). Mais sur les générations suivantes, on tombe à égalité à 2 cycles. Donc je me dis qu'aujourd'hui ça doit être pareil voir mieux. Après, je dois aussi être complet et vous informer que certaines instructions prennent moins de code machine que d'autres et que donc le poids des exécutables générés peuvent variés. Exemple : un xor eax, eax a un code machine plus petit qu'un mov eax, 0. Cela peut avoir son importance dans certains types de codes (ex.: exploit).
Concernant l'utilisation des registres, je ne peux être que d'accord. Mais étant donné que je souhaite montrer progressivement les possibilités, j'évite de parler de la pile dès le premier programme 😉 .
Ceci étant dit, on va pouvoir commencer un nouvel exercice.
Exercice 4 : énoncé
- Transformer une lettre minuscule saisie par l'utilisateur en une lettre majuscule. Si le caractère saisi est bien une lettre minuscule, on la transforme et on l'affiche, sinon on affiche un message d'erreur.
- Transformer une chaîne de caractères minuscules saisie par l'utilisateur en une chaîne de caractères majuscules. On affichera la chaîne transformée. Si une lettre n'est pas une minuscule, on l'affichera sans tenter de la transformer. Pareil si la chaîne entière ne contient aucune minuscule.
- Faire saisir une chaîne à l'utilisateur et n'afficher que les voyelles de cette chaîne. S'il n'y a aucune voyelle dans la chaîne, on affichera un message.
- Déclarer un nombre inférieur à 65535 dans le segment des données puis faire afficher ce nombre en hexadécimal.
Note : Comme d'habitude, cet énoncé n’est pas de moi : je l’ai repris à T.M. / E.R. et je l’ai modifié.
Exercice 4 : aides
Il faut :
- Regarder la table ASCII et remarquer qu'une minuscule est comprise entre 61h et 7A (soit entre 97 et 122 en décimal). Les majuscules vont de 41h à 5Ah, soit 20h en moins que les minuscules leur correspondant. A partir de là, la question 1 est résolue : il ne vous reste plus qu'a utiliser l'instruction de comparaison et les sauts pour réagir en fonction du caractère saisi 😉 .
- Pour la question 2, on réutilise la question 1 à l'intérieur d'une boucle qui parcourt la chaine saisie. Si le caractère est une minuscule, on le remplace dans la chaîne par son équivalent majuscule. Sinon non. C'est aussi simple que cela.
- Pour la question 3, il suffit de parcourir la chaîne. Si le caractère est compris dans l'ensemble {a, e, i, o, u, y, A, E, I, O, U, Y}, on l'affiche, sinon non. Je suis sûr que certains d'entre vous sont déjà en train de compter le nombre de tests à faire. Ce n'est pas la bonne méthode, je pense. Il suffit, pour chaque caractère de la chaîne saisie, de boucler sur un tableau qui contient les voyelles et de tester si le caractère saisi correspond à la voyelle en cours.
- Pour savoir si le caractère saisi par l'utilisateur n'est pas une minuscule, ou pour savoir si il n'y a pas de voyelle dans la chaîne et afficher un message en conséquence, il suffit de se faire une sorte de booléen en mémoire ou dans un registre. Si j'ai affiché une voyelle, je met le booléen à vrai, par exemple.
- Pour afficher un nombre <= 65535 (donc 16 bits) en hexadécimal, il faudrait pouvoir isoler 4 groupes de 4 bits et afficher la correspondance hexadécimale de chaque groupe. On utilisera un masque (and 000Fh) afin de récupérer uniquement les 4 bits les plus à droite dans un registre donné et de mettre à 0 les autres bits (car AND 0,1 = 0 😉 ). Ensuite, il devient simple d’afficher le caractère hexadécimal qui correspond à chacun des groupes isolés à l’aide d'une boucle et d’un tableau associatif qui, à un index numérique (0, 1, …, 15), associe le caractère hexadécimal équivalent a ce nombre ("0", "1", …., "F".). Dis comme ça, vous comprenez qu'on va toujours récupérer les mêmes 4 bits et donc afficher un nombre faux. Il faut donc décaler les bits du registre de 4 en 4 (12, 8, 4, 0) vers la droite avant de récupérer les 4 bits les plus faibles avec le masque et d'afficher le caractère hexadécimal qui correspond au groupe.
Vous avez toutes les cartes en main.
Exercice 4 : sous Windows, avec TASM et TLINK
DOSSEG .MODEL SMALL .STACK .DATA ; 1) message db "Transformation d'une lettre miniscule saisie au clavier en lettre majuscule : $" message2 db 13,10,"-> Saisie d'une lettre minuscule : $" message3 db 13,10,'-> Caractere saisi : $' message4 db 13,10,'-> Caractere transforme : $' messageException db "Le caractere saisi n'est pas une lettre minuscule $" ; 2) message5 db 13,10,10,"Transformation d'une chaine minuscule saisie au clavier en une chaine majuscule : $" message6 db 13,10,"-> Saisie d'une chaine minuscule (15 caracteres max) : $" message7 db 13,10,'-> Chaine saisie : $' message8 db 13,10,'-> Chaine transforme : $' ; 3) message9 db 13,10,10,"Afficher que les voyelles d'une chaine : $" message10 db 13,10,"-> Saisie d'une chaine (15 caracteres max) : $" message11 db 13,10,'-> Chaine saisie : $' message12 db 13,10,'-> Chaine transforme : $' messageException2 db "Il n'y a pas de voyelle dans cette chaine. $" voyelles db 'aeiouyAEIOUY' ; 4) message13 db 13,10,10,'Afficher un nombre <= 65535 initialise dans la zone DATA (ici : 1984) en hexadecimal : $' nombre dw 1984 TAB_Hexa db "0123456789ABCDEF" ; Saisies caractereSaisi db 4 dup(2) chaineSaisie db 18 dup(16) .CODE mov ax, @DATA mov ds, ax ; 1) Transformation d'une lettre minuscule saisie en majuscule mov ah, 09h mov dx, offset message int 21h mov ah, 09h mov dx, offset message2 int 21h mov ax, 0C0Ah mov dx, offset caractereSaisi int 21h mov ah, 09h mov dx, offset message3 int 21h mov ah, 02h mov dl, [caractereSaisi+2] int 21h mov ah, 09h mov dx, offset message4 int 21h mov dl, [caractereSaisi+2] cmp dl, 61h jb invalide cmp dl, 7Ah ja invalide sub dl, 20h mov ah, 02h int 21h jmp finQ1 invalide: mov ah, 09h mov dx, offset messageException int 21h finQ1 : ; 2) Transformation d'une chaine saisie en majuscule mov ah, 09h mov dx, offset message5 int 21h mov ah, 09h mov dx, offset message6 int 21h mov ax, 0C0Ah mov dx, offset chaineSaisie int 21h mov ah, 09h mov dx, offset message7 int 21h mov bx, 0 mov bl, [chaineSaisie + 1] mov [chaineSaisie + 2 + bx], '$' mov ah, 09h mov dx, offset chaineSaisie+2 int 21h mov ah, 09h mov dx, offset message8 int 21h sub bx, 1 bcl: cmp bx, 0 jl finbcl cmp [chaineSaisie+2+bx], 61h jb finP cmp [chaineSaisie+2+bx], 7Ah ja finP sub [chaineSaisie+2+bx], 20h finP: sub bx, 1 jmp bcl finbcl : mov ah, 09h mov dx, offset chaineSaisie+2 int 21h ; 3) Afficher que les voyelles d'une chaine mov ah, 09h mov dx, offset message9 int 21h mov ah, 09h mov dx, offset message10 int 21h mov ax, 0C0Ah mov dx, offset chaineSaisie int 21h mov ah, 09h mov dx, offset message11 int 21h mov bx, 0 mov bl, [chaineSaisie + 1] mov [chaineSaisie + 2 + bx], '$' sub bx, 1 mov ah, 09h mov dx, offset chaineSaisie+2 int 21h mov ah, 09h mov dx, offset message12 int 21h mov cx, 0 mov dh, bl mov bx, 0 mov ah, 02h bcl2: cmp bl, dh ja finbcl2 mov si, 11 bcl3: cmp si, 0 jl finbcl3 mov cl, [voyelles+si] cmp [chaineSaisie + 2 + bx], cl jne finP3 mov ch, 1 mov dl, cl int 21h finP3: sub si, 1 jmp bcl3 finbcl3: add bl, 1 jmp bcl2 finbcl2 : cmp ch, 1 je fin3 mov ah, 09h mov dx, offset messageException2 int 21h fin3 : ; 4) Afficher un nombre en hexadecimal mov ah, 09h mov dx, offset message13 int 21h mov ah, 02h mov cl, 12 bcl4 : cmp cl, 0 jl fin_bcl4 mov bx, [nombre] shr bx, cl and bx, 000Fh mov dl, [TAB_Hexa + bx] int 21h sub cl, 4 jmp bcl4 fin_bcl4 : ; FIN mov ax, 4C00h int 21h END |
Exercice 4 : sous Windows, avec Nasm et le linker Val
segment .data ; 1) message db "Transformation d'une lettre miniscule saisie au clavier en lettre majuscule : $" message2 db 13,10,"-> Saisie d'une lettre minuscule : $" message3 db 13,10,'-> Caractere saisi : $' message4 db 13,10,'-> Caractere transforme : $' messageException db "Le caractere saisi n'est pas une lettre minuscule $" ; 2) message5 db 13,10,10,"Transformation d'une chaine minuscule saisie au clavier en une chaine majuscule : $" message6 db 13,10,"-> Saisie d'une chaine minuscule (15 caracteres max) : $" message7 db 13,10,'-> Chaine saisie : $' message8 db 13,10,'-> Chaine transforme : $' ; 3) message9 db 13,10,10,"Afficher que les voyelles d'une chaine : $" message10 db 13,10,"-> Saisie d'une chaine (15 caracteres max) : $" message11 db 13,10,'-> Chaine saisie : $' message12 db 13,10,'-> Chaine transforme : $' messageException2 db "Il n'y a pas de voyelle dans cette chaine. $" voyelles db 'aeiouyAEIOUY' ; 4) message13 db 13,10,10,'Afficher un nombre <= 65535 initialise dans la zone DATA (ici : 1984) en hexadecimal : $' nombre dw 1984 TAB_Hexa db "0123456789ABCDEF" ; Saisies caractereSaisi times 4 db 2 chaineSaisie times 18 db 16 segment stack stack resb 64 stackstop: segment .code ..start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, stackstop ; 1) Transformation d'une lettre minuscule saisie en majuscule mov ah, 09h mov dx, message int 21h mov ah, 09h mov dx, message2 int 21h mov ax, 0C0Ah mov dx, caractereSaisi int 21h mov ah, 09h mov dx, message3 int 21h mov ah, 02h mov dl, [caractereSaisi+2] int 21h mov ah, 09h mov dx, message4 int 21h mov dl, [caractereSaisi+2] cmp dl, 61h jb invalide cmp dl, 7Ah ja invalide sub dl, 20h mov ah, 02h int 21h jmp finQ1 invalide: mov ah, 09h mov dx, messageException int 21h finQ1 : ; 2) Transformation d'une chaine saisie en majuscule mov ah, 09h mov dx, message5 int 21h mov ah, 09h mov dx, message6 int 21h mov ax, 0C0Ah mov dx, chaineSaisie int 21h mov ah, 09h mov dx, message7 int 21h mov bx, 0 mov bl, [chaineSaisie + 1] mov byte [chaineSaisie + 2 + bx], '$' mov ah, 09h mov dx, chaineSaisie+2 int 21h mov ah, 09h mov dx, message8 int 21h sub bx, 1 bcl: cmp bx, 0 jl finbcl cmp byte [chaineSaisie+2+bx], 61h jb finP cmp byte [chaineSaisie+2+bx], 7Ah ja finP sub byte [chaineSaisie+2+bx], 20h finP: sub bx, 1 jmp bcl finbcl : mov ah, 09h mov dx, chaineSaisie+2 int 21h ; 3) Afficher que les voyelles d'une chaine mov ah, 09h mov dx, message9 int 21h mov ah, 09h mov dx, message10 int 21h mov ax, 0C0Ah mov dx, chaineSaisie int 21h mov ah, 09h mov dx, message11 int 21h mov bx, 0 mov bl, [chaineSaisie + 1] mov byte [chaineSaisie + 2 + bx], '$' sub bx, 1 mov ah, 09h mov dx, chaineSaisie+2 int 21h mov ah, 09h mov dx, message12 int 21h mov cx, 0 mov dh, bl mov bx, 0 mov ah, 02h bcl2: cmp bl, dh ja finbcl2 mov si, 11 bcl3: cmp si, 0 jl finbcl3 mov cl, [voyelles+si] cmp [chaineSaisie + 2 + bx], cl jne finP3 mov ch, 1 mov dl, cl int 21h finP3: sub si, 1 jmp bcl3 finbcl3: add bl, 1 jmp bcl2 finbcl2 : cmp ch, 1 je fin3 mov ah, 09h mov dx, messageException2 int 21h fin3 : ; 4) Afficher un nombre en hexadecimal mov ah, 09h mov dx, message13 int 21h mov ah, 02h mov cl, 12 bcl4 : cmp cl, 0 jl fin_bcl4 mov bx, [nombre] shr bx, cl and bx, 000Fh mov dl, [TAB_Hexa + bx] int 21h sub cl, 4 jmp bcl4 fin_bcl4 : ; FIN mov ax, 4C00h int 21h |
Exercice 4 : sous GNU/Linux, avec Nasm et ld
section .data ; 1) message db "Transformation d'une lettre miniscule saisie au clavier en lettre majuscule : " lenMessage equ $-message message2 db 10,"-> Saisie d'une lettre minuscule : " lenMessage2 equ $-message2 message3 db 10,'-> Caractère saisi : ' lenMessage3 equ $-message3 message4 db 10,'-> Caractère transformé : ' lenMessage4 equ $-message4 messageException db "Le caractère saisi n'est pas une lettre minuscule " lenMessageException equ $-messageException ; 2) message5 db 10,10,"Transformation d'une chaine minuscule saisie au clavier en une chaine majuscule : " lenMessage5 equ $-message5 message6 db 10,"-> Saisie d'une chaine minuscule (15 caractères max) : " lenMessage6 equ $-message6 message7 db 10,'-> Chaine saisie : ' lenMessage7 equ $-message7 message8 db 10,'-> Chaine transformée : ' lenMessage8 equ $-message8 ; 3) message9 db 10,10,"Afficher que les voyelles d'une chaine : " lenMessage9 equ $-message9 message10 db 10,"-> Saisie d'une chaine (15 caractères max) : " lenMessage10 equ $-message10 message11 db 10,"-> Chaine saisie : " lenMessage11 equ $-message11 message12 db 10,"-> Chaine transformée : " lenMessage12 equ $-message12 messageException2 db "Il n'y a pas de voyelle dans cette chaine. " lenMessageException2 equ $-messageException2 voyelles db 'aeiouyAEIOUY' ; 4) message13 db 10,10,'Afficher un nombre <= 65535 initialisé dans la zone DATA (ici : 1984) en hexadécimal : ' lenMessage13 equ $-message13 nombre dw 1984 hexa db '0123456789ABCDEF' ; FIN fin db 10,'FIN',10 lenFin equ $-fin section .bss caractereSaisi resb 2 chaineSaisie resb 16 lenSaisie resd 1 buffer resb 1 noVoyelle resb 1 buffertwo resb 32 section .text global _start _start: ; 1) Transformation d'une lettre minuscule saisie en majuscule mov eax, 4 mov ebx, 1 mov ecx, message mov edx, lenMessage int 80h mov eax, 4 mov ebx, 1 mov ecx, message2 mov edx, lenMessage2 int 80h mov eax, 3 mov ebx, 0 mov ecx, caractereSaisi mov edx, 2 int 80h mov eax, 4 mov ebx, 1 mov ecx, message3 mov edx, lenMessage3 int 80h mov eax, 4 mov ebx, 1 mov ecx, caractereSaisi mov edx, 1 int 80h mov eax, 4 mov ebx, 1 mov ecx, message4 mov edx, lenMessage4 int 80h mov dl, [caractereSaisi] cmp dl, 61h jb invalide cmp dl, 7Ah ja invalide sub dl, 20h mov [buffer], dl mov eax, 4 mov ebx, 1 mov ecx, buffer mov edx, 1 int 80h jmp finQ1 invalide: mov eax, 4 mov ebx, 1 mov ecx, messageException mov edx, lenMessageException int 80h finQ1 : ; 2) Transformation d'une chaine saisie en majuscule mov eax, 4 mov ebx, 1 mov ecx, message5 mov edx, lenMessage5 int 80h mov eax, 4 mov ebx, 1 mov ecx, message6 mov edx, lenMessage6 int 80h mov eax, 3 mov ebx, 0 mov ecx, chaineSaisie mov edx, 16 int 80h mov [lenSaisie], eax mov eax, 4 mov ebx, 1 mov ecx, message7 mov edx, lenMessage7 int 80h mov eax, 4 mov ebx, 1 mov ecx, chaineSaisie mov edx, [lenSaisie] int 80h mov eax, 4 mov ebx, 1 mov ecx, message8 mov edx, lenMessage8 int 80h ; On enleve 2 car : ; -1 car la taille de la chaine ne correspond pas à ses indices : ; une chaîne d'une taille 7 va de 0 à 6 ; -1 car on ne veut pas traiter le retour à la ligne mov ebx, [lenSaisie] sub ebx, 2 bcl: cmp ebx, 0 jl finbcl cmp byte [chaineSaisie+ebx], 61h jb finP cmp byte [chaineSaisie+ebx], 7Ah ja finP sub byte [chaineSaisie+ebx], 20h finP: sub ebx, 1 jmp bcl finbcl : mov eax, 4 mov ebx, 1 mov ecx, chaineSaisie mov edx, [lenSaisie] int 80h ; 3) Afficher que les voyelles d'une chaine mov eax, 4 mov ebx, 1 mov ecx, message9 mov edx, lenMessage9 int 80h mov eax, 4 mov ebx, 1 mov ecx, message10 mov edx, lenMessage10 int 80h mov eax, 3 mov ebx, 0 mov ecx, chaineSaisie mov edx, 16 int 80h mov [lenSaisie], eax mov eax, 4 mov ebx, 1 mov ecx, message11 mov edx, lenMessage11 int 80h mov eax, 4 mov ebx, 1 mov ecx, chaineSaisie mov edx, [lenSaisie] int 80h mov eax, 4 mov ebx, 1 mov ecx, message12 mov edx, lenMessage12 int 80h mov byte [noVoyelle], 1 mov esi, 0 mov edi, 0 mov ebx, 1 mov edx, 1 sub dword [lenSaisie], 2 bcl2: cmp edi, [lenSaisie] ja finbcl2 mov esi, 11 bcl3: cmp esi, 0 jl finbcl3 mov cl, [voyelles+esi] cmp [chaineSaisie+edi], cl jne finP3 mov eax, 4 mov byte [buffer], cl mov ecx, buffer int 80h mov byte [noVoyelle], 0 finP3: sub esi, 1 jmp bcl3 finbcl3: add edi, 1 jmp bcl2 finbcl2 : cmp byte [noVoyelle], 0 je fin3 mov eax, 4 mov ebx, 1 mov ecx, messageException2 mov edx, lenMessageException2 int 80h fin3 : ; 4) Afficher un nombre en hexadecimal mov eax, 4 mov ebx, 1 mov ecx, message13 mov edx, lenMessage13 int 80h mov cl, 12 bcl4: cmp cl, 0 jl fin_bcl4 mov byte [buffer], cl mov dx, 0 mov dx, [nombre] shr dx, cl and dx, 000Fh mov eax, 4 mov ebx, 1 mov ecx, hexa add ecx, edx mov edx, 1 int 80h mov cl, [buffer] sub cl, 4 jmp bcl4 fin_bcl4: ; FIN mov eax, 4 mov ebx, 1 mov ecx, fin mov edx, lenFin int 80h mov eax, 1 mov ebx, 0 int 80h |