Académique Documents
Professionnel Documents
Culture Documents
Apprendre l'assembleur
(INTEL - DOS 16 bits)
Par Benot-M
Dsormais, pour apprendre le franais, il faudra SAVOIR le franais ! disait Coluche. De mme, ce tutoriel s'adresse tous ceux qui connaissent dj les rudiments dun langage volu tel que le BASIC, le FORTRAN, le C ou le PASCAL et qui souhaitent apprendre l'assembleur. Une connaissance mme sommaire dun de ces langages suffit ! Aucune connaissance en programmation systme nest requise : le but de ce cours est justement den introduire les fondements. Contrairement aux langages volus, l'assembleur, ou langage dassemblage est constitu d'instructions directement comprhensibles par le microprocesseur : c'est ce qu'on appelle un langage de bas niveau. Il est donc intimement li au fonctionnement de la machine. C'est pourquoi il est relativement difficile assimiler, en tout cas beaucoup plus que les langages de haut niveau. Cela explique galement pourquoi il existe au moins autant de langages dassemblage que de modles de microprocesseurs. Avant d'apprendre lassembleur INTEL 80x86, il est donc primordial de sintresser quelques notions de base concernant par exemple la mmoire ou le microprocesseur. Cest l en effet que se trouvent les principales difficults pour le dbutant. Ne soyez pas rebut par labstraction des concepts prsents dans les premiers paragraphes : il est normal que durant la lecture, beaucoup de choses ne soient pas claires dans votre esprit. Tout vous semblera beaucoup plus simple quand nous passerons la pratique dans le langage assembleur. Le microprocesseur peut fonctionner sous deux modes : le mode rel et le mode protg. Le mode protg permet daccder 232 octets de mmoire vive, alors que le mode rel ne peut en adresser que 220 = 1 Mo. Nous ne traiterons dans ce cours que le mode rel. Cest celui quutilisent la plupart des programmes DOS. Convention : toutes les adresses sont crites en notation hexadcimale. Les autres nombres seront la plupart du temps reprsents en base dcimale. Dans le cas contraire, nous ajouterons la lettre h aprs les chiffres.
PREMIERE PARTIE
NOTIONS DE BASE SUR LE FONCTIONNEMENT DE L'ORDINATEUR
I. LARITHMETIQUE SIGNEE
On appelle arithmtique non signe larithmtique dans laquelle tous les entiers sont positifs. En arithmtique signe au contraire, les nombres peuvent tre soit positifs, soit ngatifs. Un nombre sign nest donc pas forcment ngatif. Les donnes informatiques se prsentent sous la forme dune succession de chiffres binaires, les bits. Nous supposerons que les systmes de numration binaire et hexadcimal vous sont dj familiers. Il est en revanche fort possible que vous ne connaissiez pas la faon dont un ordinateur reprsente les nombres ngatifs. Il existe deux conventions : la notation en signe et valeur absolue et la notation en complment 2. La premire est extrmement simple : le premier bit reprsente le signe, et les autres bits la valeur absolue du nombre. Le bit de signe vaut 1 si le nombre est strictement ngatif, 0 sinon. Par exemple, le nombre (14) cod sur 8 bits scrit ainsi : 10001110 Cette convention nest quasiment jamais utilise en informatique. On lui prfre la reprsentation en complment 2 dont le principe est le suivant : - les nombres positifs sont cods de la mme faon quen convention signe et valeur absolue . - les nombres ngatifs sont obtenus en inversant tous les bits, puis en ajoutant 1. Exemple : le nombre 14 cod sur 8 bits est reprsent ainsi : 00001110 et (14) ainsi : inversion des bits : 11110001 ajout dune unit : 11110010 rsultat : 11110010 Remarque : le rsultat intermdiaire, 11110001, est appel complment 1 . Vous allez immdiatement comprendre lavantage de cette reprsentation. Faisons la somme de 14 et de (14), de la mme faon que sil sagissait dentiers positifs :
00001110 + 11110010 = 100000000 Le rsultat tant cod sur 8 bits, le 1 situ gauche nest pas pris en compte. On obtient donc 14 + (-14) = 0. Lintrt vident est que la diffrence de deux nombres peut se calculer avec le mme algorithme que leur somme. Il suffit de transformer au pralable le nombre retranch en son oppos. Cette conversion est trs simple et trs rapide. Au contraire, en reprsentation signe et valeur absolue , on aurait eu besoin de nombreux algorithmes, car plusieurs cas se prsentent. Remarque : la reprsentation en complment 2 revient en fait crire (-1) comme ceci : 11111111 (-2) comme cela : 11111110 (-3) comme cela : 11111101 etc Il y a ainsi une symtrie entre les nombres positifs et les nombres ngatifs. Il en rsulte que le bit le plus gauche reprsente le signe, de la mme faon quen notation signe et valeur absolue . Avant tout calcul, nous pouvons donc affirmer que le nombre 10010101 est ngatif.
Voici la solution : si l'adresse de l'octet est A17C:022E, alors son adresse effective est A17C x 16 + 022E, soit A17C0 + 022E = A19EE. On a multipli par 16, car le segment A17C dbute loctet A17C x 16, puis on a simplement ajout le dcalage. Au final, on a bien une adresse sur 20 bits puisqu'on obtient 5 chiffres hexa. Chaque petit bloc de 16 octets sappelle un paragraphe.
Remarque importante : Il est souvent plus simple de considrer quun segment est un bloc de taille quelconque qui dbute une adresse effective multiple de 16 et qui permet, laide de son adresse de segment et dun offset, dadresser le bloc entier (64 Ko au maximum). Cette dfinition est notre avis celle qui a le plus de sens, et nous l'utiliserons tout au long de ce cours.
Mais quest-ce donc quun PSP ? Pour simplifier, le PSP ( Program Segment Prefix ) est une zone de 256 (= 100h) octets qui contient des informations diverses au sujet du programme. Cest dans le PSP que se trouve la ligne de commande tape par lutilisateur. Par exemple, le PSP dun programme appel MONPROG, excut avec la commande MONPROG monfic.txt /S /H, contiendra la chane de caractres suivante : monfic.txt /S /H. Le programmeur a ainsi la possibilit daccder aux paramtres. Voici titre indicatif la structure simplifie du PSP (ne vous souciez pas de ce que vous ne comprenez pas : pour l'instant, seules les deux dernires lignes nous intressent vraiment) : Offset 00h 02h 04h 05h 0Ah 0Eh 12h 16h 2Ch 2Eh 80h 81h Description Appel de l'int 20h Adresse du 1er segment qui se trouve au del du prog. Rserv Far call de l'int 21h (inutilis) Vecteur de l'int 22h Vecteur de l'int 23h Vecteur de l'int 24h Rserv Segment du bloc d'environnement Rserv Nombre de caractres dans la ligne de commande sans compter le code ASCII 13 (retour chariot) Ligne de commande ( partir du caractre espace qui suit le nom du programme) + code ASCII 13 Taille (octets) 2 2 1 5 4 4 4 22 2 82 1 127
A prsent que nous connaissons lexistence du PSP, il nous faut revenir sur un point important. Comme nous lavons dit, un programme COM ne peut comporter quun seul segment, bien que le DOS lui rserve la totalit de la mmoire disponible. Ceci a deux consquences. La premire est que les adresses de segment sont inutiles dans le programme : les offsets seuls permettent dadresser nimporte quel octet du segment. La seconde est que le PSP fait partie de ce segment, ce qui limite 64 Ko 256 octets la taille maximale dun fichier COM. Cela implique galement que le programme lui-mme dbute loffset 100h et non loffset 0h.
Afin que le programme puisse tre charg et excut correctement, il faut que le systme sache o commence et o sarrte chacun de ces segments. A cet effet, les compilateurs crent un en-tte (ou header ) au dbut de chaque fichier EXE. Ce header ne sera pas copi en mmoire. Son rle est simplement dindiquer au DOS (lors du chargement) la position relative de chaque segment dans le fichier.
Le registre AX sert souvent de registre d'entre-sortie : on lui donne des paramtres avant d'appeler une fonction ou une procdure. Il est galement utilis pour de nombreuses oprations arithmtiques, telles que la multiplication ou la division de nombres entiers. Il est appel accumulateur . Exemples d'utilisation : linstruction MOV AX, 1982 place lentier 1982 dans AX. ADD AX, 1983 ajoute AX le nombre 1983 et place le rsultat dans AX. Le registre BX peut servir de base. Nous verrons plus tard ce que ce terme signifie.
Le registre CX est utilis comme compteur dans les boucles. Par exemple, pour rpter 15 fois une instruction en assembleur, on peut mettre la valeur 15 dans CX, crire l'instruction prcde d'une tiquette qui reprsente son adresse en mmoire, puis faire un LOOP cette adresse. Lorsqu'il reconnat l'instruction LOOP, le processeur sait que le nombre d'itrations excuter se trouve dans CX. Il se contente alors de dcrmenter CX, de vrifier que CX est diffrent de 0 puis de faire un saut ( jump ) ltiquette mentionne. Si CX vaut 0, le processeur ne fait pas de saut et passe linstruction suivante. Exemple :
Le registre DX contient souvent l'adresse d'un tampon de donnes lorsqu'on appelle une fonction du DOS. Par exemple, pour crire une chane de caractres l'cran, il faut placer loffset de cette chane dans DX avant d'appeler la fonction approprie. Chacun de ces quatre registres comporte 16 bits. On peut donc y stocker des nombres allant de 0 65535 en arithmtique non signe, et des nombres allant de 32768 32767 en arithmtique signe. Les 8 bits de poids fort dun registre sont appels partie haute et les 8 autres partie basse . Chaque registre est en fait constitu de deux sous-registres de 8 bits. La partie haute de AX sappelle AH, sa partie basse AL. Il en va de mme pour les trois autres. En arithmtique non signe, il en dcoule la formule suivante : AX = AH x 256 + AL ainsi que leurs homologues : BX = BH x 256 + BL CX = CH x 256 + CL DX = DH x 256 + DL Il va de soi quen modifiant AL ou AH, on modifie galement AX. Pour stocker un nombre de 32 bits, on peut utiliser des paires de registres. Par exemple, DX:AX signifie DX x 65535 + AX en arithmtique non signe. Cette notation (DX:AX) nest pas reconnue par lassembleur (ni par la machine). Ne confondez pas cela avec les adresses de segment et doffset.
Sur un PC relativement rcent, AX, BX, CX, DX ne sont en fait que les parties basses de registres de 32 bits nomms EAX, EBX, ECX, EDX ( E pour Extended ). On a donc un moyen plus pratique de stocker les grands nombres. En fait, chaque registre (pas seulement les registres gnraux) peut contenir 32 bits.
Contrairement aux registres gnraux, ces registres ne peuvent servir pour les oprations courantes : ils ont un rle trs prcis. On ne peut dailleurs pas les utiliser aussi facilement que AX ou BX, et une petite modification de lun deux peut suffire planter le systme. Eh oui ! Lassembleur, ce nest pas Turbo Pascal ! Il ny a aucune barrire de protection, si bien quune petite erreur peut planter le DOS. Mais rassurez-vous : tout se rpare trs bien en redmarrant lordinateur Dans le registre CS est stocke ladresse de segment de la prochaine instruction excuter. La raison pour laquelle il ne faut surtout pas changer sa valeur directement est vidente. De toute faon, vous ne le pouvez pas. Le seul moyen viable de le faire est dutiliser des instructions telles que des sauts (JMP) ou des appels (CALL) vers un autre segment. CS sera alors automatiquement actualis par le processeur en fonction de ladresse darrive. Le registre DS est quant lui destin contenir ladresse du segment des donnes du programme en cours. On peut le faire varier condition de savoir exactement pourquoi on le fait. Par exemple, on peut avoir deux segments de donnes dans son programme et vouloir accder au deuxime. Il faudra alors faire pointer DS vers ce segment. ES est un registre qui sert adresser le segment de son choix. On peut le changer aux mmes conditions que DS. Par exemple, si on veut copier des donnes dun segment vers un autre, on pourra faire pointer DS vers le premier et ES vers le second. Le registre SS adresse le segment de pile. Il est rare quon doive y toucher car le programme na quune seule pile. Intressons-nous prsent aux valeurs que le DOS donne ces registres lors du chargement en mmoire dun fichier excutable ! Elles diffrent selon que le fichier est un programme COM ou EXE. Pour crire un programme en assembleur, il est ncessaire de connatre ce tableau par cur : Registre CS DS ES Valeur avant lexcution Fichier COM Fichier EXE Adresse de lunique segment, cest Adresse du segment de code dire adresse de segment du PSP Adresse de lunique segment, cest Adresse de segment du PSP dire adresse de segment du PSP Adresse de lunique segment, cest Adresse de segment du PSP
SS
dire adresse de segment du PSP Adresse de lunique segment, cest Adresse du segment de pile dire adresse de segment du PSP
Dans un fichier EXE, le header indique au DOS les adresses initiales de chaque segment par rapport au dbut du programme (puisque le compilateur n'a aucun moyen de connatre l'adresse laquelle le programme sera charg). Lors du chargement, le DOS ajoutera ces valeurs l'adresse d'implantation pour obtenir ainsi les vritables adresses de segment. Dans le cas d'un fichier COM, tout est plus simple. Le programme ne comporte qu'un seul segment, donc il suffit tout btement au DOS de charger CS, DS, ES et SS avec l'adresse d'implantation. Remarque : Pourquoi DS et ES pointent-ils vers le PSP dans le cas dun fichier EXE ? Premire raison : pour que le programmeur puisse accder au PSP ! Deuxime raison : parce quun programme EXE peut comporter un nombre quelconque de segments de donnes. Cest donc au programmeur dinitialiser ces registres, sil veut accder ses donnes.
BP
Base pointer
Pointeur de base
Le registre IP dsigne loffset de la prochaine instruction excuter, par rapport au segment adress par CS. La combinaison de ces deux registres (i.e. CS:IP) suffit donc connatre ladresse absolue de cette instruction. Le processeur peut alors aller la chercher en mmoire et lexcuter. De plus, il actualise IP en lincrmentant de la taille de linstruction en octets. Tout comme CS, il est impossible de modifier IP directement. Le registre SP dsigne le sommet de la pile. Il faut bien comprendre le fonctionnement de la pile, aussi allons-nous insister sur ce point. La pile ne peut stocker que des mots. On appelle un mot ( word en anglais) un nombre cod sur deux octets (soit 16 bits). Prenons un exemple simple : un programme COM. Le segment de pile (adress par SS) et le segment de code ne font quun. Avant lexcution, SP vaut FFFE. Cest loffset de la dernire donne de 16 bits empile, par rapport SS bien sr. Pourquoi FFFE ? Tout simplement parce que la pile se remplit lenvers, cest--dire en partant de la fin du segment et en remontant vers le dbut. Le premier mot empil se trouve loffset FFFE. Il tient sur deux octets : loctet FFFE et loctet FFFF. Mais comment se fait-il quun mot soit dj empil avant le dbut du programme ? Ce mot est un zro que le DOS place sur la pile avant lexcution de tout programme COM. Nous en verrons la raison plus tard. A prsent, que se passe-t-il si un instant quelconque une instruction ordonne au processeur dempiler un mot ? Eh bien le stack pointer sera dcrment de 2 et le mot sera copi lendroit point par SP. Rappelez-vous que la pile se remplit lenvers ! Cest pour cette raison que SP est dcrment chaque empilage et non pas incrment. Un petit exemple pour rendre les choses plus concrtes : PUSH AX Leffet de cette instruction est dempiler le mot contenu dans le registre AX. Autrement dit, SP est automatiquement dcrment de 2, puis AX est copi ladresse SS:SP. Lors du dpilage, le mot situ au sommet de la pile, cest--dire le mot adress par SS:SP, est transfr dans un registre quelconque choisi par le programmeur, aprs quoi le stack pointer est incrment de 2. Exemple : POP BX Cette fois, on retire le dernier mot empil pour le placer dans le registre BX. Evidemment, SP sera incrment de 2 aussitt aprs. La pile est extrmement utile lorsquil sagit de stocker provisoirement le contenu dun registre qui doit tre modifi. Exemple :
Il est important de comprendre quon ne peut dpiler que le mot qui se trouve au sommet de la pile. Le premier mot empil est le dernier qui sera dpil. La pile doit tre manipule avec une extrme prcaution. Un dpilage injustifi fait planter la machine presque systmatiquement. Les trois derniers registres sont beaucoup moins lis au fonctionnement interne du processeur. Ils sont mis la disposition du programmeur qui peut les modifier sa guise et les utiliser comme des registres gnraux. Comme ces derniers cependant, ils ont une fonction qui leur est propre : servir dindex (SI et DI) ou de base (BP). Nous allons expliciter ces deux termes. Dans la mmoire, les octets se suivent et forment parfois des chanes de caractres. Pour utiliser une chane, le programmeur doit pouvoir accder facilement tous ses octets, lun aprs lautre. Or pour effectuer une opration quelconque sur un octet, il faut connatre son adresse. Cette adresse doit en gnral tre une constante valuable par le compilateur. Pourquoi une constante ? Parce que ladresse est une oprande comme les autres, elle se trouve immdiatement aprs lopcode et doit donc avoir une valeur numrique fixe ! Prenons un exemple : MOV AH, [MonOctet] Pas de panique ! Cette instruction en assembleur signifie Mettre dans AH la valeur de loctet adress par le label MonOctet ! . A la compilation, MonOctet sera remplac par la valeur numrique qu'il reprsente et on obiendra alors une instruction en langage machine telle que : 8A260601 8A26 est lopcode (hexa) de linstruction MOV AH, [constante quelconque], et 0601 est loffset de MonOctet. Il serait pourtant fastidieux, dans le cas dune chane de 112 caractres, de traiter les octets avec 112 instructions dans lesquelles seule ladresse changerait. Il faudrait pouvoir faire une boucle sur ladresse, mais alors celle-ci ne serait plus une constante, do le problme. Pour les constructeurs du microprocesseur, la seule solution tait de crer de nouveaux opcodes pour chaque opration portant sur un octet en mmoire. Ces opcodes spciaux feraient la mme action que ceux dont ils seraient drivs, mais ladresse passe en paramtre serait alors considre comme un dcalage par rapport un registre spcial. Il suffirait donc de faire varier ce registre, et le processeur y ajouterait automatiquement la valeur de loprande pour obtenir ladresse relle ! Cest cela que servent SI, DI et BP. Par exemple :
MOV AH, [MonOctet + DI] sera cod : 8AA50601 8AA5 est lopcode pour linstruction MOV AH, [constante quelconque + DI]. Remarque : les registres SI et BP auraient tout aussi bien pu tre employs, mais pas les registres gnraux, SAUF BX. En effet, BX peut jouer exactement le mme rle que BP. Noubliez pas que BX est appel registre de base , et que BP signifie Base Pointer. Nous verrons la diffrence entre une base et un index lorsque nous commencerons lassembleur.
DF ( Direction Flag ) est utilis pour les oprations sur les chanes de caractres. Sil vaut 1, celles-ci seront parcourues dans le sens des adresses dcroissantes, sinon les adresses seront croissantes. OF ( Overflow Flag ) indique quun dbordement sest produit, cest--dire que la capacit de stockage a t dpasse. Il est utile en arithmtique signe. Avec des nombres non signs, il faut utiliser ZF et SF. Remarque : Les notations CF, PF, AF, etc ne sont pas reconnues par lassembleur. Pour utiliser les flags, il existe des instructions spcifiques que nous dcrirons plus tard.
Comment sont-elles dclenches ? Une interruption peut tre dclenche par votre matriel. Cest ce qui arrive lorsque vous appuyez sur une touche du clavier. Aucun logiciel nintervient et le contrle est pass directement la routine qui gre le clavier : ce sont les interruptions matrielles. Les interruptions logicielles sont quant elles appeles par des instructions en langage machine au sein dun programme. Leur importance est capitale. Rappelez-vous que contrairement au PASCAL ou au C, lassembleur ne dispose pas de fonction prprogramme. Chaque instruction doit tre directement traduisible en langage machine. Mais alors, comment fait-on pour crire une chane de caractres lcran ? Ou bien pour lire un caractre entr au clavier ? Eh bien on le fait de la mme faon que le DOS lui-mme ! On dclenche les interruptions appropries laide de linstruction INT du langage machine. Cest donc une routine du DOS (ou parfois du BIOS) qui fera tout le travail. Les paramtres (ou leurs adresses) sont passs dans les registres. Voici un petit exemple en assembleur qui crit la lettre A lcran :
Examinons-le ligne par ligne : linstruction MOV DL, A demande au processeur de mettre dans le registre DL le code ASCII de la lettre A, cest--dire 65, ou 41h. MOV AH, 02 : mettre le nombre 2 dans AH. Enfin, la dernire instruction appelle linterruption numro 21h. Il existe 256 interruptions. Toutes sont notes en base hexadcimale. Explications : Comme vous aurez trs vite loccasion de vous en rendre compte, linterruption 21h est linterruption du DOS par excellence. Elle permet dappeler de nombreuses fonctions trs diverses. Pour cela, il suffit de mentionner leur numro dans le registre AH. Il est trs difficile de mmoriser le rle de chaque interruption, et a fortiori de chaque fonction ou sous-fonction, dautant plus delles sont dsignes par des numros hexadcimaux et quelles attendent des paramtres dans des registres prcis. Cest pourquoi tout programmeur se doit davoir sa disposition une liste des interruptions pour travailler. Celle de Ralph Brown est trs connue, et vous la trouverez sur lInternet. Revenons notre exemple. La fonction numro 2 de linterruption 21h sert crire un caractre lcran. Il faut pour cela crire le code ASCII du caractre dans le registre DL et bien sr placer le nombre 2 dans AH. Une fois que AH et DL ont t ajusts, linterruption 21h peut tre appele laide de linstruction INT. Dautres interruptions ne remplissent quune seule tche. Vous navez donc pas besoin de mettre un numro de fonction dans AH. Lappel de linterruption suffit.
2 2 2 2 2 2
Adresse doffset de linterruption numro 1 Adresse de segment de linterruption numro 1 Adresse doffset de linterruption numro 2 Adresse de segment de linterruption numro 2 Adresse doffset de linterruption numro 3 Adresse de segment de linterruption numro 3 etc etc etc Adresse doffset de linterruption numro 255 Adresse de segment de linterruption numro 255
0000:03FC 0000:03FE
2 2
La raison pour laquelle les offsets prcdent les adresses de segment vient du codage en little endian utilis par INTEL. Les processeurs de cette marque (contrairement la plupart des autres, qui travaillent en big endian ) ont une reprsentation des donnes en mmoire aussi curieuse quinsupportable pour le programmeur : ils placent le poids fort aprs le poids faible. Ainsi, le mot 4A28h sera cod 284Ah. Puisque ladresse de lISR tient sur 32 bits (16 + 16), elle est reprsente comme un double mot ( dword ), donc ladresse doffset, qui est le mot de poids faible, se trouve au dbut. Eh oui, cest pnible, mais il faudra sy faire ! Remarque : pour calculer ladresse de lentre dans la TVI correspondant linterruption numro X, il suffit de multiplier X par 4. Exemple : ladresse de lISR numro 21h est stocke 0000:0084.
DEUXIEME PARTIE
PREMIER CONTACT AVEC LE LANGAGE ASSEMBLEUR
Cette partie a pour but de vous prsenter larchitecture du langage assembleur travers de courts exemples. Ne vous inquitez pas si vous ne comprenez pas parfaitement certaines instructions : toutes seront dtailles dans la troisime partie. Lessentiel est que vous compreniez grossirement ce quon fait et pourquoi on le fait. Les explications qui accompagnent ces exemples devraient y suffire. Vous pourrez ensuite apprendre le langage en lui-mme en consultant la partie suivante.
Remarques pratiques prliminaires : Les mots qui sont imprims en italique sont ceux qui ne font pas partie du langage en luimme. Ils sont choisis par le programmeur. Tous les programmes prsents ci-dessous suivent la syntaxe du compilateur TASM. Il existe de trs lgres diffrences dun compilateur lautre. Le code peut tre crit en majuscules ou en minuscules. Chaque mot est spar des autres par des espaces ou des tabulations. Les commentaires sont prcds du signe ;. Les nombres doivent toujours commencer par un chiffre (entre 0 et 9), mme les nombres hexadcimaux. Il faut donc crire 0F2Ah et non pas F2Ah. Par contre, lcriture 5CFh est correcte. Les apostrophes et les guillemets sont quivalents. Il est cependant plus commode de rserver lusage des guillemets aux chanes de plusieurs caractres et celui des apostrophes aux caractres isols. Vous pouvez taper vos programmes avec lditeur de texte du DOS (EDIT.COM). Donnezleur lextension .asm. La compilation se fait en tapant TASM /m9 MONPROG si votre source sappelle MONPROG.ASM. Le paramtre /m9, facultatif, indique au compilateur quil devra effectuer 9 passes, cest--dire quil examinera 9 fois le code source. Comme chaque examen se fait de manire linaire, le compilateur trouve parfois des instructions qui font rfrence des labels placs plus loin dans le programme. Il manque donc dinformations, mais il continue son examen jusqu la fin. Quand il a termin, il recommence tout depuis le dbut pour rsoudre les problmes qui staient poss. La compilation cre un fichier objet (.obj). Pour obtenir un fichier EXE, tapez TLINK MONPROG. Pour un fichier COM, tapez TLINK /tdc MONPROG. Aprs ldition des liens (le linkage ), vous pouvez supprimer les fichiers
MONPROG.obj et MONPROG.map. Il est possible de crer un fichier BAT qui soccupe de toutes ces tapes. Ouvrez lditeur EDIT du DOS et tapez un programme tel que celui-ci : @ECHO OFF TASM /m9 %1.asm IF NOT EXIST %1.obj GOTO FIN TLINK %1.obj REM : ajouter ici /tdc pour obtenir un fichier COM ERASE %1.map ERASE %1.obj :FIN Enregistrez-le et nommez-le MAKE.BAT. Vous pouvez compiler et linker en tapant MAKE MONPROG. Pour compiler des fichiers COM, ajoutez le paramtre '/tdc' la commande TLINK. Pour pouvoir lancer TASM et TLINK quel que soit le dossier courant, changez la variable PATH ainsi : PATH = %path%;c:\MonChemin\DossierTASM Vous pouvez inclure cette ligne la fin de votre fichier AUTOEXEC.BAT afin quelle soit excute chaque dmarrage. Vous pouvez dbugger vos programmes (par exemple les excuter instruction par instruction en observant les changements induits dans la RAM et dans les registres,...) avec le logiciel Turbo Debugger 16/32 bits qui est trs pratique et trs performant. A dfaut, vous pouvez vous rabattre sur l'archaque DEBUG.COM (il se trouve dans votre dossier de commandes DOS). Mais alors bon courage ! Le seul travail du compilateur (et du linkeur) est de convertir chacune de vos instructions en son quivalent en langage machine. Le programme compil prsentera donc exactement la mme structure et la mme linarit que votre code source.
Ce code source commence par des directives. Une directive est une information que le programmeur fournit au compilateur. Elle nest pas transforme en une instruction en langage machine. Elle najoute donc aucun octet au programme compil. La directive .386 indique au compilateur que le programme est destin tourner sur des processeurs INTEL de modle 386 (ou suprieur). Cela nous autorise utiliser certaines instructions qui ne sont pas disponibles sur les modles antrieurs, comme PUSHA ou POPA. Dans cet exemple, cette directive aurait trs bien pu tre omise. La ligne code segment use16 sert dclarer un segment que lon appelle code. On aurait tout aussi bien pu le nommer marteau ou voiture. Ce sera le segment de notre programme. Noubliez pas quun fichier COM ne peut comporter quun seul segment. Cette ligne ne sera pas compile : elle ne sert qu indiquer au compilateur le dbut dun segment. Le mot use16 indique que les adresses de segment et doffset sont codes sur 16 bits et non sur 8 bits. Vous devez systmatiquement lcrire. La directive assume cs:code, ds:code, ss:code
informe le compilateur que tout au long du programme, CS, DS et SS pointeront de faon privilgie vers le segment code, ce qui est vident dailleurs puisque cest le seul segment Vous comprendrez le sens exact de assume dans la troisime partie du cours. Enfin, les mots org 100h signifient quil faudra ajouter 100h (soit 256) tous les offsets. Pourquoi ? Souvenez-vous de la structure dun programme COM en mmoire. Si vous ncrivez pas cette ligne, TASM considrera que le programme dbute loffset 0000. Or, lors de lexcution, le DOS le chargera aprs le PSP, cest--dire ladresse 100h. Cest pourquoi il est ncessaire de recalculer les offsets : loffset 0000 deviendra 0100. Cette directive est en quelque sorte le trait caractristique des fichiers COM. Nous en arrivons au programme proprement dit. Il commence par un label : debut : Lui non plus nest pas compil. Il ne sert qu reprsenter ladresse de linstruction qui le suit, cest--dire : mov ah, 09h Cette ligne demande au processeur de charger la valeur 9 dans le registre AH. Cest le numro de la fonction de linterruption 21h qui crit une chane de caractres lcran. Loffset de cette chane est attendu dans DX. Do la ligne suivante : mov dx, offset message Le mot-cl offset sert extraire loffset du label message qui reprsente quant lui ladresse du message crire (il contient donc une adresse de segment ET un offset). Ladresse de segment de la chane doit tre transmise dans DS. Mais il est inutile de changer ce registre, car il pointe dj vers notre segment. Il nous reste appeler linterruption 21h : int 21h et rendre la main au DOS : ret Lorsque nous aborderons les procdures, vous comprendrez mieux le sens de ce mot et ce quil fait exactement. Pour linstant, sachez simplement que seul un fichier COM peut se terminer avec cette instruction. Nous arrivons la ligne : message db Bonjour, monde !, $ Il sagit dune dfinition de donnes. Le mot db ( define byte ) signifie que le compilateur devra crire les octets qui suivent tels quils sont dans notre code source. Il va donc crire le code ASCII du B, puis celui du o, etc Il terminera en crivant le code ASCII du signe $.
Cest ainsi que la fonction 9 de linterruption 21h reconnat la fin de la chane crire. Si vous oubliez ce signe, elle crira tous les octets de la RAM jusqu ce quelle tombe par hasard sur lui. Le mot message plac en dbut de ligne est un label de donnes. Il reprsente ladresse du code ASCII du B. Remarquez quil ny a pas de caractre : aprs un label de donnes. La ligne code ends indique la fin du segment code, et enfin end debut informe le compilateur que le fichier est fini, tout comme le END. du PASCAL. Le nom du label debut est mentionn : ce sera le point dentre de notre programme. Cest vers lui que pointera CS:IP avant lexcution. Remarque : vous ntes pas tenu de rendre aux registres la valeur quils avaient au dbut de votre programme. De toute faon, avant de charger un programme, le DOS sauvegarde le contenu de tous les registres puis met le contenu des registres gnraux (ainsi que SI, DI et BP) zro. Il les restaurera quand vous lui rendrez la main.
a disparu. En effet, lors de lexcution, CS pointera vers notre segment de code et non pas vers le PSP. Il est donc inutile de dcaler les offsets de 256. Remarquons que notre programme dispose de deux segments supplmentaires : Le segment data est destin contenir les donnes, cest--dire les variables. Le segment pile sera notre segment de pile. Notre directive assume devient donc : assume cs:code, ds:data, ss:pile Ainsi, le compilateur est inform que DS pointera vers le segment data et SS vers pile. Les deux lignes suivantes, mov ax, data mov ds, ax servent initialiser le registre DS. Celui-ci pointe vers le PSP au dbut du programme mais nous voulons le faire pointer vers notre segment de donnes appel data. Cela est ncessaire puisque la fonction 9 de linterruption 21h attend ladresse de la chane dans le couple DS:DX et que notre message se trouve dans le segment de donnes. La premire instruction charge ladresse du segment data dans AX. La seconde transfre cette valeur de AX dans DS. Mais pourquoi diable utiliser AX comme intermdiaire ? Aprs tout, on pourrait crire : mov ds, data Eh bien non ! Pour la simple raison que DS est un registre de segment et quen tant que tel on ne peut pas lui charger de valeur immdiate. On appelle valeur immdiate toute constante tape directement dans linstruction ellemme. Exemples de chargement de valeurs immdiates : mov ax, 135 ;charge 135 dans AX mov bx, offset message ;charge loffset de message dans BX mov bx, offset fin offset debut ;charge le nombre doctets entre fin et debut dans BX mov es, 10 ;instruction illicite car ES est un registre de segment ! Remarque : une autre possibilit aurait t d'crire : PUSH AX (empiler AX) puis POP DS (dpiler le dernier nombre empil et le placer dans DS). Les trois lignes suivantes :
mov ah, 09h mov dx, offset message int 21h nont pas chang. Il nous faut galement terminer le programme par un appel de la fonction 4ch de linterruption 21h. Cest ainsi que se terminent les programmes EXE. mov ah, 4ch int 21h Remarque : on aurait galement pu crire : mov ax, 4c00h int 21h La seule diffrence est que AL est mis zro, ce qui indique au programme qui on rend le contrle (ici le DOS) que notre programme sest termin normalement. Les fichiers COM peuvent galement utiliser la fonction 4ch. Le segment de code se termine : code ends et le segment de donnes commence sa suite : data segment use16 message db Bonjour, monde !, $ data ends Il nous reste crire le segment de pile. Dans ce programme, il ntait pas absolument indispensable de le sparer du segment de code. Mais cest une bonne habitude de le faire. pile segment stack remplissage db 256 DUP (?) pile ends Le mot-cl stack indique que ce segment est le segment de pile. Les mots db 256 DUP (?) dclarent 256 octets non initialiss. Cest la matire de notre pile. Sachez que tout appel dinterruption se traduit par lempilage des flags et de CS:IP. Il est donc indispensable davoir une pile, mme si celle-ci peut ventuellement partager le mme segment que le code, comme dans un fichier COM. Mais dans un programme EXE, il vaut mieux rserver un segment la pile. Les 256 octets que nous dclarons ici indiquent seulement que la pile contient 256 octets. Ainsi, au dbut de lexcution, SS:SP pointera vers la fin de ces octets. Nous verrons ce que signifie le point dinterrogation dans la troisime partie.
La fin du fichier et le point dentre sont signals par : end debut Et voil ! Vous avez dcouvert lallure dun programme en assembleur. Nous pouvons donc prsent nous pencher sur ltude du langage.
TROISIEME PARTIE
LE LANGAGE ASSEMBLEUR
Pour dfinir plusieurs fois la suite les mmes donnes, on utilise DUP ( duplicate ) de la manire suivante : TOTO db 100 dup(TOTO EST BEAU) Cela revient crire : TOTO db TOTO EST BEAU TOTO db TOTO EST BEAU TOTO db TOTO EST BEAU ; etc (100 fois) Il est frquent que de nombreuses donnes naient pas besoin dtre initialises une valeur prcise. Dans ce cas, on les regroupe la fin du programme et on les remplace par le caractre ?. Les adresses des labels seront calcules de la mme faon mais aucune donnes ne sera crite dans le fichier (et a fortiori dans la RAM). On dit que lon met ses variables sur le tas ( heap en anglais). La seule diffrence avec une dfinition classique est quau dbut de lexcution, nos variables nauront pas de valeur dfinie. Leurs valeurs seront alatoires en ce sens quon ne peut les connatre au moment de la compilation. Exemple :
Remarque : Si elles ne sont pas regroupes en fin de programme, le compilateur sera oblig d'crire les donnes dans le fichier afin de ne pas fausser les adresses des variables (ou du code) qui suivent. Il remplira alors les points dinterrogation avec des zros. Pour indiquer quun chiffre est not en base hexadcimale, on lui ajoute la lettre h. La lettre b signifie que le chiffre est cod en binaire, la lettre 'o' en octal et la lettre d en base dcimale (base par dfaut).
2. Ladressage
a) ladressage immdiat
On appelle adressage immdiat ladressage qui ne fait intervenir que des constantes. Considrons cette partie de programme qui stocke le nombre 125h dans une variable appele TOTO, lui ajoute 15 puis charge le rsultat dans AX :
La ligne mov word ptr ds:[TOTO], 125h charge le nombre 125h dans le mot de la RAM adress par DS et loffset de TOTO. La ligne suivante ajoute 15 au contenu de ce mot. Lexpression word ptr devant ladresse, obligatoire ici, indique la taille de la variable dans laquelle doit tre stock le nombre 125h. Si on avait mis dword ptr, ce nombre aurait t cod sur 32 bits (00000125h) au lieu de 16 bits (0125h) : on aurait donc cras les deux octets qui suivent la variable. Si on avait mis byte ptr, la compilation aurait t impossible car un octet ne peut contenir un nombre suprieur FFh. Le compilateur na en effet aucun moyen de connatre cette taille. La variable TOTO na pas de taille (malgr le mot dw) : ce nest en fait quun pointeur vers le premier octet du word quelle reprsente. En revanche, la troisime instruction ne fait pas apparatre lexpression word ptr. Cette fois-ci elle est facultative du fait que la destination (AX = 16 bits) impose la taille. On peut videmment remplacer TOTO par une constante numrique : mov word ptr ds:[004Ch], 125h
Le mot 125h sera alors crit ladresse DS:4C. Une question se pose prsent : que se passe-t-il si le programmeur ncrit pas le registre de segment dans ladresse ? Cest l quintervient la directive assume. Le compilateur va devoir trouver lui-mme quel registre lutilisateur a voulu sous-entendre. Si on a utilis un label, alors le segment sera celui dans lequel est dclar le label. Mais le compilateur veut un REGISTRE de segment. Il va donc prendre celui qui est cens pointer vers le bon segment et pour le savoir, il examine la directive assume. Voil pourquoi cette dernire peut nous pargner dcrire pour chaque variable lexpression ds:. Comprenez bien que cette directive ne sert rien dautre qu cela et quen aucune faon elle ne force les registres de segment prendre quelque valeur que ce soit.
Base + Constante : Exemples : MOV byte ptr ds:[BP + 1], 12 MOV ds:[BX + (TOTO BOBO)/10], AX Index + Constante : Exemples : MOV CX, [TOTO + DI] MOV AL, ds:[1 + SI] Base + Index : Exemple : MOV ds:[BP + SI], AH Base + Index + Constante : Exemple : MOV AX, word ptr [TOTO + BX + DI + 1]
Remarque : si la constante nest pas un label, il est parfois impratif de spcifier le registre de segment ! Tout dpend du contexte
Le mot short ajout aprs jmp indique au compilateur que le label COUCOU se trouve une distance (signe) qui peut tre stocke sur un seul octet. Le compilateur ne le sait pas encore lorsquil essaie de compiler linstruction de saut car le label se trouve en de de cette instruction. Cest pourquoi il prvoit deux octets pour crire le saut, au cas o ladresse darrive soit loigne de plus de 128 octets. Lorsquil effectue une deuxime passe , sil saperoit quun seul octet aurait suffi il remplit alors loctet inutile avec linstruction NOP ( No Operation ) qui ne fait rien du tout. Le programme marchera parfaitement (encore heureux !), mais cela gaspille un octet. Le programmeur a la possibilit daider le compilateur en ajoutant le mot short, ainsi un seul octet est prvu pour la distance de saut. Si le label est plac plus haut que l'instruction de saut, il est inutile de l'crire. Remarque : Ce mot peut galement tre utilis avec les instructions de saut conditionnel que nous tudierons plus loin.
2. Les procdures
a) appels de procdures
Ceux qui ont dj programm en BASIC connaissent les instructions GOSUB et RETURN. Leurs quivalents en assembleur sont CALL et RET. Linstruction CALL sert appeler une procdure : Syntaxe CALL MonLabel Action : empile loffset de linstruction suivante puis fait un simple saut ladresse reprsente par MonLabel. RET (ou RETN) permet de retourner linstruction qui suit immdiatement le CALL : Syntaxe : RET Action : dpile ladresse de retour et la met dans IP. Le programme continue donc ladresse qui suit le CALL. Remarque : Rappelez-vous quil est possible de terminer un programme COM avec linstruction RET. Puisque le DOS empile un zro de deux octets au chargement du programme, IP prendra la valeur 0000 lorsque le processeur excutera cette instruction : il pointera donc vers le dbut du PSP. Or le PSP commence toujours par les deux octets suivants : CDh 20h (ce qui scrit INT 20h en assembleur). Linterruption 20h, qui permet de terminer un programme COM, sera donc appele et le contrle sera rendu au DOS. Voici un exemple dutilisation des procdures aussi simple que possible : ce programme COM appelle 12 fois une procdure qui crit un message lcran et rend la main au DOS. Il nest daucune utilit et nest pas optimis du tout.
Remarque : Les codes ASCII 10 et 13 reprsentent respectivement la fin de ligne et le retour chariot. Grce eux, on revient la ligne chaque fois quon a crit le message. Ce programme fonctionne parfaitement. Toutefois, les conventions dcriture veulent que les procdures soient crites comme suit :
Le programme compil est toujours le mme, mais le code est plus lisible puisqu'on distingue
les procdures des simples labels. Le mot near signifie que ladresse de cette procdure est rduite un offset. Il ny a pas dadresse de segment. La procdure ne pourra donc tre appele que de lintrieur mme du segment. Le mot qui a le sens contraire de near est far. Les call far sont assez peu utiliss.
Soyez toujours trs vigilant avec les appels de procdures : pensez que ladresse de retour sera dpile lorsque le programme rencontrera linstruction RET. Il serait donc suicidaire de laisser une donne empile avant dappeler cette instruction.
3. Les macros
Etant donn que certaines instructions se rptent constamment dans un programme, lcriture de macrofonctions (ou macros) est un moyen pratique de rendre votre code source plus lisible. Il est possible de choisir pour certaines suites dinstructions un nom qui les reprsente. Lorsque le compilateur (en fait, le prprocesseur) rencontrera ce nom dans votre code source, il le remplacera par les lignes de code quil dsigne. Ces lignes forment une macro . Les macros, la diffrence des procdures, nont aucune signification pour la machine. Seul le compilateur comprend leur signification. Elles ne sont quun artifice mis la disposition du programmeur pour clarifier son programme. Lorsque le compilateur rencontre le nom dune macro dans votre code, il le remplace par le code de la macro. Tout se passe exactement comme si vous aviez tap vous-mme ce code la place du nom de la macro. Ainsi, si vous appelez quinze fois une macro dans votre programme, le compilateur crira quinze fois le code de cette macro. Cest toute la diffrence avec les procdures qui ne sont crites quune seule fois mais peuvent tre appeles aussi souvent quon veut laide dun CALL. Voici comment crire une macro : lexemple suivant sert crire un message lcran.
Le code prcdent peut tre crit nimporte o dans votre programme, condition quil se trouve avant tout appel de cette macro. Afin dviter les ennuis, il est fortement conseill de runir vos macros au dbut du code, avant toute autre ligne. De toute faon, il ne sera pas compil lendroit o vous lavez crit mais aux endroits o se trouvent les appels de macros. Le mot text? est un paramtre . Le point dinterrogation nest pas requis ; nous lavons mis pour indiquer quil sagit dun paramtre et non dune variable. Une macro peut utiliser plusieurs paramtres spars par des virgules. Pour appeler cette macro, il vous suffit dcrire la ligne ecrit_texte Coucou ! Ceci est un essai ! Le compilateur se chargera alors de la remplacer par les instructions comprises entre la premire et la dernire ligne de cet exemple, en prenant le soin de remplacer le mot text? par le message fourni en paramtre. En assembleur, chaque label doit avoir un nom unique. Or, si une macro est appele plusieurs fois, les mmes noms seront utiliss. Il faut donc la plupart du temps inclure la directive LOCAL qui forcera le compilateur changer le nom des labels chaque appel de la macro. Attention : cette directive doit suivre immdiatement la dclaration de la macro. Supposons prsent que lon veuille crire lcran le message Je suis bien content et revenir la ligne laide de notre macro ecrit_texte. La syntaxe suivante : ecrit_texte Coucou ! Ceci est un essai !, 10, 13 est incorrecte, car le compilateur croirait que lon a crit trois paramtres ! Il faut alors entourer notre unique paramtre par les signes < et > :
4. La directive EQU
La directive EQU a un rle voisin de celui des macros. Elle permet de remplacer un simple mot par dautres plus complexes. Son intrt est quelle peut tre invoque en plein milieu dune ligne. Quelques exemples : Longueur EQU (fin debut) Message EQU Bonjour messieurs ! Comment allez-vous ?, $ Version EQU 2 Quitter EQU ret Quitter2 EQU int 20h Mettre_dans_AH EQU mov ah, Interruption_21h EQU int 21h Un programme qui contient de telles directives peut se terminer ainsi : Mettre_dans_AH 4Ch Interruption_21h Ou ainsi (si cest un COM) : Quitter2
5. Linclusion de fichiers
Il est possible daccder des procdures, des macros ou des dfinitions EQU qui se trouvent dans dautres fichiers. Cela permet de se constituer des librairies de macros ou de procdures que lon peut rutiliser dun programme lautre. Pour inclure le fichier TOTO.LIB, crivez au dbut de votre code source :
La condition if1 indique au compilateur que linclusion ne doit seffectuer que lors de la premire passe. Le fichier TOTO.LIB est un fichier texte tout fait banal qui contient des lignes de code en assembleur.
PRINCIPALES
INSTRUCTIONS
DU
LANGAGE
Remarques prliminaires : Le principe du langage assembleur est de remplacer chaque opcode hexadcimal par mot facile retenir. Ce mot est appel mnmonique. Par exemple, INT est mnmonique associ lopcode CDh. Chaque fois que le compilateur rencontrera mot, il le remplacera par loctet CDh et crira ensuite loprande (ici : le numro linterruption) en hexadcimal. un le ce de
Cette liste rcapitule les instructions que nous connaissons dj et en prsente de nouvelles. Elle nest pas exhaustive mais vous sera amplement suffisante pour la plupart de vos programmes. Certaines instructions, comme PUSHA, ne sont disponibles que pour des modles de processeurs plus volus que le 8086, par exemple le 286. Noubliez pas la directive .386 si vous les utilisez.
MOV [Variable2], DS ;Copie un word car DS contient 16 bits MOV word ptr [Variable3], 12 ;Ici, on spcifie que la variable est un word
JE ( Jump if Equal ) fait un saut au label spcifi si et seulement si ZF = 1. Rappelezvous que ce flag est 1 si et seulement si le rsultat de lopration prcdente vaut zro. Comme CMP ralise une soustraction, on utilise gnralement JE pour savoir si deux nombres sont gaux. Exemple :
Mnmonique quivalent : JZ ( Jump if Zero ) JG ( Jump if Greater ) fait un saut au label spcifi si et seulement si ZF = 0 et SF = OF. On lutilise en arithmtique signe pour savoir si un nombre est suprieur un autre. Exemple :
Mnmonique quivalent : JNLE ( Jump if Not Less Or Equal ) JGE ( Jump if Greater or Equal ) fait un saut au label spcifi si et seulement si SF = OF. On lutilise en arithmtique signe pour savoir si un nombre est suprieur ou gal un autre. Mnmonique quivalent : JNL ( Jump if Not Less ) JL ( Jump if Less ) fait un saut au label spcifi si et seulement si SF <> OF. On lutilise en arithmtique signe pour savoir si un nombre est infrieur un autre.
Mnmonique quivalent : JNGE ( Jump if Not Greater Or Equal ) JLE ( Jump if Less Or Equal ) fait un saut au label spcifi si et seulement si SF <> OF ou ZF = 1. On lutilise en arithmtique signe pour savoir si un nombre est infrieur ou gal un autre. Mnmonique quivalent : JNG ( Jump if Not Greater ) JA ( Jump if Above ) fait un saut au label spcifi si et seulement si ZF = 0 et CF = 0. On lutilise en arithmtique non signe pour savoir si un nombre est suprieur un autre. Mnmonique quivalent : JNBE ( Jump if Not Below Or Equal ) JAE ( Jump if Above or Equal ) fait un saut au label spcifi si et seulement si CF = 0. On lutilise en arithmtique non signe pour savoir si un nombre est suprieur ou gal un autre. Mnmonique quivalent : JNB ( Jump if Not Below ) JB ( Jump if Below ) fait un saut au label spcifi si et seulement si CF = 1. On lutilise en arithmtique non signe pour savoir si un nombre est infrieur un autre. Mnmonique quivalent : JNAE ( Jump if Not Above Or Equal ) JBE ( Jump if Below or Equal ) fait un saut au label spcifi si et seulement si CF = 1 ou ZF = 1. On lutilise en arithmtique non signe pour savoir si un nombre est infrieur ou gal un autre. Mnmonique quivalent : JNA ( Jump if Not Above )
Si lappel de la fonction 3Eh de linterruption 21H se solde par une erreur, alors la CF vaudra 1 et le saut sera accompli. Dans le cas oppos, lexcution continuera normalement de manire linaire. JNC ( Jump if not Carry ) fait un saut au label spcifi si et seulement si CF = 0. JZ ( Jump if Zero ) fait un saut au label spcifi si et seulement si ZF = 1. Ce mnmonique correspond au mme opcode que JE. JNZ ( Jump if not Zero ) fait un saut au label spcifi si et seulement si ZF = 0. Ce mnmonique correspond au mme opcode que JNE. JS ( Jump if Sign ) fait un saut au label spcifi si et seulement si SF = 1. JNS ( Jump if not Sign ) fait un saut au label spcifi si et seulement si SF = 0. JO ( Jump if Overflow ) fait un saut au label spcifi si et seulement si OF = 1. JNO ( Jump if not Overflow ) fait un saut au label spcifi si et seulement si OF = 0. JP ( Jump if Parity ) fait un saut au label spcifi si et seulement si PF = 1. JNP ( Jump if not Parity ) fait un saut au label spcifi si et seulement si PF = 0.
Si Source est un octet : AL est multipli par Source et le rsultat est plac dans AX. Si Source est un mot : AX est multipli par Source et le rsultat est plac dans DX:AX. Si Source est un double mot : EAX est multipli par Source et le rsultat est plac dans EDX:EAX. Indicateurs affects : CF, OF Remarque : Source ne peut tre une valeur immdiate. Exemples : MUL CX MUL byte ptr [TOTO]
Si Source est un double mot : EDX:EAX est divis par Source, le quotient est plac dans EAX et le reste dans EDX. Indicateurs affects : AF, OF, PF, SF, ZF Remarque : Source ne peut tre une valeur immdiate.
b) linstruction OR ( Logical OR )
Syntaxe : OR Destination, Source Description : Effectue un OU logique inclusif bit bit entre Destination et Source. Le rsultat est stock dans Destination. Indicateurs affects : CF, OF, PF, SF, ZF
Remarque : Afin doptimiser la taille et les performances du programme, on peut utiliser linstruction OR AX, AX la place de CMP AX, 0. En effet, un OU bit bit entre deux nombres identiques ne modifie pas Destination et est excut infiniment plus rapidement quune soustraction. Comme les flags sont affects, les sauts conditionnels sont possibles.
contenu de CF est ensuite rinsr droite. OF est mis 1 si et seulement si le signe de Destination a chang. Indicateurs affects : AF, CF, OF, PF, SF, ZF
10. Les instructions de gestion de la pile a) linstruction PUSH ( Push Word onto Stack )
Syntaxe : PUSH Source Description : Empile le mot Source. SP est dcrment de 2. Remarques : Source ne peut tre une valeur immdiate. Il est possible dabrger votre code source en crivant par exemple PUSH AX BX BP. Le compilateur crira alors trois fois linstruction PUSH du langage machine. Il est possible galement dempiler des doubles mots.
est incrment de 2.
Syntaxe : CALL MaProc Description : Appel de procdure. Si MaProc se trouve dans un segment extrieur, le processeur empile CS. Ensuite, dans tous les cas, il empile IP et fait un saut ltiquette MaProc.
c) linstruction RETF
Syntaxe : RETF Description : Retour de procdure se trouvant lextrieur du segment (FAR). Deux mots sont dpils et placs dans CS:IP. Le contrle retourne donc la procdure appelante.
a) linstruction AAA ( ASCII Adjust for Addition ) b) linstruction AAD ( ASCII Adjust for Division ) c) linstruction AAM ( ASCII Adjust for Multiplication ) d) linstruction AAS ( ASCII Adjust for Substraction ) e) linstruction CBW ( Convert Byte to Word )
Syntaxe : CBW Description : Convertit loctet sign stock dans AL en un mot (sign) stock dans AX. Ainsi, si AL est ngatif, AH sera rempli de 1 binaires, sinon, AH sera mis 0.
g) linstruction DAA ( Decimal Adjust for Addition ) h) linstruction DAS ( Decimal Adjust for Substraction ) i) linstruction MOVSX ( Move with Sign Extend )
Syntaxe : MOVSX Destination, Source Description : Dplace le contenu sign dun registre de 8 bits dans un registre de 16 bits, ou bien dplace le contenu sign dun registre de 16 bits dans un registre de 32 bits. Si Source est
ngatif, la partie haute de Destination sera remplie de 1 binaires, sinon elle sera remplie de 0.
QUATRIEME PARTIE
LES INTERRUPTIONS DU DOS RELATIVES AUX FICHIERS
Remarque prliminaire : Cette partie prsente quelques unes des interruptions du DOS qui servent manier les fichiers d'un disque. Nous la concluerons par l'tude d'un exemple de programme qui crypte un fichier choisi par l'utilisateur.
en fournir). Dans notre cas, le buffer va recevoir les octets lus dans le fichier. Aprs l'appel, AX contient le nombre d'octets qui ont t effectivement lus (il peut tre infrieur la taille demande si le fichier n'est pas assez long). En cas de problme, CF sera mis 1.
6. Conclusion
Le tableau suivant rcapitule ces diffrentes tapes : Fonction 3dh 3eh 3fh Description Ouvrir le fichier Fermer le fichier Lire le fichier Paramtres - DS:DX : adresse d'une chane contenant le chemin d'accs - AL : mode d'accs - BX : handle - BX : handle - CX : nombre d'octets - DS:DX : adresse d'un buffer
40h
1. La fonction 4eh
La fonction 4eh sert dfinir des critres de recherche et trouver le premier fichier qui correspond ces critres (s'il existe). On doit passer dans DS:DX l'adresse de la chane de caractres qui contient le masque de recherche (dans notre exemple, ce masque est *.com). Par dfaut, les fichiers sont cherchs dans le dossier courant. Mais on peut videmment spcifier un autre chemin dans le masque. Remarque importante : afin que le DOS puisse connatre sa taille, le masque doit imprativement tre termin par l'octet 00h ! On crit galement dans CX les attributs des fichiers que l'on dsire trouver. Si CX vaut 0, seuls les fichiers normaux pourront tre trouvs. En fait, chaque bit de CL reprsente un attribut, comme le montre le tableau ci-dessous : Bit 1 2 3 4 5 6 7 8 Signification Lecture seule Fichier cach Fichier systme Volume Rpertoire Fichier (Aucune...) (Aucune...)
Pour demander la fonction 4eh de ne pas oublier les fichiers cachs, il suffit donc de charger CX avec la valeur 2 (bit numro 2 = 1). De mme, l'attribut 00000111b (soit 7) nous permettra de trouver les fichiers en lecture seule, les fichiers cachs et les fichiers systmes. Remarque : Ne vous souciez pas trop des bits numro 4 et 6. Laissez-les 0. Une fois que les paramtres ont t ajusts, on peut appeler la fonction 4eh. Si aucun fichier n'a t trouv, le flag CF est mis 1. On doit donc fait un test sur CF pour savoir si la recherche peut continuer ou si elle doit s'arrter. Si au contraire la fonction a trouv un fichier, les caractristiques de ce fichier (i.e. son nom, sa taille, ses attributs,...) sont inscrits dans une zone de la mmoire appele DTA ( Disk Transfer Area ). Mais o se trouve donc cette DTA et quoi ressemble-t-elle ? Rponse : par dfaut, le DOS place la DTA dans le PSP de votre programme, l'offset 80h.
Remarque : il vous est naturellement possible de la dplacer en faisant appel la fonction 1ah. Voici la structure de la DTA : Adresse 00h 01h 0Ch 15h 16h 18h 1Ah 1Eh Description Lettre du lecteur (0=courant, 1=A, 2=B, ...) sur lequel se trouve le fichier Modle de la recherche Rserv Attributs du fichier Heure du fichier Date du fichier Taille du fichier Nom du fichier avec l'extension Taille (octets) 1 11 9 1 2 2 4 13
Puisque par dfaut la DTA est situe dans le PSP l'offset 80h, le nom du fichier trouv est crit l'offset 80h + 1eh = 9eh. De mme, la taille se trouve l'offset 9ah, etc...
2. La fonction 4fh
Jusqu' prsent, nous n'avons trouv qu'un seul fichier ! Pour poursuivre la recherche, appelez la fonction 4fh sans crire aucun paramtre dans les registres. Les caractristiques de votre recherche ont t mmorises dans la DTA : vous n'avez donc pas les rappeler. De mme que pour la fonction 4eh, CF est mis 1 si aucun nouveau fichier n'a t trouv. C'est le signe que vous pouvez arrter votre recherche.
3. Conclusion
Rsumons nous : Fonction 4eh 4fh Paramtres - DS:DX : adresse d'une chane Trouver le premier fichier qui correspond contenant le masque aux caractristiques spcifies - CX : attributs Trouver le prochain fichier Aucun ! Description
A titre d'exemple, crivons prsent le programme que nous voquions tout l'heure : trouver tous les fichiers COM du dossier courant et les effacer.
La seule difficult est dans la dclaration du buffer. Voici comment vous devez vous y prendre si vous considrez que l'utilisateur pourra entrer au plus n caractres :
Le premier octet de maChaine doit contenir le nombre d'octets maximal qui pourra tre entr, plus 1. Pourquoi plus 1 ? Tout simplement parce que le DOS met la fin des caractres taps le code ASCII 13 (retour chariot). Pour comprendre la suite de la dclaration, il faut savoir de quelle manire le DOS transmet les caractres qui ont t lus. Aprs la lecture, le deuxime octet du buffer contiendra le nombre exact d'octets lus (sans compter le 13 final). La chane proprement dite ne commencera donc qu'au troisime octet. Puisqu'un octet est utilis pour donner au DOS le nombre maximal de caractres autoris, un autre pour recevoir le nombre de caractres lus effectivement, et encore un autre pour recevoir le code ASCII 13, le buffer doit comporter trois octets de plus que la taille maximale de la chane. C'est pourquoi on crit : maChaine db n+1, ?, n dup(?), ?
2. Le programme
Le programme suivant demande l'utilisateur d'entrer le nom d'un fichier se trouvant dans le dossier courant puis crypte ce fichier en appliquant un NOT logique sur chaque octet. Naturellement, il n'est pas optimis du tout. Ce n'est qu'un exemple !
Suggestion : quand vous aurez compris ce petit programme, essayez donc de l'amliorer ! Par exemple, il faudrait que l'utilisateur puisse taper le nom du fichier crypter directement comme paramtre du programme (i.e. CRY MONFICH.EXE). Voyez pour cela la structure du PSP telle que prsente dans la premire partie. De plus, le programme devrait pouvoir trouver automatiquement le nom du fichier d'arrive : ce serait le mme que celui du fichier crypter, mais avec une extension diffrente (par exemple .cry). Vous pourriez galement tendre les possibilits du logiciel en permettant l'utilisateur de saisir non plus un simple nom de fichier, mais un masque complexe avec des chemins d'accs et des caractres jokers comme '*' et '?'. Vous aurez donc besoin des fonctions de recherche 4eh et 4fh. L'utilisateur doit de surcrot pouvoir choisir s'il veut crypter un fichier ou bien le dcrypter...
CONCLUSION
Voil. Nous esprons que vous avez compris lessentiel de ce cours, savoir la logique de la programmation en assembleur. Les connaissances que vous avez acquises en lisant la fin du cours devraient vous permettre de mieux saisir les premiers chapitres. Cest pourquoi nous vous invitons relire ce tutoriel. Certaines notions doivent encore vous paratre obscures. Maintenant que vous vous tes familiaris avec lassembleur, commencez par crire et tester quelques petits programmes COM inutiles et trs simples. Vous verrez quau bout de quelques essais, tout ce que vous avez lu vous semblera trs concret. Lorsque vous aurez crit vous-mme quelques tout petits programmes qui fonctionnent, lassimilation du langage deviendra alors trs rapide, car lassembleur est un langage trs logique et trs cohrent. Nous vous conseillons dcrire une librairie de petites macros ou procdures qui vous serviront dans tous vos programmes. Par exemple, vous pouvez faire une macro qui affiche un entier lcran, ou bien qui convertit une chane de caractres en un nombre entier. Voici un exemple de macro qui renvoie dans AL le nombre de chiffres dun entier non sign de deux octets pass dans AX :
N.B. : Pensez avant toute chose vous munir dune liste des interruptions du DOS. Vous ne pourrez rien faire si vous nen avez pas !