Langage d'assemblage - Traitement numérique
Le traitement des nombres en langage assembleur implique une combinaison de conversion de base, de conversion ASCII et d'opérations arithmétiques. Ce chapitre explique en détail comment saisir et afficher des nombres dans différentes bases au sein d'un programme.
Représentation des nombres
En interne, tous les nombres dans un ordinateur sont stockés en binaire, mais dans un programme, ils peuvent être représentés dans différentes bases :
| Base | Radix | Syntaxe NASM | Plage de nombres (32 bits) |
|---|---|---|---|
| Binary | 2 | 0b10101010ou10101010b | 0 et 1 |
| Octal | 8 | 0o52ou52o | 0-7 |
| Decimal | 10 | 42 | 0-9 |
| Hexadécimal | 16 | 0x2Aou2Ah | 0-9, A-F |
Relation entre l'ASCII et les nombres
Tout ce qui est saisi et affiché à l'écran estcomposé de caractères ASCII, et non de valeurs numériques binaires.
Comprendre la conversion entre caractères et valeurs numériques est au cœur du traitement numérique en assembleur :
| Caractère | Valeur ASCII | Valeur numérique correspondante |
|---|---|---|
| '0' | 0x30(48) | 0 |
| '1' | 0x31(49) | 1 |
| '9' | 0x39(57) | 9 |
| 'A' | 0x41(65) | 10 (hexadécimal) |
| 'F' | 0x46(70) | 15 (hexadécimal) |
| 'a' | 0x61(97) | 10 (hexadécimal) |
Conversion d'un caractère en nombre :数值 = ASCII码 - '0'(i.e.ASCII码 - 0x30)
Conversion d'un nombre en caractère :ASCII码 = 数值 + '0'(i.e.数值 + 0x30)
Saisie de nombres (chaîne vers valeur numérique)
Ce que l'utilisateur saisit est une séquence de caractères, qui doit être convertie en une valeur numérique binaire avant de pouvoir être utilisée dans des calculs :
Exemple
; Convertir une chaîne de chiffres décimaux en une valeur entière
section .data
input_str db '12345', 0 ; Chaîne d'entrée "12345"
input_len dd 5 ; Longueur de la chaîne
section .bss
result_val resd 1 ; Stocker la valeur numérique convertie
section .text
global _start
_start:
; Algorithme de conversion : result = 0
; result = result * 10 + (caractère courant - '0')
mov esi, input_str ; esi pointe vers la chaîne d'entrée
mov ecx, [input_len] ; ecx = compteur de boucle
mov eax, 0 ; eax = résultat accumulé
convert_loop:
mov ebx, 10
mul ebx ; eax = eax * 10
; mul ebx: edx:eax = eax * ebx
mov bl, [esi] ; Lire le caractère courant
sub bl, '0' ; Convertir en numérique : caractère - '0'
movzx ebx, bl ; Étendre ebx avec des zéros (bl -> ebx)
add eax, ebx ; eax = eax + valeur du chiffre courant
inc esi ; Passer au caractère suivant
loop convert_loop
; eax maintenant = 12345
mov [result_val], eax ; Enregistrer le résultat
mov eax, 1
mov ebx, 0
int 0x80
Analyse de l'algorithme : prenons "12345" comme exemple :
Tour 1 : résultat = 0×10 + 1 = 1
Tour 2 : résultat = 1×10 + 2 = 12
Tour 3 : résultat = 12×10 + 3 = 123
...Enfin, nous obtenons 12345.
Affichage des nombres (valeur numérique vers chaîne)
Convertir une valeur numérique binaire en une chaîne décimale affichable :
Exemple
; Convertir une valeur entière en une chaîne décimale et l'afficher
section .data
number dd 12345 ; Le nombre à afficher
newline db 0xA
section .bss
output_buf resb 12 ; Tampon de sortie (un entier 32 bits maximum fait 10 chiffres + signe + null)
section .text
global _start
_start:
mov eax, [number] ; Charger le nombre
mov edi, output_buf + 11 ; edi pointe vers la fin du tampon
mov byte [edi], 0 ; terminateur null (pour faciliter le débogage)
dec edi
mov ebx, 10 ; Diviseur = 10
mov ecx, 0 ; Compteur de chiffres
convert_digit:
mov edx, 0 ; Mettre edx à zéro (div nécessite edx:eax)
div ebx ; eax = eax/10, edx = eax%10
add dl, '0' ; Convertir le reste en caractère
mov [edi], dl ; Stocker dans le tampon (de la fin vers le début)
dec edi ; Déplacer le pointeur de tampon vers l'avant
inc ecx ; Compteur de chiffres +1
cmp eax, 0 ; Le quotient est-il 0 ?
jne convert_digit ; Non, continuer la boucle
; Maintenant edi+1 pointe vers le premier caractère de chiffre valide
inc edi ; Corriger le pointeur, pointer vers le début de la chaîne
; Calculer la longueur des caractères valides
; output_buf + 11 - edi est la longueur valide
; Afficher le nombre
mov eax, 4
mov ebx, 1
mov ecx, edi ; Pointer vers la chaîne convertie
; Calculer la longueur
mov edx, output_buf + 11
sub edx, edi ; edx = fin du tampon - début de la chaîne
int 0x80
; Afficher un saut de ligne
mov eax, 4
mov ebx, 1
mov ecx, newline
mov edx, 1
int 0x80
mov eax, 1
mov ebx, 0
int 0x80
Résultat de l'exécution:
$ nasm -f elf32 int_to_string.asm -o int_to_string.o $ ld -m elf_i386 int_to_string.o -o int_to_string $ ./int_to_string 12345
Entrée et sortie hexadécimales
La gestion de l'hexadécimal nécessite de traiter à la fois la conversion des 0-9 et des A-F/a-f:
Exemple
; Afficher une valeur au format hexadécimal
section .data
number dd 0x1A2B3C4D ; Valeur à afficher
hex_prefix db '0x'
hex_prefix_len equ $ - hex_prefix
newline db 0xA
section .bss
hex_buf resb 10 ; 8 chiffres hexadécimaux + préfixe + null
section .text
global _start
_start:
; Afficher le préfixe "0x"
mov eax, 4
mov ebx, 1
mov ecx, hex_prefix
mov edx, hex_prefix_len
int 0x80
mov eax, [number] ; Valeur à convertir
mov edi, hex_buf + 8 ; Fin du tampon (8 chiffres hexadécimaux)
mov ecx, 8 ; Boucler 8 fois (32 bits = 8 chiffres hexadécimaux)
hex_loop:
mov edx, 0 ; Préparer la division
mov ebx, 16 ; Diviser par 16
div ebx ; Maintenant : eax divisé par ebx
; Remarque : div ebx ici est en fait edx:eax / ebx
; Reste dans edx (0-15), quotient dans eax
mov ebx, eax ; Sauvegarder le quotient
; Convertir le reste en caractère hexadécimal
mov al, dl
cmp al, 10
jl digit_09 ; 0-9
add al, 'A' - 10 ; A-F
jmp store_char
digit_09:
add al, '0' ; 0-9
store_char:
dec edi
mov [edi], al
mov eax, ebx ; Restaurer le quotient
loop hex_loop
; Boucle supprimée, utilisation directe de la méthode de décrémentation
; Afficher les chiffres hexadécimaux (8 chiffres)
mov eax, 4
mov ebx, 1
mov ecx, hex_buf
mov edx, 8
int 0x80
; Afficher un saut de ligne
mov eax, 4
mov ebx, 1
mov ecx, newline
mov edx, 1
int 0x80
mov eax, 1
mov ebx, 0
int 0x80
Exemple complet : une calculatrice d'addition simple
Saisir deux nombres à un chiffre au clavier, calculer leur somme et l'afficher :
Exemple
; Saisir deux nombres à un chiffre (0-9), calculer la somme et l'afficher
section .data
prompt1 db 'Entrez le premier nombre (0-9) : '
prompt1_len equ $ - prompt1
prompt2 db 'Entrez le deuxième nombre (0-9) : '
prompt2_len equ $ - prompt2
result_msg db 'Somme = '
result_msg_len equ $ - result_msg
newline db 0xA
section .bss
num1 resb 2 ; Entrée pour le premier nombre (1 chiffre + saut de ligne)
num2 resb 2 ; Entrée pour le deuxième nombre
result_str resb 3 ; Chaîne de résultat (jusqu'à 2 chiffres + saut de ligne)
section .text
global _start
_start:
; Invite à saisir le premier nombre
mov eax, 4
mov ebx, 1
mov ecx, prompt1
mov edx, prompt1_len
int 0x80
; Lire le premier nombre
mov eax, 3
mov ebx, 0
mov ecx, num1
mov edx, 2
int 0x80
; Inviter à saisir le deuxième nombre
mov eax, 4
mov ebx, 1
mov ecx, prompt2
mov edx, prompt2_len
int 0x80
; Lire le deuxième nombre
mov eax, 3
mov ebx, 0
mov ecx, num2
mov edx, 2
int 0x80
; Calculer la somme
mov al, [num1] ; Lire le premier nombre (caractère ASCII)
sub al, '0' ; Convertir en valeur numérique
mov bl, [num2] ; Lire le deuxième nombre
sub bl, '0' ; Convertir en valeur numérique
add al, bl ; Somme
; Convertir le résultat en chaîne de caractères
mov ah, 0 ; Préparer la division
mov bl, 10 ; Diviser par 10
div bl ; al = chiffre des dizaines, ah = chiffre des unités
; Enregistrer le résultat
add al, '0' ; Convertir le chiffre des dizaines en caractère
mov [result_str], al ; Stocker le chiffre des dizaines
add ah, '0' ; Convertir le chiffre des unités en caractère
mov [result_str + 1], ah ; Stocker le chiffre des unités
mov byte [result_str + 2], 0xA ; Nouvelle ligne
; Afficher le message de résultat
mov eax, 4
mov ebx, 1
mov ecx, result_msg
mov edx, result_msg_len
int 0x80
; Afficher le résultat du calcul (peut être 1 ou 2 chiffres)
mov eax, 4
mov ebx, 1
mov ecx, result_str
cmp byte [result_str], '0'
je output_one_digit ; Si le chiffre des dizaines est '0', afficher uniquement le chiffre des unités
mov edx, 3 ; 2 chiffres + saut de ligne
jmp do_output
output_one_digit:
inc ecx ; Sauter le chiffre des dizaines '0'
mov edx, 2 ; 1 chiffre + saut de ligne
do_output:
int 0x80
mov eax, 1
mov ebx, 0
int 0x80
Résultat de l'exécution:
$ nasm -f elf32 simple_calc.asm -o simple_calc.o $ ld -m elf_i386 simple_calc.o -o simple_calc $ ./simple_calc Enter first number (0-9): 7 Enter second number (0-9): 8 Sum = 15
Autres extensionsEn assembleur, la gestion des entrées/sorties numériques nécessite une compréhension claire de l'encodage ASCII et de la conversion de base. La conversion d'un nombre en chaîne (entier en chaîne) et d'une chaîne en nombre (chaîne en entier) sont les deux fonctions utilitaires les plus élémentaires. Il est recommandé de les enregistrer comme modèles après les avoir écrites, pour les réutiliser directement plus tard.