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 :

BaseRadixSyntaxe NASMPlage de nombres (32 bits)
Binary20b10101010ou10101010b0 et 1
Octal80o52ou52o0-7
Decimal10420-9
Hexadécimal160x2Aou2Ah0-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èreValeur ASCIIValeur 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

; Chemin du fichier : string_to_int.asm
; 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

; Chemin du fichier : int_to_string.asm
; 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

; Chemin du fichier: hex_output.asm
; 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

; Chemin du fichier : simple_calc.asm
; 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

En 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.

Autres extensions