<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
<book lang="fr">
  
  <bookinfo>

    <title>Initiation au langage d'assemblage x86</title>
    
    <author>
      <firstname>Emmanuel</firstname>
      <surname>Saracco</surname>
      <affiliation>
	<orgname>Easter-eggs.com</orgname>
	<address>
	  <email>esaracco@easter-eggs.com</email>
	  <email>esaracco@free.fr</email>
	</address>
      </affiliation>
    </author>
	
    <revhistory>
      <revision>
	<revnumber>v1.0b</revnumber>
	<date>2005-12-11</date>
	<authorinitials>es</authorinitials>
	<revremark>Nommage systématique des chapitres et sections. Mise en place d'une nouvelle feuille de style. Modification du texte de licence.</revremark>
      </revision>
      <revision>
	<revnumber>v1.0a</revnumber>
	<date>2004-02-12</date>
	<authorinitials>es</authorinitials>
	<revremark>Passage d'un source SGML à un source XML.</revremark>
      </revision>
      <revision>
	<revnumber>v1.0</revnumber>
	<date>2002-04-09</date>
	<authorinitials>es</authorinitials>
	<revremark>Première version.</revremark>
      </revision>
    </revhistory>
    
    <legalnotice>
      <para>
	<citetitle>Initiation au langage d'assemblage x86</citetitle> 
      </para>
      
      <para>
        Permission is granted to copy, distribute and/or modify this document
        under the terms of the GNU Free Documentation License, Version 1.2
        or any later version published by the Free Software Foundation;
        with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
        Texts.
      </para>
      
      <para>
        A copy of the license is available on the World Wide Web at 
      <ulink url="http://www.gnu.org/licenses/fdl.html">http://www.gnu.org/licenses/fdl.html</ulink>.
      </para>
    </legalnotice>
    
    <copyright>
      <year>2002-2005</year>
      <holder>Emmanuel Saracco, Easter-eggs</holder>
    </copyright>
    
  </bookinfo>
  
  <chapter id="les-premiers-pas">
    <title>Les premiers pas</title>
    
    <para> 
      Ce document consacrés au langage d'assemblage pour processeurs x86 sous linux s'adresse à des débutants ayant quelques notions de programmation et ne se veut en aucun cas exhaustif.
    </para>
    <para>
      Ceux d'entre vous ayant déjà codé en assembleur<footnote><para>L'expression exacte serait « langage d'assemblage », l'assembleur étant le programme qui permet de transformer le langage d'assemblage en code binaire ; néanmoins, nous emploierons ici le terme « assembleur » pour nous simplifier la tâche.</para></footnote> sous DOS ou windows seront certainements surpris d'apprendre que la syntaxe qu'ils avaient employée jusqu'à présent n'est pas la seule disponible. En effet, il existe en fait deux styles de notation possibles :
    </para>
    <sect1 id="les-conventions-intel">
      <title>Les conventions Intel</title>
      <para>
	Ces conventions viennent directement du constructeur de microprocesseur ; c'est pourquoi elles sont suivies par la majeure partie des assembleurs modernes. Le code généré est clair et épuré.
      </para>

      <para>Exemple:</para>
      <para>
	<programlisting>
	  push ebp
	  mov ebp,esp
	  push ebx
	  push esi
	  push edi
	</programlisting>
      </para>
    </sect1>
    <sect1 id="les-conventions-att">
      <title>Les conventions AT&amp;T</title>
      <para>
	Ce style de codage est né en même temps qu'Unix et les conventions datent donc de cette époque. Il est un peu perturbant si l'on a pris l'habitude de coder en suivant les conventions Intel, mais on se familiarise assez rapidement avec.
      </para>
      
      <para>Exemple:</para>
      <para>
	<programlisting>
	  pushl %ebp
	  movl %esp,%ebp
	  pushl %ebx
	  pushl %esi
	  pushl %edi
	</programlisting>
      </para>
      
      <para>
	Il existe quelques assembleurs qui permettent d'utiliser les conventions Intel sur de l'Unix, le plus abouti étant Nasm. Dans ce document nous utiliserons les conventions de notation Intel et emploierons Nasm pour les raisons suivantes : 
	
	<itemizedlist mark='opencircle'>
	  <listitem>
	    <para>
	      La convention Intel nous semble être plus claire et moins source d'erreurs que la convention AT&amp;T. 
	    </para>
	  </listitem>
	  <listitem override='bullet'>
	    <para>
	      Nasm permet à ceux qui étaient habitués à coder sous DOS de garder leurs habitudes :-) 
	    </para>
	  </listitem>
	</itemizedlist>
	
	A vrai dire, deux bonnes raisons qui auraient pu nous faire pencher pour l'utilisation des conventions AT&amp;T sont : 
	
	<itemizedlist mark='opencircle'>
	  <listitem>
	    <para>
	      Le format d'affichage de GNU gdb. 
	    </para>
	  </listitem>
	  <listitem override='bullet'>
	    <para>
	      La syntaxe de l'assembleur inline dans du code C via <function>__asm__()</function>. 
	    </para>
	  </listitem>
	</itemizedlist>
	
	Néanmoins, nous adressant ici à des débutants en assembleur il ne fallait pas compliquer les choses<footnote><para>Nous envisagerons peut-être de dédier un chapitre à la convention AT&amp;T par la suite &mdash; tout dépendra de la demande :-)</para></footnote>.
	
      </para>
    </sect1>
    <sect1 id="qu-est-ce-que-c-est">
      <title>Qu'est-ce que c'est?</title>
      <para>
	Vous avez sûrement déjà codé avec un langage de haut niveau comme C ou Pascal. Ces langages permettent d'écrire de manière quasi naturelle ce que nous voulons que la machine fasse : un programme C exécutant : « afficher "coucou" » s'écrira<footnote><para>Le code est volontairement dépouillé du superflu. Utilisez <userinput>gcc coucou_c.c -o coucou_c</userinput> pour compiler cet exemple.</para></footnote>: 
      </para>
      
      
      <para>Exemple <filename>coucou_c.c</filename></para>
      <para>
	<programlisting>
	void main()
	{
		puts("coucou\n");
	}
	</programlisting>
      </para>
      
      <para>
	La même chose en assembleur s'écrirait<footnote><para>Utilisez <userinput>nasm -f elf coucou.asm ; ld -s coucou_asm.o -o coucou_asm</userinput> pour compiler cet exemple.</para></footnote>:
      </para>
      
      <para>Exemple <filename>coucou_asm.asm</filename></para>
      <para>
	<programlisting>
	section .text
		global _start
	  
		msg        db  'coucou',0x0A
		msg_len    equ $ - msg
	  
	_start:
		mov eax,4
		mov ebx,1
		mov ecx,msg
		mov edx,msg_len
		int 80h
	  
		mov eax,1
		int 80h
	</programlisting>
      </para>
      
      <para>
	Ce dernier source est tout à fait fonctionnel et autonome. Nous aurions pu également nous aider de la libc et éviter la manipulation directe de l'interruption 0x80, et nous aurions alors eu le mélange de C et d'assembleur suivant<footnote><para>Utilisez <userinput>nasm -f elf coucou_asm_libc.asm ; gcc coucou_asm_libc.o -o coucou_libc</userinput> pour compiler cet exemple.</para></footnote>: 
      </para>
      
      <para>Exemple <filename>coucou_asm_libc.asm</filename></para>
      <para>
	<programlisting>
	extern puts
	  
	section .text
		global main
  
		msg db 'coucou',0
 
	main:
		push ebp
		mov  ebp,esp
		push ebx
		push esi
		push edi
	  
		push dword msg
		call puts
	  
		pop  edi
		pop  esi
		mov  esp,ebp
		pop  ebp
		ret
	</programlisting>
      </para>
      
      <para>
	Lier son code assembleur avec la libc peut-être effectivement très pratique si l'on ne veut pas réinventer la roue ; mais il faut à ce moment se poser la question de savoir si nous n'aurions pas plus vite fait d'intégrer de l'assembleur inline dans un code C.
	
	Dans le cadre d'applications « professionnelles » il est néanmoins conseillé de ne pas passer directement par l'interruption logicielle 0x80 pour faire appel aux routinex kernel. En effet, rien ne certifie que les services proposés par cette interruption ne changeront pas &mdash; l'équipe de développement kernel conseillant de toujours passer par la libc s'agissant des appels systèmes et se réservant le droit de modifier quoi que ce soit sans crier gare. <ulink url="http://home.snafu.de/phpr/lhpsyscal.html">Pour en savoir plus sur l'interruption 0x80 et les différents appels systèmes</ulink>. 
      </para>
      
      <para>
	Malgré tout il existe quelques avantages à coder en assembleur « pur » (c'est à dire, à ne pas se lier avec la libc) :
	
	<itemizedlist mark='opencircle'>
	  <listitem>
	    <para>
	      La vitesse d'exécution. 
	    </para>
	  </listitem>
	  <listitem override='bullet'>
	    <para>
	      La taille du code généré. 
	    </para>
	  </listitem>
	  <listitem override='bullet'>
	    <para>
	      Le faible besoin en RAM. 
	    </para>
	  </listitem>
	</itemizedlist>
	
      </para>
      
      <para>
	Pour ce qui est de la vitesse d'exécution, il est vrai qu'on arrive rapidement à produire un code moins performant que celui généré par gcc, mais tout dépend de ce qu'on fait de ce pour quoi on utilise l'assembleur. Dans la majeure partie des cas la nécessité d'écrire un programme entier en assembleur ne se posera pas &mdash; s'il s'agit d'optimisation, on emploiera plus volontier de l'assembleur inline. 
      </para>
      
      <para>
	Mais si, par exemple, il est vital d'obtenir un binaire très petit, si la RAM disponible est très faible ou encore si l'on ne veut absolument pas se lier avec une librairie comme la libc, alors on pourra se fier à l'assembleur. Quelques explications supplémentaires pourront être trouvées sur <ulink url="http://linuxego.mine.nu/why.shtml">Linuxego</ulink>.
      </para>
      
      <para>
	Voici en guise d'exemple un tableau récapitulant les différents programmes sourcés ci-dessus et la taille des binaires générés (avant et après un strip <footnote><para><userinput>strip mon_prog</userinput>. Stripper un programme consiste à l'alléger en supprimant les symboles qu'il contient.</para></footnote>) : 
      </para>
      
      <table><title>Comparatif des tailles</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Nom</entry>
	      <entry>Taille</entry>
	      <entry>Strip</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><filename>coucou_asm</filename></entry>
	      <entry><literal>428</literal></entry>
	      <entry><literal>428</literal></entry>
	    </row>
	    <row>
	      <entry><filename>coucou_asm_libc</filename></entry>
	      <entry><literal>4751</literal></entry>
	      <entry><literal>2956</literal></entry>
	    </row>
	    <row>
	      <entry><filename>coucou_c</filename></entry>
	      <entry><literal>4792</literal></entry>
	      <entry><literal>3000</literal></entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>
      
      <para>
	On voit que c'est sans conteste le programme écrit en assembleur qui l'emporte au niveau de la taille finale<footnote><para>Pour vous détendre un peu et voir jusqu'ou on peut aller pour faire maigrir un code, jetez un oeil sur <ulink url="http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html">A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux</ulink></para></footnote>. 
      </para>
      
    </sect1>
    
    <sect1 id="comment-ca-fonctionne">
      <title>Comment ça fonctionne?</title>
      
      <para>
	L'assembleur permet d'opérer directement sur le processeur, la mémoire ou les périphériques. Sa syntaxe est simple, pour ne pas dire rudimentaire, et toutes les opérations possibles ne sont faites qu'à l'aide de quelques instructions comme <literal>mov</literal>, <literal>push</literal>, <literal>pop</literal> etc. En règle générale, les instructions sont nommées assez explicitement pour que l'on comprenne plus ou moins intuitivement l'opération effectivement réalisée : 
      </para>
      
      <table><title>Exemple d'instructions assembleur</title>
	<tgroup cols="2" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Instruction</entry>
	      <entry>Opération effectuée</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>mov</literal></entry>
	      <entry> Permet de « bouger » des données d'un emplacement à un autre (espace mémoire ou registre processeur)</entry>
	    </row>
	    <row>
	      <entry><literal>push</literal></entry>
	      <entry>Sert à « pousser » des données sur la pile</entry>
	    </row>
	    <row>
	      <entry><literal>pop</literal></entry>
	      <entry>Permet de récupérer les données empilées via push</entry>
	    </row>
	    <row>
	      <entry><literal>...</literal></entry>
	      <entry>...</entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>
      
      <para>
	Que veut dire exactement « s'adresser directement au processeur » ? Les langages de plus haut niveau ne permettent-ils pas cela également ? 
      </para>
      
      <para>
	En fait, le compilateur ne peut pas passer directement du C au bytecode. Il est obligé d'implémenter un analyseur syntaxique complexe qui interprètera ce que le programmeur à voulu faire dans un jargon particulier (C, Pascal etc.). 
      </para>
      
      <para>
	Lorsque vous codez en assembleur, vous ne dites pas <literal>A = 0</literal>, mais vous demandez directement au processeur de mettre un registre à <literal>0</literal>, par exemple. La variable <varname>A</varname> sera en fait le registre <varname>eax</varname> ou une de ses subdivisions (<varname>al</varname>, <varname>ah</varname> ou <varname>ax</varname> <footnote><para>Nous ne sommes pas encore entré dans les détails des registres, mais anticipons un peu en disant seulement qu'un registre peut contenir des données de différentes tailles : <varname>al</varname> et <varname>ah</varname> contiennent des données sur 8 bits et constituent à eux deux le registre <varname>ax</varname> qui lui contient des données sur 16 bits. Au-dessus nous avons le registre <varname>eax</varname> qui permet de stocker des données sur 32 bits (il n'existe pas de découpage en 2x16 bits comme pour <varname>ax</varname>).</para></footnote>) et sa mise à <literal>0</literal> pourra se faire de plusieurs manières : 
      </para>
      
      <itemizedlist mark='opencircle'>
	<listitem>
	  <para>
	    Explicitement <literal>mov eax,0</literal>.
	  </para>
	</listitem>
	<listitem override='bullet'>
	  <para>
	    Via une opération logique : <literal>xor eax,eax</literal>. Cette seconde voie étant bien plus rapide que la première. 
	  </para>
	</listitem>
      </itemizedlist>
      
      <para>
	Un compilateur C pourra par exemple traduire le code suivant :
	
      </para>
      
      <programlisting>
	A = 0
	B = 0
      </programlisting>
      
      <para>
	en : 
      </para>
      
      <programlisting>
	xor eax,eax
	mov A,eax
	xor eax,eax
	mov B,eax
      </programlisting>
      
      <para>
	On ne peut pas dire que cela soit optimum... Si nous avions fait cela directement en assembleur, nous aurions évité de définir 2 variables en utilisant le plus possible les registres : 
      </para>
      
      <programlisting>
	xor eax,eax
	xor ebx,ebx
      </programlisting>
      
      <para>
	Comme vous l'aurez compris, tout se passe souvent dans le détail, mais au bout du compte, c'est ce qui fera la différence. gcc et les compilateurs modernes génèrent du code de plus en plus optimisé, mais cela restera toujours de la génération automatique et le bytecode se verra toujours pollué de choses inutiles. 
      </para>
      
      <para>
	On pourrait cependant se demander en quoi une instruction assembleur <literal>xor</literal> est plus proche de la machine qu'une instruction C. En fait, les instructions assembleur sont juste étudiées pour être un peu plus digestes que du code hexadécimal ou binaire ; mais elles y ont leur équivalent exact. Par exemple <literal>xor ax,ax</literal> se traduira directement par le code hexadécimal <literal>31C0</literal> qui lui-même représente le code machine <literal>0011000111000000</literal>. Il faut donc bien comprendre qu'en codant en assembleur, on code en fait quasi directement en binaire, et rien ne vient interférer entre ce que l'on veut faire et la façon dont cela sera effectivement fait. 
      </para>
    </sect1>
    
  </chapter>

  <chapter id="registres-et-structures">
    <title>Registres et structures</title>

      <para>
	Dans le <xref linkend="les-premiers-pas" /> nous avions laissé de côté la description des registres du processeur et leur rôle respectif. Nous allons ici rapidement aborder le sujet &mdash; juste assez pour pouvoir nous débrouiller par la suite. 
      </para>

    <para>
      Tout d'abord, précisons que sous GNU/Linux, contrairement à ce qui se passait sous DOS, nous n'avons pas vraiment besoin de nous encombrer avec la notion de « segment ». En effet, bien que les segments soient évidemment présents, leur taille peut aller jusqu'à 4Go... Donc, nous n'aurons plus à jouer avec <literal>cs</literal>, <literal>ds</literal> et <literal>es</literal>  constamment :-) Penchons-nous plutôt sur ce qui nous touche de près : les registres du microprocesseur. Les registres qui nous intéressent particulièrement sont : 
    </para>

      <itemizedlist mark='opencircle'>
	<listitem>
	  <para>
	  Les registres de travail : <literal>eax</literal>, <literal>ebx</literal>, <literal>ecx</literal> et <literal>edx</literal>. 
	  </para>
	</listitem>
	<listitem override='bullet'>
	  <para>
	  Les registres d'offset : <literal>esi</literal>, <literal>edi</literal>, <literal>ebp</literal> et <literal>esp</literal>. 
	  </para>
	</listitem>
	<listitem override='bullet'>
	  <para>
	  Le registre des flags : <literal>eflags</literal>. 
	  </para>
	</listitem>
      
      </itemizedlist>

    <sect1 id="les-registres-de-travail">
      <title>Les registres de travail</title>

      <para>
	Comme nous l'indiquions brièvement dans le <xref linkend="les-premiers-pas" />, ces registres (sur 32 bits depuis le 80386) peuvent être décomposés en plusieurs sous-registres. Vous serez sûrement surpris de constater qu'on ne peut atteindre directement les 16 bits de poids fort des registres <literal>e*x</literal>, et que les registres <literal>*x</literal> ne représentent que les 16 bits de poids faible<footnote><para>On désigne par « bits de poids faible » les bits situés le plus à droite, et « bits de poid fort » ceux situés le plus a gauche. Par exemple, si nous travaillons avec un registre de 16 bits et que nous y mettons la valeur hexadécimale <literal>0x4d50</literal> : <literal>mov ax,4d50h</literal>, <literal>al</literal> contiendra <literal>0x50</literal> et <literal>ah</literal> contiendra <literal>0x4d</literal>.</para></footnote>. Si cela vous dérange, vous pourrez toujours utiliser l'instruction movzx qui permet d'étendre *x dans e*x. 
      </para>

      <table><title>Découpage d'un registre de travail</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <colspec colname="c1" />
	  <colspec colname="c2" />
	  <colspec colname="c3" />
	  <thead>
	    <row>
	      <entry namest="c1" nameend="c2">Registre(s)</entry>
	      <entry>Taille</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry namest="c1" nameend="c2"><literal>eax</literal></entry>
	      <entry>32 bits</entry>
	    </row>
	    <row>
	      <entry namest="c1" nameend="c2"><literal>ax</literal></entry>
	      <entry>16 bits</entry>
	    </row>
	    <row>
	      <entry><literal>ah</literal></entry>
	      <entry><literal>al</literal></entry>
	      <entry>8 bits chacun</entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	On voit que <literal>e*x</literal> (32 bits) est composé de <literal>*x</literal> (16 bits de poids faible) lui-même composé de <literal>*h</literal> (8 bits de poids fort) et <literal>*l</literal> (8 bits de poids faible). 
      </para>

      <table><title>Rôles des registres de travail</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Registre</entry>
	      <entry>Nom</entry>
	      <entry>Description</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>eax</literal></entry>
	      <entry><emphasis role="bold">A</emphasis>ccumulateur</entry>
	      <entry> Sert aux opérations mathématiques mais aussi à la transmission des numéros de fonctions à appeler lorsqu'on se sert de l'interruption système <literal>0x80</literal> et au renvoi des résultats dans les fonctions (que nous aborderons au <xref linkend="programmation-structuree-en-assembleur" />).
		
		Lorsque vous devez utiliser un registre pour une opération quelconque et que vous ne savez pas lequel utiliser, privilégiez celui-ci &mdash; c'est le plus optimisé au niveau de la rapidité d'exécution des opérations. </entry>
	    </row>
	    <row>
	      <entry><literal>ebx</literal></entry>
	      <entry><emphasis role="bold">B</emphasis>ase</entry>
	      <entry> Utilisé pour l'adressage indirect</entry>
	    </row>
	    <row>
	      <entry><literal>ecx</literal></entry>
	      <entry><emphasis role="bold">C</emphasis>ompteur</entry>
	      <entry> Ce registre est utilisé chaque fois que l'assembleur a besoin d'un « compteur ». Nous devrons l'utiliser lorsque nous mettrons en place des boucles via l'instruction loop ou encore lorsque nous ferons des transferts de données entre <literal>esi</literal> et <literal>edi</literal>.</entry>
	    </row>
	    <row>
	      <entry><literal>edx</literal></entry>
	      <entry><emphasis role="bold">D</emphasis>onnées</entry>
	      <entry>Sert aussi pour quelques opérations mathématiques.</entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	Même si chacun de ces registres est optimisé ou utilisé pour certaines opérations, ils peuvent être employés à toutes fins utiles. Il suffit juste de savoir, par exemple, que c'est le registre <literal>ecx</literal> et lui seul qui sera utilisé et décrémenté par l'instruction <literal>movsd</literal> dans le cas de la copie d'une chaîne<footnote><para>nous aborderons cet exemple plus loin lorsque nous parlerons des boucles.</para></footnote>. Cela ne veut pas dire que <literal>ecx</literal> est dédié  à cette tâche de « compteur », mais juste qu'il est optimisé par le processeur pour cette tâche et utilisé en tant que tel par d'autres instructions. 
      </para>
    </sect1>

    <sect1 id="les-registres-d-offset">
      <title>Les registres d'offset</title>

      <table id="tabregoffset"><title>Rôles des registres d'offset</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Registre</entry>
	      <entry>Nom</entry>
	      <entry>Description</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>esi</literal></entry>
	      <entry><emphasis role="bold">S</emphasis>ource <emphasis role="bold">I</emphasis>ndex</entry>
	      <entry>Utilisé lors des opérations sur les chaînes de caractères</entry>
	    </row>
	    <row>
	      <entry><literal>edi</literal></entry>
	      <entry><emphasis role="bold">D</emphasis>estination <emphasis role="bold">I</emphasis>ndex</entry>
	      <entry>Comme <literal>esi</literal>, ce registre sert lors des opérations sur des chaînes de caractères.</entry>
	    </row>
	    <row>
	      <entry><literal>ebp</literal></entry>
	      <entry><emphasis role="bold">B</emphasis>ase <emphasis role="bold">P</emphasis>ointer</entry>
	      <entry>Référence la base de la pile</entry>
	    </row>
	    <row>
	      <entry><literal>eip</literal></entry>
	      <entry><emphasis role="bold">I</emphasis>struction <emphasis role="bold">P</emphasis>ointer</entry>
	      <entry> Ce registre est particulier car il ne peut pas être manipulé directement. Il pointe en permanence sur la <emphasis>prochaine</emphasis> opération à exécuter.</entry>
	    </row>
	    <row>
	      <entry><literal>esp</literal></entry>
	      <entry><emphasis role="bold">S</emphasis>tack <emphasis role="bold">P</emphasis>ointer</entry>
	      <entry>Pointe vers le dernier élément déposé sur la pile (l'élément courant)</entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>
    </sect1>

    <sect1 id="le-registre-des-drapeaux">
      <title>Le registre des drapeaux</title>

      <para>
	<literal>eflags</literal> est un registre 32 bits qui rend compte de l'état du processeur après chaque instruction. Il est composé du sous-registre <literal>flags</literal>  (16 bits de poids faible). On n'accède jamais à ce registre dans son intégralité, mais toujours bit par bit lorsqu'on veut une information bien précise sur, par exemple, le résultat d'une comparaison ou d'une opération arithmétique. Les drapeaux les plus importants pour nous sont les suivants : 
      </para>

      <table><title>Les bits du registre <literal>eflags</literal></title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Drapeau</entry>
	      <entry>Nom</entry>
	      <entry>Position</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>cf</literal></entry>
	      <entry><emphasis role="bold">C</emphasis>arry <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>0</literal></entry>
	    </row>
	    <row>
	      <entry><literal>pf</literal></entry>
	      <entry><emphasis role="bold">P</emphasis>arity <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>2</literal></entry>
	    </row>
	    <row>
	      <entry><literal>af</literal></entry>
	      <entry><emphasis role="bold">A</emphasis>uxiliary carry <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>4</literal></entry>
	    </row>
	    <row>
	      <entry><literal>zf</literal></entry>
	      <entry><emphasis role="bold">Z</emphasis>ero <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>6</literal></entry>
	    </row>
	    <row>
	      <entry><literal>sf</literal></entry>
	      <entry><emphasis role="bold">S</emphasis>ign <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>8</literal></entry>
	    </row>
	    <row>
	      <entry><literal>if</literal></entry>
	      <entry><emphasis role="bold">I</emphasis>nterruption <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>9</literal></entry>
	    </row>
	    <row>
	      <entry><literal>df</literal></entry>
	      <entry><emphasis role="bold">D</emphasis>irection <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>10</literal></entry>
	    </row>
	    <row>
	      <entry><literal>of</literal></entry>
	      <entry><emphasis role="bold">O</emphasis>verflow <emphasis role="bold">F</emphasis>lag</entry>
	      <entry><literal>11</literal></entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	Il en existe d'autres, mais nous ne les utiliserons pas ici. 
      </para>
    </sect1>

    <sect1 id="les-structures-de-controle">
      <title>Les structures de contrôle</title>

      <para>
	On peut bien sûr produire en assembleur le même genre de boucle que dans un langage de haut niveau. Nous verrons à cette occasion l'utilité des registres <literal>eflags</literal> et <literal>ecx</literal>. 
      </para>

      <sect2 id="boucle-while">
	<title>Boucle <literal>WHILE</literal></title>

	<para>
	  Le test est fait en <emphasis>début</emphasis> de boucle. 
	</para>

	<para> En C, nous ferions:</para>
	<para>
	  <programlisting>
	    compteur = 0;
	    while ( compteur &lt; 10)
	    ++compteur;
	  </programlisting>
	</para>

	<para> En assembleur, nous faisons:</para>
	<para>
	  <programlisting>
		xor ax,ax
	debut:
		cmp ax,10
		jae fin
		inc ax
		jmp debut
	fin:
	  </programlisting>
	</para>

	<para>
	  L'instruction jae nous permet ici de tester si le terme gauche de la comparaison faite avec cmp est supérieur ou égal au terme de droite. Dès que ax est <emphasis>supérieur</emphasis> ou égale à 10 alors on saute au label <varname>fin</varname>, sinon, on incrémente <varname>ax</varname> et on saute impérativement au label debut. <literal>jae</literal> vérifie la valeur du flag <varname>CF</varname>. Si <literal>CF=0</literal>, alors la condition est remplie. 
	</para>

      </sect2>

      <sect2 id="boucle-do-while">
	<title>Boucle <literal>DO..WHILE</literal></title>

	<para>
	  Le test est fait en <emphasis>fin</emphasis> de boucle. 
	</para>

	<para> En C, nous ferions:</para>
	<para>
	  <programlisting>
		compteur = 0;
		do
			++compteur;
		while (compteur &lt; 10);
	  </programlisting>
	</para>

	<para> En assembleur, nous faisons:</para>
	<para>
	  <programlisting>
		xor ax,ax
	debut:
		inc ax
		cmp ax,10
		jb debut
	  </programlisting>
	</para>

	<para>
	  L'instruction jb nous permet ici de tester si le terme gauche de la comparaison faite avec <literal>cmp</literal> est inférieur au terme de droite. Tant que <varname>ax</varname> est <emphasis>inférieur</emphasis> à 10 alors on saute au label debut. <literal>jb</literal> vérifie la valeur du flag <varname>CF</varname>. Si <literal>CF=1</literal>, alors la condition est remplie. 
	</para>

      </sect2>

      <sect2 id="boucle-for">
	<title>Boucle <literal>FOR</literal></title>

	<para>
	  Aucun test n'est fait. On effectue un nombre determiné d'itérations. 
	</para>

	<para> En C, nous ferions:</para>
	<para>
	  <programlisting>
		int compteur;
		for (compteur = 0; compteur &lt; 10; compteur++) 
			;
	  </programlisting>
	</para>

	<para> En assembleur, nous faisons:</para>
	<para>
	  <programlisting>
		mov ecx,10
	debut:
		nop
		loop debut
	  </programlisting>
	</para>

	<para>
	  La boucle <literal>for</literal> n'étant en fin de compte qu'une variante de <literal>while</literal> il existe bien sûr plusieurs façons de faire. Ici, nous nous servons du registre compteur <varname>ecx</varname>. Nous avons un bel exemple d'instruction <literal>loop</literal> qui s'attend à trouver dans <varname>ecx</varname> le nombre d'itérations à accomplir et qui décrémente automatiquement ce registre. 
	</para>

      </sect2>
    </sect1>

    <sect1 id="copie-de-chaine">
      <title>Copie de chaîne</title>
      
      <para>
	Pour vous montrer dans quel cas on utilise les registres d'offset <varname>esi</varname> et <varname>edi</varname>, nous allons aborder sommairement, toujours dans le cadre de l'étude des boucles, le traitement des chaînes de caractères.
      </para>
      
      <para>
	Le programme suivant recopie la chaîne se trouvant dans la variable <varname>strsrc</varname> dans la variable <varname>strdst</varname><footnote><para>Utilisez <userinput>nasm -f elf strcpy.asm ; ld -s strcpy.o -o strcpy</userinput> pour compiler cet exemple.</para></footnote> : 
      </para>
      
      <para>Exemple <filename>strcpy.asm</filename></para>
      <para>
	<programlisting>
	section        .text
		global        _start
	  
	_start:
		mov esi,strsrc
		mov edi,strdst
		mov ecx,strsrc_len
	debut:
		mov eax,[esi]
		mov [edi],eax
		inc esi
		inc edi
		loop debut
	fin:       
		mov        eax,1
		int        0x80
	  
	section .data
		strsrc db "premiere chaine",0x0A
		strsrc_len equ $ - strsrc
		strdst times strsrc_len db 0
	</programlisting>
      </para>
      
      <para>
	Nous avons vu au <xref linkend="tabregoffset" /> que les registres <varname>esi</varname> et <varname>edi</varname> servaient aux opérations sur les chaînes. Ici, nous faisons pointer <varname>esi</varname> vers la variable contenant la chaîne source, et <varname>edi</varname> vers la variable de destination. Ensuite, nous mettons dans <varname>ecx</varname> le nombre de caractères à recopier, puis nous bouclons jusqu'à ce que l'instruction <literal>loop</literal> ait assez décrémenté <varname>ecx</varname> pour le mettre à zéro. 
      </para>
      
      <para>
	Au sein de la boucle, nous prenons chacun des caractères contenus par <varname>esi</varname> et nous les mettons dans <varname>edi</varname> . Pour ce faire, nous sommes obligés de passer par un registre intermédiaire car il est impossible de copier directement le contenu de l'un vers l'autre. Après chaque copie de caractère, on incrémente <varname>esi</varname> et <varname>edi</varname> pour qu'ils pointent sur le caractère suivant. 
      </para>
      
      <para>
	Ces opérations vous paraissent lourdes ? Effectivement... Ces copies étant quelque chose de courant, il existe des instructions qui les optimisent :-) 
      </para>
      
      <para>
	En fait, on aurait dû coder la boucle ci-dessus comme suit : 
      </para>
      
      <para>
	<programlisting>
	  mov ecx,strsrc_len
	  mov esi,strsrc
	  mov edi,strdst
	  rep movsb
	</programlisting>
      </para>
      
      <para>
	Le processeur exécutera ce code près de trois fois plus vite que le code précédent. L'instruction <literal>rep</literal>, associée à une instruction comme <literal>movsb</literal> répètera autant de fois que <literal>ecx</literal> le lui « demandera » en le décrémentant automatiquement. 
      </para>
      
    </sect1>
  </chapter>

  <chapter id="programmation-structuree-en-assembleur">
    <title>Programmation structurée en assembleur</title>

    <sect1 id="la-pile">
      <title>La pile</title>

      <para>
	On utilise la pile pour stocker des données ou des adresses de manière temporaire. Elle peut être vue comme un lieu d'échange dans lequel on dépose et on reprend des éléments. Elle fonctionne sur le mode LIFO<footnote><para>« Last In First Out ». Il faut voir ça comme un empilement d'assiettes un peu spécial, par exemple, auquel on ajouterait et retirerait des assiettes <emphasis>par en-dessous</emphasis>. L'exemple des assiettes est un peu trompeur tout de même ; en effet, on peut très bien accéder à n'importe quel élément de la pile en y faisant référence de manière indirecte via <varname>esp</varname>, comme nous le verrons.</para></footnote>. C'est pour cela que tout ce qu'on déposera sur la pile via l'instruction <literal>push</literal> devra obligatoirement en être retiré dès que possible dans l'ordre inverse à l'ordre de dépôt via l'instruction <literal>pop</literal>. 
	
	<programlisting>
	  push eax
	  push ebx
	  push ecx
	  
	  [...]
	  
	  pop ecx
	  pop ebx
	  pop eax
	</programlisting>

	Le code ci-dessus vaut si vous avez besoin de retrouver dans les mêmes registres les valeurs sauvegardées sur la pile. Nous aurions très bien pu récupérer la valeur poussée en premier (celle de <varname>eax</varname> dans <varname>ebx</varname> en modifiant l'ordre des <literal>pop</literal>).
	
      </para>

      <para>
	Il y a également des fois ou l'on se moque complètement de récupérer les valeurs empilées<footnote><para>Dans le cas des appels de fonctions avec passage d'arguments, par exemple.</para></footnote>. Dans ce cas l'utilisation de <literal>pop</literal>  est inutile, et même mauvaise pour les performances. Il faudra donc pouvoir accéder directement à la pile en nous servant de la valeur contenue dans le registre <varname>esp</varname>. 
      </para>

      <para>
	En fait, lorsque le programme se lance, <varname>esp</varname> pointe sur la fin du segment principal<footnote><para>Si nous n'étions pas en mode protégé, le segment relatif à la pile serait <varname>ss</varname>.</para></footnote> : le début de la pile pour notre programme. Les opérations faites par <literal>push</literal> feront descendre le pointeur, et celles effectuées par <literal>pop</literal> le feront remonter. Il faudra donc toujours penser qu'ajouter quelque chose sur la pile (en fait, <emphasis>en-dessous</emphasis>), décrémentera le pointeur courant <varname>esp</varname>. Nous venons de voir que l'on se sert habituellement de <literal>pop</literal> pour rétablir l'état de la pile, mais qu'il existe d'autres moyens. On peut très bien, puisqu'il ne s'agit en fait que de déplacer le pointeur dans la pile, augmenter la valeur de ce pointeur directement.
      </para>

      <para>
	Lorsque, par exemple, j'empile <varname>eax</varname>, je diminue la valeur du pointeur de 4 octets (32 bits), et lorsque je fais un <literal>pop</literal>, je récupère la valeur et le pointeur est incrémenté de 4 octets pour retrouver son emplacement d'origine en haut de la pile. Cette opération étant lourde au niveau des performances<footnote><para>Toute opération concernant la pile est plus lourde que les opérations avec les registres, c'est pourquoi il est préférable lorsque cela est possible de sauvegarder les valeurs dans des registres au lieu de les empiler/dépiler.</para></footnote> on pourra dès que possible vouloir juste faire en sorte que le pointeur retrouve son emplacement : 

	<programlisting>
	  push dword arg2
	  push dword arg1
	  
	  call ma_fonction
	  
	  add sp,8
	</programlisting>

	La fonction <function>ma_fonction</function> nécessitant deux arguments et les attendant sur la pile<footnote><para>Nous aurions tout aussi bien pu lui passer via deux registres, ce qui aurait d'ailleurs été plus rapide.</para></footnote>, nous les empilons avans l'appel. Au retour de la fonction nous faisons le ménage en incrémentant le pointeur de pile de 8 octets<footnote><para>Chaque valeur poussée sur la pile occupant ici 4 octets (<literal>dword</literal>).</para></footnote>. 
      </para>

      <para>
	Cela ne vaut évidemment que lorsque nous appelons une fonction qui respecte les conventions du C au niveau du passage d'arguments<footnote><para>Il existe deux conventions pour l'empilement/dépilement des arguments passés à une fonction : la convention C (c'est à l'appelant de nettoyer la pile &mdash; cela permet une gestion plus aisée des arguments variables), et la convention Pascal (c'est à la fonction appelée de nettoyer la pile).</para></footnote>. 
      </para>

      <para>
	En ce qui concerne la pile, nous en savons bien assez pour aborder les fonctions. 
      </para>

    </sect1>

    <sect1 id="les-fonctions">
      <title>Les fonctions</title>
      
      <para>
	Notre découverte de l'assembleur nous montre bien qu'au bout d'un moment les programmes doivent devenir plutôt indigestes... C'est pourquoi, comme avec tout autre langage, il est possible de structurer son code en mettant en place des fonctions<footnote><para>Que les pascaliens nous excusent, nous ne ferons pas la différence entre fonction et procédure ici, et suivrons plutôt le vocabulaire C.</para></footnote>.
      </para>

      <para>
	Une fonction utile, par exemple, pourrait prendre en charge l'affichage d'une chaîne. Voici donc notre <filename>coucou_asm.asm</filename> de la <xref linkend="qu-est-ce-que-c-est" /> un peu plus structuré<footnote><para>Compilez avec <userinput>nasm -f elf coucou_asm.asm ; ld -s coucou_asm.o -o coucou_asm</userinput></para></footnote>: 
      </para>
      
      <para>Exemple <filename>coucou_func_asm.asm</filename></para>
      <para>
	<programlisting>
	section .text
	        global _start
               
	_start:
	        push dword msg_len
	        push dword msg
	        call write_screen
	        add esp,8
       
	        mov eax,1
	        int 80h

	write_screen:
	        push ebp
	        mov ebp,esp

	        mov eax,4
	        mov ebx,1
	        mov ecx,[ebp + 8]
	        mov edx,[ebp + 12]
	        int 80h
	
	        mov esp,ebp
	        pop ebp
	        ret
	       
	section .data
	        msg db "coucou",0x0A
	        msg_len equ $ - msg
	</programlisting>
      </para>

      <para>
	Ca n'allonge notre code que de 68 petits octets, et c'est tout de même plus propre (si l'on doit faire un programme plus utile évidemment qui aura recours plus d'une fois à cette fonction).
      </para>

      <para>
	C'est la façon la plus propre de coder une fonction en assembleur. Le <emphasis>prologue</emphasis> : 

	<programlisting>
	  push ebp
	  mov ebp,esp
	</programlisting>

	et l'<emphasis>épilogue</emphasis>:

	<programlisting>
	  mov esp,ebp
	  pop ebp
	</programlisting>

	permettent à notre fonction de créer son propre espace sur la pile pour le cas ou nous aurions à nous en servir. C'est assez simple à comprendre. Nous avons vu dans le <xref linkend="tabregoffset" /> qu'il existe un registre qui contient en permanence l'offset du sommet de la pile : <varname>ebp</varname> . Donc notre programme, pour savoir ou commence la pile, se fie toujours à ce registre. Ce que nous faisons à l'entrée de notre fonction permet de déplacer la base du nouvel espace de notre pile à la fin de la pile effectivement utilisée par le programme appelant, en nous fiant à <varname>esp</varname> qui pointe la dernière valeur empilée par l'appelant. 
      </para>

      <para>
	Pour récupérer chacun des arguments passés à notre fonction, on utilise l'adressage indirect, relatif à la base de notre nouvelle pile, en remontant pour aller les chercher sur le sommet de la pile du programme appelant. On voit donc que le dernier argument empilé par l'appelant sera accessible via le plus petit déplacement (ici, 8).
      </para>
      
      <para>
	La question qui se pose est : pourquoi 8 ? En fait, l'instruction <literal>call</literal> à demandé un petit travail supplémentaire au processeur : l'empilement discret du contenu du registre <varname>eip</varname>, qui contient l'offset d'exécution de l'appelant au moment de son appel. <literal>call</literal> va donc empiler la valeur d'<varname>eip</varname> et <literal>ret</literal> s'en servira pour sauter vers cet offset une fois notre fonction terminée. 
      </para>
      
      <para>
	Après avoir poussé nos deux arguments, notre pile ressemble donc à : 
      </para>
      <table><title>Etat de la pile après empilement des arguments</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Registres</entry>
	      <entry>Pile</entry>
	      <entry>Offset factice (en décimal)</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>ebp-></literal></entry>
	      <entry><varname>msg</varname></entry>
	      <entry><literal>255</literal></entry>
	    </row>
	    <row>
	      <entry><varname>esp-></varname></entry>
	      <entry><varname>msg_len</varname></entry>
	      <entry><literal>251</literal></entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	Lorsque nous faisons appel à <literal>call</literal>, cette instruction modifie la pile comme suit : 
      </para>
      <table><title>Etat de la pile après instruction <literal>call</literal></title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Registres</entry>
	      <entry>Pile</entry>
	      <entry>Offset factice (en décimal)</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>ebp-></literal></entry>
	      <entry><varname>msg</varname></entry>
	      <entry><literal>255</literal></entry>
	    </row>
	    <row>
	      <entry> </entry>
	      <entry><varname>msg_len</varname></entry>
	      <entry><literal>251</literal></entry>
	    </row>
	    <row>
	      <entry><varname>esp-></varname> </entry>
	      <entry><varname>eip</varname></entry>
	      <entry><literal>247</literal></entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	Ensuite, après la première ligne du prologue (<literal>push ebp</literal>) : 
      </para>
      <table><title>Etat de la pile après la première ligne du prologue</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Registres</entry>
	      <entry>Pile</entry>
	      <entry>Offset factice (en décimal)</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry><literal>ebp-></literal></entry>
	      <entry><varname>msg</varname></entry>
	      <entry><literal>255</literal></entry>
	    </row>
	    <row>
	      <entry> </entry>
	      <entry><varname>msg_len</varname></entry>
	      <entry><literal>251</literal></entry>
	    </row>
	    <row>
	      <entry><varname> </varname> </entry>
	      <entry><varname>eip</varname></entry>
	      <entry><literal>247</literal></entry>
	    </row>
	    <row>
	      <entry><varname>esp-> </varname> </entry>
	      <entry><varname>ebp</varname></entry>
	      <entry><literal>243</literal></entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	et après la seconde ligne du prologue (<literal>mov ebp,esp</literal>) : 
      </para>
      <table><title>Etat de la pile après la seconde ligne du prologue</title>
	<tgroup cols="3" align="center" colsep="1" rowsep="1">
	  <thead>
	    <row>
	      <entry>Registres</entry>
	      <entry>Pile</entry>
	      <entry>Offset factice (en décimal)</entry>
	    </row>
	  </thead>
	  <tbody>
	    <row>
	      <entry> </entry>
	      <entry><varname>msg</varname></entry>
	      <entry><literal>255</literal></entry>
	    </row>
	    <row>
	      <entry> </entry>
	      <entry><varname>msg_len</varname></entry>
	      <entry><literal>251</literal></entry>
	    </row>
	    <row>
	      <entry><varname> </varname> </entry>
	      <entry><varname>eip</varname></entry>
	      <entry><literal>247</literal></entry>
	    </row>
	    <row>
	      <entry><varname>ebp = esp-> </varname> </entry>
	      <entry><varname>ebp</varname></entry>
	      <entry><literal>243</literal></entry>
	    </row>
	  </tbody>
	</tgroup>
      </table>

      <para>
	On sauvegarde donc <varname>ebp</varname> pour pouvoir retrouver la base de notre ancienne pile dans le programme appelant, et on l'initialise avec <varname>esp</varname> après lequel nous pouvons empiler tout ce que l'on voudra :-)
      </para>

      <para>
	C'est une bonne habitude à prendre, et puis cela permet également de faire des fonctions récursives qui ne crachent pas dès le premier retour d'imbrication... 
      </para>

      <para>
	On comprend à présent pourquoi il est nécessaire d'ajouter 8 octets à <varname>ebp</varname>  pour accéder à nos arguments : c'est qu'entre le début de notre nouvelle pile et la fin de celle de l'appelant, le processeur à stocké l'adresse de retour. 
      </para>

      <para>
	Il faudra bien évidemment penser à sauvegarder tous les registres que nous utiliserons au sein de notre fonction et à les restaurer avant l'épilogue. Il faut toujours avoir à l'esprit la restitution en l'état de la pile et des registres au programme appelant. 
      </para>

      <para>
	Pour les fonctions qui retournent une valeur, la convention veut que la valeur retournée le soit dans <varname>eax</varname><footnote><para>C'est en tout cas ainsi que procèdent les fonctions système Linux.</para></footnote>. 
      </para>

      <warning>
	<para>
	  Gardez bien à l'esprit tout de même que, s'il est vrai que les fonctions permettent une meilleure lisibilité et une meilleure maintenance du code, elle le ralentissent également<footnote><para>Les instructions <literal>call</literal>, <literal>ret</literal>, ainsi que toutes les opérations impliquant la pile sont très coûteuses en cycles processeur.</para></footnote>. Si c'est donc la plus grande rapidité possible que vous recherchez et que votre code n'est après tout pas si long que ca, n'hésitez pas à écrire le même code trois ou quatre fois au lieu de l'encapsuler dans une fonction. Le programme généré sera bien sûr plus long, mais aussi plus rapide. A vous de voir. 
	</para>
      </warning>

    </sect1>

    <sect1 id="les-macros">
      <title>Les macros</title>

      <para>
	Si vous préférez éviter les fonctions, vous pouvez toujours préserver une certaine lisibilité de votre code à l'aide des macros. 
      </para>

      <para>
	Contrairement aux fonctions, qui sont effectivement traduites comme telles dans le code binaire généré par le compilateur, les macros ne sont là que pour aider le programmeur à y voir plus clair, mais dans le code final on ne trouvera aucun appel. Leur code sera juste inséré à l'endroit des différentes références qui y auront été faites. 
      </para>

      <para>
	Notre programme <filename>coucou_func_asm.asm</filename> se transforme donc en : 
	</para>

      <para>Exemple <filename>coucou_func_asm.asm</filename></para>
      <para>
	<programlisting>
	%macro write_screen 2
	        mov eax,4
	        mov ebx,1
	        mov ecx,%1
	        mov edx,%2
	        int 80h
	%endmacro
	       
	section .text
	        global _start
	               
	_start:
	        write_screen msg,msg_len
	       
	        mov eax,1
	        int 80h
	       
	section .data
	        msg db "coucou",0x0A
	        msg_len equ $ - msg
	</programlisting>
      </para>

      <para>
	On voit que la définition d'une macro est toute simple. Il suffit juste de spécifier derrière son nom le nombre d'arguments qu'elle attend. Pour ce qui est de l'appel, on dirait presque un langage évolué :-) 
      </para>

      <para>
	Si vous regardez la taille du code généré, elle est presque équivalente à la taille de notre <filename>coucou_asm.asm</filename> de la <xref linkend="qu-est-ce-que-c-est" />, mais avec l'appel en plus. 
      </para>

    </sect1>
    
  </chapter>

  <bibliography id="bibliographie">
    <title>BIBLIOGRAPHIE</title>
    
    <bibliodiv><title>Livres</title>
      
      <biblioentry>
	<authorgroup>
	  <author><firstname>A.B.</firstname><surname>Fontaine</surname></author>
	</authorgroup>
	<copyright>
	  <year>1983</year><year>1984</year>
	  <holder>Masson, Paris</holder>
	</copyright>
	<editor><firstname></firstname><surname>Masson</surname></editor>
	<isbn>2-225-80313-7</isbn>
	<publisher>
	  <publishername>Masson, Paris</publishername>
	</publisher>
	<title>Le microprocesseur 16 bits 8086/8088, matériel - logiciel - système d'exploitation</title>
      </biblioentry>

      <biblioentry>
	<authorgroup>
	  <author><firstname>M.</firstname><surname>Margenstern</surname></author>
	</authorgroup>
	<copyright>
	  <year>1983</year><year>1991</year>
	  <holder>Masson, Paris</holder>
	</copyright>
	<editor><firstname></firstname><surname>Masson</surname></editor>
	<isbn>2-225-82500-9</isbn>
	<publisher>
	  <publishername>Masson, Paris</publishername>
	</publisher>
	<title>ASSEMBLAGE, modélisation, programmation (80x86)</title>
      </biblioentry>

      <biblioentry>
	<authorgroup>
	  <author><firstname>Jeff</firstname><surname>Duntemann</surname></author>
	</authorgroup>
	<copyright>
	  <year>1992</year><year>2000</year>
	  <holder>Jeff Duntemann</holder>
	</copyright>
	<editor><firstname></firstname><surname>WILEY</surname></editor>
	<isbn>2-225-80313-7</isbn>
	<publisher>
	  <publishername>John Wiley &amp; Sons, Inc</publishername>
	</publisher>
	<title>Assembly Language Step-by-Step, Second Edition, Programming with DOS and Linux</title>
      </biblioentry>

      <biblioentry>
	<authorgroup>
	  <author><firstname>Holger</firstname><surname>Schakel</surname></author>
	</authorgroup>
	<copyright>
	  <year>1992</year> <year>1993</year>
	  <holder>DATA Becker GmbH</holder>
	  <holder>Micro Application</holder>
	</copyright>
	<editor><firstname></firstname><surname>Micro Application</surname></editor>
	<isbn>2-86899-796-1</isbn>
	<publisher>
	  <publishername>Micro Application</publishername>
	</publisher>
	<title>Programmer en Assembleur sur PC</title>
      </biblioentry>

      <biblioentry>
	<authorgroup>
	  <author><firstname>Philippe</firstname><surname>Mercier</surname></author>
	</authorgroup>
	<copyright>
	  <year>1989</year>
	  <holder>Marabout</holder>
	</copyright>
	<editor><firstname></firstname><surname>Marabout</surname></editor>
	<isbn>2-501-01176-1</isbn>
	<publisher>
	  <publishername>Micro Application</publishername>
	</publisher>
	<title>Programmer en Assembleur sur PC</title>
      </biblioentry>
   
    </bibliodiv> 
  </bibliography>
  
</book>
