IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 16-bits Assembleur Discussion :

Calcul simple (Premier programme : débutant) [TASM]


Sujet :

x86 16-bits Assembleur

  1. #1
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2025
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 19
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2025
    Messages : 9
    Par défaut Calcul simple (Premier programme : débutant) [TASM]
    Bonjour,
    J'ai voulu commencer l'assembleur donc j'ai lu et commencé à coder un programme simple qui permet de faire juste un calcul avec 2 nombres demandés à l'utilisateur et le choix de l'opération à faire sur ses 2 nombres.
    Je me retrouve face à des problèmes que je ne comprends pas.
    Si quelqu'un pourrait m'expliquer les erreurs pour que je puisse avancer dans ce premier programme, merci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    .model small
    .data 
        nombre1 db ?
        nombre2 db ?
        resultat db 0
        reste db 0
        buffer db 100 dup("$")
        operation db '$'
        message1 db "Choisissez un nombre : $"
        message2 db "Choisissez un deuwieme nombre : $"
        messageoperation1 db "addition : +, soustraction: - , multiplication : *, division : /$"
        messageoperation2 db "Inscrivez l'operateur  choisit : $"
        messagereste db "Reste : "
        divpar0 db "Erreur lors de la division (division par 0)$"
        messageerreur db "Erreur lors de la saisie$"
        messagefin db "Fin du programme$"
        retourligne db 0Dh, 0Ah, '$'
     
    .code
    entrer:
        mov ax, @data
        mov ds, ax
     
        lea dx, message1
        call afficher_message
        call saisie_utilisateur
     
        mov bl, 0
        call recuperation_nombre
        mov nombre1, bl
     
        lea dx, message2
        call afficher_message
        call saisie_utilisateur
     
        mov bl, 0
        call recuperation_nombre
        mov nombre2, bl
     
        lea dx, messageoperation1
        call afficher_message
        lea dx, messageoperation2
        call afficher_message
        mov dl, buffer[1]
     
        mov al, nombre1
        mov bl, nombre2
     
        cmp dl, '+'
        je addition
        cmp dl, '-'
        je soustraction
        cmp dl, '*'
        je multiplication
        cmp dl, '/'
        je division
     
        lea dx, messageerreur
        call afficher_message
        jmp fin
     
    afficher_message:
        mov ah, 09h
        int 21h
        call afficher_retourligne
        ret
     
    afficher_retourligne:
        mov ah, 09h
        lea dx, retourligne
        int 21h
        ret
     
    saisie_utilisateur:
        lea dx, buffer
        mov ah, 0Ah
        int 21h
        ret
     
    recupavantrep:
        mov bl, 10
        mov cl, al
        jmp repetition
    repetition:
        add cl, al
        dec bl
        cmp bl, 0
        jl repetition
        jmp recuperation_nombre
    recuperation_nombre:
        mov al, buffer[1]
        sub al, 30h
        add cl, al
        cmp buffer, 0
        jne recupavantrep
        ret
     
    addition:
        add al, bl
        jmp finprocess
     
    soustraction:
        sub al, bl
        jmp finprocess
     
    multiplication:
        cmp bl, 0
        je multi0
        mov cl, al
        jmp bouclemulti
    bouclemulti:
        add cl, al
        dec bl
        jnz bouclemulti
        jmp finprocess
     
    multi0:
        mov al,0
        jmp finprocess
     
    division:
        cmp bl, 0
        je division0
        mov cl, 0
        jmp bouclediv
    bouclediv:
        cmp bl, al
        jl suitediv
        sub al, bl
        inc cl
        cmp bl, al
        jg bouclediv
        jmp suitediv
    suitediv:
        mov resultat, cl
        mov reste, al
        mov al, resultat
        call toascii
        mov ah, 09h
        int 21h
        call afficher_retourligne
        mov ah, 09h
        lea dx, messagereste
        int 21h
        mov al, reste
        call toascii
        mov ah, 09h
        int 21h
        jmp fin
     
    division0:
        lea dx, divpar0
        call afficher_message
        jmp fin
     
    toascii:
        add al, 30h
        mov dl, al
        ret
     
    finprocess:
        mov resultat, al
        call toascii
        mov ah, 09h
        int 21h
        jmp fin
     
    fin:
        lea dx, messagefin
        call afficher_message
        mov ah,4ch
        int 21h
    end entrer
    Merci de votre temps et de m'avoir écouté

  2. #2
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 036
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 036
    Par défaut
    Bien que je m'en doute, quelles sont tes difficultés ? le but étant de ne pas te donner la solution mais de t'aider à comprendre ou se situe l'erreur.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  3. #3
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2025
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 19
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2025
    Messages : 9
    Par défaut
    Citation Envoyé par chrtophe Voir le message
    Bien que je m'en doute, quelles sont tes difficultés ? le but étant de ne pas te donner la solution mais de t'aider à comprendre ou se situe l'erreur.
    Bonjour,
    Les difficultés que je rencontre je ne les connais pas bien. Vu que je débute je ne trouve pas l'erreur même si le debugger s'arrête a la ligne 34
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    call saisie_utilisateur
    Les questions que je me pose sont :
    Est ce que je manipule les registre correctement ou alors il y a de gros problème avec ceux si dans se code?
    L'appelle de jump est t'il obligatoire pour passez d'un label a l'autre même si celui ci est dans la continuité du code?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    recupavantrep:
        mov bl, 10
        mov cl, al
        jmp repetition ;cette instruction par exemple
    repetition:
    Et comment fonctionne exactement le buffer si c'est comme en c ou non?

    Voila a peu près se qui me pause problème et vous l'avez bien dit je ne veux pas de solutions direct mais plutôt trouver comment cela fonctionne exactement et corriger par moi mémé pour ne plus avoir ses problème au futur.

  4. #4
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 036
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 036
    Par défaut
    remplaces :
    par :
    L'appelle de jump est t'il obligatoire pour passez d'un label a l'autre même si celui ci est dans la continuité du code?
    là c'est inutile.

    Une chaine en assembleur c'est effectivement un peu comme en C. En C ça doit finir par \0, avec les fonctions DOS, ça doit finir par $.

    Tu passe en paramètre aux fonctions DOS l'adresse de début de la chaine.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  5. #5
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2025
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 19
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2025
    Messages : 9
    Par défaut
    Citation Envoyé par chrtophe Voir le message
    remplaces :
    par :

    là c'est inutile.

    Une chaine en assembleur c'est effectivement un peu comme en C. En C ça doit finir par \0, avec les fonctions DOS, ça doit finir par $.

    Tu passe en paramètre aux fonctions DOS l'adresse de début de la chaine.
    Pour le changement de lea -> mov j'ai été obliger de rajouter offset pour que cela fonctionne
    J'ai corriger les jump inutile
    Revu la structure du programme car elle était incomplète j'ai l'impression

    Après tout cela le code recupere bien les saisie de l'utilisateur mais j'ai des probleme sur la recuperation de nombre.

    J'ai des soucis a utiliser le buffer que j'ai reussi a mettre en place surtout a recuperer son contenue pour le mettre dans mes variable globale.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
        mov bl, 0 ;mettre 0 dans bl
        xor dx, dx ;mettre 0 tout dx (dl, dh)
        call recuperation_nombre ;appelle de la fonction recuperation_nombre
        mov nombre1, bl ;mettre bl dans la data nombre1
     
    ;... autre code
     
    recupavantrep: ;label recupavantrep
        mov bl, 10 ;mettre 10 dans bl
        mov cl, al ;mettre al dans cl
        inc dl
    repetition: ;label repetition
        add cl, al ;ajouter al a cl
        dec bl ;decrementer de 1 bl
        cmp bl, 0 ;comparaison de 0 avec bl
        jg repetition ;si 0 est strictement plus grand que bl saut au label repetition
    recuperation_nombre: ;label recuperation_nombre
        mov bx, dx
        mov al, buffer[bx] ;mettre le premier charactere du buffer dans al
        sub al, '0' ;convertion de al en nombre
        add cl, al ;ajouter al a cl
        cmp dl, longueur ;comparaison de 0 avec le buffer (buffer vide)
        jne recupavantrep ;si la comparaison n'est pas egal saut au label recupavantrep
        ret ;retour au programme d'origine
    Sachant que la recuperation de la longueur a un probleme aussi donc je ne comprend pas bien comment recuperer l'interieur du buffer ou alors le parcourir pour faire des comparaison comme une boucle while.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
        call lenstart
     
    ;... autre code
     
    lenstart:
        xor ax, ax ;mettre tout ax a 0 (al, ah)
        jmp len
    leninc:
        inc ax
    len:
        mov bx, ax
        mov bl, buffer[bx]
        cmp bl, '$'
        jne leninc
        mov longueur, al
        ret
    Voila ou j'en suis exactement dans se programme
    (programme entier)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
     
    .model small
    .data 
        nombre1 db ? ;nombre choisis 1
        nombre2 db ? ;nombre choisis 2
        longueur db 0 ;longueur initialiser a 0
        resultat db 0 ;resultat initialiser a 0
        reste db 0 ;reste initialiser a 0
        buffer db 10 dup("$") ;initialisation du buffer
        operation db '$' ;operation initialiser a un char
        message1 db "Choisissez un nombre : $" ;chaine de charactere
        message2 db "Choisissez un deuxieme nombre : $" ;chaine de charactere
        messageoperation1 db "addition : +, soustraction: - , multiplication : *, division : /$" ;chaine de charactere
        messageoperation2 db "Inscrivez l'operateur  choisit : $" ;chaine de charactere
        messagereste db "Reste : " ;chaine de charactere
        divpar0 db "Erreur lors de la division (division par 0)$" ;chaine de charactere
        messageerreur db "Erreur lors de la saisie$" ;chaine de charactere
        messagefin db "Fin du programme$" ;chaine de charactere
        retourligne db 0Dh, 0Ah, '$' ;char de retour a la ligne
     
    .code
    entrer: ;label d'entrer
        mov ax, @data ;debut duprogramme (a comprendre)
        mov ds, ax
     
        mov dx, offset message1 ;charger l'adresse du message1 dans dx
        call afficher_message ;appelle de la fonction afficher_message
        call saisie_utilisateur ;appelle de la fonction saisie_utilisateur
        call lenstart
     
        mov bl, 0 ;mettre 0 dans bl
        xor dx, dx ;mettre 0 tout dx (dl, dh)
        call recuperation_nombre ;appelle de la fonction recuperation_nombre
        mov nombre1, bl ;mettre bl dans la data nombre1
     
        mov dx, offset message2 ;charger l'adresse du message2 dans dx
        call afficher_message ;appelle de la fonction afficher_message
        call saisie_utilisateur ;appelle de la fonction saisie_utilisateur
        call lenstart
     
        mov bl, 0 ;mettre 0 dans bl
        xor dx, dx ;mettre 0 tout dx (dl, dh)
        call recuperation_nombre ;appelle de la fonction recuperation_nombre
        mov nombre2, bl ;mettre bl dans la data nombre2
     
        mov dx, offset messageoperation1 ;charger l'adresse du messageoperation1 dans dx
        call afficher_message ;appelle de la fonction afficher_message
        mov dx, offset messageoperation2 ;charger l'adresse du messageoperation2 dans dx
        call afficher_message ;appelle de la fonction afficher_message
        call saisie_utilisateur ;appelle de la fonction saisie_utilisateur
        mov dl, buffer[0] ;mettre le premier charactere du buffer dans dl
     
        mov al, nombre1 ;mettre le contenue de nombre1 dans al
        mov bl, nombre2 ;mettre le contenue de nombre2 dans bl
     
        cmp dl, '+' ;comparaison du char + avec dl 
        je addition ;si la comparaison retourne vrai alors saut au label addition
        cmp dl, '-' ;comparaison du char - avec dl
        je soustraction ;si la comparaison retourne vrai alors saut au label soustraction
        cmp dl, '*' ;comparaison du char * avec dl
        je multiplication ;si la comparaison retourne vrai saut au label multiplication
        cmp dl, '/' ;comparaison du char / avec dl
        je division ;si la comparaison retourne vrai alor saut au label division
     
        mov dx, offset messageerreur ;charger le messageerreur dans dx
        call afficher_message ;appelle de la fonction affcher_message
        jmp fin ;saut au label fin
     
    afficher_message: ;label afficher_message
        mov ah, 09h ;a comprendre
        int 21h ;appelle de l'interuption 21h (a comprendre)
        call afficher_retourligne ;appelle de la fonction afficher_retourligne
        ret ;retour au programme d'origine
     
    afficher_retourligne: ;label afficher_retourligne
        mov ah, 09h ;a comprendre
        mov dx, offset retourligne ;charger l'adresse de retour_ligne dans dx
        int 21h ;appelle de l'interuption 21h (a comprendre)
        ret ;retour au programme d'origine
     
    saisie_utilisateur: ;label saisie_utilisateur
        mov dx, offset buffer ;charger l'adresse du buffer dans dx
        mov ah, 0Ah ;a comprendre
        int 21h ;appelle de l'interuption 21h (a comprendre)
        ret ;retour au programme d'origine
     
    recupavantrep: ;label recupavantrep
        mov bl, 10 ;mettre 10 dans bl
        mov cl, al ;mettre al dans cl
        inc dl
    repetition: ;label repetition
        add cl, al ;ajouter al a cl
        dec bl ;decrementer de 1 bl
        cmp bl, 0 ;comparaison de 0 avec bl
        jg repetition ;si 0 est strictement plus grand que bl saut au label repetition
    recuperation_nombre: ;label recuperation_nombre
        mov bx, dx
        mov al, buffer[bx] ;mettre le premier charactere du buffer dans al
        sub al, '0' ;convertion de al en nombre
        add cl, al ;ajouter al a cl
        cmp dl, longueur ;comparaison de 0 avec le buffer (buffer vide)
        jne recupavantrep ;si la comparaison n'est pas egal saut au label recupavantrep
        ret ;retour au programme d'origine
     
    lenstart:
        xor ax, ax ;mettre tout ax a 0 (al, ah)
        jmp len
    leninc:
        inc ax
    len:
        mov bx, ax
        mov bl, buffer[bx]
        cmp bl, '$'
        jne leninc
        mov longueur, al
        ret
     
    addition: ;label addition
        add al, bl ;ajout de bl dans al
        jmp finprocess ;saut au label finprocess
     
    soustraction: ;label soustraction
        sub al, bl ;soustrait bl a al
        jmp finprocess ;saut au label finprocess
     
    multiplication: ;label multiplication
        cmp bl, 0 ;compare 0 avec bl
        je multi0 ;si la comparaison retourne vrai saut au label multi0
        mov cl, al ;mettre al dans cl
    bouclemulti: ;label bouclemulti
        add cl, al ;ajouter al a cl
        dec bl ;decrementer bl de 1
        jnz bouclemulti ;si le flag Z n'est pas set saut au label bouclemulti (z flag set lorsque bl vaut 0)
        jmp finprocess ;saut au label finprocess
     
    multi0: ;labelmulti0
        mov al, 0 ;mettre 0 dans al
        jmp finprocess ;saut au label finprocess
     
    division: ;label division
        cmp bl, 0 ;comparaison de 0 avec bl
        je division0 ;si la comparaison retourne vrai saut au label division0
        mov cl, 0 ;mettre 0 dans cl
        cmp bl, al ;comparaison de al avec bl
        jl suitediv ;si al est plus petit que bl saut au label suitediv
    bouclediv: ;label bouclediv
        sub al, bl ;soustrait bl de al
        inc cl ;incrementation de 1 a cl
        cmp al, bl ;comparaison de al avec bl
        jge bouclediv ;si al est plus grand que bl saut au label bouclediv
    suitediv: ;label suitediv
        mov resultat, cl ;mettre cl dans resultat
        mov reste, al ;mettre al dans reste
        mov al, resultat ;mettre resultat dans al
        call toascii ;appelle de la fonction toascii
        mov ah, 09h ;a comprendre
        int 21h ;appelle de l'interuption 21h (a comprendre)
        call afficher_retourligne ;appelle de la fonction afficher_retourligne
        mov ah, 09h ;a comprendre
        mov dx, offset messagereste ;charger l'adresse de messagereste dans dx
        int 21h ;appelle de l'interuption 21h (a comprendre)
        mov al, reste ;mettre reste dans al
        call toascii ;apppelle de la fonction toascii
        mov ah, 09h ;a comprendre
        int 21h ;appelle de l'interuption 21h (a comprendre)
        jmp fin ;saut au label fin
     
    division0: ;label division0
        mov dx, offset divpar0 ;charger l'adresse de divpar0 dans dx
        call afficher_message ;appelle de la fonction afficher message
        jmp fin ;saut au label fin
     
    toascii: ;label toascii
        add al, '0' ;convertion de nombre en ascii
        mov dl, al ;mettre al dans dl
        ret ;retour au programme d'origine
     
    finprocess: ;label finprocess
        mov resultat, al ;mettre al dans resultat
        call toascii ;appelle de la fonction toascii
        mov ah, 09h ;a comprendre
        int 21h ;appelle de l'interuption 21h (a comprendre)
        jmp fin ;saut au label fin
     
    fin: ;label fin
        mov dx, offset messagefin ;charger l'adresse du messagefin dans dx
        call afficher_message ;appelle de la fonction afficher_message
        mov ah,4ch ;a comprendre (pour fin du programme)
        int 21h ;appelle de l'interuption 21h (a comprendre) (pour fin du programme)
    end entrer ;fin du label entrer

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 417
    Par défaut
    Bonjour et bienvenue,

    Citation Envoyé par Devoghlockst Voir le message
    Si quelqu'un pourrait m'expliquer les erreurs pour que je puisse avancer dans ce premier programme, merci :D
    Oui mais effectivement, il faut prendre l'habitude de bien lire les messages d'erreur quand ils apparaissent (en assembleur, on travaille sans filet, on a beaucoup moins de chances d'en recevoir) et de bien les transmettre ensuite sur les forums car, naturellement, les participants ne sont pas derrière l'écran pour les voir. ;-)

    Avant toute chose : avec quelle plateforme (et sous quel système d'exploitation) développes-tu ? Ce que tu écris est un programme DOS, TASM l'est aussi. J'imagine que tu utilises un émulateur (à moins que la ligne de commande Windows soit encore capable de le faire, mais il me semblait qu'elle avait cessé de le faire depuis longtemps…)

    Comme il n'y a pas de contrôle à plus haut niveau, les deux seules choses qui pourraient faire s'arrêter le débugueur à une ligne donnée sont soit un breakpoint, soit une segfault mais elles n'existent pas sous DOS en mode réel, a priori, sauf usage spécial de la mémoire avec EMM386 ou le mode V86 (ça commence à être vieux…) ;

    Citation Envoyé par Devoghlockst Voir le message
    Bonjour,
    Les difficultés que je rencontre je ne les connais pas bien. Vu que je débute je ne trouve pas l'erreur même si le debugger s'arrête a la ligne 34
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    call saisie_utilisateur
    Et comment fonctionne exactement le buffer si c'est comme en c ou non?
    Pas dans le cas présent et je crois que tu as mis le doigt dessus !

    Chrtophe a raison quand il te dit qu'en effet, on utilise souvent le format C en assembleur pour les chaînes de caractères (zero-terminated) mais ce n'est pas une convention stricte (à dire vrai, les « chaînes de caractères » ne sont même pas définies comme type officiel à ce niveau non plus) et DOS utilise très souvent des dollar-terminated strings, donc des chaînes terminées par un dollar comme sentinelle finale plutôt qu'un caractère nul. Exemple : "Bonjour$". Je vois d'ailleurs que les données en tête de ton programme utilisent déjà ce format.

    Par ailleurs, ta sous-routine saisie_utilisateur utilise la fonction 0ah de l'interruption int 21h, laquelle respecte un format spécial décrit ici : https://www.gladir.com/LEXIQUE/INTR/int21f0a.htm

    En résumé, DS:DX doit ici pointer un descripteur composé de :
    • Un octet indiquant la longueur du buffer de saisie (donc maximum 255 caractères), suivi de
    • Un octet à n'importe quelle valeur (préférablement zéro) qui va recevoir la longueur de la chaîne saisie, retour à la ligne exclu, suivi de
    • n octets, la valeur n devant justement être déposée dans le premier octet de cette liste.


    Or, tu pointes vers un buffer de 100 octets remplis par des dollars $. Le code ASCII du dollar est 36 (ou 24h en hexadécimal). Ceci devrait donc malgré tout t'accorder 36 octets pour saisir ton nombre, ce qui devrait être plus que suffisant. En revanche, dans recuperation_nombre à la ligne 91, on lit :

    D'abord, je me trompe peut-être mais même s'il existe bel et bien des adressages indexés en x86 comme sur d'autres architectures, je crois que cette syntaxe, valide en langage C, ne l'est pas en assembleur x86. Mais comme elle se résout en une adresse constante et qu'elle est équivalente à « buffer + 1 », j'imagine que c'est TASM qui la tolère. Attention toutefois avec les comparaisons : il est de notoriété publique, en C, que buffer[1] soit équivalent à buffer + 1 mais pas pour les mêmes raisons. En assembleur, il s'agit d'une simple expression arithmétique évaluable par le précompilateur et travaillant sur des entiers. En C, il y a une vraie « arithmétique des pointeurs » qui est dotée de ses propres règles.

    Mais quoi qu'il en soit : tu lis ici l'octet d'offset 1, c'est-à-dire le deuxième dans le buffer pointé par DS:DX lors de saisie_utilisateur. Tu récupères donc ici la taille de la chaîne lue et pas le code ASCII du chiffre saisi.

    Je vois également que tu compares « buffer » avec 0 comme condition de sortie, alors qu'il s'agit d'une constante (étiquette donnée à ton compilateur et utilisée pour représenter l'adresse en mémoire de ton buffer) et surtout que tu ne tentes jamais de le décrémenter à chaque itération. Ça ne plante donc pas mais tu entres ici dans une boucle infinie dont tu ne sors jamais et c'est cela qui doit te donner l'impression que tu restes bloqué à la ligne 34.

    Je vois également qu'à plusieurs étapes (repetition et multiplication), tu implémentes des boucles pour effectuer des multiplications. C'est une très bonne chose en soi d'y être arrivé mais sache qu'il existe l'instruction MUL pour cela. La plupart des micro-processeurs, mêmes modestes, en sont pourvus. Ce n'est pas le cas de DIV qui est nettement plus difficile à réaliser. Le x86 la propose, mais la plupart des 8 bits, par exemple, ne l'ont jamais eu.

    Enfin, le x86 en particulier propose également LODSB, STOSB et MOVSB (sur 8 bits, respectivement ...W sur 16), qui signifient « Load String », « Store String » et « Move String ». Par « chaîne », il s'agit en fait de travailler sur des octets (dans AL) ou des mots de 16 bits (AX) et d'incrémenter automatiquement les pointeurs de données source et destination. MOVS- effectuant un LODS- suivi d'un STOS- en une seule opération.

    Il y a d'autres points que l'on pourrait traiter ici mais cela fera l'objet d'un prochain message.
    Bon courage.

  7. #7
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 036
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 036
    Par défaut
    Chrtophe a raison quand il te dit qu'en effet, on utilise souvent le format C en assembleur pour les chaînes de caractères (zero-terminated) mais ce n'est pas une convention stricte
    Effectivement, cela dépend de l'OS. En utilisant les API MS-DOS, une chaine doit se terminer par $, comme je l'évoquais. En utilisant le BIOS (pas UEFI, le mode Legacy) avec int 10h pour afficher une chaine, tu dois lui indiquer la longueur dans un des registres, idem avec Linux il me semble.

    D'abord, je me trompe peut-être mais même s'il existe bel et bien des adressages indexés en x86 comme sur d'autres architectures, je crois que cette syntaxe, valide en langage C, ne l'est pas en assembleur x8
    Peut-être spécifique à TASM, comme l'histoire de offset.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  8. #8
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2025
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 19
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2025
    Messages : 9
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Bonjour et bienvenue,

    Oui mais effectivement, il faut prendre l'habitude de bien lire les messages d'erreur quand ils apparaissent (en assembleur, on travaille sans filet, on a beaucoup moins de chances d'en recevoir) et de bien les transmettre ensuite sur les forums car, naturellement, les participants ne sont pas derrière l'écran pour les voir. ;-)
    Justement je ne vois pas bien les messages d'erreur car tant que le programme s'assemble et se build je n'ai pas de message d'erreur après, la seule chose que je regarde c'est le pas a pas au debugger

    Citation Envoyé par Obsidian Voir le message
    Avant toute chose : avec quelle plateforme (et sous quel système d'exploitation) développes-tu ? Ce que tu écris est un programme DOS, TASM l'est aussi. J'imagine que tu utilises un émulateur (à moins que la ligne de commande Windows soit encore capable de le faire, mais il me semblait qu'elle avait cessé de le faire depuis longtemps…)
    Alors je suis sous windows sur un intel mais a propos de se que j'utilise j'ai trouver que ces explications

    "GUI Turbo Assembler is an essential IDE for assembly language programming. It includes Borland Turbo Assembler (TASM), Turbo Linker (TLINK), Turbo Debugger (TD) and DOSBox, making it a comprehensive solution for assembly development in both x86 and x64 environments. Both 16-bit and 32-bit mode of Turbo Assembler (TASM32), Linker (TLINK32) and Debugger (TD32) are supported." V5.1

    je ne passe pas par le cmd pour assembler et build l'exécutable, les commande sont disponible sur l'ide déjà.

    Citation Envoyé par Obsidian Voir le message
    Comme il n'y a pas de contrôle à plus haut niveau, les deux seules choses qui pourraient faire s'arrêter le débugueur à une ligne donnée sont soit un breakpoint, soit une segfault mais elles n'existent pas sous DOS en mode réel, a priori, sauf usage spécial de la mémoire avec EMM386 ou le mode V86 (ça commence à être vieux…) ;
    Sur se que j'utilise je peut insérer des breakpoint depuis le debugger

    Merci pour ce lien je vais le garder de coté

    Citation Envoyé par Obsidian Voir le message
    D'abord, je me trompe peut-être mais même s'il existe bel et bien des adressages indexés en x86 comme sur d'autres architectures, je crois que cette syntaxe, valide en langage C, ne l'est pas en assembleur x86. Mais comme elle se résout en une adresse constante et qu'elle est équivalente à « buffer + 1 », j'imagine que c'est TASM qui la tolère. Attention toutefois avec les comparaisons : il est de notoriété publique, en C, que buffer[1] soit équivalent à buffer + 1 mais pas pour les mêmes raisons. En assembleur, il s'agit d'une simple expression arithmétique évaluable par le précompilateur et travaillant sur des entiers. En C, il y a une vraie « arithmétique des pointeurs » qui est dotée de ses propres règles.

    Mais quoi qu'il en soit : tu lis ici l'octet d'offset 1, c'est-à-dire le deuxième dans le buffer pointé par DSX lors de saisie_utilisateur. Tu récupères donc ici la taille de la chaîne lue et pas le code ASCII du chiffre saisi.
    J'ai pas bien compris cette partie :
    buffer[1] est donc buffer + 1 et pas a sont indice dans le buffer.
    Et en cas de comparaison buffer[1] récupère la taille de la chaine ?
    Se qui me permettrais peut être de supprimer tout ma partie len: qui retourne la longueur du buffer

    Citation Envoyé par Obsidian Voir le message
    Je vois également que tu compares « buffer » avec 0 comme condition de sortie, alors qu'il s'agit d'une constante (étiquette donnée à ton compilateur et utilisée pour représenter l'adresse en mémoire de ton buffer) et surtout que tu ne tentes jamais de le décrémenter à chaque itération. Ça ne plante donc pas mais tu entres ici dans une boucle infinie dont tu ne sors jamais et c'est cela qui doit te donner l'impression que tu restes bloqué à la ligne 34.
    Effectivement ceci est une belle erreur de ma part en prenant pour acquis le fait que mon buffer soit une pile et donc vider a chaque fois que je récupérer un élément dans celui-ci une sorte de pop()

    Citation Envoyé par Obsidian Voir le message
    Je vois également qu'à plusieurs étapes (repetition et multiplication), tu implémentes des boucles pour effectuer des multiplications. C'est une très bonne chose en soi d'y être arrivé mais sache qu'il existe l'instruction MUL pour cela. La plupart des micro-processeurs, mêmes modestes, en sont pourvus. Ce n'est pas le cas de DIV qui est nettement plus difficile à réaliser. Le x86 la propose, mais la plupart des 8 bits, par exemple, ne l'ont jamais eu.
    Effectivement les instructions MUL et DIV sont possibles mas pour l'instant je préfère garder le premier programme sans trop d'instruction déjà préconstruite pour me familiariser avec cette environnement.

    Citation Envoyé par Obsidian Voir le message
    Enfin, le x86 en particulier propose également LODSB, STOSB et MOVSB (sur 8 bits, respectivement ...W sur 16), qui signifient « Load String », « Store String » et « Move String ». Par « chaîne », il s'agit en fait de travailler sur des octets (dans AL) ou des mots de 16 bits (AX) et d'incrémenter automatiquement les pointeurs de données source et destination. MOVS- effectuant un LODS- suivi d'un STOS- en une seule opération.
    Je ne vois pas en quelle cas cela irait dans mon programme directement (peut être l'affichage)
    il faudrait que j'approfondisse plus le fonctionnement pour l'utiliser dans ce cas.

    Merci pour toutes ses explications normalement il y a une version un petit peu modifier du code sur mon dernier message qui enleve les boucle infinie avec des boucle incrementer cette fois.

  9. #9
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2025
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 19
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2025
    Messages : 9
    Par défaut
    Citation Envoyé par chrtophe Voir le message
    Effectivement, cela dépend de l'OS. En utilisant les API MS-DOS, une chaine doit se terminer par $, comme je l'évoquais. En utilisant le BIOS (pas UEFI, le mode Legacy) avec int 10h pour afficher une chaine, tu dois lui indiquer la longueur dans un des registres, idem avec Linux il me semble.



    Peut-être spécifique à TASM, comme l'histoire de offset.
    Oui j'ai du omettre certains détails cruciale pour que vous puissiez m'aidez j'en suis désoler.
    Mais donc il existe plein de version de langage assembleur suivant la configuration sur laquelle on est? (à part la structure x16/ x32/ x64 /x86)

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 417
    Par défaut
    Citation Envoyé par Devoghlockst Voir le message
    "GUI Turbo Assembler is an essential IDE for assembly language programming. It includes Borland Turbo Assembler (TASM), Turbo Linker (TLINK), Turbo Debugger (TD) and DOSBox, making it a comprehensive solution for assembly development in both x86 and x64 environments. Both 16-bit and 32-bit mode of Turbo Assembler (TASM32), Linker (TLINK32) and Debugger (TD32) are supported." V5.1
    D'accord, donc tu utilises DOSBox qui est effectivement l'émulateur DOS favori du moment et à l'intérieur de cet émulateur, tu utilises la suite Borland qui était également la plus populaire en son temps.
    C'est effectivement la manière la plus indiquée de faire de l'assembleur x86 aujourd'hui.

    Sur se que j'utilise je peut insérer des breakpoint depuis le debugger
    Les breakpoints oui. Par contre, il n'y avait pas encore de mode protégé sur 8086, donc pas de segfault en cas de sortie de ta zone allouée. Si tu fais un dépassement de buffer en écriture, tu risques d'écraser potentiellement le système entier et c'était vrai sur tous les ordinateurs des années 1980…

    J'ai pas bien compris cette partie :
    buffer[1] est donc buffer + 1 et pas a sont indice dans le buffer.
    Et en cas de comparaison buffer[1] récupère la taille de la chaine ?
    Se qui me permettrais peut être de supprimer tout ma partie len: qui retourne la longueur du buffer
    Si mais justement : en langage C et dans presque tous les autres langages, le premier élément d'un tableau est buffer[0] et pas buffer[1] car l'index, donc le numéro d'indice, est en fait un offset par rapport au début du tableau.

    Mais ici, je soulignais le fait que cette syntaxe dans ce cas précis risque de ne pas compiler avec d'autres assembleurs, justement parce qu'il ne s'agit pas d'un des modes d'adressages du micro-processeur. C'est une forme qui est tolérée par TASM et ce n'est pas forcément une bonne chose parce qu'elle ressemble beaucoup à d'autres cas qui eux sont légitimes et qu'il vient difficile de faire la distinction.

    Quoi qu'il en soit, en l'occurrence, si tu veux lires les premiers caractères d'un buffer, alors il faut pointer le début de ce buffer, c'est-à-dire charger dans un registre l'adresse de ce buffer (donc une constante), de se servir de ce registre comme index, donc lire ce qui se trouve « à l'adresse indiquée par le registre » (ici DX), puis incrémenter ce registre pour passer à l'adresse suivante.

    Effectivement ceci est une belle erreur de ma part en prenant pour acquis le fait que mon buffer soit une pile et donc vider a chaque fois que je récupérer un élément dans celui-ci une sorte de pop()
    Tu touches là du doigt quelque chose qui fait à la fois l'attrait et la difficulté de l'assembleur : d'abord il y a bien des piles (au moins une) prises en charge par les microprocesseurs. C'est notamment ce qui va leur permettre d'implémenter CALL ou ce qui en tient lieu. Sur x86, c'est SP qui a la fonction de pointeur de pile.

    Par contre, toutes les structures de données classiques, comme les piles, files, tableaux, etc. sont des structures abstraites a priori. Même si en informatique et spécialement en informatique théorique, on peut faire des calculs sur leur seule fonction, il arrive un moment où il faut effectivement les mettre en œuvre et le langage machine est la dernière limite.

    Effectivement les instructions MUL et DIV sont possibles mas pour l'instant je préfère garder le premier programme sans trop d'instruction déjà préconstruite pour me familiariser avec cette environnement.
    C'est une bonne chose pour l'exercice.

    Par contre, si tu peux raisonnablement mettre DIV de côté pour le moment, parce qu'elle n'est disponible que sur un petit nombre d'infrastructures et parce qu'elle implique de savoir gérer les exceptions et les interruptions, tu peux d'ores et déjà t'appuyer sur MUL parce que son fonctionnement est sûr et déterministe. C'est aussi un bon moyen de s'habituer à la composition des accu (du style CX = CH | CL) et au calcul en virgule fixe.

    Je ne vois pas en quelle cas cela irait dans mon programme directement (peut être l'affichage)
    il faudrait que j'approfondisse plus le fonctionnement pour l'utiliser dans ce cas.
    Là encore, cela n'existe pas sur tous les micro-processeurs, mais sur x86 c'est extrêmement utilisé parce que ça permet de lire ou écrire facilement une séquence d'octets, voire de la copier d'un endroit à un autre, le tout en une seule opération. Mais effectivement, tu peux t'habituer à le faire de façon ordinaire dans un premier temps.


    Citation Envoyé par Devoghlockst Voir le message
    Oui j'ai du omettre certains détails cruciale pour que vous puissiez m'aidez j'en suis désoler.
    Mais donc il existe plein de version de langage assembleur suivant la configuration sur laquelle on est?
    Oui : chaque modèle de microprocesseur a son assembleur propre, avec souvent des mnémoniques propres à son fabricant, même s'ils fonctionnent tous globalement de la même façon.

    (à part la structure x16/ x32/ x64 /x86)
    Une remarque : les architectures 16 bits, 32 bits et aujourd'hui 64 bits semblent correspondre simplement au doublement de la largeurs des bus et du format des données manipulées mais dans la pratique, elles correspondent souvent à des grandes générations au cours du temps, un peu comme la 3G, 4G ou 5G avec la téléphonie mobile ou l'ère des avions à hélices et ceux à réactions.

    « x86 » en revanche, désigne les déclinaisons du microprocesseur « 8086 » en particulier (qui était un 16 bits depuis le départ).

    L'usage dans la nomenclature des circuits électroniques (donc pas du tout le monde de l'informatique a priori) veut que lorsque qu'une puce appartient à une famille de composants similaires, la partie gauche du numéro soit attribué à son fabricant, par exemple 80 pour Intel et 68 pour Motorola, et que la partie droite corresponde au modèle du circuit. C'est ce qui se passe avec la série 74xx ou la série 40xx.

    Lorsqu'un fabricant fait évoluer une même puce au cours du temps, il peut insérer un numéro de version sous forme d'un chiffre ou d'une lettre intercalé entre ces deux parties.

    Par exemple, Motorola a produit, dans les années 70 et 80, le 6800, le 6805 et le 6809 (et de nombreux autres circuits). Ces microprocesseurs étaient notoirement cadencés à 1 Mhz, mais il a été produit deux déclinaisons un peu plus rapides, pouvant respectivement être cadencés à 1,5 Mhz et 2 Mhz. Ces deux versions ont reçu les matricules 68A09 et 68B09.

    Le 8086 (et sa version économique 8088) au départ a connu un succès fulgurant parce que c'est lui qui a été choisi pour équiper l'IBM PC. Intel l'a donc naturellement fait évoluer au cours du temps et a donc immatriculé ses versions 80186, 80286, 80386, 80486. Ses utilisateurs les ont naturellement désignés « 186 », « 286 », « 386 » si bien que la maison-mère a fini par leur donner un nom commercial : d'abord « i386 » ou « i486 » (i pour Intel) puis, à partir du 586, on les a appelés… « Pentium ».

    Tout cela pour dire que l'expression « x86 » n'est pas un préfixe multiplicateur. C'est simplement un diminutif pour « 80x86 » ou « x » se substitue à un chiffre quelconque et qui désigne en fait l'architecture dérivée du 8086 et qui évolue depuis presque cinquante ans maintenant.

Discussions similaires

  1. problèmes d'erreurs concernant mes deux premiers programmes simples
    Par 808boomm dans le forum Débuter avec Java
    Réponses: 5
    Dernier message: 16/09/2019, 01h12
  2. Premier programme python petit, simple mais buggé
    Par Leeloo.at dans le forum Général Python
    Réponses: 5
    Dernier message: 28/01/2016, 20h41
  3. [TASM] Premier programme en ASM : directives de TASM
    Par sushis dans le forum x86 16-bits
    Réponses: 0
    Dernier message: 06/08/2009, 17h37
  4. Réponses: 9
    Dernier message: 12/10/2006, 01h36
  5. Calcul simple pour code couleur
    Par Boumeur dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 16/04/2005, 11h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo