Vous êtes sur la page 1sur 37

Mthodologie de programmation en assembleur e

Philippe Preux 24 novembre 1997

Table des mati`res e


1 Introduction 2 Mthodologie e 2.1 Mthodologie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e 2.2 Aperu du langage dassemblage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . c 3 Exemple 3.1 Programme ` crire . . . . . . . . . . . . . . . . . ae 3.2 Analyse et algorithme . . . . . . . . . . . . . . . 3.3 La ralisation du programme assembleur . . . . . e 3.3.1 Structure du programme . . . . . . . . . . 3.3.2 Dnition du corps des sous-programmes e 3.3.3 Traduction des expressions . . . . . . . . 3.3.4 Allocation des pseudo-variables . . . . . . 3.3.5 Derniers ajustements . . . . . . . . . . . . 3.3.6 Programme termin . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 4 4 4 6 6 6 7 7 8 12 12 15 17 19 19 19 20 20 20 20 21 22 22 23 23 24 24 25 26 26 26 26 27 28 28 28 28 29

4 Guide pour la traduction de structures de haut niveau en assembleur 4.1 Expression arithmtique et logique . . . . . . . . . . . . . . . . . . . . . . e 4.1.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.2 Laectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Squence dinstructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . e 4.3 Les tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.2 Conditions simples . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.3 La condition est une expression . . . . . . . . . . . . . . . . . . . . 4.3.4 Conditions composes . . . . . . . . . . . . . . . . . . . . . . . . . e 4.4 Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4.1 Boucle tant-que . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4.2 Boucle rpter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e e 4.4.3 Boucle pour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 Procdures et fonctions : principe des sous-programmes . . . . . . . . . . . e 4.5.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5.2 Appel dun sous-programme . . . . . . . . . . . . . . . . . . . . . . 4.6 Sous-programmes avec param`tres et variables locales . . . . . . . . . . . e 4.6.1 Passage de param`tre en entre du sous-programme . . . . . . . . e e 4.6.2 Rception des param`tres . . . . . . . . . . . . . . . . . . . . . . . e e 4.6.3 Variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6.4 Valeur de retour . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.7 Traduction des pseudo-variables . . . . . . . . . . . . . . . . . . . . . . . . 4.7.1 Les variables globales du programme . . . . . . . . . . . . . . . . . 4.7.2 Les variables temporaires . . . . . . . . . . . . . . . . . . . . . . .

4.7.3 Les instructions de multiplication et de division 4.7.4 Les param`tres . . . . . . . . . . . . . . . . . . e 4.7.5 Les variables locales . . . . . . . . . . . . . . . 4.7.6 La valeur de retour dun sous-programme . . . 4.8 Les tableaux en assembleur . . . . . . . . . . . . . . . 4.9 Les constantes . . . . . . . . . . . . . . . . . . . . . . 4.10 Programme assembleur minimal . . . . . . . . . . . . . 4.11 Quelques fonctions utiles . . . . . . . . . . . . . . . . . 4.11.1 Acher un caract`re . . . . . . . . . . . . . . . e 4.11.2 Acher un nombre positif . . . . . . . . . . . . 4.11.3 Lire un caract`re . . . . . . . . . . . . . . . . . e

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

29 30 30 32 32 33 33 33 34 34 35

Chapitre 1

Introduction
Dans ce document, nous indiquons une mthodologie pour concevoir et raliser avec succ`s des proe e e grammes en assembleur. Tout dabord, je tiens ` porter vigoureusement un coup contre cette rumeur selon a laquelle la programmation en assembleur serait dicile, voire incomprhensible. Cest on ne peut plus faux. e Par contre, il est vrai que la programmation en assembleur ncessite une certaine rigueur et quune me e thodologie doit tre respecte pour mener ` bien tout projet, mme le plus modeste. Par ailleurs, il faut e e a e bien comprendre que lalgorithmique tant le fondement de la programmation, en assembleur, en Pascal ou e dans un quelconque langage, sa ma trise est ncessaire avant daborder la programmation. An de limiter les e probl`mes ` ce niveau, les programmes que nous crirons en assembleur ne demanderont gnralement que e a e e e la connaissance des bases de lalgorithmique : la notion de variable la notion de squence dinstructions e la notion de test la notion de boucle la notion de fonction et de param`tres e La section 2 indique la mthodologie propose. La section 3 prsente la rsolution dun probl`me en e e e e e utilisant cette mthodologie. Ces deux sections doivent tre lues en priorit. e e e Enn, la section 4 constitue un vritable manuel de rfrence pour lapplication de la mthode de proe ee e grammation. Pour chacune des structures des langages de haut niveau (expression, aectation, squence, e tests, boucles, fonctions et procdures), on indique leur traduction en assembleur. Cette section nest pas e forcment ` lire du dbut ` la n mais devra tre systmatiquement consulte lors de llaboration dun e a e a e e e e programme. Outre la traduction des instructions et structures de contrle, on y trouvera la rponse aux o e questions suivantes : comment utiliser les registres? comment passer un param`tre ` un sous-programme? e a comment utiliser un tableau en assembleur? comment faire les entres-sorties de nombres et de caract`res? e e quelles instructions dois-je utiliser pour crire une boucle tant-que? e et ` bien dautres... a

Chapitre 2

Mthodologie e
2.1 Mthodologie e

Dans la conception dun programme en assembleur, on distinguera direntes phases : e 1. la conception de lalgorithme o` on exhibera les sous-programmes ncessaires en indiquant bien les u e param`tres et leur rle et la structure des sous-programmes et du programme principal e o 2. la traduction en assembleur qui se fera en plusieurs tapes : e (a) structure du programme : en partant dun programme minimal, on ajoutera les dbuts et ns de e sous-programmes (b) dnition du corps des fonctions en commenant par dnir les structures de contrle : tie c e o e quettes, instructions de saut conditionnel et inconditionnel (c) traduction des expressions en squence dinstructions assembleur en utilisant des pseudo-variables e (d) allocation des pseudo-variables : dcider de lendroit o` sont stockes les donnes : registres, pile e u e e ou segment de donnes. Prendre en compte les param`tres des sous-programmes en priorit e e e (e) eectuer les ajustements ncessaires en particulier dus ` lutilisation des instructions mul/div, e a push/pop, ... Une pseudo-variable est une variable que nous utiliserons dans le but de simplier lcriture du proe gramme assembleur. Nexistant pas en assembleur, nous devrons les transformer en donnes manipulables e en assembleur lors de la derni`re phase de traduction en assembleur. e Sur un exemple, nous allons montrer la mise en uvre de cette mthodologie. Dans la partie 4, on a e rassembl toutes les r`gles de traduction que nous allons mettre en uvre dans lexemple qui suit. Dans le e e document intitul (( Assembleur i8086 )), on trouvera toutes les instructions dcrites une par une. On sy e e rfrera en cas de besoins lors des phases de traduction en assembleur. ee

2.2

Aperu du langage dassemblage c

Il est bon davoir une vue globale des possibilits du langage dassemblage avant de commencer quoi que e ce soit. Ainsi, nous trouvons des instructions : arithmtiques et logiques ` 2 oprandes. Aussi, toute expression ayant plus de deux oprandes devra e a e e tre dcompose en opration ` deux oprandes. Par exemple pour aliser laddition a + b + c, on e e e e a e e eectuera dabord la somme a + b au rsultat duquel on ajoutera la valeur de c e laectation

de rupture de squence. On distingue deux types : les ruptures de squences inconditionnels qui sont e e imprativement eectues ; les ruptures de squences conditionnelles qui testent des valeurs boolennes e e e e pour dcider de rompre ou non la squence. Les tests seront raliss ` laide de ce type dinstructions e e e e a appeler un sous-programme, un sous-programme tant une esp`ce de procdure e e e Par ailleurs, il existe des variables de type caract`re et des variables de type entier. Les variables de type e boolen sont reprsentes, en gnral, par une variable de type entier en suivant la convention suivante : e e e e e une valeur nulle signie faux une valeur non nulle signie vrai Enn, on utilisera toujours des commentaires pour expliquer les programmes que nous crivons. e

Chapitre 3

Exemple
Nous appliquons la mthodologie point par point sur un exemple. Cest de cette mani`re que vous devez e e concevoir et crire tous les programmes assembleur. e

3.1

Programme ` crire ae

Saisir une srie de nombres entiers positifs et acher sa somme. Chaque nombre sera entr sur une ligne e e dirente, en sparant donc deux nombres par un retour-chariot. La srie de nombres sera termine par une e e e e ligne vide (donc la frappe de deux retour-chariots successifs).

3.2

Analyse et algorithme

La premi`re tape est celle de la rdaction dun algorithme pour rsoudre le probl`me pos. e e e e e e Nous proposons lalgorithme suivant :
PROGRAMME_PRINCIPAL somme := 0 REPETER lire (nombre) SI (nombre # 0) ALORS somme := somme + nombre FIN_SI JUSQUA (nombre = 0) afficher (somme) FIN FONCTION lire : ENTIER nombre := 0 REPETER lire_caractere (c) SI (est_un_chiffre (c) = vrai) ALORS afficher_caractere (c) nombre := nombre * 10 nombre := nombre + valeur_numerique (c) FIN_SI JUSQUA (c = <retour-chariot>) RETOURNE nombre FIN PROCEDURE afficher (IN nombre) FIN FONCTION valeur_numerique (IN caractere) : ENTIER RETOURNE code_ascii (caractere) - code_ascii (0) FIN FONCTION est_un_chiffre (IN c) : BOOLEEN RETOURNE c >= 0 et c <= 9

Le programme principal eectue donc une boucle de lecture et de sommation des nombres, sarrtant e lorsquil ny a plus de nombres ` lire. a 6

La fonction LIRE lit un nombre. Il faut savoir quen assembleur, il nexiste pas de fonction prdnie qui e e eectue cette action. Donc, nous devons crire un sous-programme qui eectue cette lecture caract`re par e e caract`re et transforme la suite de caract`res lus en un nombre entier. e e De mme, il nexiste pas de fonction permettant directement dacher la valeur dun nombre ` lcran. e a e Aussi, on doit crire nous-mme un sous-programme pourle faire. Nous navons pas dtaill ici le corps de e e e e cette procdure. Nous en reparlerons plus loin. e Notons que le param`tre de la fonction valeur_numerique est forcment le code ASCII dun chire. e e

3.3

La ralisation du programme assembleur e

Partant de lalgorithme, il sagit maintenant de le transformer, pas a pas, en assembleur pour obtenir un ` programme complet.

3.3.1

Structure du programme

Lalgorithme propos se compose dune procdure, 3 fonctions et du programme principal. Cest ainsi e e que nous commencons par crire le squelette du programme assembleur : e
; Ce programme lit une squence de nombres au clavier et e ; affiche leur somme .MODEL SMALL .STACK 100H .DATA ; ; dclaration des variables e ; .CODE ; ; programme principal ; mov ax, @data mov ds, ax ; ; corps du programme principal ; mov ah, 4ch ; arr^t du programme e int 21h ; ; sous-programme LIRE ; LIRE PROC ; lit un nombre au clavier ; ret LIRE ENDP ; ; sous-programme AFFICHER ; AFFICHER PROC ; affiche un nombre a lcran ` e ; ret AFFICHER ENDP ; ; sous programme VALEUR_NUMERIQUE ; VAL_NUM PROC ; renvoie la valeur numrique dun caract`re chiffre e e ; ret VAL_NUM ENDP ; ; sous-programme EST_UN_CHIFFRE ; EST_UN_CHIFFRE PROC ; indique si le code ASCII pass en param`tre est e e ; celui dun chiffre ; ret

EST_UN_CHIFFRE ENDP END

Squelette du programme assembleur Dans le squelette, le programme principal comporte uniquement les instructions arrtant son excution. e e Les sous-programmes sont dclars mais sont galement vides. Notons que sur une ligne, tout ce qui suit un e e e caract`re ; est un commentaire. e

3.3.2

Dnition du corps des sous-programmes e

On analyse maintenant les 4 sous-programmes. On commence par dnir leurs param`tres et la valeur e e quils retournent si ce sont des fonctions. Ensuite, on dcrit leur structure, cest-`-dire leur algorithme. e a Les param`tres des sous-programmes e Nous devons tout dabord bien lister les param`tres des sous-programmes et indiquer comment ils seront e passs (o` ils seront stocks). e u e 1. sous-programme LIRE : pas de param`tre en entre e e cest une fonction et elle retourne le nombre lu 2. sous-programme AFFICHER : prend un param`tre en entre, le nombre dont la valeur doit tre ache e e e e 3. sous-programme VAL_NUM : ce sous-programme prend en entre le code ASCII du caract`re qui a t lu et ce caract`re est e e ee e forcment un chire e la valeur numrique correspondant au caract`re doit tre renvoye e e e e 4. sous-programme EST_UN_CHIFFRE : ce sous-programme prend en entre le code ASCII du caract`re ` tester e e a le rsultat est un boolen e e Structure des sous-programmes Pour chaque sous-programme, on tudie limplantation de lalgorithme de plus pr`s. e e Sous-programme LIRE Le sous-programme LIRE se compose essentiellement dune boucle rpter. Cette boucle se traduit en e e assembleur ` laide dune instruction de saut conditionnel (cf. 4.4.2). La boucle ` traduire est : a a REPETER lire_caractere (c) SI (est_un_chiffre (c) = vrai) ALORS afficher_caractere (c) nombre := nombre * 10 nombre := nombre + valeur_numerique (c) FIN_SI JUSQUA (c = <retour-chariot>)

Celle-ci est transforme en : e debut_de_boucle: lire_caractere (c) SI (est_un_chiffre (c) = vrai) ALORS afficher_caractere (c) nombre := nombre * 10 nombre := nombre + valeur_numerique (c) FIN_SI SI (c # <retour-chariot>) ALORS ALLER_EN debut_de_boucle FIN_SI Les parties en gras rsultent de limplantation de la structure de boucle. La partie en italique forme e le corps de la boucle. Nous laissons cette partie de ct et nous concentrons sur la traduction en assembleur oe de la structure de la boucle. Si lon poursuit la traduction, on obtient la traduction suivante : debut_de_boucle: lire_caractere (c) SI (est_un_chiffre (c) = vrai) ALORS afficher_caractere (c) nombre := nombre * 10 nombre := nombre + valeur_numerique (c) FIN_SI cmp code_caractere, 13 ; calcul de la condition jne debut_de_boucle Une instruction cmp compare le code ASCII du caract`re saisi ` celui du retour-chariot. En fonction e a du rsultat de cette instruction, linstruction suivante jne dclenche une rupture de squence en dbut de e e e e boucle pour eectuer une itration ou ne dclenche aucune action. Dans ce cas, lexcution du programme se e e e poursuit en squence, avec linstruction suivante du programme (non reprsente ici) : on sort de la boucle. e e e Pour passer ` une premi`re bauche en assembleur, nous devons encore savoir passer un param`tre ` a e e e a un sous-programme. Pour cela, on utilise linstruction push avec, en oprande, la valeur du param`tre. Par e e exemple, pour passer la valeur 10 en param`tre, on utilisera linstruction : e push 10

Dans notre cas, nous devons passer le code ASCII du caract`re qui a t saisi. Si on suppose que cette e ee e valeur se trouve dans la pseudo-variable code caract`re, on aura donc une instruction : push code caract`re e

Lappel dun sous-programme se fait avec linstruction call avec le nom du sous-programme a appel en e oprande. e Pour terminer, lorsquun appel de sous-programme est ralis en passant des param`tres, il faut, imme e e e diatement apr`s linstruction call mettre une instruction e add sp, xxx o` xxx est une valeur numrique gale ` deux fois le nombre de param`tres. Puisquil y a ici un seul u e e a e param`tre, on devra utiliser linstruction e add sp, 2. Donc, pour rsumer, la traduction de lappel de fonction est_un_chiffre (c) se traduira en assembleur e par les trois instructions : 9

push call add

code_caract`re e est_un_chiffre sp, 2

La fonction est_un_chiffre renvoie un rsultat boolen dont il faut tester la valeur. Comme nous lavons e e dit plus haut, une variable boolenne est reprsente par une variable enti`re. Donc, nous testerons sa valeur e e e e a ` laide de linstruction cmp vue plus haut et une instruction de saut conditionnel dclenchera le traitement e en consquence. e Dernier point, la saisie dun caract`re au clavier : ne cherchons pas plus loin, toutes les oprations e e dentres-sorties disponibles en assembleur sont indiques ` la section 4.11 de ce document et dans le chapitre e e a 5 du manuel du i8086. Ainsi, nous y trouvons que pour lire un caract`re au clavier, il faut utiliser les deux e instructions : mov int ah, 1 21h

Linstruction int dclenche lappel dune esp`ce de sous-programme prdni. La code ASCII du caract`re e e e e e saisi est ensuite accessible dans le registre al. Aussi, nous aectons ensuite la valeur du registre al ` la a pseudo-variable code caract`re. e On obtient donc la traduction en assembleur ci-dessous 1 :
LIRE PROC ; lit un nombre au clavier ; ; en entre : pas de param`tre e e ; en sortie, nombre contient la valeur du nombre saisi ; mov nombre, 0

repeter: ; saisie dun caract`re au clavier e mov ah, 1 int 21h mov code_caract`re, al e ; ; Le code ASCII du caract`re saisi est dans e ; la pseudo-variable code_caract`re e ; push code_caract`re e call est_un_chiffre add sp, 2 ; cmp valeur_renvoye, 0 e je fin_de_repeter ; ; effectuer le calcul : ; nombre := nombre * 10 ; nombre := nombre + valeur_numerique (code_caract`re) e jmp repeter fin_de_repeter: ret LIRE ENDP

Sous-programme AFFICHER Acher la valeur dun nombre nest pas chose simple, contrairement ` ce que lon pourrait penser. Aussi, a on utilisera le sous-programme affiche_nombre donn en 4.11.2. e Sous-programme VAL NUM En regardant une table des codes ASCII, on constate que le code ASCII dun chire est gal au chire e lui-mme plus le code ASCII du caract`re 0. Il sut donc de soustraire cette valeur au code ASCII pour e e obtenir le rsultat voulu : e
VAL_NUM PROC ; renvoie la valeur numrique dun caract`re nombre e e

1. attention : ce nest quun premier pas vers la traduction en assembleur ; dautres vont suivre.

10

VAL_NUM

; sub ret ENDP

parametre_de_val_num, 0

Etant donne lextrme simplicit de ce sous-programme, on inclura directement linstruction dans le e e e sous-programme qui en a besoin et nous ne dnirons donc pas de sous-programme VAL_NUM. Au contraire, e on crira tout simplement : e
sub code_du_caractere, 0

Sous-programme EST UN CHIFFRE Pour dterminer si un code ASCII est celui dun chire, il sut de constater que tous les codes ASCII e des chires se suivent de 0 ` 9. Donc, un code ASCII sera celui dun chire sil est compris entre celui a de 0 et celui de 9. Ce sous-programme doit renvoyer un rsultat boolen, donc une variable enti`re. Nous mettrons cette e e e e valeur dans une pseudo-variable nomme valeur renvoye. e Cela nous donne le sous-programme suivant :
EST_UN_CHIFFRE PROC ; indique si le code ASCII passe en param`tre est e ; celui dun chiffre ; cmp parametre, 0 jl n est pas un chiffre ; car le code est < a celui de 0 ` cmp parametre, 9 ; car le code est > a celui de 9 ` jg n est pas un chiffre mov valeur renvoye, 1 e ret n_est_pas_un_chiffre: e mov valeur renvoye, 0 ret EST_UN_CHIFFRE ENDP

On constate que lon aecte 1 ` valeur renvoye dans le cas o` le code du caract`re est ` la fois plus a e u e a grand ou gal ` celui de 0 et plus petit ou gal ` celui de 9. Dans le cas contraire, on lui aecte 0. e a e a Le programme principal Le programme principal suit lalgorithme donn plus haut. Il consiste essentiellement en une boucle de e lecture et de sommation des nombres lus.
; initialisation ncessaire dans tous les programmes assembleur e mov ax, @data mov ds, ax ; ; programme principal ; mov somme, 0 repeter: call lire cmp nombre, 0 je fin_de_repeter add somme, nombre jmp repeter fin_de_repeter: ; passer le param`tre somme e push somme call affiche_nombre add sp, 2 ; ; arr^t du programme e ; mov ah, 4ch int 21h

Apr`s saisie dun nombre, on teste sil est nul. Sil lest, on sort de la boucle ; sinon, on lajoute ` la e a somme dj` obtenue et on it`re. ea e On notera galement lappel du sous-programme affiche nombre qui suit les r`gles donnes plus haut. e e e 11

3.3.3

Traduction des expressions

Il nous faut maintenant calculer lexpression du sous-programme LIRE : ; nb := nb * 10 ; nb := nb + valeur_numerique (code_caract`re) e Les oprations en assembleur sont toujours de la forme a := a op b o` op dnote une opration (addition, e u e e soustraction, ...) et se note en assembleur : mnenomique-pour-op a, b o` mnemonique-pour-op dnote le mnmonique correspondant ` lopration que lon veut raliser. Donc, u e e a e e cela nous donne les trois instructions suivantes : mul sub add nombre, 10 code_du_caractere, 0 nombre, code_du_caractere ; on fait la multiplication ; valeur numrique du caract`re lu e e ; que lon additionne au rsultat courant e

3.3.4

Allocation des pseudo-variables

Si nous rsumons la situation, le programme est dans ltat indiqu ci-dessous : e e e


; Ce programme lit une squence de nombres au clavier et e ; affiche leur somme .MODEL SMALL .STACK 100H .DATA .CODE ; mov ax, @data mov ds, ax ; ; programme principal ; mov somme, 0 repeter1: call lire jnc fin_de_repeter1 add somme, nombre jmp repeter1 fin_de_repeter1: ; ; afficher la somme push somme call affiche_nombre add sp, 2 ; ; mov ah, 4ch int 21h LIRE PROC ; lit un nombre au clavier ; ; en entre : pas de param`tre e e ; en sortie, nombre contient la valeur du nombre saisi ; mov nombre, 0 repeter2: ; saisie dun caract`re au clavier e mov ah, 1 int 21h mov code_caract`re, al e ; Le code ASCII du caract`re saisi est dans e ; la pseudo-variable code_caract`re e ; push code_caract`re e call est_un_chiffre add sp, 2 ; cmp valeur_renvoye, 0 e je fin_de_repeter2 ; mul nombre, 10 ; on fait la multiplication

12

sub code_caractere, 0 ; valeur numrique du caract`re lu e e add nombre, code_caractere ; que lon additionne au rsultat courant e jmp repeter2 fin_de_repeter2: ret LIRE ENDP EST_UN_CHIFFRE PROC ; indique si le code ASCII passe en param`tre est e ; celui dun chiffre ; cmp parametre, 0 jl n_est_pas_un_chiffre cmp parametre, 9 jg n_est_pas_un_chiffre mov valeur_renvoye, 1 e ret n_est_pas_un_chiffre: mov valeur_renvoye, 0 e ret EST_UN_CHIFFRE ENDP END

Programme avant allocation des pseudo-variables Une tiquette donne ne pouvant rfrencer quune seule instruction, nous avons numrot les tiquettes e e ee e e e repeter et fin_de_repeter qui apparaissaient chacune deux fois, puisquil y a deux boucles, an de les distinguer. Les pseudo-variables utilises sont donc : e somme qui est une donne utilise uniquement dans le programme principal ; e e nombre qui est une donne fournie par le sous-programme LIRE et utilis dans le programme principal ; e e code caractere qui est fourni lors de la lecture dune touche puis utilis dans le sous-programme e LIRE ; valeur renvoye qui est renvoy par la fonction EST UN CHIFFRE et utilis dans le sous-programme e e e LIRE ; parametre qui est pass par le sous-programme LIRE au sous-programme EST_UN_CHIFFRE. e somme est une variable que lon dirait globale en Pascal. Aussi, on va limplanter de cette mani`re. En e assembleur, une variable globale se dclare dans la section .data. Sa valeur peut aisment devenir plus e e grande que 255. Aussi, nous la dnirons comme un mot-mmoire. Soit : e e somme dw 0

Le 0 est la valeur initiale de la variable. nombre tant utilis dans la fonction LIRE puis retourn ` son appelant, il est judicieux de limplanter e e ea dans un registre ax, bx, cx ou dx. Choisissons bx. code caract`re est une variable locale au sous-programme LIRE. On peut limplanter dans un registre e (par exemple cl), ce que nous faisons ici. On peut galement limplanter autrement : le nombre de registres tant limit et le nombre de variables e e e locales ncessaires pouvant tre important, un autre mcanisme est disponible pour pouvoir utiliser autant e e e de variables locales que lon veut. On consultera la section 4.7.5 pour plus de dtails. e e e valeur renvoye est une valeur renvoye que lon peut implanter au choix dans un registre choisi parmi ax, bx, cx ou dx. Choisissons ax. parametre est un param`tre. Dans ce cas, comme il est indiqu dans la section 4.7.4, il faut : e e 1. ajouter les deux instructions : push mov bp bp, sp 13

en tout dbut de sous-programme (juste apr`s PROC) e e 2. ajouter linstruction : pop bp

a ` la n du sous-programme, juste avant linstruction ret. Sil y a plusieurs instructions ret dans le sous-programme, il faut mettre cette instruction pop ` chaque fois. a 3. emplacer les occurences de la pseudo-variables parametre par WORD PTR [bp+4]. Tout cela nous donne le programme suivant (qui est presque termin !) : e
; Ce programme lit une squence de nombres au clavier et e ; affiche leur somme .MODEL SMALL .STACK 100H .DATA somme dw 0 .CODE mov ax, @data mov ds, ax ; ; programme principal ; ; mov somme, 0 repeter1: call lire cmp bx, 0 je fin_de_repeter1 add somme, bx jmp repeter1 fin_de_repeter1: ; ; afficher la somme push somme call affiche_nombre add sp, 2 ; ; mov ah, 4ch int 21h LIRE PROC ; lit un nombre au clavier ; ; en entre : pas de param`tre e e ; en sortie, bx contient la valeur du nombre saisi ; mov bx, 0 repeter2: ; saisie dun caract`re au clavier e mov ah, 1 int 21h mov cl, al ; Le code ASCII du caract`re saisi est dans e ; le registre cl ; push cl call est_un_chiffre add sp, 2 cmp ax, 0 je fin_de_repeter2 ; mul bx, 10 ; on fait la multiplication sub cl, 0 ; valeur numrique du caract`re lu e e add bx, cl ; que lon additionne au rsultat courant e jmp repeter2 fin_de_repeter2: ret LIRE ENDP EST_UN_CHIFFRE PROC ; indique si le code ASCII passe en param`tre est e ; celui dun chiffre ; push bp mov bp, sp cmp WORD PTR [bp+4], 0

14

jl n_est_pas_un_chiffre cmp WORD PTR [bp+4], 9 jg n_est_pas_un_chiffre mov ax, 1 pop bp ret n_est_pas_un_chiffre: mov ax, 0 pop bp ret EST_UN_CHIFFRE ENDP END

Noter que la premi`re instruction du programme principal a t mise en commentaire. Linitialisation de e ee la variable somme est eectue automatiquement. e

3.3.5

Derniers ajustements

Il nous reste encore quelques petits ajustements ` eectuer pour obtenir un programme assembleur a complet, qui sassemble sans erreur et sexcute en donnant le rsultat attendu. e e Instructions de multiplication et division Nous avons utilis linstruction mul bx, 10 qui nexiste pas : les instructions de multiplications et de e divisions (mul, imul, div, idiv) ne prennent quun seul oprande. La deuxi`me valeur prise en compte dans e e lopration est toujours le registre al ou le registre ax selon que linstruction est excute sur 8 ou 16 bits. e e e Tous les dtails sur ces instructions sont donnes plus loin dans la section 4.7.3. Nous voulons raliser e e e lopration bx := bx * 10. En appliquant ce qui est indiqu dans cette section, nous obtenons la transfore e mation suivante : mov mul mov ax, 10 bx bx, ax

qui sexplique de la mani`re suivante : e 1. la premi`re instruction aecte au registre ax la valeur 10, deuxi`me oprande de la multiplication e e e 2. la deuxi`me instruction eectue la multiplication. Le rsultat est ensuite disponible dans la paire de e e registre dx pour le poids fort, ax. Nous supposons que le rsultat de la multiplication est infrieur ` e e a 65535, donc le registre dx contient ne valeur nulle 3. la derni`re instruction transfert le rsultat de la multiplication, qui est toujours mis dans ax par mul e e dans le bon registre, bx Instructions push Dans le sous-programme LIRE, nous utilisons linstruction push cl qui est incorrecte car linstruction push ne prend en oprande que des donnes 16 bits (registre ou e e variable). Nous devons donc transformer cette instruction en une instruction valide. Le plus simple est de la transformer en push cx. Cependant, dans ce cas, il faut sassurer que les 8 bits de poids fort de cx, le registre ch sont ` 0. Il faut a donc ajouter une instruction mov ch, 0 auparavant. Cela nous donne donc : mov push ch, 0 cx 15

Compatibilit entre donnes e e Linstruction add bx, cl est incorrecte car elle mlange un registre 8 bits (cl) avec un registre 16 bits e (bx). Il faut que les deux donnes soient des mots. Donc, il faut utiliser cx ` la place de cl. Puisque nous e a venons prcdemment de nous arranger pour que cx contienne la valeur de cl en mettant ch ` zro, nous e e a e pouvons remplacer cl par cx. Propret des sous-programmes e Enn, et cette vrication doit toujours tre faite en derni`re tape, une r`gle importante demande quun e e e e e sous-programme ne modie aucune valeur qui ne lui appartienne pas (variable globale, registre) ` moins a que cela ne soit explicitement indiqu dans les spcications du sous-programme. Donc, si nous regardons le e e programme que nous avons crit, nous constatons que le sous-programme LIRE modie la valeur des registres e ax et cx alors que cela nest pas demand. Aussi, il faut modier ce sous-programme pour que cela narrive e pas. Pour cela, apr`s avoir recens les registres en question (en gnral, ce sont des registres et non des e e e e variables globales), il faut ajouter des instructions aux sous-programmes incrimins pour quils sauvegardent e les valeurs de ces registres ` leur dbut et les restaurent ` leur sortie. Ainsi, du point de vue de lappelant, a e a la valeur des registres na pas chang lors de lappel du sous-programme. e Sauvegarde des registres Si le sous-programme ne prend pas de paramt`tre, on place les instructions de sauvegarde apr`s la ligne e e PROC du sous-programme. Si le sous-programme prend des param`tres, on placera les instructions de sauvegarde juste apr`s les deux e e lignes : push mov bp bp, sp

qui doivent se trouver en dbut de sous-programme. e La sauvegarde dun registre est eectue ` laide dinstructions push en spciant le registre ` sauvegarder e a e a en oprande. Il y a donc autant dinstructions ` ajouter que de valeurs ` sauvegarder. e a a Ici, on ajoutera donc les deux lignes : push push ax cx

Restauration des registres Si le sous-programme ne prend pas de param`tre, on place les instructions de restauration avant linse truction ret du sous-programme. Si le sous-programme prend des param`tres, les instructions de restauration sont places avant linstruce e tion : pop bp qui doit se trouver ` la n du sous-programme. a La trestauration de la valeur dun registre est eectue ` laide dune instruction pop. e a Ici, nous ajouterons les deux instructions : pop pop cx ax

juste avant linstruction ret. Il faut faire tr`s attention ` observer les trois r`gles suivantes : e a e 1. il doit y avoir autant dinstructions pop que dinstructions push 16

2. il doit y avoir un jeu dinstructions pop avant chaque instruction ret du sous-programme 3. les registres doivent appara dans lordre inverse dans les instructions pop par rapport aux instructre tions push. Ainsi, ici on a push ax puis push cx pour la sauvegarde, pop cx puis pop ax pour la restauration. Le non-respect des r`gles 1 et 2 entra e nera toujours un plantage de votre programme. Le non-respect de la r`gle 3 entra e nera un dysfonctionnement.

3.3.6

Programme termin e

Pour rsumer le rsultat de tout ce qui a t dit, nous indiquons ci-dessous le programme termin, prt ` e e ee e e a tre assembl et excut. Nous nindiquons pas ci-dessous le sous-programme affiche_nombre qui est donn e e e e e a ` la section 4.11.2.
; Ce programme lit une squence de nombres au clavier et e ; affiche leur somme .MODEL SMALL .STACK 100H .DATA somme dw 0 .CODE mov ax, @data mov ds, ax ; ; programme principal ; repeter1: call lire cmp bx, 0 je fin_de_repeter1 add somme, bx jmp repeter1 fin_de_repeter1: ; ; afficher la somme push somme call affiche_nombre add sp, 2 ; ; mov ah, 4ch int 21h LIRE PROC ; lit un nombre au clavier ; ; en entre : pas de param`tre e e ; en sortie, bx contient la valeur du nombre saisi ; push ax push cx mov bx, 0 repeter2: ; saisie dun caract`re au clavier e mov ah, 1 int 21h mov cl, al ; Le code ASCII du caract`re saisi est dans e ; le registre cl ; mov ch, 0 push cx call est_un_chiffre add sp, 2 cmp ax, 0 je fin_de_repeter2 ; mov ax, 10 mul bx mov bx, ax sub cx, 0 ; valeur numrique du caract`re lu e e add bx, cx ; que lon additionne au rsultat courant e jmp repeter2 fin_de_repeter2: pop cx

17

pop ax ret LIRE ENDP EST_UN_CHIFFRE PROC ; indique si le code ASCII passe en param`tre est e ; celui dun chiffre ; push bp mov bp, sp cmp WORD PTR [bp+4], 0 jl n_est_pas_un_chiffre cmp WORD PTR [bp+4], 9 jg n_est_pas_un_chiffre mov ax, 1 pop bp ret n_est_pas_un_chiffre: mov ax, 0 pop bp ret EST_UN_CHIFFRE ENDP END

18

Chapitre 4

Guide pour la traduction de structures de haut niveau en assembleur


Ce chapitre est un manuel de rfrence auquel on se reportera pour traduire les structures algorithmiques ee en assembleur. Pour chacune des structures des langages de haut niveau, nous proposons une traduction. Nous utilsons pour cela des pseudo-variables. La transformation des pseudo-variables en assembleur est vue a ` la n de ce chapitre 4.7.

4.1

Expression arithmtique et logique e

Nous ne nous intressons ici qu` la traduction dexpressions arithmtiques o` ninterviennent que des e a e u valeurs de type entier codes sur 8 ou 16 bits. Les valeurs relles ne sont en aucun cas prises en compte ici. e e

4.1.1

Principe

La traduction dune expression arithmtique consiste ` la dcomposer en opration ayant un ou deux e a e e oprandes source et un oprande cible. Par exemple, on traduira : e e

Original
a + b - c

Traduction
mov tmp0, a add tmp0, b mov tmp1, c sub tmp1, tmp0 ; tmp1 contient la valeur de lexpression

Traduction dune expression numrique e On prendra garde ` dventuels parenthsages en calculant les termes dans le bon ordre. On utilisera des a e e pseudo-variables temporaires notes tmp... autant quil est ncessaire. e e

19

4.1.2

Laectation

Laectation du rsultat dune expression sera ralise en aectant le rsultat de la derni`re opration de e e e e e e lexpression ` la variable ` aecter : a a

Original
x := a + b - c mov add sub mov

Traduction
tmp0, a tmp0, b tmp0, c x, tmp0

Traduction dune aectation

4.2

Squence dinstructions e

Une squence dinstructions se traduit instruction par instruction, lune apr`s lautre. An dviter toute e e e confusion, on utilise de nouvelles pseudo-variables pour chaque instruction. Ainsi :

Original
x := a - b + c y := d - x z := x + y

Traduction
; instruction x := a - b + c mov tmp0, a sub tmp0, b add tmp0, c mov x, tmp0 ; instruction y := d - x mov tmp1, d sub tmp1, x mov y, tmp1 ; instruction z := x * y mov tmp2, x add tmp2, y mov z, tmp2

Traduction dune squence dinstructions e

4.3
4.3.1

Les tests
Principe
Original
SI (condition vraie) ALORS action-alors SINON action-sinon FIN_SI suite-du-programme

Traduction
calcul de la condition Jcc etiquette_sinon action-alors ... JMP etiquette_fin_si etiquette_sinon: action-sinon ... etiquette_fin_si: suite-du-programme

Traduction dun test

20

Le calcul de la condition revient ` valuer une expression, ce que lon dcrit un peu plus bas (cf. 4.3.2). Ce ae e calcul positionne certains bits du registre FLAGS dont lun sera test par une instruction de saut conditionnel e (instruction Jcc ` choisir en fonction du bit ` tester) pour dcider laquelle de la partie-alors ou de la a a e partie-sinon doit tre excute. Une fois cela traduit, il reste ` traduire les deux blocs dinstructions e e e a formant les partie-alors et partie-sinon qui ne sont que des squences dinstructions (donc, voir 4.2). e

4.3.2

Conditions simples

La condition du test est une expression logique simple ou compose. Une condition simple est de lune e des 6 formes : expression1 = expression2 expression1 # expression2 expression1 < expression2 expression1 <= expression2 expression1 > expression2 expression1 >= expression2 o` expression1 et expression2 sont des expressions arithmtiques ou de simples variables. a e Par exemple, on traduira:

Original
SI (e1 = e2) ALORS action-alors SINON action-sinon FIN_SI

Traduction
cmp e1, e2 jne etiquette_sinon ; traduction de la partie-alors ... jmp fin_si etiquette_sinon: ; traduction de la partie-sinon ... fin_si:

Traduction dun test plus gnralement, pour une condition donne on utilisera une squence de deux instructions : une inse e e e truction cmp suivie dune instruction de saut conditionnel comme il est indiqu dans la table suivante : e Condition e1 = e2 e1 # e2 e1 < e2 e1 <= e2 e1 > e2 e1 >= e2 Instruction jne je jge jg jle jl

Instructions pour la traduction de tests simples

21

4.3.3

La condition est une expression

Lorsque les valeurs ` tester rsultent du calcul dune expression, on aura par exemple : a e

Original
SI (a + b = 10) ALORS action-alors SINON action-sinon FIN_SI

Traduction
mov tmp0, a add tmp0, b cmp tmp0, 10 jne etiquette_sinon ; traduction de la partie-alors ... jmp fin_si etiquette_sinon: ; traduction de la partie-sinon ... fin_si: ...

Traduction dun test

4.3.4

Conditions composes e

Une condition compose est constitue par des conditions simples relies par des oprateurs ET, OU ou la e e e e ngation dune condition : e 1. condition1 ET condition2 2. condition1 OU condition2 3. NON condition condition1 ET condition2 Dans le premier cas, la condition est vrie si les deux conditions le sont. On va la traduire en : e e

Original
SI (condition1 vraie ET condition2 vraie) ALORS action-alors SINON action-sinon FIN_SI

Traduction
SI (condition1 vraie) ALORS SI (condition2 vraie) ALORS action-alors SINON ALLER_EN action_sinon FIN_SI SINON action_sinon: action-sinon FIN_SI

Traduction dune condition compose ET e o` lon sautorise linstruction ALLER_EN pour rendre compte de cette traduction, son criture dans un u e langage de haut niveau tant, sinon impossible, du moins tr`s fortement dconseille. e e e e

22

condition1 OU condition2 Dans le deuxi`me cas, la condition est vrie si lune des conditions est vraie. e e e

Original
SI (condition1 vraie OU condition2 vraie) ALORS action-alors SINON action-sinon FIN_SI

Traduction
SI (condition1 vraie) ALORS ALLER_EN action-alors SINON SI (condition2 vraie) ALORS action_alors: action-alors SINON action_sinon: action-sinon FIN_SI FIN_SI

Traduction dune condition compose OU e o` lon utilise ` nouveau linstruction jmp par (( abus de langage )). u a NON condition Dans le troisi`me cas, la condition est vraie si sa valeur est fausse et inversement. Aussi, sa traduction e sobtient presque comme celle dune condition simple, si ce nest que lon nutilise pas la mme instruction, e mais celle correspondant ` la condition oppose. Linstruction ` utiliser est donne dans la table suivante : a e a e Condition NON (e1 = e2) NON (e1 # e2) NON (e1 < e2) NON (e1 <= e2) NON (e1 > e2) NON (e1 >= e2) Instruction je jne jl jle jg jge

Instructions pour la traduction de tests simples (suite) Bien entendu, on peut composer plus de deux conditions. Le principe de la traduction reste le mme. e

4.4
4.4.1

Les boucles
Boucle tant-que

Une boucle tant-que consiste ` excuter un groupe dinstructions (le (( corps de boucle ))) tant quune a e condition conserve la valeur vraie. Aussi, nous allons traduire ce type de boucle par un test reposant sur la valeur de la condition qui sera suivi en fonction de son rsultat, de lexcution du corps de la boucle ou e e dun saut hors de la boucle, ` linstruction qui la suit. Dans le cas o` le corps est excut, la condition doit a u e e ensuite tre ` nouveau calcule puis teste pour dcider de poursuivre lexcution de la boucle ou la quitter. e a e e e e

23

On obtient donc une traduction du genre : Boucle tant-que originale Boucle transforme e Traduction en assembleur de la structure de la boucle

TANT-QUE (condition vraie) FAIRE action FAIT

debut_de_boucle: debut_de_boucle: SI (condition vraie) ALORS calcul de la condition action jcc fin_boucle ALLER_EN debut_de_boucle action FIN_SI jmp debut_de_boucle fin_boucle:

Boucle tant-que Il ny a donc rien ici de nouveau. La traduction va sobtenir par juxtaposition des lments vus prcee e e demment, cest-`-dire la traduction dun test et dune squence dinstructions. a e

4.4.2

Boucle rpter e e

Comme une boucle tant-que, une boucle rpter doit au pralable tre transforme dans une structure e e e e e quivalente et directement traduisible en assembleur : e Boucle rpter originale e e Boucle transforme e Traduction en assembleur de la structure de la boucle debut_de_boucle: action calcul de la condition jcc debut_de_boucle fin-boucle:

REPETER action JUSQUA (condition vraie)

debut_de_boucle: action SI (condition fausse) ALORS ALLER_EN debut_de_boucle FIN_SI

Boucle rpter e e

4.4.3

Boucle pour

Une boucle pour gnrale doit dabord tre mise sous une forme particuli`re pour tre traduite ensuite e e e e e en assembleur. En eet, en assembleur i8086, lindice de boucle doit forcment varier dune valeur positive ` e a 1, lindice tant dcrment automatiquement ` chaque itration. Par exemple, la boucle : e e e e a e somme := 0 POUR i := 1 A 10 FAIRE somme := somme + i FAIT devra tre transforme au pralable en : e e e somme := 0 POUR i := 10 A 1 FAIRE somme := somme + i FAIT

24

Si cela ne pose aucun probl`me ici, cela en pose parfois. Par exemple, e POUR i := 1 A 10 FAIRE afficher (i) FAIT et POUR i := 10 A 1 FAIRE afficher (i) FAIT ne donnent pas le mme rsultat. En fait, la transformation correcte de la premi`re boucle est : e e e POUR i := 10 to 1 FAIRE afficher (11 - i) FAIT et la transformation correcte de la deuxi`re boucle est : e somme := 0 POUR i := 10 to 1 FAIRE somme := somme + 11 - i FAIT Une fois mise sous cette forme, la traduction en assembleur est obtenue de la mani`re suivante : e Boucle pour originale Boucle transforme e Traduction en assembleur de la structure de la boucle mov cx, n debut_de_boucle: action loop debut_de_boucle

POUR i := debut A fin FAIRE action FAIT

POUR i := n A 1 FAIRE action transforme e FAIT

Boucle pour

4.5

Procdures et fonctions : principe des sous-programmes e

En assembleur, fonctions et procdures se traduisent sous la forme de sous-programmes. Un sous-programme e a pour objet essentiel de structurer un programme ; ` un sous-programme est associe la ralisation dun a e e certain traitement. Une bonne r`gle de principe consiste ` ne jamais avoir de sous-programmes dont la e a longueur dpasse une page de listing (environ 50 lignes). Au-del` de cette taille, on peut gnralement de a e e e couper tr`s naturellement le sous-programme en plusieurs traitements, chacun faisant lui-mme lobjet dun e e sous-programme. Un sous-programme se compose essentiellement dun corps qui est une succession dinstructions. Ces instructions sont des squences, des tests et des boucles. Aussi, leur traduction du langage de haut niveau e en assembleur sera eectue comme nous venons de le voir. e Ce qui est spcique ` lutilisation des sous-programmes est : e a le passage de param`tre ; e lexistence de variables locales ; 25

le renvoi dune valeur si le sous-programme traduit une fonction. Nous allons donc nous concentrer sur ces seuls points dans ce qui suit. Dans la suite de cette section, nous donnerons tout dabord la forme gnrale dun sous-programme et son e e appel. Nous tudierons ensuite le passage de param`tres, le renvoi dune valeur puis lutilisation de variables e e locales.

4.5.1
nom

Principe

Un sous-programme sappelant nom se dnira ` laide du squelette suivant : e a


PROC ; description du sous-programme : ce quil fait, ; ses param`tres, la valeur quil renvoie e .... .... ; instructions du sous-programme .... ret ENDP

nom

Squelette dun sous-programme. Nous rappelons que pour assurer le bon fonctionnement du programme, un sous-programme ne doit en aucun cas modier la valeur dune donne (registre ou autre) ` moins que cela ne soit e a demand explicitement dans la spcication du sous-programme. e e Aussi, nous prendrons toujours soin de sauvegarder le contenu des registres utiliss dans e le sous-programme et de restaurer leur valeur initiale ` la sortie du sous-programme. De cette a mani`re, lappel du sous-programme ne modiera pas la valeur des registres aecte dans lappelant avant e e lappel. Ces sauvegardes et restaurations concerneront en gnral uniquement des registres. Ceux-ci seront saue e vegards en dbut de sous-programme via des instructions push, restaurs en n de sous-programme par des e e e instructions pop.

4.5.2
... call ....

Appel dun sous-programme

Un sous-programme portant le nom toto sappelle ` laide de linstruction call : a


toto

Appel dun sous-programme

4.6
4.6.1

Sous-programmes avec param`tres et variables locales e


Passage de param`tre en entre du sous-programme e e

Seuls des donnes de la taille dun mot-mmoire peuvent tre passes en param`tre. Si lon veut passer e e e e e un octet, on passera un mot et on ne considrera que la valeur de loctet de poids faible du mot. e Les param`tres sont passs ` laide de linstruction push de la mani`re suivante : e e a e
... push ... push push call add .... param`tre n e param`tre 2 e param`tre 1 e sous programme sp, 2 * n

; attention, lire ci-dessous

Squelette dun appel de sous-programme avec passage de param`tres en entre e e

26

Lexpression 2 * n doit tre remplace par sa valeur, en fonction du nombre de param`tres passs. e e e e Si un param`tre rsulte dun calcul, des instructions eectuant ce calcul viendront sintercaler entre les e e push. Exemple : Appel original Traduction en assembleur mov add push push mov sub push call add tmp0, tmp0, tmp0 b tmp1, tmp1, tmp1 f sp, 6 a 3

f (a + 3, b,

c - d)

c d

Appel de sous-programme avec passage de param`tres calculs e e

4.6.2
nom

Rception des param`tres e e

Nous indiquons ci-dessous le squelette dun sous-programme prenant des param`tres. e


PROC ; description du sous-programme : ce quil fait, ; ces param`tres, la valeur quil renvoie e push bp mov bp, sp .... pop bp ret ENDP

nom

Squelette dun sous-programme prenant des param`tres en entre e e On notera les instructions concernant le registre bp qui ont t ajoutes par rapport au cas du sousee e programme sans param`tre. Ce registre est fondamental dans lutilisation de la valeur des param`tres. e e e param`tre ` laide de la pseudo-variable pi (la lettre p Dans lappel, on notera simplement lacc`s au i e e e a suivie du nombre i). Dans le morceau de programme ci-dessous, un sous-programme nomm appelant appelle un souse programme nomm appele en lui passant trois param`tres : 56, 25 et 10. Dans lappelant, les param`tres e e e sont accds. Apr`s linstruction dappel call, on notera linstruction add sp, 6 qui est eectue au retour e e e e du sous-programme.
appelant PROC .... ; troisi`me param`tre e e push 10 ; deuxi`me param`tre e e push 25 ; premier param`tre e push 56 call appele ; 6 = 3 x 2, o` 3 est le nombre de param`tres u e add sp, 6 .... ret ENDP PROC ; description du sous-programme push bp mov bp, sp .... mov dx, p2

appelant appele

27

appele

; dx contient la valeur du deuxi`me param`tre, soit 25 e e ..... cmp ax, p1 ; compare la valeur de ax avec celle du premier param`tre, e ; soit 56 .... mov cx, 3 add cx, p3 ; ajoute la valeur du troisi`me param`tre a cx, soit 10 e e ` ; donc, le registre cx contient maintenant la valeur 13 .... pop bp ret ENDP

Passage et utilisation de param`tres en entre e e

4.6.3

Variables locales

Une variable locale est spcie sous la forme dune pseudo-variable. e e

4.6.4

Valeur de retour

Un sous-programme peut renvoyer simplement une valeur sous forme dune pseudo-variable.

4.7

Traduction des pseudo-variables

La traduction des pseudo-variables est fortement lie ` certaines contraintes dues aux instructions asseme a bleur du type : telle instruction naccepte que tel type doprande. On a a priori beaucoup de possibilits : e e pour simplier dans un premier temps, on peut crire son programme en utilisant de nombreuses variables e globales dnies dans le segment de donnes et nutiliser les registres que quand on ne peut pas faire autree e ment. (Notons cependant que cette mthode ne fonctionne pas si on utilise des sous-programmes rcursifs.) e e ` A lautre extrme, on peut samuser ` jongler avec les registres et la pile en nutilisant pas, ou presque pas, e a de donnes globales. Comme toujours, la bonne solution se situe quelque part entre ces deux extr`mes : il e e sagira doptimiser lutilisation des registres et dviter un trop grand nombre dacc`s ` la mmoire en nale e a e louant pas systmatiquement les donnes en mmoire. Ainsi, si lon suit les consignes ` propos de la taille des e e e a sous-programmes, on peut gnralement nutiliser que des registres pour le stockage des variables globales e e et locales. Lors de lallocation dune donne en assembleur, on a le choix entre deux types de donnes selon la valeur e e que peut prendre cette donne : octet ou mot. Dans un premier temps, on pourra suivre les r`gles suivantes : e e 1. une donne contenant un caract`re sera alloue sous forme dun octet e e e 2. une donne contenant un entier sera alloue sous forme dun mot e e Lallocation des donnes en mmoire tant la phase la plus dlicate, on tiendra toujours scrupuleusement e e e e a ` jour la liste des registres utiliss ` toutes les instructions du programme et on saura toujours quelles sont e a les donnes qui se trouvent dans la pile et son niveau dans la pile. e

4.7.1

Les variables globales du programme

Les variables globales seront allous dans le segment de donnes, donc dans la section .DATA du source e e (cf. manuel i8086). Pour une donne numriaue, on dclarera une donne comme un octet ou un mot, en e e e e fonction de la valeur maximale quelle peut prendre dans le programme. Si lon utilise trois variables a, b et c stockables sur un octet et deux variables d et e ncessitant un mot e et que lon initialise dans le programme a a la valeur 23, c avec -10, d avec 10000 et les deux autres variables ` ntant pas initialises, on dclarera : e e e .DATA 28

a b c d e

db db db dw dw

23 0 -10 10000 0

On utilisera ensuite ces variables librement dans le programme en donnant leur nom dans les instructions.

4.7.2

Les variables temporaires

Les variables temporaires seront stockes dans des registres. On utilisera pour cela les registres ax, bx, cx e et dx pour stocker des nombres, ou les registres ah, al, bh, bl, ch, cl, dh et dl pour stocker des caract`res e ou des entiers petits (dont la valeur ne dpasse pas 256). Pour ces derniers, on prendra garde lorsquon les e utilise, quils font partie des premiers ; cest-`-dire quen modiant la valeur de al (par exemple), on modie a en mme temps la valeur de ax, et ainsi de suite (voir chapitre 1 du manuel i8086). e

4.7.3

Les instructions de multiplication et de division

Les instructions de multiplication et de division sont tr`s particuli`res en cela quelles nont pas la mme e e e forme que les instructions daddition et de soustraction. Ainsi, lorsque nous avons crit : e mul a, b

indiquant de raliser lopration a := a * b, cette opration ne peut pas tre traduite directement en e e e e assembleur comme une addition ou une soustraction. Il faut procder mthodiquement en se posant les questions suivantes : e e 1. les donnes que je multiplie sont-elles signes ou toujours positives? e e 2. les donnes que je multiplie sont-elles des octets ou des mots? e Si les donnes sont signes, nous allons utiliser linstruction imul pour une multiplication, idiv pour une e e division. Sinon, nous allons utiliser linstruction mul pour une multiplication, div pour une division. Il faut savoir que quand on multiplie deux donnes codes sur chacune n bits, le rsultat est cod sur 2n e e e e bits 1 . Ceci explique ce qui suit. Concernant la taille des donnes, sachons que : e si les donnes sont des octets, lun des deux oprandes devra ncessairement se trouver dans le registre e e e al. Lautre pourra tre mis dans nimporte lequel des registres 8 bits restant ou dans une variable e dclare par une directive db. Le rsultat sera plac dans le registre ax. e e e e si les donnes sont des mots, lun des oprandes devra ncessairement se trouver dans le registre ax. e e e Lautre pourra tre mis dans nimporte lequel des registres 16 bits restant ou dans une variable dclare e e e par une directive dw. Le rsultat sera plac dans les registres dx et ax (le mot de poids fort est dans dx, e e le mot de poids faible dans ax voir la description des instructions de multiplication dans le manuel i8086). Aussi, la traduction de linstruction mentionne plus haut mul e a, b se fera de la mani`re suivante. e Supposons que les deux donnes soient des octets, le rsultat tant mis dans ce cas dans le registre ax, on e e e placera la valeur de a dans le registre al. La valeur de b pourra se trouver dans un registre 8 bits ou dans une variable. Supposons quelle soit dans une variable globale de mme nom, la multiplication se traduira e par les deux instructions : mov mul mov al, a b a, al

1. cest la mme chose en base 10 : multiplier deux nombres en base 10 qui scrivent sur 3 chires, vous pouvez avoir besoin e e de 6 chires pour exprimer le rsultat : exemple 100 100 = 100000. e

29

La dmarche pour utiliser les instructions de division est tr`s similaire ` celle pour utiliser des instructions e e a de multiplication. Aussi, vous vous reporterez au manuel i8086 pour cela.

4.7.4

Les param`tres e

Dune mani`re gnrale et an dviter tout ennui, nous passerons les param`tres en : e e e e e dans lappelant, indiquant les param`tres ` passer ` laide dinstructions push ; voir ` ce propos la e a a a section 4.6.1 dans lappel, rfrenant les param`tres par des pseudo-variables pi (cf. section 4.6.2) e ee c e Remarque sur linstruction push Linstruction push ne prend que deux types doprandes : e 1. les registres 16 bits ax, bx, cx, dx, si ou di 2. des donnes globales dclares dans la section .data avec une directive dw e e e Si lon veut passer une valeur constante, il faudra passer par lintermdiaire dun registre non utilis. e e Ainsi, on traduira push 10 par une suite dinstructions : mov push ax, 10 ax

en supposant que le contenu de ax peut tre perdu. Sinon, on utilisera un autre registre dont le contenu e peut ltre. e Les param`tres dans lappel e e Les pseudo-variables notes plus haut pi seront simplement remplace par WORD PTR [bp+2+2i]. Ainsi, e e par exemple, le param`tre p3 sera remplac par WORD PTR [bp+8]. e e On prendra garde dans un sous-programme utilisant des param`tres ` : e a 1. ajouter les deux instructions : push mov bp bp, sp

en dbut de sous-programme, immdiatement apr`s PROC ; e e e 2. ajouter linstruction pop bp

avant chaque instruction ret du sous-programme.

4.7.5

Les variables locales

Les variables locales ` un sous-programme peuvent tre alloues dans des registres. Cest ce que nous a e e avons fait dans lexemple plus haut (cf. 3.3.4). Cependant cette mthode a ses limites lorsque lon veut utiliser un nombre lev de variables locales. e e e Nous prsentons donc une autre mthode permettant davoir un nombre arbitrairement grand de variables e e locales dans un sous-programme. ` 1. compter le nombre de variables locales. Nommons-le n. A chaque variable locale, lui associer un numro e compris entre 1 et n. (Ce numro doit tre dirent pour chacune des variables locales du souse e e programme.) 30

2. sil ny en a dj`, ajouter les deux instructions ea push mov bp bp, sp

au tout dbut du sous-programme, juste apr`s PROC e e 3. apr`s ces deux instructions, mettre une instruction e sub sp, 2 n

o` vous remplacez lexpression 2 n par sa valeur u 4. ` la n du sous-programme, juste avant linstruction ret, ajouter linstruction a pop bp

si elle nest pas dj` prsente ea e 5. juste avant cette instruction, ajouter linstruction add sp, 2 n

6. dans le sous-programme, remplacer chaque occurence dune variable locale par WORD PTR [bp-2i], o` u i est le numro associ ` la variable. Nous aurons ainsi WORD PTR [bp-2] pour la premi`re variable e e a e locale, WORD PTR [bp-4] pour la deuxi`me, WORD PTR [bp-6] pour la troisi`me, ... e e Si lon reprend le sous-programme LIRE de lexemple qui poss`de une variable locale et que nous applie quons la mthode indique ci-dessus, nous obtenons le source suivant : e e
LIRE PROC ; lit un nombre au clavier ; ; en entre : pas de param`tre e e ; en sortie, bx contient la valeur du nombre saisi ; push bp mov bp, sp sub sp, 2 push ax mov bx, 0 dun caract`re au clavier e ah, 1 21h ah, 0 WORD PTR [bp-2], ax WORD PTR [bp-2] est_un_chiffre sp, 2 ax, 0 fin_de_repeter2 ax, 10 bx bx, ax WORD PTR [bp-2], 0 bx, WORD PTR [bp-2] repeter2 ax sp, 2 bp

repeter2: ; saisie mov int mov mov ; push call add cmp je ; mov mul mov sub add jmp fin_de_repeter2: pop add pop ret LIRE ENDP

31

4.7.6

La valeur de retour dun sous-programme

La valeur de retour dun sous-programme sera transmise via un registre. Gnralement, on la placera e e dans le registre ax sil sagit dun mot-mmoire, al sil sagit dun octet. e

4.8

Les tableaux en assembleur

Du fait de leur utilisation courante, de nombreuses facilits existent en assembleur pour dclarer et traiter e e des tableaux. La dclaration des tableaux e Un tableau se dclare dans le segment de donnes. Deux types de tableaux existent : e e 1. tableau doctets (ou de caract`res) ; e 2. tableau de mots-mmoire (ou dentiers). e Considrons les instructions ci-dessous : e .data ....... ; autres dclarations e db 10 dup (0) ; tableau de 10 octets initialiss e db 17 dup (?) ; tableau de 17 octets non initialiss e db 5 dup (e) ; tableau de 5 octets initialiss e dw 8 dup (10000) ; tableau de 8 mots initialiss e dw 4 dup ((43 + 56) * 25) ; tableau de 4 mots initialiss e db hello ; cha^ne de caract`res e dw hello ; ceci nest pas une cha^ne de caract`res e ....... ; suite des dclarations e Dclaration de tableaux en assembleur e Elles dclarent les tableaux suivants : e t1 : tableau de 10 octets dont la valeur initiale de chacun des llemnts est 0 ee t2 : tableau de 17 octets dont la valeur initiale des lments nest pas xe ee e t3 : tableau de 5 octets dont la valeur initiale des lments est le code ASCII de la lettre e minuscule ee t4 : tableau de 8 mots dont la valeur initiale de chacun des lments est 10000 ee t5 : tableau de 4 mots dont la valeur initiale de chacun des lments est 2475 ee t6 : tableau de 5 octets dont le premier lment est initialis avec le code ASCII de la lettre h minuscule, ee e le deuxi`me lment avec le code ASCII de la lettre e minuscule, ... Ce tableau constitue une cha e ee ne de 5 caract`res, les ch e nes de caract`res tant simplement des tableaux doctets en assembleur e e t7 : tableau de 5 mots-mmoire dont le premier lment est initialis avec le code ASCII de la lettre e ee e h minuscule, le deuxi`me lment avec le code ASCII de la lettre e minuscule, ... Il ne faut en aucun e ee cas confondre ce tableau avec le prcent (t6) : le tableau t7 nest pas une cha de caract`res e e ne e parce que les lments du tableau ne sont pas des octets, mais des mots. ee

t1 t2 t3 t4 t5 t6 t7

32

Utilisation dun tableau Lutilisation dun tableau consiste gnralement ` accder en lecture ou en criture ` un lment du e e a e e a ee tableau. Pour cela, on utilise un indice qui spcie llment accd. Lautre utilisation consiste ` passer un e ee e e a tableau en param`tre. e Il faut toujours se rappeler quun tableau en assembleur a ses lments indics ` partir de 0. Ainsi, le ee e a tableau t1 dclar plus haut a 10 lments indics de 0 ` 9. e e ee e a Un lment de tableau peut-tre spci comme oprande de nimporte quelle instruction de traitement. ee e e e e Si lon veut acc`der ` llment indic 3 du tableau t, on crira : t + 3. Si une pseudo-variable i contient la e a ee e e valeur de lindice ` acc`der, on crira t + i. a e e De mani`re gnrale, les indices sont placs dans les registres si et di. e e e e Nous laissons le passage de param`tre dun tableau de ct. e oe

4.9

Les constantes

Les constantes que lon dclare dans un programme Pascal peuvent se traduire en assembleur ` laide e a dune pseudo-instruction EQU. On trouvera un exemple de traduction de constante dans la table ci-dessous : Original Traduction en assembleur de la constante N := 10 N EQU 10

CONST

Traduction dune constante.

4.10

Programme assembleur minimal

Le programme assembleur minimal qui ne fait rien, qui sassemble et sexcute sans provoquer derreur e et qui est la base dun programme rel est le suivant : e .MODEL .STACK .DATA .CODE mov mov ; mov int END SMALL 100H

ax, @data ds, ax ah, 4ch 21h

4.11

Quelques fonctions utiles

Pour terminer, nous indiquons ci-dessous quelques fonctions bien utiles qui permettent deectuer des entres-sorties simples avec le clavier et lcran. e e

33

4.11.1

Acher un caract`re e

Lachage dun caract`re est ralis en appelant une routine du BIOS via linstruction int. Voir le e e e chapitre 5 du manuel assembleur. affiche_caractere PROC ; le code ASCII du caractere a afficher a ete passe ; en parametre push bp mov bp, sp push dx ; sauvegarde des registres dx et ax push ax ; mov dx, [bp+4] ; on charge le parametre dans dx. ; Seule la valeur de dl est prise ; en compte par la suite (dh vaut 0) mov ah, 2 int 21h ; affichage ; pop ax ; restauration du contenu des pop dx ; registres sauvegardes plus haut pop bp ret ENDP

affiche_caractere

4.11.2

Acher un nombre positif

De nombreux algorithmes peuvent tre utiliss. Nous en proposons un. Le nombre ` acher1q est stock e e a e sur un mot mmoire (16 bits). Donc sa valeur varrient entre 0 et 65535 ce qui permet dutiliser des algorithmes e reposant sur ce fait. Lalgorithme utilis est le suivant : e
PROCEDURE affiche_entier (IN nombre) diviseur := 10000 indicateur := 0 REPETER quotient := nombre / diviseur reste := nombre MOD diviseur SI (quotient # 0) OU (indicateur = 1) ALORS code_ascii := quotient + 0 affiche_caractere (code_ascii) indicateur := 1 FIN_SI nombre := reste diviseur := diviseur / 10 SI diviseur = 1 ALORS indicateur := 1 FIN_SI JUSQUE (diviseur = 0)

Lalgorithme proc`de par divisions successives du nombre ` acher par 10000, 1000, 100, 10 puis 1 e a (valeurs contenus successivement dans la variable diviseur). On prend garde de ne pas acher de 0 non signicatifs ` gauche du nombre et dacher 0 si le nombre a est nul. La variable indicateur est utilise pour cela : elle indique si le chire (cest-`-dire la valeur de la e a variable quotient) doit tre ach ou non. Il doit tre ach si un chire a dj` t ach ou si lon a e e e e ea ee e atteint le chire des units. e
affiche_nombre PROC ; ce sous-programme prend un param`tre : la valeur e ; du nombre a afficher `

34

; On alloue les donnes de la mani`re suivante : e e ; ax : quotient ; bx : nombre ; cx : diviseur ; dx : reste ; si : indicateur push bp mov bp, sp push ax ; sauvegarde des registres utiliss e push bx push cx push dx push si ; ; initialisations mov bx, [bp+4] mov cx, 10000 mov si, 0 repeter:

; bx <- nombre pass en param`tre e e ; initialise diviseur ; initialise indicateur

; ; division nombre / diviseur mov dx, 0 ; prparation de la division e mov ax, bx div cx ; on fait la division ; ; analyse du resultat cmp ax, 0 ; jne affiche ; cmp si, 1 ; jne fin_si

de la division le chiffre est-il 0 ? non, donc on laffiche chiffre = 0 ; doit-on lafficher ?

affiche:

fin_si:

; ; affichage dun chiffre du nombre add ax, 0 ; affichage du chiffre push ax call affiche_caractere add sp, 2 mov si, 1 ; indicateur positionn e mov bx, dx ; nombre := reste ; diviseur := diviseur / 10 mov dx, 0 ; prpare la division e mov ax, cx mov cx, 10 div cx mov cx, ax ; affecte le rsultat de la division e ; au registre cx ; ; SI diviseur = 1 ALORS indicateur := 1 cmp cx, 1 ; si le diviseur vaut 1, on va soccuper jne suite ; du chiffre des unites quil faut mov si, 1 ; toujours afficher

suite:

; ; SI diviseur # 0 ALORS on it`re e cmp cx, 0 jne repeter ; ; restauration des registres pop si pop dx pop cx pop bx pop ax pop bp ret ENDP

affiche_nombre

4.11.3

Lire un caract`re e
PROC ; lit le code ASCII dun caract`re e ; Ce code se trouve dans le registre al au retour de lappel mov ah, 1

lit_caractere

35

lit_caractere

int ret ENDP

21h

; lecture

36

Vous aimerez peut-être aussi